diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..25bda40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+bin/
+obj/
+artifacts/
+*.user
+*.suo
+.vs/
+.idea/
diff --git a/Aquarium.Engine.slnx b/Aquarium.Engine.slnx
new file mode 100644
index 0000000..2eb83ff
--- /dev/null
+++ b/Aquarium.Engine.slnx
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/README.md b/README.md
index 40176cb..d1cfa08 100644
--- a/README.md
+++ b/README.md
@@ -30,3 +30,20 @@ Aquarium-first rather than framework-first.
Create the smallest C# executable that opens a window, owns the camera/Grid
invariant, and renders one sun plus orbiting planets with diegetic labels.
Everything else earns its way back in.
+
+## Run
+
+```powershell
+dotnet build Aquarium.Engine.slnx
+dotnet run --project src\Aquarium.Engine\Aquarium.Engine.csproj
+```
+
+Headless smoke:
+
+```powershell
+dotnet run --project src\Aquarium.Engine\Aquarium.Engine.csproj -- --headless
+```
+
+The first cut uses Stride as a NuGet-hosted shell through
+`Stride.CommunityToolkit.Windows`. Stride is scaffolding; the Aquarium owns the
+camera/Grid invariant and the renderer direction. See `docs/stride-boundary.md`.
diff --git a/docs/stride-boundary.md b/docs/stride-boundary.md
new file mode 100644
index 0000000..4110b5f
--- /dev/null
+++ b/docs/stride-boundary.md
@@ -0,0 +1,38 @@
+# Stride Boundary
+
+Stride enters Aquarium as a package dependency, not as vendored engine source.
+
+## What Stride Provides
+
+- Window/device/input scaffolding.
+- Game loop hosting.
+- Asset and graphics infrastructure where it helps.
+- A place to insert Aquarium-owned renderer passes.
+
+## What Aquarium Owns
+
+- Camera/Grid invariants.
+- World model and simulation state.
+- CultNet and CultCache boundaries.
+- Renderer architecture and field ownership.
+- Diegetic UI grammar.
+- Debug command surface.
+
+## First Invariant
+
+The camera target is a point on global XY. The Grid center is that same point.
+Grid radius is derived from camera zoom distance, not from camera pitch/yaw or
+screen projection. Orbiting the camera must not resize the Grid.
+
+Current code expresses this through `OrbitCameraRig` and `GridFrame`.
+
+## Current Warnings
+
+`Stride.CommunityToolkit.Windows` brings Stride's asset/compiler toolchain into
+the restore graph. The first restore/build currently reports transitive NuGet
+advisories for `Microsoft.Build.Tasks.Core`, `NuGet.Packaging`, and
+`NuGet.Protocol`, plus Stride's default missing `GameSettings` warning. These
+are dependency/tooling warnings, not Aquarium code warnings.
+
+Do not hide them globally yet. Track whether newer Stride/Toolkit packages clear
+the advisories before adding warning suppressions.
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..99b1511
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "version": "10.0.202",
+ "rollForward": "latestFeature"
+ }
+}
diff --git a/src/Aquarium.Engine/Aquarium.Engine.csproj b/src/Aquarium.Engine/Aquarium.Engine.csproj
new file mode 100644
index 0000000..9c2e394
--- /dev/null
+++ b/src/Aquarium.Engine/Aquarium.Engine.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Aquarium.Engine/AquariumHost.cs b/src/Aquarium.Engine/AquariumHost.cs
new file mode 100644
index 0000000..4e33cab
--- /dev/null
+++ b/src/Aquarium.Engine/AquariumHost.cs
@@ -0,0 +1,26 @@
+using Stride.CommunityToolkit.Engine;
+using Stride.Engine;
+using Stride.Games;
+
+namespace Aquarium.Engine;
+
+public static class AquariumHost
+{
+ public static int Run(string[] args)
+ {
+ using var game = new Game();
+ var runtime = new AquariumRuntime(new AquariumRuntimeOptions(ParseHeadless(args)));
+
+ game.Run(
+ context: null,
+ start: runtime.Start,
+ update: runtime.Update);
+
+ return 0;
+ }
+
+ private static bool ParseHeadless(IEnumerable args)
+ {
+ return args.Any(arg => string.Equals(arg, "--headless", StringComparison.OrdinalIgnoreCase));
+ }
+}
diff --git a/src/Aquarium.Engine/AquariumRuntime.cs b/src/Aquarium.Engine/AquariumRuntime.cs
new file mode 100644
index 0000000..2e3254b
--- /dev/null
+++ b/src/Aquarium.Engine/AquariumRuntime.cs
@@ -0,0 +1,49 @@
+using Stride.Core.Mathematics;
+using Stride.Engine;
+using Stride.Games;
+
+namespace Aquarium.Engine;
+
+public sealed class AquariumRuntime
+{
+ private readonly AquariumRuntimeOptions options;
+ private readonly OrbitCameraRig cameraRig;
+
+ private GridFrame gridFrame;
+
+ public AquariumRuntime(AquariumRuntimeOptions options)
+ {
+ this.options = options;
+ cameraRig = new OrbitCameraRig(
+ target: Vector2.Zero,
+ yawRadians: MathUtil.DegreesToRadians(35.0f),
+ pitchRadians: MathUtil.DegreesToRadians(42.0f),
+ distance: 24.0f);
+ gridFrame = GridFrame.FromCamera(cameraRig);
+ }
+
+ public void Start(Scene rootScene)
+ {
+ _ = rootScene;
+ Console.WriteLine("Aquarium Engine booted.");
+ Console.WriteLine($"Grid center: {gridFrame.Center}, radius: {gridFrame.Radius:0.00}");
+ Console.WriteLine("Stride is present as the host shell; Aquarium owns the invariants.");
+ }
+
+ public void Update(Scene rootScene, GameTime gameTime)
+ {
+ _ = rootScene;
+ _ = gameTime;
+
+ gridFrame = GridFrame.FromCamera(cameraRig);
+
+ if (options.Headless)
+ {
+ Console.WriteLine("Headless smoke reached first update.");
+ Environment.ExitCode = 0;
+ Environment.Exit(0);
+ }
+ }
+}
+
+public readonly record struct AquariumRuntimeOptions(bool Headless);
diff --git a/src/Aquarium.Engine/Program.cs b/src/Aquarium.Engine/Program.cs
new file mode 100644
index 0000000..d88c1aa
--- /dev/null
+++ b/src/Aquarium.Engine/Program.cs
@@ -0,0 +1,3 @@
+using Aquarium.Engine;
+
+return AquariumHost.Run(args);
diff --git a/src/Aquarium.Engine/World/GridFrame.cs b/src/Aquarium.Engine/World/GridFrame.cs
new file mode 100644
index 0000000..d976ef6
--- /dev/null
+++ b/src/Aquarium.Engine/World/GridFrame.cs
@@ -0,0 +1,11 @@
+using Stride.Core.Mathematics;
+
+namespace Aquarium.Engine;
+
+public readonly record struct GridFrame(Vector2 Center, float Radius)
+{
+ public static GridFrame FromCamera(OrbitCameraRig camera)
+ {
+ return new GridFrame(camera.Target, camera.Distance);
+ }
+}
diff --git a/src/Aquarium.Engine/World/OrbitCameraRig.cs b/src/Aquarium.Engine/World/OrbitCameraRig.cs
new file mode 100644
index 0000000..fa3934f
--- /dev/null
+++ b/src/Aquarium.Engine/World/OrbitCameraRig.cs
@@ -0,0 +1,53 @@
+using Stride.Core.Mathematics;
+
+namespace Aquarium.Engine;
+
+public sealed class OrbitCameraRig
+{
+ public OrbitCameraRig(Vector2 target, float yawRadians, float pitchRadians, float distance)
+ {
+ Target = target;
+ YawRadians = yawRadians;
+ PitchRadians = pitchRadians;
+ Distance = MathF.Max(1.0f, distance);
+ }
+
+ public Vector2 Target { get; private set; }
+
+ public float YawRadians { get; private set; }
+
+ public float PitchRadians { get; private set; }
+
+ public float Distance { get; private set; }
+
+ public Vector3 Position
+ {
+ get
+ {
+ var horizontal = Distance * MathF.Cos(PitchRadians);
+ var x = Target.X + horizontal * MathF.Cos(YawRadians);
+ var y = Target.Y + horizontal * MathF.Sin(YawRadians);
+ var z = Distance * MathF.Sin(PitchRadians);
+ return new Vector3(x, y, z);
+ }
+ }
+
+ public void Orbit(float yawDeltaRadians, float pitchDeltaRadians)
+ {
+ YawRadians += yawDeltaRadians;
+ PitchRadians = MathUtil.Clamp(
+ PitchRadians + pitchDeltaRadians,
+ MathUtil.DegreesToRadians(5.0f),
+ MathUtil.DegreesToRadians(85.0f));
+ }
+
+ public void Zoom(float wheelSteps)
+ {
+ Distance = MathF.Max(1.0f, Distance * MathF.Pow(0.92f, wheelSteps));
+ }
+
+ public void Pan(Vector2 gridDelta)
+ {
+ Target += gridDelta;
+ }
+}