diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
index a0a12ea621d20e4ed1952497e15860695d6581a7..fa82cccdacfce53c5b343b726da987498820f0f5 100644
--- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
+++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
@@ -1249,7 +1249,7 @@
         if (isnan(z_values[i][j])) {                  // Invalid mesh point?
 
           // Skip points the probe can't reach
-          if (!position_is_reachable_by_probe(mesh_index_to_xpos(i), mesh_index_to_ypos(j)))
+          if (!probe.can_reach(mesh_index_to_xpos(i), mesh_index_to_ypos(j)))
             continue;
 
           found_a_NAN = true;
@@ -1316,7 +1316,7 @@
           // Also for round beds, there are grid points outside the bed the nozzle can't reach.
           // Prune them from the list and ignore them till the next Phase (manual nozzle probing).
 
-          if (probe_relative ? !position_is_reachable_by_probe(mpos) : !position_is_reachable(mpos))
+          if (!(probe_relative ? probe.can_reach(mpos) : position_is_reachable(mpos)))
             continue;
 
           // Reachable. Check if it's the best_so_far location to the nozzle.
diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp
index 5173505164fb69b18745122568abc2ccc73457c9..c81e5f0a8a7d417c8fbc6bd1e05074e7e1cb94e1 100644
--- a/Marlin/src/gcode/bedlevel/abl/G29.cpp
+++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp
@@ -417,17 +417,7 @@ G29_TYPE GcodeSuite::G29() {
         );
       }
 
