diff --git a/FS25_ADSmartPickup/adChainActions.lua b/FS25_ADSmartPickup/adChainActions.lua
new file mode 100644
index 0000000..03977c7
--- /dev/null
+++ b/FS25_ADSmartPickup/adChainActions.lua
@@ -0,0 +1,65 @@
+-- AD Chain Actions (ren logik): mellemled der skal tændes/slukkes, flows der mangler eller skal stoppes.
+
+ADChainActions = {}
+
+function ADChainActions.productions(recipes, needRecipes, anyPaused)
+ local result = {enable = {}, disable = {}}
+ for _, recipe in ipairs(recipes or {}) do
+ local needed = ((needRecipes or {})[recipe.key] or 0) > 0
+ if needed and not recipe.enabled then table.insert(result.enable, recipe.key) end
+ if not needed and recipe.enabled and recipe.byChain and not anyPaused then table.insert(result.disable, recipe.key) end
+ end
+ table.sort(result.enable); table.sort(result.disable)
+ return result
+end
+
+local SUPPLIED = {production = true, husbandry = true, field = true}
+
+function ADChainActions.edges(chain)
+ local list, seen = {}, {}
+ local function walk(nodes, rootKey)
+ for _, node in ipairs(nodes or {}) do
+ local id = tostring(node.consumer) .. "|" .. tostring(node.fillType)
+ if SUPPLIED[node.source] and node.consumer ~= nil and not seen[id] then
+ seen[id] = true
+ table.insert(list, {fillType = node.fillType, consumer = node.consumer, consumerName = node.consumerName, rootKey = rootKey})
+ end
+ walk(node.children, rootKey)
+ end
+ end
+ for _, root in ipairs((chain or {}).roots or {}) do walk(root.children, root.key) end
+ return list
+end
+
+local function contains(list, value)
+ for _, item in ipairs(list or {}) do if item == value then return true end end
+ return false
+end
+
+function ADChainActions.isCovered(edge, runs)
+ for _, run in ipairs(runs or {}) do
+ if run.kind == "supply" and contains(run.targetBuildingIds, edge.consumer)
+ and (run.ingredientMode ~= "manual" or contains(run.ingredients, edge.fillType)) then
+ return true
+ end
+ end
+ return false
+end
+
+function ADChainActions.missingFlows(edges, runs)
+ local list = {}
+ for _, edge in ipairs(edges or {}) do
+ if not ADChainActions.isCovered(edge, runs) then table.insert(list, edge) end
+ end
+ return list
+end
+
+function ADChainActions.flowsToStop(runs, activeRootKeys)
+ local list = {}
+ for _, run in ipairs(runs or {}) do
+ if run.chain ~= nil and run.chain ~= "" and not run.stopped and not (activeRootKeys or {})[run.chain] then
+ table.insert(list, run)
+ end
+ end
+ return list
+end
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index 664eb7a..e6f60ba 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -57,6 +57,7 @@
+