{
  "streamed": true,
  "usage": {
    "prompt_tokens": 102,
    "completion_tokens": 13490,
    "total_tokens": 13592,
    "cost": 0.162084,
    "is_byok": false,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "cache_write_tokens": 0,
      "audio_tokens": 0,
      "video_tokens": 0
    },
    "cost_details": {
      "upstream_inference_cost": 0.162084,
      "upstream_inference_prompt_cost": 0.000204,
      "upstream_inference_completions_cost": 0.16188
    },
    "completion_tokens_details": {
      "reasoning_tokens": 5474,
      "image_tokens": 0,
      "audio_tokens": 0
    }
  },
  "finishReason": "stop",
  "content": "```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Procedural 3D Big Mac</title>\n    <style>\n        body { margin: 0; overflow: hidden; background-color: #1a1a1a; background: radial-gradient(circle at center, #333 0%, #111 100%); }\n        canvas { display: block; width: 100vw; height: 100vh; }\n        #loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-family: sans-serif; font-size: 24px; font-weight: bold; pointer-events: none; transition: opacity 0.5s; text-shadow: 0 2px 4px rgba(0,0,0,0.5); }\n    </style>\n    <!-- Import Maps for Three.js -->\n    <script type=\"importmap\">\n        {\n            \"imports\": {\n                \"three\": \"https://unpkg.com/three@0.160.0/build/three.module.js\",\n                \"three/addons/\": \"https://unpkg.com/three@0.160.0/examples/jsm/\"\n            }\n        }\n    </script>\n</head>\n<body>\n    <div id=\"loading\">Cooking...</div>\n    <script type=\"module\">\n        import * as THREE from 'three';\n        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\n        // --- Simplex Noise Implementation for Procedural Generation ---\n        class SimplexNoise {\n            constructor() {\n                this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];\n                this.p = []; for (let i=0; i<256; i++) this.p[i] = Math.floor(Math.random()*256);\n                this.perm = []; for(let i=0; i<512; i++) this.perm[i] = this.p[i & 255];\n            }\n            dot(g, x, y, z) { return g[0]*x + g[1]*y + g[2]*z; }\n            noise3D(xin, yin, zin) {\n                let n0, n1, n2, n3;\n                const F3 = 1.0/3.0, G3 = 1.0/6.0;\n                let s = (xin+yin+zin)*F3; let i = Math.floor(xin+s), j = Math.floor(yin+s), k = Math.floor(zin+s);\n                let t = (i+j+k)*G3; let X0 = i-t, Y0 = j-t, Z0 = k-t;\n                let x0 = xin-X0, y0 = yin-Y0, z0 = zin-Z0;\n                let i1, j1, k1, i2, j2, k2;\n                if(x0>=y0) { if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } }\n                else { if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } }\n                let x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;\n                let x2 = x0 - i2 + 2.0*G3, y2 = y0 - j2 + 2.0*G3, z2 = z0 - k2 + 2.0*G3;\n                let x3 = x0 - 1.0 + 3.0*G3, y3 = y0 - 1.0 + 3.0*G3, z3 = z0 - 1.0 + 3.0*G3;\n                let ii = i & 255, jj = j & 255, kk = k & 255;\n                let gi0 = this.perm[ii+this.perm[jj+this.perm[kk]]] % 12;\n                let gi1 = this.perm[ii+i1+this.perm[jj+j1+this.perm[kk+k1]]] % 12;\n                let gi2 = this.perm[ii+i2+this.perm[jj+j2+this.perm[kk+k2]]] % 12;\n                let gi3 = this.perm[ii+1+this.perm[jj+1+this.perm[kk+1]]] % 12;\n                let t0 = 0.6 - x0*x0 - y0*y0 - z0*z0; if(t0<0) n0 = 0.0; else { t0 *= t0; n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0, z0); }\n                let t1 = 0.6 - x1*x1 - y1*y1 - z1*z1; if(t1<0) n1 = 0.0; else { t1 *= t1; n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1, z1); }\n                let t2 = 0.6 - x2*x2 - y2*y2 - z2*z2; if(t2<0) n2 = 0.0; else { t2 *= t2; n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2, z2); }\n                let t3 = 0.6 - x3*x3 - y3*y3 - z3*z3; if(t3<0) n3 = 0.0; else { t3 *= t3; n3 = t3 * t3 * this.dot(this.grad3[gi3], x3, y3, z3); }\n                return 32.0*(n0 + n1 + n2 + n3);\n            }\n        }\n        const simplex = new SimplexNoise();\n\n        // --- Core Setup ---\n        const R_BURGER = 1.6;\n        const scene = new THREE.Scene();\n        const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100);\n        camera.position.set(5, 4, 8);\n        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });\n        renderer.setSize(window.innerWidth, window.innerHeight);\n        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n        renderer.shadowMap.enabled = true;\n        renderer.shadowMap.type = THREE.PCFSoftShadowMap;\n        renderer.toneMapping = THREE.ACESFilmicToneMapping;\n        renderer.toneMappingExposure = 1.0;\n        document.body.appendChild(renderer.domElement);\n\n        const controls = new OrbitControls(camera, renderer.domElement);\n        controls.enableDamping = true;\n        controls.dampingFactor = 0.05;\n        controls.autoRotate = true;\n        controls.autoRotateSpeed = 2.0;\n        controls.target.set(0, 1.2, 0);\n\n        // --- Lighting ---\n        const ambientLight = new THREE.AmbientLight(0xffeedd, 0.4);\n        scene.add(ambientLight);\n\n        const dirLight = new THREE.DirectionalLight(0xffffff, 2.5);\n        dirLight.position.set(5, 8, 4);\n        dirLight.castShadow = true;\n        dirLight.shadow.mapSize.width = 2048;\n        dirLight.shadow.mapSize.height = 2048;\n        dirLight.shadow.camera.near = 1;\n        dirLight.shadow.camera.far = 20;\n        const d = 4;\n        dirLight.shadow.camera.left = -d; dirLight.shadow.camera.right = d;\n        dirLight.shadow.camera.top = d; dirLight.shadow.camera.bottom = -d;\n        dirLight.shadow.bias = -0.001;\n        scene.add(dirLight);\n\n        const fillLight = new THREE.DirectionalLight(0xddeeff, 1.0);\n        fillLight.position.set(-5, 0, -5);\n        scene.add(fillLight);\n\n        const backLight = new THREE.PointLight(0xffccaa, 1.5, 10);\n        backLight.position.set(0, 2, -4);\n        scene.add(backLight);\n\n        // Procedural Environment setup for PBR reflections\n        const pmremGenerator = new THREE.PMREMGenerator(renderer);\n        pmremGenerator.compileEquirectangularShader();\n        const envScene = new THREE.Scene();\n        const envLight = new THREE.HemisphereLight(0xffffff, 0x444444, 2);\n        envScene.add(envLight);\n        const envLightDir = new THREE.DirectionalLight(0xffffff, 2);\n        envLightDir.position.set(1,1,1);\n        envScene.add(envLightDir);\n        scene.environment = pmremGenerator.fromScene(envScene).texture;\n\n        // --- Procedural Texture Generators ---\n        function createBunColorMap() {\n            const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512;\n            const ctx = canvas.getContext('2d');\n            const grad = ctx.createRadialGradient(256, 256, 20, 256, 256, 256);\n            grad.addColorStop(0, '#a65417'); // Dark highly baked center\n            grad.addColorStop(0.5, '#c97a2e');\n            grad.addColorStop(0.85, '#e8a964'); // Rim\n            grad.addColorStop(1, '#f1cba5'); // Underneath/Sides\n            ctx.fillStyle = grad; ctx.fillRect(0, 0, 512, 512);\n            \n            const imgData = ctx.getImageData(0,0,512,512);\n            for(let i=0; i<imgData.data.length; i+=4) {\n                let n = simplex.noise3D((i/4%512)*0.01, Math.floor(i/4/512)*0.01, 0) * 15;\n                imgData.data[i] += n; imgData.data[i+1] += n; imgData.data[i+2] += n;\n            }\n            ctx.putImageData(imgData, 0,0);\n            const tex = new THREE.CanvasTexture(canvas);\n            tex.colorSpace = THREE.SRGBColorSpace;\n            return tex;\n        }\n\n        function createBunBumpMap() {\n            const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512;\n            const ctx = canvas.getContext('2d');\n            const imgData = ctx.createImageData(512, 512);\n            for(let i=0; i<imgData.data.length; i+=4) {\n                let x = (i/4)%512; let y = Math.floor((i/4)/512);\n                let n1 = simplex.noise3D(x*0.05, y*0.05, 1) * 0.5 + 0.5;\n                let n2 = simplex.noise3D(x*0.2, y*0.2, 2) * 0.2 + 0.5;\n                let val = (n1*0.7 + n2*0.3) * 255;\n                imgData.data[i] = val; imgData.data[i+1] = val; imgData.data[i+2] = val; imgData.data[i+3]=255;\n            }\n            ctx.putImageData(imgData,0,0);\n            return new THREE.CanvasTexture(canvas);\n        }\n\n        function createMeatBumpMap() {\n            const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512;\n            const ctx = canvas.getContext('2d');\n            const imgData = ctx.createImageData(512, 512);\n            for(let i=0; i<imgData.data.length; i+=4) {\n                let n = Math.random() * 255;\n                imgData.data[i] = n; imgData.data[i+1] = n; imgData.data[i+2] = n; imgData.data[i+3]=255;\n            }\n            ctx.putImageData(imgData,0,0);\n            const tex = new THREE.CanvasTexture(canvas);\n            tex.wrapS = THREE.RepeatWrapping; tex.wrapT = THREE.RepeatWrapping;\n            return tex;\n        }\n\n        // Shared Materials\n        const texBunColor = createBunColorMap();\n        const texBunBump = createBunBumpMap();\n        const texMeatBump = createMeatBumpMap();\n\n        const matBun = new THREE.MeshPhysicalMaterial({\n            map: texBunColor,\n            bumpMap: texBunBump, bumpScale: 0.05,\n            roughness: 0.8, metalness: 0.0,\n            clearcoat: 0.1, clearcoatRoughness: 0.8 // slight baked sheen\n        });\n\n        const matBunInside = new THREE.MeshPhysicalMaterial({\n            color: 0xf5eed5, roughness: 0.9, map: texBunBump, bumpScale: 0.1\n        });\n\n        const matMeat = new THREE.MeshPhysicalMaterial({\n            color: 0x2e180d, roughness: 0.6, metalness: 0.1,\n            bumpMap: texMeatBump, bumpScale: 0.2,\n            clearcoat: 0.2, clearcoatRoughness: 0.4 // greasiness\n        });\n\n        const matCheese = new THREE.MeshPhysicalMaterial({\n            color: 0xffa200, roughness: 0.3, metalness: 0.05,\n            transmission: 0.1, thickness: 0.05, // subsurface scattering feel\n            side: THREE.DoubleSide\n        });\n\n        const matSauce = new THREE.MeshPhysicalMaterial({\n            color: 0xe89764, roughness: 0.2, metalness: 0.0,\n            clearcoat: 0.5, clearcoatRoughness: 0.2,\n            transmission: 0.2, thickness: 0.2\n        });\n\n        const matLettuce = new THREE.MeshPhysicalMaterial({\n            color: 0x7ab835, roughness: 0.4,\n            transmission: 0.4, thickness: 0.05,\n            side: THREE.DoubleSide\n        });\n\n        const matPickle = new THREE.MeshPhysicalMaterial({\n            color: 0x475e22, roughness: 0.3,\n            transmission: 0.6, thickness: 0.1, clearcoat: 0.5\n        });\n\n        const burgerGroup = new THREE.Group();\n\n        // --- Component Builders ---\n\n        // 1. Crown Bun (Top)\n        function buildCrown(yPos) {\n            const geo = new THREE.SphereGeometry(R_BURGER, 64, 32);\n            const pos = geo.attributes.position;\n            for (let i = 0; i < pos.count; i++) {\n                let y = pos.getY(i);\n                if (y < 0) { pos.setY(i, 0); } // flatten bottom\n                else { pos.setY(i, y * 0.7); } // squash top slightly\n                \n                // Add organic asymmetry\n                let x = pos.getX(i), z = pos.getZ(i);\n                let n = simplex.noise3D(x*0.5, y*0.5, z*0.5) * 0.05;\n                if(y>0) {\n                    pos.setX(i, x + x*n); pos.setZ(i, z + z*n); pos.setY(i, pos.getY(i) + n*0.5);\n                }\n            }\n            geo.computeVertexNormals();\n\n            // Needs multi-material: baked top, soft bread bottom\n            const materials = [matBun, matBunInside];\n            geo.clearGroups();\n            for(let i=0; i<pos.count; i+=3) { // rudimentary group assignment\n                // not perfect topology-wise but functional for procedural\n                geo.addGroup(i, 3, pos.getY(i) > 0.05 ? 0 : 1); \n            }\n\n            const mesh = new THREE.Mesh(geo, matBun);\n            mesh.position.y = yPos;\n            mesh.castShadow = true;\n            mesh.receiveShadow = true;\n            burgerGroup.add(mesh);\n\n            // Sesame Seeds\n            const seedGeo = new THREE.SphereGeometry(0.035, 8, 8);\n            seedGeo.scale(1, 0.4, 1.8);\n            const seedMat = new THREE.MeshPhysicalMaterial({ color: 0xf2dfa7, roughness: 0.6 });\n            const seedCount = 180;\n            const seedInstanced = new THREE.InstancedMesh(seedGeo, seedMat, seedCount);\n            \n            const matrix = new THREE.Matrix4();\n            const dummy = new THREE.Object3D();\n            \n            for(let i=0; i<seedCount; i++) {\n                // Random point on upper hemisphere\n                let phi = Math.acos(1 - (Math.random() * 0.9)); // heavily weight towards top/sides\n                let theta = Math.random() * Math.PI * 2;\n                \n                // Calculate position on the squashed sphere\n                let r = R_BURGER;\n                let sy = 0.7; // squash factor\n                let x = r * Math.sin(phi) * Math.cos(theta);\n                let z = r * Math.sin(phi) * Math.sin(theta);\n                let y = r * Math.cos(phi) * sy;\n\n                // Add base bun noise\n                let n = simplex.noise3D(x*0.5, y*0.5, z*0.5) * 0.05;\n                x += x*n; z += z*n; y += n*0.5;\n\n                dummy.position.set(x, y, z);\n                \n                // Normal approximation for squashed sphere\n                let normal = new THREE.Vector3(x/r, y/(r*sy*sy), z/r).normalize();\n                dummy.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0), normal);\n                \n                // Randomly rotate around its own local Y axis\n                dummy.rotateY(Math.random() * Math.PI);\n                \n                dummy.updateMatrix();\n                seedInstanced.setMatrixAt(i, dummy.matrix);\n            }\n            seedInstanced.position.y = yPos;\n            seedInstanced.castShadow = true;\n            burgerGroup.add(seedInstanced);\n        }\n\n        // 2 & 6. Beef Patty\n        function buildPatty(yPos) {\n            const geo = new THREE.CylinderGeometry(R_BURGER * 0.95, R_BURGER * 0.95, 0.25, 64, 8);\n            const pos = geo.attributes.position;\n            for (let i = 0; i < pos.count; i++) {\n                let x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);\n                // Displace vertices to make it look cooked and irregular\n                let n = simplex.noise3D(x * 2.0, y * 5.0, z * 2.0) * 0.08;\n                let n2 = simplex.noise3D(x * 0.5, y * 0.5, z * 0.5) * 0.1; // Macro shape\n                \n                // Keep center flatter so stacking works better\n                let dist = Math.hypot(x, z);\n                let edgeFactor = Math.min(dist / (R_BURGER*0.8), 1.0);\n                \n                pos.setX(i, x + (x)*n2);\n                pos.setZ(i, z + (z)*n2);\n                pos.setY(i, y + (n + n2*0.5) * edgeFactor);\n            }\n            geo.computeVertexNormals();\n            const mesh = new THREE.Mesh(geo, matMeat);\n            mesh.position.y = yPos;\n            mesh.castShadow = true; mesh.receiveShadow = true;\n            // Randomly rotate to not look identical\n            mesh.rotation.y = Math.random() * Math.PI; \n            burgerGroup.add(mesh);\n        }\n\n        // 4. Cheese\n        function buildCheese(yPos) {\n            const size = R_BURGER * 2.1;\n            const geo = new THREE.PlaneGeometry(size, size, 32, 32);\n            geo.rotateX(-Math.PI / 2);\n            const pos = geo.attributes.position;\n            \n            const dropDist = R_BURGER * 0.85; // Distance at which it starts drooping over meat\n            \n            for (let i = 0; i < pos.count; i++) {\n                let x = pos.getX(i), z = pos.getZ(i);\n                let dist = Math.hypot(x, z);\n                \n                // Add minor waviness\n                let y = simplex.noise3D(x*1.5, 0, z*1.5) * 0.03;\n\n                // Drape over edges\n                if (dist > dropDist) {\n                    let drape = Math.pow(dist - dropDist, 1.5) * 0.8;\n                    // Corners drape more\n                    let angle = Math.atan2(z, x);\n                    let cornerFactor = Math.abs(Math.cos(angle * 2.0)) * 0.5 + 0.5;\n                    y -= drape * cornerFactor;\n                }\n                pos.setY(i, y);\n            }\n            geo.computeVertexNormals();\n            const mesh = new THREE.Mesh(geo, matCheese);\n            // Slight offset to prevent z-fighting with patty\n            mesh.position.y = yPos + 0.13;\n            // Rotate randomly\n            mesh.rotation.y = Math.random() * 0.5;\n            mesh.castShadow = true; mesh.receiveShadow = true;\n            burgerGroup.add(mesh);\n        }\n\n        // 3 & 7. Lettuce\n        function buildLettuce(yPos) {\n            // Instantiate 30 small crinkly planes in a ring\n            const numLeaves = 35;\n            const geo = new THREE.PlaneGeometry(0.8, 0.8, 8, 8);\n            geo.rotateX(-Math.PI/2);\n            \n            // Displace single leaf geometry\n            const pos = geo.attributes.position;\n            for(let i=0; i<pos.count; i++) {\n                let x = pos.getX(i), z = pos.getZ(i);\n                let y = simplex.noise3D(x*3, z*3, 0) * 0.15;\n                pos.setY(i, y);\n            }\n            geo.computeVertexNormals();\n\n            const instanced = new THREE.InstancedMesh(geo, matLettuce, numLeaves);\n            const dummy = new THREE.Object3D();\n            \n            for (let i = 0; i < numLeaves; i++) {\n                let angle = (i / numLeaves) * Math.PI * 2 + (Math.random() * 0.2);\n                let radius = R_BURGER * 0.75 + (Math.random() * 0.2);\n                \n                let x = Math.cos(angle) * radius;\n                let z = Math.sin(angle) * radius;\n                let y = (Math.random() - 0.5) * 0.1;\n\n                dummy.position.set(x, y, z);\n                dummy.rotation.set(\n                    (Math.random()-0.5)*0.3, \n                    -angle + Math.PI/2 + (Math.random()*0.5), \n                    (Math.random()-0.5)*0.3\n                );\n                dummy.scale.set(1 + Math.random()*0.5, 1, 1 + Math.random()*0.5);\n                dummy.updateMatrix();\n                instanced.setMatrixAt(i, dummy.matrix);\n            }\n            instanced.position.y = yPos;\n            instanced.castShadow = true; instanced.receiveShadow = true;\n            burgerGroup.add(instanced);\n        }\n\n        // Sauce\n        function buildSauce(yPos) {\n            // Torus squished and distorted\n            const geo = new THREE.TorusGeometry(R_BURGER * 0.7, 0.2, 16, 64);\n            geo.rotateX(Math.PI/2);\n            const pos = geo.attributes.position;\n            for(let i=0; i<pos.count; i++) {\n                let x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);\n                let dist = Math.hypot(x, z);\n                // Squish vertically\n                y *= 0.3;\n                // Blobby edges\n                let blob = simplex.noise3D(x*1.5, 0, z*1.5) * 0.1;\n                x += (x/dist)*blob; z += (z/dist)*blob;\n                pos.setX(i, x); pos.setY(i, y); pos.setZ(i, z);\n            }\n            geo.computeVertexNormals();\n            const mesh = new THREE.Mesh(geo, matSauce);\n            mesh.position.y = yPos;\n            mesh.castShadow = true; mesh.receiveShadow = true;\n            burgerGroup.add(mesh);\n        }\n\n        // Pickles\n        function buildPickles(yPos) {\n            const geo = new THREE.CylinderGeometry(0.35, 0.35, 0.05, 32);\n            const pos = geo.attributes.position;\n            // Make edges slightly bumpy\n            for(let i=0; i<pos.count; i++) {\n                let x = pos.getX(i), z = pos.getZ(i);\n                let r = Math.hypot(x, z);\n                if(r > 0.3) { // only effect outer edge\n                    let angle = Math.atan2(z,x);\n                    let bump = Math.sin(angle * 20) * 0.01;\n                    pos.setX(i, x * (1+bump)); pos.setZ(i, z * (1+bump));\n                }\n            }\n            geo.computeVertexNormals();\n\n            // 3 pickles overlapping\n            const positions = [\n                {x: 0.5, z: 0.5}, {x: -0.6, z: 0.2}, {x: 0.2, z: -0.7}\n            ];\n\n            positions.forEach((p, index) => {\n                const mesh = new THREE.Mesh(geo, matPickle);\n                mesh.position.set(p.x, yPos + index*0.02, p.z);\n                mesh.rotation.y = Math.random() * Math.PI;\n                mesh.rotation.x = (Math.random() - 0.5) * 0.1;\n                mesh.rotation.z = (Math.random() - 0.5) * 0.1;\n                mesh.castShadow = true; mesh.receiveShadow = true;\n                burgerGroup.add(mesh);\n            });\n        }\n\n        // Club (Middle Bun) & Heel (Bottom Bun)\n        function buildMiddleBottomBun(yPos, isBottom = false) {\n            const geo = new THREE.CylinderGeometry(R_BURGER*0.98, R_BURGER, 0.3, 64, 8);\n            const pos = geo.attributes.position;\n            for (let i = 0; i < pos.count; i++) {\n                let x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);\n                let dist = Math.hypot(x,z);\n                \n                if (isBottom) {\n                    // Heel: flat top, rounded bottom rim\n                    if (y < 0 && dist > R_BURGER * 0.8) {\n                        y += (dist - R_BURGER * 0.8) * 0.8; \n                    }\n                } else {\n                    // Club: flat top and flat bottom, rounded edges\n                    if (dist > R_BURGER * 0.8) {\n                        let factor = (dist - R_BURGER * 0.8) * 0.5;\n                        if(y>0) y -= factor;\n                        if(y<0) y += factor;\n                    }\n                }\n                \n                // Add slight organic deformation\n                let n = simplex.noise3D(x*0.5, y*2.0, z*0.5) * 0.03;\n                pos.setX(i, x + x*n); pos.setZ(i, z + z*n); pos.setY(i, y + n);\n            }\n            geo.computeVertexNormals();\n            \n            // Material assignment (fake sides vs inside)\n            // Procedurally tricky without UV mapping, let's use the main baked material broadly\n            const mesh = new THREE.Mesh(geo, matBun);\n            mesh.position.y = yPos;\n            mesh.castShadow = true; mesh.receiveShadow = true;\n            burgerGroup.add(mesh);\n        }\n\n        // --- Assembly ---\n        // Stack heights (Y coordinates)\n        let stackY = 0;\n        buildMiddleBottomBun(stackY, true); // Heel\n        stackY += 0.2;\n        buildSauce(stackY);\n        stackY += 0.05;\n        buildLettuce(stackY);\n        stackY += 0.1;\n        buildCheese(stackY);\n        stackY += 0.15;\n        buildPatty(stackY);\n        stackY += 0.3;\n        buildMiddleBottomBun(stackY, false); // Club\n        stackY += 0.2;\n        buildSauce(stackY);\n        stackY += 0.05;\n        buildLettuce(stackY);\n        stackY += 0.15;\n        buildPickles(stackY);\n        stackY += 0.15;\n        buildPatty(stackY);\n        stackY += 0.35;\n        buildCrown(stackY);\n\n        // Center the whole group\n        const box = new THREE.Box3().setFromObject(burgerGroup);\n        const center = box.getCenter(new THREE.Vector3());\n        burgerGroup.position.y = -center.y;\n        scene.add(burgerGroup);\n\n        // Remove loading overlay\n        document.getElementById('loading').style.opacity = '0';\n\n        // --- Animation & Resize ---\n        window.addEventListener('resize', onWindowResize, false);\n        function onWindowResize() {\n            camera.aspect = window.innerWidth / window.innerHeight;\n            camera.updateProjectionMatrix();\n            renderer.setSize(window.innerWidth, window.innerHeight);\n        }\n\n        function animate() {\n            requestAnimationFrame(animate);\n            controls.update();\n            renderer.render(scene, camera);\n        }\n        animate();\n    </script>\n</body>\n</html>\n```"
}