VibeCoderzVibeCoderz
Telegram
All Prompts
Bespoke Configurations Section with WebGL Preview preview
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>
All Prompts