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

random.seed(12)

# ---------- reset ----------
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for datablocks in (bpy.data.meshes, bpy.data.curves, bpy.data.materials, bpy.data.cameras, bpy.data.lights):
    pass

# ---------- materials ----------
def mat(name, color, rough=0.8, metallic=0.0, emission=None, strength=0):
    m = bpy.data.materials.new(name)
    m.diffuse_color = (*color, 1)
    m.use_nodes = True
    bs = m.node_tree.nodes.get("Principled BSDF")
    bs.inputs["Base Color"].default_value = (*color, 1)
    bs.inputs["Roughness"].default_value = rough
    bs.inputs["Metallic"].default_value = metallic
    if emission:
        bs.inputs["Emission Color"].default_value = (*emission, 1)
        bs.inputs["Emission Strength"].default_value = strength
    return m

stone = mat("weathered pale sandstone", (0.43,0.40,0.32), .92)
stone_light = mat("sunlit cut stone", (0.62,0.57,0.44), .86)
stone_dark = mat("deep stone joints", (0.12,0.11,0.09), 1)
moss = mat("moss and mineral green", (0.12,0.28,0.09), 1)
moss_light = mat("yellow green leaves", (0.36,0.58,0.08), .8)
leaf = mat("deep emerald foliage", (0.035,0.20,0.07), .9)
fernmat = mat("fern fronds", (0.20,0.46,0.10), .8)
iron = mat("paint peeled iron", (0.055,0.075,0.065), .65, .65)
rust = mat("rust", (0.30,0.095,0.035), .9)
glass = mat("aged blue glass", (0.12,0.35,0.48), .22)
glass.node_tree.nodes["Principled BSDF"].inputs["Transmission Weight"].default_value = .35
gold = mat("clock brass", (0.65,0.42,0.12), .28, .7)
clockface = mat("ivory clock face", (0.87,0.79,0.59), .58)
dark = mat("unlit interiors", (0.012,0.018,0.016), 1)
amber = mat("amber CRT phosphor", (0.8,0.25,0.035), .25, emission=(1.0,.16,.015), strength=8)
green_emit = mat("phosphor green", (.04,.8,.12), .3, emission=(.03,1,.08), strength=7)
red_emit = mat("signal red", (.8,.015,.005), .25, emission=(1,.01,.002), strength=10)
blue_sky = mat("roof sky glass", (.18,.45,.65), .18)
water = mat("rain puddles", (.06,.20,.24), .08, .25)

def cube(name, loc, scale, material, bevel=0.0):
    bpy.ops.mesh.primitive_cube_add(location=loc)
    o=bpy.context.object; o.name=name; o.scale=(scale[0]/2,scale[1]/2,scale[2]/2)
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    if material: o.data.materials.append(material)
    if bevel:
        mod=o.modifiers.new("worn softened edges","BEVEL"); mod.width=bevel; mod.segments=2
    return o

def cyl(name, loc, radius, depth, material, rot=(0,0,0), verts=16, bevel=0):
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=radius, depth=depth, location=loc, rotation=rot)
    o=bpy.context.object; o.name=name
    if material:o.data.materials.append(material)
    if bevel:
        mod=o.modifiers.new("edge wear","BEVEL"); mod.width=bevel; mod.segments=2
    return o

def uv(name, loc, scale, material):
    bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=1, location=loc)
    o=bpy.context.object;o.name=name;o.scale=scale
    if material:o.data.materials.append(material)
    return o

def torus(name, loc, major, minor, material, rot=(0,0,0)):
    bpy.ops.mesh.primitive_torus_add(major_radius=major, minor_radius=minor, major_segments=32, minor_segments=8, location=loc, rotation=rot)
    o=bpy.context.object;o.name=name;o.data.materials.append(material);return o

def text_obj(name, body, loc, size, material, extrude=.015, align='CENTER'):
    cu=bpy.data.curves.new(name,'FONT'); cu.body=body; cu.align_x=align; cu.size=size; cu.extrude=extrude; cu.bevel_depth=.004
    o=bpy.data.objects.new(name,cu);bpy.context.collection.objects.link(o);o.location=loc;o.rotation_euler=(math.pi/2,0,0);o.data.materials.append(material);return o

