{
  "streamed": true,
  "usage": {
    "prompt_tokens": 186,
    "completion_tokens": 35081,
    "total_tokens": 35267,
    "cost": 0.5262708,
    "is_byok": false,
    "prompt_tokens_details": {
      "cached_tokens": 186,
      "cache_write_tokens": 0,
      "audio_tokens": 0,
      "video_tokens": 0
    },
    "cost_details": {
      "upstream_inference_cost": 0.5262708,
      "upstream_inference_prompt_cost": 0.0000558,
      "upstream_inference_completions_cost": 0.526215
    },
    "completion_tokens_details": {
      "reasoning_tokens": 27491,
      "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\"/>\n<title>Big Mac — Three.js</title>\n<style>\n  html,body{margin:0;height:100%;overflow:hidden;background:#e6dfd2;}\n  canvas{display:block;}\n</style>\n<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"https://unpkg.com/three@0.170.0/build/three.module.js\",\n    \"three/addons/\": \"https://unpkg.com/three@0.170.0/examples/jsm/\"\n  }\n}\n</script>\n</head>\n<body>\n<script type=\"module\">\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';\n\n/* ============================================================\n   Renderer / Scene / Camera / Controls\n============================================================ */\nconst renderer = new THREE.WebGLRenderer({ antialias: true });\nrenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\nrenderer.setSize(window.innerWidth, window.innerHeight);\nrenderer.shadowMap.enabled = true;\nrenderer.shadowMap.type = THREE.PCFSoftShadowMap;\nrenderer.toneMapping = THREE.ACESFilmicToneMapping;\nrenderer.toneMappingExposure = 1.02;\ndocument.body.appendChild(renderer.domElement);\n\nconst scene = new THREE.Scene();\n\nconst camera = new THREE.PerspectiveCamera(34, window.innerWidth / window.innerHeight, 0.1, 300);\ncamera.position.set(12.5, 7.6, 15.2);\n\nconst controls = new OrbitControls(camera, renderer.domElement);\ncontrols.target.set(0, 4.1, 0);\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.06;\ncontrols.minDistance = 9;\ncontrols.maxDistance = 55;\ncontrols.minPolarAngle = 0.12;\ncontrols.maxPolarAngle = 1.45;\ncontrols.enablePan = false;\n\n/* Image-based lighting for realistic reflections */\nconst pmrem = new THREE.PMREMGenerator(renderer);\nscene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture;\n\n/* ============================================================\n   Procedural texture helpers (no external assets)\n============================================================ */\nfunction mulberry32(a){\n  return function(){\n    a |= 0; a = a + 0x6D2B79F5 | 0;\n    let t = Math.imul(a ^ a >>> 15, 1 | a);\n    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;\n    return ((t ^ t >>> 14) >>> 0) / 4294967296;\n  };\n}\nconst rng = mulberry32(20240514);\n\nfunction blot(ctx, size, rng, { n, rMin, rMax, aMin, aMax, colors }){\n  for (let i = 0; i < n; i++){\n    const x = rng() * size, y = rng() * size, r = rMin + rng() * (rMax - rMin);\n    const a = aMin + rng() * (aMax - aMin);\n    const col = colors[(rng() * colors.length) | 0].replace('A', a.toFixed(3));\n    const g = ctx.createRadialGradient(x, y, 0, x, y, r);\n    g.addColorStop(0, col);\n    g.addColorStop(1, col.replace(/[\\d.]+\\)$/, '0)'));\n    ctx.fillStyle = g;\n    ctx.beginPath(); ctx.arc(x, y, r, 0, 6.2832); ctx.fill();\n  }\n}\n\nfunction grain(ctx, size, amt, rng){\n  const img = ctx.getImageData(0, 0, size, size), d = img.data;\n  for (let i = 0; i < d.length; i += 4){\n    const n = (rng() * 2 - 1) * amt;\n    d[i] += n; d[i + 1] += n; d[i + 2] += n;\n  }\n  ctx.putImageData(img, 0, 0);\n}\n\nconst maxAniso = renderer.capabilities.getMaxAnisotropy();\nfunction makeTex(size, painter, { srgb = true, rx = 1, ry = 1 } = {}){\n  const c = document.createElement('canvas'); c.width = c.height = size;\n  const ctx = c.getContext('2d');\n  painter(ctx, size);\n  const t = new THREE.CanvasTexture(c);\n  t.wrapS = t.wrapT = THREE.RepeatWrapping;\n  t.repeat.set(rx, ry);\n  t.anisotropy = maxAniso;\n  if (srgb) t.colorSpace = THREE.SRGBColorSpace;\n  return t;\n}\n\n/* ---------- texture painters ---------- */\nfunction painterBun(stops, seed){\n  return (ctx, size) => {\n    const r = mulberry32(seed);\n    const g = ctx.createLinearGradient(0, size, 0, 0);\n    for (const [o, c] of stops) g.addColorStop(o, c);\n    ctx.fillStyle = g; ctx.fillRect(0, 0, size, size);\n    blot(ctx, size, r, { n: 1000, rMin: 1, rMax: 4, aMin: 0.04, aMax: 0.10, colors: ['rgba(140,85,30,A)', 'rgba(110,60,20,A)'] });\n    blot(ctx, size, r, { n: 260, rMin: 2, rMax: 7, aMin: 0.03, aMax: 0.08, colors: ['rgba(255,238,205,A)'] });\n    blot(ctx, size, r, { n: 60, rMin: 6, rMax: 22, aMin: 0.03, aMax: 0.07, colors: ['rgba(160,95,35,A)', 'rgba(255,220,160,A)'] });\n    grain(ctx, size, 6, r);\n  };\n}\nconst painterBunBump = (ctx, size) => {\n  const r = mulberry32(77);\n  ctx.fillStyle = '#808080'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 150, rMin: 4, rMax: 18, aMin: 0.10, aMax: 0.20, colors: ['rgba(50,50,50,A)'] });\n  blot(ctx, size, r, { n: 150, rMin: 3, rMax: 14, aMin: 0.10, aMax: 0.20, colors: ['rgba(200,200,200,A)'] });\n  grain(ctx, size, 22, r);\n};\nconst painterPatty = (ctx, size) => {\n  const r = mulberry32(31);\n  ctx.fillStyle = '#5b3a23'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 130, rMin: 12, rMax: 60, aMin: 0.12, aMax: 0.30, colors: ['rgba(38,20,9,A)', 'rgba(48,26,12,A)'] });\n  blot(ctx, size, r, { n: 550, rMin: 2, rMax: 8, aMin: 0.18, aMax: 0.45, colors: ['rgba(22,11,5,A)'] });\n  blot(ctx, size, r, { n: 420, rMin: 3, rMax: 14, aMin: 0.08, aMax: 0.20, colors: ['rgba(130,88,52,A)', 'rgba(105,66,36,A)'] });\n  blot(ctx, size, r, { n: 380, rMin: 1, rMax: 3.5, aMin: 0.12, aMax: 0.28, colors: ['rgba(178,146,104,A)'] });\n  grain(ctx, size, 13, r);\n};\nconst painterPattyBump = (ctx, size) => {\n  const r = mulberry32(32);\n  ctx.fillStyle = '#808080'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 320, rMin: 3, rMax: 20, aMin: 0.20, aMax: 0.40, colors: ['rgba(40,40,40,A)'] });\n  blot(ctx, size, r, { n: 320, rMin: 2, rMax: 12, aMin: 0.20, aMax: 0.40, colors: ['rgba(210,210,210,A)'] });\n  grain(ctx, size, 55, r);\n};\nconst painterLettuce = (ctx, size) => {\n  const r = mulberry32(51);\n  ctx.fillStyle = '#bed88c'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 160, rMin: 8, rMax: 45, aMin: 0.12, aMax: 0.25, colors: ['rgba(158,186,104,A)', 'rgba(140,170,90,A)'] });\n  blot(ctx, size, r, { n: 160, rMin: 6, rMax: 30, aMin: 0.12, aMax: 0.22, colors: ['rgba(220,238,180,A)'] });\n  ctx.save(); ctx.translate(size / 2, size / 2);\n  for (let i = 0; i < 90; i++){\n    const a = r() * Math.PI * 2, bend = (r() - 0.5) * 0.8;\n    ctx.strokeStyle = `rgba(238,246,210,${0.15 + r() * 0.25})`;\n    ctx.lineWidth = 1 + r() * 3;\n    ctx.beginPath();\n    ctx.moveTo(Math.cos(a) * size * 0.04, Math.sin(a) * size * 0.04);\n    ctx.quadraticCurveTo(Math.cos(a + bend) * size * 0.25, Math.sin(a + bend) * size * 0.25,\n                         Math.cos(a + bend * 0.3) * size * 0.5, Math.sin(a + bend * 0.3) * size * 0.5);\n    ctx.stroke();\n  }\n  ctx.restore();\n  grain(ctx, size, 7, r);\n};\nconst painterPickle = (ctx, size) => {\n  const r = mulberry32(61);\n  ctx.fillStyle = '#7d8c39'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 200, rMin: 4, rMax: 22, aMin: 0.15, aMax: 0.30, colors: ['rgba(92,106,36,A)', 'rgba(70,84,26,A)'] });\n  blot(ctx, size, r, { n: 320, rMin: 1.5, rMax: 6, aMin: 0.15, aMax: 0.30, colors: ['rgba(165,180,92,A)', 'rgba(140,155,70,A)'] });\n  grain(ctx, size, 12, r);\n};\nconst painterPickleBump = (ctx, size) => {\n  const r = mulberry32(62);\n  ctx.fillStyle = '#808080'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 260, rMin: 2, rMax: 7, aMin: 0.25, aMax: 0.45, colors: ['rgba(220,220,220,A)'] });\n  blot(ctx, size, r, { n: 200, rMin: 2, rMax: 6, aMin: 0.20, aMax: 0.40, colors: ['rgba(45,45,45,A)'] });\n  grain(ctx, size, 50, r);\n};\nconst painterFloor = (ctx, size) => {\n  const r = mulberry32(71);\n  const g = ctx.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);\n  g.addColorStop(0, '#ece5d5'); g.addColorStop(1, '#cfc5b1');\n  ctx.fillStyle = g; ctx.fillRect(0, 0, size, size);\n  grain(ctx, size, 5, r);\n};\nconst painterParchment = (ctx, size) => {\n  const r = mulberry32(81);\n  ctx.fillStyle = '#eee2c8'; ctx.fillRect(0, 0, size, size);\n  blot(ctx, size, r, { n: 70, rMin: 20, rMax: 90, aMin: 0.08, aMax: 0.16, colors: ['rgba(218,200,160,A)', 'rgba(250,244,228,A)'] });\n  grain(ctx, size, 6, r);\n};\nconst painterBG = (ctx, size) => {\n  const g = ctx.createRadialGradient(size*0.5, size*0.36, size*0.05, size*0.5, size*0.5, size*0.75);\n  g.addColorStop(0, '#f8f4ec'); g.addColorStop(1, '#d6cebd');\n  ctx.fillStyle = g; ctx.fillRect(0, 0, size, size);\n};\n\n/* ---------- background & fog ---------- */\nscene.background = makeTex(1024, painterBG);\nscene.fog = new THREE.Fog(0xd6cebd, 50, 115);\n\n/* ============================================================\n   Lights\n============================================================ */\nconst key = new THREE.DirectionalLight(0xfff0da, 2.4);\nkey.position.set(11, 15, 8);\nkey.castShadow = true;\nkey.shadow.mapSize.set(2048, 2048);\nkey.shadow.camera.near = 2;  key.shadow.camera.far = 45;\nkey.shadow.camera.left = -13; key.shadow.camera.right = 13;\nkey.shadow.camera.top = 14;   key.shadow.camera.bottom = -3;\nkey.shadow.bias = -0.0004;\nkey.shadow.normalBias = 0.03;\nscene.add(key);\n\nconst fill = new THREE.DirectionalLight(0xdde8ff, 0.7);\nfill.position.set(-10, 7, -6);\nscene.add(fill);\n\nconst rim = new THREE.DirectionalLight(0xffe6c4, 1.1);\nrim.position.set(-6, 10, -14);\nscene.add(rim);\n\nscene.add(new THREE.HemisphereLight(0xfff6e6, 0x8a7a66, 0.35));\n\n/* ============================================================\n   Materials\n============================================================ */\nconst bunBump = makeTex(512, painterBunBump, { srgb: false });\nfunction bunMaterial(stops, seed){\n  return new THREE.MeshStandardMaterial({\n    map: makeTex(512, painterBun(stops, seed), { rx: 3 }),\n    bumpMap: bunBump, bumpScale: 0.3,\n    roughness: 0.55, envMapIntensity: 0.5\n  });\n}\nconst crownStops = [[0,'#f3ddb2'],[0.36,'#f0d6a8'],[0.50,'#e7ab5f'],[1,'#d9984a']];\nconst clubStops  = [[0,'#f3ddb2'],[0.36,'#f0d6a8'],[0.46,'#e2a358'],[0.56,'#e2a358'],[0.64,'#f0d6a8'],[1,'#f3ddb2']];\nconst heelStops  = [[0,'#c98844'],[0.30,'#dda054'],[0.48,'#e7ab5f'],[0.56,'#f0d6a8'],[1,'#f3ddb2']];\n\nconst pattyMat = new THREE.MeshPhysicalMaterial({\n  map: makeTex(512, painterPatty, { rx: 3 }),\n  bumpMap: makeTex(512, painterPattyBump, { srgb: false, rx: 3 }), bumpScale: 0.6,\n  roughness: 0.58, clearcoat: 0.3, clearcoatRoughness: 0.5, envMapIntensity: 0.55\n});\nconst lettuceMat = new THREE.MeshPhysicalMaterial({\n  map: makeTex(512, painterLettuce),\n  side: THREE.DoubleSide, roughness: 0.5,\n  sheen: 0.5, sheenColor: new THREE.Color(0xe8f5c0), sheenRoughness: 0.6,\n  envMapIntensity: 0.5\n});\nconst cheeseMat = new THREE.MeshPhysicalMaterial({\n  color: 0xf5a22e, roughness: 0.36, clearcoat: 0.3, clearcoatRoughness: 0.35, envMapIntensity: 0.7\n});\nconst sauceMat = new THREE.MeshPhysicalMaterial({\n  color: 0xf2a578, roughness: 0.3, clearcoat: 0.6, clearcoatRoughness: 0.25, envMapIntensity: 0.8\n});\nconst pickleMat = new THREE.MeshStandardMaterial({\n  map: makeTex(256, painterPickle),\n  bumpMap: makeTex(256, painterPickleBump, { srgb: false }), bumpScale: 0.5,\n  roughness: 0.42, envMapIntensity: 0.7\n});\nconst onionMat = new THREE.MeshStandardMaterial({ color: 0xf6f1e2, roughness: 0.35, envMapIntensity: 0.6 });\nconst seedMat  = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.45, envMapIntensity: 0.5 });\n\n/* ============================================================\n   Geometry builders\n============================================================ */\nfunction latheFromCtrl(ctrl, samples = 160){\n  const curve = new THREE.SplineCurve(ctrl.map(p => new THREE.Vector2(p[0], p[1])));\n  return curve.getPoints(samples).map(p => new THREE.Vector2(Math.max(0, p.x), p.y));\n}\nfunction bunMesh(ctrl, mat){\n  const pts = latheFromCtrl(ctrl);\n  const mesh = new THREE.Mesh(new THREE.LatheGeometry(pts, 96), mat);\n  mesh.castShadow = mesh.receiveShadow = true;\n  return { mesh, pts };\n}\nfunction profileHeight(pts, r){\n  for (let i = pts.length - 1; i >= 1; i--){\n    const a = pts[i], b = pts[i - 1];\n    if (b.x > a.x && r >= a.x && r <= b.x){\n      const t = (r - a.x) / (b.x - a.x);\n      return a.y + (b.y - a.y) * t;\n    }\n  }\n  return pts[pts.length - 1].y;\n}\n\nconst crownCtrl = [[0,0],[3.0,0],[4.4,0.02],[4.85,0.18],[5.02,0.5],[4.9,1.1],[4.5,1.8],[3.8,2.45],[2.8,2.9],[1.6,3.14],[0.5,3.24],[0,3.26]];\nconst clubCtrl  = [[0,0],[3.2,0],[4.5,0.03],[4.9,0.2],[5.06,0.52],[4.9,0.85],[4.5,1.0],[3.2,1.04],[0,1.04]];\nconst heelCtrl  = [[0,0],[3.2,0],[4.5,0.03],[4.9,0.2],[5.02,0.5],[4.85,0.82],[4.3,1.0],[3.0,1.05],[1.0,1.07],[0,1.07]];\n\nfunction makePatty(seed){\n  const geo = new THREE.CylinderGeometry(5.02, 5.08, 1.1, 120, 5);\n  const r = mulberry32(seed);\n  const p1 = r()*7, p2 = r()*7, p3 = r()*7, p4 = r()*7;\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    const th = Math.atan2(z, x);\n    const s = 1 + 0.030*Math.sin(3*th+p1) + 0.020*Math.sin(8*th+p2) + 0.011*Math.sin(17*th+p3);\n    x *= s; z *= s;\n    y += 0.045*Math.sin(5*th+p4) + 0.03*Math.sin(11*th+p2) * (Math.abs(y) > 0.4 ? 1 : 0.3);\n    pos.setXYZ(i, x, y, z);\n  }\n  geo.computeVertexNormals();\n  const m = new THREE.Mesh(geo, pattyMat);\n  m.castShadow = m.receiveShadow = true;\n  return m;\n}\n\nfunction makeLettuce(seed){\n  const r = mulberry32(seed);\n  const inner = 1.0, outer = 5.8;\n  const geo = new THREE.RingGeometry(inner, outer, 160, 10);\n  const pos = geo.attributes.position;\n  const p1 = r()*7, p2 = r()*7, p3 = r()*7;\n  for (let i = 0; i < pos.count; i++){\n    const x = pos.getX(i), y = pos.getY(i);\n    const rad = Math.hypot(x, y), th = Math.atan2(y, x);\n    const nr = rad * (1 + 0.05 * Math.sin(6*th + p3));\n    const f = Math.pow(Math.max(0, (rad - inner) / (outer - inner)), 1.7);\n    const z = f * (0.5 * Math.sin(7*th + p1) + 0.2 * Math.sin(13*th + p2)) + 0.06 * Math.sin(rad * 2.5 + p2);\n    pos.setXYZ(i, Math.cos(th) * nr, Math.sin(th) * nr, z);\n  }\n  geo.computeVertexNormals();\n  geo.rotateX(-Math.PI / 2);\n  const m = new THREE.Mesh(geo, lettuceMat);\n  m.castShadow = m.receiveShadow = true;\n  return m;\n}\n\nfunction makeCheese(){\n  const w = 8.8, h = w / 2, r = 0.7;\n  const s = new THREE.Shape();\n  s.moveTo(-h + r, -h);\n  s.lineTo(h - r, -h);  s.quadraticCurveTo(h, -h, h, -h + r);\n  s.lineTo(h, h - r);   s.quadraticCurveTo(h, h, h - r, h);\n  s.lineTo(-h + r, h);  s.quadraticCurveTo(-h, h, -h, h - r);\n  s.lineTo(-h, -h + r); s.quadraticCurveTo(-h, -h, -h + r, -h);\n  const geo = new THREE.ExtrudeGeometry(s, { depth: 0.14, bevelEnabled: true, bevelThickness: 0.05, bevelSize: 0.05, bevelSegments: 2, curveSegments: 10 });\n  geo.rotateX(-Math.PI / 2);\n  const pos = geo.attributes.position;\n  for (let i = 0; i < pos.count; i++){\n    const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);\n    const d = Math.hypot(x, z);\n    const over = Math.max(0, d - 4.0);\n    pos.setY(i, y - 0.22 * over - 0.07 * over * over + 0.03 * Math.sin(x * 1.6) * Math.sin(z * 1.8));\n  }\n  geo.computeVertexNormals();\n  const m = new THREE.Mesh(geo, cheeseMat);\n  m.rotation.y = Math.PI / 4 + 0.12;\n  m.castShadow = m.receiveShadow = true;\n  return m;\n}\n\nconst sauceTorusGeo = new THREE.TorusGeometry(4.3, 0.45, 14, 90);\nsauceTorusGeo.rotateX(-Math.PI / 2);\nconst dripGeo = new THREE.SphereGeometry(0.27, 10, 8);\nfunction makeSauce(){\n  const g = new THREE.Group();\n  const tor = new THREE.Mesh(sauceTorusGeo, sauceMat);\n  tor.scale.y = 0.55;\n  tor.castShadow = tor.receiveShadow = true;\n  g.add(tor);\n  for (let k = 0; k < 7; k++){\n    const a = rng() * Math.PI * 2;\n    const d = new THREE.Mesh(dripGeo, sauceMat);\n    const rr = 4.55 + rng() * 0.35;\n    d.position.set(Math.cos(a) * rr, -0.12 - rng() * 0.15, Math.sin(a) * rr);\n    d.scale.set(1, 1.1 + rng() * 0.8, 1);\n    d.castShadow = true;\n    g.add(d);\n  }\n  return g;\n}\n\nconst onionGeo = new THREE.BoxGeometry(0.36, 0.13, 0.3);\nfunction makeOnions(n){\n  const g = new THREE.Group();\n  for (let i = 0; i < n; i++){\n    const m = new THREE.Mesh(onionGeo, onionMat);\n    const a = rng() * Math.PI * 2, rr = 1.8 + rng() * 2.4;\n    m.position.set(Math.cos(a) * rr, (rng() - 0.5) * 0.08, Math.sin(a) * rr);\n    m.rotation.set(rng() * 0.6, rng() * Math.PI * 2, rng() * 0.6);\n    m.castShadow = true;\n    g.add(m);\n  }\n  return g;\n}\n\nconst pickleGeo = new THREE.CylinderGeometry(0.82, 0.82, 0.34, 26);\nfunction makePickles(){\n  const g = new THREE.Group();\n  const spots = [[4.15, 0.5], [4.3, 2.6], [4.0, 4.5]];\n  for (const [r, a] of spots){\n    const m = new THREE.Mesh(pickleGeo, pickleMat);\n    m.position.set(Math.cos(a) * r, 0, Math.sin(a) * r);\n    m.rotation.y = rng() * Math.PI;\n    m.castShadow = m.receiveShadow = true;\n    g.add(m);\n  }\n  return g;\n}\n\nconst seedGeo = new THREE.SphereGeometry(1, 10, 8);\nfunction makeSeeds(pts){\n  const N = 130;\n  const mesh = new THREE.InstancedMesh(seedGeo, seedMat, N);\n  const d = new THREE.Object3D(), up = new THREE.Vector3(0, 1, 0), c = new THREE.Color();\n  const H = r => profileHeight(pts, r);\n  for (let i = 0; i < N; i++){\n    const th = rng() * Math.PI * 2;\n    const rr = Math.sqrt(rng()) * 4.35;\n    const x = Math.cos(th) * rr, z = Math.sin(th) * rr;\n    const e = 0.08, dh = (H(rr + e) - H(Math.max(0, rr - e))) / (2 * e);\n    const inv = rr > 1e-3 ? 1 / rr : 0;\n    const n = new THREE.Vector3(-dh * x * inv, 1, -dh * z * inv).normalize();\n    d.position.set(x, H(rr) - 0.02, z);\n    d.quaternion.setFromUnitVectors(up, n);\n    d.rotateY(rng() * Math.PI * 2);\n    const s = 0.8 + rng() * 0.5;\n    d.scale.set(0.15 * s, 0.085 * s, 0.23 * s);\n    d.updateMatrix();\n    mesh.setMatrixAt(i, d.matrix);\n    c.setHSL(0.115, 0.42, 0.74 + rng() * 0.12);\n    mesh.setColorAt(i, c);\n  }\n  mesh.instanceMatrix.needsUpdate = true;\n  if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;\n  mesh.castShadow = true;\n  return mesh;\n}\n\n/* ============================================================\n   Assemble the Big Mac\n============================================================ */\nconst burger = new THREE.Group();\nscene.add(burger);\n\nconst heel = bunMesh(heelCtrl, bunMaterial(heelStops, 11));\nburger.add(heel.mesh);\n\nconst sauce1 = makeSauce(); sauce1.position.y = 1.04; burger.add(sauce1);\nconst onions1 = makeOnions(9); onions1.position.y = 1.16; burger.add(onions1);\nconst pickles = makePickles(); pickles.position.y = 1.30; burger.add(pickles);\nconst let1a = makeLettuce(21); let1a.position.y = 1.30; burger.add(let1a);\nconst let1b = makeLettuce(22); let1b.position.y = 1.44; let1b.rotation.y = 1.1; burger.add(let1b);\n\nconst patty1 = makePatty(31); patty1.position.y = 2.0; patty1.rotation.set(0.01, 0.7, 0.012); burger.add(patty1);\n\nconst club = bunMesh(clubCtrl, bunMaterial(clubStops, 12));\nclub.mesh.position.y = 2.52; burger.add(club.mesh);\n\nconst sauce2 = makeSauce(); sauce2.position.y = 3.58; burger.add(sauce2);\nconst onions2 = makeOnions(9); onions2.position.y = 3.70; burger.add(onions2);\nconst let2a = makeLettuce(23); let2a.position.y = 3.82; burger.add(let2a);\nconst let2b = makeLettuce(24); let2b.position.y = 3.94; let2b.rotation.y = 2.0; burger.add(let2b);\n\nconst cheese = makeCheese(); cheese.position.y = 3.98; burger.add(cheese);\n\nconst patty2 = makePatty(32); patty2.position.y = 4.62; patty2.rotation.set(-0.012, 2.3, 0.008); burger.add(patty2);\n\nconst crown = bunMesh(crownCtrl, bunMaterial(crownStops, 13));\ncrown.mesh.position.y = 5.10; burger.add(crown.mesh);\ncrown.mesh.add(makeSeeds(crown.pts));\n\n/* ---------- parchment paper ---------- */\n{\n  const pg = new THREE.PlaneGeometry(16, 16, 28, 28);\n  const ppos = pg.attributes.position;\n  for (let i = 0; i < ppos.count; i++){\n    const x = ppos.getX(i), y = ppos.getY(i);\n    let z = 0.1 * Math.sin(x * 0.9 + 1) + 0.1 * Math.sin(y * 1.1 + 2) + (rng() - 0.5) * 0.05;\n    const edge = Math.max(Math.abs(x), Math.abs(y));\n    if (edge > 5.5) z += (edge - 5.5) * 0.22;\n    ppos.setZ(i, z);\n  }\n  pg.computeVertexNormals();\n  pg.rotateX(-Math.PI / 2);\n  const paper = new THREE.Mesh(pg, new THREE.MeshStandardMaterial({\n    map: makeTex(512, painterParchment), roughness: 0.9, envMapIntensity: 0.35\n  }));\n  paper.position.y = 0.02;\n  paper.receiveShadow = true;\n  burger.add(paper);\n}\n\n/* ---------- floor ---------- */\n{\n  const floor = new THREE.Mesh(\n    new THREE.CircleGeometry(90, 64),\n    new THREE.MeshStandardMaterial({ map: makeTex(512, painterFloor), roughness: 0.95, envMapIntensity: 0.3 })\n  );\n  floor.rotation.x = -Math.PI / 2;\n  floor.receiveShadow = true;\n  scene.add(floor);\n}\n\n/* ============================================================\n   Resize / Animate\n============================================================ */\nwindow.addEventListener('resize', () => {\n  camera.aspect = window.innerWidth / window.innerHeight;\n  camera.updateProjectionMatrix();\n  renderer.setSize(window.innerWidth, window.innerHeight);\n});\n\nconst clock = new THREE.Clock();\nrenderer.setAnimationLoop(() => {\n  const dt = clock.getDelta();\n  burger.rotation.y += dt * 0.28;   // slow self-rotation\n  controls.update();\n  renderer.render(scene, camera);\n});\n</script>\n</body>\n</html>\n```"
}