<!-- HONEYCOMB-TEST · part 7 -->
ing direction
    if (len > 0.1) {
      const sin = Math.sin(camYaw);
      const cos = Math.cos(camYaw);
      // Transform input from camera-space to world-space
      // Input (mx,mz): mx=right, mz=forward in camera space
      // World: forward = (-sin, -cos), right = (cos, -sin)
      const wx = mx * cos + mz * sin;
      const wz = -mx * sin + mz * cos;
      mx = wx; mz = wz;
    }
    
    // AI Drive takes over movement (human input overrides)
    const aiMove = aiUpdate(0.016);
    if (aiMove && !joystick.active && mx === 0 && mz === 0) {
      mx = aiMove.mx; mz = aiMove.mz;
    }
    const speed = sprinting ? 57.6 : 28.8;
    tryMove(mx * speed * 0.016, mz * speed * 0.016);
    updateAvatar(0.016);
    
    // Camera: independent control via camstick (right joystick)
    // camstick.dx = yaw, camstick.dy = pitch
    if (camstick.active) {
      camYaw -= camstick.dx * 0.06;
      camPitch = Math.max(-0.5, Math.min(1.2, camPitch + camstick.dy * 0.04));
    }
    // Camera orbits player at distance, controlled by yaw/pitch
    const camDist = 20;
    const camHeight = 10;
    const cx = player.position.x + Math.sin(camYaw) * camDist * Math.cos(camPitch);
    const cz = player.position.z + Math.cos(camYaw) * camDist * Math.cos(camPitch);
    const cy = player.position.y + camHeight + Math.sin(camPitch) * camDist * 0.5;
    const camTarget = new THREE.Vector3(cx, cy, cz);
    camera.position.lerp(camTarget, 0.12);
    camera.lookAt(player.position.x, player.position.y + 2, player.position.z);
    
    // Render: the moment of truth
    renderer.render(scene, camera);

    // Performance tracking
    const frameEnd = performance.now();
    frameTimes.push(frameEnd - frameStart);
    if (frameTimes.length > 100) frameTimes.shift();
    frameCount++;
    const now = Date.now();
    if (now - lastFpsTime >= 1000) {
      currentFps = frameCount;
      frameCount = 0;
      lastFpsTime = now;
    }
  }

  function getPerformanceStats() {
    const avg = frameTimes.length ? frameTimes.reduce((a,b) => a+b, 0) / frameTimes.length : 0;
    const max = frameTimes.length ? Math.max(...frameTimes) : 0;
    return {
      fps: currentFps,
      avgFrameMs: avg.toFixed(2),
      maxFrameMs: max.toFixed(2),
      samples: frameTimes.length
    };
  }

  function onResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }

  // ═══════════════════════════════════════════════════════════════
  // HONEYCOMB CASTLE — Hexagonal rooms for epistles.xyz functions
  // King's Law: "Don't make a choice in a room with no floor"
  // Every hex has GUARANTEED solid floor. No exceptions.
  // ═══════════════════════════════════════════════════════════════

  // Hex grid: flat-top axial coordinates
  const HEX_SIZE = 15; // radius of hexagon

  function hexToPixel(q, r) {
    const x = HEX_SIZE * Math.sqrt(3) * (q + r / 2);
    const z = HEX_SIZE * 1.5 * r;
    return { x, z };
  }

  function createHexFloor(cx, cz, size, material) {
    // Hexagonal floor — GUARANTEED solid, per King's code
    // TRUE honeycomb: solid hexagonal shape, clearly visible
    const shape = new THREE.Shape();
    for (let i = 0; i < 6; i++) {
      const angle = (Math.PI / 180) * (60 * i - 30); // Flat-top orientation
      const x = Math.cos(angle) * size;
      const y = Math.sin(angle) * size;
      if (i === 0) shape.moveTo(x, y);
      else shape.lineTo(x, y);
    }
    shape.closePath();
    const geo = new THREE.ShapeGeometry(shape);
    geo.rotateX(-Math.PI / 2);
    const floor = new THREE.Mesh(geo, material);
    floor.position.set(cx, 0.5, cz); // Raised to avoid z-fighting
    floor.receiveShadow = true;
    return floor;
  }

  function createHexRoom_ORIGINAL(q, r, label, functionName, layer = 0) {
    // Creates a hexagonal room with GUARANTEED floor
    // King's Law: No room without a floor. No choice without ground.
    // Layer: vertical stacking (0 = ground, 1 = above, etc.)
    const { x: cx, z: cz } = hexToPixel(q, r);
    const layerHeight = 20; // vertical spacing between layers
    const cy = layer * layerHeight;
    const group = new THREE.Group();

    // FLOOR — always solid, always present
    // TRUE honeycomb: bright, solid, unmistakable hexagon
    const floorMat = new THREE.MeshStandardMaterial({
      color: 0x440000,
      emissive: 0x881111,
      emissiveIntensity: 0.6,
      roughness: 0.5,
      metalness: 0.4
    });
    const floor = createHexFloor(cx, cz, HEX_SIZE, floorMat);
    floor.position.y = cy + 0.1; // Layer height
    group.add(floor);

    // Glowing hex edge (red neon border)
    const edgeGeo = new THREE.BufferGeometry();
    const edgePts = [];
    for (let i = 0; i <= 6; i++) {
      const angle = (Math.PI / 180) * (60 * (i % 6));
      edgePts.push(new THREE.Vector3(
        cx + Math.cos(angle) * HEX_SIZE,
        0.2,
        cz + Math.sin(a