```python
import bpy, math, random
from mathutils import Vector, Euler

random.seed(7)
C = bpy.context
S = C.scene

# ---------- cleanup ----------
for o in list(bpy.data.objects):
    bpy.data.objects.remove(o, do_unlink=True)
for c in list(bpy.data.collections):
    if c.name != "Scene Collection":
        bpy.data.collections.remove(c)

S.render.engine = 'CYCLES'
S.cycles.samples = 64
S.render.resolution_x = 1536
S.render.resolution_y = 1536
S.view_settings.view_transform = 'AgX'
S.view_settings.look = 'Medium High Contrast'

def new_mat(name):
    m = bpy.data.materials.new(name)
    m.use_nodes = True
    return m

def link(nt, a, b):
    nt.links.new(a, b)

# ---------- WORLD ----------
w = bpy.data.worlds.new("Gallery")
S.world = w
w.use_nodes = True
w.node_tree.nodes["Background"].inputs[0].default_value = (0.0, 0.0, 0.0, 1)
w.node_tree.nodes["Background"].inputs[1].default_value = 0.0

# ---------- CONCRETE MATERIAL ----------
def concrete_mat():
    m = new_mat("Concrete")
    nt = m.node_tree
    nt.nodes.clear()
    out = nt.nodes.new("ShaderNodeOutputMaterial")
    bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
    bsdf.inputs["Roughness"].default_value = 0.75
    bsdf.inputs["Base Color"].default_value = (0.12,0.12,0.13,1)
    tex = nt.nodes.new("ShaderNodeTexNoise")
    tex.inputs["Scale"].default_value = 18.0
    tex.inputs["Detail"].default_value = 8.0
    ramp = nt.nodes.new("ShaderNodeValToRGB")
    ramp.color_ramp.elements[0].position = 0.4
    ramp.color_ramp.elements[0].color = (0.08,0.08,0.09,1)
    ramp.color_ramp.elements[1].color = (0.18,0.18,0.19,1)
    bump = nt.nodes.new("ShaderNodeBump")
    bump.inputs["Strength"].default_value = 0.15
    link(tex.outputs["Fac"], ramp.inputs["Fac"])
    link(ramp.outputs["Color"], bsdf.inputs["Base Color"])
    link(tex.outputs["Fac"], bump.inputs["Height"])
    link(bump.outputs["Normal"], bsdf.inputs["Normal"])
    link(bsdf.outputs[0], out.inputs[0])
    return m

concrete = concrete_mat()

# ---------- ROOM ----------
def add_box(name, size, loc, mat=None):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    o = C.active_object
    o.name = name
    o.scale = size
    if mat: o.data.materials.append(mat)
    return o

floor = add_box("Floor", (10,10,0.1), (0,0,-0.05), concrete)
back = add_box("BackWall", (10,0.2,6), (0,5,3), concrete)
left = add_box("LeftWall", (0.2,10,6), (-6,0,3), concrete)
right = add_box("RightWall", (0.2,10,6), (6,0,3), concrete)
ceil = add_box("Ceiling", (10,10,0.1), (0,0,6.05), concrete)

# ---------- PEDESTAL ----------
stone = new_mat("DarkStone")
nt = stone.node_tree
nt.nodes.clear()
out = nt.nodes.new("ShaderNodeOutputMaterial")
bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
bsdf.inputs["Base Color"].default_value = (0.02,0.02,0.022,1)
bsdf.inputs["Roughness"].default_value = 0.35
link(bsdf.outputs[0], out.inputs[0])

bpy.ops.mesh.primitive_cube_add(size=1, location=(0,0,0.5))
ped = C.active_object
ped.name = "Pedestal"
ped.scale = (0.6,0.6,1.0)
bpy.ops.object.modifier_add(type='BEVEL')
ped.modifiers["Bevel"].width = 0.02
ped.data.materials.append(stone)

# brass material
brass = new_mat("Brass")
nt = brass.node_tree
nt.nodes.clear()
out = nt.nodes.new("ShaderNodeOutputMaterial")
bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
bsdf.inputs["Base Color"].default_value = (0.55,0.38,0.12,1)
bsdf.inputs["Metallic"].default_value = 1.0
bsdf.inputs["Roughness"].default_value = 0.35
noise = nt.nodes.new("ShaderNodeTexNoise")
noise.inputs["Scale"].default_value = 40.0
ramp = nt.nodes.new("ShaderNodeValToRGB")
ramp.color_ramp.elements[0].color=(0.02,0.02,0.02,1)
ramp.color_ramp.elements[1].color=(0.06,0.05,0.02,1)
link(noise.outputs["Fac"], ramp.inputs["Fac"])
link(ramp.outputs["Color"], bsdf.inputs["Roughness"])
link(bsdf.outputs[0], out.inputs[0])

# placard
bpy.ops.mesh.primitive_plane_add(size=0.5, location=(0, -0.32, 1.15))
plc = C.active_object
plc.name = "Placard"
plc.rotation_euler = (math.radians(80), 0, 0)
plc.scale = (0.9, 0.35, 1)
plc.data.materials.append(brass)

bpy.ops.object.text_add(location=(-0.4, -0.335, 1.2))
txt = C.active_object
txt.rotation_euler = (math.radians(90), 0, 0)
txt.data.body = "REALITY REPROJECTION"
txt.data.size = 0.075
txt.data.extrude = 0.002
txt.scale = (1, 1, 1)
dark_mat = new_mat("TextDark")
dark_mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.01,0.01,0.01,1)
txt.data.materials.append(dark_mat)

# ---------- VELVET ROPE CORDON ----------
velvet = new_mat("Velvet")
nt = velvet.node_tree
nt.nodes.clear()
out = nt.nodes.new("ShaderNodeOutputMaterial")
bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
bsdf.inputs["Base Color"].default_value = (0.35,0.01,0.02,1)
bsdf.inputs["Roughness"].default_value = 0.9
link(bsdf.outputs[0], out.inputs[0])

def stanchion(loc):
    bpy.ops.mesh.primitive_cylinder_add(radius=0.03, depth=0.95, location=(loc[0], loc[1], 0.475))
    pole = C.active_object
    pole.data.materials.append(brass)
    bpy.ops.mesh.primitive_uv_sphere_add(radius=0.06, location=(loc[0], loc[1], 0.95))
    cap = C.active_object
    cap.data.materials.append(brass)
    bpy.ops.mesh.primitive_cylinder_add(radius=0.15, depth=0.02, location=(loc[0], loc[1], 0.02))
    base = C.active_object
    base.data.materials.append(brass)
    return pole

pts = []
R = 1.6
n = 6
for i in range(n):
    a = math.radians(360*i/n + 30)
    pts.append((R*math.cos(a), R*math.sin(a)))
    stanchion(pts[-1])

for i in range(n):
    p0 = pts[i]
    p1 = pts[(i+1)%n]
    mid = ((p0[0]+p1[0])/2, (p0[1]+p1[1])/2, 0.9)
    dx, dy = p1[0]-p0[0], p1[1]-p0[1]
    length = math.hypot(dx,dy)
    ang = math.atan2(dy,dx)
    bpy.ops.mesh.primitive_cylinder_add(radius=0.025, depth=length*0.98, location=mid)
    rope = C.active_object
    rope.rotation_euler = (0, math.radians(90), ang)
    # sag
    bpy.ops.object.modifier_add(type='SIMPLE_DEFORM')
    rope.modifiers["SimpleDeform"].deform_method = 'BEND'
    rope.modifiers["SimpleDeform"].angle = math.radians(-20)
    rope.data.materials.append(velvet)

# ---------- GLASS MATERIAL ----------
def glass_mat(name, ior=1.52, color=(1,1,1,1)):
    m = new_mat(name)
    nt = m.node_tree
    nt.nodes.clear()
    out = nt.nodes.new("ShaderNodeOutputMaterial")
    bsdf = nt.nodes.new("ShaderNodeBsdfGlass")
    bsdf.inputs["IOR"].default_value = ior
    bsdf.inputs["Color"].default_value = color
    bsdf.inputs["Roughness"].default_value = 0.02
    link(bsdf.outputs[0], out.inputs[0])
    m.blend_method = 'BLEND' if hasattr(m,'blend_method') else None
    return m

lens_glass = glass_mat("LensGlass", 1.52, (0.96,0.98,1.0,1))
crystal_glass = glass_mat("CrystalGlass", 1.77, (0.9,1.0,0.95,1))

# ---------- DEVICE ----------
device_origin = Vector((0,0,1.35))
axis_tilt = math.radians(28)

dev = bpy.data.objects.new("DeviceRoot", None)
dev.location = device_origin
dev.rotation_euler = (0,axis_tilt,0)
C.collection.objects.link(dev)

# gimbal rings
def add_ring(radius, thickness, rot, mat, parent):
    bpy.ops.mesh.primitive_torus_add(major_radius=radius, minor_radius=thickness,
                                      location=(0,0,0))
    r = C.active_object
    r.rotation_euler = rot
    r.data.materials.append(mat)
    r.parent = parent
    r.matrix_parent_inverse = parent.matrix_world.inverted()
    return r

ring1 = add_ring(0.32, 0.012, (math.radians(15),0,0), brass, dev)
ring2 = add_ring(0.24, 0.010, (0,math.radians(70),math.radians(20)), brass, dev)

# lens stack along local Z axis
lens_specs = [(0.18,0.02,0.0),(0.13,0.02,0.14),(0.09,0.02,0.25)]
for i,(rad,depth,zoff) in enumerate(lens_specs):
    bpy.ops.mesh.primitive_cylinder_add(radius=rad, depth=depth, location=(0,0,zoff))
    lens = C.active_object
    lens.name = f"Lens_{i}"
    lens.data.materials.append(lens_glass)
    lens.parent = dev
    lens.matrix_parent_inverse = dev.matrix_world.inverted()

# crystal core - beveled icosphere for facets
bpy.ops.mesh.primitive_ico_sphere_add(radius=0.09, subdivisions=1, location=(0,0,0))
crystal = C.active_object
crystal.name = "CrystalCore"
crystal.data.materials.append(crystal_glass)
crystal.parent = dev
crystal.matrix_parent_inverse = dev.matrix_world.inverted()

# small internal emissive glints (points inside crystal)
glint_mat = new_mat("Glint")
gnt = glint_mat.node_tree
gnt.nodes.clear()
gout = gnt.nodes.new("ShaderNodeOutputMaterial")
gem = gnt.nodes.new("ShaderNodeEmission")
gem.inputs["Color"].default_value = (1.0,0.9,0.7,1)
gem.inputs["Strength"].default_value = 40.0
link(gem.outputs[0], gout.inputs[0])
for i in range(4):
    ang = i*1.4
    p = (0.03*math.cos(ang), 0.03*math.sin(ang), 0.02*i-0.02)
    bpy.ops.mesh.primitive_ico_sphere_add(radius=0.008, subdivisions=1, location=p)
    gl = C.active_object
    gl.data.materials.append(glint_mat)
    gl.parent = dev
    gl.matrix_parent_inverse = dev.matrix_world.inverted()

# support / brass base for device
bpy.ops.mesh.primitive_cylinder_add(radius=0.12, depth=0.08, location=(0,0,1.05))
supp = C.active_object
supp.data.materials.append(brass)

# ---------- BEAM DIRECTION ----------
beam_dir = Vector((0, math.sin(axis_tilt), math.cos(axis_tilt)))
beam_start = device_origin + beam_dir*0.3
wall_y = 5.0
t = (wall_y - device_origin.y)/beam_dir.y
hit_point = device_origin + beam_dir*t

# ---------- VOLUMETRIC BEAM ----------
beam_len = (hit_point - beam_start).length
mid = (beam_start + hit_point)/2
bpy.ops.mesh.primitive_cone_add(radius1=0.03, radius2=0.55, depth=beam_len, location=mid)
beam = C.active_object
beam.name = "VolBeam"
# orient cone along beam_dir
zaxis = Vector((0,0,1))
rot_quat = zaxis.rotation_difference(beam_dir)
beam.rotation_euler = rot_quat.to_euler()

beam_mat = new_mat("BeamVolume")
bnt = beam_mat.node_tree
bnt.nodes.clear()
bout = bnt.nodes.new("ShaderNodeOutputMaterial")
vol = bnt.nodes.new("ShaderNodeVolumeScatter")
vol.inputs["Color"].default_value = (0.7,0.85,1.0,1)
vol.inputs["Density"].default_value = 0.35
link(vol.outputs[0], bout.inputs["Volume"])
beam.data.materials.append(beam_mat)

# ---------- PROJECTION ON WALL ----------
bpy.ops.mesh.primitive_plane_add(size=1.6, location=(hit_point.x, wall_y-0.01, hit_point.z))
proj = C.active_object
proj.name = "Projection"
proj.rotation_euler = (math.radians(90),0,0)
proj.scale = (1.3, 0.9, 1)

proj_mat = new_mat("ProjectionMat")
pnt = proj_mat.node_tree
pnt.nodes.clear()
pout = pnt.nodes.new("ShaderNodeOutputMaterial")
emis = pnt.nodes.new("ShaderNodeEmission")
emis.inputs["Strength"].default_value = 6.0

tc = pnt.nodes.new("ShaderNodeTexCoord")
grad = pnt.nodes.new("ShaderNodeTexGradient")
grad.gradient_type = 'SPHERICAL'
mapp = pnt.nodes.new("ShaderNodeMapping")
mapp.inputs["Scale"].default_value = (1.4,1.0,1.0)
ramp2 = pnt.nodes.new("ShaderNodeValToRGB")
ramp2.color_ramp.elements[0].position = 0.0
ramp2.color_ramp.elements[0].color = (0.85, 0.15, 0.9, 1)
mid_el = ramp2.color_ramp.elements.new(0.55)
mid_el.color = (0.05, 0.9, 0.75, 1)
ramp2.color_ramp.elements[-1].position = 1.0
ramp2.color_ramp.elements[-1].color = (1.0, 0.6, 0.05, 1)

noise2 = pnt.nodes.new("ShaderNodeTexNoise")
noise2.inputs["Scale"].default_value = 3.0
mixnode = pnt.nodes.new("ShaderNodeMixRGB")
mixnode.blend_type = 'ADD'
mixnode.inputs["Fac"].default_value = 0.3

# radial mask for ellipse falloff + chromatic fringe
sep = pnt.nodes.new("ShaderNodeSeparateXYZ")
lenmath = pnt.nodes.new("ShaderNodeVectorMath")
lenmath.operation = 'LENGTH'
falloff = pnt.nodes.new("ShaderNodeMath")
falloff.operation = 'SUBTRACT'
falloff.inputs[1].default_value = 0.0
mask_ramp = pnt.nodes.new("ShaderNodeValToRGB")
mask_ramp.color_ramp.elements[0].position = 0.35
mask_ramp.color_ramp.elements[0].color = (1,1,1,1)
mask_ramp.color_ramp.elements[1].position = 0.5
mask_ramp.color_ramp.elements[1].color = (0,0,0,0)
# fringe color ramp - rim tinted spectrally
fringe_ramp = pnt.nodes.new("ShaderNodeValToRGB")
fringe_ramp.color_ramp.elements[0].position = 0.30
fringe_ramp.color_ramp.elements[0].color = (0.2,0.6,1.0,1)
fmid = fringe_ramp.color_ramp.elements.new(0.42)
fmid.color = (1.0,1.0,1.0,1)
fend = fringe_ramp.color_ramp.elements.new(0.5)
fend.color = (1.0,0.15,0.1,1)

link(tc.outputs["Object"], mapp.inputs["Vector"])
link(mapp.outputs["Vector"], noise2.inputs["Vector"])
link(mapp.outputs["Vector"], grad.inputs["Vector"] if "Vector" in grad.inputs else mapp.outputs["Vector"])
link(grad.outputs["Fac"], ramp2.inputs["Fac"])
link(noise2.outputs["Fac"], mixnode.inputs["Color2"])
link(ramp2.outputs["Color"], mixnode.inputs["Color1"])

link(mapp.outputs["Vector"], sep.inputs[0])
link(mapp.outputs["Vector"], lenmath.inputs[0])
link(lenmath.outputs["Value"], mask_ramp.inputs["Fac"])
link(lenmath.outputs["Value"], fringe_ramp.inputs["Fac"])

combine_mix = pnt.nodes.new("ShaderNodeMixRGB")
combine_mix.blend_type = 'MIX'
combine_mix.inputs["Fac"].default_value = 0.4
link(mixnode.outputs["Color"], combine_mix.inputs["Color1"])
link(fringe_ramp.outputs["Color"], combine_mix.inputs["Color2"])

final_mult = pnt.nodes.new("ShaderNodeMixRGB")
final_mult.blend_type = 'MULTIPLY'
final_mult.inputs["Fac"].default_value = 1.0
link(combine_mix.outputs["Color"], final_mult.inputs["Color1"])
link(mask_ramp.outputs["Color"], final_mult.inputs["Color2"])

link(final_mult.outputs["Color"], emis.inputs["Color"])
link(emis.outputs[0], pout.inputs[0])
proj.data.materials.append(proj_mat)

# ---------- CAUSTIC SPILL ON PEDESTAL ----------
caustic_mat = new_mat("Caustic")
cnt = caustic_mat.node_tree
cnt.nodes.clear()
cout = cnt.nodes.new("ShaderNodeOutputMaterial")
cemis = cnt.nodes.new("ShaderNodeEmission")
cvor = cnt.nodes.new("ShaderNodeTexVoronoi")
cvor.inputs["Scale"].default_value = 12.0
cramp = cnt.nodes.new("ShaderNodeValToRGB")
cramp.color_ramp.elements[0].color = (0,0,0,1)
cramp.color_ramp.elements[1].color = (0.6,0.85,1.0,1)
link(cvor.outputs["Distance"], cramp.inputs["Fac"])
cemis.inputs["Strength"].default_value = 3.0
link(cramp.outputs["Color"], cemis.inputs["Color"])
link(cemis.outputs[0], cout.inputs[0])

bpy.ops.mesh.primitive_plane_add(size=0.55, location=(0,0,1.001))
caustic_plane = C.active_object
caustic_plane.name = "CausticSpill"
caustic_plane.data.materials.append(caustic_mat)

# ---------- LIGHTING ----------
def spot(loc, target, energy, size=25, blend=0.3, color=(1,0.95,0.85)):
    bpy.ops.object.light_add(type='SPOT', location=loc)
    l = C.active_object
    l.data.energy = energy
    l.data.spot_size = math.radians(size)
    l.data.spot_blend = blend
    l.data.color = color
    dirv = (Vector(target) - Vector(loc))
    l.rotation_euler = dirv.to_track_quat('-Z','Y').to_euler()
    return l

spot((0.6, -0.4, 4.0), device_origin, 900, 22, 0.25)
spot((-0.6, 0.6, 4.0), device_origin, 700, 25, 0.3)
spot((0.0, -1.0, 3.5), (0,-0.32,1.15), 300, 18, 0.2)

# faint fill so hall isn't pure void
bpy.ops.object.light_add(type='AREA', location=(0,4.5,4))
fill = C.active_object
fill.data.energy = 15
fill.data.size = 4
fill.rotation_euler = (math.radians(90),0,0)

# projection glow acting as light source in the room (visible bounce)
bpy.ops.object.light_add(type='AREA', location=(hit_point.x, wall_y-0.3, hit_point.z))
projlight = C.active_object
projlight.data.energy = 40
projlight.data.size = 1.2
projlight.data.color = (0.5,0.8,1.0)
projlight.rotation_euler = (math.radians(90),0,0)

# ---------- CAMERA ----------
bpy.ops.object.camera_add(location=(1.9, -2.3, 1.85))
cam = C.active_object
cam.data.lens = 40
look_target = Vector((0.0, 1.5, 1.15))
direction = look_target - cam.location
cam.rotation_euler = direction.to_track_quat('-Z','Y').to_euler()
S.camera = cam

# ---------- SAVE ----------
import os
outpath = bpy.path.abspath("//reality_reprojection.blend")
try:
    bpy.ops.wm.save_as_mainfile(filepath=outpath)
except Exception as e:
    print("Save failed:", e)
```