{"version":3,"sources":["../src/materials.ts","../src/materials/surface.ts","../src/materials/glass.ts","../src/materials/screen.ts","../src/materials/picture.ts","../src/materials/waterFlow.ts","../src/core/random.ts"],"sourcesContent":["// SCENA — materials: procedural surfaces and glass\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 {\n  createSurface,\n  SURFACE_PRESETS,\n  type SurfaceKind,\n  type SurfaceParams,\n  type SurfaceOptions,\n} from './materials/surface';\nexport { createGlass, type GlassOptions } from './materials/glass';\nexport {\n  createScreenPanel,\n  type ScreenPanel,\n  type ScreenPanelOptions,\n  type ScreenMode,\n  type ScreenOptions,\n} from './materials/screen';\nexport {\n  createPicture,\n  pickPictureStyle,\n  PICTURE_STYLES,\n  ALL_PICTURE_STYLES,\n  type Picture,\n  type PictureOptions,\n  type PictureStyle,\n} from './materials/picture';\nexport {\n  flowingWaterMaterial,\n  createDroplets,\n  type FlowOptions,\n  type DropletOptions,\n  type Droplets,\n} from './materials/waterFlow';\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","import { Color, MeshStandardMaterial } from 'three';\n\n/**\n * Stylized architectural glass — the facade material for modern windows,\n * balustrades, doors and curtain walls.\n *\n * Real transmission is far too heavy for whole facades, so this is a\n * transparent `MeshStandardMaterial` with two shader tricks patched in:\n *\n * - **Fresnel opacity**: face-on the pane stays see-through; edge-on it\n *   turns opaque and reflective, exactly how glass reads in life.\n * - **A procedural sky streak**: the reflected view direction samples a tiny\n *   built-in sky gradient, so panes carry a believable reflection with no\n *   environment map, no cube camera, no setup.\n *\n * `nightGlow` follows the house-window convention: the material's emissive\n * is set at the intensity `createDayCycle` scans for, so listing the mesh\n * (or its building) in the cycle's `lamps` makes the glass ignite at dusk —\n * the classic lit-window skyline, free.\n */\nexport interface GlassOptions {\n  /** Glass tint. Default a cool clear blue-grey. */\n  tint?: number | Color;\n  /** Face-on opacity (0–1). Default 0.24 (clear); frosted overrides higher. */\n  opacity?: number;\n  /** Milky translucent finish: higher opacity, rough, muted reflection. */\n  frosted?: boolean;\n  /** Sky-reflection strength (0–1). Default 0.55. */\n  reflect?: number;\n  /**\n   * Warm interior glow for `createDayCycle` to ignite at dusk (adds an\n   * emissive at lamp-scan intensity). Default false.\n   */\n  nightGlow?: boolean;\n  /** Sky gradient the reflection samples: zenith / horizon colours. */\n  sky?: number;\n  horizon?: number;\n}\n\nconst GLASS_FRAG_ALPHA = /* glsl */ `\n  // Fresnel: see-through face-on, mirror-like edge-on.\n  vec3 scenaV = normalize(vViewPosition);\n  float scenaFresnel = pow(1.0 - clamp(dot(normal, scenaV), 0.0, 1.0), 3.0);\n  diffuseColor.a = clamp(uGlassOpacity + (1.0 - uGlassOpacity) * scenaFresnel, 0.0, 1.0);\n`;\n\nconst GLASS_FRAG_SKY = /* glsl */ `\n  {\n    // Reflect the view ray and sample a tiny procedural sky by its height —\n    // an environment map's worth of \"glassiness\" for two mixes. View-space\n    // reflection is fine at this stylization level.\n    vec3 scenaR = reflect(-scenaV, normal);\n    float scenaUp = clamp(scenaR.y * 0.5 + 0.5, 0.0, 1.0);\n    vec3 scenaSky = mix(uGlassHorizon, uGlassSky, scenaUp);\n    totalEmissiveRadiance += scenaSky * uGlassReflect * (0.25 + 0.75 * scenaFresnel);\n  }\n`;\n\nexport function createGlass(options: GlassOptions = {}): MeshStandardMaterial {\n  const frosted = options.frosted ?? false;\n  const tint = new Color(options.tint ?? 0x9fc4d8);\n  const opacity = options.opacity ?? (frosted ? 0.62 : 0.24);\n  const reflect = (options.reflect ?? 0.55) * (frosted ? 0.35 : 1);\n\n  const material = new MeshStandardMaterial({\n    color: tint,\n    roughness: frosted ? 0.55 : 0.08,\n    metalness: 0.1,\n    transparent: true,\n    opacity,\n    depthWrite: false,\n  });\n  if (options.nightGlow) {\n    // The lamp-scan convention: emissiveIntensity > 0.5 means \"adopt me\" —\n    // createDayCycle dims it by day and ignites it at night.\n    material.emissive.set(0xffc978);\n    material.emissiveIntensity = 1.1;\n  }\n\n  const uniforms = {\n    uGlassOpacity: { value: opacity },\n    uGlassReflect: { value: reflect },\n    uGlassSky: { value: new Color(options.sky ?? 0x87a8c8) },\n    uGlassHorizon: { value: new Color(options.horizon ?? 0xd8e2e8) },\n  };\n\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.fragmentShader = shader.fragmentShader\n      .replace(\n        '#include <common>',\n        `#include <common>\n        uniform float uGlassOpacity;\n        uniform float uGlassReflect;\n        uniform vec3 uGlassSky;\n        uniform vec3 uGlassHorizon;`\n      )\n      // After the normal is final, before lighting: fresnel drives alpha…\n      .replace('#include <normal_fragment_maps>', '#include <normal_fragment_maps>\\n' + GLASS_FRAG_ALPHA)\n      // …and the sky streak lands with the emissive term.\n      .replace('#include <emissivemap_fragment>', '#include <emissivemap_fragment>\\n' + GLASS_FRAG_SKY);\n  };\n  material.customProgramCacheKey = () => 'scena-glass-v1';\n  (material.userData as { scenaGlass?: typeof uniforms }).scenaGlass = uniforms;\n\n  return material;\n}\n","import { Color, MeshStandardMaterial, Object3D } from 'three';\n\n/**\n * Screens — the lit rectangles that make a scene read as modern.\n *\n * A screen is not a texture here. It is drawn procedurally in the fragment\n * shader from the panel's own UVs, the same way `createSurface` draws stone\n * and brick: nothing is fetched, every panel is unique, and a phone and a\n * television share one material path.\n *\n * The content is deliberately **not text**. At the size a screen occupies on\n * a monitor — a phone is a few pixels across at conversational distance —\n * you need the *impression* of an interface, not readable content: rows,\n * tiles, a scrubbing bar, a route line. Real glyphs would cost thousands of\n * triangles for something illegible, and look worse on the rare occasion the\n * camera got close enough to read them.\n *\n * ```ts\n * const panel = createScreenPanel(0.6, 0.34, { mode: 'feed' });\n * mesh.material = panel.material;\n * game.onUpdate((t) => panel.update(t.delta));\n * ```\n *\n * The content is written into **emissive radiance** rather than base colour,\n * so a screen lights up in a dark room and the day/night cycle cannot dim it\n * — a monitor at midnight is exactly as bright as one at noon, which is the\n * whole reason anyone notices screens at night.\n */\nexport type ScreenMode =\n  | 'off'\n  | 'standby'\n  | 'home'\n  | 'feed'\n  | 'video'\n  | 'map'\n  | 'chart'\n  | 'call'\n  | 'keypad';\n\n/** Mode → shader branch. Kept explicit so the GLSL never guesses. */\nconst MODE_ID: Record<ScreenMode, number> = {\n  off: 0,\n  standby: 1,\n  home: 2,\n  feed: 3,\n  video: 4,\n  map: 5,\n  chart: 6,\n  call: 7,\n  keypad: 8,\n};\n\nexport interface ScreenOptions {\n  /** What the screen is showing. Default 'home'. */\n  mode?: ScreenMode;\n  /** Varies icon colours, chart bars, the cut rhythm of video. Default 1. */\n  seed?: number;\n  /** UI accent colour (the highlighted bar, the answer pill). */\n  accent?: number;\n  /** Overall emissive gain. Default 1. Dim a phone, brighten a TV wall. */\n  brightness?: number;\n  /** Rows scrolled per second in 'feed'. Default 0 (a still screen). */\n  scrollRate?: number;\n}\n\n/**\n * A lit panel. Structurally what ANIMA's `Viewable` wants (`surface`,\n * `width`, `height`) so a character can look at one, and what GAMA's\n * `DisplayTarget` wants (`setMode`) so a device can drive one — with no\n * library importing another.\n */\nexport interface ScreenPanel {\n  /** The lit face. Set by the prop that owns it; the gaze target. */\n  surface: Object3D;\n  /** Panel size in metres. */\n  width: number;\n  height: number;\n  material: MeshStandardMaterial;\n  readonly mode: ScreenMode;\n  setMode(mode: ScreenMode): void;\n  /**\n   * What this screen is throwing into the room right now — colour and\n   * strength, updated every frame. A light that copies these flickers in\n   * time with the content, which is the difference between a television and\n   * a blue lamp.\n   */\n  readonly glow: { color: Color; intensity: number };\n  update(dt: number): void;\n}\n\n// --- the shader ---------------------------------------------------------\n//\n// GLSL ES 1.00 (three patches the standard chunks, which are GLSL1): no\n// `switch`, constant loop bounds only. Layout is done in UV space 0..1 with\n// x converted to y-units wherever roundness or squareness matters, so a\n// 16:9 panel's icons are square and its corners are round.\n\nconst SCREEN_HELPERS = /* glsl */ `\n  float scrHash(vec2 p) {\n    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n  }\n  vec3 scrHue(float h) {\n    return 0.5 + 0.5 * cos(6.28318 * (h + vec3(0.0, 0.33, 0.67)));\n  }\n  float scrBand(float v, float lo, float hi) {\n    return step(lo, v) * step(v, hi);\n  }\n  // Rounded rect coverage. c/h in UV space, r in y-units.\n  float scrRound(vec2 uv, vec2 c, vec2 h, float r, float aspect) {\n    vec2 q = uv - c;\n    q.x *= aspect;\n    vec2 hh = vec2(h.x * aspect, h.y);\n    vec2 d = abs(q) - hh + r;\n    float dist = length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - r;\n    return 1.0 - smoothstep(0.0, 0.006, dist);\n  }\n  float scrSeg(vec2 p, vec2 a, vec2 b, float aspect) {\n    vec2 pa = p - a; vec2 ba = b - a;\n    pa.x *= aspect; ba.x *= aspect;\n    float h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-5), 0.0, 1.0);\n    return length(pa - ba * h);\n  }\n`;\n\nconst SCREEN_MODES = /* glsl */ `\n  // A phone/desktop home screen: status strip, a grid of app tiles, a dock.\n  vec3 scrHome(vec2 uv, float t, float seed, float aspect, vec3 accent) {\n    vec3 col = mix(vec3(0.03, 0.05, 0.11), vec3(0.07, 0.06, 0.15), uv.y);\n    // Status strip: a clock block left, two indicator pips right.\n    float bar = scrBand(uv.y, 0.945, 0.972);\n    col += vec3(0.55) * bar * (scrBand(uv.x, 0.06, 0.16) + scrBand(uv.x, 0.82, 0.94));\n    // 4 x 5 tile grid.\n    vec2 g = vec2(uv.x * 4.0, (uv.y - 0.17) / 0.155);\n    vec2 cell = floor(g);\n    if (uv.y > 0.17 && uv.y < 0.92) {\n      float id = scrHash(cell + seed);\n      float tile = scrRound(fract(g) - 0.5, vec2(0.0), vec2(0.30, 0.30), 0.09, aspect / 4.0 * 0.155 * 4.0);\n      col += scrHue(id) * tile * 0.85;\n      // A notification pip on one tile in six.\n      col += vec3(1.0, 0.25, 0.2) * step(0.84, id) * scrRound(fract(g) - vec2(0.78, 0.78), vec2(0.0), vec2(0.07, 0.07), 0.07, 1.0);\n    }\n    // Dock: a translucent slab holding four tiles.\n    float dock = scrRound(uv, vec2(0.5, 0.085), vec2(0.44, 0.062), 0.03, aspect);\n    col += vec3(0.10, 0.12, 0.20) * dock;\n    vec2 dg = vec2(uv.x * 4.0, 0.0);\n    if (uv.y > 0.035 && uv.y < 0.135) {\n      float did = scrHash(vec2(floor(dg.x), 9.0) + seed);\n      col += scrHue(did) * scrRound(vec2(fract(dg.x), uv.y) - vec2(0.5, 0.085), vec2(0.0), vec2(0.16, 0.036), 0.02, aspect / 4.0) * 0.9;\n    }\n    return col + accent * 0.04;\n  }\n\n  // The scrolling list everyone actually looks at: thumbnail, two text bars.\n  vec3 scrFeed(vec2 uv, float scroll, float seed, float aspect, vec3 accent) {\n    vec3 col = vec3(0.02, 0.025, 0.045);\n    float rowH = 0.235;\n    float fy = (uv.y + scroll) / rowH;\n    float row = floor(fy);\n    float ry = fract(fy);\n    float id = scrHash(vec2(row, seed));\n    if (uv.y < 0.93) {\n      // Card body.\n      col += vec3(0.055, 0.065, 0.10) * scrRound(vec2(uv.x, ry), vec2(0.5, 0.5), vec2(0.455, 0.40), 0.04, aspect * rowH);\n      // Thumbnail block, hue per row.\n      col += scrHue(id) * 0.75 * scrRound(vec2(uv.x, ry), vec2(0.16, 0.5), vec2(0.09, 0.30), 0.03, aspect * rowH);\n      // Two text bars: a long title and a short byline.\n      float w = 0.24 + id * 0.26;\n      col += vec3(0.62) * scrBand(ry, 0.55, 0.66) * scrBand(uv.x, 0.30, 0.30 + w);\n      col += vec3(0.30) * scrBand(ry, 0.36, 0.45) * scrBand(uv.x, 0.30, 0.30 + w * 0.55);\n      // Every fourth card carries the accent (a \"sponsored\" tint).\n      col += accent * 0.5 * step(0.88, id) * scrRound(vec2(uv.x, ry), vec2(0.86, 0.5), vec2(0.05, 0.16), 0.02, aspect * rowH);\n    }\n    // Fixed header above the scroll.\n    col += vec3(0.09, 0.10, 0.16) * scrBand(uv.y, 0.93, 1.0);\n    col += accent * 0.8 * scrBand(uv.y, 0.955, 0.978) * scrBand(uv.x, 0.06, 0.30);\n    return col;\n  }\n\n  // Video: letterboxed, soft moving fields, colour set by the CPU so the cuts\n  // and the light in the room are the same event.\n  vec3 scrVideo(vec2 uv, float t, float aspect, vec3 a, vec3 b) {\n    float inside = scrBand(uv.y, 0.13, 0.87);\n    vec2 p = uv - vec2(0.5);\n    p.x *= aspect;\n    // Two drifting blobs over a gradient — enough parallax to read as motion.\n    float d1 = length(p - vec2(sin(t * 0.7) * 0.28, cos(t * 0.53) * 0.14));\n    float d2 = length(p - vec2(cos(t * 0.41) * 0.34, sin(t * 0.9) * 0.10));\n    vec3 col = mix(a, b, clamp(uv.y * 1.2 - 0.1, 0.0, 1.0));\n    // Broad, overlapping washes rather than two tight spots — a tight\n    // falloff puts a single hot dot in the middle of the picture, which\n    // reads as a torch pointed at the wall, not as footage.\n    col += a * 0.30 * smoothstep(0.80, 0.06, d1);\n    col += b * 0.26 * smoothstep(0.68, 0.04, d2);\n    // A horizon: cooler above, warmer below. Almost any real frame has one,\n    // and without it the picture is a lava lamp.\n    col *= mix(vec3(1.06, 1.0, 0.92), vec3(0.72, 0.82, 1.0), smoothstep(0.4, 0.62, uv.y));\n    // Scanline-ish texture, very faint: sells \"screen\" over \"painted panel\".\n    col *= 0.93 + 0.07 * sin(uv.y * 420.0);\n    // The shot's own level, so a cut to a dark scene darkens the PICTURE and\n    // not merely the lamp in the room. Scaled to sit below clipping: emissive\n    // at 1.0 is pure white after tone mapping, which is a lightbox, not a\n    // television.\n    return col * inside * uScrLevel * 0.58;\n  }\n\n  // Navigation: a road grid, an arterial, a route, and you.\n  vec3 scrMap(vec2 uv, float t, float seed, float aspect, vec3 accent) {\n    vec3 col = vec3(0.055, 0.075, 0.075);\n    vec2 p = vec2(uv.x * aspect, uv.y);\n    // Minor grid.\n    vec2 gg = abs(fract(p * 7.0 + seed * 0.1) - 0.5);\n    float roads = (1.0 - smoothstep(0.0, 0.06, gg.x)) + (1.0 - smoothstep(0.0, 0.06, gg.y));\n    col += vec3(0.12, 0.13, 0.14) * clamp(roads, 0.0, 1.0);\n    // Two arterials.\n    col += vec3(0.22, 0.21, 0.18) * (1.0 - smoothstep(0.0, 0.02, abs(uv.y - 0.62)));\n    col += vec3(0.22, 0.21, 0.18) * (1.0 - smoothstep(0.0, 0.02, abs(uv.x - 0.38)));\n    // The route: three segments in the accent, thick and bright.\n    float r = min(\n      min(scrSeg(uv, vec2(0.20, 0.16), vec2(0.38, 0.42), aspect),\n          scrSeg(uv, vec2(0.38, 0.42), vec2(0.38, 0.62), aspect)),\n      scrSeg(uv, vec2(0.38, 0.62), vec2(0.78, 0.80), aspect));\n    col += accent * (1.0 - smoothstep(0.008, 0.022, r)) * 1.2;\n    // Position dot with a breathing accuracy halo.\n    float dot0 = length((uv - vec2(0.20, 0.16)) * vec2(aspect, 1.0));\n    col += vec3(0.35, 0.7, 1.0) * (1.0 - smoothstep(0.012, 0.02, dot0));\n    col += vec3(0.2, 0.45, 0.8) * (1.0 - smoothstep(0.03, 0.075, dot0)) * (0.25 + 0.15 * sin(t * 2.2));\n    return col;\n  }\n\n  // A dashboard: gridlines and a bar series, one column called out.\n  vec3 scrChart(vec2 uv, float t, float seed, float aspect, vec3 accent) {\n    vec3 col = vec3(0.035, 0.04, 0.06);\n    col += vec3(0.08) * scrBand(uv.y, 0.92, 1.0);\n    // Horizontal gridlines.\n    float grid = 1.0 - smoothstep(0.0, 0.004, abs(fract(uv.y * 5.0) - 0.5) * 0.2);\n    col += vec3(0.07, 0.08, 0.11) * grid * scrBand(uv.y, 0.12, 0.88);\n    // Bars.\n    float col0 = floor(uv.x * 9.0);\n    float h = 0.16 + 0.62 * scrHash(vec2(col0, seed));\n    float bar = scrBand(fract(uv.x * 9.0), 0.18, 0.82) * scrBand(uv.y, 0.12, 0.12 + h);\n    float hot = step(6.0, col0) * step(col0, 6.0);\n    col += mix(vec3(0.25, 0.45, 0.75), accent, hot) * bar;\n    // A moving read-head, because dashboards are never still.\n    col += accent * 0.5 * (1.0 - smoothstep(0.0, 0.006, abs(uv.x - fract(t * 0.13))));\n    return col;\n  }\n\n  // An incoming call: avatar, name plate, decline and answer.\n  vec3 scrCall(vec2 uv, float t, float seed, float aspect, vec3 accent) {\n    vec3 col = vec3(0.04, 0.05, 0.09);\n    float d = length((uv - vec2(0.5, 0.66)) * vec2(aspect, 1.0));\n    col += scrHue(scrHash(vec2(seed, 3.0))) * 0.8 * (1.0 - smoothstep(0.15, 0.16, d));\n    // Ring pulse radiating off the avatar.\n    float pulse = fract(t * 0.7);\n    col += accent * 0.35 * (1.0 - smoothstep(0.006, 0.02, abs(d - 0.16 - pulse * 0.16))) * (1.0 - pulse);\n    // Name plate.\n    col += vec3(0.55) * scrBand(uv.y, 0.40, 0.435) * scrBand(uv.x, 0.30, 0.70);\n    col += vec3(0.25) * scrBand(uv.y, 0.34, 0.365) * scrBand(uv.x, 0.38, 0.62);\n    // Decline / answer.\n    col += vec3(0.85, 0.15, 0.15) * scrRound(uv, vec2(0.30, 0.15), vec2(0.10, 0.055), 0.05, aspect);\n    col += vec3(0.15, 0.80, 0.35) * scrRound(uv, vec2(0.70, 0.15), vec2(0.10, 0.055), 0.05, aspect)\n           * (0.75 + 0.25 * sin(t * 6.0));\n    return col;\n  }\n\n  // A PIN pad — for the terminals, door locks and ATMs this sets up.\n  vec3 scrKeypad(vec2 uv, float t, float seed, float aspect, vec3 accent) {\n    vec3 col = vec3(0.03, 0.04, 0.05);\n    // Entry strip with masked digits.\n    col += vec3(0.09, 0.11, 0.13) * scrRound(uv, vec2(0.5, 0.87), vec2(0.40, 0.06), 0.02, aspect);\n    float typed = floor(mod(t * 0.8, 5.0));\n    float slot = floor((uv.x - 0.34) * 12.5);\n    float dotm = scrRound(vec2(fract((uv.x - 0.34) * 12.5), uv.y), vec2(0.5, 0.87), vec2(0.16, 0.016), 0.016, aspect / 12.5);\n    col += accent * dotm * step(0.0, slot) * step(slot, typed - 1.0) * scrBand(uv.x, 0.34, 0.66);\n    // 3 x 4 keys.\n    vec2 k = vec2((uv.x - 0.17) / 0.22, (uv.y - 0.06) / 0.185);\n    if (uv.x > 0.17 && uv.x < 0.83 && uv.y > 0.06 && uv.y < 0.80) {\n      vec2 kc = floor(k);\n      float lit = step(0.5, scrHash(vec2(kc.x + kc.y * 3.0, floor(t * 1.6) + seed)));\n      float key = scrRound(fract(k) - 0.5, vec2(0.0), vec2(0.36, 0.34), 0.06, aspect * 0.22 / 0.185);\n      col += mix(vec3(0.14, 0.16, 0.19), accent * 0.8, lit * 0.35) * key;\n    }\n    return col;\n  }\n`;\n\nconst SCREEN_FRAG = /* glsl */ `\n  {\n    vec2 scrUv = vUv;\n    vec3 scrCol = vec3(0.0);\n    if (uScrMode == 1) {\n      // Standby: one breathing pip, low in the corner. Everything else dark.\n      float d = length((scrUv - vec2(0.93, 0.07)) * vec2(uScrAspect, 1.0));\n      scrCol = uScrAccent * (1.0 - smoothstep(0.008, 0.014, d)) * (0.35 + 0.3 * sin(uScrTime * 1.6));\n    } else if (uScrMode == 2) {\n      scrCol = scrHome(scrUv, uScrTime, uScrSeed, uScrAspect, uScrAccent);\n    } else if (uScrMode == 3) {\n      scrCol = scrFeed(scrUv, uScrScroll, uScrSeed, uScrAspect, uScrAccent);\n    } else if (uScrMode == 4) {\n      scrCol = scrVideo(scrUv, uScrTime, uScrAspect, uScrShotA, uScrShotB);\n    } else if (uScrMode == 5) {\n      scrCol = scrMap(scrUv, uScrTime, uScrSeed, uScrAspect, uScrAccent);\n    } else if (uScrMode == 6) {\n      scrCol = scrChart(scrUv, uScrTime, uScrSeed, uScrAspect, uScrAccent);\n    } else if (uScrMode == 7) {\n      scrCol = scrCall(scrUv, uScrTime, uScrSeed, uScrAspect, uScrAccent);\n    } else if (uScrMode == 8) {\n      scrCol = scrKeypad(scrUv, uScrTime, uScrSeed, uScrAspect, uScrAccent);\n    }\n    // Written into radiance directly, NOT material.emissive, so the day/night\n    // cycle's emissiveIntensity scaling cannot dim a screen. A monitor at\n    // midnight is as bright as one at noon — that is the point of a screen.\n    totalEmissiveRadiance += scrCol * uScrBright;\n  }\n`;\n\n// --- the CPU half ------------------------------------------------------\n\n/** Per-mode resting glow: what the panel throws into the room. */\nconst MODE_GLOW: Record<ScreenMode, { color: number; intensity: number }> = {\n  off: { color: 0x000000, intensity: 0 },\n  standby: { color: 0x33ff88, intensity: 0.03 },\n  home: { color: 0x8fa6ff, intensity: 0.55 },\n  feed: { color: 0xcfd8ff, intensity: 0.7 },\n  video: { color: 0xffffff, intensity: 1.0 }, // overridden per shot\n  map: { color: 0x7fd0c0, intensity: 0.5 },\n  chart: { color: 0x6fa0e0, intensity: 0.45 },\n  call: { color: 0x9fd8ff, intensity: 0.65 },\n  keypad: { color: 0x7fc0d8, intensity: 0.35 },\n};\n\n/**\n * A shot list: the cuts a \"playing\" screen runs through. Durations vary\n * because real edits are not metronomic — a fixed cut length reads as a\n * strobe, and the room lighting gives it away long before the picture does.\n */\ninterface Shot {\n  at: number;\n  a: Color;\n  b: Color;\n  level: number;\n}\n\nfunction buildShots(seed: number, count = 48): Shot[] {\n  let s = (seed * 2654435761) >>> 0;\n  const rand = (): number => {\n    s = (s + 0x6d2b79f5) >>> 0;\n    let t = Math.imul(s ^ (s >>> 15), 1 | s);\n    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n  const shots: Shot[] = [];\n  let at = 0;\n  for (let i = 0; i < count; i++) {\n    // Most cuts are short; a few hold. Squaring biases toward the short end.\n    const hold = 0.55 + Math.pow(rand(), 2) * 3.4;\n    const hue = rand();\n    // Mostly desaturated. Real footage is skin, sky, concrete and cloth; a\n    // run of fully saturated hues lights the room like a nightclub. Squaring\n    // keeps most shots muted and lets the occasional vivid one through.\n    const sat = 0.1 + Math.pow(rand(), 2) * 0.5;\n    const warm = new Color().setHSL(hue, sat, 0.52);\n    const cool = new Color().setHSL((hue + 0.1 + rand() * 0.16) % 1, sat * 0.8, 0.3);\n    shots.push({ at, a: warm, b: cool, level: 0.45 + rand() * 0.85 });\n    at += hold;\n  }\n  return shots;\n}\n\nexport interface ScreenPanelOptions extends ScreenOptions {\n  /** Aspect override. Defaults to width / height. */\n  aspect?: number;\n}\n\n/**\n * Build a screen's material and its live state. Callers own the mesh — the\n * panel only needs to know how big it is so its UI lays out square.\n */\nexport function createScreenPanel(\n  width: number,\n  height: number,\n  options: ScreenPanelOptions = {}\n): ScreenPanel {\n  const seed = options.seed ?? 1;\n  const aspect = options.aspect ?? width / Math.max(height, 1e-4);\n  const accent = new Color(options.accent ?? 0x4d9fff);\n  const brightness = options.brightness ?? 1;\n  const scrollRate = options.scrollRate ?? 0;\n  let mode: ScreenMode = options.mode ?? 'home';\n\n  const uniforms = {\n    uScrMode: { value: MODE_ID[mode] },\n    uScrTime: { value: 0 },\n    uScrSeed: { value: seed },\n    uScrAspect: { value: aspect },\n    uScrAccent: { value: accent.clone() },\n    uScrBright: { value: brightness },\n    uScrScroll: { value: 0 },\n    uScrShotA: { value: new Color(0.6, 0.6, 0.6) },\n    uScrShotB: { value: new Color(0.2, 0.2, 0.3) },\n    uScrLevel: { value: 1 },\n  };\n\n  // A powered-off screen is not black — it is a dark mirror. Low roughness\n  // over a near-black base gives the reflection that reads as glass.\n  const material = new MeshStandardMaterial({\n    color: 0x080a0e,\n    roughness: 0.18,\n    metalness: 0.35,\n  });\n  // Force vUv: without a map three omits the varying, and the whole UI is\n  // laid out in UV space.\n  material.defines = { ...(material.defines ?? {}), USE_UV: '' };\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.fragmentShader = shader.fragmentShader\n      .replace(\n        'void main() {',\n        `uniform int uScrMode;\n         uniform float uScrTime;\n         uniform float uScrSeed;\n         uniform float uScrAspect;\n         uniform vec3 uScrAccent;\n         uniform float uScrBright;\n         uniform float uScrScroll;\n         uniform vec3 uScrShotA;\n         uniform vec3 uScrShotB;\n         uniform float uScrLevel;\n         ${SCREEN_HELPERS}\n         ${SCREEN_MODES}\n         void main() {`\n      )\n      .replace('#include <emissivemap_fragment>', `#include <emissivemap_fragment>\\n${SCREEN_FRAG}`);\n  };\n  // Distinct cache key per mode set, so two panels showing different things\n  // do not share a compiled program keyed only on material properties.\n  material.customProgramCacheKey = () => 'scenaScreen';\n\n  const shots = buildShots(seed);\n  const loop = shots[shots.length - 1].at;\n  const glow = { color: new Color(0, 0, 0), intensity: 0 };\n  let time = 0;\n  let scroll = 0;\n\n  const applyGlow = (): void => {\n    if (mode === 'video') {\n      // Find the current shot. Cheap linear scan over a small list, and it\n      // must be the SAME shot the shader is drawing — which it is, because\n      // the CPU pushes the shot colours in as uniforms rather than the\n      // shader picking its own. Light and picture cannot drift apart.\n      const t = time % loop;\n      let i = 0;\n      while (i + 1 < shots.length && shots[i + 1].at <= t) i++;\n      const shot = shots[i];\n      uniforms.uScrShotA.value.copy(shot.a);\n      uniforms.uScrShotB.value.copy(shot.b);\n      // Within a shot the level drifts a little (the camera moves, the\n      // picture breathes); at the cut it steps. That step is the flicker.\n      const into = t - shot.at;\n      const drift = 1 + 0.12 * Math.sin(into * 2.3 + shot.level * 6.0);\n      uniforms.uScrLevel.value = shot.level * drift;\n      glow.color.copy(shot.a).lerp(shot.b, 0.35);\n      glow.intensity = shot.level * drift * brightness;\n    } else {\n      uniforms.uScrLevel.value = 1;\n      const preset = MODE_GLOW[mode];\n      glow.color.set(preset.color);\n      glow.intensity = preset.intensity * brightness;\n    }\n  };\n  applyGlow();\n\n  return {\n    surface: new Object3D(), // replaced by the owning prop\n    width,\n    height,\n    material,\n    get mode() {\n      return mode;\n    },\n    setMode(next: ScreenMode) {\n      if (next === mode) return;\n      mode = next;\n      uniforms.uScrMode.value = MODE_ID[next];\n      applyGlow();\n    },\n    glow,\n    update(dt: number) {\n      time += dt;\n      uniforms.uScrTime.value = time;\n      if (mode === 'feed' && scrollRate !== 0) {\n        scroll += dt * scrollRate * 0.235;\n        uniforms.uScrScroll.value = scroll;\n      }\n      applyGlow();\n    },\n  };\n}\n","import { MeshStandardMaterial } from 'three';\n\n/**\n * Pictures — the images that go on walls.\n *\n * This is `createScreenPanel`'s twin, and deliberately so: the same trick of\n * drawing content procedurally in the fragment shader from the panel's own\n * UVs, so a gallery of forty framed pictures fetches nothing, repeats\n * nothing, and costs one material path.\n *\n * The one structural difference matters more than everything else here. A\n * screen writes into **emissive radiance** — it is a light source, and the\n * day/night cycle must not dim it. A picture writes into **base colour**. It\n * has no light of its own; a painting in an unlit room is invisible, and one\n * that keeps glowing at midnight is the single loudest tell that a wall is\n * decorated with screenshots. So this patches `map_fragment`, not\n * `emissivemap_fragment`, and everything downstream — lighting, shadow, the\n * day cycle, fog — applies to it exactly as it does to plaster.\n *\n * ```ts\n * const picture = createPicture(0.6, 0.45, { style: 'landscape', seed: 7 });\n * mesh.material = picture.material;\n * ```\n *\n * Content is not representational in any detail. At the size a picture\n * occupies on screen — a framed photo is a couple of dozen pixels across\n * from the sofa — what reads is **value structure**: where the light is,\n * where the dark mass is, roughly what the subject's silhouette does. Paint\n * that correctly and the eye supplies a landscape. Paint individual leaves\n * and you get mush that costs more.\n */\nexport type PictureStyle =\n  /** Sky, horizon, receding hills. The default wall filler. */\n  | 'landscape'\n  /** A figure: shoulders, head, hair mass, warm and low-contrast. */\n  | 'portrait'\n  /** Dark ground, a table edge, lit objects — the old-master still life. */\n  | 'stillLife'\n  /** Soft bands of colour with blurred edges. */\n  | 'abstract'\n  /** Hard-edged shapes in a limited palette. */\n  | 'geometric'\n  /** Desaturated, vignetted, a bright window and a dark subject. */\n  | 'photo'\n  /** Flat colour field, a heavy band, and blocks where the type goes. */\n  | 'poster'\n  /** Mostly white with ruled lines of text and a stamp — a printed notice. */\n  | 'notice'\n  /**\n   * Not a picture at all: a pale gradient with a skewed bright patch where a\n   * window would land. A real mirror is a second render pass per mirror,\n   * which is an absurd price for set dressing, and a metal surface with no\n   * environment map renders black. A painted reflection reads correctly at\n   * any distance you would actually film a room from.\n   */\n  | 'mirror';\n\nconst STYLE_ID: Record<PictureStyle, number> = {\n  landscape: 0,\n  portrait: 1,\n  stillLife: 2,\n  abstract: 3,\n  geometric: 4,\n  photo: 5,\n  mirror: 6,\n  poster: 7,\n  notice: 8,\n};\n\nexport const PICTURE_STYLES: PictureStyle[] = [\n  'landscape',\n  'portrait',\n  'stillLife',\n  'abstract',\n  'geometric',\n  'photo',\n];\n\n/** Everything `createPicture` can draw, including the non-painting styles. */\nexport const ALL_PICTURE_STYLES: PictureStyle[] = [\n  ...PICTURE_STYLES,\n  'mirror',\n  'poster',\n  'notice',\n];\n\nexport interface PictureOptions {\n  /** What the picture is of. Default 'landscape'. */\n  style?: PictureStyle;\n  /** Drives every hue, layout and placement choice. Default 1. */\n  seed?: number;\n  /**\n   * Yellowing, darkened varnish and a heavier vignette (0–1). Default 0.25.\n   * Push it up for a manor, down for a new-build.\n   */\n  age?: number;\n  /** Overall value. Default 1. Below 1 for a picture in a dim corner. */\n  brightness?: number;\n  /** Aspect override. Defaults to width / height. */\n  aspect?: number;\n}\n\nexport interface Picture {\n  material: MeshStandardMaterial;\n  style: PictureStyle;\n  width: number;\n  height: number;\n}\n\n// --- the shader ---------------------------------------------------------\n//\n// GLSL ES 1.00, same constraints as the screen shader: no `switch`, constant\n// loop bounds. Laid out in UV space with x scaled by aspect wherever a shape\n// has to stay round.\n\nconst PIC_HELPERS = /* glsl */ `\n  float picHash(vec2 p) {\n    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n  }\n  float picHash1(float x) { return picHash(vec2(x, 17.13)); }\n  // Value noise, for canvas weave and the softening of hard bands.\n  float picNoise(vec2 p) {\n    vec2 i = floor(p); vec2 f = fract(p);\n    f = f * f * (3.0 - 2.0 * f);\n    return mix(mix(picHash(i), picHash(i + vec2(1.0, 0.0)), f.x),\n               mix(picHash(i + vec2(0.0, 1.0)), picHash(i + vec2(1.0, 1.0)), f.x), f.y);\n  }\n  float picFbm(vec2 p) {\n    return picNoise(p) * 0.6 + picNoise(p * 2.3) * 0.26 + picNoise(p * 5.1) * 0.14;\n  }\n  // Ellipse coverage, soft-edged. Radii are in UV units on each axis, so a\n  // round shape wants r.x divided by the aspect; most shapes here are\n  // deliberately elliptical and pass their own.\n  float picBlob(vec2 uv, vec2 c, vec2 r, float soft, float aspect) {\n    vec2 q = (uv - c) / max(r, vec2(1e-4));\n    return 1.0 - smoothstep(1.0 - soft, 1.0 + soft, length(q));\n  }\n  float picStep(float v, float lo, float hi) {\n    return step(lo, v) * step(v, hi);\n  }\n  float picRect(vec2 uv, vec2 c, vec2 h, float soft) {\n    vec2 d = abs(uv - c) - h;\n    float dist = max(d.x, d.y);\n    return 1.0 - smoothstep(-soft, soft, dist);\n  }\n  // A head-and-shoulders silhouette, in units of head-heights, positioned at\n  // the base of the neck.\n  //\n  // Worth a helper because the naive version — a disc sitting on a soft blob\n  // — is a snowman, and it is a snowman at every size and in every palette.\n  // Shoulders have to WIDEN AS THEY FALL; that slope is the single feature\n  // that makes a dark shape read as a person.\n  float picBust(vec2 uv, vec2 at, float scale, float aspect) {\n    vec2 q = (uv - at) / max(scale, 1e-4);\n    q.x *= aspect;\n    float head = 1.0 - smoothstep(0.94, 1.06, length(vec2(q.x / 0.27, (q.y - 0.36) / 0.38)));\n    // Widen fast just under the neck, then stop. A spread that keeps growing\n    // all the way down is a pyramid, which is no more a person than the\n    // snowman was.\n    float spread = 0.11 + 0.80 * smoothstep(0.07, -0.26, q.y);\n    float body = (1.0 - smoothstep(spread - 0.03, spread + 0.03, abs(q.x)))\n               * smoothstep(0.10, 0.02, q.y);\n    return clamp(head + body, 0.0, 1.0);\n  }\n  // A muted painterly palette from a hue. Deliberately not saturated: full\n  // chroma on a wall reads as a poster of a colour swatch, never as paint.\n  vec3 picPigment(float h, float sat, float val) {\n    vec3 c = 0.5 + 0.5 * cos(6.28318 * (h + vec3(0.0, 0.33, 0.67)));\n    return mix(vec3(dot(c, vec3(0.33))), c, sat) * val;\n  }\n`;\n\nconst PIC_STYLES = /* glsl */ `\n  // Sky, horizon, hills going back into haze. The whole thing is value\n  // structure: light sky, mid hills, dark foreground.\n  vec3 picLandscape(vec2 uv, float seed, float aspect) {\n    float h = 0.36 + picHash1(seed) * 0.16;          // horizon height\n    float hue = 0.52 + picHash1(seed + 3.0) * 0.12;  // sky blue-ish\n    vec3 zenith = picPigment(hue, 0.42, 0.62);\n    vec3 haze = picPigment(hue + 0.06, 0.16, 0.88);\n    // Sky: darker at the top, pale at the horizon. Never the other way round.\n    vec3 col = mix(haze, zenith, smoothstep(h, 1.0, uv.y));\n    // Cloud banding, flattened horizontally the way real cloud reads.\n    float cloud = picFbm(vec2(uv.x * 3.2 + seed, uv.y * 7.0)) ;\n    col = mix(col, haze * 1.06, smoothstep(0.55, 0.85, cloud) * step(h, uv.y) * 0.55);\n    // A low sun on some of them.\n    float hasSun = step(0.55, picHash1(seed + 9.0));\n    vec2 sunAt = vec2(0.24 + picHash1(seed + 11.0) * 0.52, h + 0.10);\n    float sun = picBlob(uv, sunAt, vec2(0.035 / aspect, 0.035), 0.6, aspect);\n    col += picPigment(0.11, 0.5, 1.0) * sun * hasSun * 0.8;\n\n    // Three ridges. Each is nearer, darker and less hazy than the last —\n    // aerial perspective is doing all the work of saying \"distance\".\n    for (int i = 0; i < 3; i++) {\n      float fi = float(i);\n      float top = h - fi * 0.055 - 0.01;\n      float wob = (picFbm(vec2(uv.x * (1.6 + fi * 1.4) + seed * 0.7 + fi * 5.0, fi)) - 0.5) * (0.05 + fi * 0.035);\n      float land = step(uv.y, top + wob);\n      float depth = 1.0 - fi / 3.0;\n      vec3 hill = mix(picPigment(0.30 + picHash1(seed + fi) * 0.10, 0.30, 0.30 - fi * 0.055), haze, depth * 0.62);\n      col = mix(col, hill, land);\n    }\n    // Foreground: darkest band, slightly warmer — the ground you stand on.\n    float fg = step(uv.y, 0.085 + (picFbm(vec2(uv.x * 4.0 + seed, 9.0)) - 0.5) * 0.06);\n    col = mix(col, picPigment(0.18, 0.34, 0.16), fg);\n    return col;\n  }\n\n  // A figure. Warm, low contrast, lit from one side — that is the entire\n  // recipe, and it survives being four pixels tall.\n  vec3 picPortrait(vec2 uv, float seed, float aspect) {\n    float hue = picHash1(seed + 2.0);\n    // The value ladder is the whole picture, and it has to be a real ladder:\n    // ground ~0.10, clothes ~0.17, hair ~0.08, face ~0.62. Everything sitting\n    // inside one narrow band — which is what this did first — collapses into\n    // a ring of near-identical greys where a head should be.\n    vec3 col = picPigment(0.09 + hue * 0.05, 0.30, 0.10);\n    // A lighter wash behind the shoulder, off to one side. Centred behind the\n    // head it just outlines the hair and the figure reads as a doughnut.\n    float lit = 0.5 + (step(0.5, picHash1(seed + 17.0)) - 0.5) * 0.44;\n    col = mix(col, picPigment(0.10, 0.24, 0.26),\n      picBlob(uv, vec2(lit, 0.52), vec2(0.40, 0.42), 1.0, aspect) * 0.85);\n    // The figure, as one silhouette: neck, sloping shoulders, head. Muted,\n    // so the sitter's coat does not out-shout their face.\n    vec3 cloth = picPigment(hue, 0.22, 0.17 + picHash1(seed + 5.0) * 0.07);\n    col = mix(col, cloth, picBust(uv, vec2(0.5, 0.44), 0.40, aspect));\n    // Hair, as a CAP: the same ellipse pushed up, so it is thick over the\n    // crown and gone by the chin. Concentric with the head it comes out as\n    // an even ring, which is a helmet.\n    col = mix(col, picPigment(0.07, 0.40, 0.06 + picHash1(seed + 7.0) * 0.07),\n      picBlob(uv, vec2(0.5, 0.625), vec2(0.125, 0.150), 0.12, aspect));\n    // Face: much lighter, modelled from one side.\n    float side = smoothstep(0.60, 0.40, uv.x);\n    float head = picBlob(uv, vec2(0.5, 0.578), vec2(0.098, 0.132), 0.10, aspect);\n    vec3 skin = picPigment(0.055, 0.30, 0.50 + picHash1(seed + 13.0) * 0.22)\n      * (0.62 + side * 0.48);\n    col = mix(col, skin, head);\n    // A collar: the light note that stops it being a head balanced on a blob.\n    col = mix(col, skin * 0.9, picRect(uv, vec2(0.5, 0.425), vec2(0.085, 0.018), 0.022) * 0.8);\n    return col;\n  }\n\n  // Old-master still life: near-black ground, a table edge, three lit objects.\n  vec3 picStillLife(vec2 uv, float seed, float aspect) {\n    // A proper value ladder: near-black ground, mid table, and objects that\n    // are genuinely light where the light hits them. Everything within one\n    // narrow band — which is where this started — is a mauve rectangle with\n    // suggestions in it.\n    vec3 col = picPigment(0.08, 0.30, 0.045);\n    // The table: a horizontal edge, everything below it darker still.\n    float table = 0.30 + picHash1(seed) * 0.06;\n    col = mix(col, picPigment(0.07, 0.34, 0.10), step(uv.y, table));\n    col = mix(col, picPigment(0.09, 0.30, 0.26), picRect(uv, vec2(0.5, table), vec2(0.6, 0.006), 0.008));\n    // A vessel: tall, with a neck. Two stacked blobs is enough of a silhouette.\n    float vx = 0.34 + picHash1(seed + 4.0) * 0.10;\n    vec3 pot = picPigment(0.06 + picHash1(seed + 6.0) * 0.08, 0.26, 0.50);\n    float belly = picBlob(uv, vec2(vx, table + 0.115), vec2(0.105, 0.115), 0.10, aspect);\n    float neck = picRect(uv, vec2(vx, table + 0.235), vec2(0.030, 0.075), 0.012);\n    // Lit hard from one side: the falloff across a curved body is what says\n    // \"round\" without a single extra triangle.\n    float lit = smoothstep(vx + 0.09, vx - 0.10, uv.x) * 0.82 + 0.24;\n    col = mix(col, pot * lit, clamp(belly + neck, 0.0, 1.0));\n    // Fruit: two or three, sitting ON the table line, never floating above it.\n    for (int i = 0; i < 3; i++) {\n      float fi = float(i);\n      float r = 0.042 + picHash1(seed + fi * 3.0 + 21.0) * 0.028;\n      float fx = 0.56 + fi * 0.115 + picHash1(seed + fi + 31.0) * 0.04;\n      float f = picBlob(uv, vec2(fx, table + r * 0.92), vec2(r / aspect, r), 0.16, aspect);\n      vec3 fc = picPigment(0.02 + picHash1(seed + fi * 7.0) * 0.16, 0.52, 0.62);\n      float fl = smoothstep(fx + r * 1.1, fx - r, uv.x) * 0.78 + 0.26;\n      col = mix(col, fc * fl, f);\n    }\n    return col;\n  }\n\n  // Soft fields of colour. Blurred edges, related hues, one field dominant.\n  vec3 picAbstract(vec2 uv, float seed, float aspect) {\n    float hue = picHash1(seed);\n    // Two wandering boundaries cutting three fields, with a real spread of\n    // value between them. Stacking soft mixes on top of each other — the\n    // first attempt — averages everything back to one flat wash.\n    float wob = (picFbm(vec2(uv.x * 2.2 + seed, 3.0)) - 0.5) * 0.07;\n    float y = uv.y + wob;\n    float b1 = 0.28 + picHash1(seed + 1.0) * 0.14;\n    float b2 = b1 + 0.22 + picHash1(seed + 2.0) * 0.18;\n    vec3 col = picPigment(hue, 0.36, 0.18 + picHash1(seed + 5.0) * 0.09);\n    col = mix(col, picPigment(hue + 0.11, 0.44, 0.44 + picHash1(seed + 6.0) * 0.14),\n      smoothstep(b1 - 0.04, b1 + 0.04, y));\n    col = mix(col, picPigment(hue + 0.24, 0.26, 0.68 + picHash1(seed + 7.0) * 0.10),\n      smoothstep(b2 - 0.04, b2 + 0.04, y));\n    // A vertical cut on some of them. Without it, three horizontal fields\n    // with a light top is indistinguishable from the landscape.\n    float split = 0.32 + picHash1(seed + 8.0) * 0.36;\n    col = mix(col, col * 0.66 + picPigment(hue + 0.5, 0.5, 0.34) * 0.34,\n      step(0.5, picHash1(seed + 9.0)) * smoothstep(split - 0.012, split + 0.012, uv.x));\n    // Enough tooth that it reads as a surface and not a gradient fill.\n    col *= 0.94 + picFbm(vec2(uv.x * 40.0 * aspect, uv.y * 40.0)) * 0.12;\n    return col;\n  }\n\n  // Hard-edged shapes, limited palette, one accent. The modern-office print.\n  vec3 picGeometric(vec2 uv, float seed, float aspect) {\n    float hue = picHash1(seed + 8.0);\n    vec3 col = vec3(0.78, 0.76, 0.72) * (0.82 + picHash1(seed) * 0.2);\n    // A ground rectangle, then two shapes overlapping it.\n    col = mix(col, picPigment(hue, 0.42, 0.44),\n      picRect(uv, vec2(0.5, 0.5), vec2(0.40, 0.40), 0.004));\n    float cx = 0.36 + picHash1(seed + 2.0) * 0.2;\n    float cy = 0.40 + picHash1(seed + 4.0) * 0.2;\n    col = mix(col, picPigment(hue + 0.42, 0.55, 0.52),\n      picBlob(uv, vec2(cx, cy), vec2(0.20 / aspect, 0.20), 0.02, aspect));\n    // A bar, either upright or lying down, in the accent.\n    float upright = step(0.5, picHash1(seed + 6.0));\n    vec2 h = mix(vec2(0.30, 0.045), vec2(0.045, 0.30), upright);\n    col = mix(col, picPigment(hue + 0.14, 0.62, 0.60),\n      picRect(uv, vec2(0.5 + picHash1(seed + 10.0) * 0.16, 0.5), h, 0.004));\n    return col;\n  }\n\n  // A photograph: near-monochrome, cool, a bright window and a dark subject.\n  // Colour is what separates this from the paintings — a photo that is as\n  // chromatic as an oil reads as an oil.\n  vec3 picPhoto(vec2 uv, float seed, float aspect) {\n    float hue = 0.55 + picHash1(seed) * 0.1;\n    // Near-neutral. Colour is what separates a photograph from an oil, so\n    // almost all the chroma comes out of this one.\n    vec3 col = picPigment(hue, 0.09, 0.50 + picHash1(seed + 1.0) * 0.14);\n    // A bright region behind them: a window, a doorway, the sky.\n    vec2 at = vec2(0.28 + picHash1(seed + 3.0) * 0.44, 0.68);\n    col = mix(col, vec3(0.88, 0.89, 0.90), picBlob(uv, at, vec2(0.26, 0.28), 0.8, aspect) * 0.75);\n    // Two or three people, different sizes, none of them centred. One dark\n    // mass in the middle of the frame is not a photograph of anything.\n    float extra = step(0.5, picHash1(seed + 40.0));\n    for (int i = 0; i < 3; i++) {\n      float fi = float(i);\n      float here = step(fi, 1.0 + extra);\n      float x = 0.26 + fi * 0.23 + picHash1(seed + fi * 5.0 + 11.0) * 0.09;\n      float s = 0.30 + picHash1(seed + fi * 3.0 + 23.0) * 0.09;\n      col = mix(col, picPigment(hue - 0.05 + fi * 0.03, 0.20, 0.19 + fi * 0.045),\n        picBust(uv, vec2(x, 0.20), s, aspect) * here);\n    }\n    // Ground.\n    col = mix(col, picPigment(hue, 0.07, 0.30), 1.0 - smoothstep(0.06, 0.13, uv.y));\n    // Grain, which is the other half of \"photograph\".\n    col *= 0.93 + picHash(uv * vec2(520.0 * aspect, 520.0) + seed) * 0.14;\n    return col;\n  }\n\n  // Type, as TYPE — bands at the density a line of text has when you see it\n  // across a room, never glyph-shaped. There is no font here, and fake\n  // letterforms are the single most recognisable tell in a procedural scene:\n  // at any distance where you could tell they were letters, you can tell\n  // they are wrong.\n  float picType(vec2 uv, float top, float lines, float indent, float seed, float weight) {\n    float row = floor((top - uv.y) * lines);\n    if (row < 0.0 || row > lines - 1.0) return 0.0;\n    float band = fract((top - uv.y) * lines);\n    // Ink occupies the middle of each line's box, never the whole of it.\n    float ink = picStep(band, 0.28, 0.28 + weight);\n    // Ragged right edge: every line is a different length.\n    float len = 0.30 + picHash(vec2(row, seed)) * 0.62;\n    return ink * step(indent, uv.x) * step(uv.x, indent + len);\n  }\n\n  // A poster: one strong colour field, a heavy band, and type blocks.\n  vec3 picPoster(vec2 uv, float seed, float aspect) {\n    float hue = picHash1(seed);\n    vec3 ground = picPigment(hue, 0.45, 0.30 + picHash1(seed + 1.0) * 0.34);\n    vec3 col = ground;\n    // A band across, top or bottom, in a contrasting value.\n    float at = picHash1(seed + 3.0) > 0.5 ? 0.74 : 0.16;\n    vec3 bandCol = picPigment(hue + 0.45, 0.55, 0.72);\n    col = mix(col, bandCol, picRect(uv, vec2(0.5, at), vec2(0.6, 0.10), 0.004));\n    // A big shape: the poster's image.\n    col = mix(col, picPigment(hue + 0.18, 0.5, 0.20),\n      picBlob(uv, vec2(0.5, 0.5), vec2(0.26 / aspect, 0.26), 0.03, aspect));\n    // The headline: two heavy short bars, not letters.\n    vec3 ink = picPigment(hue + 0.5, 0.2, 0.12);\n    col = mix(col, ink, picRect(uv, vec2(0.32, at), vec2(0.16, 0.028), 0.003));\n    col = mix(col, ink, picRect(uv, vec2(0.26, at - 0.052), vec2(0.10, 0.016), 0.003));\n    // Small print at the foot.\n    col = mix(col, ink, picType(uv, at > 0.5 ? 0.14 : 0.92, 4.0, 0.16, seed, 0.34) * 0.75);\n    return col;\n  }\n\n  // A printed notice: white paper, a rule, a block of text, a stamp.\n  vec3 picNotice(vec2 uv, float seed, float aspect) {\n    vec3 col = vec3(0.80, 0.79, 0.75) * (0.94 + picHash1(seed) * 0.1);\n    vec3 ink = vec3(0.16, 0.15, 0.14);\n    // Heading, then a rule under it.\n    col = mix(col, ink, picRect(uv, vec2(0.5, 0.86), vec2(0.26, 0.026), 0.003));\n    col = mix(col, ink * 1.6, picRect(uv, vec2(0.5, 0.80), vec2(0.36, 0.004), 0.002));\n    // Body copy: ruled lines with a ragged right edge.\n    col = mix(col, ink, picType(uv, 0.74, 9.0, 0.14, seed, 0.3) * 0.85);\n    // A stamp or seal, off to one corner and never square to the page.\n    float d = length((uv - vec2(0.74, 0.20)) * vec2(aspect, 1.0));\n    vec3 stamp = picPigment(0.98, 0.55, 0.42);\n    col = mix(col, stamp, (1.0 - smoothstep(0.085, 0.095, d)) * 0.5);\n    col = mix(col, stamp, (1.0 - smoothstep(0.062, 0.07, d)) * 0.25);\n    return col;\n  }\n\n  // A mirror, painted. Pale, cool, a skewed bright patch where a window\n  // lands, and a darker lower half because the floor is darker than the sky.\n  vec3 picMirror(vec2 uv, float seed, float aspect) {\n    vec3 col = mix(vec3(0.30, 0.33, 0.36), vec3(0.56, 0.60, 0.64), uv.y);\n    // The window: a parallelogram, because a mirror is never square-on to one.\n    float skew = (uv.y - 0.5) * 0.34;\n    float win = picRect(vec2(uv.x + skew, uv.y), vec2(0.34, 0.66), vec2(0.13, 0.17), 0.02);\n    col = mix(col, vec3(0.90, 0.92, 0.95), win * 0.85);\n    // A soft second bounce, and a dark mass low down (furniture, the floor).\n    col = mix(col, vec3(0.72, 0.75, 0.79), picBlob(uv, vec2(0.72, 0.52), vec2(0.22, 0.30), 0.9, aspect) * 0.35);\n    col = mix(col, vec3(0.19, 0.21, 0.24), smoothstep(0.30, 0.02, uv.y) * 0.7);\n    // Glass is not perfectly clean.\n    col *= 0.97 + picFbm(vec2(uv.x * 6.0 * aspect, uv.y * 6.0) + seed) * 0.06;\n    return col;\n  }\n`;\n\nconst PIC_FRAG = /* glsl */ `\n  {\n    vec2 picUv = vUv;\n    vec3 picCol = vec3(0.5);\n    if (uPicStyle == 0) {\n      picCol = picLandscape(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 1) {\n      picCol = picPortrait(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 2) {\n      picCol = picStillLife(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 3) {\n      picCol = picAbstract(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 4) {\n      picCol = picGeometric(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 5) {\n      picCol = picPhoto(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 6) {\n      picCol = picMirror(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 7) {\n      picCol = picPoster(picUv, uPicSeed, uPicAspect);\n    } else if (uPicStyle == 8) {\n      picCol = picNotice(picUv, uPicSeed, uPicAspect);\n    }\n\n    if (uPicStyle < 6) {\n      // Canvas weave: a fine cross-hatch in VALUE only. Visible close, and at\n      // distance it just stops the picture looking like flat vector art.\n      float weave = sin(picUv.x * 620.0 * uPicAspect) * sin(picUv.y * 620.0);\n      picCol *= 1.0 + weave * 0.035;\n      // Varnish: old pictures go yellow and go dark at the edges, and both\n      // happen together. Applied after the weave so the darkening reads as\n      // depth rather than as a painted border.\n      picCol = mix(picCol, picCol * vec3(1.18, 1.02, 0.72), uPicAge * 0.65);\n      float r = length((picUv - 0.5) * vec2(uPicAspect, 1.0) / max(uPicAspect, 1.0));\n      picCol *= 1.0 - smoothstep(0.30, 0.78, r) * (0.16 + uPicAge * 0.34);\n    }\n\n    diffuseColor.rgb = picCol * uPicBright;\n  }\n`;\n\n/**\n * Build a picture's material. The caller owns the mesh; the picture only\n * needs to know its own proportions so shapes inside it stay round.\n *\n * Whatever the style, the output stays inside a painterly value range —\n * nothing goes to white and nothing to black. A wall picture that clips at\n * either end stops looking like a made object and starts looking like a hole\n * in the wall.\n */\nexport function createPicture(\n  width: number,\n  height: number,\n  options: PictureOptions = {}\n): Picture {\n  const style = options.style ?? 'landscape';\n  const seed = options.seed ?? 1;\n  const aspect = options.aspect ?? width / Math.max(height, 1e-4);\n  const age = options.age ?? 0.25;\n  const brightness = options.brightness ?? 1;\n\n  const uniforms = {\n    uPicStyle: { value: STYLE_ID[style] },\n    uPicSeed: { value: seed },\n    uPicAspect: { value: aspect },\n    uPicAge: { value: age },\n    uPicBright: { value: brightness },\n  };\n\n  const material = new MeshStandardMaterial({\n    color: 0xffffff,\n    // Oil on canvas is matte; a mirror is not. This is the only PBR\n    // difference between the two, and it is the one that sells it.\n    // Paper is flatter than canvas and glass is not matte at all.\n    roughness: style === 'mirror' ? 0.12 : style === 'notice' || style === 'poster' ? 0.94 : 0.86,\n    metalness: style === 'mirror' ? 0.55 : 0,\n  });\n  // Force vUv: with no map three drops the varying and the layout has\n  // nothing to lay out against.\n  material.defines = { ...(material.defines ?? {}), USE_UV: '' };\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.fragmentShader = shader.fragmentShader\n      .replace(\n        'void main() {',\n        `uniform int uPicStyle;\n         uniform float uPicSeed;\n         uniform float uPicAspect;\n         uniform float uPicAge;\n         uniform float uPicBright;\n         ${PIC_HELPERS}\n         ${PIC_STYLES}\n         void main() {`\n      )\n      // Base colour, NOT emissive. This one line is the whole difference\n      // between a painting and a television.\n      .replace('#include <map_fragment>', `#include <map_fragment>\\n${PIC_FRAG}`);\n  };\n  material.customProgramCacheKey = () => 'scenaPicture';\n\n  return { material, style, width, height };\n}\n\n/** Pick a picture style from a seed, excluding 'mirror'. */\nexport function pickPictureStyle(seed: number): PictureStyle {\n  const s = Math.abs(Math.floor(seed * 2654435761)) % PICTURE_STYLES.length;\n  return PICTURE_STYLES[s];\n}\n","import {\n  AdditiveBlending,\n  BufferAttribute,\n  BufferGeometry,\n  Color,\n  DoubleSide,\n  MeshStandardMaterial,\n  Points,\n  ShaderMaterial,\n} from 'three';\nimport { Rng } from '../core/random';\n\n/**\n * Moving water — the shared material behind streams, spouts, showers and\n * spray.\n *\n * This is the same extraction `clothWave` was: the fountain has had falling\n * water and a jet since 0.9, and every tap, shower and cascade wants both.\n * Pulling them out here is what stops the bathroom set growing a second\n * water shader.\n *\n * It also fixes what was there. The fountain's \"falling water\" was a\n * **static** translucent cylinder — at rest it reads as a glass rod, and no\n * amount of tinting fixes that, because the thing that says *water* is not\n * the colour, it is that the surface is **travelling downward and breaking\n * up as it goes**. Real falling water accelerates, so it narrows and\n * stretches, and past a certain distance it stops being a sheet and becomes\n * strands.\n */\n\nexport interface FlowOptions {\n  /** Fall height in metres — sets how far down the break-up develops. */\n  length?: number;\n  /** Downward travel, in UV lengths per second. Default 1.6. */\n  speed?: number;\n  /**\n   * How far down the fall it comes apart, 0–1. Default 0.35. Low for a thin\n   * tap stream (it breaks up almost at once); high for a thick weir.\n   */\n  breakUp?: number;\n  /** How many strands across the width. Default 7. */\n  strands?: number;\n  /** Base opacity at full flow. Default 0.55. */\n  opacity?: number;\n  color?: number | Color;\n}\n\nconst FLOW_HELPERS = /* glsl */ `\n  float flowHash(vec2 p) {\n    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n  }\n  float flowNoise(vec2 p) {\n    vec2 i = floor(p); vec2 f = fract(p);\n    f = f * f * (3.0 - 2.0 * f);\n    return mix(mix(flowHash(i), flowHash(i + vec2(1.0, 0.0)), f.x),\n               mix(flowHash(i + vec2(0.0, 1.0)), flowHash(i + vec2(1.0, 1.0)), f.x), f.y);\n  }\n`;\n\nconst FLOW_FRAG = /* glsl */ `\n  {\n    // Cylinder UVs run v = 0 at the bottom, 1 at the top; down below is how\n    // far the water has fallen.\n    float down = 1.0 - vUv.y;\n\n    // Falling water ACCELERATES, so equal spans of time cover longer spans of\n    // distance further down: the pattern has to stretch, not merely scroll.\n    // Scrolling at a constant rate is what makes a stream read as a barber's\n    // pole.\n    float stretched = down * down * 0.6 + down * 0.4;\n    float travel = stretched * 6.0 - uFlowTime * uFlowSpeed;\n\n    // Strands: bands around the column, wobbling as they fall.\n    float across = vUv.x * uFlowStrands;\n    float strand = flowNoise(vec2(across, travel));\n    float fine = flowNoise(vec2(across * 2.7, travel * 1.9 + 4.0));\n    float texture = strand * 0.65 + fine * 0.35;\n\n    // Break-up grows with distance fallen. Above the threshold it is a\n    // sheet; below it, holes open and it becomes separate ropes of water.\n    float apart = smoothstep(uFlowBreak, 1.0, down);\n    float mask = mix(1.0, smoothstep(0.28, 0.62, texture), apart);\n\n    // The leading edges catch the light — the bright rim is most of what\n    // makes water read as wet rather than as coloured glass. It has to be a\n    // strong, LOCAL highlight: a uniformly pale tube is a rod, whatever it is\n    // tinted, because what the eye reads as water is the variation along the\n    // length and not the colour.\n    float edge = smoothstep(0.38, 0.78, texture) * (0.4 + apart * 0.6);\n    diffuseColor.rgb = mix(uFlowColor * 0.8, vec3(1.25), edge);\n\n    // Fade in at the lip and out at the bottom, where it meets whatever it\n    // is falling into. A stream with a hard end looks cut off.\n    float ends = smoothstep(0.0, 0.06, down) * (1.0 - smoothstep(0.86, 1.0, down));\n    // The body of the water is quite transparent and the lit edges are not —\n    // that contrast is what separates a stream from a glass rod.\n    float body = mix(0.45, 1.0, edge);\n    diffuseColor.a *= uFlowOpacity * uFlowRate * mask * ends * body;\n  }\n`;\n\n/**\n * A material for water in motion down a surface — a spout's stream, a weir,\n * a shower's column.\n *\n * Drive it with `material.userData.flowUniforms`: `uFlowTime` every frame,\n * `uFlowRate` for how hard it is running (0 turns it off completely, which is\n * what a closed tap should look like).\n */\nexport function flowingWaterMaterial(options: FlowOptions = {}): MeshStandardMaterial {\n  const uniforms = {\n    uFlowTime: { value: 0 },\n    uFlowRate: { value: 1 },\n    uFlowSpeed: { value: options.speed ?? 1.6 },\n    uFlowBreak: { value: options.breakUp ?? 0.35 },\n    uFlowStrands: { value: options.strands ?? 7 },\n    uFlowOpacity: { value: options.opacity ?? 0.55 },\n    uFlowColor: { value: new Color(options.color ?? 0x9fd0e0) },\n  };\n\n  const material = new MeshStandardMaterial({\n    color: 0xffffff,\n    transparent: true,\n    // Water is see-through from both sides, and a stream is a thin tube: the\n    // far wall is in shot through the near one.\n    side: DoubleSide,\n    // Never write depth. Two overlapping streams sort wrong for one frame\n    // and look solid for the rest of the shot.\n    depthWrite: false,\n    roughness: 0.12,\n    metalness: 0.1,\n  });\n  material.defines = { ...(material.defines ?? {}), USE_UV: '' };\n  material.onBeforeCompile = (shader) => {\n    Object.assign(shader.uniforms, uniforms);\n    shader.fragmentShader = shader.fragmentShader\n      .replace(\n        'void main() {',\n        `uniform float uFlowTime;\n         uniform float uFlowRate;\n         uniform float uFlowSpeed;\n         uniform float uFlowBreak;\n         uniform float uFlowStrands;\n         uniform float uFlowOpacity;\n         uniform vec3 uFlowColor;\n         ${FLOW_HELPERS}\n         void main() {`\n      )\n      .replace('#include <map_fragment>', `#include <map_fragment>\\n${FLOW_FRAG}`);\n  };\n  material.customProgramCacheKey = () => 'scenaWaterFlow';\n  material.userData.flowUniforms = uniforms;\n  return material;\n}\n\n// --- droplets ------------------------------------------------------------\n\nexport interface DropletOptions {\n  /** How many points. Default 24. Each is one billboard, so keep it small. */\n  count?: number;\n  /** How far out they spread from the origin, in metres. */\n  spread?: number;\n  /**\n   * Ballistic rise before falling, in metres. A fountain jet throws water UP;\n   * a shower and a splash do not. Zero gives a pure fall.\n   */\n  rise?: number;\n  /** How far they fall, in metres. Default 0.4. */\n  fall?: number;\n  /** Point size. Default 0.28. */\n  size?: number;\n  /**\n   * Largest a droplet may get on screen, in pixels. Default 22. Point sprites\n   * scale with 1/distance and have no upper bound, so without this a splash\n   * viewed from close up becomes a screenful of glowing beach balls.\n   */\n  maxPixels?: number;\n  color?: number | Color;\n  seed?: number;\n}\n\nexport interface Droplets {\n  mesh: Points;\n  /** Advance the simulation. */\n  update(dt: number): void;\n  /** How hard it is spraying, 0–1. At 0 nothing is drawn. */\n  setRate(rate: number): void;\n}\n\nconst DROPLET_VERT = /* glsl */ `\nattribute float aPhase;\nattribute float aSpeed;\nattribute float aRad;\nattribute float aAng;\nuniform float uTime;\nuniform float uRise;\nuniform float uFall;\nuniform float uSpread;\nuniform float uSize;\nuniform float uMaxPixels;\nvarying float vLife;\nvoid main() {\n  float life = fract(uTime * aSpeed + aPhase);\n  vLife = life;\n  // Up-then-down when uRise > 0; a straight accelerating fall when it is 0.\n  // Gravity is what makes droplets read as droplets: constant-speed points\n  // are a snow effect.\n  float h = uRise > 0.0\n    ? 4.0 * life * (1.0 - life) * uRise\n    : -life * life * uFall;\n  float rad = aRad * uSpread * (uRise > 0.0 ? life : 0.35 + life * 0.65);\n  vec3 p = vec3(cos(aAng) * rad, h, sin(aAng) * rad);\n  vec4 mv = modelViewMatrix * vec4(p, 1.0);\n  // Point sprites grow without bound as the camera closes in, and a splash\n  // seen from 60 cm turns into a screenful of glowing beach balls. Cap it.\n  gl_PointSize = min(uSize * (240.0 / -mv.z), uMaxPixels);\n  gl_Position = projectionMatrix * mv;\n}`;\n\nconst DROPLET_FRAG = /* glsl */ `\nuniform vec3 uColor;\nuniform float uRate;\nvarying float vLife;\nvoid main() {\n  float d = length(gl_PointCoord - 0.5);\n  if (d > 0.5) discard;\n  // Fade in at birth, out at death; soft round dot; kept dim so many\n  // droplets read as a fine spray rather than a blown-out bloom.\n  float fade = max(0.0, 0.5 - abs(vLife - 0.5));\n  gl_FragColor = vec4(uColor, (1.0 - d * 2.0) * fade * 0.8 * uRate);\n}`;\n\n/**\n * A puff of droplets: a fountain jet, a shower's mist, the splash where a\n * stream lands.\n *\n * The only particles in the whole water set. Everything else here is shader\n * work on geometry that was going to be drawn anyway, so this is the one\n * piece with a real frame cost — keep the counts low.\n */\nexport function createDroplets(options: DropletOptions = {}): Droplets {\n  const rng = new Rng(options.seed ?? 1);\n  const count = options.count ?? 24;\n  const pos = new Float32Array(count * 3);\n  const aPhase = new Float32Array(count);\n  const aSpeed = new Float32Array(count);\n  const aRad = new Float32Array(count);\n  const aAng = new Float32Array(count);\n  for (let i = 0; i < count; i++) {\n    aPhase[i] = rng.next();\n    aSpeed[i] = rng.range(0.35, 0.7);\n    aRad[i] = rng.range(0.25, 1);\n    aAng[i] = rng.range(0, Math.PI * 2);\n  }\n  const geometry = new BufferGeometry();\n  geometry.setAttribute('position', new BufferAttribute(pos, 3));\n  geometry.setAttribute('aPhase', new BufferAttribute(aPhase, 1));\n  geometry.setAttribute('aSpeed', new BufferAttribute(aSpeed, 1));\n  geometry.setAttribute('aRad', new BufferAttribute(aRad, 1));\n  geometry.setAttribute('aAng', new BufferAttribute(aAng, 1));\n\n  const material = new ShaderMaterial({\n    uniforms: {\n      uTime: { value: 0 },\n      uRise: { value: options.rise ?? 0 },\n      uFall: { value: options.fall ?? 0.4 },\n      uSpread: { value: options.spread ?? 0.12 },\n      uSize: { value: options.size ?? 0.28 },\n      uMaxPixels: { value: options.maxPixels ?? 22 },\n      uRate: { value: 1 },\n      uColor: { value: new Color(options.color ?? 0xbfe0ee) },\n    },\n    vertexShader: DROPLET_VERT,\n    fragmentShader: DROPLET_FRAG,\n    transparent: true,\n    depthWrite: false,\n    blending: AdditiveBlending,\n  });\n  const mesh = new Points(geometry, material);\n  mesh.frustumCulled = false;\n\n  return {\n    mesh,\n    update(dt: number) {\n      material.uniforms.uTime.value += dt;\n    },\n    setRate(rate: number) {\n      const r = Math.min(1, Math.max(0, rate));\n      material.uniforms.uRate.value = r;\n      mesh.visible = r > 0.001;\n    },\n  };\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"],"mappings":"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","names":["import_three","import_three","import_three","import_three"]}