diff --git a/bun.lock b/bun.lock
index 0dab289f..cd2c69df 100644
--- a/bun.lock
+++ b/bun.lock
@@ -24,7 +24,7 @@
         "@types/react-dom": "^19.0.3",
         "@vercel/node": "^5.1.7",
         "@vitejs/plugin-react": "^4.3.4",
-        "bun-match-svg": "^0.0.9",
+        "bun-match-svg": "^0.0.12",
         "circuit-json-to-connectivity-map": "^0.0.19",
         "circuit-to-svg": "^0.0.110",
         "clsx": "^2.1.1",
@@ -487,7 +487,7 @@
 
     "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
 
-    "bun-match-svg": ["bun-match-svg@0.0.9", "", { "dependencies": { "looks-same": "^9.0.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "bun-match-svg": "cli.ts" } }, "sha512-WISE8cUd3ztIEbYRymuxs+l4qwRviHIhyRQHmDz26vnhKON9g+Ur+2L3pfJrffpGiYWGF/jtWoWmYRQiaimTxg=="],
+    "bun-match-svg": ["bun-match-svg@0.0.12", "", { "dependencies": { "looks-same": "^9.0.1" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "bun-match-svg": "cli.ts" } }, "sha512-qjQtTwRvuqGDe8xza1OEm3OMA4bUEuFDl4i4y1vH9GwdEegqB9x1/H5+AjoRwQUcMmt5kP9bfPdplkBWxmlfWg=="],
 
     "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="],
 
diff --git a/lib/solvers/HighDensitySolver/IntraNodeSolver.ts b/lib/solvers/HighDensitySolver/IntraNodeSolver.ts
index 5b3012ea..2b8ae54e 100644
--- a/lib/solvers/HighDensitySolver/IntraNodeSolver.ts
+++ b/lib/solvers/HighDensitySolver/IntraNodeSolver.ts
@@ -192,7 +192,7 @@ export class IntraNodeRouteSolver extends BaseSolver {
             )
           : this.solvedRoutes,
         futureConnections: this.unsolvedConnections,
-        layerCount: 2,
+        layerCount: this.nodeWithPortPoints.availableZ?.length ?? 2,
         hyperParameters: this.hyperParameters,
         connMap: this.connMap,
       })
diff --git a/lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts b/lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts
index 4593549e..0269557a 100644
--- a/lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts
+++ b/lib/solvers/HighDensitySolver/SingleHighDensityRouteSolver.ts
@@ -282,7 +282,7 @@ export class SingleHighDensityRouteSolver extends BaseSolver {
   computeG(node: Node) {
     return (
       (node.parent?.g ?? 0) +
-      (node.z === 0 ? 0 : this.viaPenaltyDistance) +
+      (node.parent && node.z !== node.parent.z ? this.viaPenaltyDistance : 0) +
       distance(node, node.parent!)
     )
   }
@@ -342,26 +342,23 @@ export class SingleHighDensityRouteSolver extends BaseSolver {
       }
     }
 
