Respond with the full updated file rather than snippets. 

Issues with script:

-When you rotate the spider with A and D, the legs turn and can go upside down at a certain rotation.
-The spider dosen't properly climb.

-Give me a more featured enviorment like a wall to test climbing, different kinds of terrain, a small rocky mountain, all sorts of fun stuff to test.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Procedural Spider Animation</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <style>
        body { margin: 0; overflow: hidden; background-color: #111; color: white; font-family: monospace; }
        #info {
            position: absolute;
            top: 10px;
            width: 100%;
            text-align: center;
            z-index: 100;
            display:block;
            padding: 10px;
            background-color: rgba(0,0,0,0.5);
            border-radius: 10px;
            max-width: 400px;
            left: 50%;
            transform: translateX(-50%);
        }
    </style>
</head>
<body>
    <div id="info">
        W, A, S, D to move the spider.<br/>
        Mouse to orbit the camera.
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>

    <script>
        // --- SCENE SETUP ---
        let scene, camera, renderer, controls;
        let ground, spider;
        const obstacles = [];

        // --- SPIDER CONSTANTS ---
        const SPIDER_BODY_LENGTH = 2.4;      // longer oval
        const SPIDER_BODY_RADIUS = 1.0;      // smaller height
        const SPIDER_HEAD_RADIUS = 0.7;
        const SPIDER_LEG_SEGMENTS = 3;
        const SPIDER_LEG_LENGTHS = [1.2, 1.8, 2.0]; // slightly shorter foot
        const SPIDER_LEG_TOTAL_LENGTH = SPIDER_LEG_LENGTHS.reduce((a, b) => a + b, 0);
        const SPIDER_SPEED = 5.0;
        const SPIDER_ROTATION_SPEED = 2.0;

        // --- ANIMATION GLOBALS ---
        const clock = new THREE.Clock();
        const keyboard = {};
        const raycaster = new THREE.Raycaster();
        const legMovementOrder = [0, 4, 1, 5, 2, 6, 3, 7];
        let legMovementIndex = 0;
        let timeSinceLastStep = 0;
        const stepDuration = 0.15;

        init();
        animate();

        function init() {
            // Scene
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x1d295e);
            scene.fog = new THREE.Fog(0x1d295e, 50, 150);

            // Camera
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 15, 20);

            // Renderer
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            document.body.appendChild(renderer.domElement);

            // Controls
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.target.set(0, 5, 0);
            controls.update();

            // Lighting
            const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
            scene.add(ambientLight);

            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
            directionalLight.position.set(20, 30, 20);
            directionalLight.castShadow = true;
            directionalLight.shadow.mapSize.width = 2048;
            directionalLight.shadow.mapSize.height = 2048;
            scene.add(directionalLight);

            // Ground
            createGround();

            // Obstacles
            createObstacles();

            // Spider
            spider = createSpider();
            scene.add(spider);

            // Event Listeners
            window.addEventListener('resize', onWindowResize, false);
            window.addEventListener('keydown', (event) => (keyboard[event.key.toLowerCase()] = true));
            window.addEventListener('keyup', (event) => (keyboard[event.key.toLowerCase()] = false));
        }

        function createGround() {
            const geometry = new THREE.PlaneGeometry(200, 200, 100, 100);
            const material = new THREE.MeshPhongMaterial({ color: 0x556B2F, shininess: 10 });
            ground = new THREE.Mesh(geometry, material);
            ground.rotation.x = -Math.PI / 2;
            ground.receiveShadow = true;

            const vertices = geometry.attributes.position.array;
            for (let i = 0; i < vertices.length; i += 3) {
                const x = vertices[i];
                const y = vertices[i + 1];
                vertices[i + 2] = (Math.sin(x / 10) * Math.cos(y / 10)) * 4.0;
            }
            geometry.computeVertexNormals();
            scene.add(ground);
            obstacles.push(ground);
        }

        function createObstacles() {
            const boxGeometry = new THREE.BoxGeometry(5, 5, 5);
            const sphereGeometry = new THREE.SphereGeometry(3, 16, 16);
            const material = new THREE.MeshPhongMaterial({ color: 0x8B4513 });

            const box1 = new THREE.Mesh(boxGeometry, material);
            box1.position.set(10, 2.5, 10);
            box1.castShadow = true;
            box1.receiveShadow = true;
            scene.add(box1);
            obstacles.push(box1);

            const sphere1 = new THREE.Mesh(sphereGeometry, material);
            sphere1.position.set(-15, 3, -10);
            sphere1.castShadow = true;
            sphere1.receiveShadow = true;
            scene.add(sphere1);
            obstacles.push(sphere1);
        }

        function createSpider() {
            const spiderGroup = new THREE.Group();
            
            // Abdomen (long oval)
            const bodyMat = new THREE.MeshPhongMaterial({ color: 0x222222, shininess: 80 });
            const bodyGeom = new THREE.SphereGeometry(SPIDER_BODY_RADIUS, 32, 16);
            bodyGeom.scale(1, 0.6, 1.4); // flatter & longer
            const abdomen = new THREE.Mesh(bodyGeom, bodyMat);
            abdomen.castShadow = true;
            abdomen.position.z = -0.5;
            spiderGroup.add(abdomen);
            spiderGroup.abdomen = abdomen;

            // Head
            const headGeom = new THREE.SphereGeometry(SPIDER_HEAD_RADIUS, 24, 16);
            const head = new THREE.Mesh(headGeom, bodyMat);
            head.castShadow = true;
            head.position.z = 1.6;
            spiderGroup.add(head);

            // Legs
            spiderGroup.legs = [];
            for (let i = 0; i < 8; i++) {
                const leg = createLeg();
                const angle = (i / 8) * Math.PI * 2 + (Math.PI / 8) * (i % 2);
                leg.rotation.y = angle;
                spiderGroup.add(leg);
                spiderGroup.legs.push(leg);

                leg.userData.targetPosition = new THREE.Vector3();
                leg.userData.currentPosition = new THREE.Vector3();
                leg.userData.isMoving = false;
                leg.userData.stepStartTime = 0;
            }
            
            spiderGroup.position.y = 10;
            return spiderGroup;
        }

        function createLeg() {
            const legGroup = new THREE.Group();
            const legMaterial = new THREE.MeshPhongMaterial({ color: 0x111111 });

            let parent = legGroup;
            for (let i = 0; i < SPIDER_LEG_SEGMENTS; i++) {
                const segmentLength = SPIDER_LEG_LENGTHS[i];
                const segmentGeometry = new THREE.CylinderGeometry(0.12, 0.08, segmentLength, 8);
                const segment = new THREE.Mesh(segmentGeometry, legMaterial);
                segment.castShadow = true;
                
                const joint = new THREE.Group();
                joint.add(segment);
                segment.position.y = -segmentLength / 2;
                
                if (i === 0) {
                    joint.position.set(SPIDER_BODY_RADIUS + 0.1, 0, 0);
                } else {
                    joint.position.y = -SPIDER_LEG_LENGTHS[i-1];
                }
                parent.add(joint);
                parent = joint;
            }
            return legGroup;
        }
        
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function animate() {
            requestAnimationFrame(animate);
            const delta = clock.getDelta();
            updateSpiderMovement(delta);
            updateLegs(delta);
            controls.update();
            renderer.render(scene, camera);
        }

        function updateSpiderMovement(delta) {
            const moveDirection = new THREE.Vector3();
            if (keyboard['w']) moveDirection.z -= 1;
            if (keyboard['s']) moveDirection.z += 1;
            if (keyboard['a']) spider.rotation.y += SPIDER_ROTATION_SPEED * delta;
            if (keyboard['d']) spider.rotation.y -= SPIDER_ROTATION_SPEED * delta;

            if (moveDirection.lengthSq() > 0) {
                moveDirection.normalize().applyQuaternion(spider.quaternion);
                spider.position.add(moveDirection.multiplyScalar(SPIDER_SPEED * delta));
            }
            
            // Height & tilt
            let avgLegHeight = 0;
            let groundedLegs = 0;
            spider.legs.forEach(leg => {
                if (!leg.userData.isMoving) {
                    avgLegHeight += leg.userData.currentPosition.y;
                    groundedLegs++;
                }
            });
            if (groundedLegs > 0) {
                avgLegHeight /= groundedLegs;
                const targetY = avgLegHeight + SPIDER_BODY_RADIUS * 0.9;
                spider.position.y += (targetY - spider.position.y) * 0.1;
            }

            const isMoving = (keyboard['w'] || keyboard['s'] || keyboard['a'] || keyboard['d']);
            const targetTilt = isMoving ? -0.25 : 0;
            spider.abdomen.rotation.x += (targetTilt - spider.abdomen.rotation.x) * 0.1;
        }

        function updateLegs(delta) {
            timeSinceLastStep += delta;

            spider.legs.forEach((leg, i) => {
                if (!leg.userData.isMoving) {
                    leg.userData.targetPosition.copy(getLegRestingPosition(i));
                }
            });

            const isMoving = (keyboard['w'] || keyboard['s'] || keyboard['a'] || keyboard['d']);
            if (isMoving && timeSinceLastStep > stepDuration) {
                const legIndex = legMovementOrder[legMovementIndex % 8];
                const leg = spider.legs[legIndex];
                const currentTipPos = getLegTipWorldPosition(leg);
                const newTargetPos = getLegRestingPosition(legIndex);
                if (currentTipPos.distanceTo(newTargetPos) > 2.2) {
                    leg.userData.isMoving = true;
                    leg.userData.stepStartTime = clock.getElapsedTime();
                    leg.userData.stepStartPos = currentTipPos.clone();
                    leg.userData.stepEndPos = newTargetPos;
                    legMovementIndex++;
                    timeSinceLastStep = 0;
                }
            }

            spider.legs.forEach(leg => {
                if (leg.userData.isMoving) {
                    const elapsedTime = clock.getElapsedTime() - leg.userData.stepStartTime;
                    let t = elapsedTime / stepDuration;
                    if (t >= 1) { t = 1; leg.userData.isMoving = false; }
                    const stepHeight = 2.0;
                    const yOffset = Math.sin(t * Math.PI) * stepHeight;
                    const newPos = new THREE.Vector3().lerpVectors(leg.userData.stepStartPos, leg.userData.stepEndPos, t);
                    newPos.y += yOffset;
                    leg.userData.currentPosition.copy(newPos);
                } else {
                    leg.userData.currentPosition.lerp(leg.userData.targetPosition, 0.1);
                }
                solveIK(leg, leg.userData.currentPosition);
            });
        }

        function getLegRestingPosition(legIndex) {
            const angle = (legIndex / 8) * Math.PI * 2 + (Math.PI / 8) * (legIndex % 2);
            const distance = SPIDER_LEG_TOTAL_LENGTH * 0.95;
            const offset = new THREE.Vector3(
                Math.cos(angle) * distance,
                0,
                -Math.sin(angle) * distance
            );
            offset.applyQuaternion(spider.quaternion);

            const origin = spider.position.clone().add(offset);
            origin.y += 10; // cast from above terrain
            raycaster.set(origin, new THREE.Vector3(0, -1, 0));
            const intersects = raycaster.intersectObjects(obstacles, true);

            if (intersects.length > 0) {
                // raise target so the *bottom* of the foot touches the ground
                const footLength = SPIDER_LEG_LENGTHS[SPIDER_LEG_SEGMENTS - 1];
                return intersects[0].point.clone().add(new THREE.Vector3(0, footLength, 0));
            }

            origin.y = spider.position.y;
            return origin;
        }

        function getLegTipWorldPosition(leg) {
            let joint = leg.children[0];
            for (let i = 0; i < SPIDER_LEG_SEGMENTS - 1; i++) {
                if (!joint || !joint.children[1]) return leg.getWorldPosition(new THREE.Vector3());
                joint = joint.children[1];
            }
            const tipSegment = joint.children[0];
            const tipPosition = new THREE.Vector3(0, -SPIDER_LEG_LENGTHS[SPIDER_LEG_SEGMENTS - 1] / 2, 0);
            return tipSegment.localToWorld(tipPosition);
        }
                
        function solveIK(leg, targetPosition) {
            const joints = [];
            let current = leg.children[0];
            for (let i = 0; i < SPIDER_LEG_SEGMENTS; i++) {
                if (!current) break;
                joints.push(current);
                current = current.children.length > 1 ? current.children[1] : undefined;
            }
            if (joints.length < SPIDER_LEG_SEGMENTS) return;

            const baseJoint = joints[0];
            const midJoint = joints[1];
            const tipJoint = joints[2];

            // Aim leg yaw
            const baseToTarget = targetPosition.clone().sub(leg.getWorldPosition(new THREE.Vector3()));
            const legRotationAxis = new THREE.Vector3(0, 1, 0).applyQuaternion(leg.quaternion);
            const targetOnPlane = baseToTarget.clone().projectOnPlane(legRotationAxis);
            const baseForward = new THREE.Vector3(0, 0, 1).applyQuaternion(leg.quaternion);
            const angle = baseForward.angleTo(targetOnPlane);
            const cross = new THREE.Vector3().crossVectors(baseForward, targetOnPlane);
            const sign = Math.sign(cross.dot(legRotationAxis));
            baseJoint.rotation.y = angle * sign;

            // 2-bone IK
            const l1 = SPIDER_LEG_LENGTHS[1];
            const l2 = SPIDER_LEG_LENGTHS[2];
            const basePos = baseJoint.getWorldPosition(new THREE.Vector3());
            const targetDist = basePos.distanceTo(targetPosition);
            const clampedDist = Math.min(targetDist, l1 + l2 - 0.01);

            const angle1 = Math.acos((clampedDist**2 + l1**2 - l2**2) / (2 * clampedDist * l1));
            const angle2 = Math.acos((l1**2 + l2**2 - clampedDist**2) / (2 * l1 * l2));

            const baseToTargetLocal = baseJoint.worldToLocal(targetPosition.clone());
            const angle0 = Math.atan2(baseToTargetLocal.x, -baseToTargetLocal.y);

            // Clamp angles to prevent flipping
            const maxAngle = Math.PI * 0.8;
            const minAngle = -Math.PI * 0.8;

            midJoint.rotation.z = THREE.MathUtils.clamp(angle0 - angle1, minAngle, maxAngle);
            tipJoint.rotation.z = THREE.MathUtils.clamp(Math.PI - angle2, minAngle, maxAngle);
        }
    </script>
</body>
</html>