{"version":3,"sources":["../src/environment.ts","../src/environment/terrain.ts","../src/core/random.ts","../src/core/palette.ts","../src/environment/sky.ts","../src/environment/environment.ts","../src/environment/lighting.ts","../src/environment/water.ts","../src/environment/dayCycle.ts","../src/environment/lightBudget.ts","../src/environment/lightning.ts","../src/environment/fireworks.ts","../src/environment/lightShafts.ts","../src/environment/wind.ts","../src/environment/precipitation.ts","../src/environment/ocean.ts","../src/environment/weather.ts","../src/environment/seasons.ts","../src/environment/underwater.ts","../src/environment/flock.ts","../src/environment/herd.ts","../src/environment/path.ts","../src/environment/effects.ts","../src/environment/trail.ts","../src/environment/marks.ts","../src/environment/interiorLight.ts","../src/environment/seaState.ts","../src/environment/track.ts","../src/materials/surface.ts"],"sourcesContent":["// SCENA — environment: sky, terrain, weather, ocean, light\n//\n// A sub-path entry point. `import from 'scena3d'` still gives you everything;\n// this exists so a bundler can see module boundaries, and so an import says\n// what part of the library it depends on.\n//\n// GENERATED from src/index.ts by scripts/entries.mjs — every statement below\n// is the root barrel's own, partitioned by source directory. `npm run\n// entries:check` fails if this file and the barrel disagree.\n\nexport { createTerrain, type Terrain, type TerrainOptions } from './environment/terrain';\nexport { createSky, type Sky, type SkyOptions } from './environment/sky';\nexport {\n  applyEnvironment,\n  createEnvironmentMap,\n  type EnvironmentOptions,\n  type SceneEnvironment,\n} from './environment/environment';\nexport {\n  createLightingRig,\n  applyFog,\n  type LightingRig,\n  type LightingPreset,\n  type FogPreset,\n} from './environment/lighting';\nexport { createWater, aboveWater, type Water, type WaterOptions } from './environment/water';\nexport { createDayCycle, type DayCycle, type DayCycleOptions } from './environment/dayCycle';\nexport {\n  createLightBudget,\n  type LightBudget,\n  type LightBudgetOptions,\n  type LightClaim,\n  type LightGrant,\n} from './environment/lightBudget';\nexport {\n  createLightning,\n  type Lightning,\n  type LightningOptions,\n  type LightningTargets,\n  type Strike,\n} from './environment/lightning';\nexport {\n  createFireworks,\n  type Fireworks,\n  type FireworksOptions,\n  type LaunchOptions,\n} from './environment/fireworks';\nexport {\n  createLightShafts,\n  type LightShafts,\n  type LightShaftsOptions,\n} from './environment/lightShafts';\nexport {\n  createWindField,\n  applyWind,\n  type WindField,\n  type WindFieldOptions,\n  type SwayOptions,\n  type Wind,\n  type WindOptions,\n} from './environment/wind';\nexport {\n  createPrecipitation,\n  type Precipitation,\n  type PrecipitationOptions,\n  type PrecipitationType,\n  type AccumulateOptions,\n} from './environment/precipitation';\nexport { createOcean, type Ocean, type SurfOptions,\n  type RippleOptions,\n  type OceanOptions } from './environment/ocean';\nexport {\n  createWeather,\n  type Weather,\n  type WeatherOptions,\n  type WeatherPreset,\n  type WeatherStateParams,\n} from './environment/weather';\nexport {\n  createSeasons,\n  type Seasons,\n  type SeasonsOptions,\n  type Season,\n  type SeasonGrade,\n} from './environment/seasons';\nexport {\n  createGodRays,\n  createCaustics,\n  createBubbles,\n  createWaterGrade,\n  type GodRays,\n  type GodRaysOptions,\n  type Caustics,\n  type CausticsOptions,\n  type Bubbles,\n  type BubbleOptions,\n  type WaterGrade,\n  type WaterGradeOptions,\n} from './environment/underwater';\nexport { createFlock, type Flock, type FlockOptions, type FlockType } from './environment/flock';\nexport { createHerd, type Herd, type HerdOptions, type HerdType } from './environment/herd';\nexport { createPath, type WorldPath, type PathOptions } from './environment/path';\nexport {\n  createEffects,\n  type Effects,\n  type EffectsOptions,\n  type BurstKind,\n  type BurstOptions,\n  type RingOptions,\n} from './environment/effects';\nexport { createTrail, type Trail, type TrailOptions } from './environment/trail';\nexport {\n  createMarks,\n  type Marks,\n  type MarksOptions,\n  type GroundMarkKind,\n  type StampOptions,\n} from './environment/marks';\nexport {\n  createInteriorLight,\n  type InteriorLight,\n  type InteriorLightOptions,\n  type InteriorSun,\n} from './environment/interiorLight';\nexport {\n  createSeaState,\n  fullyDeveloped,\n  fetchLimited,\n  periodFor,\n  lengthFor,\n  douglasFor,\n  SEA_KINDS,\n  type SeaState,\n  type SeaStateOptions,\n  type SeaKind,\n  type SeaCondition,\n  type Train,\n} from './environment/seaState';\nexport {\n  createTrack,\n  type RailTrack,\n  type TrackOptions,\n  type TrackPoint,\n} from './environment/track';\n","import {\n  BufferAttribute,\n  Color,\n  Mesh,\n  MeshStandardMaterial,\n  PlaneGeometry,\n} from 'three';\nimport { fractalNoise2 } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport interface TerrainOptions {\n  seed?: number;\n  /** Square side length. Default 80. */\n  size?: number;\n  /** Vertices per side. Default 96. */\n  resolution?: number;\n  /** Peak height. Default 6. */\n  amplitude?: number;\n  /** Noise feature size in world units. Default 28. */\n  noiseScale?: number;\n  octaves?: number;\n  /** Flatten low areas into meadows (0–1, higher = flatter valleys). Default 0.55. */\n  valleyFlatness?: number;\n  /** Blend low bands toward sand below (waterLevel + shore margin). */\n  waterLevel?: number;\n  palette?: Palette;\n}\n\nexport interface Terrain {\n  mesh: Mesh;\n  /** Exact analytic height at any (x, z) — same function that built the\n   *  mesh, so agents/navmesh queries never disagree with the visuals. */\n  heightAt(x: number, z: number): number;\n  size: number;\n  seed: number;\n}\n\n/**\n * A seeded low-poly terrain: fractal value noise displacing a plane, with\n * height/slope-banded vertex colors (grass → high grass → cliff → peak).\n * The height function is exported, not just baked into vertices — that's\n * what lets gameplay (spawning, scattering, navmesh baking, agent ground\n * clamping) agree exactly with what's rendered.\n */\nexport function createTerrain(options: TerrainOptions = {}): Terrain {\n  const seed = options.seed ?? 1;\n  const size = options.size ?? 80;\n  const resolution = options.resolution ?? 96;\n  const amplitude = options.amplitude ?? 6;\n  const noiseScale = options.noiseScale ?? 28;\n  const octaves = options.octaves ?? 4;\n  const flatness = options.valleyFlatness ?? 0.55;\n  const palette = options.palette ?? DEFAULT_PALETTE;\n\n  const heightAt = (x: number, z: number): number => {\n    const n = fractalNoise2(x / noiseScale, z / noiseScale, seed, octaves);\n    // Push valleys down and flatten them; keep peaks.\n    const shaped = Math.pow(n, 1 + flatness * 2);\n    return shaped * amplitude;\n  };\n\n  const geometry = new PlaneGeometry(size, size, resolution - 1, resolution - 1);\n  geometry.rotateX(-Math.PI / 2);\n  const positions = geometry.getAttribute('position') as BufferAttribute;\n  for (let i = 0; i < positions.count; i++) {\n    positions.setY(i, heightAt(positions.getX(i), positions.getZ(i)));\n  }\n  geometry.computeVertexNormals();\n\n  // Vertex colors by height band + slope.\n  const normals = geometry.getAttribute('normal') as BufferAttribute;\n  const colors = new Float32Array(positions.count * 3);\n  const grassLow = new Color(palette.grassLow);\n  const grassHigh = new Color(palette.grassHigh);\n  const cliff = new Color(palette.cliff);\n  const peak = new Color(palette.peak);\n  const sand = new Color(palette.sand);\n  const waterLevel = options.waterLevel;\n  const scratch = new Color();\n  for (let i = 0; i < positions.count; i++) {\n    const y = positions.getY(i);\n    const h = y / amplitude;\n    const slope = 1 - normals.getY(i); // 0 flat … 1 vertical\n    scratch.copy(grassLow).lerp(grassHigh, Math.min(1, h * 1.6));\n    if (h > 0.75) scratch.lerp(peak, (h - 0.75) * 4);\n    if (slope > 0.15) scratch.lerp(cliff, Math.min(1, (slope - 0.15) * 4));\n    if (waterLevel !== undefined && y < waterLevel + 0.5) {\n      scratch.lerp(sand, Math.min(1, (waterLevel + 0.5 - y) * 1.6));\n    }\n    colors[i * 3] = scratch.r;\n    colors[i * 3 + 1] = scratch.g;\n    colors[i * 3 + 2] = scratch.b;\n  }\n  geometry.setAttribute('color', new BufferAttribute(colors, 3));\n\n  const mesh = new Mesh(\n    geometry,\n    new MeshStandardMaterial({ vertexColors: true, flatShading: true })\n  );\n  mesh.name = 'terrain';\n\n  return { mesh, heightAt, size, seed };\n}\n","/**\n * Deterministic seeded randomness — the backbone of SCENA. Same seed,\n * same tree; forests are reproducible, diffable and network-syncable.\n */\nexport class Rng {\n  private state: number;\n\n  constructor(seed = 1) {\n    this.state = seed >>> 0 || 1;\n  }\n\n  /** Next float in [0, 1) (mulberry32). */\n  next(): number {\n    this.state = (this.state + 0x6d2b79f5) >>> 0;\n    let t = this.state;\n    t = Math.imul(t ^ (t >>> 15), t | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  }\n\n  /** Float in [min, max). */\n  range(min: number, max: number): number {\n    return min + this.next() * (max - min);\n  }\n\n  /** Integer in [min, max] inclusive. */\n  int(min: number, max: number): number {\n    return min + Math.floor(this.next() * (max - min + 1));\n  }\n\n  /** Random element of a non-empty array. */\n  pick<T>(items: readonly T[]): T {\n    return items[Math.floor(this.next() * items.length)];\n  }\n\n  /** value ± spread (uniform). */\n  jitter(value: number, spread: number): number {\n    return value + (this.next() * 2 - 1) * spread;\n  }\n\n  /** A new independent Rng derived from this one. */\n  fork(): Rng {\n    return new Rng(Math.floor(this.next() * 0xffffffff) || 1);\n  }\n}\n\n/** Integer-lattice hash to [0, 1) — the base of the value noise. */\nexport function hash2(ix: number, iz: number, seed: number): number {\n  let h = (ix * 374761393 + iz * 668265263 + seed * 2246822519) >>> 0;\n  h = Math.imul(h ^ (h >>> 13), 1274126177) >>> 0;\n  return ((h ^ (h >>> 16)) >>> 0) / 4294967296;\n}\n\nconst smooth = (t: number): number => t * t * (3 - 2 * t);\n\n/** 2D value noise in [0, 1). Continuous; used by terrain and scatter density. */\nexport function valueNoise2(x: number, z: number, seed: number): number {\n  const ix = Math.floor(x);\n  const iz = Math.floor(z);\n  const fx = smooth(x - ix);\n  const fz = smooth(z - iz);\n  const a = hash2(ix, iz, seed);\n  const b = hash2(ix + 1, iz, seed);\n  const c = hash2(ix, iz + 1, seed);\n  const d = hash2(ix + 1, iz + 1, seed);\n  return a + (b - a) * fx + (c - a) * fz + (a - b - c + d) * fx * fz;\n}\n\n/** Fractal (octaved) value noise in [0, 1). */\nexport function fractalNoise2(\n  x: number,\n  z: number,\n  seed: number,\n  octaves = 4,\n  lacunarity = 2,\n  gain = 0.5\n): number {\n  let amplitude = 1;\n  let frequency = 1;\n  let sum = 0;\n  let total = 0;\n  for (let i = 0; i < octaves; i++) {\n    sum += valueNoise2(x * frequency, z * frequency, seed + i * 101) * amplitude;\n    total += amplitude;\n    amplitude *= gain;\n    frequency *= lacunarity;\n  }\n  return sum / total;\n}\n","/**\n * Theme palettes: one coherent set of colors shared by every generator,\n * so procedural props look like a matched set rather than a junk drawer.\n * Pass `palette` to any generator to restyle it; whole scenes retheme by\n * building with a different palette.\n */\nexport interface Palette {\n  foliage: number[];\n  trunk: number;\n  rock: number[];\n  wood: number;\n  woodDark: number;\n  metal: number;\n  lampGlow: number;\n  grassLow: number;\n  grassHigh: number;\n  cliff: number;\n  peak: number;\n  skyTop: number;\n  skyBottom: number;\n  fog: number;\n  water: number;\n  sand: number;\n  path: number;\n  /** Building plaster/wall color. */\n  wall: number;\n  /** Building roof color. */\n  roof: number;\n}\n\nexport const PALETTES: Record<'meadow' | 'autumn' | 'dusk' | 'winter' | 'urban', Palette> = {\n  meadow: {\n    foliage: [0x2f9e57, 0x37b26a, 0x2a8f4f, 0x45b878],\n    trunk: 0x6b4a33,\n    rock: [0x8a8f98, 0x767c86, 0x9aa0a8],\n    wood: 0x8a6642,\n    woodDark: 0x6b4a33,\n    metal: 0x3d4451,\n    lampGlow: 0xffd889,\n    grassLow: 0x3f9d5a,\n    grassHigh: 0x6fae66,\n    cliff: 0x7d7a72,\n    peak: 0xe8ecef,\n    skyTop: 0x3d70b8,\n    skyBottom: 0xbfd9e8,\n    fog: 0xb8cfdd,\n    water: 0x3f7fae,\n    sand: 0xc9b98a,\n    path: 0x9a815f,\n    wall: 0xd9ccb0,\n    roof: 0xa8563e,\n  },\n  autumn: {\n    foliage: [0xc9752f, 0xd98e3a, 0xb35c2a, 0xe0a545],\n    trunk: 0x5d4030,\n    rock: [0x8d8578, 0x776f63, 0x9c948a],\n    wood: 0x7d5a3a,\n    woodDark: 0x5d4030,\n    metal: 0x463f3a,\n    lampGlow: 0xffc571,\n    grassLow: 0x9d8a3f,\n    grassHigh: 0xb59b4a,\n    cliff: 0x82746a,\n    peak: 0xe3ded4,\n    skyTop: 0x8e6ca8,\n    skyBottom: 0xe8c9a8,\n    fog: 0xd9c1a8,\n    water: 0x4a7a92,\n    sand: 0xcbb083,\n    path: 0x8d7454,\n    wall: 0xccb894,\n    roof: 0x8a4a30,\n  },\n  dusk: {\n    foliage: [0x1f5e46, 0x24684f, 0x1a5240, 0x2d7458],\n    trunk: 0x413147,\n    rock: [0x565672, 0x484861, 0x646484],\n    wood: 0x5d4a63,\n    woodDark: 0x413147,\n    metal: 0x2b2b3d,\n    lampGlow: 0xffb35c,\n    grassLow: 0x2d6b52,\n    grassHigh: 0x3d7a5e,\n    cliff: 0x52516b,\n    peak: 0xb8b8d9,\n    skyTop: 0x1d2145,\n    skyBottom: 0xc96a4a,\n    fog: 0x6a5a7a,\n    water: 0x2d4a68,\n    sand: 0x8a7a6a,\n    path: 0x6a5a52,\n    wall: 0x8d8299,\n    roof: 0x453558,\n  },\n  winter: {\n    foliage: [0x2e5c48, 0x38695a, 0x527a68, 0x87a596],\n    trunk: 0x4a3a33,\n    rock: [0x9aa4ad, 0x848e98, 0xb0b8c0],\n    wood: 0x776049,\n    woodDark: 0x54453a,\n    metal: 0x39404d,\n    lampGlow: 0xffd889,\n    grassLow: 0xcfd9de,\n    grassHigh: 0xe4ebee,\n    cliff: 0x76797d,\n    peak: 0xf4f7fa,\n    skyTop: 0x5a7ba6,\n    skyBottom: 0xd8e4ec,\n    fog: 0xccd8e0,\n    water: 0x4a7086,\n    sand: 0xb8c2c6,\n    path: 0x8b8378,\n    wall: 0xd2c9bb,\n    roof: 0x6b4638,\n  },\n  // Modern district: warm-white render, charcoal trim, teak accents, clipped\n  // green — for bungalows, towers and the Tier-4 surfaces.\n  urban: {\n    foliage: [0x4a8f56, 0x5aa065, 0x3f7f4b, 0x6aae74],\n    trunk: 0x5d4a38,\n    rock: [0x9a9a96, 0x85857f, 0xaaaaa4],\n    wood: 0x8a5c36, // teak\n    woodDark: 0x54402c,\n    metal: 0x2f353c, // charcoal powder-coat\n    lampGlow: 0xffe0a8,\n    grassLow: 0x5a9a5e,\n    grassHigh: 0x7aae6e,\n    cliff: 0x8a8a84,\n    peak: 0xe8ecef,\n    skyTop: 0x4a7ec2,\n    skyBottom: 0xcfe0ec,\n    fog: 0xc8d4dd,\n    water: 0x3f7fae,\n    sand: 0xc9bfa8,\n    path: 0xb0aca2, // pale concrete\n    wall: 0xe8e2d6, // warm-white render\n    roof: 0x3a3d42, // charcoal fascia\n  },\n};\n\nexport const DEFAULT_PALETTE: Palette = PALETTES.meadow;\n","import {\n  BackSide,\n  Color,\n  Mesh,\n  ShaderMaterial,\n  SphereGeometry,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport interface SkyOptions {\n  topColor?: number;\n  bottomColor?: number;\n  radius?: number;\n  palette?: Palette;\n}\n\nexport interface Sky {\n  mesh: Mesh;\n  setColors(top: number, bottom: number): void;\n}\n\n/**\n * A gradient sky dome (vertical color blend on an inverted sphere).\n * Colors default to the palette, so themed scenes get matching skies.\n */\nexport function createSky(options: SkyOptions = {}): Sky {\n  const palette = options.palette ?? DEFAULT_PALETTE;\n  const material = new ShaderMaterial({\n    side: BackSide,\n    depthWrite: false,\n    uniforms: {\n      topColor: { value: new Color(options.topColor ?? palette.skyTop) },\n      bottomColor: { value: new Color(options.bottomColor ?? palette.skyBottom) },\n    },\n    vertexShader: /* glsl */ `\n      varying vec3 vWorld;\n      void main() {\n        vWorld = (modelMatrix * vec4(position, 1.0)).xyz;\n        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n      }`,\n    fragmentShader: /* glsl */ `\n      uniform vec3 topColor;\n      uniform vec3 bottomColor;\n      varying vec3 vWorld;\n      void main() {\n        float t = clamp(normalize(vWorld).y * 0.5 + 0.5, 0.0, 1.0);\n        gl_FragColor = vec4(mix(bottomColor, topColor, pow(t, 0.8)), 1.0);\n      }`,\n  });\n  const mesh = new Mesh(new SphereGeometry(options.radius ?? 400, 16, 12), material);\n  mesh.name = 'sky';\n\n  return {\n    mesh,\n    setColors(top, bottom) {\n      (material.uniforms.topColor.value as Color).setHex(top);\n      (material.uniforms.bottomColor.value as Color).setHex(bottom);\n    },\n  };\n}\n","import {\n  Color,\n  DataTexture,\n  EquirectangularReflectionMapping,\n  FloatType,\n  LinearFilter,\n  RGBAFormat,\n  Vector3,\n  type DirectionalLight,\n  type Scene,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\n/**\n * A scene environment — the thing that makes metal look like metal.\n *\n * A metal has no diffuse colour. Everything you see on it is a reflection,\n * so a metal in a scene with nothing to reflect renders BLACK. That is not\n * a bug in the material: it is the material being right about a world with\n * no sky in it. `createSky` draws a beautiful gradient dome, but a dome is\n * geometry — three cannot reflect it, because reflection needs an\n * environment map and a mesh is not one.\n *\n * So this builds one, from the same gradient the sky is drawing: a tiny\n * equirectangular {@link DataTexture} — sky above, ground bounce below, and\n * the sun burned in where the lighting rig actually put it. Nothing is\n * fetched, nothing is loaded, and three PMREM-filters it on first use so\n * rough surfaces get a blurred version and polished ones a sharp one.\n *\n * ```ts\n * const rig = createLightingRig('day');\n * scene.add(createSky({ palette }).mesh, rig.group);\n * applyEnvironment(scene, { palette, sun: rig.sun });   // now chrome is chrome\n * ```\n *\n * It affects every PBR material in the scene, not just the metals: it is\n * ambient light arriving from a direction, which is what the flat\n * `AmbientLight` in the rig is a stand-in for.\n */\nexport interface EnvironmentOptions {\n  /** Palette to take the sky colours from. */\n  palette?: Palette;\n  /** Zenith colour (hex int). Defaults to the palette's `skyTop`. */\n  top?: number;\n  /** Horizon colour (hex int). Defaults to the palette's `skyBottom`. */\n  horizon?: number;\n  /**\n   * What the light bounces off below the horizon (hex int). Defaults to the\n   * palette's `grassLow` — the ground half of an environment matters more\n   * than it sounds, because it is what stops the underside of everything\n   * metal from being a black hole.\n   */\n  ground?: number;\n  /**\n   * The sun to burn into the map, as a light or a direction. A sky with no\n   * sun in it gives polished metal a soft even sheen and no highlight at\n   * all, which reads as plastic.\n   */\n  sun?: DirectionalLight | Vector3 | null;\n  /** How bright the sun disc is, relative to the sky. Default 6. */\n  sunIntensity?: number;\n  /** How tight it is: bigger is smaller. Default 380. */\n  sunFocus?: number;\n  /** Overall strength, written to `scene.environmentIntensity`. Default 1. */\n  intensity?: number;\n  /**\n   * Equirect width in texels (height is half). Default 128 — this is an\n   * environment, not a photograph, and three blurs it into a PMREM chain\n   * anyway.\n   */\n  size?: number;\n}\n\nexport interface SceneEnvironment {\n  /** The generated map, already assigned to `scene.environment`. */\n  texture: DataTexture;\n  /** Rebuild after changing the palette or moving the sun. */\n  refresh(options?: EnvironmentOptions): void;\n  /** Take it off the scene and free it. */\n  dispose(): void;\n}\n\nconst SUN_DIR = new Vector3();\nconst DIR = new Vector3();\n\nfunction sunDirection(sun: EnvironmentOptions['sun']): Vector3 | null {\n  if (!sun) return null;\n  if (sun instanceof Vector3) return SUN_DIR.copy(sun).normalize();\n  // A DirectionalLight shines from its position toward its target, so the\n  // direction light ARRIVES from is the position — which is where the sun\n  // has to be in the sky for the highlight to land in the same place.\n  return SUN_DIR.copy(sun.position).sub(sun.target.position).normalize();\n}\n\n/**\n * Paint the equirect: sky above, ground below, sun where the rig put it.\n *\n * Use this directly — rather than {@link applyEnvironment} — when only SOME\n * materials should reflect, because a per-material `envMap` is the only way\n * to do that. `scene.environment` cannot be opted out of: three overwrites\n * `material.envMapIntensity` with `scene.environmentIntensity` for every\n * material that has no `envMap` of its own, so setting it to 0 on one\n * material does nothing at all.\n *\n * ```ts\n * const map = createEnvironmentMap({ palette, sun: rig.sun });\n * chrome.envMap = map;            // this one reflects\n * plaster.envMap = null;          // this one does not\n * ```\n */\nexport function createEnvironmentMap(options: EnvironmentOptions = {}): DataTexture {\n  const palette = options.palette ?? DEFAULT_PALETTE;\n  const width = Math.max(16, Math.round(options.size ?? 128));\n  const height = Math.max(8, width >> 1);\n  const top = new Color(options.top ?? palette.skyTop);\n  const horizon = new Color(options.horizon ?? palette.skyBottom);\n  const ground = new Color(options.ground ?? palette.grassLow);\n  const sun = sunDirection(options.sun);\n  const sunPower = options.sunIntensity ?? 6;\n  const focus = options.sunFocus ?? 380;\n\n  const data = new Float32Array(width * height * 4);\n  const c = new Color();\n  for (let y = 0; y < height; y++) {\n    // Equirect: v spans the poles, u spans the full turn.\n    const phi = ((y + 0.5) / height) * Math.PI;\n    for (let x = 0; x < width; x++) {\n      const theta = ((x + 0.5) / width) * Math.PI * 2;\n      DIR.set(\n        Math.sin(phi) * Math.cos(theta),\n        Math.cos(phi),\n        Math.sin(phi) * Math.sin(theta)\n      );\n      if (DIR.y >= 0) {\n        // The same curve the sky dome's shader draws, so the reflection and\n        // the backdrop agree with each other.\n        c.copy(horizon).lerp(top, Math.pow(DIR.y, 0.8));\n      } else {\n        // Below the horizon it falls off to the ground colour rather than\n        // cutting to it — light bounces, it does not stop.\n        c.copy(horizon).lerp(ground, Math.min(1, -DIR.y * 2.2));\n      }\n      let r = c.r;\n      let g = c.g;\n      let b = c.b;\n      if (sun) {\n        const spot = Math.pow(Math.max(0, DIR.dot(sun)), focus) * sunPower;\n        if (spot > 0) {\n          r += spot;\n          g += spot * 0.97;\n          b += spot * 0.9;\n        }\n      }\n      const i = (y * width + x) * 4;\n      data[i] = r;\n      data[i + 1] = g;\n      data[i + 2] = b;\n      data[i + 3] = 1;\n    }\n  }\n\n  const texture = new DataTexture(data, width, height, RGBAFormat, FloatType);\n  // Equirect + reflection mapping is the form three knows how to PMREM.\n  texture.mapping = EquirectangularReflectionMapping;\n  texture.magFilter = LinearFilter;\n  texture.minFilter = LinearFilter;\n  texture.generateMipmaps = false;\n  texture.needsUpdate = true;\n  texture.name = 'scena-environment';\n  return texture;\n}\n\n/**\n * Give the scene something to reflect. Returns a handle so it can be\n * refreshed when the sun moves or the palette changes, and disposed.\n */\nexport function applyEnvironment(\n  scene: Scene,\n  options: EnvironmentOptions = {}\n): SceneEnvironment {\n  let current = options;\n  let texture = createEnvironmentMap(current);\n  scene.environment = texture;\n  scene.environmentIntensity = options.intensity ?? 1;\n\n  return {\n    get texture() {\n      return texture;\n    },\n    refresh(next = {}) {\n      current = { ...current, ...next };\n      texture.dispose();\n      texture = createEnvironmentMap(current);\n      scene.environment = texture;\n      if (next.intensity !== undefined) scene.environmentIntensity = next.intensity;\n    },\n    dispose() {\n      if (scene.environment === texture) scene.environment = null;\n      texture.dispose();\n    },\n  };\n}\n","import {\n  AmbientLight,\n  DirectionalLight,\n  Fog,\n  Group,\n  HemisphereLight,\n  type Scene,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport type LightingPreset = 'day' | 'golden-hour' | 'overcast' | 'night';\n\nexport interface LightingRig {\n  group: Group;\n  sun: DirectionalLight;\n  ambient: AmbientLight;\n  hemisphere: HemisphereLight;\n}\n\nconst PRESETS: Record<\n  LightingPreset,\n  { sun: number; sunIntensity: number; sunPos: [number, number, number]; ambient: number; ambientIntensity: number; skyTint: number; groundTint: number }\n> = {\n  day: {\n    sun: 0xfff4e0, sunIntensity: 1.6, sunPos: [30, 45, 20],\n    ambient: 0xdfeaff, ambientIntensity: 0.35, skyTint: 0xbcd8ff, groundTint: 0x6a7d66,\n  },\n  'golden-hour': {\n    sun: 0xffb861, sunIntensity: 1.7, sunPos: [40, 14, -12],\n    ambient: 0xffdcc0, ambientIntensity: 0.3, skyTint: 0xffcf9e, groundTint: 0x7a6a58,\n  },\n  overcast: {\n    sun: 0xd8dee8, sunIntensity: 0.7, sunPos: [12, 40, 8],\n    ambient: 0xcfd6e0, ambientIntensity: 0.65, skyTint: 0xc8d0dc, groundTint: 0x707a78,\n  },\n  night: {\n    sun: 0x8ea6d8, sunIntensity: 0.35, sunPos: [-20, 30, -25],\n    ambient: 0x3a4468, ambientIntensity: 0.35, skyTint: 0x2c3560, groundTint: 0x1e2430,\n  },\n};\n\n/**\n * The three lights every scene rebuilds, as a preset: warm directional\n * \"sun\" (position doubles as light direction), ambient fill, and a\n * hemisphere tint. Retune any of them via the returned rig.\n */\nexport function createLightingRig(preset: LightingPreset = 'day'): LightingRig {\n  const config = PRESETS[preset];\n  const group = new Group();\n  group.name = `lighting-${preset}`;\n  const sun = new DirectionalLight(config.sun, config.sunIntensity);\n  sun.position.set(...config.sunPos);\n  const ambient = new AmbientLight(config.ambient, config.ambientIntensity);\n  const hemisphere = new HemisphereLight(config.skyTint, config.groundTint, 0.5);\n  group.add(sun, ambient, hemisphere);\n  return { group, sun, ambient, hemisphere };\n}\n\nexport type FogPreset = 'clear' | 'haze' | 'thick' | 'eerie';\n\nconst FOG: Record<Exclude<FogPreset, 'clear'>, { near: number; far: number }> = {\n  haze: { near: 45, far: 160 },\n  thick: { near: 12, far: 70 },\n  eerie: { near: 6, far: 42 },\n};\n\n/** Distance fog matched to the palette's fog color. 'clear' removes it. */\nexport function applyFog(scene: Scene, preset: FogPreset, palette: Palette = DEFAULT_PALETTE): void {\n  if (preset === 'clear') {\n    scene.fog = null;\n    return;\n  }\n  const { near, far } = FOG[preset];\n  scene.fog = new Fog(palette.fog, near, far);\n}\n","import { BufferAttribute, Mesh, MeshStandardMaterial, PlaneGeometry } from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Terrain } from './terrain';\n\nexport interface WaterOptions {\n  /** World-space water surface height. Default 0.8. */\n  level?: number;\n  size?: number;\n  resolution?: number;\n  /** Wave height. Default 0.06. */\n  amplitude?: number;\n  /** Wave speed multiplier. Default 1. */\n  speed?: number;\n  palette?: Palette;\n}\n\nexport interface Water {\n  mesh: Mesh;\n  level: number;\n  /** Advance the wave animation. Call from your frame loop. */\n  update(dt: number): void;\n  /** Is ground at this height below the surface? */\n  isUnderwater(groundHeight: number): boolean;\n}\n\n/**\n * A low-poly animated water plane at a fixed level. Pair it with a\n * terrain built using the same `waterLevel` so shores blend to sand, and\n * keep scatter/agents ashore with `aboveWater(terrain, water)`.\n */\nexport function createWater(options: WaterOptions = {}): Water {\n  const level = options.level ?? 0.8;\n  const size = options.size ?? 200;\n  const resolution = options.resolution ?? 40;\n  const amplitude = options.amplitude ?? 0.06;\n  const speed = options.speed ?? 1;\n  const palette = options.palette ?? DEFAULT_PALETTE;\n\n  const geometry = new PlaneGeometry(size, size, resolution, resolution);\n  geometry.rotateX(-Math.PI / 2);\n  const positions = geometry.getAttribute('position') as BufferAttribute;\n  const mesh = new Mesh(\n    geometry,\n    new MeshStandardMaterial({\n      color: palette.water,\n      transparent: true,\n      opacity: 0.85,\n      flatShading: true,\n      metalness: 0.35,\n      roughness: 0.4,\n    })\n  );\n  mesh.name = 'water';\n  mesh.position.y = level;\n\n  let time = 0;\n  const update = (dt: number): void => {\n    time += dt * speed;\n    for (let i = 0; i < positions.count; i++) {\n      const x = positions.getX(i);\n      const z = positions.getZ(i);\n      positions.setY(\n        i,\n        Math.sin(x * 0.35 + time) * Math.cos(z * 0.3 + time * 0.8) * amplitude\n      );\n    }\n    positions.needsUpdate = true;\n    geometry.computeVertexNormals();\n  };\n  update(0);\n\n  return {\n    mesh,\n    level,\n    update,\n    isUnderwater: (groundHeight) => groundHeight < level,\n  };\n}\n\n/**\n * A scatter mask keeping placements on dry land: true when the terrain\n * at (x, z) sits above the water level plus `margin`.\n */\nexport function aboveWater(\n  terrain: Terrain,\n  water: Pick<Water, 'level'>,\n  margin = 0.25\n): (x: number, z: number) => boolean {\n  return (x, z) => terrain.heightAt(x, z) > water.level + margin;\n}\n","import { Color, PointLight, type MeshStandardMaterial, type Mesh, type Object3D, type Scene } from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { LightingRig } from './lighting';\nimport type { Sky } from './sky';\n\ninterface Keyframe {\n  t: number;\n  skyTop: number;\n  skyBottom: number;\n  sun: number;\n  sunIntensity: number;\n  ambientIntensity: number;\n  fog: number;\n}\n\nexport interface DayCycleOptions {\n  sky?: Sky;\n  rig?: LightingRig;\n  /** Scene whose fog color should track the cycle. */\n  scene?: Scene;\n  /** Lamp props/objects whose PointLights + glow bulbs ignite at night. */\n  lamps?: Array<{ object: Object3D } | Object3D>;\n  palette?: Palette;\n  /** Seconds per full day. Default 60. */\n  dayLength?: number;\n  /** Initial time: 0 = midnight, 0.25 = dawn, 0.5 = noon, 0.75 = dusk. */\n  timeOfDay?: number;\n}\n\nexport interface DayCycle {\n  timeOfDay: number;\n  /** Sun elevation in [-1, 1]; negative = below the horizon. */\n  readonly sunElevation: number;\n  readonly isNight: boolean;\n  /** Advance by dt seconds of real time and re-apply everything. */\n  update(dt: number): void;\n  /** Jump to a time of day and re-apply everything. */\n  set(t: number): void;\n}\n\n/**\n * One `timeOfDay` parameter driving the whole environment in lockstep:\n * sun position/color/intensity, sky gradient, ambient level, fog color,\n * and lamps that ignite as the sun drops below the horizon.\n *\n * ```ts\n * const cycle = createDayCycle({ sky, rig, scene, lamps: [lampA, lampB], dayLength: 120 });\n * game.onUpdate((t) => cycle.update(t.delta));\n * ```\n */\nexport function createDayCycle(options: DayCycleOptions = {}): DayCycle {\n  const palette = options.palette ?? DEFAULT_PALETTE;\n  const dayLength = options.dayLength ?? 60;\n\n  // Universal ramp; noon colors come from the palette so themes carry through.\n  const frames: Keyframe[] = [\n    { t: 0.0, skyTop: 0x0b1026, skyBottom: 0x1a2340, sun: 0x8ea6d8, sunIntensity: 0.03, ambientIntensity: 0.06, fog: 0x141a2e },\n    { t: 0.23, skyTop: 0x27335c, skyBottom: 0x6a5470, sun: 0xcf9a72, sunIntensity: 0.3, ambientIntensity: 0.12, fog: 0x4a4258 },\n    { t: 0.3, skyTop: 0x4a6ba0, skyBottom: 0xe8a97a, sun: 0xffb861, sunIntensity: 1.0, ambientIntensity: 0.26, fog: 0xb08a72 },\n    { t: 0.5, skyTop: palette.skyTop, skyBottom: palette.skyBottom, sun: 0xfff4e0, sunIntensity: 1.9, ambientIntensity: 0.45, fog: palette.fog },\n    { t: 0.7, skyTop: 0x4a5a94, skyBottom: 0xe0955e, sun: 0xffa050, sunIntensity: 1.0, ambientIntensity: 0.26, fog: 0xa07a68 },\n    { t: 0.78, skyTop: 0x232a52, skyBottom: 0x8a5560, sun: 0xd88a5a, sunIntensity: 0.25, ambientIntensity: 0.11, fog: 0x463e54 },\n    { t: 1.0, skyTop: 0x0b1026, skyBottom: 0x1a2340, sun: 0x8ea6d8, sunIntensity: 0.03, ambientIntensity: 0.06, fog: 0x141a2e },\n  ];\n\n  // Collect lamp lights and glowing bulb materials once.\n  const lampLights: Array<{ light: PointLight; base: number }> = [];\n  const lampBulbs: Array<{ material: MeshStandardMaterial; base: number }> = [];\n  for (const entry of options.lamps ?? []) {\n    const root = (entry as { isObject3D?: boolean }).isObject3D\n      ? (entry as Object3D)\n      : (entry as { object: Object3D }).object;\n    root.traverse((child) => {\n      if (child instanceof PointLight) {\n        lampLights.push({ light: child, base: child.intensity || 6 });\n      }\n      const material = (child as Mesh).material as MeshStandardMaterial | undefined;\n      if (material?.emissive && material.emissiveIntensity > 0.5) {\n        lampBulbs.push({ material, base: material.emissiveIntensity });\n      }\n    });\n  }\n\n  const colorA = new Color();\n  const colorB = new Color();\n  let timeOfDay = options.timeOfDay ?? 0.5;\n  let sunElevation = 0;\n\n  const sample = (t: number): Keyframe => {\n    let a = frames[0];\n    let b = frames[frames.length - 1];\n    for (let i = 0; i < frames.length - 1; i++) {\n      if (t >= frames[i].t && t <= frames[i + 1].t) {\n        a = frames[i];\n        b = frames[i + 1];\n        break;\n      }\n    }\n    const f = b.t === a.t ? 0 : (t - a.t) / (b.t - a.t);\n    const lerpHex = (ha: number, hb: number): number =>\n      colorA.setHex(ha).lerp(colorB.setHex(hb), f).getHex();\n    return {\n      t,\n      skyTop: lerpHex(a.skyTop, b.skyTop),\n      skyBottom: lerpHex(a.skyBottom, b.skyBottom),\n      sun: lerpHex(a.sun, b.sun),\n      sunIntensity: a.sunIntensity + (b.sunIntensity - a.sunIntensity) * f,\n      ambientIntensity: a.ambientIntensity + (b.ambientIntensity - a.ambientIntensity) * f,\n      fog: lerpHex(a.fog, b.fog),\n    };\n  };\n\n  const apply = (): void => {\n    const t = timeOfDay;\n    const frame = sample(t);\n    const sunAngle = (t - 0.25) * Math.PI * 2;\n    sunElevation = Math.sin(sunAngle);\n\n    options.sky?.setColors(frame.skyTop, frame.skyBottom);\n    if (options.rig) {\n      const { sun, ambient, hemisphere } = options.rig;\n      sun.color.setHex(frame.sun);\n      sun.intensity = frame.sunIntensity;\n      sun.position.set(Math.cos(sunAngle) * 40, Math.max(sunElevation, -0.2) * 45 + 6, 16);\n      ambient.intensity = frame.ambientIntensity;\n      // Hemisphere fill follows the ambient curve so nights actually darken.\n      hemisphere.intensity = frame.ambientIntensity * 1.4;\n    }\n    if (options.scene?.fog && 'color' in options.scene.fog) {\n      options.scene.fog.color.setHex(frame.fog);\n    }\n    // Lamps fade in as the sun dips below the horizon.\n    const night = Math.min(1, Math.max(0, (0.06 - sunElevation) / 0.16));\n    for (const { light, base } of lampLights) light.intensity = base * night;\n    for (const { material, base } of lampBulbs) {\n      material.emissiveIntensity = 0.15 * base + 0.85 * base * night;\n    }\n  };\n  apply();\n\n  return {\n    get timeOfDay() {\n      return timeOfDay;\n    },\n    set timeOfDay(t: number) {\n      timeOfDay = ((t % 1) + 1) % 1;\n      apply();\n    },\n    get sunElevation() {\n      return sunElevation;\n    },\n    get isNight() {\n      return sunElevation < 0;\n    },\n    update(dt) {\n      timeOfDay = (timeOfDay + dt / dayLength) % 1;\n      apply();\n    },\n    set(t) {\n      timeOfDay = ((t % 1) + 1) % 1;\n      apply();\n    },\n  };\n}\n","import { Group, PointLight, Vector3, type Object3D } from 'three';\n\n/**\n * The light budget — spending scarcity where the camera is looking.\n *\n * Forward-rendered WebGL affords a handful of real dynamic lights before\n * mobile GPUs weep, but a street wants twenty lamps. The resolution: every\n * luminous prop keeps its cheap glow (emissive bulb + additive halo) always,\n * and *claims* a real light it may or may not be granted. The budget owns a\n * small pool of PointLights and, each update, grants them to the\n * best-scoring claims — score is priority over distance to the viewpoint —\n * so light pools appear under the lamps near the camera and quietly leave\n * the ones behind it.\n *\n * Granting is **hysteretic**: an incumbent keeps its light until a\n * challenger clearly outscores it, so panning the camera doesn't strobe\n * lights between owners. A kept claim also keeps the *same* PointLight\n * instance — no rebind flicker.\n *\n * ```ts\n * const budget = createLightBudget({ max: 6 });\n * scene.add(budget.group);\n * for (const lamp of lamps) budget.register(lamp.claim);\n * // per frame:\n * budget.update(camera.position);\n * ```\n */\n\nexport interface LightClaim {\n  /** Where the light lives. An Object3D is tracked live (movers welcome). */\n  anchor: Object3D | { x: number; y: number; z: number };\n  color: number;\n  /** Intensity the granted PointLight burns at. */\n  intensity: number;\n  /** Falloff distance of the granted light, metres. */\n  radius: number;\n  /** Bigger outranks distance. Default 1. */\n  priority?: number;\n  /**\n   * Litness, read live — a doused fixture's claim goes ineligible without\n   * any unregistering. Luminous props close this over their own state.\n   */\n  isLit?: () => boolean;\n}\n\nexport interface LightGrant {\n  readonly claim: LightClaim;\n  /** Holding a real light right now? */\n  readonly granted: boolean;\n  /** Withdraw the claim entirely. */\n  release(): void;\n}\n\nexport interface LightBudgetOptions {\n  /** Real lights in the pool. Default 6. */\n  max?: number;\n  /**\n   * How decisively a challenger must beat an incumbent to take its light\n   * (score ratio). 1 = no stickiness. Default 1.35.\n   */\n  hysteresis?: number;\n}\n\nexport interface LightBudget {\n  /** Add this to the scene — the pool lives here. */\n  group: Group;\n  readonly max: number;\n  /** Claims currently holding a real light. */\n  readonly active: number;\n  register(claim: LightClaim): LightGrant;\n  /** Re-grant the pool for this viewpoint (camera or hero position). */\n  update(viewpoint: Object3D | { x: number; y: number; z: number }): void;\n}\n\ninterface Entry {\n  claim: LightClaim;\n  score: number;\n  light: PointLight | null;\n  released: boolean;\n}\n\nconst worldScratch = new Vector3();\n\nfunction anchorPosition(anchor: LightClaim['anchor'], out: Vector3): Vector3 {\n  if ((anchor as Object3D).isObject3D) {\n    return (anchor as Object3D).getWorldPosition(out);\n  }\n  const p = anchor as { x: number; y: number; z: number };\n  return out.set(p.x, p.y, p.z);\n}\n\nexport function createLightBudget(options: LightBudgetOptions = {}): LightBudget {\n  const max = Math.max(options.max ?? 6, 1);\n  const hysteresis = Math.max(options.hysteresis ?? 1.35, 1);\n\n  const group = new Group();\n  group.name = 'light-budget';\n  const pool: PointLight[] = [];\n  for (let i = 0; i < max; i++) {\n    const light = new PointLight(0xffffff, 0, 1, 2);\n    group.add(light);\n    pool.push(light);\n  }\n\n  const entries: Entry[] = [];\n  const view = new Vector3();\n\n  const update = (viewpoint: Object3D | { x: number; y: number; z: number }): void => {\n    anchorPosition(viewpoint as LightClaim['anchor'], view);\n\n    // Score every live claim; doused or released claims are ineligible.\n    for (const entry of entries) {\n      if (entry.released || entry.claim.isLit?.() === false) {\n        entry.score = -1;\n        continue;\n      }\n      const d = anchorPosition(entry.claim.anchor, worldScratch).distanceTo(view);\n      entry.score = (entry.claim.priority ?? 1) / (1 + d);\n    }\n\n    const eligible = entries.filter((e) => e.score > 0).sort((a, b) => b.score - a.score);\n    const cutoff = eligible.length > max ? eligible[max - 1].score : 0;\n\n    // Incumbents stay unless clearly below the cut; they keep their light.\n    const kept: Entry[] = [];\n    for (const entry of entries) {\n      if (entry.light && entry.score > 0 && entry.score * hysteresis >= cutoff) {\n        kept.push(entry);\n      } else if (entry.light) {\n        entry.light.intensity = 0;\n        entry.light = null;\n      }\n    }\n    kept.sort((a, b) => b.score - a.score);\n    for (const dropped of kept.splice(max)) {\n      // paranoia: never over budget\n      if (dropped.light) dropped.light.intensity = 0;\n      dropped.light = null;\n    }\n\n    // Fill free slots with the best of the rest.\n    const free = pool.filter((l) => !kept.some((e) => e.light === l));\n    for (const entry of eligible) {\n      if (kept.length >= max) break;\n      if (entry.light) continue;\n      const light = free.pop();\n      if (!light) break;\n      entry.light = light;\n      kept.push(entry);\n    }\n\n    // Park the granted lights on their fixtures.\n    for (const entry of kept) {\n      const light = entry.light!;\n      anchorPosition(entry.claim.anchor, worldScratch);\n      light.position.copy(worldScratch);\n      light.color.setHex(entry.claim.color);\n      light.intensity = entry.claim.intensity;\n      light.distance = entry.claim.radius;\n    }\n  };\n\n  return {\n    group,\n    max,\n    get active() {\n      return entries.filter((e) => e.light !== null).length;\n    },\n    register(claim) {\n      const entry: Entry = { claim, score: 0, light: null, released: false };\n      entries.push(entry);\n      return {\n        claim,\n        get granted() {\n          return entry.light !== null;\n        },\n        release() {\n          entry.released = true;\n          if (entry.light) {\n            entry.light.intensity = 0;\n            entry.light = null;\n          }\n          const i = entries.indexOf(entry);\n          if (i >= 0) entries.splice(i, 1);\n        },\n      };\n    },\n    update,\n  };\n}\n","import {\n  AdditiveBlending,\n  Color,\n  CylinderGeometry,\n  Group,\n  Mesh,\n  MeshBasicMaterial,\n  Vector3,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Lightning — the sky's percussion.\n *\n * A strike is three things at once: the **flash** (a two-pulse spike\n * driven through whatever targets you hand it — ambient intensity, sky\n * background, fog color — then decayed back to exactly where they\n * were), the **bolt** (a seeded forked polyline of additive tubes,\n * gone in a tenth of a second), and the **thunder** — an `onThunder`\n * callback delayed by the strike's distance, which is the handshake a\n * Soundboard answers. Distance IS the delay: close strikes crack\n * immediately, far ones rumble in late.\n *\n * ```ts\n * const storm = createLightning({\n *   targets: { ambient: rig.ambient, background: scene.background, fog: scene.fog },\n *   onThunder: (s) => sounds.crack(Math.min(1.4 - s.distance / 60, 1)),\n * });\n * scene.add(storm.group);\n * storm.storminess = 0.8;                 // auto-strikes, seeded\n * // per frame: storm.update(dt);\n * ```\n */\n\nexport interface Strike {\n  /** Game-metres from the origin. */\n  distance: number;\n  /** Bearing of the strike, radians. */\n  azimuth: number;\n  /** 0..1 — how hard the flash hits. */\n  energy: number;\n}\n\nexport interface LightningTargets {\n  ambient?: { intensity: number };\n  /** A Color — the scene's background. */\n  background?: { getHex(): number; setHex(hex: number): unknown } | null;\n  /** The scene's fog (its color is flashed). */\n  fog?: { color: { getHex(): number; setHex(hex: number): unknown } } | null;\n}\n\nexport interface LightningOptions {\n  targets?: LightningTargets;\n  seed?: number;\n  /** Mean seconds between auto-strikes at storminess 1. Default 5. */\n  cadence?: number;\n  /** Game-metres of thunder delay per real second. Default 100. */\n  soundSpeed?: number;\n  onStrike?: (strike: Strike) => void;\n  onThunder?: (strike: Strike) => void;\n}\n\nexport interface Lightning {\n  /** Bolts appear here — add it to the scene. */\n  group: Group;\n  /** 0 = clear skies, 1 = the full show. Drives auto-strikes. */\n  storminess: number;\n  /** The current flash level, 0..1 — flash your own things by it too. */\n  readonly flash: number;\n  /** Strikes so far (auto + manual). */\n  readonly strikes: number;\n  /** Force a strike now; omitted fields are seeded. */\n  strike(options?: Partial<Strike>): Strike;\n  update(dt: number): void;\n}\n\nconst FLASH_COLOR = new Color(0xcfd8ff);\nconst mixScratch = new Color();\n\ninterface Bolt {\n  group: Group;\n  material: MeshBasicMaterial;\n  life: number;\n}\n\nexport function createLightning(options: LightningOptions = {}): Lightning {\n  const rng = new Rng(options.seed ?? 1);\n  const targets = options.targets ?? {};\n  const cadence = Math.max(options.cadence ?? 5, 0.5);\n  const soundSpeed = Math.max(options.soundSpeed ?? 100, 1);\n\n  const group = new Group();\n  group.name = 'lightning';\n\n  // Where the channels rest — the flash always decays back to exactly this.\n  const baseAmbient = targets.ambient?.intensity ?? 0;\n  const baseBackground = targets.background?.getHex() ?? 0;\n  const baseFog = targets.fog?.color.getHex() ?? 0;\n\n  let flash = 0;\n  let strikes = 0;\n  let clock = 0;\n  let settled = true;\n  const pendingThunder: Array<{ at: number; strike: Strike }> = [];\n  const pendingPulse: Array<{ at: number; energy: number }> = [];\n  const bolts: Bolt[] = [];\n\n  const buildBolt = (strike: Strike): void => {\n    const boltGroup = new Group();\n    const material = new MeshBasicMaterial({\n      color: 0xe8eeff,\n      transparent: true,\n      opacity: 0.95,\n      blending: AdditiveBlending,\n      depthWrite: false,\n    });\n    const x = Math.sin(strike.azimuth) * strike.distance;\n    const z = Math.cos(strike.azimuth) * strike.distance;\n    const run = (\n      from: Vector3,\n      to: Vector3,\n      segments: number,\n      radius: number,\n      jitter: number\n    ): Vector3[] => {\n      const points: Vector3[] = [from.clone()];\n      for (let i = 1; i < segments; i++) {\n        const t = i / segments;\n        points.push(\n          new Vector3(\n            from.x + (to.x - from.x) * t + rng.range(-jitter, jitter),\n            from.y + (to.y - from.y) * t,\n            from.z + (to.z - from.z) * t + rng.range(-jitter, jitter)\n          )\n        );\n      }\n      points.push(to.clone());\n      for (let i = 0; i + 1 < points.length; i++) {\n        const a = points[i];\n        const b = points[i + 1];\n        const length = a.distanceTo(b);\n        const tube = new Mesh(new CylinderGeometry(radius, radius, length, 4), material);\n        tube.position.copy(a).add(b).multiplyScalar(0.5);\n        tube.lookAt(b);\n        tube.rotateX(Math.PI / 2);\n        boltGroup.add(tube);\n      }\n      return points;\n    };\n    const top = new Vector3(x + rng.range(-3, 3), 26, z + rng.range(-3, 3));\n    const ground = new Vector3(x, 0, z);\n    const spine = run(top, ground, 8, 0.09, 1.6);\n    // One fork, from a mid node, dying in the air.\n    const forkFrom = spine[2 + rng.int(0, 2)];\n    const forkTo = new Vector3(\n      forkFrom.x + rng.range(-5, 5),\n      forkFrom.y * rng.range(0.25, 0.5),\n      forkFrom.z + rng.range(-5, 5)\n    );\n    run(forkFrom, forkTo, 4, 0.05, 1.0);\n    group.add(boltGroup);\n    bolts.push({ group: boltGroup, material, life: 0.16 });\n  };\n\n  const strike = (partial: Partial<Strike> = {}): Strike => {\n    const s: Strike = {\n      distance: partial.distance ?? rng.range(8, 45),\n      azimuth: partial.azimuth ?? rng.range(0, Math.PI * 2),\n      energy: partial.energy ?? rng.range(0.6, 1),\n    };\n    strikes++;\n    // Far strikes flash softer.\n    const felt = s.energy * Math.max(1 - s.distance / 90, 0.25);\n    flash = Math.max(flash, felt);\n    settled = false;\n    // The double pulse — lightning never blinks just once.\n    pendingPulse.push({ at: clock + 0.09, energy: felt * 0.65 });\n    pendingThunder.push({ at: clock + s.distance / soundSpeed, strike: s });\n    pendingThunder.sort((a, b) => a.at - b.at);\n    buildBolt(s);\n    options.onStrike?.(s);\n    return s;\n  };\n\n  return {\n    group,\n    storminess: 0,\n    get flash() {\n      return flash;\n    },\n    get strikes() {\n      return strikes;\n    },\n    strike,\n    update(dt) {\n      const step = Number.isFinite(dt) ? Math.max(dt, 0) : 0;\n      clock += step;\n\n      const self = this as Lightning;\n      if (self.storminess > 0 && rng.next() < (self.storminess * step) / cadence) {\n        strike();\n      }\n\n      for (let i = pendingPulse.length - 1; i >= 0; i--) {\n        if (pendingPulse[i].at <= clock) {\n          flash = Math.max(flash, pendingPulse[i].energy);\n          pendingPulse.splice(i, 1);\n        }\n      }\n      while (pendingThunder.length && pendingThunder[0].at <= clock) {\n        // The shift must happen unconditionally — an optional call\n        // `f?.(queue.shift())` skips its ARGUMENT when f is absent, and\n        // an undrained queue is an infinite loop wearing a while.\n        const due = pendingThunder.shift()!;\n        options.onThunder?.(due.strike);\n      }\n\n      for (let i = bolts.length - 1; i >= 0; i--) {\n        const bolt = bolts[i];\n        bolt.life -= step;\n        bolt.material.opacity = Math.max(bolt.life / 0.16, 0) * 0.95;\n        if (bolt.life <= 0) {\n          group.remove(bolt.group);\n          bolt.material.dispose();\n          bolt.group.traverse((c) => (c as Mesh).geometry?.dispose?.());\n          bolts.splice(i, 1);\n        }\n      }\n\n      // Decay the flash and drive the channels; land EXACTLY on base.\n      if (flash > 0) flash = flash < 0.004 ? 0 : flash * Math.exp(-9 * step);\n      if (!settled) {\n        if (targets.ambient) targets.ambient.intensity = baseAmbient + flash * 1.6;\n        if (targets.background) {\n          mixScratch.setHex(baseBackground).lerp(FLASH_COLOR, Math.min(flash * 0.85, 1));\n          targets.background.setHex(mixScratch.getHex());\n        }\n        if (targets.fog) {\n          mixScratch.setHex(baseFog).lerp(FLASH_COLOR, Math.min(flash * 0.85, 1));\n          targets.fog.color.setHex(mixScratch.getHex());\n        }\n        if (flash === 0 && pendingPulse.length === 0) {\n          if (targets.ambient) targets.ambient.intensity = baseAmbient;\n          if (targets.background) targets.background.setHex(baseBackground);\n          if (targets.fog) targets.fog.color.setHex(baseFog);\n          settled = true;\n        }\n      }\n    },\n  };\n}\n","import {\n  AdditiveBlending,\n  Color,\n  Group,\n  InstancedMesh,\n  Matrix4,\n  MeshBasicMaterial,\n  OctahedronGeometry,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Fireworks — celebration tech.\n *\n * Seeded rockets rise on a slightly drunken line, burst at the top of\n * their fuse into a spherical shell of glowing sparks that droop under\n * gravity and gutter out. One InstancedMesh for everything in flight\n * (the effects-system idiom: octahedra, scale-is-fade, per-instance\n * color), so a grand finale is still one draw call.\n *\n * `onBurst` fires at every shell break with the position and color —\n * that's the hook for the boom, the GameFeel thump, and the crowd.\n *\n * ```ts\n * const show = createFireworks({ seed: 7, onBurst: (at) => sounds.impact('soft', 1) });\n * scene.add(show.group);\n * show.launch({ x: -4, y: 0, z: 0 }, { color: 0xff9d5c });\n * // per frame: show.update(dt);\n * ```\n */\n\nexport interface FireworksOptions {\n  seed?: number;\n  /** Spark capacity — the finale budget. Default 600. */\n  capacity?: number;\n  /** Downward pull on sparks. Default 3.4 (stylized, not Earth). */\n  gravity?: number;\n  onBurst?: (at: { x: number; y: number; z: number }, color: number) => void;\n}\n\nexport interface LaunchOptions {\n  color?: number;\n  /** Sparks in the shell. Default seeded 70–110. */\n  sparks?: number;\n}\n\nexport interface Fireworks {\n  group: Group;\n  /** Rockets currently climbing. */\n  readonly rockets: number;\n  /** Sparks currently burning. */\n  readonly sparks: number;\n  launch(from?: { x: number; y: number; z: number }, options?: LaunchOptions): void;\n  update(dt: number): void;\n}\n\nconst SHELL_COLORS = [0xffd889, 0xff9d5c, 0x9dd1ff, 0xff5f8f, 0xb8ffc8, 0xf0e6ff];\n\ninterface Particle {\n  alive: boolean;\n  x: number;\n  y: number;\n  z: number;\n  vx: number;\n  vy: number;\n  vz: number;\n  life: number;\n  maxLife: number;\n  size: number;\n  /** Rockets have a fuse and burst; sparks just burn down. */\n  fuse: number;\n  color: number;\n  shellSparks: number;\n}\n\nconst matrix = new Matrix4();\nconst colorScratch = new Color();\n\nexport function createFireworks(options: FireworksOptions = {}): Fireworks {\n  const rng = new Rng(options.seed ?? 1);\n  const capacity = Math.max(options.capacity ?? 600, 50);\n  const gravity = options.gravity ?? 3.4;\n\n  const group = new Group();\n  group.name = 'fireworks';\n  const mesh = new InstancedMesh(\n    new OctahedronGeometry(0.11),\n    new MeshBasicMaterial({ blending: AdditiveBlending, depthWrite: false, transparent: true }),\n    capacity\n  );\n  mesh.frustumCulled = false;\n  group.add(mesh);\n\n  const slots: Particle[] = Array.from({ length: capacity }, () => ({\n    alive: false,\n    x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0,\n    life: 0, maxLife: 1, size: 1, fuse: -1, color: 0xffffff, shellSparks: 0,\n  }));\n  // All slots start hidden.\n  matrix.makeScale(0, 0, 0);\n  for (let i = 0; i < capacity; i++) mesh.setMatrixAt(i, matrix);\n  mesh.instanceMatrix.needsUpdate = true;\n\n  let cursor = 0;\n  const take = (): Particle => {\n    // Oldest-recycled: a finale never crashes, the dimmest spark just dies early.\n    for (let i = 0; i < capacity; i++) {\n      const slot = slots[(cursor + i) % capacity];\n      if (!slot.alive) {\n        cursor = (cursor + i + 1) % capacity;\n        return slot;\n      }\n    }\n    cursor = (cursor + 1) % capacity;\n    return slots[cursor];\n  };\n\n  const paint = (slot: Particle, brightness = 1): void => {\n    colorScratch.setHex(slot.color).multiplyScalar(brightness);\n    mesh.setColorAt(slots.indexOf(slot), colorScratch);\n  };\n\n  const burst = (rocket: Particle): void => {\n    const count = rocket.shellSparks;\n    for (let i = 0; i < count; i++) {\n      const spark = take();\n      // Uniform-ish sphere directions, seeded.\n      const theta = rng.range(0, Math.PI * 2);\n      const cosPhi = rng.range(-1, 1);\n      const sinPhi = Math.sqrt(1 - cosPhi * cosPhi);\n      const speed = rng.range(4.5, 8) * rng.range(0.75, 1);\n      spark.alive = true;\n      spark.x = rocket.x;\n      spark.y = rocket.y;\n      spark.z = rocket.z;\n      spark.vx = Math.cos(theta) * sinPhi * speed + rocket.vx * 0.15;\n      spark.vy = cosPhi * speed + rocket.vy * 0.1;\n      spark.vz = Math.sin(theta) * sinPhi * speed + rocket.vz * 0.15;\n      spark.maxLife = spark.life = rng.range(1.4, 2.2);\n      spark.size = rng.range(0.7, 1.3);\n      spark.fuse = -1;\n      spark.color = rocket.color;\n      paint(spark, rng.range(0.85, 1.15));\n    }\n    options.onBurst?.({ x: rocket.x, y: rocket.y, z: rocket.z }, rocket.color);\n  };\n\n  return {\n    group,\n    get rockets() {\n      return slots.filter((s) => s.alive && s.fuse >= 0).length;\n    },\n    get sparks() {\n      return slots.filter((s) => s.alive && s.fuse < 0).length;\n    },\n    launch(from = { x: 0, y: 0, z: 0 }, launchOptions = {}) {\n      const rocket = take();\n      rocket.alive = true;\n      rocket.x = from.x;\n      rocket.y = from.y;\n      rocket.z = from.z;\n      rocket.vx = rng.range(-0.7, 0.7);\n      rocket.vy = rng.range(9, 11);\n      rocket.vz = rng.range(-0.7, 0.7);\n      rocket.fuse = rng.range(1.0, 1.35);\n      rocket.maxLife = rocket.life = 10; // fuse decides, not life\n      rocket.size = 0.8;\n      rocket.color = launchOptions.color ?? SHELL_COLORS[rng.int(0, SHELL_COLORS.length - 1)];\n      rocket.shellSparks = launchOptions.sparks ?? rng.int(70, 110);\n      // The climbing streak burns white-hot, whatever the shell's color.\n      const i = slots.indexOf(rocket);\n      colorScratch.setHex(0xfff6e0);\n      mesh.setColorAt(i, colorScratch);\n    },\n    update(dt) {\n      const step = Number.isFinite(dt) ? Math.max(dt, 0) : 0;\n      for (let i = 0; i < capacity; i++) {\n        const p = slots[i];\n        if (!p.alive) continue;\n        if (p.fuse >= 0) {\n          // A rocket: climb, wobble, pop.\n          p.fuse -= step;\n          p.vy -= 2.2 * step; // gentle slowdown near apex\n          p.x += p.vx * step;\n          p.y += p.vy * step;\n          p.z += p.vz * step;\n          if (p.fuse <= 0) {\n            p.alive = false;\n            burst(p);\n            matrix.makeScale(0, 0, 0);\n            mesh.setMatrixAt(i, matrix);\n            continue;\n          }\n          matrix.makeScale(p.size, p.size * 1.6, p.size); // streak, not dot\n          matrix.setPosition(p.x, p.y, p.z);\n          mesh.setMatrixAt(i, matrix);\n        } else {\n          // A spark: droop, drag, gutter.\n          p.life -= step;\n          if (p.life <= 0) {\n            p.alive = false;\n            matrix.makeScale(0, 0, 0);\n            mesh.setMatrixAt(i, matrix);\n            continue;\n          }\n          p.vy -= gravity * step;\n          const drag = Math.max(1 - 1.1 * step, 0);\n          p.vx *= drag;\n          p.vy *= drag;\n          p.vz *= drag;\n          p.x += p.vx * step;\n          p.y += p.vy * step;\n          p.z += p.vz * step;\n          const fade = p.life / p.maxLife;\n          const s = p.size * fade;\n          matrix.makeScale(s, s, s);\n          matrix.setPosition(p.x, p.y, p.z);\n          mesh.setMatrixAt(i, matrix);\n        }\n      }\n      mesh.instanceMatrix.needsUpdate = true;\n      if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true;\n    },\n  };\n}\n","import {\n  AdditiveBlending,\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  Group,\n  Mesh,\n  Points,\n  PointsMaterial,\n  ShaderMaterial,\n  Vector3,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Light shafts — god rays for the out-of-doors.\n *\n * `createInteriorLight` casts them from windows; this is the same light\n * let loose: seeded sunbeams slanting through a forest canopy, a ruin's\n * broken roof, a cavern mouth. Each shaft is a pair of crossed additive\n * cards that fade along their run (all shafts share ONE merged geometry\n * and one material — the whole grove is a single draw call), with dust\n * motes drifting slowly down the beams, twinkling as they go.\n *\n * Bind a day cycle (structurally — anything with `sunElevation`) and\n * the shafts live with the sun: full at midday, gone by dusk. No cycle\n * means a fixed sun and shafts that never die.\n *\n * ```ts\n * const shafts = createLightShafts({ count: 7, area: 8, seed: 4, cycle });\n * scene.add(shafts.group);\n * // per frame: shafts.update(dt);\n * ```\n */\n\nexport interface LightShaftsOptions {\n  /** How many beams. Default 7. */\n  count?: number;\n  /** Radius of the lit patch on the ground, metres. Default 8. */\n  area?: number;\n  /** Beam run from canopy to ground, metres. Default 13. */\n  length?: number;\n  /** Beam color. Default a warm 0xfff2c8. */\n  color?: number;\n  /** Peak card opacity. Default 0.13 — shafts suggest, never shout. */\n  strength?: number;\n  /** Dust motes per shaft. Default 18; 0 disables. */\n  dust?: number;\n  /** Tilt from vertical, radians. Default 0.32. */\n  tilt?: number;\n  /** Which way the beams lean. Default 0.7. */\n  azimuth?: number;\n  /** A DayCycle (structurally): strength follows the sun. */\n  cycle?: { readonly sunElevation: number };\n  seed?: number;\n}\n\nexport interface LightShafts {\n  group: Group;\n  /** Current effective strength (after the cycle has its say). */\n  readonly strength: number;\n  /** Override the base strength (still scaled by the cycle). */\n  setStrength(value: number): void;\n  /** Drift the motes; follow the bound cycle. */\n  update(dt: number): void;\n}\n\nconst SHAFT_VERT = /* glsl */ `\nattribute float aFade;\nvarying float vFade;\nvoid main() {\n  vFade = aFade;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}`;\n\nconst SHAFT_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uStrength;\nvarying float vFade;\nvoid main() {\n  // Brightest where the beam enters, thinning toward the ground.\n  float alpha = uStrength * (1.0 - vFade * 0.75);\n  gl_FragColor = vec4(uColor, alpha);\n}`;\n\ninterface Mote {\n  shaft: number;\n  across: number; // -0.5..0.5 of the shaft width\n  along: number; // 0 at canopy, 1 at ground\n  speed: number;\n  sway: number;\n  phase: number;\n}\n\nexport function createLightShafts(options: LightShaftsOptions = {}): LightShafts {\n  const rng = new Rng(options.seed ?? 1);\n  const count = Math.max(options.count ?? 7, 1);\n  const area = options.area ?? 8;\n  const length = options.length ?? 13;\n  const baseStrength = options.strength ?? 0.13;\n  const dustPerShaft = Math.max(options.dust ?? 18, 0);\n  const tilt = options.tilt ?? 0.32;\n  const azimuth = options.azimuth ?? 0.7;\n\n  const group = new Group();\n  group.name = 'light-shafts';\n\n  // The shared sun direction, canopy → ground.\n  const dir = new Vector3(\n    Math.sin(azimuth) * Math.sin(tilt),\n    -Math.cos(tilt),\n    Math.cos(azimuth) * Math.sin(tilt)\n  ).multiplyScalar(length);\n\n  // Each shaft: ground point seeded in the disc, top back up the sun line,\n  // two crossed cards tapering toward the top. One merged geometry.\n  const feet: Vector3[] = [];\n  const widths: number[] = [];\n  const positions: number[] = [];\n  const fades: number[] = [];\n  const indices: number[] = [];\n  const push = (v: Vector3, fade: number): number => {\n    positions.push(v.x, v.y, v.z);\n    fades.push(fade);\n    return positions.length / 3 - 1;\n  };\n  const a = new Vector3();\n  const b = new Vector3();\n  for (let i = 0; i < count; i++) {\n    const r = Math.sqrt(rng.next()) * area;\n    const theta = rng.range(0, Math.PI * 2);\n    const foot = new Vector3(Math.cos(theta) * r, 0.02, Math.sin(theta) * r);\n    const top = foot.clone().sub(dir);\n    const width = rng.range(0.7, 1.9);\n    feet.push(foot);\n    widths.push(width);\n    for (const across of [\n      new Vector3(1, 0, 0),\n      new Vector3(0, 0, 1),\n    ]) {\n      a.copy(across).multiplyScalar(width / 2);\n      b.copy(across).multiplyScalar((width / 2) * 0.55); // narrower at the canopy\n      const i0 = push(new Vector3().copy(top).sub(b), 0);\n      const i1 = push(new Vector3().copy(top).add(b), 0);\n      const i2 = push(new Vector3().copy(foot).add(a), 1);\n      const i3 = push(new Vector3().copy(foot).sub(a), 1);\n      indices.push(i0, i1, i2, i0, i2, i3);\n    }\n  }\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(Float32Array.from(positions), 3));\n  geometry.setAttribute('aFade', new BufferAttribute(Float32Array.from(fades), 1));\n  geometry.setIndex(indices);\n  const material = new ShaderMaterial({\n    uniforms: {\n      uColor: { value: new Color(options.color ?? 0xfff2c8) },\n      uStrength: { value: baseStrength },\n    },\n    vertexShader: SHAFT_VERT,\n    fragmentShader: SHAFT_FRAG,\n    transparent: true,\n    depthWrite: false,\n    blending: AdditiveBlending,\n    side: 2, // DoubleSide — a card seen from behind is still a beam\n  });\n  const mesh = new Mesh(geometry, material);\n  mesh.frustumCulled = false;\n  group.add(mesh);\n\n  // The motes: one Points cloud across every shaft, drifting down-beam.\n  const motes: Mote[] = [];\n  let points: Points | null = null;\n  let moteMaterial: PointsMaterial | null = null;\n  const motePositions = new Float32Array(count * dustPerShaft * 3);\n  if (dustPerShaft > 0) {\n    for (let s = 0; s < count; s++) {\n      for (let d = 0; d < dustPerShaft; d++) {\n        motes.push({\n          shaft: s,\n          across: rng.range(-0.5, 0.5),\n          along: rng.next(),\n          speed: rng.range(0.012, 0.035),\n          sway: rng.range(0.05, 0.16),\n          phase: rng.range(0, Math.PI * 2),\n        });\n      }\n    }\n    const moteGeometry = new BufferGeometry();\n    moteGeometry.setAttribute('position', new BufferAttribute(motePositions, 3));\n    moteMaterial = new PointsMaterial({\n      color: options.color ?? 0xfff2c8,\n      size: 0.055,\n      transparent: true,\n      opacity: 0.6,\n      blending: AdditiveBlending,\n      depthWrite: false,\n      sizeAttenuation: true,\n    });\n    points = new Points(moteGeometry, moteMaterial);\n    points.frustumCulled = false;\n    group.add(points);\n  }\n\n  let clock = rng.range(0, 10);\n  let userStrength = baseStrength;\n  let effective = baseStrength;\n\n  const placeMotes = (): void => {\n    for (let i = 0; i < motes.length; i++) {\n      const mote = motes[i];\n      const foot = feet[mote.shaft];\n      const width = widths[mote.shaft];\n      const sway = Math.sin(clock * 0.7 + mote.phase) * mote.sway;\n      // 0 = canopy, 1 = ground: walk back up the sun line.\n      const x = foot.x - dir.x * (1 - mote.along) + (mote.across + sway) * width;\n      const y = foot.y - dir.y * (1 - mote.along);\n      const z = foot.z - dir.z * (1 - mote.along) + (mote.across - sway) * width * 0.5;\n      motePositions[i * 3] = x;\n      motePositions[i * 3 + 1] = y;\n      motePositions[i * 3 + 2] = z;\n    }\n    if (points) {\n      (points.geometry.getAttribute('position') as BufferAttribute).needsUpdate = true;\n    }\n  };\n  placeMotes();\n\n  const applyStrength = (): void => {\n    const sun = options.cycle ? Math.min(Math.max(options.cycle.sunElevation * 2.2, 0), 1) : 1;\n    effective = userStrength * sun;\n    material.uniforms.uStrength.value = effective;\n    if (moteMaterial) moteMaterial.opacity = 0.6 * sun;\n  };\n  applyStrength();\n\n  return {\n    group,\n    get strength() {\n      return effective;\n    },\n    setStrength(value: number) {\n      userStrength = Math.max(value, 0);\n      applyStrength();\n    },\n    update(dt: number) {\n      const step = Number.isFinite(dt) ? Math.max(dt, 0) : 0;\n      clock += step;\n      for (const mote of motes) {\n        mote.along += mote.speed * step * 8;\n        if (mote.along > 1) mote.along -= 1; // back to the canopy, forever falling\n      }\n      if (motes.length > 0) placeMotes();\n      applyStrength();\n    },\n  };\n}\n","import { Mesh, Vector2, type Material, type Object3D } from 'three';\n\n/** The slice of the shader object `onBeforeCompile` receives. */\ninterface PatchableShader {\n  uniforms: Record<string, { value: unknown }>;\n  vertexShader: string;\n}\n\nexport interface WindFieldOptions {\n  /** Wind bearing: degrees (0 = +X, 90 = +Z) or a world [x, z] vector. Default 35°. */\n  direction?: number | [number, number];\n  /** Steady lean at full sway, in world units. Default 0.3. */\n  strength?: number;\n  /** Gustiness 0–1: 0 = constant lean, 1 = deep travelling gusts. Default 0.5. */\n  gust?: number;\n  /** Distance between gust crests, in metres (smaller = tighter ripples). Default 6. */\n  waveLength?: number;\n  /** Speed the gust crests travel downwind. Default 2.2. */\n  waveSpeed?: number;\n}\n\nexport interface SwayOptions {\n  /** Local height (metres) at which sway reaches full. Default 1. */\n  height?: number;\n  /** Stiffness curve exponent — higher keeps the base stiffer. Default 1.6. */\n  stiffness?: number;\n  /** Local height below which nothing moves (keeps trunks planted). Default 0. */\n  anchor?: number;\n}\n\nexport interface WindField {\n  /** The shared shader uniforms (one set, referenced by every bound material). */\n  readonly uniforms: Record<string, { value: unknown }>;\n  /** The (normalized) wind direction in world XZ. Mutate via `setDirection`. */\n  readonly direction: Vector2;\n  /** Current steady strength. */\n  strength: number;\n  /** Every material patched so far. */\n  materials: Material[];\n  /** Point the wind along a new bearing (degrees) or [x, z] vector. */\n  setDirection(direction: number | [number, number]): WindField;\n  /** Change the steady strength. */\n  setStrength(strength: number): WindField;\n  /** The wind vector at a world point (CPU side) — for pushing agents, particles, boats. */\n  sample(x: number, z: number, time?: number): Vector2;\n  /** Patch a material to sway. Composes with surface materials; idempotent per material. */\n  bind(material: Material, options?: SwayOptions): WindField;\n  /** Self-animate: drive the shared clock from a rendered object's `onBeforeRender`. */\n  attach(object: Object3D): WindField;\n  /** Convenience: `bind` every material under `target`, then `attach` the driver. */\n  sway(target: Object3D, options?: SwayOptions): WindField;\n  /** Advance the clock manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nconst WIND_UNIFORMS = /* glsl */ `\nuniform vec2  uWindDir;\nuniform float uWindStrength;\nuniform float uWindGust;\nuniform float uWindWaveK;\nuniform float uWindWaveSpeed;\nuniform float uWindTime;\nuniform float uWindHeight;\nuniform float uWindStiff;\nuniform float uWindAnchor;\n`;\n\n// Vertex bend: displacement grows with local height (bases stay planted), leans\n// along the wind, and rides a gust that TRAVELS downwind (so a field ripples,\n// and neighbours never sway in lockstep). Computed in world space then folded\n// back to local (assumes uniform scale + rotation, true for scattered plants).\nconst WIND_BEGIN = /* glsl */ `\n{\n  mat4 scenaWM = modelMatrix;\n  #ifdef USE_INSTANCING\n    scenaWM = modelMatrix * instanceMatrix;\n  #endif\n  vec3 scenaBase = scenaWM[3].xyz;\n  float scenaLH = max(position.y - uWindAnchor, 0.0);\n  float scenaSway = pow(clamp(scenaLH / max(uWindHeight, 1e-3), 0.0, 1.0), uWindStiff);\n  if (uWindStrength > 0.0 && scenaSway > 0.0) {\n    float scenaPhase = dot(scenaBase.xz, uWindDir) * uWindWaveK - uWindTime * uWindWaveSpeed;\n    float scenaGust = mix(1.0, 0.5 + 0.5 * sin(scenaPhase), uWindGust);\n    float scenaLean = uWindStrength * scenaGust * scenaSway;\n    float scenaFlutter = 0.3 * uWindStrength * sin(scenaPhase * 1.7 + scenaBase.x) * scenaSway;\n    vec2 scenaPerp = vec2(-uWindDir.y, uWindDir.x);\n    vec3 scenaDisp = vec3(\n      uWindDir.x * scenaLean + scenaPerp.x * scenaFlutter,\n      -0.12 * abs(scenaLean),\n      uWindDir.y * scenaLean + scenaPerp.y * scenaFlutter\n    );\n    mat3 scenaLin = mat3(scenaWM);\n    float scenaInv = 1.0 / max(dot(scenaLin[0], scenaLin[0]), 1e-5);\n    // transpose(M) * v via column dots → world displacement back into local space.\n    transformed += vec3(dot(scenaLin[0], scenaDisp), dot(scenaLin[1], scenaDisp), dot(scenaLin[2], scenaDisp)) * scenaInv;\n  }\n}\n`;\n\nfunction toDir(direction: number | [number, number], out: Vector2): Vector2 {\n  if (Array.isArray(direction)) out.set(direction[0], direction[1]);\n  else {\n    const r = (direction * Math.PI) / 180;\n    out.set(Math.cos(r), Math.sin(r));\n  }\n  if (out.lengthSq() < 1e-9) out.set(1, 0);\n  return out.normalize();\n}\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * A shared wind field for vegetation and cloth — the environmental handshake\n * that makes a world breathe. One field drives many props: trees and grass\n * bend, a wheat field ripples, banners fly, all from the *same* gust, so a\n * breeze crosses the whole scene in step. The bend is a vertex-shader effect\n * (full PBR/shadows/fog survive), the gust travels downwind so nothing sways\n * in lockstep, and the field self-animates from the render loop — no update\n * wiring needed. `sample(x, z)` exposes the same wind on the CPU, so gameplay\n * (drifting agents, bobbing boats, blown particles) can read it too.\n *\n * ```ts\n * const wind = createWindField({ direction: 40, strength: 0.35 });\n * wind.sway(forest.group, { height: 4, anchor: 1.0 });      // canopies bend\n * wind.sway(wheat.group,  { height: 0.9, stiffness: 1.2 }); // blades ripple\n * ```\n */\nexport function createWindField(options: WindFieldOptions = {}): WindField {\n  const direction = toDir(options.direction ?? 35, new Vector2());\n  let strength = options.strength ?? 0.3;\n  const gust = Math.max(0, Math.min(1, options.gust ?? 0.5));\n  const waveLength = options.waveLength ?? 6;\n  const waveSpeed = options.waveSpeed ?? 2.2;\n\n  const uniforms = {\n    uWindDir: { value: direction },\n    uWindStrength: { value: strength },\n    uWindGust: { value: gust },\n    uWindWaveK: { value: (Math.PI * 2) / Math.max(waveLength, 0.01) },\n    uWindWaveSpeed: { value: waveSpeed },\n    uWindTime: { value: 0 },\n  };\n\n  const patched: Material[] = [];\n  let manual = false;\n\n  const field: WindField = {\n    uniforms,\n    direction,\n    get strength() {\n      return strength;\n    },\n    set strength(s: number) {\n      strength = s;\n      uniforms.uWindStrength.value = s;\n    },\n    materials: patched,\n\n    setDirection(dir) {\n      toDir(dir, direction);\n      return field;\n    },\n    setStrength(s) {\n      field.strength = s;\n      return field;\n    },\n\n    sample(x, z, time) {\n      const t = time ?? (manual ? uniforms.uWindTime.value : nowSeconds());\n      const phase = (x * direction.x + z * direction.y) * uniforms.uWindWaveK.value - t * waveSpeed;\n      const gustShape = 1 - gust + gust * (0.5 + 0.5 * Math.sin(phase));\n      const mag = strength * gustShape;\n      return new Vector2(direction.x * mag, direction.y * mag);\n    },\n\n    bind(material, opts = {}) {\n      const data = (material.userData ??= {}) as { __scenaWind?: boolean };\n      if (data.__scenaWind) return field;\n      data.__scenaWind = true;\n\n      const perMat = {\n        uWindHeight: { value: opts.height ?? 1 },\n        uWindStiff: { value: opts.stiffness ?? 1.6 },\n        uWindAnchor: { value: opts.anchor ?? 0 },\n      };\n      const prevCompile = material.onBeforeCompile;\n      // Capture the base cache key BEFORE overriding, so surface+wind stays a\n      // distinct program from plain+wind and from surface alone.\n      const baseKey = material.customProgramCacheKey ? material.customProgramCacheKey() : '';\n      material.onBeforeCompile = function (shader: PatchableShader, renderer: unknown) {\n        if (prevCompile) (prevCompile as (s: PatchableShader, r: unknown) => void).call(this, shader, renderer);\n        Object.assign(shader.uniforms, uniforms, perMat);\n        shader.vertexShader = shader.vertexShader\n          .replace('#include <common>', '#include <common>\\n' + WIND_UNIFORMS)\n          .replace('#include <begin_vertex>', '#include <begin_vertex>\\n' + WIND_BEGIN);\n      };\n      material.customProgramCacheKey = () => baseKey + '|scena-wind-v1';\n      material.needsUpdate = true;\n      patched.push(material);\n      return field;\n    },\n\n    attach(object) {\n      let mesh: Mesh | null = object instanceof Mesh ? object : null;\n      if (!mesh) {\n        object.traverse((o) => {\n          if (!mesh && o instanceof Mesh) mesh = o;\n        });\n      }\n      if (mesh) {\n        const target = mesh as Mesh;\n        const prev = target.onBeforeRender;\n        target.onBeforeRender = function (this: Mesh, ...args: unknown[]) {\n          if (prev) (prev as (...a: unknown[]) => void).apply(this, args);\n          if (!manual) uniforms.uWindTime.value = nowSeconds();\n        } as Mesh['onBeforeRender'];\n      }\n      return field;\n    },\n\n    sway(target, opts) {\n      const seen = new Set<Material>();\n      target.traverse((o) => {\n        if (!(o instanceof Mesh)) return;\n        const mats = Array.isArray(o.material) ? o.material : [o.material];\n        for (const m of mats) {\n          if (m && !seen.has(m)) {\n            seen.add(m);\n            field.bind(m, opts);\n          }\n        }\n      });\n      field.attach(target);\n      return field;\n    },\n\n    update(dt) {\n      manual = true;\n      uniforms.uWindTime.value += dt;\n    },\n  };\n\n  return field;\n}\n\n// --- Backwards-compatible convenience -----------------------------------\n\nexport interface WindOptions extends WindFieldOptions, SwayOptions {\n  /** @deprecated use `anchor`. Local height where sway begins. */\n  anchorHeight?: number;\n  /** Bind against an existing field instead of making a new one. */\n  field?: WindField;\n}\n\nexport type Wind = WindField;\n\n/**\n * Make everything under `target` sway in the wind — the one-call path. Builds\n * (or reuses) a {@link WindField}, binds every material and self-animates.\n * Returns the field, so you can `sample()` it, re-aim it, or share it with more\n * props. Kept `update()`-compatible for deterministic loops.\n *\n * ```ts\n * const wind = applyWind(forest.group, { strength: 0.3, height: 4, anchor: 1 });\n * // optional: game.onUpdate((t) => wind.update(t.delta));\n * ```\n */\nexport function applyWind(target: Object3D, options: WindOptions = {}): Wind {\n  const field = options.field ?? createWindField(options);\n  field.sway(target, {\n    height: options.height ?? 2.5,\n    stiffness: options.stiffness,\n    anchor: options.anchor ?? options.anchorHeight ?? 0,\n  });\n  return field;\n}\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  LineSegments,\n  Points,\n  ShaderMaterial,\n  Sphere,\n  Vector2,\n  Vector3,\n  type Material,\n  type Object3D,\n} from 'three';\nimport { Rng } from '../core/random';\nimport type { WindField } from './wind';\n\nexport type PrecipitationType = 'rain' | 'snow' | 'petal';\n\nexport interface PrecipitationOptions {\n  /** rain (slanted streaks), snow (drifting flakes) or petal (fluttering, spinning blossom/leaf fall). Default 'rain'. */\n  type?: PrecipitationType;\n  /** Particle count. Default 6000 (rain) / 3500 (snow) / 1400 (petal). */\n  count?: number;\n  /** Box size around the camera the weather fills, in metres. Default [55, 34, 55]. */\n  area?: number | [number, number, number];\n  /** How heavy, 0–1. Default 1. */\n  intensity?: number;\n  /** A WindField — rain slants and snow/petals drift along it. */\n  wind?: WindField;\n  /** How strongly the wind pushes the fall (metres/s per unit strength). Default 9 (rain) / 4 (snow) / 5 (petal). */\n  windInfluence?: number;\n  /** Fall speed, metres/s. Default 14 (rain) / 2.2 (snow) / 1.4 (petal). */\n  speed?: number;\n  /** Streak length (rain) or particle size in px (snow/petal). Default 0.5 / 9 / 11. */\n  size?: number;\n  /** Particle colour. Default light blue-grey (rain) / white (snow) / blossom pink (petal). */\n  color?: number;\n  /** Particle opacity. Default 0.5 (rain) / 0.85 (snow) / 0.9 (petal). */\n  opacity?: number;\n  seed?: number;\n}\n\ntype SurfaceUniforms = {\n  uSurfCap: { value: number };\n  uSurfCapColor: { value: Color };\n  uSurfCapUp: { value: number };\n  uSurfCapSharp: { value: number };\n  uSurfCapRough: { value: number };\n  uSurfWet: { value: number };\n  uSurfWetCling: { value: number };\n};\n\ninterface Accumulation {\n  entries: Array<{ material: Material; configured: boolean }>;\n  color: Color;\n  capUp: number;\n  max: number;\n  rate: number;\n}\n\ninterface Soaking {\n  entries: Array<{ material: Material; configured: boolean }>;\n  cling: number;\n  max: number;\n  rate: number;\n  dry: number;\n}\n\nexport interface Precipitation {\n  /** The renderable — add it to the scene. Follows the camera; never culled. */\n  object: Points | LineSegments;\n  /** The particle material. */\n  material: ShaderMaterial;\n  /** Set how heavy it falls, 0–1 (0 stops it). */\n  setIntensity(value: number): void;\n  /** Snow only: settle a white cap onto the surfaces under `target` as it falls. */\n  accumulate(target: Object3D, options?: SoakOptions & AccumulateOptions): Precipitation;\n  /** Rain only: wet the surfaces under `target` as it falls, and dry them after. */\n  soak(target: Object3D, options?: SoakOptions): Precipitation;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nexport interface SoakOptions {\n  /** How wet it gets at full intensity, 0–1. Default 0.9. */\n  max?: number;\n  /** Wetting speed (wetness per second). Default 0.22. */\n  rate?: number;\n  /**\n   * Drying speed once the rain eases (wetness per second). Default 0.045 —\n   * a fifth of the wetting rate, because a wall soaks in a minute and takes\n   * an hour to dry, and a puddle that vanishes the moment the rain stops\n   * reads as a bug rather than as weather.\n   */\n  dry?: number;\n  /** How well water clings to vertical faces, 0–1. Default 0.55. */\n  cling?: number;\n}\n\nexport interface AccumulateOptions {\n  /** Snow colour. Default 0xf4f8fc. */\n  color?: number;\n  /** How deep it settles, 0–1 (the cap strength). Default 0.85. */\n  max?: number;\n  /** Settle speed (cap per second). Default 0.08. */\n  rate?: number;\n  /** How up-facing a face must be to collect snow. Default 0.25. */\n  capUp?: number;\n}\n\nconst RAIN_VERT = /* glsl */ `\nuniform float uTime;\nuniform vec3  uArea;\nuniform vec2  uWind;\nuniform float uFall;\nuniform float uStreak;\nuniform float uIntensity;\nattribute float aEnd;\nvarying float vKeep;\nvoid main() {\n  vec3 home = position * uArea;\n  vKeep = step(fract(position.x * 91.7 + position.z * 47.3), uIntensity);\n  vec3 vel = vec3(uWind.x, -uFall, uWind.y);\n  vec3 p = home + vel * uTime;\n  vec3 halfA = uArea * 0.5;\n  vec3 world = mod(p - (cameraPosition - halfA), uArea) + (cameraPosition - halfA);\n  world -= normalize(vel) * (uStreak * aEnd);      // tail trails up the velocity\n  gl_Position = projectionMatrix * viewMatrix * vec4(world, 1.0);\n  if (vKeep < 0.5) gl_Position = vec4(2.0, 2.0, 2.0, 1.0);\n}\n`;\n\nconst SNOW_VERT = /* glsl */ `\nuniform float uTime;\nuniform vec3  uArea;\nuniform vec2  uWind;\nuniform float uFall;\nuniform float uSize;\nuniform float uIntensity;\nvarying float vKeep;\nvoid main() {\n  vec3 home = position * uArea;\n  vKeep = step(fract(position.x * 91.7 + position.z * 47.3), uIntensity);\n  vec3 p = home;\n  p.y -= uFall * uTime;\n  p.x += uWind.x * uTime + sin(uTime * 1.3 + home.y * 3.1) * 0.6;   // lateral wobble\n  p.z += uWind.y * uTime + cos(uTime * 1.1 + home.x * 3.7) * 0.6;\n  vec3 halfA = uArea * 0.5;\n  vec3 world = mod(p - (cameraPosition - halfA), uArea) + (cameraPosition - halfA);\n  vec4 mv = viewMatrix * vec4(world, 1.0);\n  gl_Position = projectionMatrix * mv;\n  // Clamp so flakes near the camera don't balloon into a white veil.\n  gl_PointSize = vKeep * min(uSize * (320.0 / max(-mv.z, 1.0)), 16.0);\n  if (vKeep < 0.5) gl_Position = vec4(2.0);\n}\n`;\n\nconst RAIN_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uOpacity;\nvarying float vKeep;\nvoid main() {\n  if (vKeep < 0.5) discard;\n  gl_FragColor = vec4(uColor, uOpacity);\n}\n`;\n\nconst SNOW_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uOpacity;\nvarying float vKeep;\nvoid main() {\n  if (vKeep < 0.5) discard;\n  vec2 c = gl_PointCoord - 0.5;\n  float d = 1.0 - smoothstep(0.15, 0.5, length(c));   // soft round flake\n  if (d <= 0.0) discard;\n  gl_FragColor = vec4(uColor, uOpacity * d);\n}\n`;\n\n// Petals/leaves: drift like snow but flutter wide and spin as they fall.\nconst PETAL_VERT = /* glsl */ `\nuniform float uTime;\nuniform vec3  uArea;\nuniform vec2  uWind;\nuniform float uFall;\nuniform float uSize;\nuniform float uIntensity;\nvarying float vKeep;\nvarying float vSpin;\nvoid main() {\n  vec3 home = position * uArea;\n  vKeep = step(fract(position.x * 91.7 + position.z * 47.3), uIntensity);\n  float seed = fract(position.y * 57.3 + position.x * 13.1) * 6.2831;\n  vec3 p = home;\n  p.y -= uFall * uTime;\n  // Wide, lazy flutter — petals swing far more than a snowflake wobbles.\n  p.x += uWind.x * uTime + sin(uTime * 1.6 + home.y * 3.1 + seed) * 1.4;\n  p.z += uWind.y * uTime + cos(uTime * 1.3 + home.x * 3.7 + seed) * 1.4;\n  vec3 halfA = uArea * 0.5;\n  vec3 world = mod(p - (cameraPosition - halfA), uArea) + (cameraPosition - halfA);\n  vec4 mv = viewMatrix * vec4(world, 1.0);\n  gl_Position = projectionMatrix * mv;\n  gl_PointSize = vKeep * min(uSize * (320.0 / max(-mv.z, 1.0)), 18.0);\n  vSpin = uTime * 2.0 + seed;\n  if (vKeep < 0.5) gl_Position = vec4(2.0);\n}\n`;\n\nconst PETAL_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uOpacity;\nvarying float vKeep;\nvarying float vSpin;\nvoid main() {\n  if (vKeep < 0.5) discard;\n  vec2 c = gl_PointCoord - 0.5;\n  float cs = cos(vSpin), sn = sin(vSpin);\n  c = mat2(cs, -sn, sn, cs) * c;   // spin the petal\n  c.x *= 1.7;                       // squash to an oval petal\n  float a = (1.0 - smoothstep(0.28, 0.5, length(c))) * uOpacity;\n  if (a <= 0.0) discard;\n  gl_FragColor = vec4(uColor, a);\n}\n`;\n\nfunction toArea(a: number | [number, number, number] | undefined, fallback: Vector3): Vector3 {\n  if (a === undefined) return fallback;\n  if (typeof a === 'number') return new Vector3(a, a, a);\n  return new Vector3(a[0], a[1], a[2]);\n}\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * GPU-driven rain or snow that follows the camera — a finite cloud of particles\n * wrapped into an infinite fall. Every particle's position is computed in the\n * vertex shader from a fixed seed plus the clock, so there is **no per-particle\n * CPU work**: thousands of drops cost one draw call and one uniform update. Rain\n * falls as slanted streaks, snow as soft drifting flakes, both leaning along a\n * {@link WindField} if you pass one. It self-animates from the render loop.\n *\n * Snow can `accumulate` — settling a white cap onto the surfaces below, reusing\n * the surface system's up-facing cap, so roofs and ground whiten as it falls.\n *\n * ```ts\n * const snow = createPrecipitation({ type: 'snow', wind });\n * scene.add(snow.object);\n * snow.accumulate(scene);            // roofs & ground gather snow\n * ```\n */\nexport function createPrecipitation(options: PrecipitationOptions = {}): Precipitation {\n  const type = options.type ?? 'rain';\n  const isRain = type === 'rain';\n  const isPetal = type === 'petal';\n  const count = options.count ?? (isRain ? 6000 : isPetal ? 1400 : 3500);\n  const area = toArea(options.area, new Vector3(55, 34, 55));\n  const speed = options.speed ?? (isRain ? 14 : isPetal ? 1.4 : 2.2);\n  const size = options.size ?? (isRain ? 0.5 : isPetal ? 11 : 9);\n  const windInfluence = options.windInfluence ?? (isRain ? 9 : isPetal ? 5 : 4);\n  const rng = new Rng(options.seed ?? 1);\n  const wind = options.wind;\n\n  // Geometry: `position` holds each particle's home (0..1). Rain doubles up\n  // (head + tail) into line segments; snow is one point per flake.\n  const geometry = new BufferGeometry();\n  const verts = isRain ? count * 2 : count;\n  const pos = new Float32Array(verts * 3);\n  const ends = isRain ? new Float32Array(verts) : null;\n  for (let i = 0; i < count; i++) {\n    const hx = rng.next();\n    const hy = rng.next();\n    const hz = rng.next();\n    if (isRain) {\n      for (let e = 0; e < 2; e++) {\n        const v = i * 2 + e;\n        pos[v * 3] = hx;\n        pos[v * 3 + 1] = hy;\n        pos[v * 3 + 2] = hz;\n        ends![v] = e;\n      }\n    } else {\n      pos[i * 3] = hx;\n      pos[i * 3 + 1] = hy;\n      pos[i * 3 + 2] = hz;\n    }\n  }\n  geometry.setAttribute('position', new BufferAttribute(pos, 3));\n  if (ends) geometry.setAttribute('aEnd', new BufferAttribute(ends, 1));\n  // Follows the camera, so its real bounds are unknowable — never cull it.\n  geometry.boundingSphere = new Sphere(new Vector3(), 1e6);\n\n  const material = new ShaderMaterial({\n    vertexShader: isRain ? RAIN_VERT : isPetal ? PETAL_VERT : SNOW_VERT,\n    fragmentShader: isRain ? RAIN_FRAG : isPetal ? PETAL_FRAG : SNOW_FRAG,\n    transparent: true,\n    depthWrite: false,\n    uniforms: {\n      uTime: { value: 0 },\n      uArea: { value: area },\n      uWind: { value: new Vector2(0, 0) },\n      uFall: { value: speed },\n      uIntensity: { value: options.intensity ?? 1 },\n      uColor: { value: new Color(options.color ?? (isRain ? 0xafc4d8 : isPetal ? 0xf3c1d6 : 0xf4f8fc)) },\n      uOpacity: { value: options.opacity ?? (isRain ? 0.5 : isPetal ? 0.9 : 0.85) },\n      ...(isRain ? { uStreak: { value: size } } : { uSize: { value: size } }),\n    },\n  });\n\n  const object = isRain ? new LineSegments(geometry, material) : new Points(geometry, material);\n  object.frustumCulled = false;\n  object.name = `precipitation-${type}`;\n\n  let accum: Accumulation | null = null;\n  let soaking: Soaking | null = null;\n  let manual = false;\n  let last = nowSeconds();\n\n  const advance = (dt: number): void => {\n    if (wind) {\n      const dir = wind.uniforms.uWindDir.value as Vector2;\n      const s = (wind.uniforms.uWindStrength.value as number) * windInfluence;\n      (material.uniforms.uWind.value as Vector2).set(dir.x * s, dir.y * s);\n    }\n    if (accum) {\n      const target = accum.max * (material.uniforms.uIntensity.value as number);\n      for (const e of accum.entries) {\n        const data = e.material.userData as {\n          scenaSurface?: SurfaceUniforms;\n          scenaShader?: { uniforms: SurfaceUniforms };\n        };\n        // The compiled shader's uniforms are what three actually uploads; the\n        // pre-compile copy is the fallback before first render. Write both so\n        // they stay in step.\n        const live = data.scenaShader?.uniforms;\n        const base = data.scenaSurface;\n        const next = Math.min(target, (base?.uSurfCap.value ?? 0) + accum.rate * dt);\n        for (const u of [base, live]) {\n          if (!u) continue;\n          if (!e.configured) {\n            u.uSurfCapColor.value.copy(accum.color);\n            u.uSurfCapUp.value = accum.capUp;\n            u.uSurfCapSharp.value = 0.3;\n            u.uSurfCapRough.value = 0.9;\n          }\n          u.uSurfCap.value = next;\n        }\n        if (live) e.configured = true;\n      }\n    }\n    if (soaking) {\n      // Wetting is driven by how hard it is raining; DRYING happens whenever\n      // the surface is wetter than the weather justifies, at its own much\n      // slower rate. That asymmetry is the whole effect: the street goes\n      // dark in a minute and takes a long time to come back.\n      const target = soaking.max * (material.uniforms.uIntensity.value as number);\n      for (const e of soaking.entries) {\n        const data = e.material.userData as {\n          scenaSurface?: SurfaceUniforms;\n          scenaShader?: { uniforms: SurfaceUniforms };\n        };\n        const live = data.scenaShader?.uniforms;\n        const base = data.scenaSurface;\n        const now = base?.uSurfWet.value ?? 0;\n        const next =\n          now < target\n            ? Math.min(target, now + soaking.rate * dt)\n            : Math.max(target, now - soaking.dry * dt);\n        for (const u of [base, live]) {\n          if (!u) continue;\n          if (!e.configured) u.uSurfWetCling.value = soaking.cling;\n          u.uSurfWet.value = next;\n        }\n        if (live) e.configured = true;\n      }\n    }\n  };\n\n  object.onBeforeRender = () => {\n    const t = nowSeconds();\n    const dt = Math.min(0.1, Math.max(0, t - last));\n    last = t;\n    if (!manual) {\n      material.uniforms.uTime.value = t % 1000;\n      advance(dt);\n    }\n  };\n\n  const precip: Precipitation = {\n    object,\n    material,\n    setIntensity(value) {\n      material.uniforms.uIntensity.value = Math.max(0, Math.min(1, value));\n    },\n    soak(target, opts = {}) {\n      if (!isRain) return precip; // only rain wets things\n      const entries: Soaking['entries'] = [];\n      target.traverse((o) => {\n        const mats = ((o as { material?: Material | Material[] }).material ?? []) as Material | Material[];\n        for (const m of Array.isArray(mats) ? mats : [mats]) {\n          const u = (m.userData as { scenaSurface?: SurfaceUniforms } | undefined)?.scenaSurface;\n          if (u && u.uSurfWet) entries.push({ material: m, configured: false });\n        }\n      });\n      soaking = {\n        entries,\n        cling: opts.cling ?? 0.55,\n        max: opts.max ?? 0.9,\n        rate: opts.rate ?? 0.22,\n        dry: opts.dry ?? 0.045,\n      };\n      return precip;\n    },\n    accumulate(target, opts = {}) {\n      if (type !== 'snow') return precip; // only snow settles\n      const entries: Accumulation['entries'] = [];\n      target.traverse((o) => {\n        const mats = ((o as { material?: Material | Material[] }).material ?? []) as Material | Material[];\n        for (const m of Array.isArray(mats) ? mats : [mats]) {\n          const u = (m.userData as { scenaSurface?: SurfaceUniforms } | undefined)?.scenaSurface;\n          // Only settle on plain (un-capped) surfaces, so we never fight an\n          // existing snow/moss cap.\n          if (u && u.uSurfCap && u.uSurfCap.value === 0) entries.push({ material: m, configured: false });\n        }\n      });\n      accum = {\n        entries,\n        color: new Color(opts.color ?? 0xf4f8fc),\n        capUp: opts.capUp ?? 0.25,\n        max: opts.max ?? 0.85,\n        rate: opts.rate ?? 0.08,\n      };\n      return precip;\n    },\n    update(dt) {\n      manual = true;\n      material.uniforms.uTime.value += dt;\n      advance(dt);\n    },\n  };\n\n  return precip;\n}\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  Mesh,\n  MeshStandardMaterial,\n  Vector2,\n  Vector4,\n} from 'three';\nimport type { WindField } from './wind';\n\n/**\n * The surf zone — what turns a coloured plane into a coast.\n *\n * Two effects, on one clock so they agree: **breakers** (bands of\n * whitewater that form where the swell trips on the bottom and run\n * shoreward) and the **swash** (the waterline itself running up the beach\n * and draining back, leaving a mirror-thin sheet behind it).\n */\nexport interface SurfOptions {\n  /** Water depth at which the swell trips and whitens, metres. Default 1.7. */\n  breakDepth?: number;\n  /**\n   * How far up the beach the water runs, in metres of DEPTH — the edge's\n   * travel is this divided by the beach slope, so 0.42 m on a 1-in-7 face\n   * is nearly three metres of moving waterline. Default 0.42.\n   */\n  runUp?: number;\n  /** Seconds per swash cycle, in and back out again. Default 8. */\n  period?: number;\n  /** Breaker lines per metre of depth — more = tighter surf. Default 2.4. */\n  bands?: number;\n}\n\n/** Fine surface chop: the detail that makes a sea read as liquid. */\nexport interface RippleOptions {\n  /** Normal perturbation, 0–1. Default 0.34; past ~0.6 it reads as fur. */\n  strength?: number;\n  /** Ripples per metre. Bigger = finer chop. Default 0.85. */\n  scale?: number;\n}\n\nexport interface OceanOptions {\n  /** World-space sea level (the plane's Y). Default 0. */\n  level?: number;\n  /** Plane extent in metres. Default 240. */\n  size?: number;\n  /** Grid subdivisions per side (more = smoother crests). Default 180. */\n  segments?: number;\n  /** Overall wave height in metres. Default 0.5. */\n  amplitude?: number;\n  /** Crest sharpness, 0–1 (0 = rolling swell, 1 = peaked chop). Default 0.75. */\n  choppiness?: number;\n  /** Wavelength of the primary swell, metres. Default 26. */\n  wavelength?: number;\n  /** Wave heading in degrees when no wind is given. Default 30. */\n  direction?: number;\n  /** Phase-speed multiplier. Default 1. */\n  speed?: number;\n  /** A WindField — the swell turns downwind and grows with the wind. */\n  wind?: WindField;\n  /** Storm surge, 0–1, or a live source (`() => weather.storminess`): whips up\n   *  bigger, choppier, foamier, darker seas and raises the sea level. Default 0. */\n  storm?: number | (() => number);\n  /** Sea-level rise at full storm, metres (the surge). Default 1.2. */\n  surge?: number;\n  /** Terrain height sampler (`terrain.heightAt`): the ocean fades out over land and foams at the shore. */\n  shore?: (x: number, z: number) => number;\n  /**\n   * The surf zone: breakers that run shoreward, and a waterline that runs\n   * UP the beach and drains back. Needs a `shore` — without one there is\n   * no beach to break on and every term is inert. `false` turns it off.\n   */\n  surf?: false | SurfOptions;\n  /**\n   * How deep the water goes before it reads as open sea, metres. This is\n   * the width of the TURQUOISE SHELF — on a 1-in-7 beach, a shoal depth of\n   * 12 puts eighty metres of bright water between the sand and the blue,\n   * which is most of what a tropical coast actually is. Default 3.\n   */\n  shoalDepth?: number;\n  /**\n   * Fine chop riding on the swell. Four Gerstner waves give a sea its\n   * shape, but a swell alone is a rolling sheet — what reads as WATER is\n   * the ripple breaking the light into moving highlights. `false` for\n   * glass (a lagoon, a harbour at dawn).\n   */\n  ripples?: false | RippleOptions;\n  /**\n   * How see-through the shallows are, 0–1. Clear water IS its bottom: at\n   * 0.8 you read the sand, the reef and the fish through the turquoise,\n   * and the colour deepens to opaque as the floor drops away. Default 0\n   * — an opaque sheet, which is right for a grey sea and cheaper.\n   */\n  clarity?: number;\n  /**\n   * A live sea state — `() => seaState.trains`.\n   *\n   * Structurally `SeaState.trains`, duck-typed like `storm`, so the ocean\n   * knows nothing about fetch or wind history. Given one, the four wave\n   * components SPLIT: two of them run with the wind sea and two with the\n   * swell, on their own headings and their own wavelengths. That is a cross\n   * sea, and it is what makes the surface stop looking like one wave train\n   * with some noise on it.\n   *\n   * It overrides `amplitude`, `wavelength`, `direction` and any `wind`.\n   */\n  sea?: () => {\n    windSea: { height: number; period: number; length: number; from: number };\n    swell: { height: number; period: number; length: number; from: number };\n  };\n  /** Deep-water colour. Default 0x184a63. */\n  deepColor?: number;\n  /** Shallow / shoreward colour. Default 0x3f8fa6. */\n  shallowColor?: number;\n  /** Sky colour reflected at grazing angles (fresnel). Default 0xbcd4e6. */\n  skyColor?: number;\n}\n\nexport interface Ocean {\n  /** The ocean surface — add it to the scene. */\n  mesh: Mesh;\n  /** Sea level (the plane's Y). */\n  level: number;\n  /** The wave height at a world point (and time) — sit a boat on this to bob it. */\n  heightAt(x: number, z: number, time?: number): number;\n  /**\n   * The swash's run-up right now, in metres of extra depth — positive while\n   * the water is running up the beach, negative while it drains. Add it to a\n   * depth reading and gameplay agrees with what the shader is drawing: a\n   * wader gets caught by the wave that visibly arrives.\n   */\n  readonly runUp: number;\n  /**\n   * How deep the water is over ground of height `groundY`, right now,\n   * including the swash. 0 where the sea has drained away — so a player\n   * walking the edge is in and out of the water as the waves come.\n   */\n  depthOver(groundY: number): number;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nconst N = 4;\nconst G = 9.8;\n// Per-wave layout, relative to the swell heading: angular spread, wavelength\n// factor and amplitude falloff. A little detuned so the sum never repeats.\nconst REL_ANGLE = [0, 0.34, -0.52, 0.82];\nconst LEN_FACTOR = [1, 0.55, 0.32, 0.19];\nconst AMP_FACTOR = [1, 0.52, 0.3, 0.17];\n\ninterface Wave {\n  w: number; // wavenumber\n  amp: number; // base amplitude\n  speed: number; // temporal phase speed\n}\n\nconst WAVE_UNIFORMS = /* glsl */ `\nuniform float uTime;\nuniform float uStorm;\nuniform vec2  uWaveDir[${N}];\nuniform vec4  uWaveParams[${N}];  // (wavenumber, amplitude, steepness Q, phase speed)\n`;\n\n// Gerstner sum: circular vertex motion so crests sharpen and troughs flatten.\n// Computed in beginnormal_vertex (before transformed exists) off `position`,\n// so the analytic normal is ready for lighting; begin_vertex just applies it.\nconst OCEAN_BEGINNORMAL = /* glsl */ `\nvec3 scenaDisp = vec3(0.0);\nfloat scNx = 0.0, scNy = 0.0, scNz = 0.0, scCrest = 0.0;\nfor (int i = 0; i < ${N}; i++) {\n  vec2 D = uWaveDir[i];\n  float w = uWaveParams[i].x, A = uWaveParams[i].y, Q = uWaveParams[i].z, spd = uWaveParams[i].w;\n  float ph = dot(D, position.xz) * w + uTime * spd;\n  float c = cos(ph), s = sin(ph);\n  scenaDisp.x += Q * A * D.x * c;\n  scenaDisp.z += Q * A * D.y * c;\n  scenaDisp.y += A * s;\n  float wa = w * A;\n  scNx += D.x * wa * c;\n  scNz += D.y * wa * c;\n  scNy += Q * wa * s;\n  scCrest += Q * wa * s;\n}\nobjectNormal = normalize(vec3(-scNx, 1.0 - scNy, -scNz));\n// Whitecaps where the sum folds — a storm broadens them across the crests.\nvOceanFoam = smoothstep(mix(0.5, 0.18, uStorm), 1.0, scCrest);\n`;\n\nconst OCEAN_BEGIN = /* glsl */ `\ntransformed += scenaDisp;\nvOceanShore = aOceanShore;\n// World position, so the ripple field lives in the WORLD and not on the\n// mesh: ripples pinned to UVs slide about when the plane moves.\nvOceanWorld = (modelMatrix * vec4(transformed, 1.0)).xyz;\n`;\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * A Gerstner-wave ocean — the sea that makes a coast feel like a coast. The\n * surface is a subdivided plane displaced by a sum of Gerstner waves in the\n * vertex shader, so crests peak and troughs flatten like real swell, with\n * analytic normals for the light and **whitecap foam** where the waves fold.\n * It patches a `MeshStandardMaterial`, so PBR lighting, shadows and fog all\n * survive, and a fresnel term tints the surface with the sky at grazing angles.\n *\n * Pass a {@link WindField} and the swell turns downwind and grows with the wind.\n * Pass a terrain `heightAt` as `shore` and the ocean **fades out over land and\n * foams along the waterline** — the same handshake the rest of SCENA uses.\n *\n * `heightAt(x, z)` gives the wave height on the CPU, so a boat or buoy can ride\n * the swell — the buoyancy handshake, mirroring `terrain.heightAt`.\n *\n * ```ts\n * const ocean = createOcean({ level: 0, wind, shore: terrain.heightAt });\n * scene.add(ocean.mesh);\n * boat.position.y = ocean.heightAt(boat.position.x, boat.position.z);\n * ```\n */\nexport function createOcean(options: OceanOptions = {}): Ocean {\n  const level = options.level ?? 0;\n  const size = options.size ?? 240;\n  const segments = options.segments ?? 180;\n  const amplitude = options.amplitude ?? 0.5;\n  const choppiness = Math.max(0, Math.min(1, options.choppiness ?? 0.75));\n  const baseLen = options.wavelength ?? 26;\n  const speedMul = options.speed ?? 1;\n  const wind = options.wind;\n  const shore = options.shore;\n  const surf = options.surf;\n  const ripples = options.ripples;\n  const clarity = Math.max(0, Math.min(1, options.clarity ?? 0));\n  const surge = options.surge ?? 1.2;\n  const stormSrc =\n    typeof options.storm === 'function'\n      ? options.storm\n      : options.storm !== undefined\n        ? () => options.storm as number\n        : null;\n\n  // --- geometry: an XZ grid at the origin (local space = world), with a\n  // per-vertex shore depth (level − terrain height) baked from the handshake.\n  const geometry = new BufferGeometry();\n  const cols = segments + 1;\n  const pos = new Float32Array(cols * cols * 3);\n  const nrm = new Float32Array(cols * cols * 3);\n  const uv = new Float32Array(cols * cols * 2);\n  const shoreAttr = new Float32Array(cols * cols);\n  for (let iz = 0; iz < cols; iz++) {\n    for (let ix = 0; ix < cols; ix++) {\n      const k = iz * cols + ix;\n      const x = (ix / segments - 0.5) * size;\n      const z = (iz / segments - 0.5) * size;\n      pos[k * 3] = x;\n      pos[k * 3 + 1] = 0;\n      pos[k * 3 + 2] = z;\n      nrm[k * 3 + 1] = 1;\n      uv[k * 2] = ix / segments;\n      uv[k * 2 + 1] = iz / segments;\n      shoreAttr[k] = shore ? level - shore(x, z) : 999;\n    }\n  }\n  const index: number[] = [];\n  for (let iz = 0; iz < segments; iz++) {\n    for (let ix = 0; ix < segments; ix++) {\n      const a = iz * cols + ix;\n      const b = a + 1;\n      const c = a + cols;\n      const d = c + 1;\n      index.push(a, c, b, b, c, d);\n    }\n  }\n  geometry.setAttribute('position', new BufferAttribute(pos, 3));\n  geometry.setAttribute('normal', new BufferAttribute(nrm, 3));\n  geometry.setAttribute('uv', new BufferAttribute(uv, 2));\n  geometry.setAttribute('aOceanShore', new BufferAttribute(shoreAttr, 1));\n  geometry.setIndex(index);\n  geometry.computeBoundingSphere();\n\n  // --- waves: derived from the swell, detuned per component.\n  const waves: Wave[] = [];\n  for (let i = 0; i < N; i++) {\n    const w = (Math.PI * 2) / (baseLen * LEN_FACTOR[i]);\n    waves.push({ w, amp: amplitude * AMP_FACTOR[i], speed: Math.sqrt(G * w) * speedMul });\n  }\n\n  const uWaveDir = Array.from({ length: N }, () => new Vector2(1, 0));\n  const uWaveParams = Array.from({ length: N }, () => new Vector4(0, 0, 0, 0));\n  // Current absolute per-wave state, mirrored for the CPU heightAt().\n  const curDir = Array.from({ length: N }, () => new Vector2(1, 0));\n  const curAmp = new Float32Array(N);\n  const curW: number[] = new Array(N).fill(0);\n  const curSpd: number[] = new Array(N).fill(0);\n\n  const uniforms = {\n    uTime: { value: 0 },\n    uWaveDir: { value: uWaveDir },\n    uWaveParams: { value: uWaveParams },\n    uDeepColor: { value: new Color(options.deepColor ?? 0x184a63) },\n    uShallowColor: { value: new Color(options.shallowColor ?? 0x3f8fa6) },\n    uSkyColor: { value: new Color(options.skyColor ?? 0xbcd4e6) },\n    // How deep the water goes before it reads as open sea. Bigger = a\n    // wider turquoise shelf, which is most of what a tropical coast IS.\n    uShoalDepth: { value: options.shoalDepth ?? 3.0 },\n    uClarity: { value: clarity },\n    uFlow: { value: new Vector2(0.06, 0.04) },\n    uRipple: { value: ripples === false ? 0 : (ripples?.strength ?? 0.34) },\n    uRippleScale: { value: ripples === false ? 1 : (ripples?.scale ?? 0.85) },\n    uFoamBand: { value: 0.6 },\n    uStorm: { value: 0 },\n    uSurge: { value: 0 },\n    // The surf zone. Inert without a `shore`: out there the shore depth is\n    // 999, so every one of these terms multiplies out to nothing.\n    uBreakDepth: { value: surf === false ? 0 : (surf?.breakDepth ?? 1.7) },\n    uSwash: { value: surf === false ? 0 : (surf?.runUp ?? 0.42) },\n    uSwashPeriod: { value: Math.max(1, (surf === false ? 8 : surf?.period) ?? 8) },\n    uSurfBands: { value: surf === false ? 0 : (surf?.bands ?? 2.4) },\n  };\n  // The current sea level, lifted by the surge — mirrored into heightAt so\n  // boats ride the rising water, not just the base level.\n  let curLevel = level;\n\n  // Reproject the wave set for the current heading + wind strength + storm.\n  const seaSrc = options.sea;\n\n  const retune = (): void => {\n    let heading = ((options.direction ?? 30) * Math.PI) / 180;\n    let ampScale = 1;\n    if (wind) {\n      const d = wind.uniforms.uWindDir.value as Vector2;\n      heading = Math.atan2(d.y, d.x);\n      ampScale = Math.max(0.55, Math.min(1.7, 0.55 + (wind.uniforms.uWindStrength.value as number) * 2.4));\n    }\n    // Storm surge: taller, steeper seas and a raised waterline.\n    const sm = stormSrc ? Math.max(0, Math.min(1, stormSrc())) : 0;\n    const stormAmp = 1 + sm * 2.2; // up to ~3.2× at full storm\n    const chop = Math.min(1, choppiness + sm * (1 - choppiness));\n    uniforms.uStorm.value = sm;\n    uniforms.uSurge.value = surge * sm;\n    curLevel = level + surge * sm;\n    // TWO TRAINS, on their own headings, when a sea state is driving.\n    //\n    // The shader has always taken a direction PER WAVE; it was only this loop\n    // that put all four of them on one heading. Splitting them costs nothing\n    // and it is the difference between a sea and a wave.\n    const seaNow = seaSrc ? seaSrc() : null;\n    for (let i = 0; i < N; i++) {\n      let dirRad: number;\n      let amp: number;\n      let w: number;\n      let spd: number;\n      if (seaNow) {\n        // 0,1 the wind sea; 2,3 the swell. Each pair keeps its own little\n        // angular spread so neither train is a single mathematical line.\n        const t = i < 2 ? seaNow.windSea : seaNow.swell;\n        const sub = i % 2;\n        // `from` is where it COMES FROM, the way a sailor says it; a wave\n        // travels the other way, and getting this backwards makes every sea\n        // in the library run into the wind.\n        dirRad = ((t.from + 180) * Math.PI) / 180 + (sub === 0 ? 0 : REL_ANGLE[1] * 0.8);\n        // Significant height is the average of the highest third; the\n        // amplitude of one component is a good deal less than half of it.\n        amp = Math.max(0, t.height) * (sub === 0 ? 0.32 : 0.17) * stormAmp;\n        const len = Math.max(2, t.length || 26) * (sub === 0 ? 1 : 0.58);\n        w = (Math.PI * 2) / len;\n        spd = Math.sqrt(G / w) * w * speedMul;\n      } else {\n        dirRad = heading + REL_ANGLE[i];\n        amp = waves[i].amp * ampScale * stormAmp;\n        w = waves[i].w;\n        spd = waves[i].speed;\n      }\n      curDir[i].set(Math.cos(dirRad), Math.sin(dirRad));\n      uWaveDir[i].copy(curDir[i]);\n      curAmp[i] = amp;\n      curW[i] = w;\n      curSpd[i] = spd;\n      // Steepness kept so Σ Q·w·A ≤ chop ≤ 1 (no self-intersection).\n      const q = Math.min(chop / (w * amp * N || 1), 0.98 / (w * amp || 1));\n      const p = uniforms.uWaveParams.value[i] as { x: number; y: number; z: number; w: number };\n      p.x = w;\n      p.y = amp;\n      p.z = q;\n      p.w = spd;\n    }\n  };\n  retune();\n\n  const material = new MeshStandardMaterial({\n    color: 0x2a6b82,\n    metalness: 0.0,\n    roughness: 0.18,\n    // Only pay for blending when somebody asked to see through the water.\n    // depthWrite stays ON: this is one surface over opaque ground, and\n    // turning it off lets the sea floor draw over the sea.\n    transparent: clarity > 0,\n  });\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.vertexShader = shader.vertexShader\n      .replace(\n        '#include <common>',\n        `#include <common>\\n${WAVE_UNIFORMS}\\nattribute float aOceanShore;\\nvarying float vOceanFoam;\\nvarying float vOceanShore;\\nvarying vec3 vOceanWorld;`\n      )\n      .replace('#include <beginnormal_vertex>', `#include <beginnormal_vertex>\\n${OCEAN_BEGINNORMAL}`)\n      .replace('#include <begin_vertex>', `#include <begin_vertex>\\n${OCEAN_BEGIN}`);\n    shader.fragmentShader = shader.fragmentShader\n      .replace(\n        '#include <common>',\n        `#include <common>\\nuniform float uTime;\\nuniform vec3 uDeepColor;\\nuniform vec3 uShallowColor;\\nuniform vec3 uSkyColor;\\nuniform float uShoalDepth;\\nuniform float uFoamBand;\\nuniform float uStorm;\\nuniform float uSurge;\\nuniform float uBreakDepth;\\nuniform float uSwash;\\nuniform float uSwashPeriod;\\nuniform float uSurfBands;\\nuniform float uClarity;\\nuniform vec2 uFlow;\\nuniform float uRipple;\\nuniform float uRippleScale;\\nvarying float vOceanFoam;\\nvarying float vOceanShore;\\nvarying vec3 vOceanWorld;`\n      )\n      .replace(\n        '#include <map_fragment>',\n        `#include <map_fragment>\n        // THE SWASH: the whole waterline runs up the beach and drains back.\n        // Adding the run-up to the shore depth moves the edge itself — on a\n        // 1-in-7 beach face, 40 cm of water is nearly three metres of travel,\n        // which is what makes a coast stop looking like a painted line.\n        float swashPhase = uTime * 6.2831853 / uSwashPeriod;\n        float runUp = uSwash * sin(swashPhase);\n        // The surge lifts the waterline, so a storm floods higher up the beach.\n        float shoreD = vOceanShore + uSurge + runUp;\n        if (shoreD < -0.06) discard;             // terrain stands above the sea here\n        float shoal = clamp(shoreD / uShoalDepth, 0.0, 1.0);\n        // Shallow water is bright out of all proportion to its depth, so the\n        // turquoise hugs the shore instead of ramping linearly to blue.\n        diffuseColor.rgb = mix(uShallowColor, uDeepColor, pow(shoal, 0.72));\n        // THE BREAKERS: bands of whitewater that form where the swell trips\n        // on the bottom and run SHOREWARD, brightening as they shallow. The\n        // phase runs with the swash, so the water arrives after a wave breaks\n        // rather than breathing on a clock of its own.\n        float surfZone = 1.0 - smoothstep(0.0, max(uBreakDepth, 0.0001), shoreD);\n        float bands = sin(shoreD * uSurfBands + swashPhase * 2.0);\n        float breakers = smoothstep(0.1, 0.85, bands) * surfZone * surfZone;\n        float shoreFoam = (1.0 - smoothstep(0.0, uFoamBand, shoreD)) * step(0.0, shoreD);\n        float oceanFoam = clamp(max(vOceanFoam, max(shoreFoam, breakers)), 0.0, 1.0);\n        diffuseColor.rgb = mix(diffuseColor.rgb, vec3(0.94, 0.96, 0.97), oceanFoam);\n        // CLARITY. Shallow water shows its floor and deep water does not —\n        // the transition is the whole reason a reef reads as a reef and not\n        // a painted circle. Foam stays opaque: froth is not glass.\n        float seeThrough = uClarity * (1.0 - smoothstep(0.0, uShoalDepth * 0.6, shoreD));\n        diffuseColor.a = max(1.0 - seeThrough * 0.72, oceanFoam);\n        // A storm darkens and greys the water between the whitecaps.\n        diffuseColor.rgb = mix(diffuseColor.rgb, vec3(0.13, 0.18, 0.2), uStorm * 0.45 * (1.0 - oceanFoam));`\n      )\n      .replace(\n        '#include <normal_fragment_maps>',\n        `#include <normal_fragment_maps>\n        {\n          // RIPPLES. The four Gerstner waves give the sea its swell, but a\n          // swell alone is a rolling sheet — what reads as WATER is the fine\n          // chop riding on it, breaking the light into a thousand moving\n          // highlights. Three crossing trains at different scales, analytic\n          // derivatives, drifting with the swell so the surface flows one\n          // way like a real sea instead of shimmering in place.\n          vec2 wp = vOceanWorld.xz * uRippleScale;\n          vec2 drift = uFlow * uTime;\n          float p1 = dot(wp, vec2(0.92, 0.39)) + drift.x * 1.7;\n          float p2 = dot(wp, vec2(-0.45, 0.89)) * 1.9 - drift.y * 2.1;\n          float p3 = dot(wp, vec2(0.66, -0.75)) * 3.7 + (drift.x + drift.y) * 2.9;\n          // dh/dx and dh/dz of the summed trains — the slope IS the normal.\n          float dx = 0.92 * cos(p1) + (-0.45 * 1.9) * 0.55 * cos(p2) + (0.66 * 3.7) * 0.22 * cos(p3);\n          float dz = 0.39 * cos(p1) + (0.89 * 1.9) * 0.55 * cos(p2) + (-0.75 * 3.7) * 0.22 * cos(p3);\n          // Fade with distance or the chop aliases into a shimmering mess,\n          // and drop it inside foam, which is froth and has no facets.\n          float near = 1.0 - smoothstep(45.0, 260.0, length(vViewPosition));\n          float amp = uRipple * near * (1.0 - oceanFoam);\n          normal = normalize(normal + vec3(-dx, 0.0, -dz) * amp);\n          // The glitter: a rippled surface is never uniformly polished.\n          roughnessFactor = clamp(roughnessFactor + (abs(dx) + abs(dz)) * 0.04 * amp, 0.02, 1.0);\n\n          // View-space fresnel: tint toward the sky at grazing angles.\n          float fres = pow(1.0 - max(dot(normal, normalize(vViewPosition)), 0.0), 5.0);\n          diffuseColor.rgb = mix(diffuseColor.rgb, uSkyColor, fres * 0.5 * (1.0 - oceanFoam));\n        }`\n      )\n      .replace(\n        '#include <roughnessmap_fragment>',\n        `#include <roughnessmap_fragment>\n        roughnessFactor = mix(roughnessFactor, 0.85, oceanFoam);\n        // The drain sheet: the thinnest water left behind is the most\n        // mirror-like surface on a beach, and reading it as glass is what\n        // separates a wet coast from a coloured plane.\n        float sheet = (1.0 - smoothstep(0.0, 0.35, shoreD)) * step(0.0, shoreD) * (1.0 - oceanFoam);\n        roughnessFactor = mix(roughnessFactor, 0.04, sheet);`\n      );\n  };\n  material.customProgramCacheKey = () => 'scena-ocean-v3';\n\n  const mesh = new Mesh(geometry, material);\n  mesh.name = 'ocean';\n  mesh.position.y = level;\n  mesh.frustumCulled = false;\n\n  let manual = false;\n  mesh.onBeforeRender = () => {\n    if (!manual) {\n      uniforms.uTime.value = nowSeconds();\n      retune();\n      mesh.position.y = curLevel; // ride the surge up\n    }\n  };\n\n  const heightAt = (x: number, z: number, time?: number): number => {\n    const t = time ?? uniforms.uTime.value;\n    let y = 0;\n    for (let i = 0; i < N; i++) {\n      // curW and curSpd, NOT waves[i] — a sea state changes the wavelength as\n      // it builds, and reading the construction-time value here floats every\n      // boat on a sea nobody can see while the mesh shows another one.\n      y += curAmp[i] * Math.sin((curDir[i].x * x + curDir[i].y * z) * curW[i] + t * curSpd[i]);\n    }\n    return curLevel + y;\n  };\n\n  /** The same run-up the shader is drawing this frame — one clock, one truth. */\n  const runUpNow = (): number => {\n    const r =\n      uniforms.uSwash.value *\n      Math.sin((uniforms.uTime.value * Math.PI * 2) / uniforms.uSwashPeriod.value);\n    // A stilled surf multiplies the sine's sign through and hands back -0,\n    // which is true-equal to 0 but not Object.is-equal — normalise it.\n    return r === 0 ? 0 : r;\n  };\n\n  return {\n    mesh,\n    level,\n    heightAt,\n    get runUp() {\n      return runUpNow();\n    },\n    depthOver(groundY: number): number {\n      return Math.max(0, curLevel - groundY + runUpNow());\n    },\n    update(dt) {\n      manual = true;\n      uniforms.uTime.value += dt;\n      retune();\n      mesh.position.y = curLevel;\n    },\n  };\n}\n","import { Color, Fog, type Light, type Object3D, type Scene } from 'three';\nimport { Rng } from '../core/random';\nimport { createWindField, type WindField } from './wind';\nimport { createPrecipitation, type Precipitation } from './precipitation';\n\nexport type WeatherPreset =\n  | 'clear'\n  | 'overcast'\n  | 'fog'\n  | 'rain'\n  | 'storm'\n  | 'snow'\n  | 'blizzard';\n\n/** The full set of knobs a weather state drives. All are cross-faded on `set`. */\nexport interface WeatherStateParams {\n  /** Wind strength (WindField.strength). */\n  wind: number;\n  /** Gustiness, 0–1. */\n  gust: number;\n  /** Rain intensity, 0–1. */\n  rain: number;\n  /** Snow intensity, 0–1. */\n  snow: number;\n  /** Fog colour (hex). */\n  fogColor: number;\n  /** Fog near distance, metres. */\n  fogNear: number;\n  /** Fog far distance, metres. */\n  fogFar: number;\n  /** Sky / background colour (hex). */\n  sky: number;\n  /** Light level multiplier, 0–1 (dims the sun & ambient in storms). */\n  light: number;\n  /** Sea roughness, 0–1 — wire an ocean's `storm` to `() => weather.storminess`\n   *  and it whips up a surge (bigger, choppier, foamier, higher seas). Default 0. */\n  sea?: number;\n  /** Whether lightning flashes fire in this state. */\n  lightning?: boolean;\n}\n\nexport interface WeatherOptions {\n  /** Starting state. Default 'clear'. */\n  initial?: WeatherPreset | string;\n  /** Reuse an existing WindField (so flora already bound to it responds). Otherwise one is made. */\n  wind?: WindField;\n  /** The sun / key light to dim in storms (its current intensity is taken as \"full sun\"). */\n  sun?: Light;\n  /** The ambient / fill light to dim in storms. */\n  ambient?: Light;\n  /** Manage `scene.fog` (creating a Fog if absent). Default true. */\n  fog?: boolean;\n  /** Manage `scene.background` colour. Default true. */\n  background?: boolean;\n  /** Settle snow onto this object in snowy states (via Precipitation.accumulate). */\n  accumulateOn?: Object3D;\n  /** Rain particle count. Default 6000. */\n  rainCount?: number;\n  /** Snow particle count. Default 3500. */\n  snowCount?: number;\n  /** Override or add states (deep-merged over the built-ins). */\n  states?: Record<string, Partial<WeatherStateParams>>;\n  seed?: number;\n}\n\nexport interface Weather {\n  /** The shared wind field — bind flora to it so trees lean into the storm. */\n  wind: WindField;\n  /** The rain system (its intensity is driven by the current state). */\n  rain: Precipitation;\n  /** The snow system. */\n  snow: Precipitation;\n  /** The renderables added to the scene (rain + snow). */\n  objects: Object3D[];\n  /** The current target state name. */\n  readonly state: string;\n  /** The live, cross-faded sea roughness, 0–1 — wire an ocean's `storm` to this. */\n  readonly storminess: number;\n  /** Cross-fade to a state over `fade` seconds (default 4). */\n  set(name: WeatherPreset | string, options?: { fade?: number }): Weather;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nconst BUILT_IN: Record<WeatherPreset, WeatherStateParams> = {\n  clear: { wind: 0.15, gust: 0.4, rain: 0, snow: 0, fogColor: 0xbcd4e6, fogNear: 30, fogFar: 200, sky: 0xbcd4e6, light: 1, sea: 0.05 },\n  overcast: { wind: 0.3, gust: 0.5, rain: 0, snow: 0, fogColor: 0x9aa7b0, fogNear: 24, fogFar: 140, sky: 0x9aa7b0, light: 0.7, sea: 0.22 },\n  fog: { wind: 0.1, gust: 0.3, rain: 0, snow: 0, fogColor: 0xc2c8cc, fogNear: 3, fogFar: 30, sky: 0xc2c8cc, light: 0.85, sea: 0.08 },\n  rain: { wind: 0.4, gust: 0.6, rain: 0.7, snow: 0, fogColor: 0x74808a, fogNear: 16, fogFar: 90, sky: 0x74808a, light: 0.55, sea: 0.5 },\n  storm: { wind: 0.9, gust: 0.9, rain: 1, snow: 0, fogColor: 0x565f68, fogNear: 10, fogFar: 62, sky: 0x565f68, light: 0.4, sea: 1, lightning: true },\n  snow: { wind: 0.25, gust: 0.4, rain: 0, snow: 0.7, fogColor: 0xcdd6dd, fogNear: 16, fogFar: 90, sky: 0xcdd6dd, light: 0.8, sea: 0.15 },\n  blizzard: { wind: 0.8, gust: 0.9, rain: 0, snow: 1, fogColor: 0xdde6ec, fogNear: 6, fogFar: 40, sky: 0xdde6ec, light: 0.62, sea: 0.7, lightning: false },\n};\n\n/** A live, colour-aware copy of a state we can interpolate in place. */\ninterface LiveState {\n  wind: number;\n  gust: number;\n  rain: number;\n  snow: number;\n  fogColor: Color;\n  fogNear: number;\n  fogFar: number;\n  sky: Color;\n  light: number;\n  sea: number;\n  lightning: boolean;\n}\n\nfunction toLive(p: WeatherStateParams): LiveState {\n  return {\n    wind: p.wind,\n    gust: p.gust,\n    rain: p.rain,\n    snow: p.snow,\n    fogColor: new Color(p.fogColor),\n    fogNear: p.fogNear,\n    fogFar: p.fogFar,\n    sky: new Color(p.sky),\n    light: p.light,\n    sea: p.sea ?? 0,\n    lightning: !!p.lightning,\n  };\n}\n\nfunction copyLive(src: LiveState): LiveState {\n  return {\n    ...src,\n    fogColor: src.fogColor.clone(),\n    sky: src.sky.clone(),\n  };\n}\n\nconst lerp = (a: number, b: number, t: number): number => a + (b - a) * t;\nconst smooth = (t: number): number => t * t * (3 - 2 * t); // smoothstep ease\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * A weather controller that cross-fades a whole scene between named states —\n * `clear`, `overcast`, `fog`, `rain`, `storm`, `snow`, `blizzard` — by driving\n * the pieces SCENA already has: a {@link WindField}, rain and snow\n * {@link Precipitation}, the scene's fog and background colour, and (optionally)\n * the sun and ambient light. Call `set('storm')` and the wind rises, rain\n * fills in, the fog closes and darkens and lightning cracks — all eased over a\n * few seconds. It self-animates from the render loop.\n *\n * Bind your flora to `weather.wind` so the trees lean into the storm; the one\n * field drives the bend, the rain's slant and (if you have an ocean) the swell.\n *\n * ```ts\n * const weather = createWeather(scene, { sun: rig.sun, accumulateOn: ground });\n * wind = weather.wind;\n * applyWind(forest.group, { field: weather.wind, height: 4, anchor: 1 });\n * weather.set('storm', { fade: 6 });     // roll a storm in over six seconds\n * ```\n */\nexport function createWeather(scene: Scene, options: WeatherOptions = {}): Weather {\n  const manageFog = options.fog ?? true;\n  const manageBg = options.background ?? true;\n  const rng = new Rng(options.seed ?? 1);\n\n  // Resolve the state table: built-ins with any user overrides merged on top.\n  const states: Record<string, WeatherStateParams> = {};\n  for (const [name, p] of Object.entries(BUILT_IN)) states[name] = { ...p };\n  if (options.states) {\n    for (const [name, patch] of Object.entries(options.states)) {\n      states[name] = { ...(states[name] ?? BUILT_IN.clear), ...patch };\n    }\n  }\n  const resolve = (name: string): WeatherStateParams => states[name] ?? BUILT_IN.clear;\n\n  const wind = options.wind ?? createWindField({ direction: 35, strength: 0.15, gust: 0.4 });\n  const rain = createPrecipitation({ type: 'rain', wind, count: options.rainCount ?? 6000, intensity: 0 });\n  const snow = createPrecipitation({ type: 'snow', wind, count: options.snowCount ?? 3500, intensity: 0 });\n  if (options.accumulateOn) snow.accumulate(options.accumulateOn);\n  scene.add(rain.object, snow.object);\n\n  // Fog & background handles.\n  let fog: Fog | null = null;\n  if (manageFog) {\n    if (scene.fog instanceof Fog) fog = scene.fog;\n    else {\n      fog = new Fog(0xbcd4e6, 30, 200);\n      scene.fog = fog;\n    }\n  }\n  const bg = manageBg && scene.background instanceof Color ? scene.background : null;\n\n  // \"Full sun\" reference intensities, so `light` multiplies from the values the\n  // caller set up their rig with.\n  const sun = options.sun ?? null;\n  const ambient = options.ambient ?? null;\n  const sunBase = sun ? sun.intensity : 0;\n  const ambientBase = ambient ? ambient.intensity : 0;\n\n  const initial = options.initial ?? 'clear';\n  const cur = toLive(resolve(initial));\n  let from = copyLive(cur);\n  let to = copyLive(cur);\n  let p = 1; // fade progress, 1 = settled\n  let duration = 1;\n  let targetName = initial;\n\n  // Lightning state.\n  let flashTimer = rng.range(3, 9);\n  let flash = 0;\n\n  const apply = (): void => {\n    wind.setStrength(cur.wind);\n    wind.uniforms.uWindGust.value = cur.gust;\n    rain.setIntensity(cur.rain);\n    snow.setIntensity(cur.snow);\n    if (fog) {\n      fog.color.copy(cur.fogColor);\n      fog.near = cur.fogNear;\n      fog.far = cur.fogFar;\n    }\n    const lightMul = Math.min(1, cur.light + flash * 0.9);\n    if (bg) {\n      bg.copy(cur.sky);\n      if (flash > 0) bg.lerp(new Color(0xffffff), flash * 0.7);\n    }\n    if (sun) sun.intensity = sunBase * lightMul + sunBase * flash * 1.4;\n    if (ambient) ambient.intensity = ambientBase * lightMul + ambientBase * flash * 1.4;\n  };\n  apply();\n\n  const step = (dt: number): void => {\n    dt = Math.min(0.1, Math.max(0, dt));\n    if (p < 1) {\n      p = Math.min(1, p + dt / duration);\n      const e = smooth(p);\n      cur.wind = lerp(from.wind, to.wind, e);\n      cur.gust = lerp(from.gust, to.gust, e);\n      cur.rain = lerp(from.rain, to.rain, e);\n      cur.snow = lerp(from.snow, to.snow, e);\n      cur.fogNear = lerp(from.fogNear, to.fogNear, e);\n      cur.fogFar = lerp(from.fogFar, to.fogFar, e);\n      cur.light = lerp(from.light, to.light, e);\n      cur.sea = lerp(from.sea, to.sea, e);\n      cur.fogColor.lerpColors(from.fogColor, to.fogColor, e);\n      cur.sky.lerpColors(from.sky, to.sky, e);\n      cur.lightning = e > 0.5 ? to.lightning : from.lightning;\n    }\n    // Lightning: fire an occasional double-flash in stormy states.\n    if (cur.lightning) {\n      flashTimer -= dt;\n      if (flashTimer <= 0) {\n        flash = 1;\n        flashTimer = rng.range(4, 11);\n      }\n    }\n    flash = Math.max(0, flash - dt * 5.5); // ~0.18s decay\n    apply();\n  };\n\n  let manual = false;\n  let last = nowSeconds();\n  const prevRender = rain.object.onBeforeRender;\n  rain.object.onBeforeRender = function (this: typeof rain.object, ...args: unknown[]) {\n    if (prevRender) (prevRender as (...a: unknown[]) => void).apply(this, args);\n    if (!manual) {\n      const t = nowSeconds();\n      step(t - last);\n      last = t;\n    }\n  } as typeof rain.object.onBeforeRender;\n\n  const weather: Weather = {\n    wind,\n    rain,\n    snow,\n    objects: [rain.object, snow.object],\n    get state() {\n      return targetName;\n    },\n    get storminess() {\n      return cur.sea;\n    },\n    set(name, opts = {}) {\n      from = copyLive(cur);\n      to = toLive(resolve(name));\n      p = 0;\n      duration = Math.max(0.001, opts.fade ?? 4);\n      targetName = name;\n      return weather;\n    },\n    update(dt) {\n      manual = true;\n      step(dt);\n    },\n  };\n\n  return weather;\n}\n","import { Color, Mesh, type Material, type Object3D } from 'three';\nimport type { TreeSeason } from '../props/tree';\n\n/** The slice of the shader object `onBeforeCompile` receives. */\ninterface PatchableShader {\n  uniforms: Record<string, { value: unknown }>;\n  fragmentShader: string;\n}\n\n/** A season is the four names a foliage `Seasons` controller cross-fades between. */\nexport type Season = TreeSeason; // 'spring' | 'summer' | 'autumn' | 'winter'\n\n/**\n * How a season re-grades foliage albedo. A pure colour operation — tint the\n * leaves toward a seasonal hue, push or drop their saturation, and lighten or\n * darken them — so no geometry is rebuilt and a whole wood turns over a few\n * seconds by lerping these numbers.\n */\nexport interface SeasonGrade {\n  /** Hue the foliage is blended toward (hex). */\n  tint: number;\n  /** How far toward `tint`, 0–1. */\n  tintAmount: number;\n  /** Saturation multiplier (1 = unchanged, >1 richer, <1 toward grey). */\n  saturation: number;\n  /** Brightness multiplier (1 = unchanged). */\n  brightness: number;\n}\n\nexport interface SeasonsOptions {\n  /** Starting season. Default 'summer' (the as-authored look — no grade). */\n  initial?: Season;\n  /** Override or extend the built-in grades (merged over the defaults). */\n  grades?: Partial<Record<Season, Partial<SeasonGrade>>>;\n}\n\nexport interface Seasons {\n  /** The shared shader uniforms (one set, referenced by every bound material). */\n  readonly uniforms: Record<string, { value: unknown }>;\n  /** The current target season. */\n  readonly season: Season;\n  /** Every foliage material re-graded so far. */\n  materials: Material[];\n  /** Patch one material to take the seasonal grade. Composes with wind; idempotent. */\n  bind(material: Material): Seasons;\n  /** Self-animate: drive the cross-fade clock from a rendered object's `onBeforeRender`. */\n  attach(object: Object3D): Seasons;\n  /** Bind every tagged foliage material under `target`, then attach the driver. */\n  apply(target: Object3D): Seasons;\n  /** Cross-fade to a season over `fade` seconds (default 6). */\n  set(season: Season, options?: { fade?: number }): Seasons;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\n// Summer is the neutral baseline (the tree as `createTree` authored it); the\n// other three grade away from it. Winter reads as bare/dead — desaturated,\n// browned and darkened — since we recolour rather than drop leaves.\nconst BUILT_IN: Record<Season, SeasonGrade> = {\n  spring: { tint: 0xbfe070, tintAmount: 0.32, saturation: 1.12, brightness: 1.08 },\n  summer: { tint: 0x3f7d2f, tintAmount: 0.0, saturation: 1.0, brightness: 1.0 },\n  autumn: { tint: 0xcf7a24, tintAmount: 0.62, saturation: 1.2, brightness: 0.95 },\n  winter: { tint: 0x6f6350, tintAmount: 0.58, saturation: 0.35, brightness: 0.72 },\n};\n\nconst SEASON_UNIFORMS = /* glsl */ `\nuniform vec3  uSeasonTint;\nuniform float uSeasonTintAmt;\nuniform float uSeasonSat;\nuniform float uSeasonBright;\n`;\n\n// A pure albedo grade: desaturate/enrich around luma, blend toward the season\n// hue, then scale brightness. Pure 0–1 maths, no world space — safe on mediump.\nconst SEASON_FRAG = /* glsl */ `\n{\n  vec3 scenaSeason = diffuseColor.rgb;\n  float scenaLum = dot(scenaSeason, vec3(0.299, 0.587, 0.114));\n  scenaSeason = mix(vec3(scenaLum), scenaSeason, uSeasonSat);\n  scenaSeason = mix(scenaSeason, uSeasonTint, uSeasonTintAmt);\n  diffuseColor.rgb = max(scenaSeason * uSeasonBright, vec3(0.0));\n}\n`;\n\nconst lerp = (a: number, b: number, t: number): number => a + (b - a) * t;\nconst smooth = (t: number): number => t * t * (3 - 2 * t);\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/** A live, colour-aware grade we can interpolate in place. */\ninterface LiveGrade {\n  tint: Color;\n  tintAmount: number;\n  saturation: number;\n  brightness: number;\n}\n\nfunction toLive(g: SeasonGrade): LiveGrade {\n  return { tint: new Color(g.tint), tintAmount: g.tintAmount, saturation: g.saturation, brightness: g.brightness };\n}\n\n/**\n * A season controller for foliage — the counterpart to {@link createWeather},\n * but for the trees themselves. It cross-fades a whole wood between `spring`,\n * `summer`, `autumn` and `winter` by re-grading each canopy's albedo (tint,\n * saturation, brightness) in the shader — no geometry rebuilt, so thousands of\n * scattered trees turn together for the cost of a few uniform writes. Only\n * foliage is touched; trunks stay planted and unchanged.\n *\n * It patches the same foliage materials `createTree` tags, composes cleanly with\n * a {@link WindField} (a tree can sway *and* turn), and self-animates from the\n * render loop — so `set('autumn')` and the leaves warm over a few seconds.\n *\n * ```ts\n * const seasons = createSeasons({ initial: 'summer' });\n * seasons.apply(forest.group);          // re-grade every tagged canopy\n * seasons.apply(oak.object);            // and standalone trees\n * seasons.set('autumn', { fade: 8 });   // turn the wood over eight seconds\n * ```\n */\nexport function createSeasons(options: SeasonsOptions = {}): Seasons {\n  const grades: Record<Season, SeasonGrade> = {\n    spring: { ...BUILT_IN.spring },\n    summer: { ...BUILT_IN.summer },\n    autumn: { ...BUILT_IN.autumn },\n    winter: { ...BUILT_IN.winter },\n  };\n  if (options.grades) {\n    for (const key of Object.keys(options.grades) as Season[]) {\n      grades[key] = { ...grades[key], ...options.grades[key] };\n    }\n  }\n\n  const initial = options.initial ?? 'summer';\n  const cur = toLive(grades[initial]);\n\n  // The shared uniforms — one set, referenced by every bound material, so a\n  // single update re-grades the whole forest.\n  const uniforms = {\n    uSeasonTint: { value: cur.tint },\n    uSeasonTintAmt: { value: cur.tintAmount },\n    uSeasonSat: { value: cur.saturation },\n    uSeasonBright: { value: cur.brightness },\n  };\n\n  const patched: Material[] = [];\n  let from = toLive(grades[initial]);\n  let to = toLive(grades[initial]);\n  let p = 1; // fade progress, 1 = settled\n  let duration = 1;\n  let targetName: Season = initial;\n\n  const push = (): void => {\n    uniforms.uSeasonTint.value = cur.tint;\n    uniforms.uSeasonTintAmt.value = cur.tintAmount;\n    uniforms.uSeasonSat.value = cur.saturation;\n    uniforms.uSeasonBright.value = cur.brightness;\n  };\n\n  const step = (dt: number): void => {\n    if (p >= 1) return;\n    dt = Math.min(0.1, Math.max(0, dt));\n    p = Math.min(1, p + dt / duration);\n    const e = smooth(p);\n    cur.tint.lerpColors(from.tint, to.tint, e);\n    cur.tintAmount = lerp(from.tintAmount, to.tintAmount, e);\n    cur.saturation = lerp(from.saturation, to.saturation, e);\n    cur.brightness = lerp(from.brightness, to.brightness, e);\n    push();\n  };\n\n  let manual = false;\n  let last = nowSeconds();\n\n  const seasons: Seasons = {\n    uniforms,\n    get season() {\n      return targetName;\n    },\n    materials: patched,\n\n    bind(material) {\n      const data = (material.userData ??= {}) as { __scenaSeason?: boolean };\n      if (data.__scenaSeason) return seasons;\n      data.__scenaSeason = true;\n\n      const prevCompile = material.onBeforeCompile;\n      // Capture the base cache key BEFORE overriding, so foliage+season stays a\n      // distinct program from foliage+wind and from foliage+wind+season.\n      const baseKey = material.customProgramCacheKey ? material.customProgramCacheKey() : '';\n      material.onBeforeCompile = function (shader: PatchableShader, renderer: unknown) {\n        if (prevCompile) (prevCompile as (s: PatchableShader, r: unknown) => void).call(this, shader, renderer);\n        Object.assign(shader.uniforms, uniforms);\n        shader.fragmentShader = shader.fragmentShader\n          .replace('#include <common>', '#include <common>\\n' + SEASON_UNIFORMS)\n          .replace('#include <color_fragment>', '#include <color_fragment>\\n' + SEASON_FRAG);\n      };\n      material.customProgramCacheKey = () => baseKey + '|scena-season-v1';\n      material.needsUpdate = true;\n      patched.push(material);\n      return seasons;\n    },\n\n    attach(object) {\n      let mesh: Mesh | null = object instanceof Mesh ? object : null;\n      if (!mesh) {\n        object.traverse((o) => {\n          if (!mesh && o instanceof Mesh) mesh = o;\n        });\n      }\n      if (mesh) {\n        const target = mesh as Mesh;\n        const prev = target.onBeforeRender;\n        target.onBeforeRender = function (this: Mesh, ...args: unknown[]) {\n          if (prev) (prev as (...a: unknown[]) => void).apply(this, args);\n          if (!manual) {\n            const t = nowSeconds();\n            step(t - last);\n            last = t;\n          }\n        } as Mesh['onBeforeRender'];\n      }\n      return seasons;\n    },\n\n    apply(target) {\n      const seen = new Set<Material>();\n      target.traverse((o) => {\n        if (!(o instanceof Mesh)) return;\n        const mats = Array.isArray(o.material) ? o.material : [o.material];\n        for (const m of mats) {\n          if (m && (m.userData as { scenaFoliage?: boolean })?.scenaFoliage && !seen.has(m)) {\n            seen.add(m);\n            seasons.bind(m);\n          }\n        }\n      });\n      seasons.attach(target);\n      return seasons;\n    },\n\n    set(season, opts = {}) {\n      from = toLive({\n        tint: cur.tint.getHex(),\n        tintAmount: cur.tintAmount,\n        saturation: cur.saturation,\n        brightness: cur.brightness,\n      });\n      to = toLive(grades[season] ?? grades.summer);\n      p = 0;\n      duration = Math.max(0.001, opts.fade ?? 6);\n      targetName = season;\n      last = nowSeconds();\n      return seasons;\n    },\n\n    update(dt) {\n      manual = true;\n      step(dt);\n    },\n  };\n\n  push();\n  return seasons;\n}\n","import {\n  AdditiveBlending,\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  Mesh,\n  Points,\n  ShaderMaterial,\n  Sphere,\n  Vector3,\n  type Material,\n  type Object3D,\n} from 'three';\nimport { Rng } from '../core/random';\n\n// ======================================================================\n//  God rays — volumetric light shafts descending through water\n// ======================================================================\n\nexport interface GodRaysOptions {\n  /** How many shafts. Default 18. */\n  count?: number;\n  /** Shaft length downward, metres. Default 20. */\n  height?: number;\n  /** Shaft width, metres. Default 1.4. */\n  width?: number;\n  /** Radius of the disc the shafts scatter across. Default 14. */\n  spread?: number;\n  /** Sun tilt in degrees — how far the shafts lean from vertical. Default 18. */\n  tilt?: number;\n  /** Sun azimuth in degrees — which way they lean. Default 0. */\n  azimuth?: number;\n  /** Shaft colour. Default 0xbfe6f0 (pale cyan). */\n  color?: number;\n  /** Additive brightness, 0–1. Default 0.14. */\n  opacity?: number;\n  /** How far the shafts waver, metres. Default 0.5. */\n  sway?: number;\n  seed?: number;\n}\n\nexport interface GodRays {\n  /** The additive shaft mesh — add it to the scene, position it at the surface. Self-animates. */\n  object: Mesh;\n  material: ShaderMaterial;\n  /** Set the additive brightness, 0–1. */\n  setOpacity(value: number): void;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nconst RAY_VERT = /* glsl */ `\nuniform float uTime;\nuniform float uSway;\nattribute float aRayV;\nattribute float aRayU;\nattribute float aPhase;\nvarying float vV;\nvarying float vU;\nvarying float vPhase;\nvoid main() {\n  vec3 p = position;\n  p.x += sin(uTime * 0.5 + aPhase) * uSway * aRayV;\n  p.z += cos(uTime * 0.4 + aPhase * 1.3) * uSway * 0.6 * aRayV;\n  vV = aRayV; vU = aRayU; vPhase = aPhase;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);\n}\n`;\n\nconst RAY_FRAG = /* glsl */ `\nuniform vec3  uColor;\nuniform float uOpacity;\nuniform float uTime;\nvarying float vV;\nvarying float vU;\nvarying float vPhase;\nvoid main() {\n  float vert = 1.0 - vV;          // brightest near the surface\n  vert *= vert;\n  float horiz = 1.0 - abs(vU);    // soft feathered sides\n  horiz *= horiz;\n  float flick = 0.7 + 0.3 * sin(uTime * 1.3 + vPhase * 4.0);\n  float a = vert * horiz * uOpacity * flick;\n  gl_FragColor = vec4(uColor * a, a);\n}\n`;\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * Volumetric light shafts falling through water — the \"god rays\" that make a\n * submerged scene read as *underwater*. Each shaft is a pair of crossed additive\n * quads (so it holds up from any camera angle), brightest at the surface and\n * feathered at the edges, wavering gently in the current. The whole set is one\n * additive draw call and it self-animates from the render loop.\n *\n * Position the object at the water surface; the shafts hang below it, leaning\n * with the sun (`tilt` / `azimuth`).\n *\n * ```ts\n * const rays = createGodRays({ count: 20, height: 22, tilt: 20 });\n * rays.object.position.set(0, waterLevel, 0);\n * scene.add(rays.object);\n * ```\n */\nexport function createGodRays(options: GodRaysOptions = {}): GodRays {\n  const count = options.count ?? 18;\n  const height = options.height ?? 20;\n  const width = options.width ?? 1.4;\n  const spread = options.spread ?? 14;\n  const tilt = ((options.tilt ?? 18) * Math.PI) / 180;\n  const azimuth = ((options.azimuth ?? 0) * Math.PI) / 180;\n  const rng = new Rng(options.seed ?? 1);\n\n  const halfW = width * 0.5;\n  const slant = Math.tan(tilt) * height;\n  const slantX = Math.cos(azimuth) * slant;\n  const slantZ = Math.sin(azimuth) * slant;\n\n  // Each shaft = two crossed quads (one spanning local X, one spanning Z), so\n  // it reads as a solid beam of light from any angle. 8 verts, 4 tris a shaft.\n  const vertsPerShaft = 8;\n  const trisPerShaft = 4;\n  const pos = new Float32Array(count * vertsPerShaft * 3);\n  const rayV = new Float32Array(count * vertsPerShaft);\n  const rayU = new Float32Array(count * vertsPerShaft);\n  const phase = new Float32Array(count * vertsPerShaft);\n  const index: number[] = [];\n\n  for (let s = 0; s < count; s++) {\n    // Scatter the top over a disc.\n    const r = Math.sqrt(rng.next()) * spread;\n    const a = rng.next() * Math.PI * 2;\n    const cx = Math.cos(a) * r;\n    const cz = Math.sin(a) * r;\n    const ph = rng.range(0, Math.PI * 2);\n    const bx = cx + slantX;\n    const bz = cz + slantZ;\n    const base = s * vertsPerShaft;\n\n    // Quad A spans local X; quad B spans local Z. Layout per quad:\n    // 0 top-left, 1 top-right, 2 bottom-left, 3 bottom-right.\n    const quads: Array<[number, number, number, number, number, number]> = [\n      // top-left,               top-right,              bottom-left,                 bottom-right\n      [cx - halfW, cz, cx + halfW, cz, bx - halfW, bz], // A (spans X)\n      [cx, cz - halfW, cx, cz + halfW, bx, bz - halfW], // B (spans Z) — bottoms mirror\n    ];\n    for (let q = 0; q < 2; q++) {\n      const [tlx, tlz, trx, trz, blx, blz] = quads[q];\n      const brx = q === 0 ? bx + halfW : bx;\n      const brz = q === 0 ? bz : bz + halfW;\n      const corners: Array<[number, number, number, number, number]> = [\n        [tlx, 0, tlz, 0, -1], // top-left   v0 u-1\n        [trx, 0, trz, 0, 1], // top-right   v0 u+1\n        [blx, -height, blz, 1, -1], // bottom-left  v1 u-1\n        [brx, -height, brz, 1, 1], // bottom-right v1 u+1\n      ];\n      const off = base + q * 4;\n      for (let c = 0; c < 4; c++) {\n        const v = off + c;\n        pos[v * 3] = corners[c][0];\n        pos[v * 3 + 1] = corners[c][1];\n        pos[v * 3 + 2] = corners[c][2];\n        rayV[v] = corners[c][3];\n        rayU[v] = corners[c][4];\n        phase[v] = ph;\n      }\n      index.push(off, off + 2, off + 1, off + 1, off + 2, off + 3);\n    }\n  }\n\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(pos, 3));\n  geometry.setAttribute('aRayV', new BufferAttribute(rayV, 1));\n  geometry.setAttribute('aRayU', new BufferAttribute(rayU, 1));\n  geometry.setAttribute('aPhase', new BufferAttribute(phase, 1));\n  geometry.setIndex(index);\n  geometry.boundingSphere = new Sphere(new Vector3(0, -height * 0.5, 0), spread + height);\n  void trisPerShaft;\n\n  const material = new ShaderMaterial({\n    vertexShader: RAY_VERT,\n    fragmentShader: RAY_FRAG,\n    transparent: true,\n    depthWrite: false,\n    blending: AdditiveBlending,\n    side: 2, // DoubleSide\n    uniforms: {\n      uTime: { value: 0 },\n      uSway: { value: options.sway ?? 0.5 },\n      uColor: { value: new Color(options.color ?? 0xbfe6f0) },\n      uOpacity: { value: options.opacity ?? 0.14 },\n    },\n  });\n\n  const object = new Mesh(geometry, material);\n  object.name = 'god-rays';\n  object.frustumCulled = false;\n\n  let manual = false;\n  object.onBeforeRender = () => {\n    if (!manual) material.uniforms.uTime.value = nowSeconds();\n  };\n\n  return {\n    object,\n    material,\n    setOpacity(value) {\n      material.uniforms.uOpacity.value = Math.max(0, value);\n    },\n    update(dt) {\n      manual = true;\n      material.uniforms.uTime.value += dt;\n    },\n  };\n}\n\n// ======================================================================\n//  Caustics — the dancing light network on the seabed\n// ======================================================================\n\ninterface PatchableShader {\n  uniforms: Record<string, { value: unknown }>;\n  vertexShader: string;\n  fragmentShader: string;\n}\n\nexport interface CausticsOptions {\n  /** Caustic tint (added to emissive, so it glows regardless of the day cycle). Default 0x9fd8e6. */\n  color?: number;\n  /** Pattern scale — larger = finer cells. Default 0.5. */\n  scale?: number;\n  /** How fast the network shifts. Default 0.6. */\n  speed?: number;\n  /** Brightness of the caustics. Default 0.5. */\n  intensity?: number;\n}\n\nexport interface Caustics {\n  /** The shared shader uniforms (one set, referenced by every bound material). */\n  readonly uniforms: Record<string, { value: unknown }>;\n  /** Every material patched so far. */\n  materials: Material[];\n  /** Set the caustic brightness. */\n  setIntensity(value: number): Caustics;\n  /** Project caustics onto a material's emissive. Composes with surfaces; idempotent per material. */\n  bind(material: Material): Caustics;\n  /** Self-animate: drive the shared clock from a rendered object's `onBeforeRender`. */\n  attach(object: Object3D): Caustics;\n  /** Convenience: `bind` every material under `target`, then `attach` the driver. */\n  apply(target: Object3D): Caustics;\n  /** Advance the clock manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\n// Caustics use large world coordinates, so — like the surface noise — they need\n// highp in the fragment stage or mobile mediump loses precision and the network\n// swims. SwiftShader (headless) ignores the qualifier, so this can't be seen in\n// the automated captures; it matters on real phones.\nconst CAUSTIC_COMMON = /* glsl */ `\nvarying highp vec3 vCausticWorld;\n`;\n\nconst CAUSTIC_FRAG_FN = /* glsl */ `\nuniform vec3  uCausticColor;\nuniform highp float uCausticScale;\nuniform highp float uCausticTime;\nuniform float uCausticSpeed;\nuniform float uCausticIntensity;\nfloat scenaCausticCell(highp vec2 p) {\n  highp float s = sin(p.x) * sin(p.y);\n  return pow(max(s, 0.0), 8.0);\n}\nfloat scenaCaustics(highp vec2 uv, highp float t) {\n  mat2 R = mat2(0.8, -0.6, 0.6, 0.8);\n  float a = scenaCausticCell(uv + vec2(t, t * 0.7));\n  float b = scenaCausticCell(R * uv * 1.3 + vec2(-t * 0.8, t * 0.5));\n  float c = scenaCausticCell(R * R * uv * 0.7 + vec2(t * 0.5, -t * 0.6));\n  return clamp(a + b + c, 0.0, 1.0);\n}\n`;\n\n/**\n * Caustics — the rippling net of light that the water surface throws onto the\n * seabed. It patches a `MeshStandardMaterial` (so PBR, shadows and fog all\n * survive) and adds a shifting caustic network to the material's *emissive*, so\n * the light dances regardless of the day/night cycle. Bind it to the sand and\n * rocks under your ocean; it composes with SCENA surfaces and self-animates.\n *\n * ```ts\n * const caustics = createCaustics({ intensity: 0.5 });\n * caustics.apply(seabed);   // sand + rocks catch the moving light\n * ```\n */\nexport function createCaustics(options: CausticsOptions = {}): Caustics {\n  const uniforms = {\n    uCausticColor: { value: new Color(options.color ?? 0x9fd8e6) },\n    uCausticScale: { value: options.scale ?? 0.5 },\n    uCausticTime: { value: 0 },\n    uCausticSpeed: { value: options.speed ?? 0.6 },\n    uCausticIntensity: { value: options.intensity ?? 0.5 },\n  };\n\n  const patched: Material[] = [];\n  let manual = false;\n\n  const caustics: Caustics = {\n    uniforms,\n    materials: patched,\n\n    setIntensity(value) {\n      uniforms.uCausticIntensity.value = Math.max(0, value);\n      return caustics;\n    },\n\n    bind(material) {\n      const data = (material.userData ??= {}) as { __scenaCaustics?: boolean };\n      if (data.__scenaCaustics) return caustics;\n      data.__scenaCaustics = true;\n\n      const prevCompile = material.onBeforeCompile;\n      const baseKey = material.customProgramCacheKey ? material.customProgramCacheKey() : '';\n      material.onBeforeCompile = function (shader: PatchableShader, renderer: unknown) {\n        if (prevCompile) (prevCompile as (s: PatchableShader, r: unknown) => void).call(this, shader, renderer);\n        Object.assign(shader.uniforms, uniforms);\n        shader.vertexShader = shader.vertexShader\n          .replace('#include <common>', '#include <common>\\n' + CAUSTIC_COMMON)\n          .replace(\n            '#include <begin_vertex>',\n            `#include <begin_vertex>\n             {\n               mat4 scenaCWM = modelMatrix;\n               #ifdef USE_INSTANCING\n                 scenaCWM = modelMatrix * instanceMatrix;\n               #endif\n               vCausticWorld = (scenaCWM * vec4(transformed, 1.0)).xyz;\n             }`\n          );\n        shader.fragmentShader = shader.fragmentShader\n          .replace('#include <common>', '#include <common>\\n' + CAUSTIC_COMMON + CAUSTIC_FRAG_FN)\n          .replace(\n            '#include <emissivemap_fragment>',\n            `#include <emissivemap_fragment>\n             {\n               float caust = scenaCaustics(vCausticWorld.xz * uCausticScale, uCausticTime * uCausticSpeed);\n               totalEmissiveRadiance += uCausticColor * caust * uCausticIntensity;\n             }`\n          );\n      };\n      material.customProgramCacheKey = () => baseKey + '|scena-caustics-v1';\n      material.needsUpdate = true;\n      patched.push(material);\n      return caustics;\n    },\n\n    attach(object) {\n      let mesh: Mesh | null = object instanceof Mesh ? object : null;\n      if (!mesh) {\n        object.traverse((o) => {\n          if (!mesh && o instanceof Mesh) mesh = o;\n        });\n      }\n      if (mesh) {\n        const target = mesh as Mesh;\n        const prev = target.onBeforeRender;\n        target.onBeforeRender = function (this: Mesh, ...args: unknown[]) {\n          if (prev) (prev as (...a: unknown[]) => void).apply(this, args);\n          if (!manual) uniforms.uCausticTime.value = nowSeconds();\n        } as Mesh['onBeforeRender'];\n      }\n      return caustics;\n    },\n\n    apply(target) {\n      const seen = new Set<Material>();\n      target.traverse((o) => {\n        if (!(o instanceof Mesh)) return;\n        const mats = Array.isArray(o.material) ? o.material : [o.material];\n        for (const m of mats) {\n          if (m && !seen.has(m)) {\n            seen.add(m);\n            caustics.bind(m);\n          }\n        }\n      });\n      caustics.attach(target);\n      return caustics;\n    },\n\n    update(dt) {\n      manual = true;\n      uniforms.uCausticTime.value += dt;\n    },\n  };\n\n  return caustics;\n}\n\n// ======================================================================\n//  Bubble columns — streams of bubbles rising from the seabed\n// ======================================================================\n\nexport interface BubbleOptions {\n  /** Total bubbles across all columns. Default 240. */\n  count?: number;\n  /** Number of vent points bubbles rise from (ignored if `sources` is given). Default 6. */\n  columns?: number;\n  /** Explicit vent positions in local XZ; otherwise `columns` are scattered in `area`. */\n  sources?: Array<[number, number]>;\n  /** Radius (or [x, z]) the vents scatter across when `sources` is omitted. Default 16. */\n  area?: number | [number, number];\n  /** Seabed Y the bubbles rise from. Default 0. */\n  floor?: number;\n  /** How far they rise before popping, metres. Default 8. */\n  rise?: number;\n  /** Rise speed, metres/s. Default 1.2. */\n  speed?: number;\n  /** Bubble size in px. Default 8. */\n  size?: number;\n  /** Lateral wander, metres. Default 0.35. */\n  wobble?: number;\n  /** Bubble colour. Default 0xcfeaf0. */\n  color?: number;\n  /** Opacity. Default 0.4. */\n  opacity?: number;\n  seed?: number;\n}\n\nexport interface Bubbles {\n  /** The bubble points — add it to the scene, position it at the seabed. Self-animates. */\n  object: Points;\n  material: ShaderMaterial;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\nconst BUBBLE_VERT = /* glsl */ `\nuniform float uTime;\nuniform float uSpeed;\nuniform float uRise;\nuniform float uFloor;\nuniform float uSize;\nuniform float uWobble;\nuniform float uOpacity;\nattribute float aPhase;\nattribute float aWobble;\nattribute float aScale;\nvarying float vAlpha;\nvoid main() {\n  float prog = fract(aPhase + uTime * uSpeed / uRise);\n  float y = uFloor + prog * uRise;\n  float w = uWobble * prog;                       // wander grows as it rises\n  float x = position.x + sin(uTime * 1.5 + aWobble) * w;\n  float z = position.z + cos(uTime * 1.2 + aWobble * 1.7) * w;\n  vec4 mv = viewMatrix * modelMatrix * vec4(x, y, z, 1.0);\n  gl_Position = projectionMatrix * mv;\n  float grow = (0.55 + prog * 0.7) * aScale;       // bubbles swell as pressure drops\n  gl_PointSize = clamp(uSize * grow * (300.0 / max(-mv.z, 1.0)), 1.0, 22.0);\n  // Fade in off the vent, pop near the top.\n  vAlpha = uOpacity * smoothstep(0.0, 0.06, prog) * (1.0 - smoothstep(0.82, 1.0, prog));\n}\n`;\n\nconst BUBBLE_FRAG = /* glsl */ `\nuniform vec3 uColor;\nvarying float vAlpha;\nvoid main() {\n  vec2 c = gl_PointCoord - 0.5;\n  float d = length(c);\n  if (d > 0.5) discard;\n  // A hollow bubble: bright rim, faint fill.\n  float body = 1.0 - smoothstep(0.34, 0.5, d);\n  float rim = smoothstep(0.3, 0.44, d) * (1.0 - smoothstep(0.44, 0.5, d));\n  float a = (body * 0.22 + rim * 0.95) * vAlpha;\n  if (a <= 0.0) discard;\n  gl_FragColor = vec4(uColor, a);\n}\n`;\n\nfunction toRadius(a: number | [number, number] | undefined): [number, number] {\n  if (a === undefined) return [16, 16];\n  return typeof a === 'number' ? [a, a] : a;\n}\n\n/**\n * Streams of bubbles rising from the seabed — from a vent, a wreck, a diver.\n * Bubbles wander a little and swell as they rise (pressure drops), then pop near\n * the top; the columns are anchored to fixed world vents, not the camera. Every\n * bubble's position is computed in the vertex shader from a seed and the clock,\n * so it's one draw call and no per-particle CPU work. It self-animates.\n *\n * ```ts\n * const bubbles = createBubbles({ columns: 6, area: 14, rise: 9 });\n * bubbles.object.position.y = seabedY;\n * scene.add(bubbles.object);\n * ```\n */\nexport function createBubbles(options: BubbleOptions = {}): Bubbles {\n  const count = options.count ?? 240;\n  const columns = options.columns ?? 6;\n  const rng = new Rng(options.seed ?? 1);\n  const [ax, az] = toRadius(options.area);\n\n  // Vent positions: given, or scattered over the area disc.\n  const vents: Array<[number, number]> =\n    options.sources ??\n    Array.from({ length: columns }, () => {\n      const r = Math.sqrt(rng.next());\n      const a = rng.next() * Math.PI * 2;\n      return [Math.cos(a) * r * ax, Math.sin(a) * r * az] as [number, number];\n    });\n\n  const pos = new Float32Array(count * 3);\n  const phase = new Float32Array(count);\n  const wobble = new Float32Array(count);\n  const scale = new Float32Array(count);\n  for (let i = 0; i < count; i++) {\n    const v = vents[i % vents.length];\n    // Jitter each bubble a touch off its vent so a column has width.\n    pos[i * 3] = v[0] + rng.range(-0.3, 0.3);\n    pos[i * 3 + 1] = 0;\n    pos[i * 3 + 2] = v[1] + rng.range(-0.3, 0.3);\n    phase[i] = rng.next();\n    wobble[i] = rng.range(0, Math.PI * 2);\n    scale[i] = rng.range(0.6, 1.4);\n  }\n\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(pos, 3));\n  geometry.setAttribute('aPhase', new BufferAttribute(phase, 1));\n  geometry.setAttribute('aWobble', new BufferAttribute(wobble, 1));\n  geometry.setAttribute('aScale', new BufferAttribute(scale, 1));\n  geometry.boundingSphere = new Sphere(new Vector3(0, (options.rise ?? 8) * 0.5, 0), Math.max(ax, az) + (options.rise ?? 8));\n\n  const material = new ShaderMaterial({\n    vertexShader: BUBBLE_VERT,\n    fragmentShader: BUBBLE_FRAG,\n    transparent: true,\n    depthWrite: false,\n    uniforms: {\n      uTime: { value: 0 },\n      uSpeed: { value: options.speed ?? 1.2 },\n      uRise: { value: options.rise ?? 8 },\n      uFloor: { value: options.floor ?? 0 },\n      uSize: { value: options.size ?? 8 },\n      uWobble: { value: options.wobble ?? 0.35 },\n      uOpacity: { value: options.opacity ?? 0.4 },\n      uColor: { value: new Color(options.color ?? 0xcfeaf0) },\n    },\n  });\n\n  const object = new Points(geometry, material);\n  object.name = 'bubbles';\n  object.frustumCulled = false;\n\n  let manual = false;\n  object.onBeforeRender = () => {\n    if (!manual) material.uniforms.uTime.value = nowSeconds();\n  };\n\n  return {\n    object,\n    material,\n    update(dt) {\n      manual = true;\n      material.uniforms.uTime.value += dt;\n    },\n  };\n}\n\n// ======================================================================\n//  Water grade — depth/distance colour extinction (the underwater look)\n// ======================================================================\n\nexport interface WaterGradeOptions {\n  /** World Y of the water surface — depth is measured down from here. Default 0. */\n  surface?: number;\n  /** The colour everything grades toward with distance & depth. Default 0x0e3a49. */\n  color?: number;\n  /** Extinction per metre of view distance. Default 0.022. */\n  density?: number;\n  /** Extra extinction per metre of depth below the surface. Default 0.03. */\n  depthDensity?: number;\n  /** How much faster warm light (red, then green) is absorbed than blue, 0–1. Default 0.6. */\n  redShift?: number;\n}\n\nexport interface WaterGrade {\n  /** The shared shader uniforms. */\n  readonly uniforms: Record<string, { value: unknown }>;\n  /** Every material patched so far. */\n  materials: Material[];\n  /** Set the base extinction density. */\n  setDensity(value: number): WaterGrade;\n  /** Grade a material by depth & distance. Composes with surfaces; idempotent per material. */\n  bind(material: Material): WaterGrade;\n  /** Grade every material under `target`. */\n  apply(target: Object3D): WaterGrade;\n}\n\nconst GRADE_COMMON = /* glsl */ `\nvarying highp vec3 vWaterWorld;\n`;\n\n/**\n * The underwater colour grade — the tint that makes a scene read as *deep*.\n * Real water absorbs light by wavelength (red first, then green, leaving blue),\n * so distant and deep things go blue-green and dark. This patches a\n * `MeshStandardMaterial` and applies **per-channel Beer–Lambert extinction**\n * toward the water colour, driven by view distance and depth below the surface\n * — physically flavoured, not a flat fog. Bind it to the seabed, rocks, kelp\n * and fish and the whole world sinks into the blue. Composes with SCENA\n * surfaces and [caustics](#caustics).\n *\n * ```ts\n * const grade = createWaterGrade({ surface: 0, density: 0.03 });\n * grade.apply(reef);   // everything fades into the deep with distance & depth\n * ```\n */\nexport function createWaterGrade(options: WaterGradeOptions = {}): WaterGrade {\n  const uniforms = {\n    uWaterSurface: { value: options.surface ?? 0 },\n    uWaterColor: { value: new Color(options.color ?? 0x0e3a49) },\n    uWaterDensity: { value: options.density ?? 0.022 },\n    uWaterDepth: { value: options.depthDensity ?? 0.03 },\n    uWaterRedShift: { value: options.redShift ?? 0.6 },\n  };\n\n  const patched: Material[] = [];\n\n  const grade: WaterGrade = {\n    uniforms,\n    materials: patched,\n\n    setDensity(value) {\n      uniforms.uWaterDensity.value = Math.max(0, value);\n      return grade;\n    },\n\n    bind(material) {\n      const data = (material.userData ??= {}) as { __scenaWaterGrade?: boolean };\n      if (data.__scenaWaterGrade) return grade;\n      data.__scenaWaterGrade = true;\n\n      const prevCompile = material.onBeforeCompile;\n      const baseKey = material.customProgramCacheKey ? material.customProgramCacheKey() : '';\n      material.onBeforeCompile = function (shader: PatchableShader, renderer: unknown) {\n        if (prevCompile) (prevCompile as (s: PatchableShader, r: unknown) => void).call(this, shader, renderer);\n        Object.assign(shader.uniforms, uniforms);\n        shader.vertexShader = shader.vertexShader\n          .replace('#include <common>', '#include <common>\\n' + GRADE_COMMON)\n          .replace(\n            '#include <begin_vertex>',\n            `#include <begin_vertex>\n             {\n               mat4 scenaGWM = modelMatrix;\n               #ifdef USE_INSTANCING\n                 scenaGWM = modelMatrix * instanceMatrix;\n               #endif\n               vWaterWorld = (scenaGWM * vec4(transformed, 1.0)).xyz;\n             }`\n          );\n        shader.fragmentShader = shader.fragmentShader\n          .replace(\n            '#include <common>',\n            `#include <common>\\n${GRADE_COMMON}\n             uniform highp float uWaterSurface;\n             uniform vec3 uWaterColor;\n             uniform float uWaterDensity;\n             uniform float uWaterDepth;\n             uniform float uWaterRedShift;`\n          )\n          .replace(\n            '#include <fog_fragment>',\n            `{\n               // Beer–Lambert extinction: warm light dies first, so distance and\n               // depth pull everything toward the deep-water colour.\n               highp float dist = length(vViewPosition);\n               highp float depth = max(uWaterSurface - vWaterWorld.y, 0.0);\n               highp float d = dist * uWaterDensity + depth * uWaterDepth;\n               vec3 sigma = vec3(1.0 + uWaterRedShift, 1.0 + uWaterRedShift * 0.4, 1.0);\n               vec3 trans = exp(-sigma * d);\n               gl_FragColor.rgb = mix(uWaterColor, gl_FragColor.rgb, clamp(trans, 0.0, 1.0));\n             }\n             #include <fog_fragment>`\n          );\n      };\n      material.customProgramCacheKey = () => baseKey + '|scena-watergrade-v1';\n      material.needsUpdate = true;\n      patched.push(material);\n      return grade;\n    },\n\n    apply(target) {\n      const seen = new Set<Material>();\n      target.traverse((o) => {\n        if (!(o instanceof Mesh)) return;\n        const mats = Array.isArray(o.material) ? o.material : [o.material];\n        for (const m of mats) {\n          if (m && !seen.has(m)) {\n            seen.add(m);\n            grade.bind(m);\n          }\n        }\n      });\n      return grade;\n    },\n  };\n\n  return grade;\n}\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  InstancedBufferAttribute,\n  InstancedMesh,\n  Matrix4,\n  MeshStandardMaterial,\n  Vector3,\n} from 'three';\nimport { Rng } from '../core/random';\n\nexport type FlockType = 'birds' | 'fish';\n\nexport interface FlockOptions {\n  /** birds (fly, wings flap) or fish (swim, tail sways). Default 'birds'. */\n  type?: FlockType;\n  /** How many. Default 60 (birds) / 80 (fish). */\n  count?: number;\n  /** Centre of the volume the flock roams, world space. Default [0, 12, 0] birds / [0, 2, 0] fish. */\n  center?: [number, number, number];\n  /** Half-extents of that volume (a box), or one number for a cube. Default [26, 6, 26]. */\n  bounds?: [number, number, number] | number;\n  /** Cruise speed, m/s. Default 7 (birds) / 3 (fish). */\n  speed?: number;\n  /** Creature length, metres. Default 0.5 (birds) / 0.4 (fish). */\n  size?: number;\n  /** Body colour. Default 0x2b2b30 birds / 0x6a86a0 fish. */\n  color?: number;\n  /** Wing-beat / tail-beat rate. Default 9 (birds) / 5 (fish). */\n  beat?: number;\n  /** Steering weights. */\n  separation?: number;\n  alignment?: number;\n  cohesion?: number;\n  /** Wheel around the centre at this radius (birds circling a tower). Off by default. */\n  circle?: number;\n  seed?: number;\n}\n\nexport interface Flock {\n  /** The instanced flock — add it to the scene. Self-animates. */\n  object: InstancedMesh;\n  count: number;\n  /** Live boid positions (world space) — for gameplay: scare them, follow one, etc. */\n  positions: readonly Vector3[];\n  /** Move the roaming volume's centre (the flock drifts to follow). */\n  setCenter(x: number, y: number, z: number): void;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\n// --- creature geometry ---------------------------------------------------\n\n/** A low-poly bird: a dart body and two triangular wings. `aFlap` is signed by\n *  side and grows to the tips, so the shader beats the wings up together. */\nfunction birdGeometry(size: number): BufferGeometry {\n  const s = size;\n  // prettier-ignore\n  const tris: Array<[number, number, number, number]> = [\n    // body (aFlap 0): thin diamond along +Z\n    [0, 0, 0.55 * s, 0], [0, 0.09 * s, -0.2 * s, 0], [0.05 * s, 0, -0.15 * s, 0],\n    [0, 0, 0.55 * s, 0], [-0.05 * s, 0, -0.15 * s, 0], [0, 0.09 * s, -0.2 * s, 0],\n    [0, 0, 0.55 * s, 0], [0.05 * s, 0, -0.15 * s, 0], [0, -0.05 * s, -0.18 * s, 0],\n    [0, 0, 0.55 * s, 0], [0, -0.05 * s, -0.18 * s, 0], [-0.05 * s, 0, -0.15 * s, 0],\n    [0, 0, -0.5 * s, 0], [0, 0.09 * s, -0.2 * s, 0], [0, -0.05 * s, -0.18 * s, 0],\n    // right wing (aFlap +): root → tip → back\n    [0.04 * s, 0, 0.08 * s, 0.3], [0.95 * s, 0.02 * s, -0.1 * s, 1.0], [0.04 * s, 0, -0.28 * s, 0.3],\n    // left wing (aFlap −)\n    [-0.04 * s, 0, 0.08 * s, -0.3], [-0.04 * s, 0, -0.28 * s, -0.3], [-0.95 * s, 0.02 * s, -0.1 * s, -1.0],\n  ];\n  return meshFromTris(tris);\n}\n\n/** A low-poly fish: an elongated body and a tail fin. `aFlap` grows toward the\n *  tail, so the shader sways it side to side like a swimming wave. */\nfunction fishGeometry(size: number): BufferGeometry {\n  const s = size;\n  // prettier-ignore\n  const tris: Array<[number, number, number, number]> = [\n    // body octahedron-ish along Z (head +Z)\n    [0, 0, 0.7 * s, 0], [0.13 * s, 0, 0, 0], [0, 0.16 * s, 0, 0],\n    [0, 0, 0.7 * s, 0], [0, 0.16 * s, 0, 0], [-0.13 * s, 0, 0, 0],\n    [0, 0, 0.7 * s, 0], [0, -0.14 * s, 0, 0], [0.13 * s, 0, 0, 0],\n    [0, 0, 0.7 * s, 0], [-0.13 * s, 0, 0, 0], [0, -0.14 * s, 0, 0],\n    [0, 0, -0.5 * s, 0.55], [0, 0.16 * s, 0, 0], [0.13 * s, 0, 0, 0],\n    [0, 0, -0.5 * s, 0.55], [-0.13 * s, 0, 0, 0], [0, 0.16 * s, 0, 0],\n    [0, 0, -0.5 * s, 0.55], [0.13 * s, 0, 0, 0], [0, -0.14 * s, 0, 0],\n    [0, 0, -0.5 * s, 0.55], [0, -0.14 * s, 0, 0], [-0.13 * s, 0, 0, 0],\n    // tail fin (aFlap 1): sways most\n    [0, 0, -0.5 * s, 0.7], [0, 0.24 * s, -0.85 * s, 1.0], [0, -0.22 * s, -0.85 * s, 1.0],\n  ];\n  return meshFromTris(tris);\n}\n\nfunction meshFromTris(tris: Array<[number, number, number, number]>): BufferGeometry {\n  const n = tris.length;\n  const pos = new Float32Array(n * 3);\n  const flap = new Float32Array(n);\n  for (let i = 0; i < n; i++) {\n    pos[i * 3] = tris[i][0];\n    pos[i * 3 + 1] = tris[i][1];\n    pos[i * 3 + 2] = tris[i][2];\n    flap[i] = tris[i][3];\n  }\n  const geo = new BufferGeometry();\n  geo.setAttribute('position', new BufferAttribute(pos, 3));\n  geo.setAttribute('aFlap', new BufferAttribute(flap, 1));\n  geo.computeVertexNormals();\n  return geo;\n}\n\n// --- material: vertex flap + per-instance phase --------------------------\n\nfunction flockMaterial(color: number, beat: number, fish: boolean): {\n  material: MeshStandardMaterial;\n  uniforms: { uTime: { value: number } };\n} {\n  const uniforms = {\n    uTime: { value: 0 },\n    uFlapSpeed: { value: beat },\n    uFlapAmp: { value: fish ? 0.6 : 0.9 },\n    uFishMode: { value: fish ? 1 : 0 },\n  };\n  const material = new MeshStandardMaterial({ color, roughness: 0.7, metalness: 0, flatShading: true });\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.vertexShader = shader.vertexShader\n      .replace(\n        '#include <common>',\n        `#include <common>\n         attribute float aFlap;\n         attribute float aPhase;\n         uniform float uTime, uFlapSpeed, uFlapAmp, uFishMode;`\n      )\n      .replace(\n        '#include <begin_vertex>',\n        `#include <begin_vertex>\n         {\n           float wave = sin(uTime * uFlapSpeed + aPhase) * uFlapAmp;\n           float ang = wave * aFlap;\n           float c = cos(ang), s = sin(ang);\n           if (uFishMode > 0.5) transformed.xz = mat2(c, -s, s, c) * transformed.xz;\n           else                 transformed.xy = mat2(c, -s, s, c) * transformed.xy;\n         }`\n      );\n  };\n  material.customProgramCacheKey = () => 'scena-flock-v1';\n  return { material, uniforms };\n}\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * A flock of birds or a school of fish — the thing that makes a sky or a sea\n * feel *alive*. A lightweight boid simulation (separation, alignment, cohesion,\n * plus soft bounds and a little wander) steers every creature on the CPU, and\n * the whole flock draws as **one InstancedMesh** whose wings beat (or tail\n * sways) in the vertex shader from a per-instance phase — so no two flap in\n * lockstep. It self-animates from the render loop; pass `circle` and the birds\n * wheel around the centre like crows over a tower.\n *\n * `positions` exposes the live boids, so gameplay can read them — scatter the\n * flock when an NPC gets close, or have a cat watch one fish.\n *\n * ```ts\n * const crows = createFlock({ type: 'birds', center: [0, 16, 0], circle: 14 });\n * scene.add(crows.object);\n * ```\n */\nexport function createFlock(options: FlockOptions = {}): Flock {\n  const type = options.type ?? 'birds';\n  const fish = type === 'fish';\n  const count = options.count ?? (fish ? 80 : 60);\n  const center = new Vector3(...(options.center ?? (fish ? [0, 2, 0] : [0, 12, 0])));\n  const b = options.bounds ?? [26, 6, 26];\n  const bounds = new Vector3(...(typeof b === 'number' ? [b, b, b] : b));\n  const speed = options.speed ?? (fish ? 3 : 7);\n  const size = options.size ?? (fish ? 0.4 : 0.5);\n  const beat = options.beat ?? (fish ? 5 : 9);\n  const wSep = options.separation ?? 1.5;\n  const wAli = options.alignment ?? 1;\n  const wCoh = options.cohesion ?? 0.9;\n  const circle = options.circle ?? 0;\n  const rng = new Rng(options.seed ?? 1);\n\n  const geometry = fish ? fishGeometry(size) : birdGeometry(size);\n  const { material, uniforms } = flockMaterial(options.color ?? (fish ? 0x6a86a0 : 0x2b2b30), beat, fish);\n\n  // Per-instance flap phase so the beat is desynchronised.\n  const phases = new Float32Array(count);\n  for (let i = 0; i < count; i++) phases[i] = rng.range(0, Math.PI * 2);\n  geometry.setAttribute('aPhase', new InstancedBufferAttribute(phases, 1));\n\n  const mesh = new InstancedMesh(geometry, material, count);\n  mesh.name = `flock-${type}`;\n  mesh.frustumCulled = false;\n\n  // Boid state.\n  const pos: Vector3[] = [];\n  const vel: Vector3[] = [];\n  for (let i = 0; i < count; i++) {\n    pos.push(\n      new Vector3(\n        center.x + rng.range(-bounds.x, bounds.x),\n        center.y + rng.range(-bounds.y, bounds.y),\n        center.z + rng.range(-bounds.z, bounds.z)\n      )\n    );\n    const v = new Vector3(rng.range(-1, 1), rng.range(-0.3, 0.3), rng.range(-1, 1));\n    if (v.lengthSq() < 1e-4) v.set(1, 0, 0);\n    vel.push(v.setLength(speed));\n  }\n\n  const m = new Matrix4();\n  const sep = new Vector3();\n  const ali = new Vector3();\n  const coh = new Vector3();\n  const acc = new Vector3();\n  const tmp = new Vector3();\n  const xAxis = new Vector3();\n  const yAxis = new Vector3();\n  const UP = new Vector3(0, 1, 0);\n  const neighbor = 4.5;\n  const sepDist = fish ? 1.2 : 2.0;\n\n  const writeMatrices = (): void => {\n    for (let i = 0; i < count; i++) {\n      const p = pos[i];\n      const v = vel[i];\n      // Orient +Z along velocity; build an orthonormal basis.\n      const z = tmp.copy(v).normalize();\n      xAxis.copy(UP).cross(z);\n      if (xAxis.lengthSq() < 1e-5) xAxis.set(1, 0, 0);\n      xAxis.normalize();\n      yAxis.copy(z).cross(xAxis).normalize();\n      m.makeBasis(xAxis, yAxis, z);\n      m.setPosition(p.x, p.y, p.z);\n      mesh.setMatrixAt(i, m);\n    }\n    mesh.instanceMatrix.needsUpdate = true;\n  };\n  writeMatrices();\n\n  let manual = false;\n  let last = nowSeconds();\n\n  const step = (dt: number): void => {\n    dt = Math.min(0.05, Math.max(0, dt));\n    for (let i = 0; i < count; i++) {\n      const p = pos[i];\n      const v = vel[i];\n      sep.set(0, 0, 0);\n      ali.set(0, 0, 0);\n      coh.set(0, 0, 0);\n      let near = 0;\n      for (let j = 0; j < count; j++) {\n        if (j === i) continue;\n        const q = pos[j];\n        const d = p.distanceTo(q);\n        if (d < neighbor) {\n          ali.add(vel[j]);\n          coh.add(q);\n          near++;\n          if (d < sepDist && d > 1e-4) {\n            tmp.copy(p).sub(q).multiplyScalar(1 / (d * d));\n            sep.add(tmp);\n          }\n        }\n      }\n      acc.set(0, 0, 0);\n      if (near > 0) {\n        ali.multiplyScalar(1 / near).setLength(speed).sub(v).multiplyScalar(wAli);\n        coh.multiplyScalar(1 / near).sub(p).multiplyScalar(wCoh * 0.5);\n        acc.add(ali).add(coh);\n      }\n      if (sep.lengthSq() > 0) acc.add(sep.setLength(speed).multiplyScalar(wSep));\n\n      // Soft bounds: steer back toward the centre near the walls.\n      tmp.copy(center).sub(p);\n      tmp.x = Math.abs(p.x - center.x) > bounds.x * 0.85 ? tmp.x : 0;\n      tmp.y = Math.abs(p.y - center.y) > bounds.y * 0.85 ? tmp.y : 0;\n      tmp.z = Math.abs(p.z - center.z) > bounds.z * 0.85 ? tmp.z : 0;\n      acc.add(tmp.multiplyScalar(2.2));\n\n      // Wheel around the centre (birds circling a tower).\n      if (circle > 0) {\n        const rx = p.x - center.x;\n        const rz = p.z - center.z;\n        const r = Math.hypot(rx, rz) || 1e-3;\n        acc.x += (-rz / r) * speed * 1.2 + (rx / r) * (circle - r) * 0.4; // tangent + radius hold\n        acc.z += (rx / r) * speed * 1.2 + (rz / r) * (circle - r) * 0.4;\n        acc.y += (center.y - p.y) * 0.6; // hold height\n      }\n\n      // Wander.\n      acc.x += rng.range(-1, 1) * speed * 0.4;\n      acc.y += rng.range(-1, 1) * speed * (fish ? 0.15 : 0.2);\n      acc.z += rng.range(-1, 1) * speed * 0.4;\n\n      v.addScaledVector(acc, dt);\n      // Keep birds moving; clamp to a speed band.\n      const sp = v.length();\n      if (sp > speed * 1.5) v.setLength(speed * 1.5);\n      else if (sp < speed * 0.6) v.setLength(speed * 0.6);\n      p.addScaledVector(v, dt);\n    }\n    writeMatrices();\n  };\n\n  mesh.onBeforeRender = () => {\n    if (!manual) {\n      const t = nowSeconds();\n      uniforms.uTime.value = t % 1000;\n      step(t - last);\n      last = t;\n    }\n  };\n\n  return {\n    object: mesh,\n    count,\n    positions: pos,\n    setCenter(x, y, z) {\n      center.set(x, y, z);\n    },\n    update(dt) {\n      manual = true;\n      uniforms.uTime.value += dt;\n      step(dt);\n    },\n  };\n}\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  InstancedBufferAttribute,\n  InstancedMesh,\n  Matrix4,\n  MeshStandardMaterial,\n  Vector3,\n} from 'three';\nimport { Rng } from '../core/random';\n\nexport type HerdType = 'deer' | 'sheep';\n\nexport interface HerdOptions {\n  /** deer (tall, slender, antlered) or sheep (short, woolly, tight flock). Default 'deer'. */\n  type?: HerdType;\n  /** How many. Default 12 (deer) / 16 (sheep). */\n  count?: number;\n  /** Where the herd grazes, world XZ (y is taken from the ground). Default [0, 0]. */\n  center?: [number, number];\n  /** Half-extents of the roaming area in XZ, or one number for a square. Default 18. */\n  radius?: [number, number] | number;\n  /** Ground-height handshake — `terrain.heightAt`. Feet clamp to it every frame. Default flat 0. */\n  ground?: (x: number, z: number) => number;\n  /** Cruise speed while walking, m/s. Default 2.2 (deer) / 1.4 (sheep). */\n  speed?: number;\n  /** Body length, metres. Default 1 (deer) / 0.85 (sheep). */\n  size?: number;\n  /** Body colour. Default 0xa9855a deer / 0xe7e2d6 sheep. */\n  color?: number;\n  /** Fraction of time spent grazing (head down, still), 0–1. Default 0.6 (deer) / 0.75 (sheep). */\n  grazing?: number;\n  /** How strongly the body tips to follow the slope, 0–1. Default 0.6. */\n  slopeAlign?: number;\n  /** Steering weights. */\n  separation?: number;\n  alignment?: number;\n  cohesion?: number;\n  seed?: number;\n}\n\nexport interface Herd {\n  /** The instanced herd — add it to the scene. Self-animates. */\n  object: InstancedMesh;\n  count: number;\n  /** Live animal positions (world space) — for gameplay: spook them, count them, herd them. */\n  positions: readonly Vector3[];\n  /** Move the roaming area's centre in XZ (the herd drifts to follow). */\n  setCenter(x: number, z: number): void;\n  /** Advance manually instead of self-driving (for deterministic loops). */\n  update(dt: number): void;\n}\n\n// --- creature geometry ---------------------------------------------------\n//\n// A quadruped built from boxes. Every vertex carries a small gait rig:\n//   aHipY/aHipZ  — the pivot a leg swings about (for a body vertex this is the\n//                  vertex's own y/z, so the swing rotation is a no-op),\n//   aLegPhase    — 0 or π, the two diagonal trot pairs,\n//   aHead        — head/antler weight, for the grazing head-dip.\n// Legs swing about the hip in the vertex shader; the body bobs; the head dips\n// when the animal is grazing. No skeleton, no clips — it's all one attribute set.\n\ninterface Build {\n  pos: number[];\n  hipY: number[];\n  hipZ: number[];\n  legPhase: number[];\n  head: number[];\n  col: number[];\n}\n\nfunction newBuild(): Build {\n  return { pos: [], hipY: [], hipZ: [], legPhase: [], head: [], col: [] };\n}\n\ninterface BoxOpts {\n  /** If set, the box is a swinging leg pivoting about this (y, z). */\n  hip?: [number, number];\n  legPhase?: number;\n  head?: number;\n  color: [number, number, number];\n}\n\n/** Emit a box (min→max corners) as 12 flat-shaded triangles with the gait rig. */\nfunction box(\n  b: Build,\n  min: [number, number, number],\n  max: [number, number, number],\n  o: BoxOpts\n): void {\n  const [x0, y0, z0] = min;\n  const [x1, y1, z1] = max;\n  // 8 corners\n  const v = [\n    [x0, y0, z0], [x1, y0, z0], [x1, y1, z0], [x0, y1, z0], // back (-z)\n    [x0, y0, z1], [x1, y0, z1], [x1, y1, z1], [x0, y1, z1], // front (+z)\n  ];\n  // prettier-ignore\n  const faces = [\n    [0, 1, 2], [0, 2, 3], // -z\n    [5, 4, 7], [5, 7, 6], // +z\n    [4, 0, 3], [4, 3, 7], // -x\n    [1, 5, 6], [1, 6, 2], // +x\n    [3, 2, 6], [3, 6, 7], // +y\n    [4, 5, 1], [4, 1, 0], // -y\n  ];\n  for (const f of faces) {\n    for (const idx of f) {\n      const p = v[idx];\n      b.pos.push(p[0], p[1], p[2]);\n      if (o.hip) {\n        b.hipY.push(o.hip[0]);\n        b.hipZ.push(o.hip[1]);\n      } else {\n        b.hipY.push(p[1]); // rigid: pivot = self → no swing\n        b.hipZ.push(p[2]);\n      }\n      b.legPhase.push(o.legPhase ?? 0);\n      b.head.push(o.head ?? 0);\n      b.col.push(o.color[0], o.color[1], o.color[2]);\n    }\n  }\n}\n\n/**\n * A quadruped facing +Z, standing on y = 0 (feet at the origin plane). `size`\n * scales the whole body; deer are lankier and antlered, sheep are stout and\n * woolly.\n */\nfunction quadrupedGeometry(type: HerdType, size: number): { geo: BufferGeometry; stand: number } {\n  const s = size;\n  const b = newBuild();\n  const deer = type === 'deer';\n\n  // Two-tone: a darker face/legs against the body colour (baked as a vertex-\n  // colour multiplier; the material carries the actual body hue).\n  const BODY: [number, number, number] = [1, 1, 1];\n  const DARK: [number, number, number] = deer ? [0.55, 0.5, 0.45] : [0.32, 0.3, 0.32];\n\n  const legLen = (deer ? 0.62 : 0.42) * s;\n  const legR = (deer ? 0.05 : 0.06) * s;\n  const bodyY = legLen; // torso underside\n  const bodyH = (deer ? 0.34 : 0.42) * s;\n  const bodyHalfX = (deer ? 0.16 : 0.2) * s;\n  const bodyHalfZ = (deer ? 0.5 : 0.42) * s;\n  const stand = legLen; // origin sits legLen above the ground plane\n\n  // Torso.\n  box(b, [-bodyHalfX, bodyY, -bodyHalfZ], [bodyHalfX, bodyY + bodyH, bodyHalfZ], { color: BODY });\n\n  // Legs — four thin boxes, diagonal trot pairs (FL+BR phase 0, FR+BL phase π).\n  const lx = bodyHalfX - legR;\n  const lz = bodyHalfZ - legR * 1.4;\n  const legs: Array<[number, number, number]> = [\n    [-lx, lz, 0], // FL  (front-left,  +z)\n    [lx, lz, Math.PI], // FR\n    [-lx, -lz, Math.PI], // BL\n    [lx, -lz, 0], // BR  (back-right)\n  ];\n  for (const [x, z, phase] of legs) {\n    box(b, [x - legR, 0, z - legR], [x + legR, bodyY + 0.02 * s, z + legR], {\n      hip: [bodyY, z],\n      legPhase: phase,\n      color: DARK,\n    });\n  }\n\n  // Neck + head at the front (+z), lifted; carries aHead for the graze-dip.\n  const neckZ = bodyHalfZ;\n  const neckTop = bodyY + bodyH + (deer ? 0.32 : 0.14) * s;\n  box(\n    b,\n    [-bodyHalfX * 0.6, bodyY + bodyH * 0.4, neckZ - 0.02 * s],\n    [bodyHalfX * 0.6, neckTop, neckZ + (deer ? 0.12 : 0.16) * s],\n    { head: 0.6, color: BODY }\n  );\n  // Head.\n  const hz0 = neckZ + (deer ? 0.06 : 0.1) * s;\n  const hz1 = hz0 + (deer ? 0.26 : 0.24) * s;\n  const hy0 = neckTop - (deer ? 0.16 : 0.16) * s;\n  const hy1 = neckTop + (deer ? 0.1 : 0.06) * s;\n  box(b, [-bodyHalfX * 0.55, hy0, hz0], [bodyHalfX * 0.55, hy1, hz1], { head: 1, color: DARK });\n\n  if (deer) {\n    // A pair of simple antlers — thin angled boxes off the head, head-weighted.\n    const ax = bodyHalfX * 0.4;\n    const ay = hy1;\n    const az = hz1 - 0.06 * s;\n    for (const sgn of [-1, 1]) {\n      box(\n        b,\n        [sgn * ax - 0.02 * s, ay, az - 0.02 * s],\n        [sgn * ax + 0.02 * s, ay + 0.3 * s, az + 0.02 * s],\n        { head: 1, color: DARK }\n      );\n      box(\n        b,\n        [sgn * ax - 0.02 * s, ay + 0.24 * s, az - 0.18 * s],\n        [sgn * ax + 0.02 * s, ay + 0.28 * s, az + 0.02 * s],\n        { head: 1, color: DARK }\n      );\n    }\n  } else {\n    // Sheep: a woolly crown lump on top of the torso for a rounder profile.\n    box(\n      b,\n      [-bodyHalfX * 1.05, bodyY + bodyH * 0.7, -bodyHalfZ * 0.7],\n      [bodyHalfX * 1.05, bodyY + bodyH + 0.12 * s, bodyHalfZ * 0.55],\n      { color: BODY }\n    );\n  }\n\n  // Tail (-z).\n  box(\n    b,\n    [-0.04 * s, bodyY + bodyH * 0.5, -bodyHalfZ - (deer ? 0.12 : 0.08) * s],\n    [0.04 * s, bodyY + bodyH * 0.85, -bodyHalfZ],\n    { color: deer ? BODY : DARK }\n  );\n\n  const geo = new BufferGeometry();\n  geo.setAttribute('position', new BufferAttribute(new Float32Array(b.pos), 3));\n  geo.setAttribute('aHipY', new BufferAttribute(new Float32Array(b.hipY), 1));\n  geo.setAttribute('aHipZ', new BufferAttribute(new Float32Array(b.hipZ), 1));\n  geo.setAttribute('aLegPhase', new BufferAttribute(new Float32Array(b.legPhase), 1));\n  geo.setAttribute('aHead', new BufferAttribute(new Float32Array(b.head), 1));\n  geo.setAttribute('color', new BufferAttribute(new Float32Array(b.col), 3));\n  geo.computeVertexNormals();\n  return { geo, stand };\n}\n\n// --- material: leg gait + head dip in the vertex shader ------------------\n\nfunction herdMaterial(color: number, gaitSpeed: number): {\n  material: MeshStandardMaterial;\n  uniforms: { uTime: { value: number } };\n} {\n  const uniforms = {\n    uTime: { value: 0 },\n    uGaitSpeed: { value: gaitSpeed },\n    uSwingAmp: { value: 0.7 },\n    uBobAmp: { value: 0.03 },\n    uGrazeDip: { value: 0.28 },\n  };\n  const material = new MeshStandardMaterial({\n    color,\n    roughness: 0.85,\n    metalness: 0,\n    flatShading: true,\n    vertexColors: true,\n  });\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.vertexShader = shader.vertexShader\n      .replace(\n        '#include <common>',\n        `#include <common>\n         attribute float aHipY;\n         attribute float aHipZ;\n         attribute float aLegPhase;\n         attribute float aHead;\n         attribute float aPhase;\n         attribute float aMove;\n         uniform float uTime, uGaitSpeed, uSwingAmp, uBobAmp, uGrazeDip;`\n      )\n      .replace(\n        '#include <begin_vertex>',\n        `#include <begin_vertex>\n         {\n           float t = uTime * uGaitSpeed + aPhase;\n           // Legs swing about the hip (rotate y/z about the pivot), scaled by\n           // how fast the animal is actually moving.\n           float swing = sin(t + aLegPhase) * uSwingAmp * aMove;\n           float ly = transformed.y - aHipY;\n           float lz = transformed.z - aHipZ;\n           float c = cos(swing), sn = sin(swing);\n           transformed.y = aHipY + (c * ly - sn * lz);\n           transformed.z = aHipZ + (sn * ly + c * lz);\n           // Body bob at twice stride, only while walking.\n           transformed.y += sin(t * 2.0) * uBobAmp * aMove;\n           // Head dips to graze when standing still.\n           float graze = (1.0 - aMove) * (0.5 + 0.5 * sin(uTime * 1.3 + aPhase));\n           transformed.y -= aHead * graze * uGrazeDip;\n           transformed.z += aHead * graze * uGrazeDip * 0.6;\n         }`\n      );\n  };\n  material.customProgramCacheKey = () => 'scena-herd-v1';\n  return { material, uniforms };\n}\n\nfunction nowSeconds(): number {\n  return typeof performance !== 'undefined' ? performance.now() * 0.001 : 0;\n}\n\n/**\n * A herd of deer or a flock of sheep — ground-dwelling ambient life. A boid\n * simulation steers every animal across the XZ plane (herds clump, so cohesion\n * runs high), while their feet clamp to the terrain every frame through the\n * `ground` handshake (`terrain.heightAt`). Animals graze in place — head down,\n * legs still — then walk a few steps and graze again. The whole herd draws as\n * **one InstancedMesh** whose legs stride and head dips in the vertex shader,\n * scaled by each animal's real speed, so a walking deer strides and a grazing\n * one nibbles. It self-animates from the render loop.\n *\n * `positions` exposes the live animals, so gameplay can read them — spook the\n * herd, count the flock, or wire the leader to a GAMA agent.\n *\n * ```ts\n * const terrain = createTerrain({ seed: 3 });\n * const deer = createHerd({ type: 'deer', center: [0, 0], ground: terrain.heightAt });\n * scene.add(terrain.mesh, deer.object);\n * ```\n */\nexport function createHerd(options: HerdOptions = {}): Herd {\n  const type = options.type ?? 'deer';\n  const sheep = type === 'sheep';\n  const count = options.count ?? (sheep ? 16 : 12);\n  const c = options.center ?? [0, 0];\n  const center = { x: c[0], z: c[1] };\n  const r = options.radius ?? 18;\n  const radius = typeof r === 'number' ? { x: r, z: r } : { x: r[0], z: r[1] };\n  const ground = options.ground ?? (() => 0);\n  const speed = options.speed ?? (sheep ? 1.4 : 2.2);\n  const size = options.size ?? (sheep ? 0.85 : 1);\n  const grazing = options.grazing ?? (sheep ? 0.75 : 0.6);\n  const slopeAlign = options.slopeAlign ?? 0.6;\n  const wSep = options.separation ?? 1.6;\n  const wAli = options.alignment ?? 0.7;\n  const wCoh = options.cohesion ?? (sheep ? 1.6 : 1.1);\n  const rng = new Rng(options.seed ?? 1);\n\n  const { geo, stand } = quadrupedGeometry(type, size);\n  const { material, uniforms } = herdMaterial(options.color ?? (sheep ? 0xe7e2d6 : 0xa9855a), sheep ? 5 : 6);\n\n  // Per-instance gait phase (desync) and live movement amount (gait/graze).\n  const phases = new Float32Array(count);\n  const move = new Float32Array(count);\n  for (let i = 0; i < count; i++) phases[i] = rng.range(0, Math.PI * 2);\n  geo.setAttribute('aPhase', new InstancedBufferAttribute(phases, 1));\n  const moveAttr = new InstancedBufferAttribute(move, 1);\n  moveAttr.setUsage(0x88e8 /* DYNAMIC_DRAW */);\n  geo.setAttribute('aMove', moveAttr);\n\n  const mesh = new InstancedMesh(geo, material, count);\n  mesh.name = `herd-${type}`;\n  mesh.castShadow = true;\n  mesh.frustumCulled = false;\n\n  // Boid state (XZ plane; y is clamped to the ground).\n  const pos: Vector3[] = [];\n  const vel: Vector3[] = [];\n  const graze: number[] = []; // seconds left in the current graze pause\n  for (let i = 0; i < count; i++) {\n    const x = center.x + rng.range(-radius.x, radius.x);\n    const z = center.z + rng.range(-radius.z, radius.z);\n    pos.push(new Vector3(x, ground(x, z) + stand, z));\n    const a = rng.range(0, Math.PI * 2);\n    vel.push(new Vector3(Math.cos(a), 0, Math.sin(a)).multiplyScalar(speed));\n    graze.push(rng.range(0, 4));\n  }\n\n  const m = new Matrix4();\n  const sep = new Vector3();\n  const ali = new Vector3();\n  const coh = new Vector3();\n  const acc = new Vector3();\n  const tmp = new Vector3();\n  const fwd = new Vector3();\n  const up = new Vector3();\n  const right = new Vector3();\n  const upWorld = new Vector3(0, 1, 0);\n  const gn = new Vector3();\n  const neighbor = sheep ? 5 : 7;\n  const sepDist = sheep ? 1.1 : 1.6;\n\n  const groundNormal = (x: number, z: number): Vector3 => {\n    const e = 0.6;\n    const hl = ground(x - e, z);\n    const hr = ground(x + e, z);\n    const hd = ground(x, z - e);\n    const hu = ground(x, z + e);\n    return gn.set(hl - hr, 2 * e, hd - hu).normalize();\n  };\n\n  const writeMatrices = (): void => {\n    for (let i = 0; i < count; i++) {\n      const p = pos[i];\n      const v = vel[i];\n      // Forward from velocity, flattened to the ground plane.\n      fwd.set(v.x, 0, v.z);\n      if (fwd.lengthSq() < 1e-6) fwd.set(0, 0, 1);\n      fwd.normalize();\n      // Up leans toward the terrain normal on slopes.\n      up.copy(upWorld).lerp(groundNormal(p.x, p.z), slopeAlign).normalize();\n      right.copy(up).cross(fwd);\n      if (right.lengthSq() < 1e-6) right.set(1, 0, 0);\n      right.normalize();\n      up.copy(fwd).cross(right).normalize(); // re-orthogonalise\n      m.makeBasis(right, up, fwd);\n      m.setPosition(p.x, p.y, p.z);\n      mesh.setMatrixAt(i, m);\n    }\n    mesh.instanceMatrix.needsUpdate = true;\n  };\n  writeMatrices();\n\n  let manual = false;\n  let last = nowSeconds();\n\n  const step = (dt: number): void => {\n    dt = Math.min(0.05, Math.max(0, dt));\n    for (let i = 0; i < count; i++) {\n      const p = pos[i];\n      const v = vel[i];\n\n      // Grazing rhythm: count down the pause, then flip state.\n      graze[i] -= dt;\n      const isGrazing = graze[i] > 0;\n      if (graze[i] < -0.01) {\n        // Choose the next state: graze for a while, or walk for a while.\n        graze[i] = rng.next() < grazing ? rng.range(2.5, 6) : -rng.range(2, 4.5);\n      }\n\n      sep.set(0, 0, 0);\n      ali.set(0, 0, 0);\n      coh.set(0, 0, 0);\n      let near = 0;\n      for (let j = 0; j < count; j++) {\n        if (j === i) continue;\n        const q = pos[j];\n        const dx = p.x - q.x;\n        const dz = p.z - q.z;\n        const d = Math.hypot(dx, dz);\n        if (d < neighbor) {\n          ali.add(vel[j]);\n          coh.add(q);\n          near++;\n          if (d < sepDist && d > 1e-4) {\n            sep.x += dx / (d * d);\n            sep.z += dz / (d * d);\n          }\n        }\n      }\n\n      acc.set(0, 0, 0);\n      if (!isGrazing) {\n        if (near > 0) {\n          ali.multiplyScalar(1 / near);\n          ali.y = 0;\n          if (ali.lengthSq() > 1e-6) ali.setLength(speed).sub(v).multiplyScalar(wAli);\n          acc.add(ali);\n          coh.multiplyScalar(1 / near);\n          tmp.set(coh.x - p.x, 0, coh.z - p.z).multiplyScalar(wCoh * 0.4);\n          acc.add(tmp);\n        }\n        if (sep.lengthSq() > 0) {\n          sep.y = 0;\n          acc.add(sep.setLength(speed).multiplyScalar(wSep));\n        }\n        // Soft bounds pull strays back toward the grazing ground.\n        tmp.set(center.x - p.x, 0, center.z - p.z);\n        tmp.x = Math.abs(p.x - center.x) > radius.x * 0.85 ? tmp.x : 0;\n        tmp.z = Math.abs(p.z - center.z) > radius.z * 0.85 ? tmp.z : 0;\n        acc.add(tmp.multiplyScalar(1.6));\n        // A little wander.\n        acc.x += rng.range(-1, 1) * speed * 0.5;\n        acc.z += rng.range(-1, 1) * speed * 0.5;\n      }\n\n      v.addScaledVector(acc, dt);\n      v.y = 0;\n      if (isGrazing) {\n        v.multiplyScalar(Math.max(0, 1 - dt * 6)); // brake to a standstill\n      } else {\n        const sp = Math.hypot(v.x, v.z);\n        if (sp > speed * 1.4) v.setLength(speed * 1.4);\n        else if (sp < speed * 0.5) {\n          if (sp < 1e-4) v.set(rng.range(-1, 1), 0, rng.range(-1, 1));\n          v.setLength(speed * 0.5);\n        }\n      }\n      p.x += v.x * dt;\n      p.z += v.z * dt;\n      p.y = ground(p.x, p.z) + stand; // clamp feet to the terrain\n\n      // Smooth the per-instance movement amount that drives the gait/graze.\n      const target = Math.min(1, Math.hypot(v.x, v.z) / (speed * 0.9));\n      move[i] += (target - move[i]) * Math.min(1, dt * 5);\n    }\n    moveAttr.needsUpdate = true;\n    writeMatrices();\n  };\n\n  mesh.onBeforeRender = () => {\n    if (!manual) {\n      const t = nowSeconds();\n      uniforms.uTime.value = t % 1000;\n      step(t - last);\n      last = t;\n    }\n  };\n\n  return {\n    object: mesh,\n    count,\n    positions: pos,\n    setCenter(x, z) {\n      center.x = x;\n      center.z = z;\n    },\n    update(dt) {\n      manual = true;\n      uniforms.uTime.value += dt;\n      step(dt);\n    },\n  };\n}\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  CatmullRomCurve3,\n  Mesh,\n  MeshStandardMaterial,\n  Vector3,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport interface PathOptions {\n  /** Ribbon width. Default 1.8. */\n  width?: number;\n  /** Ground height lookup; a number means flat ground. Default 0. */\n  surface?: number | ((x: number, z: number) => number);\n  /** Samples per world unit of path length. Default 1. */\n  samplesPerUnit?: number;\n  /** Close the path into a loop. Default false. */\n  loop?: boolean;\n  /** Extra clearance added to scatter keep-out circles. Default 0.6. */\n  keepOutMargin?: number;\n  palette?: Palette;\n}\n\nexport interface WorldPath {\n  mesh: Mesh;\n  /** Smoothed centerline draped on the surface — feed straight into a\n   *  GAMA `Path` for patrols, or use as camera dolly points. */\n  route: Vector3[];\n  /** Keep-out circles for `scatter()` so nothing grows on the road. */\n  keepOut: Array<{ center: { x: number; z: number }; radius: number }>;\n  /** Is (x, z) on the path surface? (e.g. to exclude grass) */\n  contains(x: number, z: number): boolean;\n  loop: boolean;\n}\n\n/**\n * A dirt path: a Catmull-Rom-smoothed ribbon draped over the surface.\n * One authored polyline feeds three things at once — the visual ribbon,\n * scatter keep-out, and a patrol route for agents. That's the SCENA\n * handshake applied to level design.\n *\n * ```ts\n * const road = createPath([a, b, c], { surface: terrain.heightAt, loop: true });\n * scene.add(road.mesh);\n * scatter({ ..., keepOut: road.keepOut });\n * agent.addBehavior(new FollowPath(new Path(road.route, road.loop), 1.5));\n * ```\n */\nexport function createPath(\n  points: Array<Vector3 | { x: number; z: number }>,\n  options: PathOptions = {}\n): WorldPath {\n  const width = options.width ?? 1.8;\n  const surface = options.surface ?? 0;\n  const heightAt =\n    typeof surface === 'number' ? () => surface : (x: number, z: number) => surface(x, z);\n  const loop = options.loop ?? false;\n  const palette = options.palette ?? DEFAULT_PALETTE;\n\n  const controls = points.map((p) => new Vector3(p.x, 0, 'z' in p ? p.z : 0));\n  const curve = new CatmullRomCurve3(controls, loop, 'centripetal');\n  const length = curve.getLength();\n  const samples = Math.max(8, Math.ceil(length * (options.samplesPerUnit ?? 1)));\n\n  const route: Vector3[] = [];\n  for (let i = 0; i <= samples; i++) {\n    if (loop && i === samples) break; // avoid duplicate closing point\n    const p = curve.getPoint(i / samples);\n    route.push(new Vector3(p.x, heightAt(p.x, p.z), p.z));\n  }\n\n  // Ribbon strip: left/right edge vertices per sample.\n  const edgeCount = loop ? route.length + 1 : route.length;\n  const positions = new Float32Array(edgeCount * 2 * 3);\n  const direction = new Vector3();\n  const perp = new Vector3();\n  for (let i = 0; i < edgeCount; i++) {\n    const current = route[i % route.length];\n    const previous = route[(i - 1 + route.length) % route.length];\n    const next = route[(i + 1) % route.length];\n    if (!loop && i === 0) direction.subVectors(next, current);\n    else if (!loop && i === edgeCount - 1) direction.subVectors(current, previous);\n    else direction.subVectors(next, previous);\n    perp.set(-direction.z, 0, direction.x).normalize().multiplyScalar(width / 2);\n    const left = i * 6;\n    positions[left] = current.x - perp.x;\n    positions[left + 1] = heightAt(current.x - perp.x, current.z - perp.z) + 0.05;\n    positions[left + 2] = current.z - perp.z;\n    positions[left + 3] = current.x + perp.x;\n    positions[left + 4] = heightAt(current.x + perp.x, current.z + perp.z) + 0.05;\n    positions[left + 5] = current.z + perp.z;\n  }\n  const indices: number[] = [];\n  for (let i = 0; i < edgeCount - 1; i++) {\n    const a = i * 2;\n    indices.push(a, a + 1, a + 2, a + 1, a + 3, a + 2);\n  }\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(positions, 3));\n  geometry.setIndex(indices);\n  geometry.computeVertexNormals();\n\n  const mesh = new Mesh(\n    geometry,\n    new MeshStandardMaterial({\n      color: palette.path,\n      flatShading: true,\n      polygonOffset: true,\n      polygonOffsetFactor: -1,\n    })\n  );\n  mesh.name = 'path';\n\n  const keepOutMargin = options.keepOutMargin ?? 0.6;\n  const keepOut = route\n    .filter((_p, i) => i % 2 === 0 || i === route.length - 1) // never drop the endpoint\n    .map((p) => ({ center: { x: p.x, z: p.z }, radius: width / 2 + keepOutMargin }));\n\n  const half = width / 2;\n  const contains = (x: number, z: number): boolean => {\n    const count = loop ? route.length : route.length - 1;\n    for (let i = 0; i < count; i++) {\n      const a = route[i];\n      const b = route[(i + 1) % route.length];\n      const abx = b.x - a.x;\n      const abz = b.z - a.z;\n      const lengthSq = abx * abx + abz * abz || 1e-9;\n      const t = Math.max(0, Math.min(1, ((x - a.x) * abx + (z - a.z) * abz) / lengthSq));\n      const dx = x - (a.x + abx * t);\n      const dz = z - (a.z + abz * t);\n      if (dx * dx + dz * dz <= half * half) return true;\n    }\n    return false;\n  };\n\n  return { mesh, route, keepOut, contains, loop };\n}\n","import {\n  Color,\n  Group,\n  InstancedMesh,\n  Matrix4,\n  Mesh,\n  MeshBasicMaterial,\n  OctahedronGeometry,\n  Quaternion,\n  RingGeometry,\n  Vector3,\n  AdditiveBlending,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Impact effects — the visible half of game feel.\n *\n * A hit that only changes a number is a spreadsheet event. What sells\n * contact is debris: dust where a boot lands, sparks where metal meets\n * stone, droplets where something takes the water. These are the burst\n * effects for those moments, built the way everything here is built —\n * low-poly, seeded, and cheap enough to spend freely.\n *\n * Two draw calls for every particle in flight, whatever you spawn. All\n * matte particles share one InstancedMesh, all glowing ones another, and\n * a particle is a tiny octahedron rather than a billboarded sprite — a\n * solid needs no camera to face, so `update(dt)` needs no camera, and at\n * this size the silhouette difference is invisible while the *tumble* a\n * flat sprite cannot do reads clearly. Fade is done with scale, not\n * opacity: instanced per-particle opacity would cost a custom shader, and\n * a mote shrinking to nothing is indistinguishable from one fading out.\n *\n * ```ts\n * const fx = createEffects({ seed: 4 });\n * scene.add(fx.group);\n * fx.burst('dust', boot.position);\n * fx.burst('sparks', hit.point, { direction: hit.normal });\n * fx.ring(splashPoint, { color: 0xbfe3ff });\n * // per frame:\n * fx.update(dt);\n * ```\n */\n\nexport type BurstKind = 'dust' | 'sparks' | 'debris' | 'splash' | 'confetti';\n\nexport interface EffectsOptions {\n  /** Maximum simultaneous particles; past it the oldest are recycled. Default 320. */\n  capacity?: number;\n  /** Ground height particles settle against. Default 0. */\n  floor?: number;\n  /** Same seed, same debris. Default 1. */\n  seed?: number;\n}\n\nexport interface BurstOptions {\n  /** Particle count for this burst. Default depends on kind. */\n  count?: number;\n  /** Tint override (confetti ignores it — confetti is many colours or it is litter). */\n  color?: number;\n  /** Particle size multiplier. Default 1. */\n  size?: number;\n  /** Initial speed multiplier. Default 1. */\n  speed?: number;\n  /** Launch direction bias (an impact normal). Default straight up. */\n  direction?: Vector3;\n  /** Cone spread around the direction, 0 tight – 1 hemisphere. Default per kind. */\n  spread?: number;\n}\n\nexport interface RingOptions {\n  /** Final radius in metres. Default 1.4. */\n  radius?: number;\n  /** Seconds to reach it and fade. Default 0.55. */\n  life?: number;\n  color?: number;\n  /** Starting opacity. Default 0.65. */\n  opacity?: number;\n}\n\nexport interface Effects {\n  /** Add this to the scene; both particle meshes and all rings live in it. */\n  group: Group;\n  /** Spend some particles at a point. */\n  burst(kind: BurstKind, at: Vector3, options?: BurstOptions): void;\n  /** An expanding, fading ground ring — landings, splashes, shockwaves. */\n  ring(at: Vector3, options?: RingOptions): void;\n  /** Live particle count, for tests and debug readouts. */\n  readonly alive: number;\n  update(dt: number): void;\n}\n\ninterface KindVoice {\n  count: number;\n  speed: number;\n  spread: number;\n  /** Downward pull in m/s² — dust barely falls, debris drops like debris. */\n  gravity: number;\n  /** Velocity kept per second — dust bleeds speed into the air, sparks keep it. */\n  drag: number;\n  life: [number, number];\n  size: [number, number];\n  /** Scale over life: >1 grows (a dust cloud billows), <1 shrinks. */\n  grow: number;\n  spin: number;\n  glow: boolean;\n  bounce: boolean;\n  flutter: number;\n  colors: number[];\n}\n\nconst VOICES: Record<BurstKind, KindVoice> = {\n  dust: {\n    count: 10, speed: 1.7, spread: 0.85, gravity: 1.2, drag: 0.12,\n    life: [0.5, 0.9], size: [0.09, 0.16], grow: 2.6, spin: 2, glow: false,\n    bounce: false, flutter: 0, colors: [0xb8a98c, 0xa89a80, 0xc7bba1],\n  },\n  sparks: {\n    count: 14, speed: 7.5, spread: 0.5, gravity: 9.8, drag: 0.75,\n    life: [0.3, 0.55], size: [0.05, 0.09], grow: 0.25, spin: 6, glow: true,\n    bounce: true, flutter: 0, colors: [0xffc76a, 0xffa73d, 0xfff3b0],\n  },\n  debris: {\n    count: 8, speed: 4.2, spread: 0.7, gravity: 9.8, drag: 0.85,\n    life: [0.8, 1.3], size: [0.08, 0.17], grow: 0.9, spin: 9, glow: false,\n    bounce: true, flutter: 0, colors: [0x8a7a64, 0x6e6152, 0x9c8c74],\n  },\n  splash: {\n    count: 12, speed: 3.4, spread: 0.55, gravity: 9.8, drag: 0.9,\n    life: [0.4, 0.65], size: [0.05, 0.1], grow: 0.5, spin: 3, glow: false,\n    bounce: false, flutter: 0, colors: [0xcfe8f7, 0xa8d4ee, 0xe8f5fc],\n  },\n  confetti: {\n    count: 24, speed: 3.2, spread: 0.9, gravity: 1.6, drag: 0.55,\n    life: [1.6, 2.6], size: [0.06, 0.1], grow: 1, spin: 11, glow: false,\n    bounce: false, flutter: 2.4,\n    colors: [0xef6a6a, 0xf3c34e, 0x6fcf74, 0x64a9ef, 0xc77df0, 0xf090c8],\n  },\n};\n\ninterface Particle {\n  aliveFlag: boolean;\n  glow: boolean;\n  pos: Vector3;\n  vel: Vector3;\n  quat: Quaternion;\n  axis: Vector3;\n  spin: number;\n  age: number;\n  life: number;\n  size: number;\n  grow: number;\n  gravity: number;\n  drag: number;\n  bounce: boolean;\n  flutter: number;\n  phase: number;\n  born: number;\n}\n\nconst ZERO = new Matrix4().makeScale(0, 0, 0);\n\nexport function createEffects(options: EffectsOptions = {}): Effects {\n  const capacity = Math.max(8, options.capacity ?? 320);\n  const floor = options.floor ?? 0;\n  const rng = new Rng(options.seed ?? 1);\n  const group = new Group();\n  group.name = 'effects';\n\n  const shard = new OctahedronGeometry(0.5, 0);\n  const matte = new InstancedMesh(\n    shard,\n    new MeshBasicMaterial({ transparent: true, opacity: 0.95 }),\n    capacity\n  );\n  const glow = new InstancedMesh(\n    shard,\n    new MeshBasicMaterial({\n      transparent: true,\n      blending: AdditiveBlending,\n      depthWrite: false,\n      toneMapped: false,\n    }),\n    capacity\n  );\n  // Particles scatter across the world; the meshes' own bounds mean nothing.\n  matte.frustumCulled = false;\n  glow.frustumCulled = false;\n  const white = new Color(0xffffff);\n  for (let i = 0; i < capacity; i++) {\n    matte.setMatrixAt(i, ZERO);\n    glow.setMatrixAt(i, ZERO);\n    matte.setColorAt(i, white);\n    glow.setColorAt(i, white);\n  }\n  group.add(matte, glow);\n\n  const particles: Particle[] = Array.from({ length: capacity }, () => ({\n    aliveFlag: false, glow: false,\n    pos: new Vector3(), vel: new Vector3(),\n    quat: new Quaternion(), axis: new Vector3(0, 1, 0),\n    spin: 0, age: 0, life: 1, size: 0.1, grow: 1,\n    gravity: 0, drag: 1, bounce: false, flutter: 0, phase: 0, born: 0,\n  }));\n  let aliveCount = 0;\n  let stamp = 0; // monotonic birth order, so recycling always takes the OLDEST\n\n  /** A slot for a new particle: a dead one if any, else the oldest living. */\n  const slot = (): Particle => {\n    let oldest: Particle | null = null;\n    for (const p of particles) {\n      if (!p.aliveFlag) return p;\n      if (!oldest || p.born < oldest.born) oldest = p;\n    }\n    aliveCount--; // the oldest is being evicted, not expiring\n    return oldest as Particle;\n  };\n\n  const scratchDir = new Vector3();\n  const scratchColor = new Color();\n\n  const burst = (kind: BurstKind, at: Vector3, opts: BurstOptions = {}): void => {\n    const voice = VOICES[kind];\n    const n = Math.max(1, opts.count ?? voice.count);\n    const speed = (opts.speed ?? 1) * voice.speed;\n    const spread = opts.spread ?? voice.spread;\n    const dir = scratchDir.copy(opts.direction ?? UP);\n    if (dir.lengthSq() < 1e-8) dir.set(0, 1, 0);\n    dir.normalize();\n\n    for (let k = 0; k < n; k++) {\n      const p = slot();\n      const index = particles.indexOf(p);\n      // An evicted particle may change pools (matte ↔ glow); zero BOTH of its\n      // slots or the old pool keeps rendering the stale matrix forever.\n      matte.setMatrixAt(index, ZERO);\n      glow.setMatrixAt(index, ZERO);\n      p.aliveFlag = true;\n      p.born = stamp++;\n      p.glow = voice.glow;\n      p.pos.copy(at);\n      // A random direction inside the cone: lerp from the bias axis toward a\n      // fully random one by `spread`, so 0 is a jet and 1 is a puffball.\n      p.vel\n        .set(rng.range(-1, 1), rng.range(-1, 1), rng.range(-1, 1))\n        .normalize()\n        .multiplyScalar(spread)\n        .add(dir)\n        .normalize()\n        .multiplyScalar(speed * rng.range(0.55, 1.25));\n      p.quat.set(0, 0, 0, 1);\n      p.axis.set(rng.range(-1, 1), rng.range(-1, 1), rng.range(-1, 1)).normalize();\n      p.spin = rng.jitter(voice.spin, voice.spin * 0.5);\n      p.age = 0;\n      p.life = rng.range(voice.life[0], voice.life[1]);\n      p.size = rng.range(voice.size[0], voice.size[1]) * (opts.size ?? 1);\n      p.grow = voice.grow;\n      p.gravity = voice.gravity;\n      p.drag = voice.drag;\n      p.bounce = voice.bounce;\n      p.flutter = voice.flutter;\n      p.phase = rng.range(0, Math.PI * 2);\n      const base = opts.color !== undefined && kind !== 'confetti'\n        ? scratchColor.setHex(opts.color)\n        : scratchColor.setHex(rng.pick(voice.colors));\n      // A pinch of value variation so a burst is a material, not a swatch.\n      (p.glow ? glow : matte).setColorAt(index, base.multiplyScalar(rng.range(0.85, 1.1)));\n      aliveCount++;\n    }\n    if (matte.instanceColor) matte.instanceColor.needsUpdate = true;\n    if (glow.instanceColor) glow.instanceColor.needsUpdate = true;\n  };\n\n  // -- Rings ----------------------------------------------------------------\n\n  interface RingFx {\n    mesh: Mesh;\n    material: MeshBasicMaterial;\n    age: number;\n    life: number;\n    radius: number;\n    opacity: number;\n  }\n  const ringGeometry = new RingGeometry(0.72, 1, 40);\n  ringGeometry.rotateX(-Math.PI / 2);\n  const rings: RingFx[] = [];\n\n  const ring = (at: Vector3, opts: RingOptions = {}): void => {\n    let fx = rings.find((r) => r.age >= r.life);\n    if (!fx) {\n      if (rings.length >= 16) {\n        fx = rings.reduce((a, b) => (a.age / a.life > b.age / b.life ? b : a));\n      } else {\n        const material = new MeshBasicMaterial({ transparent: true, depthWrite: false });\n        const mesh = new Mesh(ringGeometry, material);\n        mesh.renderOrder = 1;\n        group.add(mesh);\n        fx = { mesh, material, age: 0, life: 1, radius: 1, opacity: 1 };\n        rings.push(fx);\n      }\n    }\n    fx.age = 0;\n    fx.life = opts.life ?? 0.55;\n    fx.radius = opts.radius ?? 1.4;\n    fx.opacity = opts.opacity ?? 0.65;\n    fx.material.color.setHex(opts.color ?? 0xd8d2c4);\n    fx.material.opacity = fx.opacity;\n    fx.mesh.visible = true;\n    // A hair above the point, so a ground ring does not z-fight the ground.\n    fx.mesh.position.copy(at).y += 0.02;\n    fx.mesh.scale.setScalar(0.001);\n  };\n\n  // -- Simulation -----------------------------------------------------------\n\n  const matrix = new Matrix4();\n  const spinQuat = new Quaternion();\n  const scale = new Vector3();\n\n  const integrate = (step: number): void => {\n    for (let i = 0; i < capacity; i++) {\n      const p = particles[i];\n      if (!p.aliveFlag) continue;\n      p.age += step;\n      if (p.age >= p.life) {\n        p.aliveFlag = false;\n        aliveCount--;\n        (p.glow ? glow : matte).setMatrixAt(i, ZERO);\n        continue;\n      }\n      p.vel.y -= p.gravity * step;\n      const keep = Math.pow(p.drag, step);\n      p.vel.multiplyScalar(keep);\n      if (p.flutter > 0) {\n        // Confetti falls the way paper falls — nothing straight about it.\n        p.vel.x += Math.sin(p.age * 7 + p.phase) * p.flutter * step;\n        p.vel.z += Math.cos(p.age * 6.3 + p.phase) * p.flutter * step;\n      }\n      p.pos.addScaledVector(p.vel, step);\n      if (p.pos.y < floor + p.size * 0.5) {\n        p.pos.y = floor + p.size * 0.5;\n        if (p.bounce) {\n          if (p.vel.y < 0) {\n            p.vel.y *= -0.35;\n            p.vel.x *= 0.6;\n            p.vel.z *= 0.6;\n          }\n        } else {\n          // Dust pancakes and confetti rests — nothing sinks through a floor.\n          p.vel.y = 0;\n          const settle = Math.pow(0.02, step);\n          p.vel.x *= settle;\n          p.vel.z *= settle;\n        }\n      }\n      spinQuat.setFromAxisAngle(p.axis, p.spin * step);\n      p.quat.premultiply(spinQuat);\n\n      const t = p.age / p.life;\n      // Ease from 1 to `grow`, then collapse to zero in the last quarter —\n      // the collapse IS the fade.\n      const growth = 1 + (p.grow - 1) * t;\n      const vanish = t > 0.75 ? 1 - (t - 0.75) / 0.25 : 1;\n      const s = Math.max(p.size * growth * vanish, 1e-4);\n      matrix.compose(p.pos, p.quat, scale.setScalar(s));\n      (p.glow ? glow : matte).setMatrixAt(i, matrix);\n    }\n  };\n\n  const update = (dt: number): void => {\n    // A lag spike must advance time honestly — a clamp that freezes the\n    // clock turns one slow frame into effects that outstay their lives.\n    // But explicit Euler explodes on big steps, so the half-second cap is\n    // walked in sub-steps the integrator can survive.\n    const total = Number.isFinite(dt) ? Math.min(Math.max(dt, 0), 0.5) : 0;\n    let remaining = total;\n    while (remaining > 1e-9) {\n      const step = Math.min(remaining, 1 / 30);\n      remaining -= step;\n      integrate(step);\n    }\n    matte.instanceMatrix.needsUpdate = true;\n    glow.instanceMatrix.needsUpdate = true;\n\n    for (const fx of rings) {\n      if (fx.age >= fx.life) continue;\n      fx.age += total;\n      const t = Math.min(fx.age / fx.life, 1);\n      // Fast out, easing as it goes — how a real wavefront loses steam.\n      const eased = 1 - (1 - t) * (1 - t);\n      fx.mesh.scale.setScalar(Math.max(fx.radius * eased, 0.001));\n      fx.material.opacity = fx.opacity * (1 - t);\n      if (t >= 1) fx.mesh.visible = false;\n    }\n  };\n\n  return {\n    group,\n    burst,\n    ring,\n    get alive() {\n      return aliveCount;\n    },\n    update,\n  };\n}\n\nconst UP = new Vector3(0, 1, 0);\n","import {\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  DoubleSide,\n  Mesh,\n  MeshBasicMaterial,\n  Vector3,\n} from 'three';\n\n/**\n * A motion trail — the ribbon a fast thing leaves behind.\n *\n * Feed it a position every frame; it keeps the recent past as a ribbon\n * that tapers and fades toward the tail. The fade is real per-vertex\n * alpha: the colour attribute carries four components, which three treats\n * as RGBA when the material opts into vertex colours — no custom shader.\n *\n * The ribbon is laid perpendicular to the motion within the plane\n * orthogonal to `up`. With the default Y-up that is exactly right for\n * vehicles, skids and running characters; for a sword swing, pass the\n * swing plane's normal as `up`.\n *\n * ```ts\n * const trail = createTrail({ color: 0x8fd0ff, width: 0.3 });\n * scene.add(trail.mesh);\n * // per frame:\n * trail.push(kart.position);\n * trail.update(dt);\n * ```\n */\n\nexport interface TrailOptions {\n  /** Maximum points kept. Default 48. */\n  length?: number;\n  /** Ribbon width at the head, in metres — it tapers to zero at the tail. Default 0.25. */\n  width?: number;\n  /** Seconds a point survives. Default 0.7. */\n  life?: number;\n  color?: number;\n  /** Head opacity. Default 0.7. */\n  opacity?: number;\n  /** Normal of the ribbon plane. Default +Y (a ground trail). */\n  up?: Vector3;\n  /** Points closer than this to the last are ignored (metres). Default 0.05. */\n  minDistance?: number;\n}\n\nexport interface Trail {\n  mesh: Mesh;\n  /** Record the emitter's position this frame. */\n  push(point: Vector3): void;\n  /** Age the ribbon; drop what has faded. */\n  update(dt: number): void;\n  /** Forget everything — teleports should not draw a streak across the map. */\n  clear(): void;\n  /** Live point count, for tests and debug readouts. */\n  readonly count: number;\n}\n\ninterface TrailPoint {\n  pos: Vector3;\n  age: number;\n}\n\nexport function createTrail(options: TrailOptions = {}): Trail {\n  const length = Math.max(4, options.length ?? 48);\n  const width = options.width ?? 0.25;\n  const life = options.life ?? 0.7;\n  const opacity = options.opacity ?? 0.7;\n  const up = (options.up ?? new Vector3(0, 1, 0)).clone().normalize();\n  const minDistance = options.minDistance ?? 0.05;\n  const color = new Color(options.color ?? 0xffffff);\n\n  const points: TrailPoint[] = [];\n\n  // Two vertices per point, preallocated for the maximum; drawRange does the\n  // rest. Rebuilding attributes per frame would thrash the GC for nothing.\n  const positions = new Float32Array(length * 2 * 3);\n  const colors = new Float32Array(length * 2 * 4);\n  const indices = new Uint16Array((length - 1) * 6);\n  for (let i = 0; i < length - 1; i++) {\n    const a = i * 2;\n    indices.set([a, a + 1, a + 2, a + 2, a + 1, a + 3], i * 6);\n  }\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(positions, 3));\n  geometry.setAttribute('color', new BufferAttribute(colors, 4));\n  geometry.setIndex(new BufferAttribute(indices, 1));\n  geometry.setDrawRange(0, 0);\n\n  const material = new MeshBasicMaterial({\n    vertexColors: true,\n    transparent: true,\n    depthWrite: false,\n    side: DoubleSide,\n  });\n  const mesh = new Mesh(geometry, material);\n  mesh.frustumCulled = false; // the ribbon goes where the emitter went\n  mesh.name = 'trail';\n\n  const dir = new Vector3();\n  const side = new Vector3();\n  const lastSide = new Vector3(1, 0, 0);\n\n  const rebuild = (): void => {\n    const n = points.length;\n    if (n < 2) {\n      geometry.setDrawRange(0, 0);\n      return;\n    }\n    for (let i = 0; i < n; i++) {\n      const point = points[i];\n      // Direction through this point: neighbour to neighbour, so corners\n      // get the average of the segments meeting there.\n      const ahead = points[Math.max(i - 1, 0)].pos;\n      const behind = points[Math.min(i + 1, n - 1)].pos;\n      dir.subVectors(ahead, behind);\n      side.crossVectors(up, dir);\n      // A stall (zero-length segment) keeps the previous side vector: a\n      // ribbon must never collapse to NaN because the emitter stood still.\n      if (side.lengthSq() < 1e-10) side.copy(lastSide);\n      else side.normalize();\n      lastSide.copy(side);\n\n      const fade = Math.max(1 - point.age / life, 0);\n      const taper = 1 - i / (n - 1);\n      const w = (width / 2) * fade * (0.25 + 0.75 * taper);\n      const v = i * 6;\n      positions[v] = point.pos.x + side.x * w;\n      positions[v + 1] = point.pos.y + side.y * w;\n      positions[v + 2] = point.pos.z + side.z * w;\n      positions[v + 3] = point.pos.x - side.x * w;\n      positions[v + 4] = point.pos.y - side.y * w;\n      positions[v + 5] = point.pos.z - side.z * w;\n      const c = i * 8;\n      const alpha = opacity * fade * fade;\n      for (const offset of [0, 4]) {\n        colors[c + offset] = color.r;\n        colors[c + offset + 1] = color.g;\n        colors[c + offset + 2] = color.b;\n        colors[c + offset + 3] = alpha;\n      }\n    }\n    geometry.setDrawRange(0, (n - 1) * 6);\n    geometry.attributes.position.needsUpdate = true;\n    geometry.attributes.color.needsUpdate = true;\n  };\n\n  return {\n    mesh,\n    push(point: Vector3): void {\n      const head = points[0];\n      if (head && head.pos.distanceToSquared(point) < minDistance * minDistance) return;\n      const recycled = points.length >= length ? (points.pop() as TrailPoint) : null;\n      const entry = recycled ?? { pos: new Vector3(), age: 0 };\n      entry.pos.copy(point);\n      entry.age = 0;\n      points.unshift(entry);\n    },\n    update(dt: number): void {\n      const step = Number.isFinite(dt) ? Math.max(dt, 0) : 0;\n      for (const point of points) point.age += step;\n      while (points.length && points[points.length - 1].age >= life) points.pop();\n      rebuild();\n    },\n    clear(): void {\n      points.length = 0;\n      geometry.setDrawRange(0, 0);\n    },\n    get count() {\n      return points.length;\n    },\n  };\n}\n","import {\n  Color,\n  DoubleSide,\n  InstancedBufferAttribute,\n  InstancedMesh,\n  Matrix4,\n  PlaneGeometry,\n  Quaternion,\n  ShaderMaterial,\n  Vector3,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Marks — the world remembering what happened on it.\n *\n * A skid mark is the cheapest persistence-of-action trick in games: the\n * corner you overcooked is still written on the tarmac three laps later.\n * This is a pool of ground decals — skids, footprints, scorches — that\n * fade out over tens of seconds and recycle their oldest slot when full.\n *\n * One draw call for every mark on the map. The pool is a single\n * InstancedMesh of unit quads; which SHAPE a quad shows is decided in the\n * fragment shader by a per-instance attribute (a soft-ended streak, an\n * ellipse, a radial scorch), and the slow fade is a second per-instance\n * attribute — the two things instancing famously cannot vary are exactly\n * the two things a decal needs, so the shader carries them.\n *\n * ```ts\n * const marks = createMarks({ seed: 2 });\n * scene.add(marks.mesh);\n * marks.stamp('skid', kart.position, kart.heading, { length: 1.4 });\n * marks.stamp('footprint', foot.position, walkDir);\n * // per frame:\n * marks.update(dt);\n * ```\n */\n\nexport type GroundMarkKind = 'skid' | 'footprint' | 'scorch';\n\nexport interface MarksOptions {\n  /** Maximum marks on the ground at once. Default 96. */\n  capacity?: number;\n  /** Seconds a mark takes to fade away. Default 18. */\n  fade?: number;\n  /** Mark colour — dark, it multiplies against the ground. Default 0x1c1a17. */\n  color?: number;\n  /** Height above y=0 the decals float to dodge z-fighting. Default 0.015. */\n  lift?: number;\n  seed?: number;\n}\n\nexport interface StampOptions {\n  /** Along-direction size in metres. Defaults: skid 1.2, footprint 0.26, scorch = width. */\n  length?: number;\n  /** Across-direction size in metres. Defaults: skid 0.16, footprint 0.11, scorch 0.9. */\n  width?: number;\n  /** Starting opacity 0..1. Default 0.75 (footprint 0.5). */\n  strength?: number;\n}\n\nexport interface Marks {\n  mesh: InstancedMesh;\n  /**\n   * Leave a mark at `at`, oriented along `direction` (XZ). Scorches ignore\n   * the direction and take a seeded rotation instead — burn marks have no\n   * heading.\n   */\n  stamp(kind: GroundMarkKind, at: Vector3, direction?: Vector3, options?: StampOptions): void;\n  /** Live (visible) mark count. */\n  readonly count: number;\n  update(dt: number): void;\n  /** Wipe the ground clean. */\n  clear(): void;\n}\n\nconst SHAPE: Record<GroundMarkKind, number> = { skid: 0, footprint: 1, scorch: 2 };\n\nconst DEFAULTS: Record<GroundMarkKind, { length: number; width: number; strength: number }> = {\n  skid: { length: 1.2, width: 0.16, strength: 0.75 },\n  footprint: { length: 0.26, width: 0.11, strength: 0.5 },\n  scorch: { length: 0.9, width: 0.9, strength: 0.75 },\n};\n\nexport function createMarks(options: MarksOptions = {}): Marks {\n  const capacity = Math.max(8, options.capacity ?? 96);\n  const fade = options.fade ?? 18;\n  const lift = options.lift ?? 0.015;\n  const rng = new Rng(options.seed ?? 1);\n\n  const geometry = new PlaneGeometry(1, 1);\n  geometry.rotateX(-Math.PI / 2); // flat on the ground, +X is \"along\"\n  const alpha = new InstancedBufferAttribute(new Float32Array(capacity), 1);\n  const shape = new InstancedBufferAttribute(new Float32Array(capacity), 1);\n  geometry.setAttribute('aAlpha', alpha);\n  geometry.setAttribute('aShape', shape);\n\n  const material = new ShaderMaterial({\n    transparent: true,\n    depthWrite: false,\n    side: DoubleSide,\n    uniforms: { uColor: { value: new Color(options.color ?? 0x1c1a17) } },\n    vertexShader: /* glsl */ `\n      attribute float aAlpha;\n      attribute float aShape;\n      varying vec2 vUv;\n      varying float vAlpha;\n      varying float vShape;\n      void main() {\n        vUv = uv;\n        vAlpha = aAlpha;\n        vShape = aShape;\n        gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);\n      }\n    `,\n    fragmentShader: /* glsl */ `\n      uniform vec3 uColor;\n      varying vec2 vUv;\n      varying float vAlpha;\n      varying float vShape;\n      void main() {\n        vec2 p = vUv - 0.5;\n        float mask;\n        if (vShape < 0.5) {\n          // Skid: a streak, soft across, softer still at the ends.\n          float across = 1.0 - smoothstep(0.28, 0.5, abs(p.y));\n          float along = 1.0 - smoothstep(0.32, 0.5, abs(p.x));\n          mask = across * along;\n        } else if (vShape < 1.5) {\n          // Footprint: an ellipse, slightly heavier at the heel end.\n          float d = length(p * vec2(2.0, 2.4));\n          mask = (1.0 - smoothstep(0.62, 1.0, d)) * (0.75 + 0.25 * smoothstep(0.5, -0.5, p.x));\n        } else {\n          // Scorch: darkest at the centre, ragged-ish falloff.\n          float d = length(p) * 2.0;\n          mask = 1.0 - smoothstep(0.35, 1.0, d);\n        }\n        float a = mask * vAlpha;\n        if (a < 0.004) discard;\n        gl_FragColor = vec4(uColor, a);\n      }\n    `,\n  });\n\n  const mesh = new InstancedMesh(geometry, material, capacity);\n  mesh.frustumCulled = false; // marks are wherever the game happened\n  mesh.renderOrder = 1;\n  mesh.name = 'marks';\n\n  const zero = new Matrix4().makeScale(0, 0, 0);\n  for (let i = 0; i < capacity; i++) mesh.setMatrixAt(i, zero);\n  mesh.instanceMatrix.needsUpdate = true;\n\n  // Per-slot state: current alpha and a monotonic birth stamp so a full\n  // pool always recycles its OLDEST mark, not an arbitrary one.\n  const level = new Float32Array(capacity);\n  const born = new Float32Array(capacity);\n  let stampCounter = 0;\n  let liveCount = 0;\n\n  const matrix = new Matrix4();\n  const quat = new Quaternion();\n  const scale = new Vector3();\n  const pos = new Vector3();\n  const UP = new Vector3(0, 1, 0);\n\n  return {\n    mesh,\n    stamp(kind, at, direction, opts = {}) {\n      const preset = DEFAULTS[kind];\n      // A dead slot if there is one, else evict the oldest.\n      let index = -1;\n      for (let i = 0; i < capacity; i++) {\n        if (level[i] <= 0) { index = i; break; }\n      }\n      if (index === -1) {\n        index = 0;\n        for (let i = 1; i < capacity; i++) if (born[i] < born[index]) index = i;\n      } else {\n        liveCount++;\n      }\n\n      const angle =\n        kind === 'scorch' || !direction || direction.lengthSq() < 1e-10\n          ? rng.range(0, Math.PI * 2)\n          : Math.atan2(-direction.z, direction.x);\n      quat.setFromAxisAngle(UP, angle);\n      scale.set(opts.length ?? preset.length, 1, opts.width ?? preset.width);\n      pos.copy(at);\n      pos.y += lift;\n      matrix.compose(pos, quat, scale);\n      mesh.setMatrixAt(index, matrix);\n      mesh.instanceMatrix.needsUpdate = true;\n\n      level[index] = Math.min(Math.max(opts.strength ?? preset.strength, 0), 1);\n      born[index] = stampCounter++;\n      shape.setX(index, SHAPE[kind]);\n      alpha.setX(index, level[index]);\n      shape.needsUpdate = true;\n      alpha.needsUpdate = true;\n    },\n    update(dt) {\n      const step = Number.isFinite(dt) ? Math.max(dt, 0) : 0;\n      if (step === 0) return;\n      let changed = false;\n      for (let i = 0; i < capacity; i++) {\n        if (level[i] <= 0) continue;\n        level[i] -= step / fade;\n        if (level[i] <= 0) {\n          level[i] = 0;\n          liveCount--;\n          mesh.setMatrixAt(i, zero);\n          mesh.instanceMatrix.needsUpdate = true;\n        }\n        alpha.setX(i, level[i]);\n        changed = true;\n      }\n      if (changed) alpha.needsUpdate = true;\n    },\n    clear() {\n      for (let i = 0; i < capacity; i++) {\n        level[i] = 0;\n        alpha.setX(i, 0);\n        mesh.setMatrixAt(i, zero);\n      }\n      liveCount = 0;\n      alpha.needsUpdate = true;\n      mesh.instanceMatrix.needsUpdate = true;\n    },\n    get count() {\n      return liveCount;\n    },\n  };\n}\n","import {\n  AdditiveBlending,\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  Group,\n  HemisphereLight,\n  Mesh,\n  Points,\n  ShaderMaterial,\n  Vector3,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Room, RoomWindow } from '../kits/room';\n\nexport interface InteriorSun {\n  /** Sun elevation in [-1, 1] — same scale as `DayCycle.sunElevation`. */\n  elevation: number;\n  /**\n   * Compass angle of the sun in radians: 0 = +z, π/2 = +x, -π/2 = -x.\n   * Default 0.35 (a pleasant morning slant).\n   */\n  azimuth?: number;\n}\n\nexport interface InteriorLightOptions {\n  /**\n   * A `DayCycle` (or anything with `sunElevation` + `timeOfDay`) to follow:\n   * each `update()` re-aims the shafts and re-grades the fill from it.\n   */\n  cycle?: { readonly sunElevation: number; timeOfDay: number };\n  /** Static sun when no cycle is bound. Default { elevation: 0.75, azimuth: 0.35 }. */\n  sun?: InteriorSun;\n  /** Peak shaft opacity (0-1). Default 0.16. */\n  shaftStrength?: number;\n  /** Dust motes drifting in each shaft. Default 26; 0 disables. */\n  dust?: number;\n  palette?: Palette;\n}\n\nexport interface InteriorLight {\n  /** Already added to `room.group` (shafts are room-local). */\n  group: Group;\n  /** The ambient fill — exposed for manual grading. */\n  hemisphere: HemisphereLight;\n  /** Aim the sun by hand (ignores any bound cycle until the next update). */\n  setSun(sun: InteriorSun): void;\n  /** Re-read the bound day cycle, if any. Call from the game loop. */\n  update(dt?: number): void;\n}\n\nconst SHAFT_VERT = /* glsl */ `\nattribute float aFade;\nvarying float vFade;\nvoid main() {\n  vFade = aFade;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}`;\n\nconst SHAFT_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uStrength;\nvarying float vFade;\nvoid main() {\n  float alpha = uStrength * (1.0 - vFade * 0.85);\n  gl_FragColor = vec4(uColor, alpha);\n}`;\n\nconst PATCH_VERT = /* glsl */ `\nvarying vec2 vUv;\nvoid main() {\n  vUv = uv;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}`;\n\nconst PATCH_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uStrength;\nvarying vec2 vUv;\nvoid main() {\n  float d = length(vUv - 0.5) * 2.0;\n  float alpha = uStrength * smoothstep(1.0, 0.15, d);\n  gl_FragColor = vec4(uColor, alpha);\n}`;\n\nconst DUST_VERT = /* glsl */ `\nattribute vec3 aCell;   // (across, up, along) in [0,1] within the shaft prism\nattribute float aPhase;\nuniform vec3 uP;        // window opening center\nuniform vec3 uR;        // right axis * opening width\nuniform vec3 uUp;       // up axis * opening height\nuniform vec3 uL;        // light direction * shaft length\nuniform float uTime;\nvarying float vTwinkle;\nvoid main() {\n  vec3 p = uP + uR * (aCell.x - 0.5) + uUp * (aCell.y - 0.5) + uL * aCell.z;\n  p.x += sin(uTime * 0.4 + aPhase * 6.28) * 0.06;\n  p.y += sin(uTime * 0.27 + aPhase * 9.4) * 0.05;\n  p.z += cos(uTime * 0.33 + aPhase * 7.7) * 0.06;\n  vTwinkle = (0.55 + 0.45 * sin(uTime * 1.7 + aPhase * 12.0)) * (1.0 - aCell.z * 0.7);\n  vec4 mv = modelViewMatrix * vec4(p, 1.0);\n  gl_PointSize = 26.0 / -mv.z;\n  gl_Position = projectionMatrix * mv;\n}`;\n\nconst DUST_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uStrength;\nvarying float vTwinkle;\nvoid main() {\n  float d = length(gl_PointCoord - 0.5);\n  if (d > 0.5) discard;\n  gl_FragColor = vec4(uColor, uStrength * vTwinkle * (1.0 - d * 2.0));\n}`;\n\nconst MAX_SHAFT_LENGTH = 9;\n\ninterface Shaft {\n  window: RoomWindow;\n  mesh: Mesh;\n  positions: Float32Array;\n  patch: Mesh;\n  shaftU: { uColor: { value: Color }; uStrength: { value: number } };\n  patchU: { uColor: { value: Color }; uStrength: { value: number } };\n  dustU: {\n    uP: { value: Vector3 };\n    uR: { value: Vector3 };\n    uUp: { value: Vector3 };\n    uL: { value: Vector3 };\n    uTime: { value: number };\n    uColor: { value: Color };\n    uStrength: { value: number };\n  } | null;\n}\n\n/**\n * Interior daylight for a `createRoom` interior: a palette-tinted hemisphere\n * fill (warm floor bounce under a cool ceiling), a volumetric-looking light\n * shaft through every sun-facing window — angled by the sun, landing in a\n * soft pool on the floor, with dust motes drifting through it — and the\n * window panes brightening at noon and going dark at night.\n *\n * No real lights beyond the single hemisphere: shafts and patches are unlit\n * additive quads, so the whole effect costs a handful of draw calls. Real\n * point lights stay a budget the room controls (`hearthLight`, torches).\n *\n * ```ts\n * const light = createInteriorLight(room, { cycle });   // follows the day\n * game.onUpdate(() => light.update());                  // dusk = shafts die\n * ```\n *\n * The group is added to `room.group` automatically (shafts are room-local),\n * so `room.setActive(false)` hides the light rig too.\n */\nexport function createInteriorLight(room: Room, options: InteriorLightOptions = {}): InteriorLight {\n  const palette = options.palette ?? DEFAULT_PALETTE;\n  const shaftStrength = options.shaftStrength ?? 0.16;\n  const dustCount = options.dust ?? 26;\n  const cycle = options.cycle;\n\n  const group = new Group();\n  group.name = 'interiorLight';\n\n  // Ambient fill: sky tint from above, warm wood bounce from below.\n  const ground = new Color(palette.wood).lerp(new Color(0xffffff), 0.2);\n  const hemisphere = new HemisphereLight(palette.skyBottom, ground, 0.55);\n  group.add(hemisphere);\n\n  const warm = new Color(0xffd9a0);\n  const pale = new Color(0xfff3df);\n  const nightPane = new Color(0x141c30);\n  const dayPane = new Color(palette.skyBottom);\n\n  const shafts: Shaft[] = room.windows.map((window) => {\n    // A skewed prism: the window quad swept along the light direction to the\n    // floor. 8 vertices, updated on the CPU whenever the sun moves.\n    const positions = new Float32Array(8 * 3);\n    const fades = new Float32Array(8);\n    fades.fill(0, 0, 4);\n    fades.fill(1, 4, 8);\n    const geometry = new BufferGeometry();\n    geometry.setAttribute('position', new BufferAttribute(positions, 3));\n    geometry.setAttribute('aFade', new BufferAttribute(fades, 1));\n    // Quads between window verts (0-3) and landing verts (4-7); windows are\n    // corners in the order (-r,-u) (r,-u) (r,u) (-r,u).\n    geometry.setIndex([\n      0, 1, 5, 0, 5, 4, // bottom sheet\n      2, 3, 7, 2, 7, 6, // top sheet\n      1, 2, 6, 1, 6, 5, // side\n      3, 0, 4, 3, 4, 7, // side\n      4, 5, 6, 4, 6, 7, // landing cap\n    ]);\n    const shaftU = { uColor: { value: warm.clone() }, uStrength: { value: 0 } };\n    const material = new ShaderMaterial({\n      uniforms: shaftU,\n      vertexShader: SHAFT_VERT,\n      fragmentShader: SHAFT_FRAG,\n      transparent: true,\n      depthWrite: false,\n      blending: AdditiveBlending,\n      side: 2, // DoubleSide — visible from any angle inside the room\n    });\n    const mesh = new Mesh(geometry, material);\n    mesh.frustumCulled = false;\n    group.add(mesh);\n\n    // The pool of light where the shaft lands.\n    const patchU = { uColor: { value: warm.clone() }, uStrength: { value: 0 } };\n    const patchGeo = new BufferGeometry();\n    patchGeo.setAttribute(\n      'position',\n      new BufferAttribute(new Float32Array([-0.5, 0, -0.5, 0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5]), 3)\n    );\n    patchGeo.setAttribute('uv', new BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2));\n    patchGeo.setIndex([0, 1, 2, 0, 2, 3]);\n    const patch = new Mesh(\n      patchGeo,\n      new ShaderMaterial({\n        uniforms: patchU,\n        vertexShader: PATCH_VERT,\n        fragmentShader: PATCH_FRAG,\n        transparent: true,\n        depthWrite: false,\n        blending: AdditiveBlending,\n      })\n    );\n    patch.frustumCulled = false;\n    group.add(patch);\n\n    // Dust motes drifting inside the prism.\n    let dustU: Shaft['dustU'] = null;\n    if (dustCount > 0) {\n      const cells = new Float32Array(dustCount * 3);\n      const phases = new Float32Array(dustCount);\n      for (let i = 0; i < dustCount; i++) {\n        cells[i * 3] = (i * 0.618033) % 1;\n        cells[i * 3 + 1] = (i * 0.754877) % 1;\n        cells[i * 3 + 2] = (i * 0.569840) % 1;\n        phases[i] = (i * 0.414213) % 1;\n      }\n      const dustGeo = new BufferGeometry();\n      dustGeo.setAttribute('position', new BufferAttribute(new Float32Array(dustCount * 3), 3));\n      dustGeo.setAttribute('aCell', new BufferAttribute(cells, 3));\n      dustGeo.setAttribute('aPhase', new BufferAttribute(phases, 1));\n      dustU = {\n        uP: { value: window.position.clone() },\n        uR: { value: new Vector3() },\n        uUp: { value: new Vector3() },\n        uL: { value: new Vector3() },\n        uTime: { value: 0 },\n        uColor: { value: pale.clone() },\n        uStrength: { value: 0 },\n      };\n      const dust = new Points(\n        dustGeo,\n        new ShaderMaterial({\n          uniforms: dustU,\n          vertexShader: DUST_VERT,\n          fragmentShader: DUST_FRAG,\n          transparent: true,\n          depthWrite: false,\n          blending: AdditiveBlending,\n        })\n      );\n      dust.frustumCulled = false;\n      group.add(dust);\n    }\n    return { window, mesh, positions, patch, shaftU, patchU, dustU };\n  });\n\n  // The dust clock self-drives from the render loop, like every SCENA flame.\n  if (shafts.length > 0 && dustCount > 0) {\n    shafts[0].mesh.onBeforeRender = () => {\n      const t = performance.now() * 0.001;\n      for (const shaft of shafts) if (shaft.dustU) shaft.dustU.uTime.value = t;\n    };\n  }\n\n  const up = new Vector3(0, 1, 0);\n  const setSun = ({ elevation, azimuth = 0.35 }: InteriorSun): void => {\n    const day = Math.max(0, Math.min(1, elevation));\n    hemisphere.intensity = 0.18 + 0.55 * day;\n    hemisphere.color.copy(dayPane).lerp(nightPane, 1 - day);\n\n    // Sun direction (toward the sun) and light travel direction into rooms.\n    // The apparent elevation is capped short of the zenith so a noon sun still\n    // slants through windows instead of vanishing straight overhead.\n    const el = Math.asin(Math.min(0.88, Math.max(0.02, day)));\n    const sunDir = new Vector3(\n      Math.sin(azimuth) * Math.cos(el),\n      Math.sin(el),\n      Math.cos(azimuth) * Math.cos(el)\n    );\n    const travel = sunDir.clone().negate();\n    const color = warm.clone().lerp(pale, day);\n\n    for (const shaft of shafts) {\n      const { window } = shaft;\n      window.pane.emissiveIntensity = 0.12 + 1.15 * day;\n      window.pane.emissive.copy(dayPane).lerp(nightPane, 1 - day);\n      window.pane.color.copy(window.pane.emissive);\n\n      // Only windows the sun actually shines through get a shaft.\n      const admit = travel.dot(window.normal);\n      const strength = shaftStrength * Math.min(1, admit * 2.5) * Math.min(1, day * 3);\n      const lit = strength > 0.005;\n      shaft.mesh.visible = lit;\n      shaft.patch.visible = lit;\n      shaft.shaftU.uStrength.value = Math.max(0, strength);\n      shaft.shaftU.uColor.value.copy(color);\n      shaft.patchU.uStrength.value = Math.max(0, strength) * 1.4;\n      shaft.patchU.uColor.value.copy(color);\n      if (shaft.dustU) {\n        shaft.dustU.uStrength.value = lit ? Math.min(0.5, strength * 3) : 0;\n        shaft.dustU.uColor.value.copy(color);\n      }\n      if (!lit) continue;\n\n      const right = new Vector3().crossVectors(up, window.normal).normalize();\n      const halfR = right.clone().multiplyScalar(window.width / 2);\n      const halfU = up.clone().multiplyScalar(window.height / 2);\n      const corners = [\n        window.position.clone().sub(halfR).sub(halfU),\n        window.position.clone().add(halfR).sub(halfU),\n        window.position.clone().add(halfR).add(halfU),\n        window.position.clone().sub(halfR).add(halfU),\n      ];\n      const landingSum = new Vector3();\n      let maxLen = 0;\n      corners.forEach((corner, i) => {\n        shaft.positions[i * 3] = corner.x;\n        shaft.positions[i * 3 + 1] = corner.y;\n        shaft.positions[i * 3 + 2] = corner.z;\n        const t = Math.min(MAX_SHAFT_LENGTH, (corner.y - 0.03) / Math.max(0.05, -travel.y));\n        maxLen = Math.max(maxLen, t);\n        const landing = corner.clone().addScaledVector(travel, t);\n        shaft.positions[(i + 4) * 3] = landing.x;\n        shaft.positions[(i + 4) * 3 + 1] = landing.y;\n        shaft.positions[(i + 4) * 3 + 2] = landing.z;\n        landingSum.add(landing);\n      });\n      (shaft.mesh.geometry.getAttribute('position') as BufferAttribute).needsUpdate = true;\n\n      const landingCenter = landingSum.multiplyScalar(0.25);\n      shaft.patch.visible = lit && landingCenter.y < 0.4;\n      shaft.patch.position.set(landingCenter.x, 0.035, landingCenter.z);\n      // Low sun drags the pool longer along the light's floor direction.\n      const stretch = 1.2 + (1 - Math.abs(travel.y)) * 1.4;\n      shaft.patch.scale.set(window.width * 1.5, 1, window.width * stretch);\n      shaft.patch.rotation.set(0, Math.atan2(travel.x, travel.z), 0);\n\n      if (shaft.dustU) {\n        shaft.dustU.uP.value.copy(window.position);\n        shaft.dustU.uR.value.copy(right).multiplyScalar(window.width);\n        shaft.dustU.uUp.value.copy(up).multiplyScalar(window.height);\n        shaft.dustU.uL.value.copy(travel).multiplyScalar(maxLen);\n      }\n    }\n  };\n\n  setSun(options.sun ?? (cycle ? sunFromCycle(cycle) : { elevation: 0.75, azimuth: 0.35 }));\n  room.group.add(group);\n\n  return {\n    group,\n    hemisphere,\n    setSun,\n    update() {\n      if (cycle) setSun(sunFromCycle(cycle));\n    },\n  };\n}\n\n/** Map a day cycle's time to a sweeping azimuth: east at dawn, west at dusk. */\nfunction sunFromCycle(cycle: { readonly sunElevation: number; timeOfDay: number }): InteriorSun {\n  const progress = (cycle.timeOfDay - 0.25) / 0.5; // 0 dawn → 1 dusk\n  return {\n    elevation: cycle.sunElevation,\n    azimuth: (0.5 - Math.max(0, Math.min(1, progress))) * Math.PI,\n  };\n}\n","/**\n * The sea state — and the one thing in this library with a **memory**.\n *\n * The wind can get up in twenty minutes and drop in ten. The sea it raises\n * cannot: it takes hours to build and **days** to die. So the sea you are in\n * is almost never the sea that the wind you can feel would make.\n *\n * ```ts\n * const sea = createSeaState({ kind: 'ocean' });\n * const ocean = createOcean({ sea: () => sea.trains, size: 900 });\n *\n * sea.setWind(18, 250);          // a gale from the west-south-west\n * sea.state;                     // 'building' — and it will be for hours\n * sea.setWind(0, 250);           // …and now it drops flat calm\n * sea.state;                     // 'dying'. The sea does not care.\n * sea.height;                    // still 6 m, and still 4 m tomorrow\n * ```\n *\n * That asymmetry is the module. A boat can shelter from wind behind a\n * headland and cannot shelter from the swell that came round it; a harbour\n * mouth is workable in a gale and unworkable the morning after one; and the\n * swell running under you right now may have been raised by a storm a\n * thousand miles away that you will never see.\n *\n * ## You cannot make an ocean sea in a lake\n *\n * A sea needs **fetch** — a stretch of open water for the wind to work on —\n * and **duration**. The same forty knots makes ripples across a lake, a nasty\n * short sea in a channel, and a thirty-foot swell in the Southern Ocean, and\n * the only difference is how far the wind had to work with:\n *\n * ```ts\n * createSeaState({ kind: 'lake' }).setWind(20, 0);    // limit: 0.4 m\n * createSeaState({ kind: 'ocean' }).setWind(20, 0);   // limit: 9.8 m\n * ```\n *\n * ## Wind sea and swell are two different seas\n *\n * They come from different directions, because the swell came from somewhere\n * else. Where they cross, the sea is **confused** — and a confused sea is the\n * dangerous one, because there is no pattern in it to steer to.\n */\nexport type SeaKind = 'lake' | 'coastal' | 'shelf' | 'ocean';\n\n/** How much sea this water can hold, which is a question about its size. */\nexport const SEA_KINDS: SeaKind[] = ['lake', 'coastal', 'shelf', 'ocean'];\n\n/**\n * rest / transitioning-toward / at-target / drifting-back — and the axis is\n * **the sea against the wind that is on it now.**\n *\n * `'dying'` is the state this module exists for: a big sea running under a\n * wind that could not possibly have raised it, because the wind that did has\n * gone somewhere else.\n */\nexport type SeaCondition = 'calm' | 'building' | 'full' | 'dying';\n\n/** One train of waves. Two of these make a sea. */\nexport interface Train {\n  /** Significant height, m — the average of the highest third, which is what\n   *  a sailor means by \"the sea is running two metres\". */\n  height: number;\n  /** Seconds between crests. */\n  period: number;\n  /** Crest to crest, m. `1.56 × period²` in deep water. */\n  length: number;\n  /** Degrees it is coming FROM, the way a sailor says it. */\n  from: number;\n}\n\nexport interface SeaState {\n  kind: SeaKind;\n  /** `speed` m/s, `from` degrees. */\n  setWind(speed: number, from?: number): void;\n  readonly wind: number;\n  readonly windFrom: number;\n\n  /** What this wind has managed to raise HERE, so far. */\n  readonly windSea: Train;\n  /** What was raised somewhere else, and arrived. */\n  readonly swell: Train;\n  /** The two of them, for `createOcean({ sea })`. Mutated in place — a live\n   *  view, not a snapshot. */\n  readonly trains: { windSea: Train; swell: Train };\n\n  /** Combined significant height, m. Two trains add in QUADRATURE, not\n   *  arithmetically: two 3 m seas crossing make 4.2 m, not 6. */\n  readonly height: number;\n  /** Of the bigger train. */\n  readonly period: number;\n  /** Douglas sea state, 0–9. */\n  readonly douglas: number;\n  /**\n   * How confused it is, 0–1. Peaks when two trains of equal height cross at\n   * right angles, and is zero when they run together — which is why a big\n   * swell with the wind behind it is comfortable and the same swell on the\n   * beam is not.\n   */\n  readonly confusion: number;\n  /** The most this wind could ever raise here, m. Fetch-limited or fully\n   *  developed, whichever is smaller. */\n  readonly limit: number;\n  /** Metres of open water the wind has to work on. */\n  readonly fetch: number;\n  /** Seconds until the sea is within a tenth of `limit`. `Infinity` if this\n   *  wind will never get there; 0 if it is already there. */\n  readonly building: number;\n  /** Seconds until the sea is down to `metres`, if the wind stays as it is.\n   *  `Infinity` if it never will. */\n  fallsTo(metres: number): number;\n\n  /** A swell from a storm you will never see. */\n  swellIn(from: number, height: number, period: number): void;\n\n  readonly state: SeaCondition;\n  onState?: (state: SeaCondition) => void;\n  update(dt: number): void;\n}\n\nexport interface SeaStateOptions {\n  kind?: SeaKind;\n  /** Override the fetch, m. */\n  fetch?: number;\n  /** Start with a sea already running. */\n  height?: number;\n  /** …from here. */\n  from?: number;\n  wind?: number;\n  windFrom?: number;\n}\n\nconst G = 9.81;\nconst clamp01 = (t: number): number => (t < 0 ? 0 : t > 1 ? 1 : t);\nconst wrap360 = (d: number): number => ((d % 360) + 360) % 360;\n\n/** Shortest signed turn from a to b, degrees. */\nconst delta = (a: number, b: number): number => {\n  const d = wrap360(b - a);\n  return d > 180 ? d - 360 : d;\n};\n\nconst FETCH: Record<SeaKind, number> = {\n  // A lake: a couple of miles, and it will never be anything but chop.\n  lake: 3_000,\n  // Inside a headland or across a channel.\n  coastal: 30_000,\n  // Off soundings but not across an ocean.\n  shelf: 200_000,\n  // Nothing to windward for a thousand miles.\n  ocean: 2_000_000,\n};\n\n/** Significant height a wind would raise given all the time and sea room in\n *  the world. `0.0246 U²` — twenty metres a second makes ten metres of sea. */\nexport function fullyDeveloped(wind: number): number {\n  return 0.0246 * wind * wind;\n}\n\n/**\n * What a wind can raise across a limited fetch, m.\n *\n * `0.0016 · U · √(F/g)`, the SMB form. It is the reason a lake in a gale is\n * unpleasant and not dangerous: forty knots across three kilometres of water\n * cannot make anything bigger than about half a metre, no matter how long it\n * blows.\n */\nexport function fetchLimited(wind: number, fetch: number): number {\n  return 0.0016 * Math.abs(wind) * Math.sqrt(Math.max(0, fetch) / G);\n}\n\n/** Seconds between crests, for a sea of this height. Longer seas are older\n *  seas, and that is the whole of it. */\nexport function periodFor(height: number): number {\n  return height <= 1e-4 ? 0 : 3.9 * Math.sqrt(height) + 1.5;\n}\n\n/** Deep-water wavelength for a period, m. */\nexport function lengthFor(period: number): number {\n  return (G * period * period) / (2 * Math.PI);\n}\n\n/** Douglas sea state, 0–9, from significant height. */\nexport function douglasFor(height: number): number {\n  // Douglas 0 is GLASSY — not \"small\", but nothing at all — so the bottom of\n  // the scale is a case and not a mark. Written as a mark at zero, a millpond\n  // comes out as sea state 1 and the scale never reads its own first entry.\n  if (height <= 0) return 0;\n  const marks = [0.1, 0.5, 1.25, 2.5, 4, 6, 9, 14];\n  let n = 1;\n  for (const m of marks) if (height >= m) n++;\n  return Math.min(9, n);\n}\n\nexport function createSeaState(options: SeaStateOptions = {}): SeaState {\n  const kind = options.kind ?? 'ocean';\n  const fetch = options.fetch ?? FETCH[kind];\n\n  let wind = Math.max(0, options.wind ?? 0);\n  let windFrom = wrap360(options.windFrom ?? 270);\n\n  const windSea: Train = { height: 0, period: 0, length: 0, from: windFrom };\n  const swell: Train = {\n    height: Math.max(0, options.height ?? 0),\n    period: periodFor(Math.max(0, options.height ?? 0)),\n    length: 0,\n    from: wrap360(options.from ?? windFrom),\n  };\n  swell.length = lengthFor(swell.period);\n  const trains = { windSea, swell };\n\n  let state: SeaCondition = 'calm';\n\n  const limitOf = (): number => Math.min(fullyDeveloped(wind), fetchLimited(wind, fetch));\n\n  /**\n   * How long a sea of this size takes to build, in seconds.\n   *\n   * Bigger seas take longer, which is why a squall makes nothing much and a\n   * three-day gale makes everything. Roughly ten hours to fully developed at\n   * ten metres a second, twenty at twenty.\n   */\n  const buildTau = (target: number): number => 3600 * 2.2 * Math.sqrt(Math.max(0.05, target));\n\n  /** A swell dies over DAYS. It is the longest time constant in the trilogy\n   *  and it is the entire point of the module. */\n  const SWELL_TAU = 3600 * 30;\n\n  const api: SeaState = {\n    kind,\n    fetch,\n    trains,\n    windSea,\n    swell,\n    setWind(speed: number, from?: number) {\n      wind = Math.max(0, Number.isFinite(speed) ? speed : 0);\n      if (from !== undefined && Number.isFinite(from)) windFrom = wrap360(from);\n    },\n    get wind() {\n      return wind;\n    },\n    get windFrom() {\n      return windFrom;\n    },\n    get limit() {\n      return limitOf();\n    },\n    get height() {\n      // IN QUADRATURE. Two three-metre seas crossing make four and a bit, not\n      // six — wave energy adds, and height is the square root of energy.\n      return Math.sqrt(windSea.height ** 2 + swell.height ** 2);\n    },\n    get period() {\n      return windSea.height >= swell.height ? windSea.period : swell.period;\n    },\n    get douglas() {\n      return douglasFor(api.height);\n    },\n    get confusion() {\n      const a = windSea.height;\n      const b = swell.height;\n      if (a < 0.05 || b < 0.05) return 0;\n      // Worst when they are the same size — one big sea with a ripple across\n      // it is not confused — and worst when they cross at a right angle.\n      const evenness = (2 * Math.sqrt(a * b)) / (a + b);\n      const cross = Math.sin((delta(windSea.from, swell.from) * Math.PI) / 180);\n      return clamp01(evenness * cross * cross);\n    },\n    get building() {\n      const target = limitOf();\n      if (target <= windSea.height * 1.1) return 0;\n      // Exponential approach: the time to close 90% of the gap.\n      const tau = buildTau(target);\n      const gap = (target - windSea.height) / Math.max(1e-6, target * 0.1);\n      return gap <= 1 ? 0 : tau * Math.log(gap);\n    },\n    fallsTo(metres: number) {\n      if (api.height <= metres) return 0;\n      // Under a held wind the wind-sea settles at `limit` and never goes\n      // below it, so anything under that is unreachable.\n      const floor = limitOf();\n      if (metres < floor) return Infinity;\n      // Everything above the floor is swell, and swell goes on the long clock.\n      const excess = Math.sqrt(Math.max(0, api.height ** 2 - floor ** 2));\n      const want = Math.sqrt(Math.max(0, metres * metres - floor * floor));\n      if (excess <= want) return 0;\n      if (want <= 1e-4) return Infinity;\n      return SWELL_TAU * Math.log(excess / want);\n    },\n    swellIn(from: number, height: number, period: number) {\n      const h = Math.max(0, height);\n      if (h <= 0) return;\n      // Two swells running at once are still one swell: they combine in\n      // quadrature and the direction goes with the weight, because there is\n      // one swell train and pretending otherwise needs a second shader.\n      const total = Math.sqrt(swell.height ** 2 + h * h);\n      const w = (h * h) / Math.max(1e-9, total * total);\n      swell.from = wrap360(swell.from + delta(swell.from, wrap360(from)) * w);\n      swell.period = swell.period * (1 - w) + Math.max(1, period) * w;\n      swell.height = total;\n      swell.length = lengthFor(swell.period);\n    },\n    get state() {\n      return state;\n    },\n    update(dt: number) {\n      if (!(dt > 0)) return;\n      const target = limitOf();\n\n      if (windSea.height < target) {\n        // BUILDING. Toward what this wind can do here, on the hours clock.\n        const k = 1 - Math.exp(-dt / buildTau(target));\n        windSea.height += (target - windSea.height) * k;\n        // The sea turns to the wind, but not instantly — an old sea takes a\n        // while to come round to a new breeze, which is a cross sea in itself.\n        windSea.from = wrap360(windSea.from + delta(windSea.from, windFrom) * Math.min(1, dt / 5400));\n      } else {\n        // THE WIND HAS DROPPED, and the sea it left does not vanish: it stops\n        // being wind sea and becomes SWELL. Nothing is lost here — it moves\n        // from the train that answers the wind to the train that does not.\n        //\n        // AND THE BOOKKEEPING IS IN ENERGY, NOT IN HEIGHT. Take it off one\n        // train linearly and add it to the other in quadrature and most of it\n        // simply disappears: written that way, a full Atlantic gale of nine\n        // and a half metres came down to two and a third in one hour, which is\n        // the exact opposite of the thing this module is for.\n        const before = windSea.height;\n        const after = target + (before - target) * Math.exp(-dt / 2400);\n        const shedEnergy = Math.max(0, before * before - after * after);\n        windSea.height = after;\n        if (shedEnergy > 1e-9) {\n          const total = Math.sqrt(swell.height ** 2 + shedEnergy);\n          const w = shedEnergy / Math.max(1e-9, total * total);\n          swell.from = wrap360(\n            swell.from + delta(swell.from, windSea.from) * (swell.height < 1e-4 ? 1 : w)\n          );\n          swell.period =\n            swell.height < 1e-4\n              ? windSea.period\n              : swell.period * (1 - w) + windSea.period * w;\n          swell.height = total;\n        }\n      }\n      windSea.period = periodFor(windSea.height);\n      windSea.length = lengthFor(windSea.period);\n\n      // And the swell bleeds away over DAYS. Thirty hours to fall by a third.\n      swell.height *= Math.exp(-dt / SWELL_TAU);\n      if (swell.height < 0.01) swell.height = 0;\n      // …and it LENGTHENS as it goes. The short components die first, so an\n      // old swell is a long low one, which is how you know it came a long way.\n      swell.period = Math.min(22, swell.period + dt / (3600 * 40));\n      swell.length = lengthFor(swell.period);\n\n      const next = ((): SeaCondition => {\n        if (api.height < 0.12) return 'calm';\n        // The axis is the sea AGAINST the wind that is on it now, with a\n        // two-sided band so a sea sitting at its limit does not chatter.\n        const ratio = windSea.height / Math.max(0.02, target);\n        const wide = state === 'full';\n        if (api.height > Math.max(target, 0.02) * (wide ? 1.35 : 1.15)) return 'dying';\n        if (ratio < (wide ? 0.75 : 0.9)) return 'building';\n        return 'full';\n      })();\n      if (next !== state) {\n        state = next;\n        api.onState?.(state);\n      }\n    },\n  };\n  return api;\n}\n","import {\n  BoxGeometry,\n  BufferAttribute,\n  BufferGeometry,\n  CatmullRomCurve3,\n  Group,\n  InstancedMesh,\n  Matrix4,\n  Mesh,\n  Quaternion,\n  Vector3,\n  type Material,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport { createSurface } from '../materials/surface';\n\n/**\n * Railway track: two rails, sleepers, ballast, and — the part that matters —\n * a way to ask where you are at a given distance along it.\n *\n * ```ts\n * const line = createTrack([a, b, c], { surface: terrain.heightAt });\n * scene.add(line.object);\n *\n * const where = line.at(120);      // 120 m along\n * carriage.position.copy(where.position);\n * carriage.quaternion.copy(where.rotation);\n * ```\n *\n * ## `at(distance)` is the whole point\n *\n * Everything else in the trilogy steers: an agent picks a direction and the\n * simulation integrates it. A train does not. Its entire position is one\n * number — how far along — and the track turns that number into a place and a\n * facing. That single function is what a controller drives, what a carriage\n * is placed by, and what a station stop is expressed in.\n *\n * It is deliberately the ONLY thing a driver needs, so GAMA's rail controller\n * can take `{ length, at }` structurally and never import SCENA. Same\n * handshake as everywhere else in the trilogy: a shape, not a package.\n *\n * ## Arc length, not curve parameter\n *\n * `CatmullRomCurve3.getPoint(t)` walks the curve's PARAMETER, which is not\n * distance: on a curve with a tight bend and a long straight, equal steps in\n * `t` cover wildly unequal ground. A train driven on `t` would speed up and\n * slow down for no reason as it went round a bend, which is exactly the class\n * of defect `measureFootSkate` exists to catch in a walk cycle.\n *\n * So the curve is resampled into a table of equally-spaced-in-DISTANCE points\n * once, at build time, and `at()` interpolates that. `distanceError` reports\n * how far off the table is — see the note on it.\n */\nexport interface TrackOptions {\n  /** Distance between rail centres. Default 1.435 — standard gauge, in metres. */\n  gauge?: number;\n  /** Ground height lookup; a number means flat ground. Default 0. */\n  surface?: number | ((x: number, z: number) => number);\n  /** Metres between sleepers. Default 0.65. */\n  sleeperSpacing?: number;\n  /** Close the track into a loop. Default false. */\n  loop?: boolean;\n  /** Extra clearance added to scatter keep-out circles. Default 2.4. */\n  keepOutMargin?: number;\n  /**\n   * Samples per metre in the arc-length table. Default 2.\n   *\n   * This is a resolution/memory trade, not a quality dial for the mesh: the\n   * rails are built from the same table, so raising it smooths tight curves\n   * and costs vertices. `distanceError` says whether it is enough.\n   */\n  samplesPerMetre?: number;\n  /** Build the ballast shoulder. Default true. */\n  ballast?: boolean;\n  palette?: Palette;\n}\n\n/** Where the track is, and which way it faces, at some distance along it. */\nexport interface TrackPoint {\n  position: Vector3;\n  /** Unit vector along the track, pointing in the direction of travel. */\n  tangent: Vector3;\n  /** A rotation that faces −Z down the track and keeps +Y up. */\n  rotation: Quaternion;\n}\n\nexport interface RailTrack {\n  object: Group;\n  /** Total length in metres. `at(length)` is the far end. */\n  length: number;\n  gauge: number;\n  loop: boolean;\n  /**\n   * Position and facing at `distance` metres along the track.\n   *\n   * Past the ends it CLAMPS rather than extrapolating (or wraps, on a loop) —\n   * a train that overruns should stop at the buffers, not fly off down the\n   * tangent into the scenery.\n   *\n   * Pass `out` to avoid allocating; the same object is returned.\n   */\n  at(distance: number, out?: TrackPoint): TrackPoint;\n  /** The centreline, for scatter keep-out or a camera dolly. */\n  route: Vector3[];\n  keepOut: Array<{ center: { x: number; z: number }; radius: number }>;\n  /**\n   * Worst gap between the arc-length table's spacing and its nominal step,\n   * as a fraction. Near zero means `at()` is honest about distance.\n   *\n   * Reported rather than asserted, because the honest value depends on how\n   * sharply the caller's own control points turn. A track laid with a 5 m\n   * radius curve cannot be resampled evenly at 0.5 m steps, and the number\n   * says so instead of the library pretending otherwise.\n   */\n  distanceError: number;\n  dispose(): void;\n}\n\nconst UP = new Vector3(0, 1, 0);\n\n/** The curve's length through its DEFAULT table — enough to size a better one. */\nconst rawLength = (curve: CatmullRomCurve3): number => curve.getLength();\n\n/**\n * Lay track along a polyline.\n *\n * Four draw calls whatever the length: two rails, one instanced sleeper mesh,\n * one ballast ribbon. A kilometre of track at 0.65 m spacing is 1,538\n * sleepers, and one mesh each would be 1,538 draw calls — which is the whole\n * reason `npm run geometry` counts them.\n */\nexport function createTrack(\n  points: Array<Vector3 | { x: number; z: number }>,\n  options: TrackOptions = {}\n): RailTrack {\n  const gauge = options.gauge ?? 1.435;\n  const spacing = options.sleeperSpacing ?? 0.65;\n  const loop = options.loop ?? false;\n  const palette = options.palette ?? DEFAULT_PALETTE;\n  const surface = options.surface ?? 0;\n  const heightAt =\n    typeof surface === 'number' ? () => surface : (x: number, z: number) => surface(x, z);\n\n  const controls = points.map(\n    (p) => new Vector3(p.x, 0, 'z' in p ? p.z : (p as Vector3).z)\n  );\n  const curve = new CatmullRomCurve3(controls, loop, 'centripetal');\n  // three.js builds its arc-length lookup from `arcLengthDivisions`, default\n  // 200. On a 700 m line that is one entry every 3.5 m, and \"equally spaced\"\n  // comes out 8% uneven on the bends — measured. Scale it with the line so a\n  // long track is not resampled through a coarse table.\n  curve.arcLengthDivisions = Math.max(200, Math.ceil(rawLength(curve) * 4));\n\n  // Resample to EQUAL DISTANCE. `getSpacedPoints` divides by arc length rather\n  // than by parameter, which is the difference between a train that holds its\n  // speed round a bend and one that does not.\n  const rough = curve.getLength();\n  const steps = Math.max(8, Math.ceil(rough * (options.samplesPerMetre ?? 2)));\n  const spaced = curve.getSpacedPoints(steps);\n  if (loop) spaced.pop(); // getSpacedPoints repeats the first point on a loop\n\n  const route: Vector3[] = spaced.map((p) => new Vector3(p.x, heightAt(p.x, p.z), p.z));\n\n  // Cumulative distance along the resampled polyline. This — not the curve's\n  // own length — is what `at()` and `length` report, because it is the length\n  // of the thing actually drawn.\n  const cumulative: number[] = [0];\n  for (let i = 1; i < route.length; i++) {\n    cumulative.push(cumulative[i - 1] + route[i].distanceTo(route[i - 1]));\n  }\n  if (loop) cumulative.push(cumulative[cumulative.length - 1] + route[0].distanceTo(route[route.length - 1]));\n  const length = cumulative[cumulative.length - 1];\n\n  // How uneven did the resampling actually come out? Draping onto terrain\n  // stretches segments that climb, so this is not always the flat-ground zero.\n  const nominal = length / (cumulative.length - 1);\n  let distanceError = 0;\n  for (let i = 1; i < cumulative.length; i++) {\n    const step = cumulative[i] - cumulative[i - 1];\n    distanceError = Math.max(distanceError, Math.abs(step - nominal) / nominal);\n  }\n\n  const at = (distance: number, out?: TrackPoint): TrackPoint => {\n    const target = out ?? {\n      position: new Vector3(),\n      tangent: new Vector3(),\n      rotation: new Quaternion(),\n    };\n    let d = distance;\n    if (loop) {\n      d = ((d % length) + length) % length;\n    } else {\n      d = Math.max(0, Math.min(length, d));\n    }\n    // Binary search the cumulative table: O(log n) per query, and a train\n    // queries it once per carriage per frame.\n    let lo = 0;\n    let hi = cumulative.length - 1;\n    while (lo < hi - 1) {\n      const mid = (lo + hi) >> 1;\n      if (cumulative[mid] <= d) lo = mid;\n      else hi = mid;\n    }\n    const span = cumulative[hi] - cumulative[lo] || 1;\n    const t = (d - cumulative[lo]) / span;\n    const a = route[lo % route.length];\n    const b = route[hi % route.length];\n    target.position.lerpVectors(a, b, t);\n    target.tangent.subVectors(b, a).normalize();\n    // Face −Z down the track: three.js convention, and what every SCENA\n    // vehicle prop is authored to.\n    const yaw = Math.atan2(target.tangent.x, target.tangent.z);\n    target.rotation.setFromAxisAngle(UP, yaw + Math.PI);\n    return target;\n  };\n\n  // ---- the mesh -----------------------------------------------------------\n\n  const group = new Group();\n  group.name = 'track';\n\n  const railMaterial = createSurface('steel', { seed: 3 });\n  const sleeperMaterial = createSurface('wood', { color: palette.woodDark, seed: 7 });\n  const ballastMaterial = createSurface('gravel', { seed: 11 });\n\n  const half = gauge / 2;\n  const railHead = 0.075;\n  const railHeight = 0.14;\n  const sleeperTop = 0.12;\n\n  /**\n   * A flat ribbon following the route at a lateral offset, at a height.\n   *\n   * Rails and ballast are the same shape at different widths, and building\n   * them from the SAME resampled route is what stops a rail disagreeing with\n   * `at()` about where the track is — a train riding half a sleeper off its\n   * own rails is the defect this shares code to avoid.\n   */\n  const ribbon = (offset: number, width: number, height: number, material: Material, name: string): Mesh => {\n    const edges = loop ? route.length + 1 : route.length;\n    const positions = new Float32Array(edges * 2 * 3);\n    const indices: number[] = [];\n    const dir = new Vector3();\n    const perp = new Vector3();\n    for (let i = 0; i < edges; i++) {\n      const current = route[i % route.length];\n      const ahead = route[(i + 1) % route.length];\n      const behind = route[(i - 1 + route.length) % route.length];\n      // At an open end there is no neighbour on one side, so use the point\n      // itself — a centred difference there would fold the ribbon over.\n      const from = i === 0 && !loop ? current : behind;\n      const to = i >= edges - 1 && !loop ? current : ahead;\n      dir.subVectors(to, from);\n      if (dir.lengthSq() < 1e-12) dir.set(0, 0, 1);\n      dir.normalize();\n      perp.set(-dir.z, 0, dir.x);\n      const cx = current.x + perp.x * offset;\n      const cz = current.z + perp.z * offset;\n      const y = current.y + height;\n      positions.set([cx - perp.x * width * 0.5, y, cz - perp.z * width * 0.5], i * 6);\n      positions.set([cx + perp.x * width * 0.5, y, cz + perp.z * width * 0.5], i * 6 + 3);\n      if (i < edges - 1) {\n        const v = i * 2;\n        indices.push(v, v + 1, v + 2, v + 1, v + 3, v + 2);\n      }\n    }\n    const geometry = new BufferGeometry();\n    geometry.setAttribute('position', new BufferAttribute(positions, 3));\n    geometry.setIndex(indices);\n    geometry.computeVertexNormals();\n    const mesh = new Mesh(geometry, material);\n    mesh.name = name;\n    return mesh;\n  };\n\n  if (options.ballast ?? true) {\n    group.add(ribbon(0, gauge + 1.9, 0.01, ballastMaterial, 'ballast'));\n  }\n  group.add(ribbon(-half, railHead, sleeperTop + railHeight, railMaterial, 'rail-left'));\n  group.add(ribbon(half, railHead, sleeperTop + railHeight, railMaterial, 'rail-right'));\n\n  // Sleepers: ONE instanced mesh for the whole line, whatever its length.\n  // A kilometre at 0.65 m spacing is 1,538 of them, and a Mesh each would be\n  // 1,538 draw calls for a thing nobody looks at directly.\n  const count = Math.max(1, Math.floor(length / spacing));\n  const sleepers = new InstancedMesh(\n    new BoxGeometry(gauge + 0.55, 0.12, 0.24),\n    sleeperMaterial,\n    count\n  );\n  sleepers.name = 'sleepers';\n  const matrix = new Matrix4();\n  const probe: TrackPoint = {\n    position: new Vector3(),\n    tangent: new Vector3(),\n    rotation: new Quaternion(),\n  };\n  const one = new Vector3(1, 1, 1);\n  const seat = new Vector3();\n  for (let i = 0; i < count; i++) {\n    at((i + 0.5) * spacing, probe);\n    seat.copy(probe.position).setY(probe.position.y + sleeperTop - 0.06);\n    matrix.compose(seat, probe.rotation, one);\n    sleepers.setMatrixAt(i, matrix);\n  }\n  sleepers.instanceMatrix.needsUpdate = true;\n  group.add(sleepers);\n\n  const keepOut = route\n    .filter((_, i) => i % 4 === 0)\n    .map((p) => ({\n      center: { x: p.x, z: p.z },\n      radius: gauge + (options.keepOutMargin ?? 2.4),\n    }));\n\n  return {\n    object: group,\n    length,\n    gauge,\n    loop,\n    at,\n    route,\n    keepOut,\n    distanceError,\n    dispose(): void {\n      group.traverse((o) => {\n        const mesh = o as Mesh;\n        if (mesh.geometry) mesh.geometry.dispose();\n      });\n      railMaterial.dispose();\n      sleeperMaterial.dispose();\n      ballastMaterial.dispose();\n    },\n  };\n}\n","import { Color, MeshPhysicalMaterial, MeshStandardMaterial, Vector2, Vector3 } from 'three';\n\n/**\n * Procedural surface materials — the reason low-poly SCENA props can look\n * richer than a downloaded GLTF at a fraction of the bytes.\n *\n * A downloaded model ships baked albedo/normal/roughness textures (often\n * megabytes). Here the same detail — weathered stone, wood grain, mottled\n * plaster, thatch, tile — is generated in the shader from triplanar value\n * noise, so nothing is fetched and every prop is unique. The result is a\n * plain `MeshStandardMaterial` with an `onBeforeCompile` patch, so it keeps\n * three's full PBR lighting, shadows, fog, tone-mapping, and SCENA's\n * day/night emissive dimming — none of which a raw `ShaderMaterial` would.\n *\n * ```ts\n * const mat = createSurface('stone', { color: 0x8a8f98 });\n * mesh.material = mat;                       // that's it\n * ```\n *\n * Triplanar means no UVs are needed (a `BoxGeometry` has none worth using),\n * and because the noise is sampled in world space a wall built from several\n * abutting boxes reads as one continuous stone face with no visible seams.\n */\nexport type SurfaceKind =\n  | 'plaster'\n  | 'stone'\n  | 'wood'\n  | 'plank'\n  | 'thatch'\n  | 'tile'\n  | 'metal'\n  | 'dirt'\n  // Tier 1 — ground & terrain\n  | 'sand'\n  | 'gravel'\n  | 'mud'\n  | 'grass'\n  // Tier 1 — stone & masonry\n  | 'sandstone'\n  | 'granite'\n  | 'slate'\n  // Tier 1 — organic\n  | 'bark'\n  | 'leather'\n  | 'canvas'\n  | 'parchment'\n  | 'terracotta'\n  | 'bone'\n  // Tier 1 — metals\n  | 'rust'\n  | 'bronze'\n  | 'brass'\n  // Tier 2 — masonry tiling\n  | 'brick'\n  | 'cobblestone'\n  | 'ashlar'\n  | 'floortile'\n  | 'shingle'\n  // Tier 3 — cap & glow\n  | 'snow'\n  | 'moss'\n  | 'lava'\n  | 'crystal'\n  // Tier 4 — modern & machined\n  | 'concrete'\n  | 'paint'\n  | 'marble'\n  | 'terrazzo'\n  | 'steel'\n  | 'chrome'\n  | 'paintedMetal'\n  | 'corten'\n  | 'teak'\n  | 'porcelain'\n  | 'glaze'\n  | 'mosaic'\n  | 'parquet'\n  | 'patternedTile'\n  // Tier 5 — industrial\n  | 'corrugatedIron'\n  | 'asphalt'\n  | 'diamondPlate'\n  | 'galvanised'\n  | 'copperPatina'\n  | 'basalt'\n  // Tier 6 — physical (MeshPhysicalMaterial; see `physical`)\n  | 'velvet'\n  | 'silk'\n  | 'brushedMetal'\n  | 'nacre'\n  | 'ice'\n  | 'gemstone';\n\nexport interface SurfaceParams {\n  /**\n   * Natural base colour for this kind (hex int). Used when the caller passes\n   * no `color`, so `createSurface('sand')` looks like sand out of the box.\n   * A caller's `color` always wins.\n   */\n  baseColor?: number;\n  /** PBR base roughness. */\n  roughness: number;\n  /** PBR metalness. */\n  metalness: number;\n  /** Noise frequency in world units (higher = finer grain). */\n  scale: number;\n  /** How strongly the fine noise lightens/darkens the albedo (0–1). */\n  albedoVar: number;\n  /** Secondary colour blended into cavities, as a hex int. */\n  tint: number;\n  /** How much of `tint` shows in cavities (0–1). */\n  tintAmount: number;\n  /** Low-frequency cavity darkening — the baked-AO look (0–1). */\n  ao: number;\n  /** Surface relief strength (normal perturbation from the noise). */\n  bump: number;\n  /** Roughness variation added from the noise (0–1). */\n  roughVar: number;\n  /** Anisotropic grain strength for wood-like surfaces (0 = none). */\n  grain: number;\n  /** Grain ring frequency. */\n  grainScale: number;\n  /** World axis the grain runs along. */\n  grainAxis: Vector3;\n  /** flatShading default for this kind. */\n  flat: boolean;\n\n  // --- masonry tiling (all optional; `tile: 0` — the default — disables it) ---\n  /** Tiling strength (0 off, 1 full). Turns on the brick/tile grid. */\n  tile?: number;\n  /** Cell width in world metres (the length of a brick/tile). */\n  tileW?: number;\n  /** Cell height in world metres (the course height). */\n  tileH?: number;\n  /** Mortar-joint width in world metres. */\n  mortar?: number;\n  /** Row offset: 0 = aligned grid, 1 = half-cell running bond. */\n  bond?: number;\n  /** Cell profile: 0 = flat tiles, 1 = domed cobbles. */\n  round?: number;\n  /** Per-cell brightness/roughness jitter (0–1), so no two read alike. */\n  tileJitter?: number;\n  /** Mortar-joint colour, as a hex int. */\n  mortarColor?: number;\n  /** Groove relief strength for the joints (normal perturbation). */\n  tileRelief?: number;\n  /** Fraction of cells painted solid `tint` (mosaic accent chips, 0–1). */\n  tileTint?: number;\n  /** 1 = shear alternate column bands ±45° — chevron/herringbone parquet. */\n  chevron?: number;\n  /** Per-cell ring + dot motif painted in `tint` (patterned cement tiles). */\n  motif?: number;\n\n  // --- snow / moss cap (settles on up-facing faces; `cap: 0` disables it) ---\n  /** Cap strength (0 off, 1 full) — snow, moss, dust on the tops. */\n  cap?: number;\n  /** Cap colour, as a hex int (white snow, green moss…). */\n  capColor?: number;\n  /** How up-facing a face must be before the cap takes (0 everywhere … 1 only dead-level tops). */\n  capUp?: number;\n  /** Softness of the cap edge (bigger = more gradual). */\n  capSharp?: number;\n  /** Roughness inside the capped area (fresh snow reads matte/bright). */\n  capRough?: number;\n\n  // --- physical: light responses MeshStandardMaterial cannot produce -----\n  /**\n   * Build a `MeshPhysicalMaterial` instead of a `MeshStandardMaterial`.\n   *\n   * Some surfaces are defined by a light response the standard model has no\n   * term for at all — the retroreflective rim of velvet, the stretched\n   * highlight on silk, the thin-film hue shift on a shell, light going\n   * THROUGH ice. None of those can be faked with albedo and roughness.\n   *\n   * It costs: a physical material is a bigger shader, and `transmission` in\n   * particular makes three render the scene a second time into a buffer. Use\n   * it on OBJECTS, not on facades — which is exactly why `createGlass` fakes\n   * its glass rather than transmitting it.\n   */\n  physical?: boolean;\n  /** Sheen strength — velvet, felt, wool. The rim IS the material. */\n  sheen?: number;\n  /** Sheen colour, as a hex int. */\n  sheenColor?: number;\n  /** Sheen roughness. */\n  sheenRough?: number;\n  /**\n   * Anisotropy strength: a stretched highlight instead of a round one.\n   * Silk, brushed metal, carbon fibre. NOTE it follows the geometry's UVs,\n   * so a mesh with no UV attribute will not show it.\n   */\n  anisotropy?: number;\n  /** Which way the grain runs, in radians. */\n  anisotropyRotation?: number;\n  /** Thin-film iridescence — nacre, beetle shell, oil on water. */\n  iridescence?: number;\n  /** Iridescent film IOR. */\n  iridescenceIOR?: number;\n  /** Film thickness range in nanometres, [min, max]. */\n  iridescenceThickness?: [number, number];\n  /** Transmission: light passing THROUGH. Expensive — see `physical`. */\n  transmission?: number;\n  /** How thick the volume is, for transmission. */\n  thickness?: number;\n  /** Index of refraction (water 1.33, ice 1.31, glass 1.5, diamond 2.42). */\n  ior?: number;\n  /**\n   * Chromatic dispersion: how far apart the red and blue refractions fall.\n   *\n   * A real transparent solid has a DIFFERENT index of refraction for every\n   * wavelength, which is why a cut stone throws colour and a window does not.\n   * three refracts red, green and blue separately over\n   * `ior ± (ior - 1) * 0.025 * dispersion`, so the spread grows with the IOR\n   * as well — 0 is a plain glass, 10 on a diamond-grade IOR is a gemstone.\n   *\n   * Needs `transmission` and a non-zero `thickness`: with nothing passing\n   * through the volume there is nothing to split. Costs a third transmission\n   * sample per pixel.\n   */\n  dispersion?: number;\n  /** Colour light picks up on its way through, as a hex int. */\n  attenuationColor?: number;\n  /** How far light travels before it is fully attenuated, in metres. */\n  attenuationDistance?: number;\n\n  // --- ribs: parallel ridges (opt-in; `ribs: 0` — the default — is flat) ---\n  /** Ridge relief strength (0 off). Corrugated sheet, fluting, tread plate. */\n  ribs?: number;\n  /** Ridges per world metre. */\n  ribScale?: number;\n  /**\n   * 1 = turn the ridges through 90° on the face. Ribs are laid out in the\n   * FACE's own plane, not along a world axis: two world axes that differ in\n   * space collapse onto the same direction once projected onto a wall, which\n   * turned a crossed tread plate back into plain stripes.\n   */\n  ribTurn?: number;\n  /** 1 = a second set crossed over the first: the diamond studs of tread plate. */\n  ribCross?: number;\n\n  // --- aggregate: hard-edged chips (opt-in; `speck: 0` — the default — off) ---\n  /**\n   * Chip contrast. The rest of the shader runs on smooth fbm, which reads as\n   * mottling; asphalt is STONES IN TAR, and stones have edges.\n   */\n  speck?: number;\n  /** Chips per world metre. */\n  speckScale?: number;\n\n  // --- cells: warped Voronoi (opt-in; `cells: 0` — the default — off) ---\n  /**\n   * Cell strength. Irregular polygons: the columnar jointing of basalt and\n   * the zinc spangle on galvanised steel are the same function at two very\n   * different scales.\n   */\n  cells?: number;\n  /** Cells per world metre. */\n  cellScale?: number;\n  /** How dark and deep the seams between cells run (0–1). */\n  cellEdge?: number;\n  /** Per-cell brightness jitter (0–1). */\n  cellJitter?: number;\n  /**\n   * 1 = lay the cells out in PLAN and extrude them vertically, whatever the\n   * face is pointing at. That is what columnar jointing is — a crazy paving\n   * seen from above, pulled up into columns — and projecting it per-face\n   * instead gives blotches on a wall rather than columns in a cliff.\n   */\n  cellPlan?: number;\n\n  // --- crust: a mineral layer (opt-in; `crust: 0` — the default — off) ---\n  /**\n   * How much crust has grown: verdigris on copper, a rust bloom, lichen.\n   * It takes in the cavities and on the up-facing side, and it is a CRUST,\n   * not a tint — wherever it takes hold the metalness goes with it, because\n   * a mineral scab does not reflect like the metal underneath.\n   */\n  crust?: number;\n  /** Crust colour, as a hex int (verdigris green, rust orange…). */\n  crustColor?: number;\n  /** Roughness inside the crust. */\n  crustRough?: number;\n\n  // --- wear: water (opt-in; `wet: 0` — the default — is bone dry) ---\n  /**\n   * How wet the surface is: 0 dry, 1 running with water.\n   *\n   * This is a STATE, not a kind. Every one of the presets above can be\n   * rained on, and water behaves the same way on all of them — it darkens\n   * the albedo, collapses the roughness to a film, and, most of all, it\n   * POOLS: at a light wetting only the hollows and the mortar joints are\n   * dark and glossy, and the face only sheets over when it is properly\n   * raining.\n   */\n  wet?: number;\n  /**\n   * How well water clings to vertical faces: 0 = only the tops ever get\n   * wet, 1 = a wall wets as fast as a floor. Default 0.55 — rain falls\n   * down, so a sill soaks while the wall under it is merely damp. Sealed,\n   * shedding surfaces (glass, glaze, chrome) want less; things that wick\n   * (plaster, concrete, canvas) want more.\n   */\n  wetCling?: number;\n\n  // --- emissive glow (opt-in; `glow: 0` — the default — keeps it dark) ---\n  /** Glow strength added straight to emissive radiance (lava, crystal, runes). */\n  glow?: number;\n  /** Glow colour, as a hex int. */\n  glowColor?: number;\n  /** How much of the surface glows: low = only the deep cracks, high = most of it. */\n  glowThreshold?: number;\n}\n\nconst V = (x: number, y: number, z: number): Vector3 => new Vector3(x, y, z);\n\nexport const SURFACE_PRESETS: Record<SurfaceKind, SurfaceParams> = {\n  // Lime-washed cottage walls: soft warm mottle, gentle relief.\n  plaster: {\n    baseColor: 0xd9ccb0,\n    roughness: 0.92, metalness: 0, scale: 3.4, albedoVar: 0.14, tint: 0x9c8f74,\n    tintAmount: 0.12, ao: 0.18, bump: 0.15, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Rough weathered stone: strong cavity AO + mossy tint, pitted relief.\n  stone: {\n    baseColor: 0x8a8f98,\n    roughness: 0.96, metalness: 0, scale: 2.6, albedoVar: 0.26, tint: 0x5c6b44,\n    tintAmount: 0.16, ao: 0.34, bump: 0.42, roughVar: 0.14, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Structural timber: pronounced grain rings darkening the albedo.\n  wood: {\n    baseColor: 0x8a6642,\n    roughness: 0.82, metalness: 0, scale: 5.5, albedoVar: 0.12, tint: 0x3a2a1c,\n    tintAmount: 0.14, ao: 0.16, bump: 0.12, roughVar: 0.12, grain: 0.55,\n    grainScale: 3.2, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Sawn planks: finer, straighter grain, a touch smoother.\n  plank: {\n    baseColor: 0x9a7a52,\n    roughness: 0.78, metalness: 0, scale: 6.5, albedoVar: 0.1, tint: 0x40301f,\n    tintAmount: 0.1, ao: 0.12, bump: 0.1, roughVar: 0.1, grain: 0.4,\n    grainScale: 5.0, grainAxis: V(1, 0, 0), flat: true,\n  },\n  // Straw thatch: busy fibrous streaking, high roughness, deep shadowing.\n  thatch: {\n    baseColor: 0xb39a5c,\n    roughness: 0.98, metalness: 0, scale: 9.0, albedoVar: 0.3, tint: 0x6a5324,\n    tintAmount: 0.2, ao: 0.28, bump: 0.3, roughVar: 0.08, grain: 0.35,\n    grainScale: 8.0, grainAxis: V(0, 0, 1), flat: true,\n  },\n  // Clay roof tiles: regular ridged rows, warm cavity tint.\n  tile: {\n    baseColor: 0xa8563e,\n    roughness: 0.7, metalness: 0, scale: 4.0, albedoVar: 0.14, tint: 0x6e2f22,\n    tintAmount: 0.16, ao: 0.24, bump: 0.34, roughVar: 0.1, grain: 0.6,\n    grainScale: 6.0, grainAxis: V(1, 0, 0), flat: true,\n  },\n  // Aged iron/bronze: mild mottle, low roughness variance, metallic.\n  metal: {\n    baseColor: 0x3d4451,\n    roughness: 0.52, metalness: 0.85, scale: 4.5, albedoVar: 0.16, tint: 0x2a2118,\n    tintAmount: 0.18, ao: 0.22, bump: 0.16, roughVar: 0.2, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Turf. There is a `createGrass` prop for blades you can see individually;\n  // this is the ground they stand in, and the ground on its own from any\n  // distance. `moss` is NOT this — it is a grey-green cap that grows ON stone,\n  // and reaching for it as a lawn gives a field the colour of a damp wall.\n  // (Written after doing exactly that.)\n  grass: {\n    baseColor: 0x5c7a3c,\n    roughness: 0.97, metalness: 0, scale: 5.5, albedoVar: 0.26, tint: 0x33471f,\n    tintAmount: 0.24, ao: 0.24, bump: 0.22, roughVar: 0.08, grain: 0.22,\n    grainScale: 11.0, grainAxis: V(0, 0, 1), flat: true,\n  },\n  // Packed earth: broad soft variation, strong low-frequency patches.\n  dirt: {\n    baseColor: 0x8a7a58,\n    roughness: 1.0, metalness: 0, scale: 2.0, albedoVar: 0.22, tint: 0x4a3524,\n    tintAmount: 0.2, ao: 0.3, bump: 0.1, roughVar: 0.05, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n\n  // --- Tier 1: ground & terrain -----------------------------------------\n  // Fine granular sand with soft wind ripples; smooth-shaded dunes.\n  sand: {\n    baseColor: 0xcbb98a,\n    roughness: 1.0, metalness: 0, scale: 7.0, albedoVar: 0.16, tint: 0x9c8048,\n    tintAmount: 0.14, ao: 0.14, bump: 0.14, roughVar: 0.06, grain: 0.18,\n    grainScale: 3.0, grainAxis: V(1, 0, 0), flat: false,\n  },\n  // Loose gravel / riverbed: chunky faceted stones, strong relief.\n  gravel: {\n    baseColor: 0x9a948a,\n    roughness: 0.95, metalness: 0, scale: 5.5, albedoVar: 0.3, tint: 0x605a4f,\n    tintAmount: 0.18, ao: 0.32, bump: 0.5, roughVar: 0.16, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Wet churned mud: darkened patches with a damp sheen (lower roughness).\n  mud: {\n    baseColor: 0x4a3826,\n    roughness: 0.6, metalness: 0, scale: 2.4, albedoVar: 0.2, tint: 0x241708,\n    tintAmount: 0.3, ao: 0.34, bump: 0.16, roughVar: 0.24, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n\n  // --- Tier 1: stone & masonry ------------------------------------------\n  // Warm sandstone: soft, faintly streaked, gentle relief — temples, cliffs.\n  sandstone: {\n    baseColor: 0xc9a06a,\n    roughness: 0.9, metalness: 0, scale: 3.4, albedoVar: 0.18, tint: 0x8a6a3c,\n    tintAmount: 0.16, ao: 0.22, bump: 0.24, roughVar: 0.1, grain: 0.22,\n    grainScale: 2.4, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Speckled granite: fine mineral fleck, low relief, a hint of polish.\n  granite: {\n    baseColor: 0x8e8a94,\n    roughness: 0.58, metalness: 0, scale: 9.0, albedoVar: 0.34, tint: 0x45414d,\n    tintAmount: 0.14, ao: 0.14, bump: 0.12, roughVar: 0.2, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Dark slate: blue-grey cleavage plates, low roughness, subtle streaks.\n  slate: {\n    baseColor: 0x50565e,\n    roughness: 0.55, metalness: 0, scale: 3.0, albedoVar: 0.14, tint: 0x2c3540,\n    tintAmount: 0.2, ao: 0.2, bump: 0.18, roughVar: 0.12, grain: 0.2,\n    grainScale: 3.0, grainAxis: V(1, 0, 0), flat: true,\n  },\n\n  // --- Tier 1: organic --------------------------------------------------\n  // Tree bark: deep vertical ridges (grain rings around the trunk axis).\n  bark: {\n    baseColor: 0x5a4535,\n    roughness: 0.92, metalness: 0, scale: 6.0, albedoVar: 0.2, tint: 0x2a1c10,\n    tintAmount: 0.2, ao: 0.26, bump: 0.45, roughVar: 0.12, grain: 0.7,\n    grainScale: 5.5, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Leather / hide: soft mottled grain with a gentle sheen — straps, armour.\n  leather: {\n    baseColor: 0x6a4630,\n    roughness: 0.62, metalness: 0, scale: 5.0, albedoVar: 0.16, tint: 0x2e1a0f,\n    tintAmount: 0.22, ao: 0.2, bump: 0.18, roughVar: 0.12, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Woven canvas: fine directional weave, matte — tents, sails, sacks.\n  canvas: {\n    baseColor: 0xcabd9c,\n    roughness: 0.95, metalness: 0, scale: 11.0, albedoVar: 0.14, tint: 0x8a7a5c,\n    tintAmount: 0.12, ao: 0.14, bump: 0.12, roughVar: 0.06, grain: 0.3,\n    grainScale: 12.0, grainAxis: V(1, 0, 0), flat: false,\n  },\n  // Aged parchment: near-white with soft foxing stains — signs, scrolls.\n  parchment: {\n    baseColor: 0xe0d4b0,\n    roughness: 0.9, metalness: 0, scale: 3.0, albedoVar: 0.12, tint: 0x9a8558,\n    tintAmount: 0.18, ao: 0.16, bump: 0.08, roughVar: 0.06, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Unglazed terracotta: warm clay, smooth, low relief — pots, urns.\n  terracotta: {\n    baseColor: 0xb5623a,\n    roughness: 0.72, metalness: 0, scale: 4.0, albedoVar: 0.12, tint: 0x7a3a20,\n    tintAmount: 0.16, ao: 0.16, bump: 0.14, roughVar: 0.08, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Weathered bone / ivory: off-white with cavity staining — ossuaries, décor.\n  bone: {\n    baseColor: 0xdcd2ba,\n    roughness: 0.55, metalness: 0, scale: 6.0, albedoVar: 0.14, tint: 0x8a7f66,\n    tintAmount: 0.2, ao: 0.22, bump: 0.12, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n\n  // --- Tier 1: metals ---------------------------------------------------\n  // Rusted iron: patchy orange corrosion, part-metallic, high roughness spread.\n  rust: {\n    baseColor: 0x8a4a2c,\n    roughness: 0.85, metalness: 0.25, scale: 4.0, albedoVar: 0.28, tint: 0x5a2a12,\n    tintAmount: 0.3, ao: 0.26, bump: 0.28, roughVar: 0.3, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Bronze: warm dark metal with patina in the cavities — bells, statues.\n  bronze: {\n    baseColor: 0x9a6a3a,\n    roughness: 0.44, metalness: 0.9, scale: 4.5, albedoVar: 0.16, tint: 0x3a2410,\n    tintAmount: 0.24, ao: 0.22, bump: 0.16, roughVar: 0.18, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n  // Brass: brighter warm metal, smoother and shinier — fittings, instruments.\n  brass: {\n    baseColor: 0xc9a24a,\n    roughness: 0.34, metalness: 0.95, scale: 5.0, albedoVar: 0.12, tint: 0x6a4a12,\n    tintAmount: 0.16, ao: 0.16, bump: 0.1, roughVar: 0.14, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n  },\n\n  // --- Tier 2: masonry tiling -------------------------------------------\n  // Fired brick in running bond: long thin courses, pale mortar, bricks that\n  // each weather a little differently.\n  brick: {\n    baseColor: 0x9e4a34,\n    roughness: 0.86, metalness: 0, scale: 6.0, albedoVar: 0.14, tint: 0x5a2418,\n    tintAmount: 0.16, ao: 0.2, bump: 0.1, roughVar: 0.12, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    tile: 1, tileW: 0.25, tileH: 0.085, mortar: 0.014, bond: 1, round: 0,\n    tileJitter: 0.18, mortarColor: 0xb3a892, tileRelief: 0.06,\n  },\n  // Rounded cobblestones: small domed setts, wide earthy joints, heavy\n  // per-stone variation — streets and courtyards.\n  cobblestone: {\n    baseColor: 0x8a8f98,\n    roughness: 0.9, metalness: 0, scale: 5.0, albedoVar: 0.2, tint: 0x4a4436,\n    tintAmount: 0.18, ao: 0.28, bump: 0.2, roughVar: 0.14, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    tile: 1, tileW: 0.17, tileH: 0.15, mortar: 0.03, bond: 1, round: 1,\n    tileJitter: 0.24, mortarColor: 0x35322b, tileRelief: 0.13,\n  },\n  // Ashlar: large squared blocks, tight fine joints — castle and keep walls.\n  ashlar: {\n    baseColor: 0xb9b2a4,\n    roughness: 0.9, metalness: 0, scale: 3.0, albedoVar: 0.16, tint: 0x7a725f,\n    tintAmount: 0.16, ao: 0.2, bump: 0.12, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    tile: 1, tileW: 0.55, tileH: 0.32, mortar: 0.02, bond: 0.5, round: 0,\n    tileJitter: 0.1, mortarColor: 0x857e6f, tileRelief: 0.05,\n  },\n  // Floor tiles: an aligned grid of square flags, dark grout, low relief —\n  // halls and plazas.\n  floortile: {\n    baseColor: 0x6a6e72,\n    roughness: 0.5, metalness: 0, scale: 4.0, albedoVar: 0.12, tint: 0x33363a,\n    tintAmount: 0.16, ao: 0.16, bump: 0.08, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    tile: 1, tileW: 0.4, tileH: 0.4, mortar: 0.014, bond: 0, round: 0,\n    tileJitter: 0.12, mortarColor: 0x2a2a2c, tileRelief: 0.045,\n  },\n  // Wooden shingles: overlapping courses with grain, deep shadow lines —\n  // roofs and spires.\n  shingle: {\n    baseColor: 0x6b4a33,\n    roughness: 0.85, metalness: 0, scale: 6.0, albedoVar: 0.16, tint: 0x2e2012,\n    tintAmount: 0.18, ao: 0.24, bump: 0.12, roughVar: 0.12, grain: 0.32,\n    grainScale: 6.0, grainAxis: V(0, 1, 0), flat: true,\n    tile: 1, tileW: 0.2, tileH: 0.13, mortar: 0.012, bond: 1, round: 0,\n    tileJitter: 0.2, mortarColor: 0x241811, tileRelief: 0.09,\n  },\n\n  // --- Tier 3: cap & glow -----------------------------------------------\n  // Snow settled on cold rock: white on every up-facing face, grey stone on\n  // the sides, its edge broken by the noise. Cap works on any surface.\n  snow: {\n    baseColor: 0x9aa0a8,\n    roughness: 0.9, metalness: 0, scale: 4.0, albedoVar: 0.14, tint: 0x6a7280,\n    tintAmount: 0.14, ao: 0.2, bump: 0.24, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    cap: 0.95, capColor: 0xf4f8fc, capUp: 0.12, capSharp: 0.32, capRough: 0.9,\n  },\n  // Moss creeping over a boulder: grey stone with green growth on the tops\n  // and shoulders, thicker (lower capUp) than snow.\n  moss: {\n    baseColor: 0x8a8f88,\n    roughness: 0.94, metalness: 0, scale: 3.5, albedoVar: 0.2, tint: 0x4a5240,\n    tintAmount: 0.16, ao: 0.28, bump: 0.34, roughVar: 0.12, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    cap: 0.74, capColor: 0x40592a, capUp: 0.34, capSharp: 0.3, capRough: 0.96,\n  },\n  // Cooling lava: dark basalt crust with molten orange glowing up through the\n  // cracks. The glow burns constant, day or night.\n  lava: {\n    baseColor: 0x2a1712,\n    roughness: 0.88, metalness: 0, scale: 3.2, albedoVar: 0.18, tint: 0x120806,\n    tintAmount: 0.26, ao: 0.34, bump: 0.5, roughVar: 0.14, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    glow: 2.6, glowColor: 0xff5a1e, glowThreshold: 0.3,\n  },\n  // Glowing crystal: faceted blue mineral lit from within — most of the face\n  // emits, the deepest cavities darkest.\n  crystal: {\n    baseColor: 0x3a5c8a,\n    roughness: 0.22, metalness: 0, scale: 5.0, albedoVar: 0.14, tint: 0x1a2f52,\n    tintAmount: 0.2, ao: 0.16, bump: 0.3, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    glow: 1.5, glowColor: 0x6fd6ff, glowThreshold: 0.72,\n  },\n\n  // --- Tier 4: modern & machined ----------------------------------------\n  // Fair-faced concrete: near-even grey with fine mottle and shutter-panel\n  // joint lines (the tiling grid, dialed way down) — the modern wall.\n  concrete: {\n    baseColor: 0xb5b3ac,\n    roughness: 0.88, metalness: 0, scale: 3.0, albedoVar: 0.08, tint: 0x8a887f,\n    tintAmount: 0.08, ao: 0.12, bump: 0.06, roughVar: 0.08, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    tile: 1, tileW: 1.2, tileH: 0.6, mortar: 0.006, bond: 0, round: 0,\n    tileJitter: 0.05, mortarColor: 0x9a9891, tileRelief: 0.03,\n  },\n  // Modern painted render: almost flat, just enough micro-variation to be a\n  // material at all. Colour it anything; satin sheen.\n  paint: {\n    baseColor: 0xdedbd2,\n    roughness: 0.6, metalness: 0, scale: 6.0, albedoVar: 0.04, tint: 0xb8b4a8,\n    tintAmount: 0.05, ao: 0.05, bump: 0.02, roughVar: 0.05, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Polished marble: near-white slab, dark warped veins (the grain rings at a\n  // very low frequency), glossy — lobby floors and counters.\n  marble: {\n    baseColor: 0xe8e6e0,\n    roughness: 0.22, metalness: 0, scale: 1.6, albedoVar: 0.08, tint: 0x7a8494,\n    tintAmount: 0.12, ao: 0.08, bump: 0.04, roughVar: 0.08, grain: 0.5,\n    grainScale: 0.7, grainAxis: V(0.35, 1, 0.2), flat: false,\n  },\n  // Terrazzo: a cement field packed with tiny per-cell chips, a share of them\n  // in the accent tint — the classic speckled floor.\n  terrazzo: {\n    baseColor: 0xd8d2c6,\n    roughness: 0.35, metalness: 0, scale: 10.0, albedoVar: 0.1, tint: 0x6a604f,\n    tintAmount: 0.04, ao: 0.06, bump: 0.03, roughVar: 0.08, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    tile: 1, tileW: 0.045, tileH: 0.045, mortar: 0.01, bond: 0, round: 1,\n    tileJitter: 0.35, mortarColor: 0xcfc9bd, tileRelief: 0.02, tileTint: 0.18,\n  },\n  // Brushed stainless steel: fine directional streaks (the grain machinery\n  // pointed at metal), cool and semi-gloss — railings, kick plates.\n  steel: {\n    baseColor: 0xaeb4bc,\n    roughness: 0.38, metalness: 0.92, scale: 8.0, albedoVar: 0.06, tint: 0x6a707a,\n    tintAmount: 0.08, ao: 0.06, bump: 0.03, roughVar: 0.18, grain: 0.3,\n    grainScale: 26.0, grainAxis: V(1, 0, 0), flat: false,\n  },\n  // Chrome: near-mirror metal — fittings and trim. (Stylized: no envmap, the\n  // lighting rig does the selling.)\n  chrome: {\n    baseColor: 0xc9ced4,\n    roughness: 0.08, metalness: 1.0, scale: 5.0, albedoVar: 0.04, tint: 0x8a9098,\n    tintAmount: 0.05, ao: 0.04, bump: 0.01, roughVar: 0.05, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Powder-coated metal: coloured semi-gloss with a whisper of orange peel —\n  // gates, frames, railings. Colour it from the palette.\n  paintedMetal: {\n    baseColor: 0x44505c,\n    roughness: 0.42, metalness: 0.35, scale: 14.0, albedoVar: 0.04, tint: 0x222a30,\n    tintAmount: 0.06, ao: 0.06, bump: 0.04, roughVar: 0.08, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Corten weathering steel: the even architectural oxide bloom, warmer and\n  // calmer than 'rust' — feature walls and planters.\n  corten: {\n    baseColor: 0x9a5a34,\n    roughness: 0.82, metalness: 0.18, scale: 3.2, albedoVar: 0.16, tint: 0x5a2e16,\n    tintAmount: 0.22, ao: 0.16, bump: 0.1, roughVar: 0.18, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Oiled teak: warm decking/furniture wood under a varnish sheen.\n  teak: {\n    baseColor: 0x8a5c36,\n    roughness: 0.3, metalness: 0, scale: 6.0, albedoVar: 0.08, tint: 0x3c2614,\n    tintAmount: 0.12, ao: 0.1, bump: 0.05, roughVar: 0.08, grain: 0.45,\n    grainScale: 4.5, grainAxis: V(1, 0, 0), flat: false,\n  },\n  // Large-format porcelain: big glossy slabs, hairline grout, offset courses.\n  porcelain: {\n    baseColor: 0xd9d6cf,\n    roughness: 0.18, metalness: 0, scale: 3.0, albedoVar: 0.05, tint: 0x9a968c,\n    tintAmount: 0.06, ao: 0.08, bump: 0.03, roughVar: 0.06, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    tile: 1, tileW: 1.2, tileH: 0.6, mortar: 0.005, bond: 0.5, round: 0,\n    tileJitter: 0.06, mortarColor: 0xb0aca2, tileRelief: 0.03,\n  },\n  // Vitreous glaze: sanitaryware. Note it is NOT 'porcelain' — that preset is\n  // large-format porcelain FLOOR TILE, complete with grout, and a bath shell\n  // built from it comes out looking like a tiled box rather than one fired\n  // piece. Glaze is a single unbroken skin: glossy, almost no variation, and\n  // no tiling at all.\n  glaze: {\n    baseColor: 0xf2f0ea,\n    roughness: 0.12, metalness: 0, scale: 5.0, albedoVar: 0.02, tint: 0xc8c6be,\n    tintAmount: 0.05, ao: 0.07, bump: 0.008, roughVar: 0.03, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n  },\n  // Glass mosaic: tiny gridded tesserae, strong per-chip variation, a share of\n  // accent-tint chips, pale grout — pools and feature walls.\n  mosaic: {\n    baseColor: 0x3f7fae,\n    roughness: 0.25, metalness: 0, scale: 8.0, albedoVar: 0.08, tint: 0x1d4e74,\n    tintAmount: 0.05, ao: 0.08, bump: 0.04, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    tile: 1, tileW: 0.055, tileH: 0.055, mortar: 0.006, bond: 0, round: 0,\n    tileJitter: 0.3, mortarColor: 0xd8d4c8, tileRelief: 0.05, tileTint: 0.3,\n  },\n  // Chevron parquet: narrow varnished planks laid in alternating ±45° bands.\n  parquet: {\n    baseColor: 0x9a6b40,\n    roughness: 0.32, metalness: 0, scale: 6.0, albedoVar: 0.07, tint: 0x4a2f18,\n    tintAmount: 0.1, ao: 0.08, bump: 0.04, roughVar: 0.08, grain: 0.3,\n    grainScale: 7.0, grainAxis: V(1, 0, 0), flat: false,\n    tile: 1, tileW: 0.5, tileH: 0.09, mortar: 0.006, bond: 0, round: 0,\n    tileJitter: 0.14, mortarColor: 0x5a3b22, tileRelief: 0.04, chevron: 1,\n  },\n  // Patterned cement tiles: a cream field, each tile stamped with a ring-and-\n  // dot motif in the tint colour — verandas, courtyards, feature floors.\n  patternedTile: {\n    baseColor: 0xdcd7c9,\n    roughness: 0.3, metalness: 0, scale: 3.0, albedoVar: 0.05, tint: 0x365f74,\n    tintAmount: 0.04, ao: 0.08, bump: 0.03, roughVar: 0.06, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    tile: 1, tileW: 0.33, tileH: 0.33, mortar: 0.008, bond: 0, round: 0,\n    tileJitter: 0.05, mortarColor: 0xa9a396, tileRelief: 0.035, motif: 0.9,\n  },\n\n  // --- Tier 5: industrial ------------------------------------------------\n  // Sheet steel rolled into a wave, galvanised, and left out in the rain.\n  // The corrugation is the ONLY reason a flat plane reads as a roof from\n  // fifty metres, and it is why this is the most recognisable industrial\n  // material there is: a shanty wall, a barn, a lock-up, a site hoarding.\n  corrugatedIron: {\n    baseColor: 0x9aa2a6,\n    roughness: 0.58, metalness: 0.5, scale: 9, albedoVar: 0.1, tint: 0x6a5545,\n    tintAmount: 0.16, ao: 0.16, bump: 0.35, roughVar: 0.18, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    ribs: 1, ribScale: 5,\n    // Rust takes in the valleys, where the water sits — but a sheet that is\n    // more rust than iron reads as rust, not as a roof.\n    crust: 0.3, crustColor: 0x8a4a26, crustRough: 0.95,\n  },\n  // Stones in tar. The chips are the point: a smooth mottle is mud, and\n  // the reason a road reads as a road at all is that it is visibly made of\n  // gravel somebody poured glue over.\n  asphalt: {\n    baseColor: 0x3b3b3e,\n    roughness: 0.93, metalness: 0, scale: 14, albedoVar: 0.1, tint: 0x1e1e20,\n    tintAmount: 0.18, ao: 0.2, bump: 0.4, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    speck: 0.42, speckScale: 40,\n  },\n  // Tread plate: raised studs in a diamond lattice, so a gantry floor has\n  // grip and a stair tread reads as a stair tread.\n  diamondPlate: {\n    baseColor: 0x8d9399,\n    roughness: 0.44, metalness: 0.55, scale: 12, albedoVar: 0.05, tint: 0x5c6166,\n    tintAmount: 0.1, ao: 0.1, bump: 0.5, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    ribs: 1, ribScale: 6, ribCross: 1,\n  },\n  // THE SPANGLE is the whole material: hot-dip galvanising freezes into\n  // visible zinc crystals, each catching the light its own way, and no\n  // other metal has that.\n  galvanised: {\n    baseColor: 0xa9b1b5,\n    roughness: 0.46, metalness: 0.5, scale: 3.5, albedoVar: 0.04, tint: 0x7d868b,\n    tintAmount: 0.06, ao: 0.05, bump: 0.1, roughVar: 0.05, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    // Grains you can SEE, each catching the light its own way. Real spangle\n    // is millimetres, but a crystal too small to resolve is not a crystal —\n    // it is per-pixel noise, and on a metal that is a sparkling mess.\n    cells: 1, cellScale: 6, cellEdge: 0.22, cellJitter: 0.4,\n  },\n  // Copper does not stay copper. Verdigris grows in the cavities and on\n  // the up-facing side, and where it grows the metal is gone — which is\n  // why a green roof is matte and a copper one is not.\n  copperPatina: {\n    baseColor: 0x9c5b34,\n    roughness: 0.42, metalness: 0.75, scale: 6, albedoVar: 0.08, tint: 0x6b3a1f,\n    tintAmount: 0.12, ao: 0.14, bump: 0.2, roughVar: 0.12, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    crust: 0.95, crustColor: 0x4fa88a, crustRough: 0.92,\n  },\n  // Columnar jointing: lava that cooled slowly and cracked into polygons,\n  // then stood up as columns. Laid out in PLAN and extruded, because that\n  // is what the rock actually did.\n  basalt: {\n    baseColor: 0x54585e,\n    roughness: 0.86, metalness: 0.04, scale: 3, albedoVar: 0.06, tint: 0x2b2e33,\n    tintAmount: 0.12, ao: 0.16, bump: 0.5, roughVar: 0.1, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    cells: 1, cellScale: 3.2, cellEdge: 0.85, cellJitter: 0.16, cellPlan: 1,\n  },\n\n  // --- Tier 6: physical --------------------------------------------------\n  // Each of these is defined by a light response the standard model has no\n  // term for. They are the one part of the catalogue that costs more than a\n  // MeshStandardMaterial, and they are worth it only because nothing else\n  // gets you there.\n\n  // THE RIM IS THE MATERIAL. Velvet is dark where you look straight at it\n  // and bright at every grazing edge, because the pile scatters sideways —\n  // and no amount of albedo and roughness produces that.\n  velvet: {\n    baseColor: 0x6d1730,\n    roughness: 0.92, metalness: 0, scale: 7, albedoVar: 0.05, tint: 0x3a0a19,\n    tintAmount: 0.14, ao: 0.12, bump: 0.015, roughVar: 0.03, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    physical: true, sheen: 1, sheenColor: 0xd8869c, sheenRough: 0.32,\n  },\n  // A stretched highlight instead of a round one: the thread runs one way,\n  // so the light does too.\n  silk: {\n    baseColor: 0xc9b7d8,\n    roughness: 0.2, metalness: 0.05, scale: 6, albedoVar: 0.04, tint: 0x8f7fa6,\n    tintAmount: 0.08, ao: 0.06, bump: 0.01, roughVar: 0.02, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    physical: true, anisotropy: 0.9, anisotropyRotation: 0,\n    sheen: 0.35, sheenColor: 0xffffff, sheenRough: 0.2,\n  },\n  // The linisher went one way. Brushed aluminium, a kitchen front, a lift\n  // door — the streak is the only thing separating it from plain steel.\n  brushedMetal: {\n    baseColor: 0xb4b9be,\n    roughness: 0.34, metalness: 0.7, scale: 5, albedoVar: 0.02, tint: 0x8b9196,\n    tintAmount: 0.04, ao: 0.03, bump: 0.008, roughVar: 0.02, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    physical: true, anisotropy: 0.95, anisotropyRotation: Math.PI / 2,\n  },\n  // Thin-film interference: the hue depends on how thick the film is and\n  // which way you are looking, which is why a shell has no fixed colour.\n  nacre: {\n    baseColor: 0xdcd8d0,\n    roughness: 0.12, metalness: 0.2, scale: 4, albedoVar: 0.04, tint: 0xa8b0c0,\n    tintAmount: 0.18, ao: 0.05, bump: 0.02, roughVar: 0.02, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    // Low metalness on purpose. Iridescence is strongest over a metallic\n    // F0, but a metal with no environment map to reflect renders as dead\n    // grey — so the film sits over a pale pearl instead, and a thick one\n    // (a high IOR) makes the highlight visibly COLOURED rather than white.\n    physical: true, iridescence: 1, iridescenceIOR: 2.4,\n    iridescenceThickness: [300, 900],\n  },\n  // Light goes THROUGH. The cells are the fracture planes inside it — ice\n  // is not clear, it is full of the cracks it froze around.\n  ice: {\n    baseColor: 0xcfe8f2,\n    roughness: 0.13, metalness: 0, scale: 2.5, albedoVar: 0.05, tint: 0x7fb4cc,\n    tintAmount: 0.14, ao: 0.08, bump: 0.06, roughVar: 0.03, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: false,\n    cells: 1, cellScale: 1.8, cellEdge: 0.22, cellJitter: 0.08,\n    physical: true, transmission: 0.85, thickness: 0.5, ior: 1.31,\n    attenuationColor: 0x9fd4e8, attenuationDistance: 1.4,\n  },\n  // WHY A STONE IS CUT AT ALL. Glass and diamond are both transparent; the\n  // difference you can see across a room is that diamond has a different\n  // index of refraction for every wavelength, so the white light that goes\n  // in comes out separated. Facets exist to make that happen more often.\n  //\n  // Both halves of that are here. `dispersion` does the splitting — and the\n  // spread scales with the IOR, so a gem-grade 2.2 throws far more colour\n  // than a window's 1.5 at the same setting. `flat: true` does the facets:\n  // flat shading on a low-poly solid IS a cut stone, and the coarse cells\n  // are the inclusions that stop it looking like moulded acrylic.\n  gemstone: {\n    baseColor: 0xe6f4ff,\n    roughness: 0.03, metalness: 0, scale: 3.5, albedoVar: 0.03, tint: 0x8fc2e8,\n    tintAmount: 0.08, ao: 0.02, bump: 0, roughVar: 0.015, grain: 0,\n    grainScale: 1, grainAxis: V(0, 1, 0), flat: true,\n    cells: 0.5, cellScale: 2.2, cellEdge: 0.12, cellJitter: 0.25,\n    // A long attenuation distance on purpose. The colour is supposed to come\n    // OUT of the stone, and one that swallows the light on its way through\n    // is a dark blob whatever the IOR is doing.\n    physical: true, transmission: 1, thickness: 0.55, ior: 2.2, dispersion: 14,\n    attenuationColor: 0xb8e4ff, attenuationDistance: 2.4,\n  },\n};\n\nexport interface SurfaceOptions extends Partial<SurfaceParams> {\n  /** Base colour (hex int or three Color). Defaults to a neutral grey. */\n  color?: number | Color;\n  /**\n   * Seed offset so two props with the same colour still weather\n   * differently. Any number; shifts the noise field.\n   */\n  seed?: number;\n}\n\n// ---- GLSL --------------------------------------------------------------\n\nconst NOISE_GLSL = /* glsl */ `\n// World-space coordinates get large (props far from the origin, plus the seed\n// offset), and mobile GPUs default the fragment stage to mediump — where\n// fract() of a big number loses precision and the noise/grain visibly swims.\n// Force highp on the world varyings and the noise maths so it stays put.\nvarying highp vec3 vSurfWorldPos;\nvarying highp vec3 vSurfWorldNormal;\nuniform highp vec3 uSurfSeed;\nuniform float uSurfScale;\nuniform float uSurfAlbedoVar;\nuniform vec3  uSurfTint;\nuniform float uSurfTintAmount;\nuniform float uSurfAO;\nuniform float uSurfBump;\nuniform float uSurfRoughVar;\nuniform float uSurfGrain;\nuniform float uSurfGrainScale;\nuniform vec3  uSurfGrainAxis;\nuniform float uSurfTile;\nuniform highp vec2 uSurfTileSize;\nuniform float uSurfMortar;\nuniform float uSurfTileBond;\nuniform float uSurfTileRound;\nuniform float uSurfTileJitter;\nuniform vec3  uSurfMortarColor;\nuniform float uSurfTileRelief;\nuniform float uSurfTileTint;\nuniform float uSurfTileChevron;\nuniform float uSurfTileMotif;\nuniform float uSurfCap;\nuniform vec3  uSurfCapColor;\nuniform float uSurfCapUp;\nuniform float uSurfCapSharp;\nuniform float uSurfCapRough;\nuniform float uSurfGlow;\nuniform vec3  uSurfGlowColor;\nuniform float uSurfGlowThresh;\nuniform float uSurfWet;\nuniform float uSurfWetCling;\nuniform float uSurfRibs;\nuniform float uSurfRibScale;\nuniform float uSurfRibTurn;\nuniform float uSurfRibCross;\nuniform float uSurfSpeck;\nuniform float uSurfSpeckScale;\nuniform float uSurfCells;\nuniform float uSurfCellScale;\nuniform float uSurfCellEdge;\nuniform float uSurfCellJitter;\nuniform float uSurfCellPlan;\nuniform float uSurfCrust;\nuniform vec3  uSurfCrustColor;\nuniform float uSurfCrustRough;\n\nfloat scenaHash13(highp vec3 p){\n  p = fract(p * 0.1031);\n  p += dot(p, p.yzx + 33.33);\n  return fract((p.x + p.y) * p.z);\n}\nfloat scenaVNoise(highp vec3 x){\n  highp vec3 i = floor(x); highp vec3 f = fract(x);\n  f = f * f * (3.0 - 2.0 * f);\n  float n000 = scenaHash13(i + vec3(0.0,0.0,0.0));\n  float n100 = scenaHash13(i + vec3(1.0,0.0,0.0));\n  float n010 = scenaHash13(i + vec3(0.0,1.0,0.0));\n  float n110 = scenaHash13(i + vec3(1.0,1.0,0.0));\n  float n001 = scenaHash13(i + vec3(0.0,0.0,1.0));\n  float n101 = scenaHash13(i + vec3(1.0,0.0,1.0));\n  float n011 = scenaHash13(i + vec3(0.0,1.0,1.0));\n  float n111 = scenaHash13(i + vec3(1.0,1.0,1.0));\n  return mix(mix(mix(n000,n100,f.x), mix(n010,n110,f.x), f.y),\n             mix(mix(n001,n101,f.x), mix(n011,n111,f.x), f.y), f.z);\n}\nfloat scenaFbm(highp vec3 p){\n  float a = 0.5, s = 0.0;\n  for (int i = 0; i < 4; i++){ s += a * scenaVNoise(p); p *= 2.02; a *= 0.5; }\n  return s;\n}\n// Triplanar fbm: blend three axis-projected samples by the world normal, so\n// box faces need no UVs and adjacent boxes share one continuous field.\nfloat scenaTri(highp vec3 wp, vec3 wn, float scale){\n  highp vec3 p = wp * scale + uSurfSeed;\n  vec3 w = abs(normalize(wn)); w = pow(w, vec3(4.0)); w /= (w.x + w.y + w.z + 1e-4);\n  return scenaFbm(p.yzx) * w.x + scenaFbm(p.zxy) * w.y + scenaFbm(p.xyz) * w.z;\n}\n// Concentric grain rings around the grain axis, warped by noise.\nfloat scenaGrain(highp vec3 wp){\n  highp vec3 ax = normalize(uSurfGrainAxis);\n  highp float along = dot(wp, ax);\n  highp vec3 perp = wp - ax * along;\n  highp float rings = length(perp) * uSurfGrainScale + scenaFbm(wp * uSurfGrainScale * 0.4) * 2.0;\n  return abs(fract(rings) - 0.5) * 2.0; // triangle wave 0..1\n}\n// A masonry grid on the dominant-axis face (so box walls/floors/roofs get a\n// clean 2D pattern and abutting boxes align). Running-bond rows, mortar bands\n// and per-cell jitter. Returns: x = mortar mask (1 in the joint), y = per-cell\n// hash (0..1), z = surface height (tile face high → joint low), w = the domed\n// stone height for cobbles.\nvec4 scenaTile(highp vec3 wp, vec3 wn){\n  vec3 an = abs(normalize(wn));\n  highp vec2 uv;\n  if (an.x >= an.y && an.x >= an.z) uv = wp.zy;\n  else if (an.y >= an.x && an.y >= an.z) uv = wp.xz;\n  else uv = wp.xy;\n  uv += uSurfSeed.xy;\n  highp vec2 ts = max(uSurfTileSize, vec2(1e-3));\n  // Chevron parquet: shear alternate column bands ±45° (about each band's own\n  // centre, so the offset stays small and mediump-safe far from the origin).\n  if (uSurfTileChevron > 0.5) {\n    highp float band = floor(uv.x / ts.x);\n    highp float local = uv.x - (band + 0.5) * ts.x;\n    uv.y += (mod(band, 2.0) * 2.0 - 1.0) * local;\n  }\n  highp float row = floor(uv.y / ts.y);\n  highp float bond = mod(row, 2.0) * uSurfTileBond * 0.5;\n  highp float cxf = uv.x / ts.x + bond;\n  highp float col = floor(cxf);\n  highp vec2 cell = vec2(col, row);\n  highp float fx = fract(cxf);\n  highp float fy = fract(uv.y / ts.y);\n  // Distance to the nearest cell edge, in world units → mortar band.\n  float ex = min(fx, 1.0 - fx) * ts.x;\n  float ey = min(fy, 1.0 - fy) * ts.y;\n  float edge = min(ex, ey);\n  float m = max(uSurfMortar, 1e-4);\n  float mortar = 1.0 - smoothstep(m, m * 1.7, edge);\n  // Domed profile for cobbles: peaks at the cell centre, falls to the joint.\n  float dome = clamp(1.0 - length(vec2(fx - 0.5, fy - 0.5)) * 2.0, 0.0, 1.0);\n  float flatH = 1.0 - mortar;\n  float height = mix(flatH, dome * (1.0 - mortar), uSurfTileRound);\n  float h = scenaHash13(vec3(cell + vec2(3.1, 7.3), 5.0));\n  return vec4(mortar, h, height, dome);\n}\n// Snow / moss cap: settles on up-facing surfaces, its edge broken up by the\n// noise the shader already sampled (no extra noise cost). Returns 0..1.\nfloat scenaCapMask(vec3 wn, float breakup){\n  float up = normalize(wn).y * 0.5 + 0.5;      // 0 (down) .. 1 (up)\n  float s = max(uSurfCapSharp, 1e-3);\n  return clamp(smoothstep(uSurfCapUp - s, uSurfCapUp + s, up + (breakup - 0.5) * 0.6), 0.0, 1.0);\n}\n// Parallel ridges along an axis — a corrugated sheet. Crossed with a second\n// set, the two lattices intersect in isolated studs: tread plate.\nfloat scenaRibs(highp vec3 wp, vec3 wn){\n  if (uSurfRibs <= 0.0) return 0.0;\n  // In the FACE's own plane, like the masonry grid. A world axis is no use\n  // here: on a wall, two different world axes project onto the same\n  // direction, which turns a crossed tread plate back into plain stripes.\n  vec3 an = abs(normalize(wn));\n  highp vec2 uv;\n  if (an.x >= an.y && an.x >= an.z) uv = wp.zy;\n  else if (an.y >= an.x && an.y >= an.z) uv = wp.xz;\n  else uv = wp.xy;\n  uv = (uSurfRibTurn > 0.5 ? uv.yx : uv) * uSurfRibScale;\n  float r = abs(fract(uv.x) - 0.5) * 2.0;\n  if (uSurfRibCross > 0.5) {\n    // Two sets at ±45° IN THAT PLANE. MIN, not max: crossed ridges are high\n    // together only where they actually cross, and that is a field of studs\n    // rather than a waffle grid.\n    highp vec2 d = vec2(uv.x + uv.y, uv.x - uv.y) * 0.7071;\n    r = min(abs(fract(d.x) - 0.5), abs(fract(d.y) - 0.5)) * 2.0;\n  }\n  return r * r * (3.0 - 2.0 * r);   // round the triangle off\n}\n// Aggregate. Everything else in this shader runs on smooth fbm, which reads\n// as mottling; asphalt is STONES IN TAR, and stones have edges.\nfloat scenaSpeck(highp vec3 wp){\n  if (uSurfSpeck <= 0.0) return 0.5;\n  return scenaHash13(floor(wp * uSurfSpeckScale + uSurfSeed));\n}\n// Warped Voronoi. Returns x = per-cell hash, y = distance to the nearest\n// seam (0 on it). Basalt's columnar jointing and the zinc spangle on\n// galvanised steel are this one function two orders of magnitude apart.\nvec2 scenaCells(highp vec3 wp, vec3 wn){\n  if (uSurfCells <= 0.0) return vec2(0.5, 1.0);\n  highp vec2 uv;\n  if (uSurfCellPlan > 0.5) {\n    // Columnar jointing is a crazy paving seen from ABOVE, pulled up into\n    // columns — so it is laid out in plan whatever the face is pointing at.\n    uv = wp.xz;\n  } else {\n    vec3 an = abs(normalize(wn));\n    if (an.x >= an.y && an.x >= an.z) uv = wp.zy;\n    else if (an.y >= an.x && an.y >= an.z) uv = wp.xz;\n    else uv = wp.xy;\n  }\n  uv = uv * uSurfCellScale + uSurfSeed.xy;\n  highp vec2 g = floor(uv);\n  highp vec2 f = uv - g;\n  float d1 = 8.0, d2 = 8.0, id = 0.5;\n  for (int j = -1; j <= 1; j++) {\n    for (int i = -1; i <= 1; i++) {\n      vec2 o = vec2(float(i), float(j));\n      float h = scenaHash13(vec3(g + o, 11.0));\n      vec2 seed = o + vec2(h, fract(h * 37.31));\n      float d = length(seed - f);\n      if (d < d1) { d2 = d1; d1 = d; id = h; }\n      else if (d < d2) { d2 = d; }\n    }\n  }\n  // d2 - d1 is 0 exactly on the boundary between two cells and grows inward,\n  // which is a seam that does not care how big the cells are.\n  return vec2(id, clamp(d2 - d1, 0.0, 1.0));\n}\n// A mineral crust — verdigris, a rust bloom, lichen — taking hold in the\n// cavities and on the up-facing side.\nfloat scenaCrustMask(vec3 wn, float low){\n  if (uSurfCrust <= 0.0) return 0.0;\n  float up = normalize(wn).y * 0.5 + 0.5;\n  return clamp(smoothstep(0.66, 0.28, low) * uSurfCrust * mix(0.5, 1.0, up), 0.0, 1.0);\n}\n// WATER FILLS FROM THE BOTTOM. Wetness is a LEVEL, not a multiply: every\n// point has a height (the surface's own low-frequency band, with the\n// mortar joints counted as the lowest ground there is), and it is wet when\n// the level is above it. That is what makes a light shower read as dark\n// glossy lines in the joints and hollows while the faces stay dry, and a\n// downpour sheet the whole wall — from one scalar.\n//\n// The level is lower on a vertical face than a horizontal one, because\n// rain falls down: a sill soaks while the wall beneath it is merely damp.\nfloat scenaWetMask(vec3 wn, float low, float mortar){\n  if (uSurfWet <= 0.0) return 0.0;\n  float up = normalize(wn).y * 0.5 + 0.5;\n  float level = uSurfWet * mix(clamp(uSurfWetCling, 0.0, 1.0), 1.0, up) * 1.4 - 0.2;\n  float height = min(low, 1.0 - mortar);\n  return clamp(smoothstep(height - 0.2, height + 0.2, level), 0.0, 1.0);\n}\n`;\n\nfunction vertexPatch(src: string): string {\n  return src\n    .replace(\n      '#include <common>',\n      '#include <common>\\nvarying highp vec3 vSurfWorldPos;\\nvarying highp vec3 vSurfWorldNormal;'\n    )\n    .replace(\n      '#include <begin_vertex>',\n      `#include <begin_vertex>\n      {\n        vec4 scenaWP = modelMatrix * vec4(transformed, 1.0);\n        #ifdef USE_INSTANCING\n          scenaWP = modelMatrix * instanceMatrix * vec4(transformed, 1.0);\n        #endif\n        vSurfWorldPos = scenaWP.xyz;\n      }`\n    )\n    .replace(\n      '#include <beginnormal_vertex>',\n      `#include <beginnormal_vertex>\n      {\n        vec3 scenaON = objectNormal;\n        #ifdef USE_INSTANCING\n          scenaON = mat3(instanceMatrix) * scenaON;\n        #endif\n        vSurfWorldNormal = normalize(mat3(modelMatrix) * scenaON);\n      }`\n    );\n}\n\nfunction fragmentPatch(src: string): string {\n  return src\n    .replace('#include <common>', '#include <common>\\n' + NOISE_GLSL)\n    .replace(\n      '#include <map_fragment>',\n      `#include <map_fragment>\n      float scenaN   = scenaTri(vSurfWorldPos, vSurfWorldNormal, uSurfScale);\n      float scenaLow = scenaTri(vSurfWorldPos, vSurfWorldNormal, uSurfScale * 0.25);\n      float scenaG   = scenaGrain(vSurfWorldPos);\n      float scenaRib = scenaRibs(vSurfWorldPos, vSurfWorldNormal);\n      float scenaSp  = scenaSpeck(vSurfWorldPos);\n      vec2  scenaC   = scenaCells(vSurfWorldPos, vSurfWorldNormal);\n      // Seam mask: 1 on the join between two cells, 0 inside one.\n      float scenaSeam = (1.0 - smoothstep(0.0, 0.085, scenaC.y)) * uSurfCells;\n      // masonry grid (no-op when uSurfTile == 0)\n      vec4  scenaT   = scenaTile(vSurfWorldPos, vSurfWorldNormal);\n      float scenaMortar = scenaT.x * uSurfTile;\n      // fine mottle\n      diffuseColor.rgb *= 1.0 + (scenaN - 0.5) * uSurfAlbedoVar;\n      // per-cell brightness jitter, so no two bricks/stones read the same\n      diffuseColor.rgb *= 1.0 + uSurfTileJitter * (scenaT.y - 0.5) * uSurfTile;\n      // cavity ambient occlusion (dark where the low band is low)\n      diffuseColor.rgb *= 1.0 - uSurfAO * (1.0 - scenaLow);\n      // cavity tint\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfTint, uSurfTintAmount * (1.0 - scenaLow));\n      // grain darkening (no-op when uSurfGrain == 0)\n      diffuseColor.rgb *= 1.0 - uSurfGrain * scenaG * 0.5;\n      // aggregate chips — hard-edged, unlike everything else here\n      diffuseColor.rgb *= 1.0 + (scenaSp - 0.5) * uSurfSpeck;\n      // cells: each one its own shade, and the seams between them dark\n      diffuseColor.rgb *= 1.0 + (scenaC.x - 0.5) * uSurfCellJitter * uSurfCells;\n      diffuseColor.rgb *= 1.0 - scenaSeam * uSurfCellEdge;\n      // the ridge valleys hold shadow the lighting alone will not give them\n      diffuseColor.rgb *= 1.0 - uSurfRibs * (1.0 - scenaRib) * 0.22;\n      // accent cells (mosaic chips) painted solid tint — a no-op at 0\n      float scenaAccent = uSurfTile * uSurfTileTint;\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfTint, step(scenaT.y, scenaAccent) * uSurfTile);\n      // per-cell ring + dot motif (patterned cement tiles) — a no-op at 0\n      float scenaMotifM = smoothstep(0.40, 0.47, scenaT.w) - smoothstep(0.60, 0.68, scenaT.w)\n        + smoothstep(0.86, 0.93, scenaT.w);\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfTint,\n        clamp(scenaMotifM, 0.0, 1.0) * uSurfTileMotif * uSurfTile);\n      // recessed mortar joint: to the mortar colour, shadowed in the groove\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfMortarColor, scenaMortar);\n      diffuseColor.rgb *= 1.0 - 0.35 * scenaMortar;\n      // snow / moss cap settling on the up-facing faces (over the mortar too)\n      float scenaCapM = scenaCapMask(vSurfWorldNormal, scenaN) * uSurfCap;\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfCapColor, scenaCapM);\n      // WET. Water darkens a surface because it fills the pores: light gets\n      // in, scatters, and comes back out with less of it. So POROUS things\n      // darken hard and sealed ones barely change — a wet flagstone is\n      // almost black, wet chrome is just chrome — and metal, which has no\n      // subsurface to wet, does not darken at all.\n      // A CRUST, not a tint: verdigris, rust bloom, lichen. It goes over\n      // everything above it, because it grew on top of all of it.\n      float scenaCrustM = scenaCrustMask(vSurfWorldNormal, scenaLow);\n      diffuseColor.rgb = mix(diffuseColor.rgb, uSurfCrustColor, scenaCrustM);\n      float scenaWetM = scenaWetMask(vSurfWorldNormal, scenaLow, scenaMortar);\n      float scenaPorous = (1.0 - clamp(metalness, 0.0, 1.0)) * clamp(roughness, 0.0, 1.0);\n      diffuseColor.rgb *= mix(1.0, mix(0.93, 0.45, scenaPorous), scenaWetM);`\n    )\n    .replace(\n      '#include <roughnessmap_fragment>',\n      `#include <roughnessmap_fragment>\n      roughnessFactor = clamp(roughnessFactor + (scenaN - 0.5) * uSurfRoughVar + uSurfGrain * scenaG * 0.12\n        + scenaMortar * 0.25 + (scenaT.y - 0.5) * uSurfTileJitter * 0.3 * uSurfTile, 0.04, 1.0);\n      roughnessFactor = clamp(roughnessFactor + (scenaSp - 0.5) * uSurfSpeck * 0.35\n        + scenaSeam * 0.2, 0.04, 1.0);\n      roughnessFactor = mix(roughnessFactor, uSurfCrustRough, scenaCrustM);\n      roughnessFactor = mix(roughnessFactor, uSurfCapRough, scenaCapM);\n      // A film of water is a mirror, whatever is underneath it.\n      roughnessFactor = mix(roughnessFactor, 0.05, scenaWetM * 0.92);`\n    )\n    .replace(\n      '#include <metalnessmap_fragment>',\n      `#include <metalnessmap_fragment>\n      // A mineral scab does not reflect like the metal it grew on, so where\n      // the crust has taken hold the metal is simply not there any more.\n      // This is the difference between patina and green paint.\n      metalnessFactor = mix(metalnessFactor, 0.0, scenaCrustM);`\n    )\n    .replace(\n      '#include <normal_fragment_maps>',\n      `#include <normal_fragment_maps>\n      {\n        // three's perturbNormalArb, in view space, driven by the noise height\n        // plus the tile relief (a step down into each mortar joint).\n        // Standing water fills the micro-relief, so the bump flattens out\n        // under it — the puddle is smooth even where the stone is not.\n        float scenaH = (scenaN + uSurfGrain * scenaG * 0.5 + scenaT.z * uSurfTile * uSurfTileRelief\n          + scenaRib * uSurfRibs * 0.9 + scenaC.y * uSurfCells * uSurfCellEdge * 0.5)\n          * (1.0 - scenaWetM * 0.7);\n        vec3 sX = dFdx(-vViewPosition);\n        vec3 sY = dFdy(-vViewPosition);\n        vec3 sN = normal;\n        vec3 R1 = cross(sY, sN);\n        vec3 R2 = cross(sN, sX);\n        float det = dot(sX, R1);\n        vec3 grad = sign(det) * (dFdx(scenaH) * R1 + dFdy(scenaH) * R2);\n        normal = normalize(abs(det) * sN - uSurfBump * grad);\n      }`\n    )\n    .replace(\n      '#include <emissivemap_fragment>',\n      `#include <emissivemap_fragment>\n      {\n        // Procedural glow (lava cracks, crystal): drawn straight into the\n        // emissive radiance, NOT via material.emissive — so it burns constant\n        // and the day/night cycle (which scales emissiveIntensity) can't dim\n        // it. A no-op when uSurfGlow == 0. Glow fills the low-noise areas.\n        float scenaGlow = smoothstep(uSurfGlowThresh + 0.16, uSurfGlowThresh - 0.16, scenaLow) * uSurfGlow;\n        totalEmissiveRadiance += uSurfGlowColor * scenaGlow;\n      }`\n    );\n}\n\n/**\n * Build a procedural surface material. Pass a preset name for the defaults,\n * plus any overrides (colour, roughness, bump, seed, …).\n */\nexport function createSurface(kind: SurfaceKind, options: SurfaceOptions = {}): MeshStandardMaterial {\n  const preset = SURFACE_PRESETS[kind];\n  const p: SurfaceParams = { ...preset, ...options };\n  const seed = options.seed ?? 0;\n\n  const base = {\n    color: options.color ?? preset.baseColor ?? 0x9a9a9a,\n    roughness: p.roughness,\n    metalness: p.metalness,\n    flatShading: p.flat,\n  };\n  // A MeshPhysicalMaterial only when the preset genuinely needs one. It is a\n  // bigger shader, and `transmission` makes three render the scene a second\n  // time — so the other 52 kinds keep the cheap material they have always had.\n  const material: MeshStandardMaterial = p.physical\n    ? new MeshPhysicalMaterial({\n        ...base,\n        ...(p.sheen === undefined ? {} : {\n          sheen: p.sheen,\n          sheenColor: new Color(p.sheenColor ?? 0xffffff),\n          sheenRoughness: p.sheenRough ?? 0.3,\n        }),\n        ...(p.anisotropy === undefined ? {} : {\n          anisotropy: p.anisotropy,\n          anisotropyRotation: p.anisotropyRotation ?? 0,\n        }),\n        ...(p.iridescence === undefined ? {} : {\n          iridescence: p.iridescence,\n          iridescenceIOR: p.iridescenceIOR ?? 1.3,\n          iridescenceThicknessRange: p.iridescenceThickness ?? [100, 400],\n        }),\n        ...(p.transmission === undefined ? {} : {\n          transmission: p.transmission,\n          thickness: p.thickness ?? 0.5,\n          ior: p.ior ?? 1.5,\n          // Only when asked for: three compiles USE_DISPERSION the moment\n          // this crosses zero, and that is three transmission samples a\n          // pixel instead of one.\n          ...(p.dispersion ? { dispersion: p.dispersion } : {}),\n          ...(p.attenuationColor === undefined ? {} : {\n            attenuationColor: new Color(p.attenuationColor),\n            attenuationDistance: p.attenuationDistance ?? 1,\n          }),\n        }),\n      })\n    : new MeshStandardMaterial(base);\n\n  const uniforms = {\n    uSurfScale: { value: p.scale },\n    uSurfAlbedoVar: { value: p.albedoVar },\n    uSurfTint: { value: new Color(p.tint) },\n    uSurfTintAmount: { value: p.tintAmount },\n    uSurfAO: { value: p.ao },\n    uSurfBump: { value: p.bump },\n    uSurfRoughVar: { value: p.roughVar },\n    uSurfGrain: { value: p.grain },\n    uSurfGrainScale: { value: p.grainScale },\n    uSurfGrainAxis: { value: p.grainAxis.clone().normalize() },\n    // A large, seed-driven world-space offset so equal colours weather apart.\n    uSurfSeed: {\n      value: new Vector3(\n        Math.sin(seed * 12.9898) * 43.75,\n        Math.cos(seed * 78.233) * 51.13,\n        Math.sin(seed * 37.719) * 29.41\n      ),\n    },\n    // Masonry tiling (uSurfTile 0 disables the whole grid at no visual cost).\n    uSurfTile: { value: p.tile ?? 0 },\n    uSurfTileSize: { value: new Vector2(p.tileW ?? 0.25, p.tileH ?? 0.1) },\n    uSurfMortar: { value: p.mortar ?? 0.014 },\n    uSurfTileBond: { value: p.bond ?? 1 },\n    uSurfTileRound: { value: p.round ?? 0 },\n    uSurfTileJitter: { value: p.tileJitter ?? 0.12 },\n    uSurfMortarColor: { value: new Color(p.mortarColor ?? 0x3a3a3a) },\n    uSurfTileRelief: { value: p.tileRelief ?? 0.06 },\n    uSurfTileTint: { value: p.tileTint ?? 0 },\n    uSurfTileChevron: { value: p.chevron ?? 0 },\n    uSurfTileMotif: { value: p.motif ?? 0 },\n    // Snow/moss cap (uSurfCap 0 disables it).\n    uSurfCap: { value: p.cap ?? 0 },\n    uSurfCapColor: { value: new Color(p.capColor ?? 0xf2f6fa) },\n    uSurfCapUp: { value: p.capUp ?? 0.5 },\n    uSurfCapSharp: { value: p.capSharp ?? 0.28 },\n    uSurfCapRough: { value: p.capRough ?? 0.88 },\n    // Emissive glow (uSurfGlow 0 keeps it dark, day-cycle-safe).\n    uSurfGlow: { value: p.glow ?? 0 },\n    uSurfGlowColor: { value: new Color(p.glowColor ?? 0xff6a2a) },\n    uSurfGlowThresh: { value: p.glowThreshold ?? 0.45 },\n    // Wear: water (uSurfWet 0 is bone dry and costs one compare).\n    uSurfWet: { value: p.wet ?? 0 },\n    uSurfWetCling: { value: p.wetCling ?? 0.55 },\n    // Ribs, aggregate, cells and crust (each 0 = off, and off costs a compare).\n    uSurfRibs: { value: p.ribs ?? 0 },\n    uSurfRibScale: { value: p.ribScale ?? 8 },\n    uSurfRibTurn: { value: p.ribTurn ?? 0 },\n    uSurfRibCross: { value: p.ribCross ?? 0 },\n    uSurfSpeck: { value: p.speck ?? 0 },\n    uSurfSpeckScale: { value: p.speckScale ?? 40 },\n    uSurfCells: { value: p.cells ?? 0 },\n    uSurfCellScale: { value: p.cellScale ?? 4 },\n    uSurfCellEdge: { value: p.cellEdge ?? 0.4 },\n    uSurfCellJitter: { value: p.cellJitter ?? 0.18 },\n    uSurfCellPlan: { value: p.cellPlan ?? 0 },\n    uSurfCrust: { value: p.crust ?? 0 },\n    uSurfCrustColor: { value: new Color(p.crustColor ?? 0x4fa88a) },\n    uSurfCrustRough: { value: p.crustRough ?? 0.9 },\n  };\n\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.vertexShader = vertexPatch(shader.vertexShader);\n    shader.fragmentShader = fragmentPatch(shader.fragmentShader);\n    // Keep the live shader so weather can drive uniforms after compilation —\n    // the object three actually uploads from, not just our pre-compile copy.\n    (material.userData as { scenaShader?: typeof shader }).scenaShader = shader;\n  };\n  // All surface materials inject identical source (uniforms carry the\n  // differences), so one cache key groups them — and, crucially, keeps them\n  // from colliding with a plain MeshStandardMaterial that has matching base\n  // params but no injection. three still appends its own feature key, so\n  // flat/smooth/instanced variants stay separate programs.\n  const cacheKey = p.physical ? 'scena-surface-v5-physical' : 'scena-surface-v5';\n  material.customProgramCacheKey = () => cacheKey;\n\n  // Expose the live uniforms so weather can drive them after the fact — e.g.\n  // snow settling ramps uSurfCap, rain darkens/glosses via the same handles.\n  (material.userData as { scenaSurface?: typeof uniforms }).scenaSurface = uniforms;\n\n  return material;\n}\n"],"mappings":"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","names":["import_three","import_three","import_three","import_three","import_three","import_three","import_three","strike","import_three","import_three","import_three","import_three","nowSeconds","import_three","nowSeconds","import_three","smooth","nowSeconds","p","import_three","BUILT_IN","lerp","smooth","nowSeconds","toLive","import_three","nowSeconds","import_three","nowSeconds","UP","import_three","nowSeconds","import_three","import_three","matrix","import_three","import_three","matrix","UP","import_three","SHAFT_VERT","SHAFT_FRAG","G","import_three","import_three","UP","matrix"]}