import bpy, math, random, os
from mathutils import Vector
from math import sin, cos, pi

from pathlib import Path
_here = Path(__file__).resolve().parent
ROOT = str(Path(os.environ.get('KORU_ROOT', _here.parent if _here.name == 'source' else _here / 'Koru-output')))
for _sub in ('renders','renders/v2','delivery','references','source'): Path(ROOT,_sub).mkdir(parents=True,exist_ok=True)
random.seed(42)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for d in bpy.data.materials: bpy.data.materials.remove(d)
scene = bpy.context.scene
scene.render.engine = 'BLENDER_EEVEE'
scene.render.resolution_x=1920; scene.render.resolution_y=1080
scene.render.resolution_percentage=100
scene.render.fps=24; scene.frame_start=1; scene.frame_end=288
scene.render.image_settings.file_format='PNG'
scene.render.film_transparent=False
scene.render.filepath=ROOT+'/renders/v2/frame_'
scene.render.use_file_extension=True
scene.eevee.taa_render_samples=32
scene.view_settings.view_transform='AgX'
scene.view_settings.look='AgX - Medium High Contrast'
scene.view_settings.exposure=0.0
scene.render.image_settings.color_mode='RGB'

def mat(name, color, metal=0, rough=.35, emission=0):
    m=bpy.data.materials.new(name); m.diffuse_color=(*color,1); m.use_nodes=True
    p=m.node_tree.nodes.get('Principled BSDF')
    p.inputs['Base Color'].default_value=(*color,1)
    p.inputs['Metallic'].default_value=metal; p.inputs['Roughness'].default_value=rough
    if emission:
        p.inputs['Emission Color'].default_value=(*color,1); p.inputs['Emission Strength'].default_value=emission
    return m
navy=mat('Midnight blue • polished hull',(.014,.035,.058),.58,.23)
white=mat('Warm pearl • yacht enamel',(.83,.82,.72),.3,.24)
bronze=mat('Champagne bronze • mounting hardware',(.49,.28,.105),.82,.24)
chrome=mat('Brushed marine stainless',(.38,.45,.47),.87,.23)
black=mat('Carbon and graphite',(.013,.020,.026),.25,.35)
glass=mat('Smoked blue glazing',(.012,.073,.105),.75,.14)
red=mat('Burgundy boot stripe',(.25,.026,.022),.25,.29)
rope=mat('Graphite standing rigging',(.11,.125,.13),.5,.4)
foam=mat('Whitewater',(.6,.79,.78),.05,.36)
light=mat('Warm deck lighting',(.98,.52,.17),.2,.25,3)
bluefire=mat('Ion blue exhaust',(.055,.36,1),.0,.3,5)
corefire=mat('White hot exhaust core',(.55,.78,1),.0,.25,11)
orange=mat('Exhaust rim amber',(1,.24,.018),.0,.3,5)
teak=mat('Quarter sawn teak',(.30,.14,.055),0,.5)
nt=teak.node_tree; p=nt.nodes.get('Principled BSDF')
tex=nt.nodes.new('ShaderNodeTexNoise'); tex.inputs['Scale'].default_value=3; tex.inputs['Detail'].default_value=3
tc=nt.nodes.new('ShaderNodeTexCoord'); mapping=nt.nodes.new('ShaderNodeVectorMath'); mapping.operation='MULTIPLY'; mapping.inputs[1].default_value=(.12,7,3)
nt.links.new(tc.outputs['Generated'],mapping.inputs[0]); nt.links.new(mapping.outputs[0],tex.inputs['Vector'])
ramp=nt.nodes.new('ShaderNodeValToRGB'); ramp.color_ramp.elements[0].color=(.12,.048,.017,1); ramp.color_ramp.elements[1].color=(.46,.25,.10,1)
nt.links.new(tex.outputs['Fac'],ramp.inputs[0]); nt.links.new(ramp.outputs[0],p.inputs['Base Color'])
sailmat=mat('Ivory woven sailcloth',(.9,.86,.72),0,.62)
sp=sailmat.node_tree.nodes.get('Principled BSDF'); sp.inputs['Sheen Weight'].default_value=.25
sp.inputs['Subsurface Weight'].default_value=.055
sn=sailmat.node_tree.nodes.new('ShaderNodeTexNoise'); sn.inputs['Scale'].default_value=210
sb=sailmat.node_tree.nodes.new('ShaderNodeBump'); sb.inputs['Strength'].default_value=.16; sb.inputs['Distance'].default_value=.018
sailmat.node_tree.links.new(sn.outputs['Fac'],sb.inputs['Height']); sailmat.node_tree.links.new(sb.outputs[0],sp.inputs['Normal'])
seammat=mat('Sail panel seams',(.58,.56,.48),0,.7)

col=bpy.data.collections.new('KORU / yacht and propulsion'); scene.collection.children.link(col)
ship=bpy.data.objects.new('KORU • motion rig',None); scene.collection.objects.link(ship)
def finish(obj,name,ma=None,parent=True):
    obj.name=name
    if ma: obj.data.materials.append(ma)
    if parent: obj.parent=ship
    if obj.type=='MESH':
        for p in obj.data.polygons:p.use_smooth=True
    return obj
def mesh(name,vs,fs,ma,parent=True):
    d=bpy.data.meshes.new(name); d.from_pydata(vs,[],fs); d.update()
    o=bpy.data.objects.new(name,d); scene.collection.objects.link(o); return finish(o,name,ma,parent)
def cube(name,loc,scale,ma,bevel=.15,parent=True):
    bpy.ops.mesh.primitive_cube_add(size=1,location=loc); o=bpy.context.object
    o.dimensions=scale; bpy.ops.object.transform_apply(location=False,rotation=False,scale=True)
    finish(o,name,ma,parent)
    if bevel:
        b=o.modifiers.new('Soft manufactured edges','BEVEL'); b.width=bevel; b.segments=3
        o.modifiers.new('Weighted corner normals','WEIGHTED_NORMAL')
    return o
def line(name,pts,r,ma,parent=True):
    d=bpy.data.curves.new(name,'CURVE'); d.dimensions='3D'; d.resolution_u=2; d.bevel_depth=r; d.bevel_resolution=2
    s=d.splines.new('POLY'); s.points.add(len(pts)-1)
    for p,co in zip(s.points,pts):p.co=(*co,1)
    o=bpy.data.objects.new(name,d); scene.collection.objects.link(o); return finish(o,name,ma,parent)
def rod(name,a,b,r,ma,r2=None,vertices=16):
    a=Vector(a); b=Vector(b); delta=b-a
    bpy.ops.mesh.primitive_cone_add(vertices=vertices,radius1=r,radius2=r if r2 is None else r2,depth=delta.length,location=(a+b)/2)
    o=bpy.context.object; o.rotation_euler=delta.to_track_quat('Z','Y').to_euler(); return finish(o,name,ma)
def uv(name,loc,scale,ma,parent=True):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=24,ring_count=12,location=loc); o=bpy.context.object; o.scale=scale; return finish(o,name,ma,parent)
def text(name,body,loc,size,ma,rot=(pi/2,0,0)):
    d=bpy.data.curves.new(name,'FONT'); d.body=body; d.size=size; d.align_x='CENTER'; d.extrude=.004; d.space_character=1.3
    o=bpy.data.objects.new(name,d); scene.collection.objects.link(o); o.location=loc; o.rotation_euler=rot; return finish(o,name,ma)

# Fair hull: fine clipper bow, rounded canoe stern and curved topsides.
def width(x):
    t=(x+55)/110
    return max(.04,8.25*max(0,sin(pi*t))**.63*(1-.20*t))
def sheer(x):return 5.7+1.5*(abs(x)/55)**2.7+.32*x/55
nx=100; nr=48; vs=[]; fs=[]
for i in range(nx+1):
    x=-55+110*i/nx; w=width(x)
    for j in range(nr+1):
        a=-pi/2+pi*j/nr
        y=w*sin(a); z=sheer(x)-(sheer(x)+3.5)*max(0,cos(a))**.60
        vs.append((x-5*(x/55)**3*cos(a),y,z))
for i in range(nx):
    for j in range(nr):
        a=i*(nr+1)+j; fs.append((a,a+1,a+nr+2,a+nr+1))
hull=mesh('Continuous sculpted hull',vs,fs,navy)
sub=hull.modifiers.new('Fair the hull','SUBSURF');sub.levels=1
# Deck follows the same planform.
vs=[];fs=[]
for i in range(nx+1):
    x=-54.8+109.6*i/nx
    vs.extend([(x,-width(x)*.975,sheer(x)-.13),(x,width(x)*.975,sheer(x)-.13)])