-      if (
-        #if IS_SCARA || ENABLED(DELTA)
-             !position_is_reachable_by_probe(probe_position_lf.x, 0)
-          || !position_is_reachable_by_probe(probe_position_rb.x, 0)
-          || !position_is_reachable_by_probe(0, probe_position_lf.y)
-          || !position_is_reachable_by_probe(0, probe_position_rb.y)
-        #else
-             !position_is_reachable_by_probe(probe_position_lf)
-          || !position_is_reachable_by_probe(probe_position_rb)
-        #endif
-      ) {
+      if (!probe.good_bounds(probe_position_lf, probe_position_rb)) {
         SERIAL_ECHOLNPGM("? (L,R,F,B) out of bounds.");
         G29_RETURN(false);
       }
@@ -704,7 +694,7 @@ G29_TYPE GcodeSuite::G29() {
 
           #if IS_KINEMATIC
             // Avoid probing outside the round or hexagonal area
-            if (!position_is_reachable_by_probe(probePos)) continue;
+            if (!probe.can_reach(probePos)) continue;
           #endif
 
           if (verbose_level) SERIAL_ECHOLNPAIR("Probing mesh point ", int(pt_index), "/", int(GRID_MAX_POINTS), ".");
diff --git a/Marlin/src/gcode/calibrate/G34_M422.cpp b/Marlin/src/gcode/calibrate/G34_M422.cpp
index e7bc63828f1f86ff7e22bbac0a906d61a20168e3..7144038ebd391b18042a075794967e27bb85cab8 100644
--- a/Marlin/src/gcode/calibrate/G34_M422.cpp
+++ b/Marlin/src/gcode/calibrate/G34_M422.cpp
@@ -432,11 +432,11 @@ void GcodeSuite::M422() {
   };
 
   if (is_probe_point) {
-    if (!position_is_reachable_by_probe(pos.x, Y_CENTER)) {
+    if (!probe.can_reach(pos.x, Y_CENTER)) {
       SERIAL_ECHOLNPGM("?(X) out of bounds.");
       return;
     }
-    if (!position_is_reachable_by_probe(pos)) {
+    if (!probe.can_reach(pos)) {
       SERIAL_ECHOLNPGM("?(Y) out of bounds.");
       return;
     }
diff --git a/Marlin/src/gcode/calibrate/G76_M871.cpp b/Marlin/src/gcode/calibrate/G76_M871.cpp
index 4340084ec6d4e164a01b975f1d654ec20dcef063..8cd9c8a0cf47a5ddb08c283e353676cc8f7891c6 100644
--- a/Marlin/src/gcode/calibrate/G76_M871.cpp
+++ b/Marlin/src/gcode/calibrate/G76_M871.cpp
@@ -116,7 +116,7 @@ void GcodeSuite::G76() {
       temp_comp.measure_point_x - probe.offset_xy.x,
       temp_comp.measure_point_y - probe.offset_xy.y
     );
-    if (!position_is_reachable_by_probe(destination)) {
+    if (!probe.can_reach(destination)) {
       SERIAL_ECHOLNPGM("!Probe position unreachable - aborting.");
       return;
     }
diff --git a/Marlin/src/gcode/calibrate/M48.cpp b/Marlin/src/gcode/calibrate/M48.cpp
index 8fba7a646fe90def235aba8925d27390219f3af3..fb8651779951f433c0d976b4a64673e9bdd9cd1a 100644
--- a/Marlin/src/gcode/calibrate/M48.cpp
+++ b/Marlin/src/gcode/calibrate/M48.cpp
@@ -80,11 +80,11 @@ void GcodeSuite::M48() {
   xy_float_t next_pos = current_position;
 
   const xy_pos_t probe_pos = {
-    parser.linearval('X', next_pos.x + probe.offset_xy.x),
-    parser.linearval('Y', next_pos.y + probe.offset_xy.y)
+    parser.linearval('X', next_pos.x + probe.offset_xy.x),  // If no X use the probe's current X position
+    parser.linearval('Y', next_pos.y + probe.offset_xy.y)   // If no Y, ditto
   };
 
-  if (!position_is_reachable_by_probe(probe_pos)) {
+  if (!probe.can_reach(probe_pos)) {
     SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
     return;
   }
@@ -179,7 +179,7 @@ void GcodeSuite::M48() {
           #else
             // If we have gone out too far, we can do a simple fix and scale the numbers
             // back in closer to the origin.
-            while (!position_is_reachable_by_probe(next_pos)) {
+            while (!probe.can_reach(next_pos)) {
               next_pos *= 0.8f;
               if (verbose_level > 3)
                 SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
diff --git a/Marlin/src/gcode/probe/G30.cpp b/Marlin/src/gcode/probe/G30.cpp
index 8b2ada9ceca8a0f43a3b226b6bb39869fdc61b70..21d56b3fd47b3acdf35fcf0fdb482c5556f686a5 100644
--- a/Marlin/src/gcode/probe/G30.cpp
+++ b/Marlin/src/gcode/probe/G30.cpp
@@ -43,7 +43,7 @@ void GcodeSuite::G30() {
   const xy_pos_t pos = { parser.linearval('X', current_position.x + probe.offset_xy.x),
                          parser.linearval('Y', current_position.y + probe.offset_xy.y) };
 
-  if (!position_is_reachable_by_probe(pos)) return;
+  if (!probe.can_reach(pos)) return;
 
   // Disable leveling so the planner won't mess with us
   #if HAS_LEVELING
diff --git a/Marlin/src/module/motion.h b/Marlin/src/module/motion.h
index c50da25a3e272fdea7ab0c604ec4211bccd82a27..f1c2dc069cff645746d7d7edc757ffbea996a932 100644
--- a/Marlin/src/module/motion.h
+++ b/Marlin/src/module/motion.h
@@ -30,10 +30,6 @@
 
 #include "../inc/MarlinConfig.h"
 
-#if HAS_BED_PROBE
-  #include "probe.h"
-#endif
-
 #if IS_SCARA
   #include "scara.h"
 #endif
@@ -58,7 +54,7 @@ FORCE_INLINE bool homing_needed() {
 }
 
 // Error margin to work around float imprecision
-constexpr float slop = 0.0001;
+constexpr float fslop = 0.0001;
 
 extern bool relative_mode;
 
@@ -306,7 +302,7 @@ void homeaxis(const AxisEnum axis);
   // Return true if the given point is within the printable area
   inline bool position_is_reachable(const float &rx, const float &ry, const float inset=0) {
     #if ENABLED(DELTA)
-      return HYPOT2(rx, ry) <= sq(DELTA_PRINTABLE_RADIUS - inset + slop);
+      return HYPOT2(rx, ry) <= sq(DELTA_PRINTABLE_RADIUS - inset + fslop);
     #elif IS_SCARA
       const float R2 = HYPOT2(rx - SCARA_OFFSET_X, ry - SCARA_OFFSET_Y);
       return (
@@ -322,67 +318,24 @@ void homeaxis(const AxisEnum axis);
     return position_is_reachable(pos.x, pos.y, inset);
   }
 
-  #if HAS_BED_PROBE
-
-    #if HAS_PROBE_XY_OFFSET
-
-      // Return true if the both nozzle and the probe can reach the given point.
-      // Note: This won't work on SCARA since the probe offset rotates with the arm.
-      inline bool position_is_reachable_by_probe(const float &rx, const float &ry) {
-        return position_is_reachable(rx - probe.offset_xy.x, ry - probe.offset_xy.y)
-               && position_is_reachable(rx, ry, ABS(MIN_PROBE_EDGE));
-      }
-
-    #else
-
-      FORCE_INLINE bool position_is_reachable_by_probe(const float &rx, const float &ry) {
-        return position_is_reachable(rx, ry, MIN_PROBE_EDGE);
-      }
-
-    #endif
-
-  #endif // HAS_BED_PROBE
-
 #else // CARTESIAN
 
   // Return true if the given position is within the machine bounds.
   inline bool position_is_reachable(const float &rx, const float &ry) {
-    if (!WITHIN(ry, Y_MIN_POS - slop, Y_MAX_POS + slop)) return false;
+    if (!WITHIN(ry, Y_MIN_POS - fslop, Y_MAX_POS + fslop)) return false;
     #if ENABLED(DUAL_X_CARRIAGE)
       if (active_extruder)
-        return WITHIN(rx, X2_MIN_POS - slop, X2_MAX_POS + slop);
+        return WITHIN(rx, X2_MIN_POS - fslop, X2_MAX_POS + fslop);
       else
-        return WITHIN(rx, X1_MIN_POS - slop, X1_MAX_POS + slop);
+        return WITHIN(rx, X1_MIN_POS - fslop, X1_MAX_POS + fslop);
     #else
-      return WITHIN(rx, X_MIN_POS - slop, X_MAX_POS + slop);
+      return WITHIN(rx, X_MIN_POS - fslop, X_MAX_POS + fslop);
     #endif
   }
   inline bool position_is_reachable(const xy_pos_t &pos) { return position_is_reachable(pos.x, pos.y); }
 
-  #if HAS_BED_PROBE
-
-    /**
-     * Return whether the given position is within the bed, and whether the nozzle
-     * can reach the position required to put the probe at the given position.
-     *
-     * Example: For a probe offset of -10,+10, then for the probe to reach 0,0 the
-     *          nozzle must be be able to reach +10,-10.
-     */
-    inline bool position_is_reachable_by_probe(const float &rx, const float &ry) {
-      return position_is_reachable(rx - probe.offset_xy.x, ry - probe.offset_xy.y)
-          && WITHIN(rx, probe.min_x() - slop, probe.max_x() + slop)
-          && WITHIN(ry, probe.min_y() - slop, probe.max_y() + slop);
-    }
-
-  #endif // HAS_BED_PROBE
-
 #endif // CARTESIAN
 
-#if !HAS_BED_PROBE
-  FORCE_INLINE bool position_is_reachable_by_probe(const float &rx, const float &ry) { return position_is_reachable(rx, ry); }
-#endif
-FORCE_INLINE bool position_is_reachable_by_probe(const xy_pos_t &pos) { return position_is_reachable_by_probe(pos.x, pos.y); }
-
 /**
  * Duplication mode
  */
diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp
index 858dcae747acb2569ab8aeb51cd6e603a3922fe3..67c1655374b865188c851550f87ccf388b527ee8 100644
--- a/Marlin/src/module/probe.cpp
+++ b/Marlin/src/module/probe.cpp
@@ -89,7 +89,7 @@ Probe probe;
 xyz_pos_t Probe::offset; // Initialized by settings.load()
 
 #if HAS_PROBE_XY_OFFSET
-  const xyz_pos_t &Probe::offset_xy = probe.offset;
+  const xyz_pos_t &Probe::offset_xy = Probe::offset;
 #endif
 
 #if ENABLED(Z_PROBE_SLED)
@@ -727,7 +727,7 @@ float Probe::probe_at_point(const float &rx, const float &ry, const ProbePtRaise
   // TODO: Adapt for SCARA, where the offset rotates
   xyz_pos_t npos = { rx, ry };
   if (probe_relative) {                                     // The given position is in terms of the probe
-    if (!position_is_reachable_by_probe(npos)) {
+    if (!can_reach(npos)) {
       if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Position Not Reachable");
       return NAN;
     }
diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h
index 6f17090e69c05098c0c7cc8fb1361d841cce7f7a..84a8f953cab2a55d7220597a8a9132facabb8fea 100644
--- a/Marlin/src/module/probe.h
+++ b/Marlin/src/module/probe.h
@@ -27,6 +27,8 @@
 
 #include "../inc/MarlinConfig.h"
 
+#include "motion.h"
+
 #if HAS_BED_PROBE
   enum ProbePtRaise : uint8_t {
     PROBE_PT_NONE,      // No raise or stow after run_z_probe
@@ -45,6 +47,39 @@ public:
 
     static bool set_deployed(const bool deploy);
 
+
+    #if IS_KINEMATIC
+
+      #if HAS_PROBE_XY_OFFSET
+        // Return true if the both nozzle and the probe can reach the given point.
+        // Note: This won't work on SCARA since the probe offset rotates with the arm.
+        static inline bool can_reach(const float &rx, const float &ry) {
+          return position_is_reachable(rx - offset_xy.x, ry - offset_xy.y) // The nozzle can go where it needs to go?
+              && position_is_reachable(rx, ry, ABS(MIN_PROBE_EDGE));       // Can the nozzle also go near there?
+        }
+      #else
+        FORCE_INLINE static bool can_reach(const float &rx, const float &ry) {
+          return position_is_reachable(rx, ry, MIN_PROBE_EDGE);
+        }
+      #endif
+
+    #else
+
+      /**
+       * Return whether the given position is within the bed, and whether the nozzle
+       * can reach the position required to put the probe at the given position.
+       *
+       * Example: For a probe offset of -10,+10, then for the probe to reach 0,0 the
+       *          nozzle must be be able to reach +10,-10.
+       */
+      static inline bool can_reach(const float &rx, const float &ry) {
+        return position_is_reachable(rx - offset_xy.x, ry - offset_xy.y)
+            && WITHIN(rx, min_x() - fslop, max_x() + fslop)
+            && WITHIN(ry, min_y() - fslop, max_y() + fslop);
+      }
+
+    #endif
+
     #ifdef Z_AFTER_PROBING
       static void move_z_after_probing();
     #endif
@@ -62,8 +97,22 @@ public:
 
     static bool set_deployed(const bool) { return false; }
 
+    FORCE_INLINE static bool can_reach(const float &rx, const float &ry) { return position_is_reachable(rx, ry); }
+
   #endif
 
+  FORCE_INLINE static bool can_reach(const xy_pos_t &pos) { return can_reach(pos.x, pos.y); }
+
+  FORCE_INLINE static bool good_bounds(const xy_pos_t &lf, const xy_pos_t &rb) {
+    return (
+      #if IS_KINEMATIC
+         can_reach(lf.x, 0) && can_reach(rb.x, 0) && can_reach(0, lf.y) && can_reach(0, rb.y)
+      #else
+         can_reach(lf) && can_reach(rb)
+      #endif
+    );
+  }
+
   // Use offset_xy for read only access
   // More optimal the XY offset is known to always be zero.
   #if HAS_PROBE_XY_OFFSET