diff --git a/assets/blender/evolution/README.md b/assets/blender/evolution/README.md new file mode 100644 index 0000000..7809a85 --- /dev/null +++ b/assets/blender/evolution/README.md @@ -0,0 +1,13 @@ +# Grand Tourer — Evolution + +Open `nightdrive-evolution.blend` directly to edit the final source. The `Portfolio Evolution` scene contains individually editable bodywork, louvers, wheel vanes, wing and marker housings. The original source in the parent folder was not overwritten. No external assets or paid services were used. + +`export-grand-tourer.py` exports the loaded `Portfolio Evolution` scene into eight joined material groups, without lights, cameras, textures or animation. Execute it inside Blender with `__file__` set to the script path. The web renderer sanitizes Blender's duplicate-name suffixes before assigning its runtime lighting behavior. + +Design history: + +1. The original source scene was appended from `../nightdrive-grand-tourer.blend` and named `Portfolio Evolution`. +2. `refine-grand-tourer.py` applied the first paint, hub, louver and deck-lip pass. This first pass is historical and should not be reapplied to the finished source. +3. `add-personality.py` added the swept wing, mounted cyan guides and turbine wheel faces. This second pass replaces its own `signature-` objects on repeat runs. + +To reproduce from the original, follow that order in a separate scene. For ordinary editing, use the finished `.blend` and export script. Asset counts and provenance are recorded in `nightdrive-evolution.asset.json`. diff --git a/assets/blender/evolution/add-personality.py b/assets/blender/evolution/add-personality.py new file mode 100644 index 0000000..d4030d3 --- /dev/null +++ b/assets/blender/evolution/add-personality.py @@ -0,0 +1,66 @@ +"""Second design pass; run inside Blender with Portfolio Evolution loaded. +Re-running replaces only this pass's objects. Keeps the eight material groups. +""" +import bpy +import math +from pathlib import Path + +root_path = Path(__file__).resolve().parents[3] +scene = bpy.data.scenes['Portfolio Evolution'] +bpy.context.window.scene = scene +car = next(o for o in scene.objects if o.name.split('.')[0] == 'chase-car') +materials = {m.name.split('.')[0]: m for o in scene.objects if o.type == 'MESH' for m in o.data.materials} +for obj in list(scene.objects): + if obj.name.startswith('signature-'): + bpy.data.objects.remove(obj, do_unlink=True) + +def finish(obj, name, material, bevel=0.008): + obj.name = 'signature-' + name + obj.parent = car + obj.data.materials.append(materials[material]) + if bevel: + mod = obj.modifiers.new('Soft machined edge', 'BEVEL') + mod.width = bevel + mod.segments = 2 + obj.modifiers.new('Weighted normals', 'WEIGHTED_NORMAL') + return obj + +def box(name, location, dimensions, material, bevel=0.008): + bpy.ops.mesh.primitive_cube_add(size=1, location=location) + obj = bpy.context.object + obj.dimensions = dimensions + bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + return finish(obj, name, material, bevel) + +# A swept floating wing. Its high tips and low center echo the body haunches. +outline = [(-1.22, 2.28), (-0.98, 2.08), (-0.72, 2.13), + (0.72, 2.13), (0.98, 2.08), (1.22, 2.28), + (1.18, 2.51), (0.72, 2.42), (-0.72, 2.42), (-1.18, 2.51)] +n = len(outline) +verts = [(x, y, 1.20 + (0.06 if abs(x) > 0.95 else 0) + dz) + for dz in [0, 0.065] for x, y in outline] +faces = [tuple(reversed(range(n))), tuple(range(n, 2*n))] +faces += [(i, (i+1)%n, (i+1)%n+n, i+n) for i in range(n)] +mesh = bpy.data.meshes.new('Swept aerofoil') +mesh.from_pydata(verts, [], faces) +mesh.update() +wing = bpy.data.objects.new('Swept wing', mesh) +scene.collection.objects.link(wing) +finish(wing, 'floating-wing', 'M_Body_Haunch', 0.018) +for side in [-1, 1]: + box('wing-pylon', (side*0.66, 2.27, 1.045), (0.075, 0.14, 0.35), 'M_Lower_Graphite') + box('wing-endplate', (side*1.185, 2.35, 1.245), (0.045, 0.30, 0.20), 'M_Body_Gunmetal') + box('wing-marker', (side*1.212, 2.37, 1.28), (0.014, 0.19, 0.025), 'M_Cyan_Accent', 0.003) + # Illuminated outer guides frame the dark diffuser, below the coral lamp. + box('rear-guide-housing', (side*0.88, 2.56, 0.50), (0.07, 0.23, 0.235), 'M_Lower_Graphite') + box('rear-guide', (side*0.88, 2.678, 0.50), (0.022, 0.02, 0.19), 'M_Cyan_Accent', 0.004) + for y, z in [(-1.62, 0.42), (1.62, 0.46)]: + # Eight swept metallic vanes sit inside each original rim ring. + for index in range(8): + angle = index * math.tau / 8 + obj = box('turbine-vane', (side*1.186, y+math.sin(angle)*0.19, z+math.cos(angle)*0.19), + (0.022, 0.064, 0.19), 'M_Wheel', 0.005) + obj.rotation_euler.x = -angle + side*0.3 + +bpy.data.libraries.write(str(root_path / 'assets/blender/evolution/nightdrive-evolution.blend'), {scene}, fake_user=True, compress=True) +exec(compile((root_path / 'assets/blender/evolution/export-grand-tourer.py').read_text(), str(root_path / 'assets/blender/evolution/export-grand-tourer.py'), 'exec')) diff --git a/assets/blender/evolution/export-grand-tourer.py b/assets/blender/evolution/export-grand-tourer.py new file mode 100644 index 0000000..d35af7f --- /dev/null +++ b/assets/blender/evolution/export-grand-tourer.py @@ -0,0 +1,39 @@ +"""Export the current authored Portfolio Evolution scene. Run inside Blender.""" +import bpy +from pathlib import Path +root_path = Path(__file__).resolve().parents[3] +scene = bpy.data.scenes["Portfolio Evolution"] +# A separate export scene combines geometry by material: eight runtime draw groups. +export_scene = bpy.data.scenes.new('Evolution Export') +bpy.context.window.scene = export_scene +root = bpy.data.objects.new('evolution-chase-car', None) +export_scene.collection.objects.link(root) +names = {'M_Body_Gunmetal': 'car-body', 'M_Body_Haunch': 'body-haunches', 'M_Glass_Smoke': 'rear-glass', 'M_Rear_Light': 'rear-light-bar', 'M_Cyan_Accent': 'car-underglow-source', 'M_Lower_Graphite': 'body-trim', 'M_Wheel': 'wheel-machining', 'M_Tire': 'tires'} +bpy.context.window.scene = scene +depsgraph = bpy.context.evaluated_depsgraph_get() +groups = {} +for obj in scene.objects: + if obj.type != 'MESH' or obj.name.startswith('LOOKDEV'): + continue + evaluated = obj.evaluated_get(depsgraph) + mesh = bpy.data.meshes.new_from_object(evaluated, depsgraph=depsgraph) + mesh.transform(obj.matrix_world) + clone = bpy.data.objects.new('export-' + obj.name, mesh) + export_scene.collection.objects.link(clone) + key = obj.data.materials[0].name.split('.')[0] + groups.setdefault(key, []).append(clone) +bpy.context.window.scene = export_scene +for key, objects in groups.items(): + bpy.ops.object.select_all(action='DESELECT') + for obj in objects: + obj.select_set(True) + bpy.context.view_layer.objects.active = objects[0] + if len(objects) > 1: + bpy.ops.object.join() + joined = bpy.context.object + joined.name = 'evolution-' + names[key] + joined.parent = root +bpy.ops.object.select_all(action='SELECT') +bpy.ops.export_scene.gltf(filepath=str(root_path / 'src/assets/nightdrive-grand-tourer-evolution.glb'), export_format='GLB', use_selection=True, use_active_scene=True, export_apply=True, export_animations=False, export_cameras=False, export_lights=False) +print('Exported', len(groups), 'material groups') +bpy.context.window.scene = scene diff --git a/assets/blender/evolution/nightdrive-evolution.asset.json b/assets/blender/evolution/nightdrive-evolution.asset.json new file mode 100644 index 0000000..b59f24b --- /dev/null +++ b/assets/blender/evolution/nightdrive-evolution.asset.json @@ -0,0 +1,32 @@ +{ + "name": "Nightdrive Grand Tourer \u2014 Evolution", + "source": "Derivative refinement of the original portfolio-authored Nightdrive Grand Tourer, with user-authorized Blender modeling", + "license": "Original work", + "thirdPartyAssets": [], + "externalProviders": [], + "sourceFile": "nightdrive-evolution.blend", + "parentSourceFile": "../nightdrive-grand-tourer.blend", + "exportFile": "../../../src/assets/nightdrive-grand-tourer-evolution.glb", + "exportProfile": { + "format": "GLB", + "meshObjects": 8, + "triangles": 18564, + "materials": 8, + "textures": 0, + "animations": 0, + "bytes": 754228 + }, + "exportSha256": "57c5f4250eab89145e3d7a026d21b12d510e47fade248e481335199d3b963d2d", + "details": [ + "Silver-blue metallic paint", + "Recessed wheel hubs and eight-vane turbine wheel faces", + "Six rear-window louvers", + "Low deck lip and swept floating wing with cyan endplate markers", + "Segmented coral tail light and mounted cyan rear guide lamps" + ], + "posterSources": [ + "src/assets/hero-evolution.jpg", + "src/assets/hero-evolution-mobile.jpg" + ], + "posterProvenance": "Canvas-only captures of the real Three.js world at desktop and phone viewport sizes. Decorative fallback only; not project evidence." +} diff --git a/assets/blender/evolution/nightdrive-evolution.blend b/assets/blender/evolution/nightdrive-evolution.blend new file mode 100644 index 0000000..7b77a21 Binary files /dev/null and b/assets/blender/evolution/nightdrive-evolution.blend differ diff --git a/assets/blender/evolution/refine-grand-tourer.py b/assets/blender/evolution/refine-grand-tourer.py new file mode 100644 index 0000000..646ce1b --- /dev/null +++ b/assets/blender/evolution/refine-grand-tourer.py @@ -0,0 +1,94 @@ +"""Run in Blender after loading the original into the isolated Portfolio Evolution scene. +Original scene and other worktrees are never saved or overwritten. +""" +import bpy +from pathlib import Path + +root_path = Path(__file__).resolve().parents[3] +scene = bpy.data.scenes['Portfolio Evolution'] +bpy.context.window.scene = scene +car = next(o for o in scene.objects if o.name.split('.')[0] == 'chase-car') +materials = {m.name.split('.')[0]: m for o in scene.objects if o.type == 'MESH' for m in o.data.materials} +for name, color, metallic, roughness in [ + ('M_Body_Gunmetal', (0.14, 0.21, 0.28, 1), 0.78, 0.28), + ('M_Body_Haunch', (0.19, 0.28, 0.34, 1), 0.8, 0.24), + ('M_Wheel', (0.28, 0.34, 0.39, 1), 0.9, 0.23), +]: + bsdf = materials[name].node_tree.nodes.get('Principled BSDF') + bsdf.inputs['Base Color'].default_value = color + bsdf.inputs['Metallic'].default_value = metallic + bsdf.inputs['Roughness'].default_value = roughness + bsdf.inputs['Coat Weight'].default_value = 0.28 if 'Body' in name else 0 + bsdf.inputs['Coat Roughness'].default_value = 0.23 + +# Recess the old projecting cylinders into the tire face. +for o in scene.objects: + stem = o.name.split('.')[0] + if '-rim-' in stem and '-ring-' not in stem: + o.scale.z *= 0.16 + o.location.x = (1 if o.location.x > 0 else -1) * 1.145 + if stem.startswith('diffuser-fin-'): + o.scale.z *= 0.65 + +def box(name, location, dimensions, material, bevel=0.012): + bpy.ops.mesh.primitive_cube_add(size=1, location=location) + obj = bpy.context.object + obj.name = name + obj.dimensions = dimensions + bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + obj.parent = car + obj.data.materials.append(material) + mod = obj.modifiers.new('Machined edge', 'BEVEL') + mod.width = bevel + mod.segments = 3 + obj.modifiers.new('Weighted normals', 'WEIGHTED_NORMAL') + return obj + +# Six individually editable louvers, following the slope of the rear glass. +for index in range(6): + t = index / 5 + y = 0.30 + t * 0.74 + z = 1.245 - t * 0.275 + box('evolution-louver-%02d' % index, (0, y, z), (1.25 + t * 0.18, 0.052, 0.026), materials['M_Lower_Graphite'], 0.008) +box('evolution-deck-lip', (0, 2.07, 0.89), (1.72, 0.095, 0.055), materials['M_Body_Haunch'], 0.018) +# Dark dividers articulate a continuous lamp without increasing emissive energy. +for x in [-0.54, -0.27, 0, 0.27, 0.54]: + box('evolution-lamp-divider', (x, 2.68, 0.694), (0.012, 0.012, 0.047), materials['M_Lower_Graphite'], 0.002) + +# Write just this authored scene and its dependencies, not other open projects. +bpy.data.libraries.write(str(root_path / 'assets/blender/evolution/nightdrive-evolution.blend'), {scene}, fake_user=True, compress=True) + +# A separate export scene combines geometry by material: eight runtime draw groups. +export_scene = bpy.data.scenes.new('Evolution Export') +bpy.context.window.scene = export_scene +root = bpy.data.objects.new('evolution-chase-car', None) +export_scene.collection.objects.link(root) +names = {'M_Body_Gunmetal': 'car-body', 'M_Body_Haunch': 'body-haunches', 'M_Glass_Smoke': 'rear-glass', 'M_Rear_Light': 'rear-light-bar', 'M_Cyan_Accent': 'car-underglow-source', 'M_Lower_Graphite': 'body-trim', 'M_Wheel': 'wheel-machining', 'M_Tire': 'tires'} +bpy.context.window.scene = scene +depsgraph = bpy.context.evaluated_depsgraph_get() +groups = {} +for obj in scene.objects: + if obj.type != 'MESH' or obj.name.startswith('LOOKDEV'): + continue + evaluated = obj.evaluated_get(depsgraph) + mesh = bpy.data.meshes.new_from_object(evaluated, depsgraph=depsgraph) + mesh.transform(obj.matrix_world) + clone = bpy.data.objects.new('export-' + obj.name, mesh) + export_scene.collection.objects.link(clone) + key = obj.data.materials[0].name.split('.')[0] + groups.setdefault(key, []).append(clone) +bpy.context.window.scene = export_scene +for key, objects in groups.items(): + bpy.ops.object.select_all(action='DESELECT') + for obj in objects: + obj.select_set(True) + bpy.context.view_layer.objects.active = objects[0] + if len(objects) > 1: + bpy.ops.object.join() + joined = bpy.context.object + joined.name = 'evolution-' + names[key] + joined.parent = root +bpy.ops.object.select_all(action='SELECT') +bpy.ops.export_scene.gltf(filepath=str(root_path / 'src/assets/nightdrive-grand-tourer-evolution.glb'), export_format='GLB', use_selection=True, use_active_scene=True, export_apply=True, export_animations=False, export_cameras=False, export_lights=False) +print('Exported', len(groups), 'material groups') +bpy.context.window.scene = scene diff --git a/docs/reviews/evidence/gpt6-evolution/after-desktop.png b/docs/reviews/evidence/gpt6-evolution/after-desktop.png new file mode 100644 index 0000000..c9c65be Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/after-desktop.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/after-phone.png b/docs/reviews/evidence/gpt6-evolution/after-phone.png new file mode 100644 index 0000000..6105fee Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/after-phone.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/after-work.png b/docs/reviews/evidence/gpt6-evolution/after-work.png new file mode 100644 index 0000000..0e7c655 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/after-work.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/before-desktop.png b/docs/reviews/evidence/gpt6-evolution/before-desktop.png new file mode 100644 index 0000000..40f8909 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/before-desktop.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/before-phone.png b/docs/reviews/evidence/gpt6-evolution/before-phone.png new file mode 100644 index 0000000..19a69ee Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/before-phone.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/blender-coachwork.png b/docs/reviews/evidence/gpt6-evolution/blender-coachwork.png new file mode 100644 index 0000000..dd3ff69 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/blender-coachwork.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/desktop-contact.png b/docs/reviews/evidence/gpt6-evolution/desktop-contact.png new file mode 100644 index 0000000..095bda6 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/desktop-contact.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/desktop-toolkit.png b/docs/reviews/evidence/gpt6-evolution/desktop-toolkit.png new file mode 100644 index 0000000..0f47cef Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/desktop-toolkit.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/narrow-contact.png b/docs/reviews/evidence/gpt6-evolution/narrow-contact.png new file mode 100644 index 0000000..efbd40a Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/narrow-contact.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/nightdrive-motion.webm b/docs/reviews/evidence/gpt6-evolution/nightdrive-motion.webm new file mode 100644 index 0000000..efe6217 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/nightdrive-motion.webm differ diff --git a/docs/reviews/evidence/gpt6-evolution/reference-desktop.png b/docs/reviews/evidence/gpt6-evolution/reference-desktop.png new file mode 100644 index 0000000..39752fb Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/reference-desktop.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/reference-phone.png b/docs/reviews/evidence/gpt6-evolution/reference-phone.png new file mode 100644 index 0000000..2076aed Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/reference-phone.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/short-infrastructure.png b/docs/reviews/evidence/gpt6-evolution/short-infrastructure.png new file mode 100644 index 0000000..b491b54 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/short-infrastructure.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/tablet-infrastructure.png b/docs/reviews/evidence/gpt6-evolution/tablet-infrastructure.png new file mode 100644 index 0000000..fc09b46 Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/tablet-infrastructure.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/tablet-start.png b/docs/reviews/evidence/gpt6-evolution/tablet-start.png new file mode 100644 index 0000000..af6d13c Binary files /dev/null and b/docs/reviews/evidence/gpt6-evolution/tablet-start.png differ diff --git a/docs/reviews/evidence/gpt6-evolution/transfer-budgets.json b/docs/reviews/evidence/gpt6-evolution/transfer-budgets.json new file mode 100644 index 0000000..51ae49b --- /dev/null +++ b/docs/reviews/evidence/gpt6-evolution/transfer-budgets.json @@ -0,0 +1,15 @@ +{ + "totals": { + "initialHtmlCriticalCss": 33252, + "initialApplicationJs": 90134, + "heroPoster": 189323, + "initialFonts": 69040, + "firstLiveScene": 279010, + "deferredProjectVisuals": 198652, + "highTierExtras": 0, + "deferredNonVideo": 477662, + "deferredAudio": 15307863 + }, + "violations": [], + "scope": "Transfer-only check. Not runtime performance certification." +} diff --git a/docs/reviews/gpt6-evolution-direction.md b/docs/reviews/gpt6-evolution-direction.md new file mode 100644 index 0000000..6edd47c --- /dev/null +++ b/docs/reviews/gpt6-evolution-direction.md @@ -0,0 +1,24 @@ +# Midnight grand tour + +Base: main 642f5b5, containing approved content/media and finished adaptive audio. Isolated branch: codex/gpt6-portfolio-evolution. Existing untracked work stays in the original checkout. + +## Visual grammar + +A continuous midnight highway, silver-blue bodywork, coral horizon, cyan interface accents. Retain the authored GLB and runtime terrain. Geist remains the self-hosted type family, now with open display composition and quieter supporting labels. Panels use a consistent 16px outer / 8px inner radius; controls use 8px. Keep the dark theme required by the night-drive identity. All approved wording, metadata, media, links, navigation and audio contracts are immutable for this pass, overriding generic design-skill copy and theme rules. + +## Scene and document ledger + +- Start: open name and equal roles on the left, rear three-quarter vehicle and low coral sun on the right. Lower, longer lens establishes scale. +- Profile: world shifts left to leave a calm reading surface on the right. +- Experience: world shifts right; broad timeline with clear supporting operations grouping. +- Infrastructure: centered panorama behind a wide architectural plate, generous diagram nodes and a distinct operations case. +- Selected builds: broad media-first staging. Portrait screenshots become a carefully spaced three-screen presentation; Footy Tracker receives an evidence-led split composition because no screenshot is approved. All captions and evidence remain readable. +- Additional work: a two-column compact collection instead of another narrow rail. +- Toolkit: horizontal groups with larger recognizable technology marks. +- Contact: camera settles right, warmer horizon, clear destination with unchanged contact actions. + +Use render-only camera endpoints and smoothing; never change canonical journey state or audio inputs. Geometry, lights and shader materials remain persistent. Preserve current native scrolling, sign contract, quality governor, resource disposal and fallback. + +## Implementation and verification + +Work in existing Vue / native CSS / Three.js. No new dependencies. Build on the existing scene and components. Add a focused presentation stylesheet and camera ledger, improve sun/terrain/lighting, and keep semantic markup unchanged wherever possible. Inspect the compiled, validated preview across desktop, tablet, phone and short sizes; exercise navigation, scroll/reversal, resize, static/reduced motion and audio controls. No test edits. Run existing build checks once, then typecheck/client/SSR/prerender/artifact/transfer checks after implementation. Record actual observations and limitations. diff --git a/docs/reviews/gpt6-evolution-review.md b/docs/reviews/gpt6-evolution-review.md new file mode 100644 index 0000000..7a44993 --- /dev/null +++ b/docs/reviews/gpt6-evolution-review.md @@ -0,0 +1,43 @@ +# Midnight Grand Tour — visual review + +Implemented on `codex/gpt6-portfolio-evolution`, based on main `642f5b5`, in `.worktrees/gpt6-portfolio-evolution`. Local preview: http://localhost:41812. Ready for visual review; not merged or deployed. + +## The visible changes + +The opening now combines an open typographic introduction with a longer-lens night drive. A larger coral horizon, broad cyan mountain waves and cooler metallic reflections establish one visual language. Camera compositions ease between chapters without changing native scrolling or the canonical journey/audio state. + +The Blender-authored car retains the original wedge body, now with a swept floating wing, turbine-style wheel faces, rear-window louvers, segmented coral lamps and cyan rear guides. The editable source and provenance live in `assets/blender/evolution/`. It exports into eight material groups and 18,564 triangles; reflections are baked once into a small procedural environment, with no per-frame reflection pass. + +The professional material has more space: a broad career timeline, an architectural infrastructure plate, larger real project screenshots, an evidence-led Footy Tracker layout, and clearer toolkit groups. Project images open at full size. Controls keep their existing behavior, with refined spacing, focus and touch states. Static and reduced-motion visitors receive desktop or phone artwork captured from the real world. + +## Before and after + +Desktop before: + +![Approved baseline, desktop](evidence/gpt6-evolution/before-desktop.png) + +Desktop after: + +![Evolution, desktop](evidence/gpt6-evolution/after-desktop.png) + +[Phone before](evidence/gpt6-evolution/before-phone.png) · [Phone after](evidence/gpt6-evolution/after-phone.png) · [Tablet opening](evidence/gpt6-evolution/tablet-start.png) + +[Project screenshots](evidence/gpt6-evolution/after-work.png) · [Infrastructure](evidence/gpt6-evolution/tablet-infrastructure.png) · [Toolkit](evidence/gpt6-evolution/desktop-toolkit.png) · [Contact](evidence/gpt6-evolution/desktop-contact.png) + +[Blender detail render](evidence/gpt6-evolution/blender-coachwork.png) · [Recorded drive, scroll reversal and navigation](evidence/gpt6-evolution/nightdrive-motion.webm) + +## Preservation and practical checks + +- Normalized rendered main text is identical to the freshly built approved baseline: 12,333 characters. Original content links match. The eight section IDs and their order are unchanged. +- Content, approved metadata, audio source, canonical journey source and test files have no changes. All compiled approved project media and audio files match the baseline byte for byte. +- Final typecheck, client build, server build, validated prerender, artifact validation and transfer-budget check passed. No budget limits were changed. The initial baseline build also ran the required existing unit suite: 662 checks passed; it was not repeatedly rerun during visual iteration. No tests were added or edited. +- Inspected all chapters at desktop 1440×900, tablet 768×1024, phone 390×844, narrow 320×568 and short 1024×600. No horizontal overflow was observed. Exercised menu navigation, scroll, fast jumps, reversal, resizing, keyboard scrolling and focus states. +- Soundtrack reached the running state; mute/unmute, volume at 25%, pause/resume motion and graphics selection worked. Static mode, system reduced motion and a no-JavaScript browser retained content and appropriate poster artwork. Full-size project-image navigation worked. +- The final direct preview produced no JavaScript errors or console warnings, including after graphics-quality changes. Resetting texture-upload flags between renderer generations fixed the observed WebGL warning. +- Final compressed-transfer totals: initial application 90,134 bytes; first live scene including the car 279,010 bytes; both fallback posters together 189,323 bytes. All transfer checks passed. Detailed totals are in `evidence/gpt6-evolution/transfer-budgets.json`. + +## Practical limits + +The inspection browser reports ANGLE/SwiftShader software rendering. A short direct Low-quality sample averaged about 12 fps at 1440×900 and 40 fps at 390×844; an instrumented High-quality sample was slower. These are software-renderer measurements, not a hardware-GPU performance certification. Existing adaptive quality and Static controls remain available. Vite still reports the existing large deferred Three.js chunk warning; the measured compressed scene transfer remains within its budget. + +The recording shows actual browser output and is silent. Audio controls and preservation were checked separately; no claim is made of a complete auditory mix review or physical-device testing. diff --git a/src/App.vue b/src/App.vue index f2d6de6..957afd4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -6,6 +6,7 @@ import './styles/base.css'; import './styles/layout.css'; import './styles/components.css'; import './styles/world.css'; +import './styles/evolution.css'; import './styles/print.css'; defineProps<{ inputs: Readonly }>(); diff --git a/src/assets/hero-evolution-mobile.jpg b/src/assets/hero-evolution-mobile.jpg new file mode 100644 index 0000000..597c907 Binary files /dev/null and b/src/assets/hero-evolution-mobile.jpg differ diff --git a/src/assets/hero-evolution.jpg b/src/assets/hero-evolution.jpg new file mode 100644 index 0000000..03fb2c5 Binary files /dev/null and b/src/assets/hero-evolution.jpg differ diff --git a/src/assets/nightdrive-grand-tourer-evolution.glb b/src/assets/nightdrive-grand-tourer-evolution.glb new file mode 100644 index 0000000..8adb64c Binary files /dev/null and b/src/assets/nightdrive-grand-tourer-evolution.glb differ diff --git a/src/components/ProjectMedia.vue b/src/components/ProjectMedia.vue index a7f7f3b..4d96e8b 100644 --- a/src/components/ProjectMedia.vue +++ b/src/components/ProjectMedia.vue @@ -77,14 +77,16 @@ const posterUrl = computed(() => { :allow-automatic="allowAutomaticVideo" />
- + + +
{{ presentation.caption }}

{{ presentation.longDescription }} diff --git a/src/components/SceneCanvas.vue b/src/components/SceneCanvas.vue index 49e7cda..080ec44 100644 --- a/src/components/SceneCanvas.vue +++ b/src/components/SceneCanvas.vue @@ -4,6 +4,8 @@ import { useSceneController, type SceneHostState } from '@/composables/useSceneC import type { JourneySnapshot } from '@/journey/types'; import type { MotionPolicy, QualityTier } from '@/journey/motion-policy'; import { isTestFault, recordTestFaultEvent } from '@/testing/fault-hooks'; +import posterUrl from '@/assets/hero-evolution.jpg'; +import mobilePosterUrl from '@/assets/hero-evolution-mobile.jpg'; declare const __PORTFOLIO_TEST_BUILD__: boolean; const TEST_BUILD = typeof __PORTFOLIO_TEST_BUILD__ !== 'undefined' && __PORTFOLIO_TEST_BUILD__; @@ -53,16 +55,10 @@ if (posterFailed.value) recordTestFaultEvent('poster-failure'); :data-static-fallback="!showCanvas" :data-poster-failed="posterFailed" > - -

- - - - - - - -
+ + + +