for i in range(nx):fs.append((2*i,2*i+1,2*i+3,2*i+2))
mesh('Swept teak main deck',vs,fs,teak)
for side in (-1,1):
    for dz,rad,ma in [(0,.15,white),(-.46,.045,bronze),(-4.75,.16,red)]:
        pts=[]
        for i in range(160):
            x=-54.8+109.6*i/159
            # waterline slightly inboard on rounded hull
            pts.append((x,side*width(x)*(1 if dz>-1 else .83),sheer(x)+dz))
        line('Continuous sheer trim',pts,rad,ma)
    # Fine stanchions and two wire lifelines.
    for zoff,r,ma in [(.97,.042,chrome),(.48,.018,rope)]:
        line('Deck safety rail',[(x,side*width(x)*.97,sheer(x)+zoff) for x in [-54+108*i/140 for i in range(141)]],r,ma)
    for i in range(44):
        x=-53+106*i/43; y=side*width(x)*.97
        rod('Stainless stanchion',(x,y,sheer(x)),(x,y,sheer(x)+.97),.039,chrome,vertices=8)
    for x in range(-43,43,4):
        z=3.1; y=side*width(x)*.965
        uv('Hull oval portlight',(x,y,z),(.49,.045,.23),bronze)
        uv('Hull portlight glass',(x,y+side*.038,z),(.40,.055,.17),glass)
# Teak seams clipped to the deck outline.
for k in range(-26,27):
    y=k*.27; pts=[]
    for i in range(200):
        x=-54+108*i/199
        if abs(y)<width(x)*.93:pts.append((x,y,sheer(x)-.112))
    if len(pts)>1:line('Teak caulking',pts,.013,black)
# Low, long, stepped deckhouses.
cube('Lower saloon enamel',(-9,0,7.1),(53,11.8,2.7),white,.8)
cube('Lower panoramic glazing',(-10,0,7.8),(48,12.02,1.12),glass,.3)
for side in (-1,1):
    for x in range(-32,16,3):cube('Window mullion',(x,side*6.035,7.8),(.14,.1,1.26),white,.035)
cube('Lower teak roof',(-11,0,8.8),(56,12.9,.36),teak,.4)
cube('Lower fascia',(-11,0,8.62),(56.4,13.1,.20),white,.12)
cube('Upper deckhouse',(-6,0,10.0),(36,9.6,2.5),white,.65)
cube('Upper blue glass',(-5,0,10.3),(35,9.75,1.35),glass,.3)
for side in (-1,1):
    for x in range(-22,13,3):cube('Bridge mullion',(x,side*4.91,10.3),(.16,.13,1.5),white,.03)
cube('Bridge roof',(-6,0,11.47),(38,10.5,.30),white,.4)
cube('Bridge roof teak',(-6,0,11.65),(37,10,.12),teak,.3)
# Shaded aft cockpit, pool, lounge details.
cube('Aft canopy',(-33,0,9.1),(15,11,.25),white,.5)
for x in (-39,-27):
    for y in (-5,5):rod('Canopy bronze support',(x,y,6.4),(x,y,9.1),.085,bronze)
cube('Pool marble coping',(-43,0,6.82),(7,4.7,.26),white,.65)
pool=mat('Aft pool turquoise',(.016,.31,.35),.35,.13)
cube('Aft reflecting pool',(-43,0,6.99),(6.45,4.13,.08),pool,.6)
for x in (-37,-31,16,21):
    for y in (-3.6,3.6):
        cube('Teak chaise frame',(x,y,7 if x<0 else 6.45),(2.2,1,.28),teak,.15)
        cube('Ivory chaise cushion',(x,y,7.22 if x<0 else 6.67),(2.05,.91,.21),white,.12)
for x in (-17,2):
    uv('Satellite radome',(x,3,12.5),(.75,.75,.85),white)
    rod('Antenna',(x,-3,11.8),(x,-3,14),.04,white)
for side in (-1,1):
    for x in range(-36,25,4):uv('Deck courtesy lamp',(x,side*6.4,6.5),(.13,.08,.05),light)
    for x in (-44,40):
        rod('Deck cleat',(x,side*width(x)*.8,6.9),(x+1,side*width(x)*.8,6.9),.1,chrome)
text('Hull name port','K O R U',(-39,-width(-39)*.985,4.8),1.05,bronze)
text('Hull name starboard','K O R U',(-39,width(-39)*.985,4.8),1.05,bronze,(pi/2,0,pi))
rod('Long tapered bowsprit',(49,0,7.4),(70,0,8.3),.30,white,.10)
for side in (-1,1):line('Bowsprit whisker stay',[(46,side*3,6.7),(70,0,8.3),(51,0,1.7)],.042,rope)

# Three-masted schooner rig with broad, cambered quadrilateral sails.
def sail_point(x,h,u,v,span):
    # u up the mast, v aft across the sail. Top narrows to preserve a gaff silhouette.
    z=12+(h-15)*u-12*u*(1-v)
    chord=span*(1-.25*u)
    xx=x-1.0-v*chord
    yy=-.5-3.0*sin(pi*v)*sin(pi*(.08+.88*u))-.07*v*sin(u*8)
    return (xx,yy,z)
