diff --git a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.BoundaryFirst.CorridorReroute.cs b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.BoundaryFirst.CorridorReroute.cs
new file mode 100644
index 000000000..5425eba68
--- /dev/null
+++ b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.BoundaryFirst.CorridorReroute.cs
@@ -0,0 +1,126 @@
+namespace StellaOps.ElkSharp;
+
+internal static partial class ElkEdgeRouterIterative
+{
+ ///
+ /// Detects long horizontal sweeps with under-node violations and reroutes
+ /// them through the top corridor (above the graph), similar to how backward
+ /// edges are routed through external corridors.
+ ///
+ private static ElkRoutedEdge[]? RerouteLongSweepsThroughCorridor(
+ ElkRoutedEdge[] edges,
+ ElkPositionedNode[] nodes,
+ ElkLayoutDirection direction,
+ double minLineClearance)
+ {
+ if (direction != ElkLayoutDirection.LeftToRight || nodes.Length == 0)
+ {
+ return null;
+ }
+
+ var graphMinY = nodes.Min(n => n.Y);
+ var graphMaxY = nodes.Max(n => n.Y + n.Height);
+ var graphWidth = nodes.Max(n => n.X + n.Width) - nodes.Min(n => n.X);
+ var minSweepLength = graphWidth * 0.4d;
+ var corridorY = graphMinY - 56d;
+ var nodesById = nodes.ToDictionary(n => n.Id, StringComparer.Ordinal);
+
+ var underNodeSeverity = new Dictionary(StringComparer.Ordinal);
+ ElkEdgeRoutingScoring.CountUnderNodeViolations(edges, nodes, underNodeSeverity, 1);
+ if (underNodeSeverity.Count == 0)
+ {
+ return null;
+ }
+
+ ElkRoutedEdge[]? result = null;
+
+ for (var edgeIndex = 0; edgeIndex < edges.Length; edgeIndex++)
+ {
+ var edge = edges[edgeIndex];
+ if (!underNodeSeverity.ContainsKey(edge.Id))
+ {
+ continue;
+ }
+
+ var path = ExtractPath(edge);
+ if (path.Count < 2)
+ {
+ continue;
+ }
+
+ // Find the longest horizontal segment.
+ var bestSegStart = -1;
+ var bestSegLength = 0d;
+ for (var i = 0; i < path.Count - 1; i++)
+ {
+ if (Math.Abs(path[i].Y - path[i + 1].Y) > 2d)
+ {
+ continue;
+ }
+
+ var segLength = Math.Abs(path[i + 1].X - path[i].X);
+ if (segLength > bestSegLength)
+ {
+ bestSegLength = segLength;
+ bestSegStart = i;
+ }
+ }
+
+ if (bestSegStart < 0 || bestSegLength < minSweepLength)
+ {
+ continue;
+ }
+
+ // Build corridor path: source exit → up to corridor → across → down to target.
+ var sourcePoint = path[0];
+ var targetPoint = path[^1];
+ var exitX = sourcePoint.X;
+ var approachX = targetPoint.X;
+
+ // Determine corridor direction based on source position relative to graph.
+ var sourceNode = nodesById.GetValueOrDefault(edge.SourceNodeId ?? string.Empty);
+ var sourceTopY = sourceNode?.Y ?? sourcePoint.Y;
+
+ // Build corridor path with a perpendicular exit stub.
+ // The stub prevents NormalizeBoundaryAngles from collapsing
+ // the vertical corridor segment (it removes path[1] while
+ // path[1].X == sourceX, so the stub at exitX+24 survives).
+ var stubX = exitX + 24d;
+ var newPath = new List
+ {
+ sourcePoint,
+ new() { X = stubX, Y = sourcePoint.Y },
+ new() { X = stubX, Y = corridorY },
+ new() { X = approachX, Y = corridorY },
+ targetPoint,
+ };
+
+ result ??= (ElkRoutedEdge[])edges.Clone();
+ result[edgeIndex] = new ElkRoutedEdge
+ {
+ Id = edge.Id,
+ SourceNodeId = edge.SourceNodeId,
+ TargetNodeId = edge.TargetNodeId,
+ SourcePortId = edge.SourcePortId,
+ TargetPortId = edge.TargetPortId,
+ Kind = edge.Kind,
+ Label = edge.Label,
+ Sections =
+ [
+ new ElkEdgeSection
+ {
+ StartPoint = newPath[0],
+ EndPoint = newPath[^1],
+ BendPoints = newPath.Skip(1).Take(newPath.Count - 2).ToArray(),
+ },
+ ],
+ };
+
+ ElkLayoutDiagnostics.LogProgress(
+ $"Corridor reroute: {edge.Id} sweep={bestSegLength:F0}px " +
+ $"from Y={path[bestSegStart].Y:F0} to corridorY={corridorY:F0}");
+ }
+
+ return result;
+ }
+}
diff --git a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
index dacb91e16..c172ca50e 100644
--- a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
+++ b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
@@ -88,6 +88,34 @@ internal static partial class ElkEdgeRouterIterative
}
}
+ // Reroute long horizontal sweeps through the top corridor.
+ // Edges spanning > half the graph width with under-node violations
+ // should route above the graph (like backward edges) instead of
+ // cutting straight through the node field.
+ if (current.RetryState.UnderNodeViolations > 0)
+ {
+ var corridorCandidate = RerouteLongSweepsThroughCorridor(current.Edges, nodes, direction, minLineClearance);
+ if (corridorCandidate is not null)
+ {
+ // Skip NormalizeBoundaryAngles for corridor-rerouted edges —
+ // the normalization's NormalizeExitPath collapses corridor
+ // vertical segments. The corridor path already has a correct
+ // perpendicular exit stub.
+ var corridorScore = ElkEdgeRoutingScoring.ComputeScore(corridorCandidate, nodes);
+ if (corridorScore.Value > current.Score.Value
+ && corridorScore.NodeCrossings <= current.Score.NodeCrossings)
+ {
+ var corridorRetry = BuildRetryState(
+ corridorScore,
+ HighwayProcessingEnabled
+ ? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(corridorCandidate, nodes).Count
+ : 0);
+ current = current with { Score = corridorScore, RetryState = corridorRetry, Edges = corridorCandidate };
+ ElkLayoutDiagnostics.LogProgress($"Hybrid winner refinement after corridor reroute: {DescribeSolution(current)}");
+ }
+ }
+ }
+
// Targeted under-node elevation with net-total promotion.
// ElevateUnderNodeViolations can fix remaining under-node edges
// (gateway-exit lanes, long horizontal sweeps) but the standard