import type { ArtMovement } from '../types'; import type { SurfaceTextureKind } from '../utils/galleryProceduralTextures'; export type GalleryWindowStyle = | 'roman-arch' | 'gothic-lancet' | 'baroque-pair' | 'sash' | 'factory' | 'skylight' | 'art-nouveau' | 'glass-block' | 'clerestory' | 'round-oculus'; export interface GalleryWindowSpec { wall: 'back' | 'left' | 'right' | 'ceiling'; /** Offset along wall axis from center (meters). */ x: number; /** Vertical center height (meters). */ y: number; width: number; height: number; style: GalleryWindowStyle; lightColor: string; lightIntensity: number; } export interface MovementInteriorStyle { id: string; label: string; subtitle: string; surfaces: { wall: SurfaceTextureKind; wallSide?: SurfaceTextureKind; ceiling: SurfaceTextureKind; floor: SurfaceTextureKind; }; tints: { wall: string; wallSide?: string; ceiling: string; floor: string; trim: string; }; titleColor: string; ambient: number; warmLight: string; sunLight: string; fog: string; background: string; doorWood: [string, string, string]; windows: GalleryWindowSpec[]; trackLights: number; /** Scales the shared hall/scene lights. <1 for deliberately dim period interiors. */ lightScale: number; /** * Which period architecture the hall is built from. Every movement has its * own — the interiors are not shared between movements — and each kind maps * to one component under `components/hall-details/`. */ details: MovementDetailKind; } export type MovementDetailKind = | 'roman' | 'byzantine' | 'gothic' | 'quattrocento' | 'cinquecento' | 'flemish' | 'mannerist' | 'baroque' | 'rococo' | 'neoclassical' | 'gothic-revival' | 'bourgeois' | 'north-light' | 'paris-atelier' | 'symbolist' | 'art-nouveau' | 'fauvist' | 'expressionist' | 'cubist-atelier' | 'futurist' | 'suprematist' | 'constructivist' | 'dada' | 'surrealist' | 'loft' | 'white-cube'; function windows(...specs: GalleryWindowSpec[]): GalleryWindowSpec[] { return specs; } function mk( id: string, label: string, subtitle: string, surfaces: MovementInteriorStyle['surfaces'], tints: MovementInteriorStyle['tints'], opts: Partial> & { details: MovementInteriorStyle['details']; windows: GalleryWindowSpec[]; } ): MovementInteriorStyle { return { id, label, subtitle, surfaces, tints, titleColor: opts.titleColor ?? '#4a3020', ambient: opts.ambient ?? 0.55, warmLight: opts.warmLight ?? '#fff4e8', sunLight: opts.sunLight ?? '#fffaf0', fog: opts.fog ?? '#1a1814', background: opts.background ?? '#121010', doorWood: opts.doorWood ?? ['#3d2818', '#4e3624', '#261a10'], windows: opts.windows, trackLights: opts.trackLights ?? 0.8, lightScale: opts.lightScale ?? 1, details: opts.details, }; } /** Unique photo-real interior per movement (keyed by database id). */ const BY_MOVEMENT_ID: Record = { 27: mk( 'ancient-classical', 'Roman atrium', 'Marble-clad villa · mosaic floor · clerestory daylight', { wall: 'limestone', ceiling: 'plaster-warm', floor: 'mosaic-roman' }, { wall: '#8e8980', ceiling: '#9c9994', floor: '#c8b898', trim: '#a89878' }, { details: 'roman', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#5a4830', ambient: 0.62, warmLight: '#fff8e8', windows: windows( { wall: 'back', x: -2.5, y: 3.2, width: 1.4, height: 1.8, style: 'roman-arch', lightColor: '#fff8e0', lightIntensity: 3.2 }, { wall: 'back', x: 2.5, y: 3.2, width: 1.4, height: 1.8, style: 'roman-arch', lightColor: '#fff8e0', lightIntensity: 3.2 }, { wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.6, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 }, { wall: 'right', x: 0, y: 3.0, width: 2.0, height: 1.6, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 } ), trackLights: 0.4, doorWood: ['#6a5840', '#7a6848', '#5a4830'], } ), 28: mk( 'byzantine-sanctuary', 'Byzantine basilica', 'Marble revetment · coffered timber ceiling · Cosmatesque stone floor', { wall: 'marble-revetment', ceiling: 'coffered-wood', floor: 'marble-opus-sectile' }, { wall: '#a89880', ceiling: '#6a4526', floor: '#a09079', trim: '#8a6440' }, { details: 'byzantine', titleColor: '#e8d8b0', ambient: 0.44, warmLight: '#ffdca8', sunLight: '#ffdca0', fog: '#100b07', background: '#0a0705', windows: windows( { wall: 'back', x: 0, y: 3.1, width: 0.9, height: 1.9, style: 'roman-arch', lightColor: '#ffd28a', lightIntensity: 3.4 }, { wall: 'left', x: -3.5, y: 3.2, width: 0.7, height: 1.6, style: 'roman-arch', lightColor: '#ffd28a', lightIntensity: 3.0 }, { wall: 'left', x: 3.5, y: 3.2, width: 0.7, height: 1.6, style: 'roman-arch', lightColor: '#ffd28a', lightIntensity: 3.0 }, { wall: 'right', x: -3.5, y: 3.2, width: 0.7, height: 1.6, style: 'roman-arch', lightColor: '#ffd28a', lightIntensity: 3.0 }, { wall: 'right', x: 3.5, y: 3.2, width: 0.7, height: 1.6, style: 'roman-arch', lightColor: '#ffd28a', lightIntensity: 3.0 } ), trackLights: 0.35, // Basilica interiors are lit by shafts from high windows, not evenly flooded. lightScale: 0.3, doorWood: ['#3a2416', '#4c3220', '#241608'], } ), 29: mk( 'gothic-cathedral', 'Gothic nave', 'Blind-arcaded ashlar · ribbed stone vault · worn flagstones', { wall: 'gothic-ashlar', ceiling: 'gothic-vault', floor: 'gothic-stone-floor' }, { wall: '#8d8471', ceiling: '#6d6555', floor: '#6f6759', trim: '#7d7159' }, { details: 'gothic', titleColor: '#efe4cc', ambient: 0.6, warmLight: '#ffe6bc', sunLight: '#dbe8ff', // Cool shadowed stone, but never a pure void — walls must stay readable. fog: '#1b1a18', background: '#131211', windows: windows( { wall: 'back', x: -2, y: 2.8, width: 0.8, height: 2.8, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.8 }, { wall: 'back', x: 2, y: 2.8, width: 0.8, height: 2.8, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.8 }, { wall: 'left', x: -4.5, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.2 }, { wall: 'left', x: 4.5, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.2 }, { wall: 'right', x: -4.5, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.2 }, { wall: 'right', x: 4.5, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.2 } ), trackLights: 0.5, // Naves are lit by window shafts against shadowed stone, not flooded. lightScale: 0.26, } ), 30: mk( 'early-renaissance-palazzo', 'Florentine palazzo', 'Lime-washed walls · terracotta accents · arched windows', { wall: 'painted-lime', wallSide: 'stucco-cream', ceiling: 'fresco-worn', floor: 'terracotta-tiles' }, { wall: '#89847e', ceiling: '#96948f', floor: '#c89070', trim: '#c9a227' }, { details: 'quattrocento', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#6b4423', windows: windows( { wall: 'back', x: -2.2, y: 2.6, width: 1.3, height: 1.7, style: 'roman-arch', lightColor: '#fff4e0', lightIntensity: 3.0 }, { wall: 'back', x: 2.2, y: 2.6, width: 1.3, height: 1.7, style: 'roman-arch', lightColor: '#fff4e0', lightIntensity: 3.0 }, { wall: 'left', x: 0, y: 2.8, width: 2.2, height: 1.4, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.6 } ), } ), 31: mk( 'high-renaissance-palazzo', 'Roman palazzo', 'Marble and stucco · coffered ceiling · checker floor', { wall: 'marble-veined-carrara', wallSide: 'stucco-cream', ceiling: 'plaster-warm', floor: 'marble-checker' }, { wall: '#918f8a', wallSide: '#8f8980', ceiling: '#9c9994', floor: '#ddd8cc', trim: '#c9a227' }, { details: 'cinquecento', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#6b4423', windows: windows( { wall: 'back', x: 0, y: 2.8, width: 2.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8f0', lightIntensity: 3.8 }, { wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.5, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 }, { wall: 'right', x: 0, y: 3.0, width: 2.0, height: 1.5, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 }, { wall: 'ceiling', x: 0, y: 0, width: 3.0, height: 2.0, style: 'round-oculus', lightColor: '#ffffff', lightIntensity: 4.0 } ), } ), 32: mk( 'northern-renaissance-hall', 'Flemish panel hall', 'Oak wainscoting · leaded glass · herringbone floor', { wall: 'oak-panel', wallSide: 'plaster-warm', ceiling: 'dark-wood-panel', floor: 'parquet-herringbone' }, { wall: '#8a6848', wallSide: '#8c8985', ceiling: '#3a2818', floor: '#8a6848', trim: '#5c4030' }, { details: 'flemish', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#f0e8d8', warmLight: '#ffe8c8', windows: windows( { wall: 'back', x: -2, y: 2.5, width: 1.2, height: 1.5, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.8 }, { wall: 'back', x: 2, y: 2.5, width: 1.2, height: 1.5, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.8 }, { wall: 'left', x: 0, y: 2.6, width: 1.8, height: 1.4, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.4 } ), doorWood: ['#4a3020', '#5c3a28', '#3a2010'], } ), 33: mk( 'mannerist-villa', 'Mannerist gallery', 'Dramatic stucco · elongated windows · veined marble', { wall: 'stucco-terracotta', ceiling: 'gilded-stucco', floor: 'marble-veined-emerald' }, { wall: '#98816b', ceiling: '#a79456', floor: '#dce8e0', trim: '#b8860b' }, { details: 'mannerist', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#4a2818', windows: windows( { wall: 'back', x: -1.8, y: 2.7, width: 1.0, height: 2.2, style: 'roman-arch', lightColor: '#fff0d8', lightIntensity: 2.8 }, { wall: 'back', x: 1.8, y: 2.7, width: 1.0, height: 2.2, style: 'roman-arch', lightColor: '#fff0d8', lightIntensity: 2.8 }, { wall: 'right', x: 0, y: 3.0, width: 1.6, height: 1.2, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.2 } ), } ), 34: mk( 'baroque-palace', 'Baroque state gallery', 'Crimson velvet · gilded stucco · grand windows', { wall: 'velvet-crimson', ceiling: 'gilded-stucco', floor: 'marble-veined-carrara' }, { wall: '#5c1828', ceiling: '#d8c070', floor: '#ece8e0', trim: '#d4af37' }, { details: 'baroque', titleColor: '#f0d890', ambient: 0.58, warmLight: '#ffd898', fog: '#100808', windows: windows( { wall: 'back', x: 0, y: 2.6, width: 3.2, height: 2.2, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 4.5 }, { wall: 'left', x: 0, y: 2.8, width: 1.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 3.2 }, { wall: 'right', x: 0, y: 2.8, width: 1.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 3.2 }, { wall: 'ceiling', x: 0, y: 0, width: 2.5, height: 1.8, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.5 } ), trackLights: 0.6, doorWood: ['#1a1008', '#2a1810', '#120c06'], } ), 35: mk( 'rococo-salon', 'Rococo salon', 'Pastel painted walls · gilt trim · chevron parquet', { wall: 'painted-oil-satin', ceiling: 'silk-pale', floor: 'parquet-chevrons' }, { wall: '#918a91', ceiling: '#9b9995', floor: '#c8a882', trim: '#d4af37' }, { details: 'rococo', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#6a4858', warmLight: '#fff0f0', windows: windows( { wall: 'back', x: -2, y: 2.5, width: 1.4, height: 1.8, style: 'baroque-pair', lightColor: '#fff8ff', lightIntensity: 3.4 }, { wall: 'back', x: 2, y: 2.5, width: 1.4, height: 1.8, style: 'baroque-pair', lightColor: '#fff8ff', lightIntensity: 3.4 }, { wall: 'ceiling', x: 0, y: 0, width: 2.0, height: 1.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.0 } ), } ), 36: mk( 'neoclassical-museum', 'Neoclassical museum', 'White painted walls · coffered ceiling · skylit salon', { wall: 'painted-flat', ceiling: 'plaster-white', floor: 'marble-veined-carrara' }, { wall: '#908f8d', ceiling: '#9e9e9e', floor: '#eceae6', trim: '#b8b0a4' }, { details: 'neoclassical', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#4a4844', ambient: 0.65, windows: windows( { wall: 'back', x: 0, y: 2.6, width: 2.8, height: 2.0, style: 'baroque-pair', lightColor: '#ffffff', lightIntensity: 4.0 }, { wall: 'ceiling', x: 0, y: 0, width: 4.0, height: 2.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 5.0 }, { wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.2, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.5 } ), trackLights: 0.7, } ), 37: mk( 'romantic-gothic-revival', 'Romantic gallery', 'Dark walnut paneling · pointed windows · Persian carpet tones', { wall: 'walnut-panel', ceiling: 'dark-plaster', floor: 'parquet-herringbone' }, { wall: '#5a4030', ceiling: '#2a2420', floor: '#6a5040', trim: '#8a7050' }, { details: 'gothic-revival', titleColor: '#e8dcc8', ambient: 0.5, warmLight: '#ffe0c0', windows: windows( { wall: 'back', x: -1.5, y: 2.6, width: 0.9, height: 2.2, style: 'gothic-lancet', lightColor: '#c8d8ff', lightIntensity: 2.6 }, { wall: 'back', x: 1.5, y: 2.6, width: 0.9, height: 2.2, style: 'gothic-lancet', lightColor: '#c8d8ff', lightIntensity: 2.6 }, { wall: 'right', x: 0, y: 2.5, width: 1.4, height: 1.6, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.2 } ), } ), 38: mk( 'realist-bourgeois', 'Realist picture gallery', 'Warm painted walls · bourgeois salon · oak parquet', { wall: 'painted-emulsion', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' }, { wall: '#88847e', ceiling: '#97948f', floor: '#a08060', trim: '#8a7050' }, { details: 'bourgeois', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, windows: windows( { wall: 'back', x: 0, y: 2.5, width: 2.4, height: 1.6, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 3.2 }, { wall: 'left', x: 0, y: 2.7, width: 1.6, height: 1.3, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.4 } ), } ), 39: mk( 'impressionist-salon', 'Impressionist salon', 'North-light skylight · pale painted walls · herringbone floor', { wall: 'painted-emulsion', ceiling: 'painted-flat', floor: 'parquet-herringbone' }, { wall: '#8f8c87', ceiling: '#9e9e9e', floor: '#c8a882', trim: '#c9a96e' }, { details: 'north-light', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, ambient: 0.68, windows: windows( { wall: 'ceiling', x: 0, y: 0, width: 5.0, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.0 }, { wall: 'back', x: -2, y: 2.6, width: 1.2, height: 1.4, style: 'sash', lightColor: '#f0f8ff', lightIntensity: 2.5 }, { wall: 'back', x: 2, y: 2.6, width: 1.2, height: 1.4, style: 'sash', lightColor: '#f0f8ff', lightIntensity: 2.5 } ), trackLights: 0.5, } ), 40: mk( 'post-impressionist-atelier', 'Montmartre atelier', 'Painted studio walls · large north window · worn floorboards', { wall: 'painted-emulsion', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' }, { wall: '#847f76', ceiling: '#918f88', floor: '#9a7858', trim: '#8a6848' }, { details: 'paris-atelier', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, windows: windows( { wall: 'left', x: 0, y: 2.4, width: 2.8, height: 2.0, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 4.5 }, { wall: 'back', x: 0, y: 2.8, width: 1.0, height: 1.2, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.0 } ), } ), 41: mk( 'symbolist-chamber', 'Symbolist chamber', 'Deep green painted walls · amber window glow · dark parquet', { wall: 'painted-oil-matte', ceiling: 'dark-plaster', floor: 'parquet-herringbone' }, { wall: '#1a4030', ceiling: '#1a1814', floor: '#4a3828', trim: '#8a7050' }, { details: 'symbolist', titleColor: '#d8e8c8', ambient: 0.55, warmLight: '#ffd898', windows: windows( { wall: 'back', x: 0, y: 2.4, width: 1.0, height: 1.8, style: 'sash', lightColor: '#ffb860', lightIntensity: 2.0 }, { wall: 'right', x: 0, y: 2.6, width: 0.8, height: 1.4, style: 'gothic-lancet', lightColor: '#ffd080', lightIntensity: 1.6 } ), trackLights: 0.9, } ), 42: mk( 'art-nouveau-salon', 'Art Nouveau salon', 'Painted plaster · stained glass · terrazzo floor', { wall: 'painted-oil-satin', ceiling: 'silk-gold', floor: 'terrazzo' }, { wall: '#8f8c87', ceiling: '#a0946b', floor: '#d8d0c4', trim: '#6a9868' }, { details: 'art-nouveau', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#3a5840', windows: windows( { wall: 'back', x: 0, y: 2.5, width: 2.2, height: 2.0, style: 'art-nouveau', lightColor: '#e8ffe8', lightIntensity: 3.2 }, { wall: 'left', x: 0, y: 2.6, width: 1.4, height: 1.8, style: 'art-nouveau', lightColor: '#ffe8f0', lightIntensity: 2.4 }, { wall: 'ceiling', x: 0, y: 0, width: 2.0, height: 1.2, style: 'skylight', lightColor: '#ffffff', lightIntensity: 2.8 } ), } ), 43: mk( 'fauvist-studio', 'Fauvist studio', 'Bold painted walls · flooded with color and light', { wall: 'painted-oil-matte', wallSide: 'painted-oil-matte', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' }, { wall: '#e89060', wallSide: '#e8c860', ceiling: '#9e998e', floor: '#a07048', trim: '#c04020' }, { details: 'fauvist', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#402010', ambient: 0.7, windows: windows( { wall: 'left', x: 0, y: 2.5, width: 3.0, height: 2.2, style: 'factory', lightColor: '#fff8f0', lightIntensity: 5.0 }, { wall: 'back', x: 0, y: 2.8, width: 1.6, height: 1.2, style: 'sash', lightColor: '#fff0e0', lightIntensity: 2.5 } ), } ), 44: mk( 'expressionist-room', 'Expressionist room', 'Angular wood panels · dramatic raking light', { wall: 'dark-wood-panel', ceiling: 'dark-plaster', floor: 'parquet-herringbone' }, { wall: '#3a2818', ceiling: '#2a2018', floor: '#5a4030', trim: '#8a6040' }, { details: 'expressionist', titleColor: '#f0d8b0', ambient: 0.52, windows: windows( { wall: 'back', x: -1.5, y: 2.6, width: 1.0, height: 1.6, style: 'sash', lightColor: '#ffe8c0', lightIntensity: 2.8 }, { wall: 'right', x: 0, y: 2.4, width: 2.0, height: 1.8, style: 'factory', lightColor: '#fff0d8', lightIntensity: 3.5 } ), } ), 45: mk( 'cubist-studio', 'Cubist studio', 'Paris atelier · factory windows · painted plaster', { wall: 'painted-emulsion', ceiling: 'painted-flat', floor: 'parquet-herringbone' }, { wall: '#8c8a85', ceiling: '#999999', floor: '#9a8870', trim: '#888888' }, { details: 'cubist-atelier', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, ambient: 0.65, windows: windows( { wall: 'left', x: 0, y: 2.5, width: 3.5, height: 2.4, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 5.5 }, { wall: 'back', x: 0, y: 3.0, width: 1.8, height: 1.0, style: 'clerestory', lightColor: '#ffffff', lightIntensity: 2.5 } ), } ), 46: mk( 'futurist-loft', 'Futurist loft', 'Steel and glass · industrial concrete · sweeping daylight', { wall: 'concrete-raw', ceiling: 'concrete-raw', floor: 'industrial-floor' }, { wall: '#858585', ceiling: '#7a7a7a', floor: '#888880', trim: '#606060' }, { details: 'futurist', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#303030', ambient: 0.62, fog: '#181818', windows: windows( { wall: 'back', x: 0, y: 2.6, width: 4.0, height: 2.4, style: 'factory', lightColor: '#ffffff', lightIntensity: 6.0 }, { wall: 'left', x: 0, y: 2.8, width: 2.5, height: 1.6, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 3.5 }, { wall: 'ceiling', x: 0, y: 0, width: 3.0, height: 2.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 4.0 } ), doorWood: ['#606060', '#707070', '#505050'], } ), 47: mk( 'suprematist-gallery', 'Suprematist white cube', 'Matte white paint · geometric light · polished floor', { wall: 'painted-flat', ceiling: 'painted-flat', floor: 'concrete-polished' }, { wall: '#a8a8a8', ceiling: '#b3b3b3', floor: '#e0e0e0', trim: '#cccccc' }, { details: 'suprematist', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#222222', ambient: 0.72, fog: '#1a1a1a', background: '#111111', windows: windows( { wall: 'ceiling', x: 0, y: 0, width: 4.5, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.5 }, { wall: 'back', x: 0, y: 2.8, width: 2.0, height: 1.2, style: 'clerestory', lightColor: '#ffffff', lightIntensity: 3.0 } ), trackLights: 0.4, doorWood: ['#aaaaaa', '#bbbbbb', '#999999'], } ), 48: mk( 'constructivist-space', 'Constructivist space', 'Glass block · concrete · angular daylight', { wall: 'concrete-block', ceiling: 'concrete-raw', floor: 'concrete-polished' }, { wall: '#858585', ceiling: '#7a7a7a', floor: '#c0c0c0', trim: '#808080' }, { details: 'constructivist', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#303030', ambient: 0.65, windows: windows( { wall: 'back', x: 0, y: 2.5, width: 2.8, height: 2.0, style: 'glass-block', lightColor: '#f0f8ff', lightIntensity: 4.0 }, { wall: 'left', x: 0, y: 2.6, width: 2.0, height: 1.8, style: 'glass-block', lightColor: '#f0f8ff', lightIntensity: 3.2 }, { wall: 'ceiling', x: 0, y: 0, width: 2.5, height: 1.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.5 } ), doorWood: ['#707070', '#808080', '#606060'], } ), 49: mk( 'dada-salon', 'Dada salon', 'Eclectic bourgeois room · painted walls and exposed brick', { wall: 'painted-emulsion', wallSide: 'brick', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' }, { wall: '#8b8478', wallSide: '#8a5040', ceiling: '#97948d', floor: '#8a6848', trim: '#6a4830' }, { details: 'dada', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, windows: windows( { wall: 'back', x: -1.5, y: 2.5, width: 1.0, height: 1.4, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.5 }, { wall: 'back', x: 1.5, y: 2.6, width: 0.8, height: 1.8, style: 'gothic-lancet', lightColor: '#ffe8c0', lightIntensity: 2.0 }, { wall: 'right', x: 0, y: 2.4, width: 1.6, height: 1.6, style: 'factory', lightColor: '#ffffff', lightIntensity: 3.0 } ), trackLights: 1.0, } ), 50: mk( 'surrealist-interior', 'Surrealist interior', 'Dark painted walls · uncanny warm light', { wall: 'painted-oil-matte', ceiling: 'dark-plaster', floor: 'parquet-herringbone' }, { wall: '#1a2848', ceiling: '#2a2428', floor: '#5a4838', trim: '#8a7050' }, { details: 'surrealist', titleColor: '#e8dcc8', ambient: 0.58, warmLight: '#ffd898', windows: windows( { wall: 'back', x: 0, y: 2.5, width: 1.4, height: 1.6, style: 'sash', lightColor: '#ffe8c8', lightIntensity: 2.8 }, { wall: 'left', x: 0, y: 2.7, width: 1.0, height: 1.2, style: 'sash', lightColor: '#c8e0ff', lightIntensity: 2.0 }, { wall: 'ceiling', x: 0, y: 0, width: 1.5, height: 1.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 2.5 } ), trackLights: 0.85, } ), 51: mk( 'abstract-expressionist-loft', 'NYC loft', 'Raw brick and painted plaster · north-light factory windows', { wall: 'brick', wallSide: 'painted-flat', ceiling: 'concrete-raw', floor: 'industrial-floor' }, { wall: '#8a5040', wallSide: '#858585', ceiling: '#989898', floor: '#888880', trim: '#606060' }, { details: 'loft', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#303030', ambient: 0.65, windows: windows( { wall: 'left', x: 0, y: 2.5, width: 4.0, height: 2.6, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 6.5 }, { wall: 'ceiling', x: 0, y: 0, width: 3.5, height: 2.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 4.5 } ), } ), 52: mk( 'pop-art-gallery', 'Pop Art gallery', 'White painted walls · fluorescent daylight · polished concrete', { wall: 'painted-flat', ceiling: 'painted-flat', floor: 'concrete-polished' }, { wall: '#a8a8a8', ceiling: '#b3b3b3', floor: '#d8d8d8', trim: '#cccccc' }, { details: 'white-cube', // Lit like the basilica: shared lights held back so the walls read as // surfaces rather than blowing out. Window shafts are not scaled. lightScale: 0.3, titleColor: '#222222', ambient: 0.75, warmLight: '#ffffff', fog: '#1a1a1a', background: '#101010', windows: windows( { wall: 'back', x: 0, y: 2.6, width: 3.0, height: 2.0, style: 'factory', lightColor: '#ffffff', lightIntensity: 5.5 }, { wall: 'ceiling', x: 0, y: 0, width: 5.0, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.0 } ), trackLights: 0.6, doorWood: ['#888888', '#999999', '#777777'], } ), }; function blendAccent(style: MovementInteriorStyle, accentHex?: string): MovementInteriorStyle { if (!accentHex) return style; const w = 0.08; const mix = (a: string, b: string) => { const pa = parseInt(a.replace('#', ''), 16); const pb = parseInt(b.replace('#', ''), 16); const r = Math.round(((pa >> 16) & 255) * w + ((pb >> 16) & 255) * (1 - w)); const g = Math.round(((pa >> 8) & 255) * w + ((pb >> 8) & 255) * (1 - w)); const bl = Math.round((pa & 255) * w + (pb & 255) * (1 - w)); return `#${((r << 16) | (g << 8) | bl).toString(16).padStart(6, '0')}`; }; return { ...style, tints: { ...style.tints, wall: mix(accentHex, style.tints.wall), wallSide: style.tints.wallSide ? mix(accentHex, style.tints.wallSide) : undefined, trim: mix(accentHex, style.tints.trim), }, }; } /** * Styles were authored with legacy ids 27–52; gallery_dev uses 1–26. * Northern (5) / High Renaissance (6) are swapped vs the legacy sequence. */ const DB_ID_TO_STYLE_KEY: Record = { 1: 27, 2: 28, 3: 29, 4: 30, 5: 32, 6: 31, 7: 33, 8: 34, 9: 35, 10: 36, 11: 37, 12: 38, 13: 39, 14: 40, 15: 41, 16: 42, 17: 43, 18: 44, 19: 45, 20: 46, 21: 47, 22: 48, 23: 49, 24: 50, 25: 51, 26: 52, }; /** Fallback name-based resolver for movements not in the catalog. */ function fallbackByName(movement: ArtMovement & { era_name?: string }): MovementInteriorStyle { const n = movement.name.toLowerCase(); const era = (movement.era_name ?? '').toLowerCase(); if (n.includes('byzantine')) return BY_MOVEMENT_ID[28]; if (n.includes('gothic')) return BY_MOVEMENT_ID[29]; if (n.includes('renaissance')) return BY_MOVEMENT_ID[31]; if (n.includes('baroque') || n.includes('rococo')) return BY_MOVEMENT_ID[34]; if (era.includes('medieval')) return BY_MOVEMENT_ID[29]; if (n.includes('impression')) return BY_MOVEMENT_ID[39]; if (era.includes('modern') || era.includes('contemporary')) return BY_MOVEMENT_ID[52]; return BY_MOVEMENT_ID[36]; } export function resolveMovementInteriorStyle( movement: ArtMovement & { era_name?: string } ): MovementInteriorStyle { const styleKey = DB_ID_TO_STYLE_KEY[movement.id] ?? movement.id; const base = BY_MOVEMENT_ID[styleKey] ?? fallbackByName(movement); return blendAccent(base, movement.color); } /** @deprecated use surfaces.floor — kept for transitional imports */ export type GalleryFloorKind = string;