<!-- SAGA3 · part 20 -->
setIndex( accessor );

			} );
			pending.push( accessor );

		}

		assignExtrasToUserData( geometry, primitiveDef );
		computeBounds( geometry, primitiveDef, parser );
		return Promise.all( pending ).then( function () {

			return primitiveDef.targets !== undefined ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry;

		} );

	}
	/**
 * @param {BufferGeometry} geometry
 * @param {Number} drawMode
 * @return {BufferGeometry}
 */


	function toTrianglesDrawMode( geometry, drawMode ) {

		let index = geometry.getIndex(); // generate index if not present

		if ( index === null ) {

			const indices = [];
			const position = geometry.getAttribute( 'position' );

			if ( position !== undefined ) {

				for ( let i = 0; i < position.count; i ++ ) {

					indices.push( i );

				}

				geometry.setIndex( indices );
				index = geometry.getIndex();

			} else {

				console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
				return geometry;

			}

		} //


		const numberOfTriangles = index.count - 2;
		const newIndices = [];

		if ( drawMode === THREE.TriangleFanDrawMode ) {

			// gl.TRIANGLE_FAN
			for ( let i = 1; i <= numberOfTriangles; i ++ ) {

				newIndices.push( index.getX( 0 ) );
				newIndices.push( index.getX( i ) );
				newIndices.push( index.getX( i + 1 ) );

			}

		} else {

			// gl.TRIANGLE_STRIP
			for ( let i = 0; i < numberOfTriangles; i ++ ) {

				if ( i % 2 === 0 ) {

					newIndices.push( index.getX( i ) );
					newIndices.push( index.getX( i + 1 ) );
					newIndices.push( index.getX( i + 2 ) );

				} else {

					newIndices.push( index.getX( i + 2 ) );
					newIndices.push( index.getX( i + 1 ) );
					newIndices.push( index.getX( i ) );

				}

			}

		}

		if ( newIndices.length / 3 !== numberOfTriangles ) {

			console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );

		} // build final geometry


		const newGeometry = geometry.clone();
		newGeometry.setIndex( newIndices );
		return newGeometry;

	}

	THREE.GLTFLoader = GLTFLoader;

} )();


===== FILE: public/saga3/index.html =====
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Coders100</title>
<style>
  /* ===== FOUNDATION ===== */
  html, body {
    margin: 0; padding: 0; height: 100%;
    overflow: hidden; background: #0b0906;
    font-family: Georgia, serif;
  }
  #view {
    position: fixed; inset: 0;
    width: 100%; height: 100%;
    display: block; touch-action: none;
  }

  /* ===== CODE BAR — the line to punch in ===== */
  #codebar {
    position: fixed; top: 12px; left: 12px; right: 12px;
    z-index: 20; display: flex; gap: 8px;
  }
  #codeinput {
    flex: 1; height: 40px; border-radius: 20px;
    border: 1px solid #ffd700; background: rgba(0,0,0,0.7);
    color: #ffd700; padding: 0 16px; font-size: 14px;
    outline: none;
  }
  #codeinput::placeholder { color: #8a6a1c; }
  #codesend {
    width: 60px; height: 40px; border-radius: 20px;
    border: 1px solid #ffd700; background: #ffd700;
    color: #000; font-weight: bold; font-size: 14px;
    cursor: pointer;
  }

  /* ===== THE OS — every control lives here ===== */
  #os {
    position: fixed; bottom: 24px; left: 50%;
    transform: translateX(-50%);
    z-index: 100; display: none;
    flex-direction: row; flex-wrap: wrap;
    gap: 12px; justify-content: center; align-items: center;
    max-width: 94vw; max-height: 60vh; overflow-y: auto;
    padding: 18px;
    background: rgba(10,8,4,0.95);
    border: 2px solid #8a6a1c; border-radius: 24px;
  }
  #os.open { display: flex; }
  #os button {
    width: 60px; height: 60px; border-radius: 50%;
    background: rgba(20,14,6,0.9); border: 1px solid #d8a920;
    color: #ffd700; font-size: 24px;
    cursor: pointer; padding: 0; margin: 0; flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    display: flex; align-items: center; justify-content: center;
  }
  #os button:active { background: #d8a920; color: #111; }
  #os button.on { background: #d8a920; color: #111; }
  #os-toggle {
    position: fixed; right: 16px; bottom: 16px;
    width: 64px; height: 64px; border-radius: 50%;
    background: #d8a920; border: 2px solid #ffd700;
    color: #111; font-size: 28px; z-index: 101;
    cursor: pointer; padding: 0;
    -webkit-tap-highlight-color: transparent;
    box-shadow: 0 0 20px rgba(255,215,0,0.4);
  }

  /* ===== JOYSTICK — never hides ===== */
  #joy {
    position: fixed; left: 20px; bottom: 100px;
    width: 90px; height: 90px; border-radius: 50%;
    border: 2px solid #8a6a1c; background: rgba(20,14,6,0.4);
    z-index: 50; display: none; touch-action: none;
  }
  #joyknob {
    position: absolute; left: 50%; top: 50%;
    width: 36px; height: 36px; margin: -18px 0 0 -18px;
    border-radius: 50%; background: rgba(216,169,32