<!-- SOLIDABILITY-WORLD · part 24 -->
  container.appendChild(renderer.domElement);

    // Lighting: bright, neutral — see the whole building clearly
    const ambient = new THREE.AmbientLight(0xffffff, 0.85);
    scene.add(ambient);
    const directional = new THREE.DirectionalLight(0xffffff, 0.9);
    directional.position.set(20, 40, 20);
    directional.castShadow = true;
    scene.add(directional);
    // Fill light from opposite side to kill dark spots
    const fill = new THREE.DirectionalLight(0xccccff, 0.4);
    fill.position.set(-20, 20, -20);
    scene.add(fill);

    // Player: simple avatar (cylinder body + sphere head)
    const playerGroup = new THREE.Group();
    const bodyGeo = new THREE.CylinderGeometry(0.4, 0.4, 1.2, 12);
    const bodyMat = new THREE.MeshStandardMaterial({ color: 0x00ffcc });
    const body = new THREE.Mesh(bodyGeo, bodyMat);
    body.position.y = 0.6;
    body.castShadow = true;
    playerGroup.add(body);
    const headGeo = new THREE.SphereGeometry(0.3, 12, 12);
    const head = new THREE.Mesh(headGeo, bodyMat);
    head.position.y = 1.5;
    head.castShadow = true;
    playerGroup.add(head);
    player = playerGroup;
    player.position.set(0, 0, -15);
    scene.add(player);

    // Ground: lowered to y=-0.5 to avoid z-fighting with building floor 0
    const groundGeo = new THREE.PlaneGeometry(200, 200);
    const groundMat = new THREE.MeshStandardMaterial({ color: 0x2a2a35 });
    const ground = new THREE.Mesh(groundGeo, groundMat);
    ground.rotation.x = -Math.PI / 2;
    ground.position.y = -0.5;
    ground.receiveShadow = true;
    scene.add(ground);

    const grid = new THREE.GridHelper(200, 40, 0x555566, 0x3a3a48);
    grid.position.y = -0.45;
    scene.add(grid);

    setupInput(container);
    animate();
  }

  function setupInput(container) {
    // Keyboard
    window.addEventListener('keydown', e => { keys[e.key.toLowerCase()] = true; });
    window.addEventListener('keyup', e => { keys[e.key.toLowerCase()] = false; });

    // Touch joystick (simplified from /newworld/input.js patterns)
    const joyZone = document.getElementById('joystick');
    if (joyZone && 'ontouchstart' in window) {
      joyZone.style.display = 'block';
      let touchId = null, startX = 0, startY = 0;
      joyZone.addEventListener('touchstart', e => {
        const t = e.changedTouches[0];
        touchId = t.identifier; startX = t.clientX; startY = t.clientY;
        joystick.active = true;
      });
      joyZone.addEventListener('touchmove', e => {
        for (const t of e.changedTouches) {
          if (t.identifier === touchId) {
            joystick.dx = Math.max(-1, Math.min(1, (t.clientX - startX) / 50));
            joystick.dy = Math.max(-1, Math.min(1, (t.clientY - startY) / 50));
          }
        }
      });
      joyZone.addEventListener('touchend', e => {
        for (const t of e.changedTouches) {
          if (t.identifier === touchId) {
            joystick.active = false; joystick.dx = 0; joystick.dy = 0;
          }
        }
      });
    }
  }

  function updatePlayer(dt) {
    // Hyper Controller AI DRIVE moves the player — don't fight it
    const hcDriving = (typeof SWHyper !== 'undefined') &&
      (SWHyper.getMode() === 'AI_DRIVE' || SWHyper.isAIDriving());

    const speed = 6; // slow, deliberate — inspection pace, not game pace
    let mx = 0, mz = 0;

    if (!hcDriving) {
      if (keys['w'] || keys['arrowup']) mz -= 1;
      if (keys['s'] || keys['arrowdown']) mz += 1;
      if (keys['a'] || keys['arrowleft']) mx -= 1;
      if (keys['d'] || keys['arrowright']) mx += 1;
      if (joystick.active) { mx += joystick.dx; mz += joystick.dy; }
    } else {
      // Any manual input immediately cancels AI Drive (owner takes over)
      if (keys['w'] || keys['s'] || keys['a'] || keys['d'] ||
          keys['arrowup'] || keys['arrowdown'] || keys['arrowleft'] || keys['arrowright'] ||
          joystick.active) {
        SWHyper.stopAI();
      }
    }

    // Normalize diagonal
    const len = Math.sqrt(mx * mx + mz * mz);
    if (len > 1) { mx /= len; mz /= len; }

    // Move with wall collision
    tryMove(mx * speed * dt, mz * speed * dt);

    // Stair climbing / floor snapping
    updateStairs();

    // Camera follows behind and above
    const camTarget = new THREE.Vector3(
      player.position.x,
      player.position.y + 6,
      player.position.z + 10
    );
    camera.position.lerp(camTarget, 0.1);
    camera.lookAt(player.position.x, player.position.y + 1, player.position.z);
  }

  // Wall collision: outer building walls solid, room walls solid except door gaps
  function tryMove(dx, dz) {
    const layout = SWWorld.getLayout();
    const r = 0.6; // player radius
    const newX = player.position.x + dx;
    const newZ = player.position.z + dz;

    // Public world solid colliders (work without rendering)
    if (typeof SWWorld !== 'undefined' && SWWorld.checkCollision) {
      if (SWWorld.checkCollision(newX, newZ, r)) return;
    }