-    const viaNeighbor = {
-      ...node,
-      parent: node,
-      z: node.z === 0 ? this.layerCount - 1 : 0,
-    }
-
-    if (
-      !this.exploredNodes.has(this.getNodeKey(viaNeighbor)) &&
-      !this.isNodeTooCloseToObstacle(
-        viaNeighbor,
-        this.viaDiameter / 2 + this.obstacleMargin / 2,
-        true,
-      ) &&
-      !this.isNodeTooCloseToEdge(viaNeighbor, true)
-    ) {
-      viaNeighbor.g = this.computeG(viaNeighbor)
-      viaNeighbor.h = this.computeH(viaNeighbor)
-      viaNeighbor.f = this.computeF(viaNeighbor.g, viaNeighbor.h)
-
-      neighbors.push(viaNeighbor)
+    for (let z = 0; z < this.layerCount; z++) {
+      if (z === node.z) continue
+      const viaNeighbor = { ...node, parent: node, z }
+      if (
+        !this.exploredNodes.has(this.getNodeKey(viaNeighbor)) &&
+        !this.isNodeTooCloseToObstacle(
+          viaNeighbor,
+          this.viaDiameter / 2 + this.obstacleMargin / 2,
+          true,
+        ) &&
+        !this.isNodeTooCloseToEdge(viaNeighbor, true)
+      ) {
+        viaNeighbor.g = this.computeG(viaNeighbor)
+        viaNeighbor.h = this.computeH(viaNeighbor)
+        viaNeighbor.f = this.computeF(viaNeighbor.g, viaNeighbor.h)
+        neighbors.push(viaNeighbor)
+      }
     }
 
     return neighbors
diff --git a/package.json b/package.json
index df6b8909..ac6f0664 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
     "@types/react-dom": "^19.0.3",
     "@vercel/node": "^5.1.7",
     "@vitejs/plugin-react": "^4.3.4",
-    "bun-match-svg": "^0.0.9",
+    "bun-match-svg": "^0.0.12",
     "circuit-json-to-connectivity-map": "^0.0.19",
     "circuit-to-svg": "^0.0.110",
     "clsx": "^2.1.1",
diff --git a/tests/__snapshots__/e2e-4layer.snap.svg b/tests/__snapshots__/e2e-4layer.snap.svg
new file mode 100644
index 00000000..83aa978e
--- /dev/null
+++ b/tests/__snapshots__/e2e-4layer.snap.svg
@@ -0,0 +1,84 @@
+
\ No newline at end of file
diff --git a/tests/e2e-4layer.test.ts b/tests/e2e-4layer.test.ts
new file mode 100644
index 00000000..91b6f869
--- /dev/null
+++ b/tests/e2e-4layer.test.ts
@@ -0,0 +1,14 @@
+import { expect, test } from "bun:test"
+import { CapacityMeshSolver } from "../lib"
+import type { SimpleRouteJson } from "../lib/types"
+import srj from "./fixtures/simple-4layer.json"
+import { convertSrjToGraphicsObject } from "./fixtures/convertSrjToGraphicsObject"
+
+test("should solve 4 layer board", async () => {
+  const solver = new CapacityMeshSolver(srj as SimpleRouteJson)
+  await solver.solve()
+  const result = solver.getOutputSimpleRouteJson()
+  expect(convertSrjToGraphicsObject(result)).toMatchGraphicsSvg(
+    import.meta.path,
+  )
+})
diff --git a/tests/fixtures/convertSrjToGraphicsObject.ts b/tests/fixtures/convertSrjToGraphicsObject.ts
index eb27b16b..8df0ff45 100644
--- a/tests/fixtures/convertSrjToGraphicsObject.ts
+++ b/tests/fixtures/convertSrjToGraphicsObject.ts
@@ -10,7 +10,7 @@ export const convertSrjToGraphicsObject = (srj: SimpleRouteJson) => {
   const points: Point[] = []
 
   const colorMap: Record = getColorMap(srj)
-  const layerCount = 2
+  const layerCount = srj.layerCount ?? 2
 
   // Add points for each connection's pointsToConnect
   if (srj.connections) {
@@ -45,7 +45,7 @@ export const convertSrjToGraphicsObject = (srj: SimpleRouteJson) => {
             radius: 0.3, // 0.6 via diameter
             fill: "blue",
             stroke: "none",
-            layer: "z0,1",
+            layer: `z${mapLayerNameToZ(routePoint.from_layer!, layerCount)},${mapLayerNameToZ(routePoint.to_layer!, layerCount)}`,
           })
         } else if (
           routePoint.route_type === "wire" &&
diff --git a/tests/fixtures/simple-4layer.json b/tests/fixtures/simple-4layer.json
new file mode 100644
index 00000000..a42afd38
--- /dev/null
+++ b/tests/fixtures/simple-4layer.json
@@ -0,0 +1,31 @@
+{
+  "layerCount": 4,
+  "minTraceWidth": 0.15,
+  "obstacles": [
+    {
+      "type": "rect",
+      "layers": ["top"],
+      "center": { "x": 5, "y": 5 },
+      "width": 1,
+      "height": 1,
+      "connectedTo": []
+    }
+  ],
+  "connections": [
+    {
+      "name": "conn1",
+      "pointsToConnect": [
+        { "x": 2, "y": 2, "layer": "top" },
+        { "x": 8, "y": 2, "layer": "inner1" }
+      ]
+    },
+    {
+      "name": "conn2",
+      "pointsToConnect": [
+        { "x": 2, "y": 8, "layer": "top" },
+        { "x": 8, "y": 8, "layer": "bottom" }
+      ]
+    }
+  ],
+  "bounds": { "minX": 0, "maxX": 10, "minY": 0, "maxY": 10 }
+}