All Prompts
All Prompts

featuresectioninteractivewebgltailwindmarketing
Bespoke Configurations Section with WebGL Preview
Секция для маркетинга с WebGL-превью кастомизаций. Интерактивный просмотр продукта, список преимуществ, отзыв и CTA. Адаптивный дизайн.
Prompt
<section class="py-32 relative bg-[#F2EFEA] text-[#2C2824] overflow-hidden" id="bespoke-section">
<!-- Interactive Script Container -->
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
<div class="max-w-[88rem] mx-auto px-6 lg:px-12 grid grid-cols-1 md:grid-cols-2 gap-16 items-center">
<!-- Content Column -->
<div class="max-w-md">
<h2 class="text-4xl md:text-5xl tracking-tighter mb-6 font-light"
style="font-family: 'Plus Jakarta Sans', sans-serif;">
Bespoke<br>Configurations
</h2>
<p class="text-lg opacity-80 font-normal leading-relaxed mb-12 border-b border-black/10 pb-12"
style="font-family: 'Geist', sans-serif;">
Our engineering team is available to calibrate piece metrics precisely to your spatial and biomechanical
requirements.
</p>
<div class="flex gap-8 items-start mb-12">
<iconify-icon icon="solar:arrow-right-up-linear" class="text-6xl text-black/20 flex-shrink-0"></iconify-icon>
<div>
<p class="font-medium tracking-tight mb-4 text-sm uppercase opacity-50"
style="font-family: 'Geist', sans-serif;">Consultation Protocols</p>
<ul class="space-y-3 text-base font-medium opacity-90 tracking-tight"
style="font-family: 'Geist', sans-serif;">
<li class="flex items-center gap-3">
<span class="w-1.5 h-1.5 rounded-full border border-current"></span>
Spatial analysis & sizing
</li>
<li class="flex items-center gap-3">
<span class="w-1.5 h-1.5 rounded-full border border-current"></span>
Textile and density selection
</li>
<li class="flex items-center gap-3">
<span class="w-1.5 h-1.5 rounded-full border border-current"></span>
Integration with existing architecture
</li>
<li class="flex items-center gap-3">
<span class="w-1.5 h-1.5 rounded-full border border-current"></span>
Manufacturing timeline review
</li>
</ul>
</div>
</div>
<!-- Testimonial Card -->
<div class="flex items-center gap-6 mb-8 p-4 rounded-xl bg-white/40 backdrop-blur-sm border border-black/5">
<img src="https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=200&auto=format&fit=crop" alt="Consultant" class="w-14 h-14 rounded-full object-cover">
<div style="font-family: 'Geist', sans-serif;">
<p class="text-sm font-medium tracking-tight">"We refine every curve until it feels inevitable."</p>
<p class="text-xs opacity-50 mt-1 uppercase tracking-wider">Elena R. — Lead Ergonomist</p>
</div>
</div>
<!-- Action Button -->
<a href="#"
class="inline-flex items-center gap-2 bg-[#2C2824] text-[#F2EFEA] px-6 py-3 rounded-full text-sm font-medium transition-transform hover:scale-105"
style="font-family: 'Geist', sans-serif; position: relative; z-index: 1;">
<span>Initiate Request</span>
<iconify-icon icon="solar:arrow-right-up-linear"></iconify-icon>
</a>
</div>
<!-- WebGL Image Column -->
<div class="h-full min-h-[500px] relative rounded-2xl overflow-hidden cursor-crosshair group" id="bespoke-card"
style="background: rgba(255, 255, 255, 0.4); border: 1px solid rgba(0, 0, 0, 0.05);">
<div
class="absolute inset-0 z-10 pointer-events-none transition-opacity duration-500 opacity-0 group-hover:opacity-100"
style="background: radial-gradient(600px circle at var(--m-x, 50%) var(--m-y, 50%), rgba(255, 255, 255, 0.8), transparent 40%);">
</div>
<div class="w-full h-full absolute inset-0 z-0">
<img id="webgl-target-img"
src="https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/226163af-42a4-443b-977d-7e18fe3f4d61_1600w.webp"
alt="Chair Configuration"
class="w-full h-full object-cover">
</div>
</div>
</div>
<script>
(function() {
const section = document.getElementById('bespoke-section');
const card = document.getElementById('bespoke-card');
const targetImg = document.getElementById('webgl-target-img');
// Flashlight Tracking
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
card.style.setProperty('--m-x', `${e.clientX - rect.left}px`);
card.style.setProperty('--m-y', `${e.clientY - rect.top}px`);
});
// WebGL Logic
const vs = `attribute vec2 p;varying vec2 u;void main(){u=p*0.5+0.5;gl_Position=vec4(p,0,1);}`;
const fs = `precision highp float;varying vec2 u;uniform sampler2D t;uniform float g;void main(){float c=4.0;float i=floor(u.x*c);float s=0.15;float d=i*s;float p=clamp((g*(1.0+3.0*s)-d),0.0,1.0);float e=pow(p-1.0,3.0)+1.0;if(u.y<1.0-e)discard;gl_FragColor=texture2D(t,u);}`;
function initWebGL(img) {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl', { alpha: true });
if (!gl) return;
const image = new Image();
image.crossOrigin = "anonymous";
image.onload = () => {
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
canvas.className = img.className;
img.parentNode.replaceChild(canvas, img);
const createShader = (t, s) => {
const sh = gl.createShader(t);
gl.shaderSource(sh, s);
gl.compileShader(sh);
return sh;
};
const prog = gl.createProgram();
gl.attachShader(prog, createShader(gl.VERTEX_SHADER, vs));
gl.attachShader(prog, createShader(gl.FRAGMENT_SHADER, fs));
gl.linkProgram(prog);
gl.useProgram(prog);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), gl.STATIC_DRAW);
const pLoc = gl.getAttribLocation(prog, "p");
gl.enableVertexAttribArray(pLoc);
gl.vertexAttribPointer(pLoc, 2, gl.FLOAT, false, 0, 0);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
const gLoc = gl.getUniformLocation(prog, "g");
let start = null;
const draw = (now) => {
if (!start) start = now;
const progress = Math.min((now - start) / 1200, 1);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform1f(gLoc, progress);
gl.drawArrays(gl.TRIANGLES, 0, 6);
if (progress < 1) requestAnimationFrame(draw);
};
const obs = new IntersectionObserver((es) => {
if (es[0].isIntersecting) {
requestAnimationFrame(draw);
obs.disconnect();
}
}, { threshold: 0.1 });
obs.observe(canvas);
};
image.src = img.src;
}
initWebGL(targetImg);
})();
</script>
</section>