def beam_between(name,a,b,r,material):
    a,b=Vector(a),Vector(b); d=b-a
    o=cyl(name,(a+b)/2,r,d.length,material,verts=8)
    o.rotation_mode='QUATERNION';o.rotation_quaternion=d.to_track_quat('Z','Y');return o

# ---------- ground, cutting and tracks ----------
cube("wide rain-darkened ground",(0,5,-1.2),(42,48,2),stone_dark)
# elevated platforms / retaining walls
cube("left monumental retaining wall",(-8,7,3.0),(2.0,38,7),stone, .18)
cube("right monumental retaining wall",(8,7,3.0),(2.0,38,7),stone, .18)
cube("left platform",(-4.2,7,2.25),(5.5,37,.55),stone_light,.12)
cube("right platform",(4.2,7,2.25),(5.5,37,.55),stone_light,.12)
# block courses and masonry accents
for side in (-1,1):
    for z in [0.3,1.7,3.1,4.5]:
        for y in range(-8,25,4):
            off=1.5 if (y//4)%2 else 0
            cube("oversized ashlar block",(side*8.0,y+off,z),(1.7,3.6,1.15),stone_light,.08)
# rails, sleepers
for x in (-2.1,-0.4,0.4,2.1):
    cube("sunlit steel rail",(x,7,0.05),(.13,36,.18),iron,.02)
for y in range(-10,26,1):
    cube("wooden sleeper",(0,y,-.10),(5.1,.30,.30),rust,.04)
# platform edge coping
for x in (-5.9,5.9):
    cube("platform coping",(x,7,2.65),(.32,37,.32),stone_light,.05)
# puddles
for i in range(14):
    x=random.uniform(-6,6); y=random.uniform(-8,23)
    cube("shallow reflective puddle",(x,y,0.13),(random.uniform(.5,2.4),random.uniform(.3,1.4),.025),water,.08)

# ---------- station hall ----------
cube("grand booking hall rear facade",(0,4.0,7.0),(19,1.1,10),stone,.16)
# masonry blocks on facade
for z in [4.0,5.5,7.0,8.5,10.0]:
    for x in range(-8,9,3):
        cube("facade stone slab",(x+(z%3)*.2,3.38,z),(2.7,.38,1.15),stone_light,.06)
# monumental columns
for x in (-8,-5.3,-2.7,2.7,5.3,8):
    cube("monumental square column",(x,3.15,7.3),(0.75,1.1,7.1),stone_light,.12)
    cube("column capital",(x,3.12,10.7),(1.3,1.45,.42),stone_light,.08)
# dark tall windows and arched tops
for x in (-6.7,-4.0,-1.35,1.35,4.0,6.7):
    cube("broken tall arched window",(x,3.28,7.6),(1.45,.10,4.1),dark,.03)
    cyl("round window arch",(x,3.27,9.55),.72,.11,dark,rot=(math.pi/2,0,0),verts=24)
    # a few remaining panes
    if x in (-4.0,4.0):
        cube("remaining blue glass",(x,3.18,7.7),(.72,.06,2.1),glass)
# canopy / roof framing
for x in (-8,-4,0,4,8):
    beam_between("iron roof truss",(x,3.7,11),(x,9.0,14),.16,iron)
    beam_between("iron roof crossbar",(x,3.7,11),(x,0.0,11),.13,iron)
for y in (0,4,8,12):
    beam_between("long Victorian roof beam",(-9,y,11),(9,y,11),.16,iron)
# broken glass panels
for x in (-6,-2,2,6):
    cube("remaining roof glazing",(x,5.8,12.1),(3.0,6,.08),blue_sky)
# front canopy
for x in (-8,-4,0,4,8):
    beam_between("decorative canopy support",(x,2.2,10),(x,0.0,7.8),.12,iron)
cube("canopy fascia",(0,1.8,10),(19,.35,.35),iron)

# stairs descending to track
for side in (-1,1):
    for i in range(8):
        cube("broad worn stone stair",(side*(3.0-i*.12),1.0-i*.48,2.45-i*.27),(3.3,.7,.28),stone_light,.05)
# railings
for side in (-1,1):
    for x in (side*1.7,side*4.8):
        for y in (-1,2,5):
            beam_between("old iron railing",(x,y,3.0),(x,y,4.7),.07,iron)
        beam_between("railing handrail",(x,-1,4.7),(x,8,4.7),.08,iron)

# ---------- clock tower ----------
cube("clock tower shaft",(0,5.0,14),(6.0,5.0,18),stone_light,.2)
# tower corner pilasters
for x in (-2.75,2.75):
    for y in (3.0,7.0):
        cube("tower corner buttress",(x,y,14),(0.65,.75,18),stone,.1)
# tower upper dark openings
for z in (17.2,19.0):
    for x in (-1.65,1.65):
        cube("tower belfry opening",(x,2.43,z),(1.0,.12,1.35),dark,.04)
# clock front disk oriented to front (-Y)
cyl("great ivory clock face",(0,2.34,20.0),2.15,.22,clockface,rot=(math.pi/2,0,0),verts=48)
torus("clock iron rim",(0,2.18,20.0),2.22,.16,iron,rot=(math.pi/2,0,0))
for i in range(12):
    a=math.radians(i*30); x=math.sin(a)*1.78; z=20+math.cos(a)*1.78
    cyl("clock hour mark",(x,2.13,z),.09,.10,iron,rot=(math.pi/2,0,0),verts=8)
# hands as front-facing bars
h1=cube("clock minute hand",(0,2.02,20.8),(.12,.10,1.4),iron);h1.rotation_euler[1]=-.35
h2=cube("clock hour hand",(-.35,2.01,19.65),(.15,.11,1.0),iron);h2.rotation_euler[1]=1.0
cyl("clock center",(0,1.96,20),.18,.18,gold,rot=(math.pi/2,0,0),verts=16)
# tower cap
cube("tower cornice",(0,5,23.2),(6.8,5.7,.55),stone_light,.1)
cube("tower roof",(0,5,24.4),(5.2,4.2,2.0),stone,.1)
# heritage sign
cube("heritage sign backing",(0,2.62,11.0),(11.7,.18,.85),iron,.04)
text_obj("clearly readable heritage wordmark","RAVENSTHWAITE & DISTRICT RAILWAY Co.",(0,2.48,10.86),.43,gold,.012)

# departures board
cube("departures board",(0,1.95,8.8),(7.4,.35,1.7),iron,.08)
text_obj("departures lettering","DEPARTURES   14:25  NORTH", (0,1.72,8.75),.37,green_emit,.012)

# ticket office CRT
cube("ticket office counter",(-6.0,2.3,6.0),(3.0,1.0,.65),stone_light,.08)
cube("amber CRT terminal",(-6.0,1.72,7.0),(1.25,.65,1.0),dark,.12)
cube("curved glowing CRT screen",(-6.0,1.36,7.08),(.88,.05,.62),amber,.11)
text_obj("CRT text","R&D",(-6.0,1.30,7.03),.22,clockface,.005)
# green staff lamp
cyl("phosphor green staff lamp",(-8.0,2.0,6.7),.22,.55,green_emit,verts=20)
cube("staff door",-8.0 and (-8.0,3.25,7.2),(1.6,.1,3.2),dark,.04)

# ---------- distant signal ----------
cyl("signal post",(2.1,22,3.2),.12,5,iron,verts=10)
cyl("single red railway signal",(2.1,22,5.8),.35,.22,red_emit,rot=(math.pi/2,0,0),verts=24)

# ---------- vegetation ----------
# vines as bevel curves
def vine(points, material=moss, thick=.07):
    cu=bpy.data.curves.new("climbing ivy vine","CURVE");cu.dimensions='3D';cu.bevel_depth=thick;cu.bevel_resolution=2
    sp=cu.splines.new('BEZIER');sp.bezier_points.add(len(points)-1)
    for p,co in zip(sp.bezier_points,points):
        p.co=co;p.handle_left_type='AUTO';p.handle_right_type='AUTO'
    ob=bpy.data.objects.new("hanging ivy",cu);bpy.context.collection.objects.link(ob);ob.data.materials.append(material)
    return ob
for x in (-2.3,2.1,7.2,-7.4):
    vine([(x,2.55,3),(x+.3,2.45,8),(x-.5,2.6,13),(x+.2,3.2,18)],moss,.12)
for i in range(18):
    x=random.uniform(-9,9); z=random.uniform(3,11)
    vine([(x,2.6,z),(x+random.uniform(-.5,.5),2.3,z-1),(x+random.uniform(-.8,.8),2.1,z-2)],moss_light,.045)
# trees around edges
for x,y,h in [(-14,3,16),(14,8,19),(-13,17,14),(13,22,16),(-11,29,15),(12,-5,13)]:
    cyl("ancient tree trunk",(x,y,h/2),.75,h,rust,verts=12)
    for j in range(7):
        uv("luminous tree crown",(x+random.uniform(-3,3),y+random.uniform(-2.5,2.5),h+random.uniform(-1,3)),(random.uniform(2,4),random.uniform(2,4),random.uniform(2,3.5)),leaf if j%2 else moss_light)
    for j in range(3):
        beam_between("overhanging branch",(x,y,h*.7),(x+random.uniform(-5,5),y+random.uniform(-3,3),h+random.uniform(1,5)),.22,rust)
# shrubs and ferns along tracks
for i in range(75):
    x=random.uniform(-7,7); y=random.uniform(-9,25)
    z=random.uniform(.1,2.0)
    if abs(x)<2.5 and random.random()<.45: continue
    uv("dense reclaimed shrub",(x,y,z+random.uniform(.3,1.2)),(random.uniform(.25,.7),random.uniform(.25,.7),random.uniform(.4,1.3)),fernmat if i%3 else moss_light)
# scattered rubble
for i in range(30):
    x=random.uniform(-7,7); y=random.uniform(-8,24)
    o=cube("fallen masonry rubble",(x,y,.3),(random.uniform(.2,.7),random.uniform(.2,.8),random.uniform(.15,.5)),stone_light,.04)
    o.rotation_euler=(random.random(),random.random(),random.random())

# ---------- atmospheric clouds ----------
for x,y,z in [(-18,16,27),(-8,24,29),(10,20,28),(18,3,30)]:
    for j in range(5):
        uv("large white summer cloud",(x+j*1.7-3,y+random.uniform(-1,1),z+random.uniform(-.5,.7)),(2.5,1.5,1.0),mat("cloud white",(0.9,.94,.98),1))

# ---------- lighting/world ----------
world=bpy.context.scene.world
world.use_nodes=True
world.node_tree.nodes["Background"].inputs["Color"].default_value=(0.055,0.16,0.32,1)
world.node_tree.nodes["Background"].inputs["Strength"].default_value=.35
bpy.ops.object.light_add(type='SUN', location=(-10,-12,25))
sun=bpy.context.object; sun.name="warm summer sun";sun.rotation_euler=(math.radians(28),math.radians(-22),math.radians(-28));sun.data.energy=3.2;sun.data.angle=math.radians(12);sun.data.color=(1.0,.78,.52)
bpy.ops.object.light_add(type='AREA', location=(0,-5,16))
area=bpy.context.object;area.name="soft skylight";area.data.energy=900;area.data.shape='DISK';area.data.size=18;area.data.color=(.55,.75,1.0)
area.rotation_euler=(math.radians(35),0,0)
# glowing CRT spill
bpy.ops.object.light_add(type='POINT', location=(-6,1.8,7)); bpy.context.object.data.energy=45; bpy.context.object.data.color=(1,.18,.03)
bpy.ops.object.light_add(type='POINT', location=(-8,2,6.8)); bpy.context.object.data.energy=30; bpy.context.object.data.color=(.05,1,.12)
bpy.ops.object.light_add(type='POINT', location=(2.1,21.5,5.8)); bpy.context.object.data.energy=28; bpy.context.object.data.color=(1,.01,.005)

# ---------- camera ----------
bpy.ops.object.camera_add(location=(21,-29,16))
cam=bpy.context.object;cam.name="presentation camera"
def look(obj, target):
    obj.rotation_euler=(Vector(target)-obj.location).to_track_quat('-Z','Y').to_euler()
look(cam,(0,7,8.5)); cam.data.lens=42
bpy.context.scene.camera=cam

# ---------- render settings ----------
sc=bpy.context.scene
sc.render.engine='BLENDER_EEVEE_NEXT'
sc.render.resolution_x=1536;sc.render.resolution_y=1536;sc.render.resolution_percentage=100
sc.render.image_settings.file_format='PNG'
sc.render.filepath='/tmp/ravensthwaite.png'
sc.render.film_transparent=False
sc.view_settings.look='AgX - Medium High Contrast'
# ensure glTF-friendly materials and save
sc.render.image_settings.color_mode='RGBA'
bpy.ops.wm.save_as_mainfile(filepath='/tmp/ravensthwaite_terminus.blend')
```