for idx,(x,h,span) in enumerate([(30,67,23),(3,69,24),(-24,64,23)]):
    rod('Mast %d • pearl spar'%(idx+1),(x,0,6),(x-1.0,0,h),.47,white,.18,24)
    rod('Boom %d'%(idx+1),(x,0,11.7),(x-span-1,-.7,11.7),.22,white,.14)
    for z,breadth in [(26,3.0),(42,2.5),(54,1.9)]:
        rod('Spreaders',(x-.4,-breadth,z),(x-.4,breadth,z),.07,chrome)
    for side in (-1,1):
        for dx in (-3,3):
            line('Standing shroud',[(x+dx,side*width(x)*.9,sheer(x)+.2),(x-.5,side*3,26),(x-.7,side*2.5,42),(x-1,0,h)],.026,rope)
        line('Backstay',[(x-1,0,h),(max(-52,x-24),side*4.2,7)],.03,rope)
    vs=[]; fs=[]; nu=24; nv=18
    for i in range(nu+1):
        for j in range(nv+1):vs.append(sail_point(x,h,i/nu,j/nv,span))
    for i in range(nu):
        for j in range(nv):
            a=i*(nv+1)+j;fs.append((a,a+1,a+nv+2,a+nv+1))
    s=mesh('Sail %d • shaped ivory cloth'%(idx+1),vs,fs,sailmat)
    sol=s.modifiers.new('Sailcloth thickness','SOLIDIFY');sol.thickness=.018
    # Barely perceptible breathing in the cloth.
    s.shape_key_add(name='Calm'); key=s.shape_key_add(name='Wind pressure')
    for k,v in enumerate(key.data):
        u=(k//(nv+1))/nu; t=(k%(nv+1))/nv
        v.co.y-=.50*sin(pi*t)*sin(pi*u)
    for f,value in [(1,.2),(55,.8),(110,.25),(175,.9),(240,.2)]:key.value=value;key.keyframe_insert('value',frame=f)
    for u in [i/12 for i in range(13)]:
        line('Horizontal sail seam',[sail_point(x,h,u,j/40,span) for j in range(41)],.018,seammat)
    for v in [.0,.25,.5,.75,1]:
        line('Vertical sail seam',[sail_point(x,h,i/50,v,span) for i in range(51)],.018,seammat)
    for u in [.18,.43,.69,.89]:
        line('Sail batten',[sail_point(x,h,u,.66+.34*j/12,span) for j in range(13)],.042,seammat)
    line('Sail leech',[sail_point(x,h,i/40,1,span) for i in range(41)],.045,white)
    line('Masthead stay',[(x-1,0,h),(70,0,8.3) if idx==0 else (x+27-1,0,h+1)],.028,rope)

def jib(name,A,B,C,belly):
    vs=[];fs=[];n=30
    # Rectangular parameterization pinches at head.
    for i in range(n+1):
        u=i/n
        for j in range(n+1):
            v=j/n
            p=(1-u)*((1-v)*Vector(A)+v*Vector(B))+u*Vector(C)
            p.y-=belly*sin(pi*v)*sin(pi*u)
            vs.append(tuple(p))
    for i in range(n):
        for j in range(n):a=i*(n+1)+j;fs.append((a,a+1,a+n+2,a+n+1))
    mesh(name,vs,fs,sailmat)
    for a,b in [(A,B),(B,C),(C,A)]:line('Jib bolt rope',[a,b],.048,white)
    for t in [.2,.4,.6,.8]:
        a=Vector(A).lerp(Vector(C),t); b=Vector(B).lerp(Vector(C),t)
        line('Jib stitched panel',[tuple(a.lerp(b,j/30)+Vector((0,-belly*sin(pi*j/30)*sin(pi*t),0))) for j in range(31)],.021,seammat)
jib('Outer headsail',(69,0,9),(48,-1.6,17),(29,0,65),1.8)
jib('Inner staysail',(52,-.2,9),(33,-1.3,12),(29.3,0,48),1.7)


# Original Blue Origin vector outlines, embedded for reproducible standalone builds.
LOGO_SVGS = {'blue-origin-wordmark.svg': '<?xml version="1.0" encoding="UTF-8"?>\n<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In  -->\n<svg width="800" height="150" enable-background="new 0 0 159 26.8" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><title>Logo of Blue Origin</title><path d="m210.17 108.33c0 9.2831-3.9087 13.68-11.726 13.68s-11.726-4.3973-11.726-13.68v-95.763h-21.986v95.274c0 21.986 12.703 33.712 33.712 33.712 21.009 0 33.712-12.215 33.712-33.712v-95.274h-21.986zm-147.55-35.667c10.749-4.3973 15.146-15.635 15.146-26.872 0-21.498-10.26-33.712-33.712-33.712h-32.735v127.03h34.69c24.918 0 34.201-15.635 34.201-36.644 0-16.123-6.8402-24.918-17.589-29.804zm-29.315-42.507h8.306c9.7717 0 14.169 5.863 14.169 18.078 0 10.749-6.3516 16.123-14.658 16.123h-8.306zm8.7945 91.366h-8.7945v-39.087h6.8402c13.192 0 18.078 6.3516 18.078 19.543 0 12.703-4.3973 19.543-16.123 19.543zm231.59-38.11h28.338v-19.055h-28.338v-32.735h36.644v-19.055h-58.63v127.03h61.073v-19.055h-39.087zm-153.9-70.845h-21.498v127.03h56.187v-19.055h-34.201v-107.98zm556.5 127.03h21.986v-127.03h-21.986zm-52.279-128.99c-22.964 0-33.712 14.658-33.712 32.735v65.471c0 18.078 10.749 32.735 33.712 32.735 0 0 12.215-0.48858 21.498-7.8174l11.726 7.8174v-33.224-32.735h-35.178v17.1h15.635v15.635 0.48859c0 14.169-12.215 13.68-12.215 13.68h-1.4658c-8.7945 0-11.726-4.3973-11.726-14.169v-64.493c0-9.2831 2.9315-14.169 11.726-14.169 8.7946 0 11.726 4.3973 11.726 14.169v9.7717h21.986v-10.26c0-18.078-10.749-32.735-33.712-32.735zm-217.91 0c-22.964 0-33.224 14.658-33.224 32.735v65.471c0 18.078 10.749 32.735 33.712 32.735s33.712-14.658 33.712-32.735v-65.471c-0.48859-18.078-11.237-32.735-34.201-32.735zm11.726 97.717c0 9.2831-2.9315 14.169-11.726 14.169-8.7946 0-11.726-4.3973-11.726-14.169v-64.493c0-9.2831 2.9315-14.169 11.726-14.169 8.7945 0 11.726 4.3973 11.726 14.169zm350.8-95.763v75.242l-29.804-75.242h-19.543v127.03h19.543v-75.242l29.804 75.242h19.543v-127.03zm-220.35 127.03h21.986v-127.03h-21.986zm-21.986-89.9c0-27.361-12.215-37.621-37.133-37.621h-30.781v127.03h21.986v-51.79c3.9087 0 8.306 0 11.237-0.48858l15.635 52.767h21.986l-18.566-57.653c7.8174-3.4201 15.635-11.726 15.635-32.247zm-37.621 19.543h-7.8174v-39.087h7.8174c11.726 0 16.123 4.3973 16.123 19.543 0 15.146-4.3973 19.543-16.123 19.543z" fill="#00f" stroke-width="4.8859"/></svg>', 'blue-origin-feather.svg': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 110 56.05" height="225.3" width="442"><defs><style>.cls-1{fill:#03d;fill-rule:evenodd;}</style></defs><title>FeatherFooter</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M63.11,48.58Z"/><path class="cls-1" d="M110,13.09l0-.64v0l0-.25a0,0,0,0,0,0,0h0l-.4.16s0,0,0,0l.23-.25s0,0,0,0l-.1.06s0,0,0,0l.22-.31s0-.05,0,0a4.86,4.86,0,0,1-.5.18s.35-.27.56-.41a0,0,0,0,0,0,0s0,0,0,0l-.27,0a.05.05,0,0,1,0-.09l.22-.19a.14.14,0,0,0,.06-.15l-.12-1-.07-.44a.3.3,0,0,0-.17-.22l-.48-.15a.05.05,0,0,1,0-.1h.47a.08.08,0,0,0,.08-.08l-.07-.43a2.43,2.43,0,0,0-.21-.68L109,7.41s0,0-.06-.09a.15.15,0,0,0-.15-.09l-.43,0a.06.06,0,0,1-.07-.08.09.09,0,0,0-.09-.11h-.32s0-.09,0-.09a6.92,6.92,0,0,0,.79-.21h0v0h-.09s-.89-1.43-.93-1.47l-.74-.72-.1-.07-.48-.29a.27.27,0,0,0-.26,0,2.21,2.21,0,0,0-.58.38s-.1,0-.08-.06a1.94,1.94,0,0,0,.16-.59.33.33,0,0,0-.13-.31h0l-.37-.27-1-.59-.06,0-.05,0a.31.31,0,0,0-.28,0c-.29.17-.62.36-.92.55a0,0,0,0,1-.05,0c.11-.19.21-.4.3-.6,0,0,0-.07-.06,0-.29.24-.61.53-.77.69s-.06,0,0,0l.46-.91a.07.07,0,0,0-.06-.11L102,2.15l-.08,0L101.73,2a.1.1,0,0,0-.09,0,.33.33,0,0,1-.13,0c-.38,0-.58.76-.71,1.12a.58.58,0,0,1-.15.25,18.38,18.38,0,0,0-1.4,2,.08.08,0,0,1-.14-.07L100.49,2a.35.35,0,0,0-.21-.46l-.07,0a.31.31,0,0,0-.23,0l-.07,0a.32.32,0,0,1-.22,0l-.22-.06a.09.09,0,0,0-.11,0,22.82,22.82,0,0,0-3.47,7.57c0,.06-.1,0-.09,0,.67-4.17,1.45-7.65,1.6-8.28,0,0,0-.05,0,0l0,0s0,0,0,0h0a0,0,0,0,0,0,0l0,.06h0a21.92,21.92,0,0,0-1,3.46c-.27,1,.32-2.55.49-3.53A.11.11,0,0,0,96.54.6h0a.11.11,0,0,0-.14.07c-.09.23-.2.51-.33.86a2.87,2.87,0,0,0,0-.65.49.49,0,0,0-.42-.46l-.17,0a.16.16,0,0,0-.18.12c-.22.92-.66,2.68-1.46,5.81a.12.12,0,0,1-.23,0A21.13,21.13,0,0,0,93.41.2.25.25,0,0,0,93.23,0H93l-.33,0a.08.08,0,0,0-.08.08c-.06.72-.2,2.25-.54,4.44a.05.05,0,0,1-.1,0C92.05,3,92,1.21,92,.35a.21.21,0,0,0-.23-.21L90.44.49a.42.42,0,0,0-.28.19v0c-.92,1.63-1.65,5-2.25,7.81,0,.06-.1,0-.09,0a35.64,35.64,0,0,1,1.66-7.36A.08.08,0,0,0,89.35,1l-.61.31-.32.21a1.27,1.27,0,0,0-.46.54,35.14,35.14,0,0,0-1.49,5,.1.1,0,0,1-.2,0,18.44,18.44,0,0,1,1.17-4.75,0,0,0,0,0,0,0h0l-1.2,1L84.5,5a1.9,1.9,0,0,0-.35.49l-.32.63a.06.06,0,0,1-.11,0h0s0,0,0,0l0-.16a.2.2,0,0,1,0-.11,7.44,7.44,0,0,1,.2-.78.65.65,0,0,1,0-.13c.07-.18.18-.43.24-.57h0l-.18.1a2.78,2.78,0,0,0-.21.46s0,0,0,0v0l.06-.31h0l-.26.21a4.7,4.7,0,0,1-.16.49s-.06,0-.06,0,0-.18,0-.28h0a3.34,3.34,0,0,0-.35.45c-.09.32-.15.66-.25,1h0a4.07,4.07,0,0,0-.28.55l0,.05c-.18.77-.42,1.54-.58,2.31-.06.28-.14.56-.2.83a.16.16,0,0,1-.06.08s0,0,0,0a.59.59,0,0,0,0,.08l0,.06a1.12,1.12,0,0,0-.16.22l0,.09a.12.12,0,0,1-.22,0l-.29-.49a.65.65,0,0,0-.39-.3h0a.91.91,0,0,1-.36-.2h0a.06.06,0,0,0-.1,0h0a1,1,0,0,1-.15.53l-.26.42a0,0,0,0,1,0,0h0a0,0,0,1,0,0,0l-.26.48,0,0-.07.16,0,.05,0-.24s0,0,0,0l-.55,1.66c-.06,0-.1,0-.08-.16s.09-.42.13-.64a.08.08,0,0,0-.13-.08h0a1.29,1.29,0,0,0-.45.63l-.09.24a.07.07,0,0,1-.12,0s0-.1-.11,0a.08.08,0,0,0,0,.06c-.06.29-.09.29-.18.62l-.15.27a3.6,3.6,0,0,0-.12.44l-.09.36a0,0,0,1,1-.08,0v0l0-.08s0-.09,0-.11-.08,0-.08,0l0,.06h0a13.57,13.57,0,0,0-.86,1.29.07.07,0,0,1-.06,0h0a.07.07,0,0,1-.07-.09v-.06c.15-.64.3-1.27.44-1.91a4,4,0,0,0,.18-.53c.08-.5.61-1.92.58-1.89.41-1.29.45-1.76.33-1.84h0c-.14-.08-.48.35-.69.64-1,1.94-2.67,3.52-5.3,5.19h0l-.13.08h0L70,16c-2.12,1.24-4.34,2.35-4.38,2.37a5.9,5.9,0,0,0-2,1.53s0,0,0,0v0s0,0,0,0,0,0,0,0a4.9,4.9,0,0,0-.3.54.25.25,0,0,1,0,.07c-.34.59-.57,1.21-.9,1.79-.13.23-.18.52-.31.75s-.33.77-.53,1.13a21.2,21.2,0,0,0-1.13,2.53,14.16,14.16,0,0,0-.62,1.8c0,.11-.08.22-.13.33-.11.51-.27,1-.41,1.52a.05.05,0,0,1-.05,0h0a0,0,0,0,1,0,0c.05-.22.09-.45.14-.67.21-1.09.44-2.16.68-3.23.1-.44.31-.88.42-1.32A7.86,7.86,0,0,1,60.7,24l.16-.46c.24-.44.43-.9.69-1.33s.4-.59.61-.88a5.5,5.5,0,0,0,.27-.82.08.08,0,0,0-.13-.09h0a2.52,2.52,0,0,0-.47.38c-.14.12-.3.23-.47.36h0l-.15.12a27.47,27.47,0,0,1-2.62,1.66,9.38,9.38,0,0,0-1.8,1.31h0l-.15.15h0a6.31,6.31,0,0,0-.89.9c-.42.61-.82,1.23-1.19,1.87-.15.26-.26.55-.41.82,0,.08,0,.16-.08.24a4.62,4.62,0,0,0-.29.73,2.39,2.39,0,0,0-.13.28c-.14.6-.37,1.19-.51,1.8-.29,1.29-.53,2.59-.8,3.89-.14.69-.39,1.35-.55,2a4.35,4.35,0,0,1-.4,1c-.07.18-.14.35-.2.53-.26.46-.51.94-.81,1.38a4.78,4.78,0,0,1-.34.42c-.09.1-.22.16-.29.26h0a.05.05,0,0,1-.06-.05c.11-.44.43-.84.53-1.28.18-.76.4-1.5.51-2.28.15-1.09.12-2.21.17-3.34a38,38,0,0,1,.57-5.71,8.2,8.2,0,0,1,.81-2.32c.13-.24.19-.53.32-.76s.34-.66.52-1l.05-.1,0-.15a.3.3,0,0,1,0-.08s0-.08,0-.11-.07-.19-.14-.14-.11.14-.17.15a.14.14,0,0,1-.11,0s-.41.47-.51.61l-.06.11,0,.12,0,0c-.13.17-.3.27-.42.44s-.32.54-.47.8c0,0,0,0,0,0a.05.05,0,0,1-.05-.07.14.14,0,0,0,0-.06c0-.11,0-.23.07-.34a0,0,0,0,0-.07,0l0,.05a.46.46,0,0,1-.17.14c-.27.14-.58.79-.7,1a2.2,2.2,0,0,1-.3.31l-.28.7v0l-.07.07s0,0,0,0,0,0,0,0v-.1l.16-.74a.06.06,0,0,0-.07-.07l0,0a10.35,10.35,0,0,0-2.4,1.17s-.08,0,0-.05.18-.25.21-.31h0s0,0,0,0a3.46,3.46,0,0,0-.46.32l-1,.7a.11.11,0,0,0-.13,0l0,0c-.12.13-.23.29-.35.42a.73.73,0,0,1-.26.2h0a.72.72,0,0,0-.25.18l-1,1.09h0A15.67,15.67,0,0,0,43,31.6a5.88,5.88,0,0,0-.33.72l-.06.17a.14.14,0,0,1-.09.09h0a.13.13,0,0,1-.15-.14c0-.12,0-.23,0-.35A3.63,3.63,0,0,1,43,30.77l.08-.13.13-.28a0,0,0,0,0,0-.06H43a.61.61,0,0,0-.24.07h0l-.34.19a1.23,1.23,0,0,0-.41.34,4.72,4.72,0,0,0-.51.75c-.14.26-.19.59-.35.85-.06.07-.14.11-.19.19s-.16.3-.25.43-.21.22-.3.36-.19.47-.31.69a8.48,8.48,0,0,0-.87,2.94c0,.16,0,.3,0,.47a6.2,6.2,0,0,0-.06.62c0,1.28,0,2.63,0,3.9,0,.3,0,.68,0,1a.08.08,0,0,1-.16,0h0c0-.52-.15-1.06-.22-1.59-.05-.34-.17-.61-.22-1-.09-.66-.24-1.34-.32-2a10.58,10.58,0,0,1-.18-1.18,44.67,44.67,0,0,1,.23-4.81,6.33,6.33,0,0,0,0-.87l-.06-.45c0-.19,0-.41,0-.59a3.44,3.44,0,0,1,0-.78,0,0,0,0,0,0,0h0a0,0,0,0,0-.06,0c-.06.15-.14.4-.2.62s0,.39,0,.39-.07.09-.09.06l0-.15a0,0,0,0,0-.08,0c-.09.14-.16.28-.24.42v.06a.25.25,0,0,1-.13.14h0a1.63,1.63,0,0,0-.22.38l0,0-.09.06a1.58,1.58,0,0,0-.13.22,0,0,0,0,1-.07,0h0a0,0,0,0,1,0-.06,4,4,0,0,0,.15-.4c0-.23.12-.47.16-.71a3.16,3.16,0,0,1,0-.32,0,0,0,0,0,0,0h0a0,0,0,0,0,0,0,7.46,7.46,0,0,0-.61,1.15,1.88,1.88,0,0,0-.22.42,4.18,4.18,0,0,1-.2.68c-.09.14-.24.22-.33.35s-.29.46-.43.69A8.73,8.73,0,0,0,34.39,37a9.54,9.54,0,0,0-.07,1.22,1.55,1.55,0,0,0,0,.43c0-.05-.06-.2-.08-.29-.05-.28-.16-.51-.21-.79-.13-.79-.31-1.59-.44-2.34a.42.42,0,0,0-.16-.27,1.52,1.52,0,0,1-.09-.52.08.08,0,0,0-.07-.07h0a.08.08,0,0,0-.07,0c0,.1,0,.2-.05.29a0,0,0,0,1-.09,0h0a.05.05,0,0,0,0,0,.07.07,0,0,0-.11,0,1.62,1.62,0,0,0-.16.5.83.83,0,0,0,0,.22.42.42,0,0,1-.1.32l0,0h0a.06.06,0,0,0-.11,0,.25.25,0,0,0-.05.15h0c-.06-.16-.1-.32-.15-.48a.08.08,0,0,0-.15,0c0,.14,0,.29,0,.43,0,.5,0,1,0,1.56,0,1.07.09,2.16.12,3.26,0,.71.19,1.39.26,2.1s.14,1.11.2,1.7a3,3,0,0,0,.08.65,2.46,2.46,0,0,1-.22-.71l-.55-1.65a3.9,3.9,0,0,1-.32-.89c-.05-.25-.22-.48-.26-.7l-.15-.33a6.37,6.37,0,0,1-.36-.91l-.21-.48c-.1-.54-.48-1-.58-1.55a4.06,4.06,0,0,0-.42-.89c-.15-.33-.3-.66-.46-1-.08-.11-.12-.27-.21-.39L29,35.49a.11.11,0,0,0-.2.06,3.58,3.58,0,0,0,0,.58.07.07,0,0,1,0,.07.14.14,0,0,0,0,.07c0,.34,0,.71,0,1.06a.08.08,0,0,1-.15,0l-.09-.17a.08.08,0,0,0-.14,0,6.7,6.7,0,0,0-.08.92,3.94,3.94,0,0,0-.14.7,9.69,9.69,0,0,0,.21,1.46c0,.15.08.3.13.45.07.51.34,1.05.44,1.58l.19.49c.06.3.27.61.32.93s.22.49.27.76.19.4.23.62.2.4.24.61a8,8,0,0,0,.37,1,.06.06,0,0,1-.06.09h0c-.2.06-.37-.27-.5-.39-.43-.43-.84-.91-1.29-1.33a9.78,9.78,0,0,0-1.53-1.24l-.59-.25c-.05,0-.08,0-.12,0-.19-.11-.33-.27-.51-.38l-.37-.11c-.24-.14-.48-.42-.75-.46-.1,0-.18,0-.27,0s-.39-.14-.58-.18a.86.86,0,0,0-.16,0,.14.14,0,0,0-.13.17h0c0,.36.23.34.45.5s.12.15.2.19v0h0a.08.08,0,0,0-.05.13c.5.64,1.13,1.11,1.65,1.68a5.89,5.89,0,0,1,.6.74l.3.67.35.5c.1.14,0,.22,0,.32s.1.13.15.23a5.73,5.73,0,0,1-.69.16c-.37.07-.72.21-1.07.27-.64.1-1.27.36-1.89.46-.39.07-.7.23-1.08.3-1.28.22-2.57.7-3.85.91-.18,0-.28.11-.46.14-.76.13-1.5.42-2.27.54s-1.38.37-2.1.49c-1.72.29-3.43.83-5.14,1.13-.15,0-.23.09-.36.11-.66.1-1.35.34-2,.45l-.59.17c-1,.18-2.12.64-3.19.84-.42.07-.8.27-1.2.34l-.53.19c-.7.14-.94.5-.87,1,.14,1,1.07.63,1.67.57,1.1-.11,2.23-.31,3.35-.47.55-.08,1-.26,1.59-.33,1.48-.22,3-.67,4.46-.92.57-.09,1.11-.3,1.66-.39l.47-.13c.59-.1,1.23-.32,1.82-.42l.71-.2c.82-.14,1.67-.45,2.48-.59l.86-.24c.92-.16,1.86-.52,2.78-.68l1-.27c1.31-.23,2.65-.79,4-1a10.9,10.9,0,0,1,1.94-.47.15.15,0,0,1,.14.12h0a.09.09,0,0,0,0,.08.1.1,0,0,0,.08,0s.07,0,.08,0,.06.3.18.34h0s0,0,0,0,0-.12,0-.18a1.21,1.21,0,0,0,0-.25s0,0,0,0h0a0,0,0,0,1,0,0,.07.07,0,0,0,0,0c.05,0,.06,0,.07,0s0,0,.05,0a3.05,3.05,0,0,0,.24.51.76.76,0,0,1,.08.15h0a1.83,1.83,0,0,0-.09-.71s0-.06.05-.07h0s0,0,.06,0a.54.54,0,0,1,.09.15l.17.47c.06.36.15.74.2,1.08s.15.19.17.38a5.62,5.62,0,0,1,0,.58,1.48,1.48,0,0,1,0,.42h0l.07-.07s.06,0,.08,0h0a.11.11,0,0,1,0,.11c0,.2.05.42,0,.63a0,0,0,0,0,.06,0,1.87,1.87,0,0,0,.15-.32v0h0a1.71,1.71,0,0,1,.06.66s0,.07.07,0a.5.5,0,0,0,.16-.35h0v0c0,.05.05.09.1.05s.13-.3.22-.45v0h.07a2.56,2.56,0,0,0,.3-1.12.75.75,0,0,0,0-.15.19.19,0,0,1,0-.16l0,0,0-.06a2.14,2.14,0,0,0,.06-.27c0-.12.1-.25.13-.37A5.71,5.71,0,0,0,32,48.72a.05.05,0,0,1,0-.07h0a.05.05,0,0,1,.07,0c.08.17.15.34.23.52.28.38.43.84.71,1.19.43.54.8,1.15,1.23,1.7.15.2.25.45.39.65l.18.33c.32.43.52,1,.84,1.46l.21.41c.19.25.39.47.57.69a.09.09,0,0,0,.15,0,.13.13,0,0,0,0-.06h0a.06.06,0,0,0,0-.06,0,0,0,0,1,0,0h0a0,0,0,0,1,0,0c.11.08.18.25.29.34a1.71,1.71,0,0,0,.43.29A.06.06,0,0,0,37.5,56a5.83,5.83,0,0,1-.46-1.2.75.75,0,0,0-.11-.2,3.25,3.25,0,0,1-.19-.6c-.08-.36-.42-.66-.46-.94a3.07,3.07,0,0,0-.53-1.3c-.08-.11-.11-.28-.19-.38s0-.05,0,0,.18.13.28.17a2,2,0,0,0,.35.07.06.06,0,0,0,.05-.1c-.13-.1-.29-.18-.41-.29s-.38-.33-.51-.48a1.6,1.6,0,0,0-.1-.23l-.09-.13s0,0,0,0h0s0,0,0,0l.17.2a8.4,8.4,0,0,0,1.52,1.07c.18.1.77.49.94.27.12.06.31.09.4,0,.12.08.24.06.37.11s.18.12.28.18l.1,0a.16.16,0,0,0,.14-.28h0c-.25-.16-.55-.28-.8-.45a.94.94,0,0,1-.12-.09.13.13,0,0,1,0-.2h0a.16.16,0,0,1,.09,0l.12,0a3.83,3.83,0,0,1,.61.3,3.77,3.77,0,0,0,1.21.45,5.76,5.76,0,0,0,1.53-.25,1.25,1.25,0,0,0,.25-.09h0a2.33,2.33,0,0,1,.5-.24c.14,0,.08-.21-.09-.19h0a2.53,2.53,0,0,1-1.62-.48l-.26-.16L40,49.93h0l-.81-.87h0c-.29-.35-.56-.75-.85-1.13a2.69,2.69,0,0,1-.26-.44,0,0,0,0,1,0,0h0s0,0,.06,0a12.91,12.91,0,0,0,1.33,1.49l.22.24.86.74c2,1.37,5.88.64,5.88.64s1.59-.21,1.63-.24l.09,0h.15a.11.11,0,0,0,0-.2h0l-.2-.07c-.33-.1-.62-.2-.86-.29l-.39-.15a11.09,11.09,0,0,1-3-1.92c-.41-.37-.77-.82-1.16-1.22l-.13-.15s0-.1.06-.06h0l1,1a9.6,9.6,0,0,0,3.94,2.4c.23.05.86.28,1.3.43a1.43,1.43,0,0,0,.65,0h0a4.58,4.58,0,0,1,.64,0l.35-.06.05,0s0,0,0,0a.06.06,0,0,1,0-.09h0A.55.55,0,0,1,51,49.7a4.31,4.31,0,0,1,1.12-.09l.29,0c.06,0,.06-.11,0-.11h0a0,0,0,0,1,0,0,5.81,5.81,0,0,0,.59,0c.11,0,.22,0,.31,0h0l0,0a0,0,0,0,1,0,0,4,4,0,0,1,.59,0c.08,0,.14-.06.2-.05l.11,0a.06.06,0,0,0,0-.11,4,4,0,0,1-.51-.05c-.16,0-.29,0-.44,0-.38-.05-.76-.13-1.13-.21a12.4,12.4,0,0,1-1.92-.54l-.46-.17c-.26-.14-.53-.25-.77-.39-.46-.27-.89-.59-1.33-.9l-.1-.07a.11.11,0,0,1,0-.13.1.1,0,0,1,.15,0l.61.43A10.05,10.05,0,0,0,51.3,48.3a15.35,15.35,0,0,0,1.52.38,5.23,5.23,0,0,0,1.19,0c.14,0,.41-.09.58-.11a.73.73,0,0,0,.24-.07l.08,0a0,0,0,0,0,0-.09h0s0,0,0-.05a2.43,2.43,0,0,0,.8-.29,2,2,0,0,1,.58-.4,2,2,0,0,0,.5-.18l.15,0,.06,0,.39-.18a.06.06,0,0,0,0-.11L57,47h0l-.1,0-.16.05a3.46,3.46,0,0,1-1.51-.23h0l-.32-.11-.24-.11-.89-.5c-.51-.24-.95-.48-1.15-.58a.1.1,0,0,1,0-.15h0a.08.08,0,0,1,.1,0,14.47,14.47,0,0,0,3.36,1.12l1.3.33s1,.2,2.29.54c.89.24,1.74.62,2.6.93.17.06.38.13.55.17l.32.06a0,0,0,0,0,0,0,.06.06,0,0,0,0-.07c-.13-.09-.29-.13-.41-.21s-.11-.11-.18-.16h0a.57.57,0,0,1-.11-.11h0s-.13,0-.17-.08l-.07-.08-.06-.11L62,47.64l-.22-.14,0,0h0s0,0,0,0h0a.08.08,0,0,1,.08,0,2.84,2.84,0,0,0,.32.14c.26.06.52.15.77.21l.22,0s0,0,0,0,0,0,0,0a4,4,0,0,0-.45-.15,2,2,0,0,1-.25-.13l-.82-.3a37.33,37.33,0,0,1-4-1.76,20.69,20.69,0,0,1-3-2.46l-.21-.2h0a.17.17,0,0,1,.23-.26c1,.67,1.91,1.4,2.91,2,.38.21.82.32,1.19.52a19.26,19.26,0,0,0,2.7.94,3.43,3.43,0,0,0,.55.06c.11,0,.22-.06.33,0l.05,0h0l.61-.33.64-.41.16-.14h0a.32.32,0,0,0-.12-.08v0a2.4,2.4,0,0,0,.83-.25h0a.1.1,0,0,0-.07-.17,3.56,3.56,0,0,1-.67-.05,2.42,2.42,0,0,1-.5-.14l-.47-.14a10.81,10.81,0,0,1-3.49-1.86,7.11,7.11,0,0,1-.6-.49,0,0,0,0,1,0,0,.06.06,0,0,1,.09-.05c.25.16.5.32.76.47.63.39,1.3.7,1.95,1.06.2.12.46.17.67.28l.16.09a4.94,4.94,0,0,0,3.23.15,4.16,4.16,0,0,1,1.15-.5,5.15,5.15,0,0,1,.94-.2L68,43h.09c.43,0,1,0,1.23,0a0,0,0,0,0,0,0s0,0,0,0,0,0-.06-.05h0a.24.24,0,0,1,.07-.33l.19-.13a.21.21,0,0,1,.11,0h0a2.24,2.24,0,0,0,.29-.11c.27,0,.79-.21,1.07-.25s1.42-.6,1.6-.58a8.44,8.44,0,0,1,.89-.26l.22-.12a3.51,3.51,0,0,0,.5-.15.15.15,0,0,0,.09-.22l0,0a.13.13,0,0,0-.1-.07h-.23s-.06,0-.06,0a.1.1,0,0,1,0-.08,5.55,5.55,0,0,1,.59-.18c.18,0,.37-.25.51-.25l.21-.09h0l-.16,0v0h0l.26-.14.66-.28.83-.41.7-.42,1.26-.88.37-.29,1.42-1.11a.71.71,0,0,1,.14-.1h0a3.51,3.51,0,0,1,1.26-.4h0c.09,0,.13-.07.19-.09a2.06,2.06,0,0,1,.36,0,2,2,0,0,1,.33,0l.33.07.13,0,.28,0h.26a.93.93,0,0,0,.39-.12.71.71,0,0,0,.14-.1l.1-.09,0,0,.32-.3,0,0L85,35l.09-.09,0,0a1,1,0,0,1,.2-.13l.12-.06a.54.54,0,0,1,.6.07c.08.07.12,0,.11-.06a.79.79,0,0,0-.69-.39h0s-.6,0-.83,0a.11.11,0,0,1-.13-.11.12.12,0,0,1,.1-.11l.17,0h0l.44-.08a2.54,2.54,0,0,0,1.27-.57l.06-.06.72-.87.59-.73h0a.15.15,0,0,1,.06-.11,9.49,9.49,0,0,0,.77-.79,3.42,3.42,0,0,1,.45-.2.57.57,0,0,0,.19-.13l.06,0a0,0,0,1,0,0,0l-.09,0s0,0,0,0h0A7.24,7.24,0,0,1,90,30l.24-.13a.46.46,0,0,0,.15-.09,4,4,0,0,1,.86-.47l.17-.08a1,1,0,0,1,.29-.08,4.09,4.09,0,0,1,1.21,0h0l.63.2.09,0a2.44,2.44,0,0,0,.51.18h.12a.49.49,0,0,0,.17,0h.15l.06,0c.17-.09-.17-.23-.28-.26L94,29.12l-1-.44h0a.28.28,0,0,1,.16-.45c.25-.06.5-.12.75-.16h0a.16.16,0,0,0,.11-.09l0,0a.13.13,0,0,0-.13-.2h-.09s-.15,0-.18-.08.1-.14.12-.15l.06,0,.29-.09.1,0a5.79,5.79,0,0,0,1.45-.61h0l1.15-.87.2-.14h0l.2-.17,5.1-3.75.17-.08a.08.08,0,0,0,.06-.08h0a.09.09,0,0,0-.12-.09l-.14,0s-.07,0-.07-.05a.13.13,0,0,1,0-.1,2.48,2.48,0,0,1,.25-.18h0l.8-.54.83-.43.23-.15.06,0h0l.38-.25.87-.62.05,0h0l.49-.44.07-.11h0a0,0,0,0,0,0-.07.22.22,0,0,0-.11,0s0,0,0,0,.18-.12.11-.12a.07.07,0,0,1,0-.13l.21-.05.06,0,.24-.11a0,0,0,0,0,0,0h-.23s0,0,0,0a3.16,3.16,0,0,0,.83-.44h0a.21.21,0,0,1,0-.11,3.37,3.37,0,0,0,.63-.68l.18-.33h0a0,0,0,0,0,0,0v-.06a.07.07,0,0,1,0-.05v0h0a2.36,2.36,0,0,0-.37,0s0,0,0,0l.13,0a1.06,1.06,0,0,0,.52-.33l0,0s.29-.37,0-.34h-.17l-.06,0s0,0,0,0a1.19,1.19,0,0,0,.36-.28l0,0s0,0,0,0a.46.46,0,0,0-.16.06h0s0,0,0,0a4.42,4.42,0,0,0,.41-.52.08.08,0,0,0,0-.07h0l0,0a0,0,0,0,1-.07,0v0a.08.08,0,0,1,0,0,1.47,1.47,0,0,1,.14-.28h0s0,0,0,0h0a0,0,0,0,1-.06,0v0l.06-.1a.52.52,0,0,1,.38-.3.89.89,0,0,0,.62-.27.08.08,0,0,0,.05-.05l0-.09A1,1,0,0,0,110,13.09Z"/></g></g></svg>\n'}
for filename, svg in LOGO_SVGS.items(): Path(ROOT,'references',filename).write_text(svg)
logo_blue=mat('Blue Origin • authentic blue livery',(0,.0331,.7231),.12,.30)
def vector_livery(filename,cx,cy,cz,width_m,side):
    before=set(bpy.data.objects)
    bpy.ops.import_curve.svg(filepath=str(Path(ROOT,'references',filename)))
    imported=[o for o in bpy.data.objects if o not in before]
    bpy.ops.object.select_all(action='DESELECT')
    for o in imported:o.select_set(True)
    bpy.context.view_layer.objects.active=imported[0]
    bpy.ops.object.convert(target='MESH')
    bpy.ops.object.join(); o=bpy.context.object
    # Bake SVG transforms before measuring the exact original outline.
    bpy.ops.object.transform_apply(location=True,rotation=True,scale=True)
    xs=[v.co.x for v in o.data.vertices];ys=[v.co.y for v in o.data.vertices]
    mx=(min(xs)+max(xs))/2;my=(min(ys)+max(ys))/2;scale=width_m/(max(xs)-min(xs))
    # Subdivide planar faces before wrapping so wide glyphs follow the nacelle.
    bpy.ops.object.mode_set(mode='EDIT');bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.mesh.quads_convert_to_tris();bpy.ops.mesh.subdivide(number_cuts=4);bpy.ops.object.mode_set(mode='OBJECT')
    for v in o.data.vertices:
        x=cx+(v.co.x-mx)*scale*(-side);dz=(v.co.y-my)*scale
        r=2.15 if x<=-7 else (2.15-(x+7)*.0375 if x<=-3 else 2-(x+3)*.15)
        v.co=(x,cy+side*(math.sqrt(max(.05,r*r-dz*dz))+.025),cz+dz)
    o.data.materials.clear();finish(o,'Blue Origin / original SVG / '+filename,logo_blue)
    # Imported face winding varies after the cylinder mapping; render both sides.
    return o

# Bespoke strap-on propulsion: lacquered tapered nacelles and visible saddles.
def lathe_x(name,profile,y,z,ma,n=64):
    vs=[];fs=[]
    for x,r in profile:
        for k in range(n):a=2*pi*k/n;vs.append((x,y+r*cos(a),z+r*sin(a)))
    for i in range(len(profile)-1):
        for k in range(n):a=i*n+k;b=i*n+(k+1)%n;fs.append((a,b,b+n,a+n))
    return mesh(name,vs,fs,ma)
for side in (-1,1):
    y=side*10.3; z=4.75
    lathe_x('Pearl rocket nacelle', [(-35,1.5),(-33,2.0),(-28,2.15),(-7,2.15),(-3,2.0),(0,1.55),(3,.65),(4,.04)],y,z,white)
    # Polished nose and navy aft taper.
    lathe_x('Bronze nose cap',[(1.8,1.04),(3,.65),(4,.04)],y,z,bronze)
    lathe_x('Navy thrust section',[(-39,1.6),(-37,1.65),(-35,1.5),(-33,2.015)],y,z,navy)
    # Broad bands are the visually legible straps; structural saddles connect to the hull.
    for x in (-27,-10):
        lathe_x('Bronze mounting strap',[(x-.55,2.17),(x+.55,2.17)],y,z,bronze)
        for xx in (x-.62,x+.62):lathe_x('Strap graphite gasket',[(xx-.06,2.175),(xx+.06,2.175)],y,z,black)
        cube('Saddle connector',(x,side*8.1,5.5),(1.7,3.8,1.0),navy,.25)
        cube('Mounting foot',(x,side*6.6,6.23),(3.0,1.15,.55),bronze,.16)
        for theta in [-1.1,-.55,0,.55,1.1]:
            yy=y+side*2.19*cos(theta);zz=z+2.19*sin(theta)
            uv('Strap fastening bolt',(x,yy,zz),(.14,.1,.14),chrome)
    vector_livery('blue-origin-wordmark.svg',-18,y,z+.05,10.6,side)
    vector_livery('blue-origin-feather.svg',-5.4,y,z,5.7,side)
    text('New Shepard identification','NEW SHEPARD  /  BE-3PM',(-18,y+side*1.79,z-1.23),.25,navy,(pi/2,0,0 if side<0 else pi))
    # New Shepard ring-fin language, with folded landing braces aft.
    lathe_x('New Shepard forward ring fin',[(-1.0,2.03),(-.65,2.32),(-.4,2.32),(-.1,1.59)],y,z,white)
    for a in (pi/4,3*pi/4,5*pi/4,7*pi/4):
        yy=y+2.05*cos(a);zz=z+2.05*sin(a)
        rod('Stowed landing strut',(-34,yy,zz),(-28,yy,zz),.12,black)

    # Cooling flutes and a recessed bell, all geometry rather than flat textures.
    for x in [-34+i*.30 for i in range(13)]:lathe_x('Cooling collar rib',[(x,1.89),(x+.08,1.89)],y,z,chrome,48)
    lathe_x('Graphite expansion nozzle',[(-37,1.0),(-38,1.22),(-39.4,1.72),(-39.7,1.74),(-39.7,1.51),(-39.3,1.49),(-38,1.02)],y,z,black)
    lathe_x('Nozzle bronze lip',[(-39.7,1.75),(-39.45,1.75)],y,z,bronze)
    lathe_x('Nozzle ignition ring',[(-39.71,1.5),(-39.64,1.5)],y,z,orange)
    # Three nested tapered, rippling plumes and shock diamonds.
    flame_root=bpy.data.objects.new('Animated exhaust envelope',None);scene.collection.objects.link(flame_root);flame_root.parent=ship;flame_root.location=(-39.7,y,z)
    for radius,length,ma in [(1.34,24,bluefire),(.85,18,corefire),(.36,27,corefire)]:
        profile=[]
        for i in range(61):
            t=i/60;rr=radius*(1-t)**.75*(.84+.16*cos(t*35))
            profile.append((-length*t,max(.005,rr)))
        o=lathe_x('Sculpted exhaust plume',profile,0,0,ma,32);o.parent=flame_root
    for i in range(6):
        x=-2.2-i*2.6;r=.68*(1-i*.1)
        o=uv('Supersonic shock diamond',(x,0,0),(1.05,r,r),corefire);o.parent=flame_root
    for f in range(1,289,4):
        strength=1.0
        flame_root.scale=(max(.04,strength*(.94+.07*sin(f*1.3))),max(.10,strength),max(.10,strength))
        flame_root.keyframe_insert('scale',frame=f)
    ld=bpy.data.lights.new('Blue reflected engine light','POINT');ld.energy=1600;ld.color=(.06,.32,1);ld.shadow_soft_size=2
    lo=bpy.data.objects.new('Engine sea glow',ld);scene.collection.objects.link(lo);lo.parent=ship;lo.location=(-41,y,4)

# Procedural open sea, real displaced geometry near the yacht and infinite horizon.
ocean=mat('Deep ocean • moving capillary waves',(.015,.105,.14),.35,.21)
nt=ocean.node_tree;p=nt.nodes.get('Principled BSDF');p.inputs['IOR'].default_value=1.333;p.inputs['Metallic'].default_value=.12;p.inputs['Roughness'].default_value=.26
tc=nt.nodes.new('ShaderNodeTexCoord'); add=nt.nodes.new('ShaderNodeVectorMath');add.operation='ADD'
nt.links.new(tc.outputs['Object'],add.inputs[0])
for f,v in [(1,(0,0,0)),(288,(4,1,0))]:add.inputs[1].default_value=v;add.inputs[1].keyframe_insert('default_value',frame=f)
n1=nt.nodes.new('ShaderNodeTexNoise');n1.inputs['Scale'].default_value=.6;n1.inputs['Detail'].default_value=4;n1.inputs['Roughness'].default_value=.7
nt.links.new(add.outputs[0],n1.inputs['Vector'])
b=nt.nodes.new('ShaderNodeBump');b.inputs['Strength'].default_value=.42;b.inputs['Distance'].default_value=.3
nt.links.new(n1.outputs['Fac'],b.inputs['Height']);nt.links.new(b.outputs[0],p.inputs['Normal'])
cr=nt.nodes.new('ShaderNodeValToRGB');cr.color_ramp.elements[0].color=(.006,.038,.052,1);cr.color_ramp.elements[1].color=(.035,.16,.19,1)
nt.links.new(n1.outputs['Fac'],cr.inputs[0]);nt.links.new(cr.outputs[0],p.inputs['Base Color'])
cube('Infinite ocean',(0,0,-.7),(20000,20000,.2),ocean,0,False)
vs=[];fs=[];n=190;size=680
for i in range(n+1):
    x=-size/2+size*i/n
    for j in range(n+1):
        y=-size/2+size*j/n
        z=.18*sin(x*.17+y*.12)+.13*sin(x*.33-y*.18)+.09*sin(y*.61+x*.15)
        vs.append((x,y,z-.22))
for i in range(n):
    for j in range(n):a=i*(n+1)+j;fs.append((a,a+1,a+n+2,a+n+1))
sea=mesh('Rolling ocean surface',vs,fs,ocean,False)
sea.shape_key_add(name='Swell A');sk=sea.shape_key_add(name='Swell B')
for v in sk.data:
    x,y,z=v.co;v.co.z=.18*sin(x*.17+y*.12+1.5)+.13*sin(x*.33-y*.18+2)+.09*sin(y*.61+x*.15+1)-.22
for f,v in [(1,0),(75,1),(150,0),(240,1)]:sk.value=v;sk.keyframe_insert('value',frame=f)

wake_start=set(bpy.data.objects)
# Layered foam ribbons and streaks follow the hull and expand aft.
wakemat=mat('Aerated wake foam',(.42,.68,.69),.1,.4)
nt=wakemat.node_tree;p=nt.nodes.get('Principled BSDF');no=nt.nodes.new('ShaderNodeTexNoise');no.inputs['Scale'].default_value=2.5;no.inputs['Detail'].default_value=3
ra=nt.nodes.new('ShaderNodeValToRGB');ra.color_ramp.elements[0].position=.33;ra.color_ramp.elements[0].color=(.015,.13,.17,1);ra.color_ramp.elements[1].position=.65;ra.color_ramp.elements[1].color=(.68,.85,.8,1)
nt.links.new(no.outputs['Fac'],ra.inputs[0]);nt.links.new(ra.outputs[0],p.inputs['Base Color'])
for side in (-1,1):
    for k in range(8):
        vs=[];fs=[]
        for i in range(150):
            t=i/149;x=52-175*t
            y=side*(width(max(-54,min(54,x))) if x>-48 else 4+(abs(x)-48)*.20)
            y+=side*(k*.45+sin(t*35+k)*.18)
            w=(.2+.45*t)*(1+.6*sin(t*29+k))
            for yy in (y-w,y+w):vs.append((x,yy,.09+.07*sin(x*.2+yy*.3)+k*.007))
        for i in range(149):fs.append((2*i,2*i+1,2*i+3,2*i+2))
        mesh('Lacing wake ribbon',vs,fs,wakemat)
    # Bow spray delicate arcs.
    for i in range(26):
        x=46-i*.28;y=side*(width(x)+random.uniform(.1,.7));z=.3+random.random()*.4
        line('Bow wave spray',[(x,y,z),(x-1,y+side*.5,z+.5),(x-3,y+side*.7,.15)],.025+random.random()*.03,foam)
for i in range(135):
    x=random.uniform(-110,-37);y=random.uniform(-10,10);z=random.uniform(.01,.2)
    uv('Broken trailing foam',(x,y,z),(random.uniform(.4,2),random.uniform(.05,.25),.018),wakemat)

# Advect foam backward in the moving reference frame: sea-speed 14.936 m/s.
wake_root=bpy.data.objects.new('Water effects • translation only',None);scene.collection.objects.link(wake_root)
for o in set(bpy.data.objects)-wake_start:
    if o!=wake_root and o.parent==ship:o.parent=wake_root
# Shared low-poly droplets, animated with analytic ages so the wake never freezes.
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=1,radius=1)
prototype=bpy.context.object;droplet_mesh=prototype.data;bpy.data.objects.remove(prototype,do_unlink=True)
droplet_mesh.materials.append(foam)
for i in range(420):
    side=-1 if i%2==0 else 1;phase=random.random()*9.5
    ob=bpy.data.objects.new('Advected bow spray / %03d'%i,droplet_mesh);scene.collection.objects.link(ob);ob.parent=wake_root
    age='(((frame-1)/24+%.5f)%%9.5)'%phase
    def drive(prop,index,expr):ob.driver_add(prop,index).driver.expression=expr
    drive('location',0,'48-14.936*'+age)
    offset=random.uniform(.1,3.4);spread=random.uniform(.65,1.5)
    # Broad wake fans out downstream, droplets settle into foam on the surface.
    drive('location',1,'%d*(3.7+%f+%f*%s)'%(side,offset,spread,age))
    height=random.uniform(.3,2.0)
    drive('location',2,'.12+%f*max(0,1-%s/1.6)*(sin(%s*2.0)**2)'%(height,age,age))
    length=random.uniform(.25,1.2);breadth=random.uniform(.06,.22)
    envelope='min(1,%s*3)*min(1,(9.5-%s)*2)'%(age,age)
    drive('scale',0,'%f*(1+%s*.3)*(%s)'%(length,age,envelope))
    drive('scale',1,'%f*(%s)'%(breadth,envelope))
    drive('scale',2,'(.035+.08*max(0,1-%s/1.6))*(%s)'%(age,envelope))

# Atmospheric golden-hour environment.
world=bpy.data.worlds.new('Marine evening sky');scene.world=world;world.use_nodes=True
nt=world.node_tree;bg=nt.nodes.get('Background');bg.inputs['Strength'].default_value=.8
geo=nt.nodes.new('ShaderNodeTexCoord');sep=nt.nodes.new('ShaderNodeSeparateXYZ');nt.links.new(geo.outputs['Normal'],sep.inputs[0]);flip=nt.nodes.new('ShaderNodeMath');flip.operation='ABSOLUTE';nt.links.new(sep.outputs['Z'],flip.inputs[0])
sky=nt.nodes.new('ShaderNodeValToRGB')
sky.color_ramp.elements.remove(sky.color_ramp.elements[1])
for pos,color in [(0,(.52,.59,.62,1)),(.18,(.28,.40,.52,1)),(.55,(.085,.20,.34,1)),(1,(.045,.12,.23,1))]:
    e=sky.color_ramp.elements[0] if pos==0 else sky.color_ramp.elements.new(pos);e.position=pos;e.color=color
nt.links.new(flip.outputs[0],sky.inputs[0]);nt.links.new(sky.outputs[0],bg.inputs['Color'])
ld=bpy.data.lights.new('Late sun','SUN');ld.energy=3.0;ld.angle=math.radians(9);ld.color=(1,.79,.54)
lo=bpy.data.objects.new('Late sun',ld);scene.collection.objects.link(lo);lo.rotation_euler=(math.radians(32),math.radians(-20),math.radians(-35))
ld=bpy.data.lights.new('Soft sky fill','AREA');ld.energy=75000;ld.shape='DISK';ld.size=100;ld.color=(.53,.72,1)
lo=bpy.data.objects.new('Soft sky fill',ld);scene.collection.objects.link(lo);lo.location=(30,-65,85);lo.rotation_euler=(Vector((0,0,10))-lo.location).to_track_quat('-Z','Y').to_euler()

# Twelve-second steady full-throttle excerpt. 29.029 kn is conditional model equilibrium.
SPEED=29.029199*1852/3600
tracking=bpy.data.objects.new('Camera tracking • same forward speed',None);scene.collection.objects.link(tracking)
for ob in (ship,wake_root,tracking):
    ob.driver_add('location',0).driver.expression='(frame-1)/24*%.8f'%SPEED
ship.driver_add('location',2).driver.expression='.12+.10*sin(frame*.045)'
ship.driver_add('rotation_euler',0).driver.expression='.003*sin(frame*.035)'
# Small artistic bow-up trim, not a solved planing attitude.
ship.driver_add('rotation_euler',1).driver.expression='-.0105+.002*sin(frame*.045)'
target=bpy.data.objects.new('Camera aim',None);scene.collection.objects.link(target);target.parent=tracking
bpy.ops.object.camera_add();cam=bpy.context.object;cam.name='CAM • full speed tracking orbit';scene.camera=cam;cam.parent=tracking
cam.data.lens=48;cam.data.clip_end=30000
con=cam.constraints.new('TRACK_TO');con.target=target;con.track_axis='TRACK_NEGATIVE_Z';con.up_axis='UP_Y'
for f,pos,aim,lens in [(1,(113,-221,60),(0,0,26),48),(85,(85,-222,51),(-3,0,26),49),(168,(39,-224,44),(-5,0,27),50),(288,(-20,-237,46),(-6,0,27),50)]:
    cam.location=pos;cam.keyframe_insert('location',frame=f);cam.data.lens=lens;cam.data.keyframe_insert('lens',frame=f)
    target.location=aim;target.keyframe_insert('location',frame=f)
wide_marker=scene.timeline_markers.new('Steady full throttle • 29 kn scenario',frame=1);wide_marker.camera=cam
bpy.ops.object.camera_add();detail=bpy.context.object;detail.name='CAM • authentic Blue Origin livery';detail.parent=tracking;detail.data.lens=52;detail.data.clip_end=30000
dt=bpy.data.objects.new('Detail camera aim',None);scene.collection.objects.link(dt);dt.parent=tracking;dt.location=(-19,-3,5.5)
dc=detail.constraints.new('TRACK_TO');dc.target=dt;dc.track_axis='TRACK_NEGATIVE_Z';dc.up_axis='UP_Y'
for f,pos in [(169,(5,-101,24)),(240,(-18,-94,20))]:
    detail.location=pos;detail.keyframe_insert('location',frame=f)
marker=scene.timeline_markers.new('Original Blue Origin vector livery',frame=169);marker.camera=detail
marker=scene.timeline_markers.new('Full speed • broadside finale',frame=241);marker.camera=cam
scene.camera=cam

# A restrained optical bloom around the engines.
scene.use_nodes=True
nt=bpy.data.node_groups.new('Koru optical finish','CompositorNodeTree');scene.compositing_node_group=nt;nt.interface.new_socket(name='Image',in_out='OUTPUT',socket_type='NodeSocketColor')
rl=nt.nodes.new('CompositorNodeRLayers');gl=nt.nodes.new('CompositorNodeGlare');gl.inputs['Type'].default_value='Fog Glow';gl.inputs['Quality'].default_value='Medium';gl.inputs['Threshold'].default_value=2;gl.inputs['Size'].default_value=.35
out=nt.nodes.new('NodeGroupOutput')
scene.view_layers[0].use_pass_mist=True;world.mist_settings.start=180;world.mist_settings.depth=1900;world.mist_settings.falloff='LINEAR'
mix=nt.nodes.new('ShaderNodeMix');mix.data_type='RGBA';mix.inputs[7].default_value=(.416,.472,.496,1)
nt.links.new(rl.outputs['Mist'],mix.inputs[0]);nt.links.new(rl.outputs['Image'],mix.inputs[6]);nt.links.new(mix.outputs[2],gl.inputs['Image']);nt.links.new(gl.outputs['Image'],out.inputs['Image'])
scene['Concept']='KORU / IMPULSE — fictional Koru conversion using Blue Origin New Shepard / BE-3PM inspired propulsion pods. Fairings and integration are artistic adaptations, not flight hardware replicas.'
scene['Source']='Procedurally modeled from public Koru photographs. No engineering accuracy claimed.'
scene['Animation']='288 frames / 24 fps. Steady full throttle at conditional model equilibrium 29.029 kn; no claim of planing. Authentic vector livery.'
scene.frame_set(125)
# Open into a useful camera view.
for screen in bpy.data.screens:
    for area in screen.areas:
        if area.type=='VIEW_3D':area.spaces.active.region_3d.view_perspective='CAMERA'
bpy.ops.wm.save_as_mainfile(filepath=ROOT+'/Koru-Impulse.blend')
scene.render.resolution_percentage=50;scene.eevee.taa_render_samples=24
scene.render.filepath=ROOT+'/renders/v2/preview.png'
bpy.ops.render.render(write_still=True)
print('KORU_BUILD_COMPLETE',flush=True)
