.e-con:has(> .elementor-widget #kh-bg),
.e-con:has(> .e-con-inner > .elementor-widget #kh-bg) {
position: relative;
overflow: hidden;
}
.elementor-widget:has(#kh-bg) {
position: absolute !important;
top: 0;
left: 0;
width: 100% !important;
height: 100%;
margin: 0 !important;
z-index: 0;
}
#kh-bg {
position: absolute;
inset: 0;
overflow: hidden;
isolation: isolate;
background:
radial-gradient(60% 45% at 50% 105%, rgba(30, 255, 160, 0.32), transparent 70%),
radial-gradient(45% 35% at 28% 92%, rgba(120, 70, 255, 0.28), transparent 70%),
linear-gradient(#030712, #061225);
}
#kh-bg .kh-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
opacity: 0;
transition: opacity 1.4s ease;
}
#kh-bg.kh-ready .kh-canvas {
opacity: 1;
}
#kh-bg.kh-fallback .kh-canvas {
display: none;
}
(function () {
var CONFIG = { maxDpr: 1.5, minQuality: 0.5, offsetY: 0.1 };
var root = document.getElementById('kh-bg');
if (!root) return;
var canvas = root.querySelector('.kh-canvas');
var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var VERT = 'attribute vec2 aPos;void main(){gl_Position=vec4(aPos,0.0,1.0);}';
var FRAG = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform vec2 uRes;
uniform float uTime;
uniform float uOff;
uniform mat3 uRot;
const float FOC = 2.25;
const float ZP = -2.5;
const float IOR = 1.45;
const float BR = 1.75;
float hash(vec2 p) {
p = fract(p * vec2(123.34, 456.21));
p += dot(p, p + 45.32);
return fract(p.x * p.y);
}
float vnoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
float fbm(vec2 p) {
float v = 0.0;
float a = 0.5;
for (int i = 0; i < 4; i++) {
v += a * vnoise(p);
p = p * 2.03 + vec2(17.3, 9.1);
a *= 0.5;
}
return v;
}
vec3 aurora(vec2 p) {
float t = uTime;
vec3 acc = vec3(0.0);
for (int i = 0; i 0.0) {
float sq = sqrt(disc);
float tFar = -b + sq;
float t = max(-b - sq, 0.0);
bool hit = false;
for (int i = 0; i < 56; i++) {
float d = crystal(ro + rd * t);
if (d tFar) break;
}
if (hit) {
vec3 pos = ro + rd * t;
vec3 n = calcNormal(pos);
vec3 refl = envAt(pos, reflect(rd, n), S);
float fres = pow(1.0 - clamp(dot(-rd, n), 0.0, 1.0), 3.0);
fres = mix(0.05, 1.0, fres);
vec3 dd = refract(rd, n, 1.0 / IOR);
vec3 q = pos - n * 0.02;
float thick = 0.0;
vec3 dR = dd;
vec3 dG = dd;
vec3 dB = dd;
for (int bnc = 0; bnc < 3; bnc++) {
float ti = 0.0;
for (int j = 0; j < 24; j++) {
float di = -crystal(q + dd * ti);
if (di 0.0) {
vec3 rR = refract(dd, -ne, IOR - 0.07);
vec3 rB = refract(dd, -ne, IOR + 0.10);
dG = rG;
dR = dot(rR, rR) > 0.0 ? rR : rG;
dB = dot(rB, rB) > 0.0 ? rB : rG;
break;
}
dd = reflect(dd, ne);
q -= ne * 0.02;
dR = dd;
dG = dd;
dB = dd;
}
vec3 trans = vec3(envAt(q, dR, S).r, envAt(q, dG, S).g, envAt(q, dB, S).b);
vec3 tint = exp(-thick * vec3(1.1, 0.25, 0.45));
trans *= tint * 2.1;
vec3 ld = normalize(vec3(-0.55, 0.85, 0.65));
float diff = max(dot(n, ld), 0.0);
float spec = pow(max(dot(reflect(rd, n), ld), 0.0), 48.0);
vec3 inner = vec3(0.06, 0.42, 0.50) * (0.35 + 0.65 * diff) * exp(-thick * 0.8);
vec3 ld2 = normalize(vec3(0.7, -0.4, 0.5));
float diff2 = max(dot(n, ld2), 0.0);
float rim = pow(1.0 - clamp(dot(-rd, n), 0.0, 1.0), 2.5);
col = mix(trans, refl * 1.5, fres);
col += inner * 0.55;
col += vec3(0.05, 0.22, 0.30) * diff * 0.25;
col += vec3(0.38, 0.16, 0.70) * diff2 * 0.38;
col += vec3(0.30, 0.95, 0.85) * rim * 0.32;
col += vec3(0.9, 1.0, 1.0) * spec * 1.3;
}
}
col *= mix(0.55, 1.0, smoothstep(-1.0, -0.35, p.y));
col *= 1.0 - 0.32 * dot(p * 0.55, p * 0.55);
col = 1.0 - exp(-col * 1.35);
col = pow(col, vec3(0.92));
col += (hash(gl_FragCoord.xy + fract(uTime)) - 0.5) / 128.0;
gl_FragColor = vec4(col, 1.0);
}
`;
var gl = null;
var loc = {};
var quality = 1;
var animTime = 0;
var last = 0;
var raf = 0;
var running = false;
var visible = true;
var frames = 0;
var acc = 0;
var mouse = { x: 0, y: 0, tx: 0, ty: 0 };
function compile(type, src) {
var s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.warn(gl.getShaderInfoLog(s));
gl.deleteShader(s);
return null;
}
return s;
}
function setup() {
var vs = compile(gl.VERTEX_SHADER, VERT);
var fs = compile(gl.FRAGMENT_SHADER, FRAG);
if (!vs || !fs) return false;
var prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
console.warn(gl.getProgramInfoLog(prog));
return false;
}
gl.useProgram(prog);
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
var a = gl.getAttribLocation(prog, 'aPos');
gl.enableVertexAttribArray(a);
gl.vertexAttribPointer(a, 2, gl.FLOAT, false, 0, 0);
loc.res = gl.getUniformLocation(prog, 'uRes');
loc.time = gl.getUniformLocation(prog, 'uTime');
loc.off = gl.getUniformLocation(prog, 'uOff');
loc.rot = gl.getUniformLocation(prog, 'uRot');
return true;
}
function resize() {
var r = root.getBoundingClientRect();
var dpr = Math.min(window.devicePixelRatio || 1, CONFIG.maxDpr) * quality;
var w = Math.max(2, Math.round(r.width * dpr));
var h = Math.max(2, Math.round(r.height * dpr));
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w;
canvas.height = h;
}
gl.viewport(0, 0, w, h);
}
function draw() {
var ay = animTime * 0.22 + mouse.x * 1.3;
var ax = mouse.y * 0.4 + 0.18 * Math.sin(animTime * 0.31);
var cx = Math.cos(ax);
var sx = Math.sin(ax);
var cy = Math.cos(ay);
var sy = Math.sin(ay);
var m = new Float32Array([cy, 0, -sy, sy * sx, cx, cy * sx, sy * cx, -sx, cy * cx]);
gl.uniform2f(loc.res, canvas.width, canvas.height);
gl.uniform1f(loc.time, animTime);
gl.uniform1f(loc.off, CONFIG.offsetY);
gl.uniformMatrix3fv(loc.rot, false, m);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
function frame(now) {
if (!running) return;
raf = requestAnimationFrame(frame);
var raw = (now - last) / 1000;
last = now;
var dt = Math.min(raw, 0.05);
animTime += dt;
var k = Math.min(1, dt * 3);
mouse.x += (mouse.tx - mouse.x) * k;
mouse.y += (mouse.ty - mouse.y) * k;
draw();
if (!root.classList.contains('kh-ready')) root.classList.add('kh-ready');
if (raw 20) {
acc += raw;
if (frames >= 60) {
var avg = acc / 40;
frames = 20;
acc = 0;
if (avg > 0.026 && quality > CONFIG.minQuality) {
quality = Math.max(CONFIG.minQuality, quality * 0.82);
resize();
}
}
}
}
}
function start() {
if (running || !visible || document.hidden || reduceMotion || !gl) return;
running = true;
last = performance.now();
raf = requestAnimationFrame(frame);
}
function stop() {
running = false;
cancelAnimationFrame(raf);
}
function onResize() {
if (!gl) return;
resize();
if (reduceMotion) draw();
}
gl = canvas.getContext('webgl', { antialias: false, alpha: false, powerPreference: 'high-performance' }) ||
canvas.getContext('experimental-webgl');
if (!gl || !setup()) {
root.classList.add('kh-fallback');
return;
}
window.addEventListener('pointermove', function (e) {
var r = root.getBoundingClientRect();
if (r.bottom window.innerHeight) return;
mouse.tx = Math.max(-1, Math.min(1, ((e.clientX - r.left) / r.width) * 2 - 1));
mouse.ty = Math.max(-1, Math.min(1, -(((e.clientY - r.top) / r.height) * 2 - 1)));
}, { passive: true });
document.addEventListener('visibilitychange', function () {
if (document.hidden) stop(); else start();
});
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
visible = entries[0].isIntersecting;
if (visible) start(); else stop();
}).observe(root);
}
if ('ResizeObserver' in window) {
new ResizeObserver(onResize).observe(root);
} else {
window.addEventListener('resize', onResize);
}
canvas.addEventListener('webglcontextlost', function (e) {
e.preventDefault();
stop();
});
canvas.addEventListener('webglcontextrestored', function () {
if (setup()) {
resize();
if (reduceMotion) draw(); else start();
} else {
root.classList.add('kh-fallback');
}
});
resize();
if (reduceMotion) {
animTime = 6;
draw();
root.classList.add('kh-ready');
} else {
start();
}
})();