diff --git a/devTools/types/FC/data.d.ts b/devTools/types/FC/data.d.ts
index c6efdfe4f8217a064dbdd580d08b11e422b5de85..48e6a8e94a673b25ced0e5a153d3eb5d24006772 100644
--- a/devTools/types/FC/data.d.ts
+++ b/devTools/types/FC/data.d.ts
@@ -62,15 +62,6 @@ declare namespace FC {
 			manager: ManagerJobDesc | null;
 		}
 
-		interface ProstheticDefinition {
-			name: string;
-			adjust: number;
-			craft: number;
-			research: number;
-			level: number;
-			costs: number;
-		}
-
 		namespace SlaveSummary {
 			interface SmartPiercing {
 				setting: {
diff --git a/devTools/types/SugarCubeExtensions.d.ts b/devTools/types/SugarCubeExtensions.d.ts
index 571c21cc390dc5a4c454f388b7114b043b622cbb..c26ed89e0664e6f849bec9fe226bc18c5ad614d9 100644
--- a/devTools/types/SugarCubeExtensions.d.ts
+++ b/devTools/types/SugarCubeExtensions.d.ts
@@ -43,7 +43,6 @@ declare module "twine-sugarcube" {
 
 		baseNationalities: string[];
 		paraphiliaList: string[]; // actually FC.SexualFlaw[]
-		prosthetics: Record<string, FC.Data.ProstheticDefinition>;
 	}
 
 	// These are SugarCube private APIs used in the project
diff --git a/js/003-data/miscData.js b/js/003-data/miscData.js
index 115fde838b2bcd7d4644101628380081f33d1681..e47f9f1b2aa6c509f7d77e359f31fbfa5e6f6189 100644
--- a/js/003-data/miscData.js
+++ b/js/003-data/miscData.js
@@ -1,133 +1,4 @@
 App.Data.misc = {
-	/* prosthetic stuff */
-	/* do not change order, order = display order */
-	prostheticIDs: ["interfaceP1", "interfaceP2", "basicL", "sexL", "beautyL", "combatL", "cyberneticL", "ocular", "cochlear", "electrolarynx", "interfaceTail", "modT", "sexT", "combatT", "erectile"],
-
-	/* .name is expected to be singular and uncapitalized; 10 = 1 week without upgrades */
-	prosthetics: {
-		interfaceP1: {
-			name: "basic prosthetic interface",
-			adjust: 40,
-			craft: 50,
-			research: 100,
-			level: 1,
-			costs: 5000
-		},
-		interfaceP2: {
-			name: "advanced prosthetic interface",
-			adjust: 80,
-			craft: 80,
-			research: 160,
-			level: 2,
-			costs: 10000
-		},
-		basicL: {
-			name: "set of basic prosthetic limbs",
-			adjust: 40,
-			craft: 40,
-			research: 80,
-			level: 1,
-			costs: 7000
-		},
-		sexL: {
-			name: "set of advanced sex limbs",
-			adjust: 60,
-			craft: 70,
-			research: 140,
-			level: 2,
-			costs: 15000
-		},
-		beautyL: {
-			name: "set of advanced beauty limbs",
-			adjust: 60,
-			craft: 70,
-			research: 140,
-			level: 2,
-			costs: 15000
-		},
-		combatL: {
-			name: "set of advanced combat limbs",
-			adjust: 60,
-			craft: 70,
-			research: 140,
-			level: 2,
-			costs: 15000
-		},
-		cyberneticL: {
-			name: "set of cybernetic limbs",
-			adjust: 80,
-			craft: 150,
-			research: 250,
-			level: 3,
-			costs: 25000
-		},
-		ocular: {
-			name: "ocular implant",
-			adjust: 60,
-			craft: 80,
-			research: 150,
-			level: 2,
-			costs: 20000
-		},
-		cochlear: {
-			name: "cochlear implant",
-			adjust: 40,
-			craft: 40,
-			research: 80,
-			level: 1,
-			costs: 5000
-		},
-		electrolarynx: {
-			name: "electrolarynx",
-			adjust: 40,
-			craft: 40,
-			research: 40,
-			level: 1,
-			costs: 5000
-		},
-		interfaceTail: {
-			name: "prosthetic tail interface",
-			adjust: 50,
-			craft: 60,
-			research: 120,
-			level: 1,
-			costs: 5000
-		},
-		modT: {
-			name: "modular tail",
-			adjust: 40,
-			craft: 40,
-			research: 80,
-			level: 1,
-			costs: 5000
-		},
-		combatT: {
-			name: "combat tail",
-			adjust: 70,
-			craft: 70,
-			research: 140,
-			level: 2,
-			costs: 15000
-		},
-		sexT: {
-			name: "pleasure tail",
-			adjust: 60,
-			craft: 60,
-			research: 120,
-			level: 2,
-			costs: 10000
-		},
-		erectile: {
-			name: "erectile implant",
-			adjust: 40,
-			craft: 50,
-			research: 100,
-			level: 1,
-			costs: 7000
-		}
-	},
-	/* prosthetic stuff end */
-
 	/** * pregmod exclusive start ***/
 
 	/* Double 20 week point for human data — not a bug. Do not change! (It's transfer point in data source, from data without CTR to with CTR) */
diff --git a/js/003-data/slaveProstheticsData.js b/js/003-data/slaveProstheticsData.js
new file mode 100644
index 0000000000000000000000000000000000000000..a212d2ce48625aba23a24b1882e493fb6867198b
--- /dev/null
+++ b/js/003-data/slaveProstheticsData.js
@@ -0,0 +1,204 @@
+/**
+ * do not change order, order = display order
+ *
+ * @type {string[]}
+ */
+App.Data.prostheticIDs =
+	["interfaceP1", "interfaceP2", "basicL", "sexL", "beautyL", "combatL", "cyberneticL", "ocular", "cochlear",
+		"electrolarynx", "interfaceTail", "modT", "sexT", "combatT", "erectile"];
+
+/**
+ * @typedef {object} prosthetics
+ * @property {string} name expected to singular and lowercase
+ * @property {number} adjust time required to adjust an existing prosthetic to a slave
+ * @property {number} craft time required to create a new, not to  a specific slave fitted prosthetic
+ * @property {number} research time required to research the prosthetic
+ * @property {number} level minimum level the prosthetics lab needs to research/craft the prosthetic
+ * @property {number} costs cash required to buy the prosthetic
+ *
+ * For all time values: 10 = 1 week without upgrades
+ */
+
+/**
+ * @type {Object<string, prosthetics>}
+ */
+App.Data.prosthetics = {
+	interfaceP1: {
+		name: "basic prosthetic interface",
+		adjust: 40,
+		craft: 50,
+		research: 100,
+		level: 1,
+		costs: 5000
+	},
+	interfaceP2: {
+		name: "advanced prosthetic interface",
+		adjust: 80,
+		craft: 80,
+		research: 160,
+		level: 2,
+		costs: 10000
+	},
+	basicL: {
+		name: "set of basic prosthetic limbs",
+		adjust: 40,
+		craft: 40,
+		research: 80,
+		level: 1,
+		costs: 7000
+	},
+	sexL: {
+		name: "set of advanced sex limbs",
+		adjust: 60,
+		craft: 70,
+		research: 140,
+		level: 2,
+		costs: 15000
+	},
+	beautyL: {
+		name: "set of advanced beauty limbs",
+		adjust: 60,
+		craft: 70,
+		research: 140,
+		level: 2,
+		costs: 15000
+	},
+	combatL: {
+		name: "set of advanced combat limbs",
+		adjust: 60,
+		craft: 70,
+		research: 140,
+		level: 2,
+		costs: 15000
+	},
+	cyberneticL: {
+		name: "set of cybernetic limbs",
+		adjust: 80,
+		craft: 150,
+		research: 250,
+		level: 3,
+		costs: 25000
+	},
+	ocular: {
+		name: "ocular implant",
+		adjust: 60,
+		craft: 80,
+		research: 150,
+		level: 2,
+		costs: 20000
+	},
+	cochlear: {
+		name: "cochlear implant",
+		adjust: 40,
+		craft: 40,
+		research: 80,
+		level: 1,
+		costs: 5000
+	},
+	electrolarynx: {
+		name: "electrolarynx",
+		adjust: 40,
+		craft: 40,
+		research: 40,
+		level: 1,
+		costs: 5000
+	},
+	interfaceTail: {
+		name: "prosthetic tail interface",
+		adjust: 50,
+		craft: 60,
+		research: 120,
+		level: 1,
+		costs: 5000
+	},
+	modT: {
+		name: "modular tail",
+		adjust: 40,
+		craft: 40,
+		research: 80,
+		level: 1,
+		costs: 5000
+	},
+	combatT: {
+		name: "combat tail",
+		adjust: 70,
+		craft: 70,
+		research: 140,
+		level: 2,
+		costs: 15000
+	},
+	sexT: {
+		name: "pleasure tail",
+		adjust: 60,
+		craft: 60,
+		research: 120,
+		level: 2,
+		costs: 10000
+	},
+	erectile: {
+		name: "erectile implant",
+		adjust: 40,
+		craft: 50,
+		research: 100,
+		level: 1,
+		costs: 7000
+	}
+};
+
+/**
+ * @type {Map<FC.TailShape, {animal: string, desc: string}>}
+ */
+App.Data.modTails = new Map([
+	["neko", {animal: "Cat", desc: "a long, slender cat tail"}],
+	["inu", {animal: "Dog", desc: "a bushy dog tail"}],
+	["kit", {animal: "Fox", desc: "a soft, fluffy fox tail"}],
+	["kitsune", {animal: "Kitsune", desc: "three incredibly soft, fluffy fox tails"}],
+	["tanuki", {animal: "Tanuki", desc: "a long, fluffy tanuki tail"}],
+	["ushi", {animal: "Cow", desc: "a long cow tail"}],
+	["usagi", {animal: "Rabbit", desc: "a short rabbit tail"}],
+	["risu", {animal: "Squirrel", desc: "a large squirrel tail"}],
+	["uma", {animal: "Horse", desc: "a long horse tail"}]
+]);
+
+/**
+ * TODO expand with beauty stats and similar
+ *
+ * @type {Map<number, {minimumInterface: number, prostheticKey: string, short: string}>}
+ */
+App.Data.limbs = new Map([
+	/* TODO think about how to include these
+	[0, {
+		short: "None",
+		isProsthetic: false,
+	}],
+	[0, {
+		short: "Human",
+		isProsthetic: false,
+	}],
+	*/
+	[2, {
+		short: "basic prosthetic",
+		prostheticKey: "basicL",
+		minimumInterface: 1,
+	}],
+	[3, {
+		short: "advanced sex",
+		prostheticKey: "sexL",
+		minimumInterface: 1,
+	}],
+	[4, {
+		short: "advanced beauty",
+		prostheticKey: "beautyL",
+		minimumInterface: 1,
+	}],
+	[5, {
+		short: "advanced combat",
+		prostheticKey: "combatL",
+		minimumInterface: 1,
+	}],
+	[6, {
+		short: "cybernetic",
+		prostheticKey: "cyberneticL",
+		minimumInterface: 2,
+	}],
+]);
diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index 21d6128f3dd3ecb7055481f5b84101be9cafa1d8..dd73ecd4768ebb690fe8f9d16ec4a6c9b2a7e18c 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -1777,7 +1777,7 @@ App.Update.oldVersions = function(node) {
 		if (jQuery.isEmptyObject(V.prosthetics)) {
 			if (jQuery.isEmptyObject(V.stockpile)) {
 				V.prosthetics = {};
-				setup.prostheticIDs.forEach(function(id) {
+				App.Data.prostheticIDs.forEach(function(id) {
 					V.prosthetics[id] = {amount: 0, research: 0};
 				});
 			} else {
diff --git a/src/endWeek/labReport.js b/src/endWeek/labReport.js
index f32bf642cc28a4131be1ab6f82623e80de1a9084..dfbeed085d39e2a8504febc7d51d1c47e3271dee 100644
--- a/src/endWeek/labReport.js
+++ b/src/endWeek/labReport.js
@@ -18,24 +18,24 @@ App.EndWeek.labReport = function() {
 				} else if (work < task.workLeft) {
 					task.workLeft -= work;
 					work = 0;
-					r.push(`The lab continues ${task.type === "research" ? "research" : "work"} on <span class="yellow">${addA(setup.prosthetics[task.id].name)}.</span> It will take approximately ${numberWithPluralNonZero(Math.floor(task.workLeft / V.researchLab.speed) + 1, "week")} to complete.`);
+					r.push(`The lab continues ${task.type === "research" ? "research" : "work"} on <span class="yellow">${addA(App.Data.prosthetics[task.id].name)}.</span> It will take approximately ${numberWithPluralNonZero(Math.floor(task.workLeft / V.researchLab.speed) + 1, "week")} to complete.`);
 				} else {
 					work -= task.workLeft;
 					r.push(`Your lab staff have <span class="green">completed</span> their`);
 					switch (task.type) {
 						case "research":
 							V.prosthetics[task.id].research = 1;
-							r.push(`${setup.prosthetics[task.id].name} research project.`);
+							r.push(`${App.Data.prosthetics[task.id].name} research project.`);
 							break;
 						case "craft":
 							V.prosthetics[task.id].amount += 1;
-							r.push(`${setup.prosthetics[task.id].name} construction project.`);
+							r.push(`${App.Data.prosthetics[task.id].name} construction project.`);
 							break;
 						case "craftFit":
 							task.workLeft = 0;
 							V.adjustProsthetics.push(task);
 							V.adjustProstheticsCompleted++;
-							r.push(`project to construct ${addA(setup.prosthetics[task.id].name)} for ${SlaveFullName(getSlave(task.slaveID))}.`);
+							r.push(`project to construct ${addA(App.Data.prosthetics[task.id].name)} for ${SlaveFullName(getSlave(task.slaveID))}.`);
 							break;
 					}
 					V.researchLab.tasks.shift();
diff --git a/src/facilities/penthouse/penthousePassage.js b/src/facilities/penthouse/penthousePassage.js
index 7095e9bf390d60c9843df14077abf5bfa58f929e..93661633bf7c7c6fb584a0619224e5bb52640d0d 100644
--- a/src/facilities/penthouse/penthousePassage.js
+++ b/src/facilities/penthouse/penthousePassage.js
@@ -307,7 +307,7 @@ App.UI.managePenthouse = function() {
 			if (V.researchLab.level > 0) {
 				r.push(`Your penthouse is equipped with an advanced prosthetic lab.`);
 			} else {
-				r.push(makeLink("Clear out one of the floors and install equipment to construct prosthetics yourself|Manage Penthouse", () => {
+				r.push(makeLink("Clear out one of the floors and install equipment to construct prosthetics yourself", () => {
 					V.researchLab.level = 1;
 					V.researchLab.maxSpace = 5;
 				}, 150000));
diff --git a/src/facilities/surgery/remoteSurgery.tw b/src/facilities/surgery/remoteSurgery.tw
index f4627e8e7a2c70b0f11367e2faafb6c8b6367dc3..10eb2b11eaa4ec8482d3d216ff8c068a8b8d039c 100644
--- a/src/facilities/surgery/remoteSurgery.tw
+++ b/src/facilities/surgery/remoteSurgery.tw
@@ -897,7 +897,7 @@
 				<<link "Amputate limb(s)">>
 					<<set _atleastOne = 0>>
 					/* temporary story variable */
-					<<set $oldLimbs = App.Desc.limbChange().currentLimbs(getSlave($AS))>>
+					<<set $oldLimbs = App.Medicine.Prosthetics.currentLimbs(getSlave($AS))>>
 					<<if _LA === 1>>
 						<<run removeLimbs(getSlave($AS), "left arm")>>
 						<<set _atleastOne++>>
@@ -931,13 +931,13 @@
 		<div>
 			<<if !hasAllNaturalLimbs(getSlave($AS)) && getSlave($AS).PLimb == 0>>
 				<<if isProstheticAvailable(getSlave($AS), "interfaceP1")>>
-					[[Install basic prosthetic interface|Surgery Degradation][$oldLimbs = App.Desc.limbChange().currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),20), $surgeryType = "PLimb interface"]]
+					[[Install basic prosthetic interface|Surgery Degradation][$oldLimbs = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),20), $surgeryType = "PLimb interface"]]
 				<</if>>
 				<<if isProstheticAvailable(getSlave($AS), "interfaceP2")>> |
-					[[Install advanced prosthetic interface|Surgery Degradation][$oldLimbs = App.Desc.limbChange().currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),20), $surgeryType = "PLimb interface"]]
+					[[Install advanced prosthetic interface|Surgery Degradation][$oldLimbs = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),20), $surgeryType = "PLimb interface"]]
 				<</if>>
 			<<elseif getSlave($AS).PLimb == 1 && isProstheticAvailable(getSlave($AS), "interfaceP2")>>
-				[[Upgrade to advanced prosthetic interface|Surgery Degradation][$oldLimbs = App.Desc.limbChange().currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),5), $surgeryType = "PLimb interface"]]
+				[[Upgrade to advanced prosthetic interface|Surgery Degradation][$oldLimbs = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS),5), $surgeryType = "PLimb interface"]]
 			<</if>>
 		</div>
 
diff --git a/src/init/storyInit.tw b/src/init/storyInit.tw
index 2aad4145719a1fd06fff7c34499655d220ee14c2..c11306c4ec50e275ba67daff4711b3d1cbd5e0a8 100644
--- a/src/init/storyInit.tw
+++ b/src/init/storyInit.tw
@@ -41,7 +41,7 @@ You should have received a copy of the GNU General Public License along with thi
 
 <<run assistant.object()>>
 <<run repX(1000, "event")>>
-<<run setup.prostheticIDs.forEach(function(id) {
+<<run App.Data.prostheticIDs.forEach(function(id) {
 	$prosthetics[id] = {amount: 0, research: 0};
 })>>
 <<set $JobIDMap = makeJobIdMap()>>
diff --git a/src/interaction/main/mainLinks.js b/src/interaction/main/mainLinks.js
index dfab76fb1dda51647360d6904af8d7f5e6465cea..2c025b2bae3059510aba92ae98b74c37f660bc0b 100644
--- a/src/interaction/main/mainLinks.js
+++ b/src/interaction/main/mainLinks.js
@@ -189,7 +189,7 @@ App.UI.View.mainLinks = function() {
 				if (V.adjustProsthetics[j].workLeft <= 0) {
 					const div = document.createElement("div");
 					div.classList.add("yellow");
-					div.append(`The lab has completed ${addA(setup.prosthetics[V.adjustProsthetics[j].id].name)} for `,
+					div.append(`The lab has completed ${addA(App.Data.prosthetics[V.adjustProsthetics[j].id].name)} for `,
 						App.UI.DOM.makeElement("span", App.UI.DOM.link(SlaveFullName(slave), () => { V.AS = slave.ID; }, [], "Slave Interact"), "clear-formatting"),
 						" which is ready to be attached.");
 					fragment.append(div);
diff --git a/src/interaction/prostheticConfig.js b/src/interaction/prostheticConfig.js
new file mode 100644
index 0000000000000000000000000000000000000000..01c07dceccad1ba509fd0f28e000ddbb18b175f5
--- /dev/null
+++ b/src/interaction/prostheticConfig.js
@@ -0,0 +1,460 @@
+/**
+ * @param {App.Entity.SlaveState} slave
+ */
+App.UI.prostheticsConfig = function(slave) {
+	/* get all prosthetics that are ready for this slave */
+	if (V.adjustProstheticsCompleted > 0) {
+		V.adjustProsthetics = V.adjustProsthetics.filter(function(p) {
+			if (p.workLeft <= 0 && p.slaveID === slave.ID) {
+				addProsthetic(slave, p.id);
+				V.adjustProstheticsCompleted--;
+				return false;
+			}
+			return true;
+		});
+	}
+
+	const {He, his, him} = getPronouns(slave);
+
+	const fragment = document.createDocumentFragment();
+
+	App.UI.DOM.appendNewElement("h1", fragment, "Prosthetic Configuration");
+	const introP = App.UI.DOM.makeElement("p", "This room is lined with shelves and cabinets; it could be easily mistaken for a storage room if it were not for the examination table in its center.", "scene-intro");
+
+	if (hasBothLegs(slave)) {
+		App.UI.DOM.appendNewElement("div", introP, `${slave.slaveName} is obediently waiting for your instructions.`);
+	} else {
+		App.UI.DOM.appendNewElement("div", introP, `${slave.slaveName} is lying on the table, waiting for your instructions.`);
+	}
+
+	fragment.append(introP);
+
+	fragment.append(eyes());
+	fragment.append(ears());
+	fragment.append(voice());
+	fragment.append(limbs());
+	fragment.append(tail());
+
+	fragment.append(buyScreen());
+
+	return fragment;
+
+	function eyes() {
+		const f = document.createDocumentFragment();
+
+		if (hasAnyCyberneticEyes(slave)) {
+			App.UI.DOM.appendNewElement("h2", f, "Eyes");
+			const p = document.createElement("p");
+
+			p.append(`${He} has ${hasBothCyberneticEyes(slave) ? "ocular implants" : "an ocular implant"} installed. You can change ${hasBothCyberneticEyes(slave) ? "their" : "its"} settings:`);
+
+			const eyeContainer = document.createElement("div");
+			eyeContainer.classList.add("eyeContainer", "choices");
+
+			let _on = 0, _blur = 0, _off = 0;
+			if (getLeftEyeType(slave) === 3) {
+				App.UI.DOM.appendNewElement("div", eyeContainer, "Left:");
+				let div = document.createElement("div");
+				if (getLeftEyeVision(slave) !== 2) {
+					_on++;
+					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "fix")));
+				} else {
+					div.append("[ON]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+
+				if (getLeftEyeVision(slave) !== 1) {
+					_blur++;
+					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "blur")));
+				} else {
+					div.append("[BLUR]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+				if (getLeftEyeVision(slave) !== 0) {
+					_off++;
+					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "blind")));
+				} else {
+					div.append("[OFF]");
+				}
+				eyeContainer.append(div);
+			}
+			if (getRightEyeType(slave) === 3) {
+				App.UI.DOM.appendNewElement("div", eyeContainer, "Right:");
+				let div = document.createElement("div");
+				if (getRightEyeVision(slave) !== 2) {
+					_on++;
+					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "fix")));
+				} else {
+					div.append("[ON]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+
+				if (getRightEyeVision(slave) !== 1) {
+					_blur++;
+					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "blur")));
+				} else {
+					div.append("[BLUR]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+				if (getRightEyeVision(slave) !== 0) {
+					_off++;
+					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "blind")));
+				} else {
+					div.append("[OFF]");
+				}
+				eyeContainer.append(div);
+			}
+			if (hasBothCyberneticEyes(slave)) {
+				App.UI.DOM.appendNewElement("div", eyeContainer, "Both:");
+				let div = document.createElement("div");
+				if (_on > 0) {
+					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "fix")));
+				} else {
+					div.append("[ON]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+
+				if (_blur > 0) {
+					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "blur")));
+				} else {
+					div.append("[BLUR]");
+				}
+				eyeContainer.append(div);
+				div = document.createElement("div");
+				if (_off > 0) {
+					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "blind")));
+				} else {
+					div.append("[OFF]");
+				}
+				eyeContainer.append(div);
+			}
+			p.append(eyeContainer);
+
+			const colorDiv = document.createElement("div");
+			colorDiv.append(`${He} has ${App.Desc.eyesColor(slave)}. To change ${his} eye color visit the `, App.UI.DOM.passageLink("auto salon", "Salon"), ".");
+			p.append(colorDiv);
+
+			f.append(p);
+		}
+		return f;
+	}
+
+	function ears() {
+		const f = document.createDocumentFragment();
+		if (slave.earImplant === 1) {
+			App.UI.DOM.appendNewElement("h2", f, "Ears");
+
+			const p = document.createElement("p");
+			p.append(`${He} has cochlear implants installed.`);
+			if (slave.hears === 0) {
+				p.append("They are operating normally.");
+			} else if (slave.hears === -1) {
+				p.append(`They are set to muffle ${his} hearing.`);
+			} else {
+				p.append("They are turned off.");
+			}
+
+			const links = [];
+			if (slave.hears !== 0) {
+				links.push(App.UI.DOM.passageLink("Restore hearing", "Prosthetics Configuration", () => {
+					slave.hears = 0;
+					V.prostheticsConfig = "hearing";
+				}));
+			}
+			if (slave.hears !== -1) {
+				links.push(App.UI.DOM.passageLink("Muffle hearing", "Prosthetics Configuration", () => {
+					slave.hears = -1;
+					V.prostheticsConfig = "hearing";
+				}));
+			}
+			if (slave.hears !== -2) {
+				links.push(App.UI.DOM.passageLink("Disable", "Prosthetics Configuration", () => {
+					slave.hears = -2;
+					V.prostheticsConfig = "hearing";
+				}));
+			}
+			App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
+
+			f.append(p);
+		}
+		return f;
+	}
+
+	function voice() {
+		const f = document.createDocumentFragment();
+
+		if (slave.electrolarynx === 1) {
+			App.UI.DOM.appendNewElement("h2", f, "Voice");
+			const p = document.createElement("p");
+			p.append(`${He} has an electrolarynx installed.`);
+			if (slave.voice === 0) {
+				p.append("It is turned off.");
+			} else if (slave.voice === 1) {
+				p.append(`It is set to its "deep voice" setting.`);
+			} else if (slave.voice === 2) {
+				p.append(`It is set to its "normal voice" setting.`);
+			} else if (slave.voice === 3) {
+				p.append(`It is set to its "high voice" setting.`);
+			}
+
+			const links = [];
+			if (slave.voice !== 0) {
+				links.push(App.UI.DOM.passageLink("Disable", "Prosthetics Configuration", () => {
+					slave.voice = 0;
+					V.prostheticsConfig = "voice";
+				}));
+			}
+			if (slave.voice !== 1) {
+				links.push(App.UI.DOM.passageLink("Deep voice setting", "Prosthetics Configuration", () => {
+					slave.voice = 1;
+					V.prostheticsConfig = "voice";
+				}));
+			}
+			if (slave.voice !== 2) {
+				links.push(App.UI.DOM.passageLink("Standard voice setting", "Prosthetics Configuration", () => {
+					slave.voice = 2;
+					V.prostheticsConfig = "voice";
+				}));
+			}
+			if (slave.voice !== 3) {
+				links.push(App.UI.DOM.passageLink("High voice setting", "Prosthetics Configuration", () => {
+					slave.voice = 3;
+					V.prostheticsConfig = "voice";
+				}));
+			}
+			App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
+
+			f.append(p);
+		}
+		return f;
+	}
+
+	function limbs() {
+		const f = document.createDocumentFragment();
+		App.UI.DOM.appendNewElement("h2", f, "Limbs");
+
+		App.UI.DOM.appendNewElement("p", f, App.Medicine.Prosthetics.selector(slave, App.Medicine.Prosthetics.currentLimbs(slave)));
+
+		return f;
+	}
+
+	function tail() {
+		const f = document.createDocumentFragment();
+		App.UI.DOM.appendNewElement("h2", f, "Tail");
+
+		const p = document.createElement("p");
+
+		if (slave.PTail === 1) {
+			App.UI.DOM.appendNewElement("div", p, `${He} has a neural tail interface installed. You can assign and adjust ${his} tail here.`);
+
+			if (slave.tail !== "none") {
+				App.UI.DOM.appendNewElement("div", p, `${He} currently has a tail attached, if you wish to change it you first need to detach it.`);
+				App.UI.DOM.appendNewElement("div", p,
+					App.UI.DOM.passageLink("Detach", "Prosthetics Configuration", () => {
+						V.prostheticsConfig = "detachTail";
+						V.nextButton = "Continue";
+						V.nextLink = "Prosthetics Configuration";
+					}), "choices");
+			} else {
+				if (isProstheticAvailable(slave, "modT")) {
+					App.UI.DOM.appendNewElement("div", p, "Attach a modular tail designed to look like a:");
+
+					const links = [];
+					App.Data.modTails.forEach((value, key) => {
+						links.push(App.UI.DOM.passageLink(`${value.animal}'s Tail`, "Prosthetics Configuration",
+							() => {
+								V.prostheticsConfig = "attachTail";
+								slave.tail = "mod";
+								slave.tailShape = key;
+								slave.tailColor = slave.hColor;
+							}
+						));
+					});
+					App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
+				}
+				const links = [];
+				if (isProstheticAvailable(slave, "combatT")) {
+					links.push(App.UI.DOM.passageLink("Attach Combat Tail", "Prosthetics Configuration", () => {
+						V.prostheticsConfig = "attachTail";
+						slave.tail = "combat";
+						slave.tailColor = "jet black";
+					}));
+				}
+				if (isProstheticAvailable(slave, "sexT")) {
+					links.push(App.UI.DOM.passageLink("Attach Pleasure Tail", "Prosthetics Configuration", () => {
+						V.prostheticsConfig = "attachTail";
+						slave.tail = "sex";
+						slave.tailColor = "pink";
+					}));
+				}
+				App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
+			}
+
+			if (slave.tail === "mod") {
+				App.UI.DOM.appendNewElement("div", p,
+					`${He} currently has a modular tail, styled to look like ${App.Data.modTails.get(slave.tailShape).desc}. Modify ${his} tail's appearance:`);
+
+				const links = [];
+				App.Data.modTails.forEach((value, key) => {
+					links.push(App.UI.DOM.passageLink(value.animal, "Prosthetics Configuration", () => {
+						slave.tailShape = key;
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+					}));
+				});
+				App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
+			}
+		} else {
+			App.UI.DOM.appendNewElement("span", p, `${He} does not have a neural tail interface installed so you cannot attach a tail.`, "note");
+		}
+		f.append(p);
+		return f;
+	}
+
+	function buyScreen() {
+		const f = document.createDocumentFragment();
+		App.UI.DOM.appendNewElement("h2", f, "Prosthetics");
+
+		const p = document.createElement("p");
+		App.UI.DOM.appendNewElement("span", p, `Fit prosthetics to ${him}:`, "note");
+
+		const gridDiv = document.createElement("div");
+		gridDiv.classList.add("buy-prosthetics");
+
+		gridDiv.append(document.createElement("div"));
+
+		App.UI.DOM.appendNewElement("div", gridDiv, "Buy and fit");
+
+		let tooltip;
+		if (V.researchLab.level > 0) {
+			tooltip = [`Depending on lab speed, it might be faster than fitting an existing prosthetic but should almost always be faster than first building and then fitting it to ${him}.`];
+		} else {
+			tooltip = ["With a lab you could both increase speed and decrease cost."];
+		}
+		App.UI.DOM.appendNewElement("div", gridDiv, App.UI.DOM.disabledLink("Construct in lab", tooltip));
+
+		tooltip = [];
+		if (V.researchLab.speed >= 300) { /* max speed */
+			tooltip = ["Your lab is so fast that fitting prosthetics to your slave can done instantly though you will sacrifice some efficiency."];
+		} else if (V.researchLab.level > 0) {
+			tooltip = ["Your lab is not fast enough to fit prosthetics instantly."];
+		}
+
+		const fastDiv = document.createElement("div");
+		if (tooltip.length > 0) {
+			fastDiv.append(App.UI.DOM.disabledLink("Fast assembly", tooltip));
+			fastDiv.style.textAlign = "right";
+		}
+		gridDiv.append(fastDiv);
+
+		for (const prostheticID of App.Data.prostheticIDs) {
+			// TODO comment erectile out in data directly
+			if (prostheticID !== "erectile") { /* exclude erectile implant */
+				addBuyRow(prostheticID, gridDiv);
+			}
+		}
+		p.append(gridDiv);
+		f.append(p);
+
+		return f;
+	}
+
+	/**
+	 * @param {string} prosthetic
+	 * @param {HTMLDivElement} container
+	 */
+	function addBuyRow(prosthetic, container) {
+		App.UI.DOM.appendNewElement("div", container, capFirstChar(App.Data.prosthetics[prosthetic].name));
+
+		if (V.adjustProsthetics.findIndex(function(p) { return p.id === prosthetic && p.slaveID === slave.ID; }) !== -1
+			|| V.researchLab.tasks.findIndex(function(p) { return p.type === "craftFit" && p.id === prosthetic && p.slaveID === slave.ID; }) !== -1) {
+			App.UI.DOM.appendNewElement("div", container, `Currently being fitted to ${him}.`, ["full", "note"]);
+		} else if (App.Data.prosthetics[prosthetic].level > V.prostheticsUpgrade) {
+			App.UI.DOM.appendNewElement("div", container, `Better contracts are needed to buy these.`, ["full", "note"]);
+		} else if (isProstheticAvailable(slave, prosthetic)) {
+			App.UI.DOM.appendNewElement("div", container, `Completed.`, ["full", "note"]);
+		} else {
+			if (V.prosthetics[prosthetic].amount > 0) {
+				App.UI.DOM.appendNewElement("div", container,
+					App.UI.DOM.passageLink("From storage", "Prosthetics Configuration", () => {
+						V.adjustProsthetics.push({
+							id: prosthetic,
+							workLeft: App.Data.prosthetics[prosthetic].adjust,
+							slaveID: slave.ID
+						});
+						V.prosthetics[prosthetic].amount -= 1;
+					}));
+			} else {
+				App.UI.DOM.appendNewElement("div", container,
+					App.UI.DOM.passageLink(cashFormat(App.Data.prosthetics[prosthetic].costs), "Prosthetics Configuration", () => {
+						V.adjustProsthetics.push({
+							id: prosthetic,
+							workLeft: App.Data.prosthetics[prosthetic].adjust,
+							slaveID: slave.ID
+						});
+						cashX(forceNeg(App.Data.prosthetics[prosthetic].costs), "slaveMod", slave);
+					}));
+			}
+
+			if (V.prosthetics[prosthetic].research > 0) {
+				const craftDiv = document.createElement("div");
+				craftDiv.style.textAlign = "center";
+				if (V.researchLab.level > 0 && V.prosthetics[prosthetic].research > 0) {
+					craftDiv.append(App.UI.DOM.passageLink("Construct", "Prosthetics Configuration", () => {
+						V.researchLab.tasks.push({
+							type: "craftFit",
+							id: prosthetic,
+							/* 1.5: longer than adjust, but faster than adjust+craft. */
+							workLeft: (App.Data.prosthetics[prosthetic].adjust + App.Data.prosthetics[prosthetic].craft) / 1.5,
+							slaveID: slave.ID
+						});
+					}));
+				}
+				container.append(craftDiv);
+
+				const instantDiv = document.createElement("div");
+				instantDiv.style.textAlign = "right";
+				if (V.researchLab.speed >= 300 && V.prosthetics[prosthetic].research > 0) { /* max speed */
+					if (V.prosthetics[prosthetic].amount > 0) {
+						const cost = App.Data.prosthetics[prosthetic].adjust * 50;
+						instantDiv.append(App.UI.DOM.passageLink(`From storage: ${cashFormat(cost)}`,
+							"Prosthetics Configuration", () => {
+								cashX(-cost, "slaveMod", slave);
+								addProsthetic(slave, prosthetic);
+							}));
+					} else {
+						const cost = App.Data.prosthetics[prosthetic].costs * 2 + App.Data.prosthetics[prosthetic].adjust * 50;
+						instantDiv.append(App.UI.DOM.passageLink(cashFormat(cost), "Prosthetics Configuration", () => {
+							cashX(-cost, "slaveMod", slave);
+							addProsthetic(slave, prosthetic);
+						}));
+					}
+				}
+				container.append(instantDiv);
+			} else if (V.researchLab.level > 0) {
+				App.UI.DOM.appendNewElement("div", container, "Not researched.", ["research", "note"]);
+			} else {
+				App.UI.DOM.appendNewElement("div", container, "You need to construct a lab first.", ["research", "note"]);
+			}
+		}
+	}
+
+	/**
+	 * @param {Array<Element>} links
+	 * @returns {DocumentFragment}
+	 */
+	function linkStrip(links) {
+		const f = document.createDocumentFragment();
+		if (links.length > 0) {
+			f.append(links[0]);
+			for (let i = 1; i < links.length; i++) {
+				f.append(" | ", links[i]);
+			}
+		}
+		return f;
+	}
+};
diff --git a/src/interaction/prostheticConfig.tw b/src/interaction/prostheticConfig.tw
index 12d06a338c6179b92aff4d51c59c66a7d9000f0f..c26fc69341fc707febae20b65be828080cd36ecf 100644
--- a/src/interaction/prostheticConfig.tw
+++ b/src/interaction/prostheticConfig.tw
@@ -5,360 +5,9 @@
 <<switch $prostheticsConfig>>
 
 <<case "main">>
-	<<set $nextButton = "Confirm changes", $nextLink = "Slave Interact">>
-	/* get all prosthetics that are ready for this slave */
-	<<if $adjustProstheticsCompleted > 0>>
-		<<set $adjustProsthetics = $adjustProsthetics.filter(function(p) {
-			if (p.workLeft <= 0 && p.slaveID == getSlave($AS).ID) {
-				addProsthetic(getSlave($AS), p.id);
-				$adjustProstheticsCompleted--;
-				return false;
-			}
-			return true;
-		})>>
-	<</if>>
-
-/* base screen START */
-<h1>Prosthetic Configuration</h1>
-
-<p class="scene-intro">
-This room is lined with shelves and cabinets; it could be easily mistaken for a storage room if it were not for the examination table in its center.
-<div>
-<<if hasBothLegs(getSlave($AS))>>
-	<<= getSlave($AS).slaveName>> is obediently waiting for your instructions.
-<<else>>
-	<<= getSlave($AS).slaveName>> is lying on the table, waiting for your instructions.
-<</if>>
-</div>
-</p>
-
-<<if hasAnyCyberneticEyes(getSlave($AS))>>
-	<h2>Eyes</h2>
-	<p class="choices"> /* tab works better with links */
-	$He has <<if hasBothCyberneticEyes(getSlave($AS))>> ocular implants <<else>> an ocular implant <</if>> installed. You can change <<if hasBothCyberneticEyes(getSlave($AS))>>their<<else>>its<</if>> settings:
-
-	<div class="eyeContainer">
-	<<set _on = 0, _blur = 0, _off = 0>>
-	<<if getLeftEyeType(getSlave($AS)) === 3>>
-		<div>Left:</div>
-		<div>
-		<<if getLeftEyeVision(getSlave($AS)) !== 2>>
-			<<set _on++>>
-			[["[ON]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "left", "fix")]]
-		<<else>>
-			<<print "[ON]">>
-		<</if>>
-		</div><div>
-		<<if getLeftEyeVision(getSlave($AS)) !== 1>>
-			<<set _blur++>>
-			[["[BLUR]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "left", "blur")]]
-		<<else>>
-			<<print "[BLUR]">>
-		<</if>>
-		</div><div>
-		<<if getLeftEyeVision(getSlave($AS)) !== 0>>
-			<<set _off++>>
-			[["[OFF]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "left", "blind")]]
-		<<else>>
-			<<print "[OFF]">>
-		<</if>>
-		</div>
-	<</if>>
-	<<if getRightEyeType(getSlave($AS)) === 3>>
-		<div>Right:</div>
-		<div>
-		<<if getRightEyeVision(getSlave($AS)) !== 2>>
-			<<set _on++>>
-			[["[ON]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "right", "fix")]]
-		<<else>>
-			<<print "[ON]">>
-		<</if>>
-		</div><div>
-		<<if getRightEyeVision(getSlave($AS)) !== 1>>
-			<<set _blur++>>
-			[["[BLUR]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "right", "blur")]]
-		<<else>>
-			<<print "[BLUR]">>
-		<</if>>
-		</div><div>
-		<<if getRightEyeVision(getSlave($AS)) !== 0>>
-			<<set _off++>>
-			[["[OFF]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "right", "blind")]]
-		<<else>>
-			<<print "[OFF]">>
-		<</if>>
-		</div>
-	<</if>>
-	<<if hasBothCyberneticEyes(getSlave($AS))>>
-		<div>Both:</div>
-		<div>
-		<<if _on > 0>>
-			[["[ON]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "both", "fix")]]
-		<<else>>
-			<<print "[ON]">>
-		<</if>>
-		</div><div>
-		<<if _blur > 0>>
-			[["[BLUR]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "both", "blur")]]
-		<<else>>
-			<<print "[BLUR]">>
-		<</if>>
-		</div><div>
-		<<if _off > 0>>
-			[["[OFF]"|Prosthetics Configuration][eyeSurgery(getSlave($AS), "both", "blind")]]
-		<<else>>
-			<<print "[OFF]">>
-		<</if>>
-		</div>
-	<</if>>
-	</div>
-	</p>
-
-	<p class="indent">
-	$He has <<print App.Desc.eyesColor(getSlave($AS))>>. To change $his eye color visit the [[auto salon|Salon]].
-	</p>
-<</if>>
-
-<<if getSlave($AS).earImplant == 1>>
-	<h2>Ears</h2>
-	<p class="indent">
-	$He has cochlear implants installed.
-	<<if getSlave($AS).hears == 0>>
-		They are operating normally.
-	<<elseif getSlave($AS).hears == -1>>
-		They are set to muffle $his hearing.
-	<<else>>
-		They are turned off.
-	<</if>>
-	</p>
-	<p class="indent">
-	<<if getSlave($AS).hears != 0>>
-		[[Restore hearing|Prosthetics Configuration][getSlave($AS).hears = 0, $prostheticsConfig = "hearing"]] |
-	<</if>>
-	<<if getSlave($AS).hears != -1>>
-		[[Muffle|Prosthetics Configuration][getSlave($AS).hears = -1, $prostheticsConfig = "hearing"]]
-		<<if getSlave($AS).hears != -2>>
-			|
-		<</if>>
-	<</if>>
-	<<if getSlave($AS).hears != -2>>
-		[[Disable|Prosthetics Configuration][getSlave($AS).hears = -2, $prostheticsConfig = "hearing"]]
-	<</if>>
-	</p>
-<</if>>
-
-<<if getSlave($AS).electrolarynx == 1>>
-	<h2>Voice</h2>
-	<p class="indent">
-	$He has an electrolarynx installed.
-	<<if getSlave($AS).voice == 0>>
-		It is turned off.
-	<<elseif getSlave($AS).voice == 1>>
-		It is set to its "deep voice" setting.
-	<<elseif getSlave($AS).voice == 2>>
-		It is set to its "normal voice" setting.
-	<<elseif getSlave($AS).voice == 3>>
-		It is set to its "high voice" setting.
-	<</if>>
-	</p>
-	<p class="indent">
-	<<if getSlave($AS).voice != 0>>
-		[[Disable|Prosthetics Configuration][getSlave($AS).voice = 0, $prostheticsConfig = "voice"]] |
-	<</if>>
-	<<if getSlave($AS).voice != 1>>
-		[[Deep voice setting|Prosthetics Configuration][getSlave($AS).voice = 1, $prostheticsConfig = "voice"]] |
-	<</if>>
-	<<if getSlave($AS).voice != 2>>
-		[[Standard voice setting|Prosthetics Configuration][getSlave($AS).voice = 2, $prostheticsConfig = "voice"]]
-		<<if getSlave($AS).voice != 3>>
-			|
-		<</if>>
-	<</if>>
-	<<if getSlave($AS).voice != 3>>
-		[[High voice setting|Prosthetics Configuration][getSlave($AS).voice = 3, $prostheticsConfig = "voice"]]
-	<</if>>
-	</p>
-<</if>>
-
-<h2>Limbs</h2>
-<p class="choices">
-	<<= App.Desc.limbChange().selector(getSlave($AS), App.Desc.limbChange().currentLimbs(getSlave($AS)))>>
-</p>
-
-<h2>Tail</h2>
-<p class="indent">
-<<if getSlave($AS).PTail == 1>>
-	<div>
-	$He has a neural tail interface installed. You can assign and adjust $his tail here.
-	</div>
-	<div class="double-indent">
-	<<if getSlave($AS).tail != "none">>
-		$He currently has a tail attached, if you wish to change it you will first need to detach it.
-		<div>
-		[[Detach|Prosthetics Configuration][$prostheticsConfig = "detachTail",$nextButton = "Continue", $nextLink = "Prosthetics Configuration"]]
-		</div>
-	<<else>>
-		<<if isProstheticAvailable(getSlave($AS), "modT")>>
-			<div>
-			Attach a modular tail designed to look like a:
-			</div>
-			<div>
-			[[Cat's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "neko", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Dog's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "inu", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Fox's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "kit", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Kitsune's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "kitsune", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Tanuki's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "tanuki", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Cow's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "ushi", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Rabbit's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "usagi", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Squirrel's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "risu", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			| [[Horse's Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "mod", getSlave($AS).tailShape = "uma", getSlave($AS).tailColor = getSlave($AS).hColor]]
-			</div>
-		<</if>>
-		<div>
-		<<if isProstheticAvailable(getSlave($AS), "combatT")>>
-			[[Attach Combat Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "combat", getSlave($AS).tailColor = "jet black"]]
-			<<if isProstheticAvailable(getSlave($AS), "sexT")>>
-				|
-			<</if>>
-		<</if>>
-		<<if isProstheticAvailable(getSlave($AS), "sexT")>>
-			[[Attach Pleasure Tail|Prosthetics Configuration][$prostheticsConfig = "attachTail", getSlave($AS).tail = "sex", getSlave($AS).tailColor = "pink"]]
-		<</if>>
-		</div>
-	<</if>>
-	</div>
-<<else>>
-	//$He does not have a neural tail interface installed so you cannot attach a tail.//
-<</if>>
-<<if getSlave($AS).tail == "mod">>
-	<div>
-	$He currently has a modular tail, styled to look like
-	<<if getSlave($AS).tailShape == "neko">>
-		a long, slender cat tail.
-	<<elseif getSlave($AS).tailShape == "inu">>
-		a bushy dog tail.
-	<<elseif getSlave($AS).tailShape == "kit">>
-		a soft, fluffy fox tail.
-	<<elseif getSlave($AS).tailShape == "kitsune">>
-		three incredibly soft, fluffy fox tails.
-	<<elseif getSlave($AS).tailShape == "tanuki">>
-		a long, fluffy tanuki tail.
-	<<elseif getSlave($AS).tailShape == "ushi">>
-		a long cow tail.
-	<<elseif getSlave($AS).tailShape == "usagi">>
-		a short rabbit tail.
-	<<elseif getSlave($AS).tailShape == "risu">>
-		a large squirrel tail.
-	<<elseif getSlave($AS).tailShape == "uma">>
-		a long horse tail.
-	<</if>>
-	</div>
-
-	<div class="double-indent">
-	Modify $his tail's appearance:
-	<div>
-	[[Cat|Prosthetics Configuration][getSlave($AS).tailShape = "neko", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Dog|Prosthetics Configuration][getSlave($AS).tailShape = "inu", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Fox|Prosthetics Configuration][getSlave($AS).tailShape = "kit", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Kitsune|Prosthetics Configuration][getSlave($AS).tailShape = "kitsune", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Tanuki|Prosthetics Configuration][getSlave($AS).tailShape = "tanuki", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Bovine|Prosthetics Configuration][getSlave($AS).tailShape = "ushi", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Rabbit|Prosthetics Configuration][getSlave($AS).tailShape = "usagi", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Squirrel|Prosthetics Configuration][getSlave($AS).tailShape = "risu", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	| [[Horse|Prosthetics Configuration][getSlave($AS).tailShape = "uma", cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-	</div>
-	</div>
-<</if>>
-</p>
-
-<h2>Prosthetics</h2>
-<p class="choices">
-//Fit prosthetics to $him://
-
-<div class="buy-prosthetics">
-	<div></div>
-	<div>''Buy and fit''</div>
-	<div>
-		<<if $researchLab.level > 0>>
-			<<= App.UI.disabledLink(`''Construct in lab''`, ["Depending on lab speed, it might be faster than fitting an existing prosthetic but should almost always be faster than first building and then fitting it to $him."])>>
-		<<else>>
-			<<= App.UI.disabledLink(`''Construct in lab''`, ["With a lab you could both increase speed and decrease cost."])>>
-		<</if>>
-	</div>
-	<div style="text-align:right">
-		<<if $researchLab.speed >= 300>> /* max speed */
-			<<= App.UI.disabledLink(`''Fast assembly''`, ["Your lab is so fast that fitting prosthetics to your slave can done instantly though you will sacrifice some efficiency."])>>
-		<<elseif $researchLab.level > 0>>
-			<<= App.UI.disabledLink(`''Fast assembly''`, ["Your lab is not fast enough to fit prosthetics instantly."])>>
-		<</if>>
-	</div>
-
-<<for _p range setup.prostheticIDs>>
-	<<if _p != "erectile">> /* exclude erectile implant */
-		<div><<= capFirstChar(setup.prosthetics[_p].name)>></div>
-		<<if $adjustProsthetics.findIndex(function(p) {return p.id == _p && p.slaveID == getSlave($AS).ID}) != -1 || $researchLab.tasks.findIndex(function(p) {return p.type == "craftFit" && p.id == _p && p.slaveID == getSlave($AS).ID}) != -1>>
-			<div class="full">//Currently being fitted to $him.//</div>
-		<<elseif setup.prosthetics[_p].level > $prostheticsUpgrade>>
-			<div class="full">//Better contracts are needed to buy these.//</div>
-		<<elseif isProstheticAvailable(getSlave($AS), _p)>>
-			<div class="full">//Completed.//</div>
-		<<else>>
-			<<capture _p>>
-			<div>
-				<<if $prosthetics[_p].amount > 0>>
-					<<link "From storage" "Prosthetics Configuration">>
-						<<set $adjustProsthetics.push({id: _p, workLeft: setup.prosthetics[_p].adjust, slaveID: getSlave($AS).ID}), $prosthetics[_p].amount -= 1>>
-					<</link>>
-				<<else>>
-					<<link "<<= cashFormat(setup.prosthetics[_p].costs)>>" "Prosthetics Configuration">>
-						<<set $adjustProsthetics.push({id: _p, workLeft: setup.prosthetics[_p].adjust, slaveID: getSlave($AS).ID}), cashX(forceNeg(setup.prosthetics[_p].costs), "slaveMod", getSlave($AS))>>
-					<</link>>
-				<</if>>
-			</div>
-			<<if $prosthetics[_p].research > 0>>
-				<div style="text-align:center">
-					<<if $researchLab.level > 0 && $prosthetics[_p].research > 0>>
-						<<link "Construct" "Prosthetics Configuration">>
-							<<set $researchLab.tasks.push({
-								type: "craftFit",
-								id: _p,
-								workLeft: (setup.prosthetics[_p].adjust + setup.prosthetics[_p].craft) / 1.5,
-								slaveID: $AS})>>
-							/* 1.5: longer than adjust, but faster than adjust+craft. */
-						<</link>>
-					<</if>>
-				</div>
-				<div style="text-align:right">
-					<<if $researchLab.speed >= 300 && $prosthetics[_p].research > 0>> /* max speed */
-						<<if $prosthetics[_p].amount > 0>>
-							<<link "From storage: <<= cashFormat(setup.prosthetics[_p].adjust * 50)>>" "Prosthetics Configuration">>
-								<<set cashX(forceNeg(setup.prosthetics[_p].costs * 1.5), "slaveMod", getSlave($AS)), addProsthetic(getSlave($AS), _p)>>
-							<</link>>
-						<<else>>
-							<<link "<<= cashFormat(setup.prosthetics[_p].costs + setup.prosthetics[_p].adjust * 100)>>" "Prosthetics Configuration">>
-								<<set cashX(forceNeg(setup.prosthetics[_p].costs + setup.prosthetics[_p].adjust * 100), "slaveMod", getSlave($AS)), addProsthetic(getSlave($AS), _p)>>
-							<</link>>
-						<</if>>
-					<</if>>
-				</div>
-			<<elseif $researchLab.level > 0>>
-				<div class="research">
-					//Not researched.//
-				</div>
-			<<else>>
-				<div class="research">
-					//You need to construct a lab first.//
-				</div>
-			<</if>>
-			<</capture>>
-		<</if>>
-	<</if>>
-<</for>>
-</div>
-
-</p>
-
-/* base screen END */
+	<<set $nextButton = "Confirm changes">>
+	<<set $nextLink = "Slave Interact">>
+	<<includeDOM App.UI.prostheticsConfig(getSlave($AS))>>
 
 /*
 <<case "eyes">>
@@ -374,7 +23,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 
 <<case "limbs">>
 	<<set $prostheticsConfig = "main", $nextButton = "Continue", $nextLink = "Prosthetics Configuration">>
-	<<= App.Desc.limbChange().reaction(getSlave($AS), $oldLimbs)>>
+	<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), $oldLimbs)>>
 	<<unset $oldLimbs>>
 
 /*
@@ -509,7 +158,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 			<br><br>Since you already have prepared limbs for $him you might as well attach them while you are working on $him:<br>
 			<<set _first = 0>>
 		<</if>>
-		<<link "Attach <<= addA(setup.prosthetics.basicL.name)>>">>
+		<<link "Attach <<= addA(App.Data.prosthetics.basicL.name)>>">>
 			<<set removeLimbs(getSlave($AS), "all"), attachLimbs(getSlave($AS), "all", 2), $prostheticsConfig = "basicPLimbs">>
 			<<replace #attach>><br><br><<include "Prosthetics Configuration">><<set $nextLink = "Remote Surgery">><</replace>>
 		<</link>>
@@ -520,7 +169,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 			<br><br>Since you already have prepared limbs for $him you might as well attach them while you are working on $him:<br>
 			<<set _first = 0>>
 		<</if>>
-		<<link "Attach <<= addA(setup.prosthetics.sexL.name)>>">>
+		<<link "Attach <<= addA(App.Data.prosthetics.sexL.name)>>">>
 			<<set removeLimbs(getSlave($AS), "all"), attachLimbs(getSlave($AS), "all", 3), $prostheticsConfig = "sexPLimbs">>
 			<<replace #attach>><br><br><<include "Prosthetics Configuration">><<set $nextLink = "Remote Surgery">><</replace>>
 		<</link>>
@@ -531,7 +180,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 			<br><br>Since you already have prepared limbs for $him you might as well attach them while you are working on $him:<br>
 			<<set _first = 0>>
 		<</if>>
-		<<link "Attach <<= addA(setup.prosthetics.beautyL.name)>>">>
+		<<link "Attach <<= addA(App.Data.prosthetics.beautyL.name)>>">>
 			<<set removeLimbs(getSlave($AS), "all"), attachLimbs(getSlave($AS), "all", 4), $prostheticsConfig = "beautyPLimbs">>
 			<<replace #attach>><br><br><<include "Prosthetics Configuration">><<set $nextLink = "Remote Surgery">><</replace>>
 		<</link>>
@@ -542,7 +191,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 			<br><br>Since you already have prepared limbs for $him you might as well attach them while you are working on $him:<br>
 			<<set _first = 0>>
 		<</if>>
-		<<link "Attach <<= addA(setup.prosthetics.combatL.name)>>">>
+		<<link "Attach <<= addA(App.Data.prosthetics.combatL.name)>>">>
 			<<set removeLimbs(getSlave($AS), "all"), attachLimbs(getSlave($AS), "all", 5), $prostheticsConfig = "combatPLimbs">>
 			<<replace #attach>><br><br><<include "Prosthetics Configuration">><<set $nextLink = "Remote Surgery">><</replace>>
 		<</link>>
@@ -554,7 +203,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 				<br><br>Since you already have prepared limbs for $him you might as well attach them while you are working on $him:<br>
 				<<set _first = 0>>
 			<</if>>
-			<<link "Attach <<= addA(setup.prosthetics.cyberneticL.name)>>" "Prosthetics Configuration">>
+			<<link "Attach <<= addA(App.Data.prosthetics.cyberneticL.name)>>" "Prosthetics Configuration">>
 				<<set removeLimbs(getSlave($AS), "all"), attachLimbs(getSlave($AS), "all", 6), $prostheticsConfig = "cyberPLimbs">>
 				<<replace #attach>><br><br><<include "Prosthetics Configuration">><<set $nextLink = "Remote Surgery">><</replace>>
 			<</link>>
diff --git a/src/interaction/prostheticLab.tw b/src/interaction/prostheticLab.tw
index d79406219171cdbe75c9cb7732cc43e0a8073730..2335fd2f8a1ed2f2937cfde19f31bf17da4c2c6b 100644
--- a/src/interaction/prostheticLab.tw
+++ b/src/interaction/prostheticLab.tw
@@ -13,7 +13,7 @@
 	<<for _p range $adjustProsthetics>>
 		<<if _p.workLeft > 0>>
 			<div class="indent">
-				<<= capFirstChar(setup.prosthetics[_p.id].name)>> for <<= SlaveFullName($slaves[$slaveIndices[_p.slaveID]])>>
+				<<= capFirstChar(App.Data.prosthetics[_p.id].name)>> for <<= SlaveFullName($slaves[$slaveIndices[_p.slaveID]])>>
 			</div>
 		<</if>>
 	<</for>>
@@ -161,7 +161,7 @@
 				@@.error;Error: Unknown $researchLab.tasks[].type: $researchLab.tasks[_i].type@@
 			<</switch>>
 			<<set _j += $researchLab.tasks[_i].workLeft>>
-			@@.noteworthy;<<= capFirstChar(setup.prosthetics[$researchLab.tasks[_i].id].name)>>@@.
+			@@.noteworthy;<<= capFirstChar(App.Data.prosthetics[$researchLab.tasks[_i].id].name)>>@@.
 			<<if $researchLab.speed > 0>>
 				Finished in approximately <<= (Math.floor(_j / $researchLab.speed) + 1)>> week(s).
 			<</if>>
@@ -188,19 +188,19 @@
 	<h3>Research</h3>
 
 	Available research projects:
-	<<for _p range setup.prostheticIDs>>
+	<<for _p range App.Data.prostheticIDs>>
 		<<if $prosthetics[_p].research == 0>>
 			<<if _p != "erectile">> /*excludes erectile*/
 			<div class="indent">
-			<<if setup.prosthetics[_p].level <= $prostheticsUpgrade>>
+			<<if App.Data.prosthetics[_p].level <= $prostheticsUpgrade>>
 				<<capture _p>>
-				<<link "Reverse engineer <<= addA(setup.prosthetics[_p].name)>>" "Prosthetic Lab">>
-					<<set cashX(forceNeg(setup.prosthetics[_p].costs), "labResearch"), $prosthetics[_p].research = -1, $researchLab.tasks.push({type: "research", id: _p, workLeft: setup.prosthetics[_p].research})>>
+				<<link "Reverse engineer <<= addA(App.Data.prosthetics[_p].name)>>" "Prosthetic Lab">>
+					<<set cashX(forceNeg(App.Data.prosthetics[_p].costs), "labResearch"), $prosthetics[_p].research = -1, $researchLab.tasks.push({type: "research", id: _p, workLeft: App.Data.prosthetics[_p].research})>>
 				<</link>>
 				<</capture>>
-					//Costs <<= cashFormat(setup.prosthetics[_p].costs)>> of initial investment.//
+					//Costs <<= cashFormat(App.Data.prosthetics[_p].costs)>> of initial investment.//
 			<<else>>
-				//You need better contracts to get the required research material for reverse engineering <<= addA(setup.prosthetics[_p].name)>>.//
+				//You need better contracts to get the required research material for reverse engineering <<= addA(App.Data.prosthetics[_p].name)>>.//
 			<</if>>
 			</div>
 			<</if>>
@@ -210,13 +210,13 @@
 	<h3>Manufacture</h3>
 
 	Available building projects:
-	<<for _p range setup.prostheticIDs>>
+	<<for _p range App.Data.prostheticIDs>>
 		<<if $prosthetics[_p].research == 1>>
 			<<if _p != "erectile">> /*excludes erectile*/
 			<div class="indent">
 			<<capture _p>>
-			<<link "Build <<= addA(setup.prosthetics[_p].name)>>" "Prosthetic Lab">>
-				<<set $researchLab.tasks.push({type: "craft", id: _p, workLeft: setup.prosthetics[_p].craft})>>
+			<<link "Build <<= addA(App.Data.prosthetics[_p].name)>>" "Prosthetic Lab">>
+				<<set $researchLab.tasks.push({type: "craft", id: _p, workLeft: App.Data.prosthetics[_p].craft})>>
 			<</link>>
 			<</capture>>
 			</div>
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index 879e7b183a43f00e64cc41cb84f82cd8048677e5..a17231211b49c87fe7148ca4e46c2a79127a8755 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -2488,7 +2488,7 @@ App.Entity.SlaveState = class SlaveState {
 		 * Array that holds a slaves fitted prosthetics. Objects are used to ensure easier expansion later (tattoos for limbs and similar).
 		 *
 		 * Elements of the array should be objects.
-		 * * .id: ID of the prosthetic, see setup.prostheticIDs
+		 * * .id: ID of the prosthetic, see App.Data.prostheticIDs
 		 * @type {Array.<{id:string}>} */
 		this.readyProsthetics = [];
 		/** */
diff --git a/src/npc/children/ChildState.js b/src/npc/children/ChildState.js
index 3451fbc14378d62a84630ab428ceb4f5c49a8f6a..8694e2f4ae32de76817984da7aa489f580f354a8 100644
--- a/src/npc/children/ChildState.js
+++ b/src/npc/children/ChildState.js
@@ -1810,7 +1810,7 @@ App.Facilities.Nursery.ChildState = class ChildState {
 		 * Array that holds a slaves fitted prosthetics. Objects are used to ensure easier expansion later (tattoos for limbs and similar).
 		 *
 		 * Elements of the array should be objects.
-		 * * .id: ID of the prosthetic, see setup.prostheticIDs
+		 * * .id: ID of the prosthetic, see App.Data.prostheticIDs
 		 * @type {Array.<{id:string}>} */
 		this.readyProsthetics = [];
 		/**  */
diff --git a/src/npc/descriptions/limbs.js b/src/npc/descriptions/limbs.js
index 8cc98f12efed0c9870497f79b8f3ac257e6f2a2a..3ed278edafea2863b396b554bd3ed2d454ecfb7b 100644
--- a/src/npc/descriptions/limbs.js
+++ b/src/npc/descriptions/limbs.js
@@ -1,4 +1,4 @@
-App.Desc.limbChange = function() {
+App.Medicine.Prosthetics = (function() {
 	return {
 		currentLimbs: currentLimbs,
 		amputate: amputate,
@@ -11,7 +11,7 @@ App.Desc.limbChange = function() {
 	/**
 	 * Generates an object usable with the standard limb check functions.
 	 * @param {App.Entity.SlaveState} slave
-	 * @returns {{}}
+	 * @returns {FC.LimbsState}
 	 */
 	function currentLimbs(slave) {
 		let s = {arm: {left: {type: 1}, right: {type: 1}}, leg: {left: {type: 1}, right: {type: 1}}, PLimb: 0};
@@ -43,42 +43,48 @@ App.Desc.limbChange = function() {
 	 * @param {App.Entity.SlaveState} slave
 	 * @param {FC.LimbsState} oldLimbs
 	 * @param {string} returnTo
-	 * @returns {string}
+	 * @returns {Element|DocumentFragment}
 	 */
 	function amputate(slave, oldLimbs, returnTo) {
 		const {his} = getPronouns(slave);
 
-		/**
-		 * @param {number} id
-		 */
-		function install(id) {
-			slave.PLimb = id;
-			surgeryDamage(slave, 10);
-			App.UI.replace("#amputate", App.Desc.limbChange().prosthetic(slave, oldLimbs, returnTo));
-		}
-
-		function noInstall() {
-			App.UI.replace("#amputate", App.Desc.limbChange().reaction(slave, oldLimbs, returnTo));
-		}
+		const r = [];
 
 		let implant = false;
-		let r = "";
-
 		if (slave.PLimb < 1 && isProstheticAvailable(slave, "interfaceP1")) {
 			implant = true;
-			r += `<div>${App.UI.link("Install basic interface", install, [1])}</div>`;
+			r.push(App.UI.DOM.makeElement("div", App.UI.DOM.link("Install basic interface", () => install(1))));
 		}
 		if (slave.PLimb < 2 && isProstheticAvailable(slave, "interfaceP2")) {
 			implant = true;
-			r += `<div>${App.UI.link("Install advanced interface", install, [2])}</div>`;
+			r.push(App.UI.DOM.makeElement("div", App.UI.DOM.link("Install advanced interface", () => install(2))));
 		}
 
+		// check if we can install a limb interface and if yes, give player the option to do so.
 		if (implant) {
-			return "<span id='amputate'>" +
-				`<div>Since you already have a prosthetic interface prepared for this slave, you can install it during the operation. The procedure will put additional strain on ${his} health but less so than if you were to perform the procedures separately.</div>` +
-				`${r}<div>${App.UI.link("Do not install", noInstall)}</div></span>`;
+			const outerDiv = document.createElement("div");
+			outerDiv.id = "amputate";
+			outerDiv.append(
+				App.UI.DOM.makeElement("div", `Since you already have a prosthetic interface prepared for this slave, you can install it during the operation. The procedure will put additional strain on ${his} health but less so than if you were to perform the procedures separately.`),
+				...r,
+				App.UI.DOM.makeElement("div", App.UI.DOM.link("Do not install", noInstall)));
+			return outerDiv;
 		}
 
+		/**
+		 * @param {number} id
+		 */
+		function install(id) {
+			slave.PLimb = id;
+			surgeryDamage(slave, 10);
+			App.UI.DOM.replace("#amputate", App.Medicine.Prosthetics.prosthetic(slave, oldLimbs, returnTo));
+		}
+
+		function noInstall() {
+			App.UI.DOM.replace("#amputate", App.Medicine.Prosthetics.reaction(slave, oldLimbs, returnTo));
+		}
+
+		// check if there is a limb interface installed already, if there is show limb selection screen
 		if (slave.PLimb > 0) {
 			return prosthetic(slave, oldLimbs, returnTo);
 		}
@@ -89,7 +95,7 @@ App.Desc.limbChange = function() {
 	 * @param {App.Entity.SlaveState} slave
 	 * @param {FC.LimbsState} oldLimbs
 	 * @param {string} returnTo
-	 * @returns {string}
+	 * @returns {DocumentFragment|HTMLDivElement}
 	 */
 	function prosthetic(slave, oldLimbs, returnTo) {
 		if (!(isProstheticAvailable(slave, "basicL") || isProstheticAvailable(slave, "sexL")
@@ -99,28 +105,76 @@ App.Desc.limbChange = function() {
 		}
 		const {him} = getPronouns(slave);
 
-		let r = `<div>Since you already have limbs prepared for ${him} you might as well attach them while you are working on ${him}:</div>` +
-			selector(slave, oldLimbs, returnTo);
+		const div = document.createElement("div");
+		div.id = "selector";
+
+		App.UI.DOM.appendNewElement("div", div, `Since you already have limbs prepared for ${him} you might as well attach them while you are working on ${him}:`);
+		div.append(selector(slave, oldLimbs, returnTo));
 
-		return `<span id="selector">${r}</span>`;
+		return div;
 	}
 
 	/**
 	 * Displays a selector for prosthetic limbs of getSlave(V.AS)
 	 * @param {App.Entity.SlaveState} slave
-	 * @param {{}} oldLimbs
-	 * @param {string} [returnTo]
-	 * @returns {string}
+	 * @param {FC.LimbsState} oldLimbs
+	 * @param {string} [returnTo=""]
+	 * @returns {HTMLSpanElement|DocumentFragment}
 	 */
 	function selector(slave, oldLimbs, returnTo = "") {
 		const {her} = getPronouns(slave);
 		if (hasAllNaturalLimbs(slave)) {
-			return `<span class='detail'>You must amputate ${her} limbs before you can attach prosthetics.</span>`;
+			return App.UI.DOM.makeElement("span", `You must amputate ${her} limbs before you can attach prosthetics.`, "detail");
 		}
 		if (slave.PLimb < 1) {
-			return "<span class='detail'>You must install a prosthetic interface before you can attach prosthetics.</span>";
+			return App.UI.DOM.makeElement("span", `You must install a prosthetic interface before you can attach prosthetics.`, "detail");
+		}
+
+		const {his} = getPronouns(slave);
+		const newState = currentState(slave);
+
+		const f = document.createDocumentFragment();
+
+		const limbSelector = document.createElement("div");
+		limbSelector.classList.add("limb-selector");
+
+		App.UI.DOM.appendNewElement("div", limbSelector, "");
+		App.UI.DOM.appendNewElement("div", limbSelector, "Left Arm");
+		App.UI.DOM.appendNewElement("div", limbSelector, "Right Arm");
+		App.UI.DOM.appendNewElement("div", limbSelector, "Left Leg");
+		App.UI.DOM.appendNewElement("div", limbSelector, "Right Leg");
+
+		limbSelector.append(row("None", 0));
+		App.Data.limbs.forEach((limb, key) => {
+			if (isProstheticAvailable(slave, limb.prostheticKey)) {
+				if (limb.minimumInterface <= slave.PLimb) {
+					limbSelector.append(row(capFirstChar(limb.short), key));
+				} else {
+					App.UI.DOM.appendNewElement("div", limbSelector,
+						`You need to upgrade ${his} prosthetic interface to attach ${limb.short} limbs.`, ["full", "detail"]);
+				}
+			}
+		});
+
+		f.append(limbSelector);
+		f.append(apply());
+
+		return f;
+
+		function apply() {
+			V.AS = slave.ID;
+
+			return App.UI.DOM.link("Apply", () => {
+				App.Medicine.Prosthetics.applySelector(slave, newState);
+				if (returnTo) {
+					App.UI.DOM.replace("#selector", reaction(slave, oldLimbs, returnTo));
+				} else {
+					V.prostheticsConfig = "limbs";
+					V.oldLimbs = oldLimbs;
+					Engine.play("Prosthetics Configuration");
+				}
+			});
 		}
-		const state = currentState(slave);
 
 		/**
 		 * Generates an array with the current limbs of a slave.
@@ -128,71 +182,48 @@ App.Desc.limbChange = function() {
 		 * @returns {number[]}
 		 */
 		function currentState(slave) {
-			let a = [];
-			a[0] = getLeftArmID(slave);
-			a[1] = getRightArmID(slave);
-			a[2] = getLeftLegID(slave);
-			a[3] = getRightLegID(slave);
-			return a;
+			return [getLeftArmID(slave), getRightArmID(slave), getLeftLegID(slave), getRightLegID(slave)];
 		}
 
 		/**
-		 * @param {number} pos
+		 * @param {number} limb
 		 * @param {number} id
-		 * @returns {string}
+		 * @returns {HTMLDivElement}
 		 */
-		function entry(pos, id) {
-			if (state[pos] === 1) {
-				return "<div></div>";
+		function radio(limb, id) {
+			const div = document.createElement("div");
+
+			if (newState[limb] !== 1) {
+				const radio = document.createElement("input");
+				radio.type = "radio";
+				radio.name = "limb" + limb;
+				if (newState[limb] === id) {
+					radio.checked = true;
+				}
+				radio.onclick = () => {
+					newState[limb] = id;
+				};
+				div.append(radio);
 			}
-			return `<div><<radiobutton "_newState[${pos}]" ${id}${state[pos] === id ? " checked" : ""}>></div>`;
+
+			return div;
 		}
 
 		/**
 		 * @param {string} title
 		 * @param {number} id
-		 * @returns {string}
+		 * @returns {DocumentFragment}
 		 */
 		function row(title, id) {
-			return `<div>${title}</div>${entry(0, id)}${entry(1, id)}${entry(2, id)}${entry(3, id)}`;
-		}
+			const f = document.createDocumentFragment();
 
-		let r = "<div></div><div>Left Arm</div><div>Right Arm</div><div>Left Leg</div><div>Right Leg</div>";
+			App.UI.DOM.appendNewElement("div", f, title);
 
-		r += row("None", 0);
-		if (isProstheticAvailable(slave, "basicL")) {
-			r += row("Basic prosthetic", 2);
-		}
-		if (isProstheticAvailable(slave, "sexL")) {
-			r += row("Advanced sex limb", 3);
-		}
-		if (isProstheticAvailable(slave, "beautyL")) {
-			r += row("Advanced beauty limb", 4);
-		}
-		if (isProstheticAvailable(slave, "combatL")) {
-			r += row("Advanced combat limb", 5);
-		}
-		if (isProstheticAvailable(slave, "cyberneticL")) {
-			if (slave.PLimb > 1) {
-				r += row("Cybernetic limb", 6);
-			} else {
-				r += "<div class='full'><span class='detail'>Install an advanced interface to attach cybernetic limbs.</span></div>";
+			for (let i = 0; i < newState.length; i++) {
+				f.append(radio(i, id));
 			}
-		}
-
-		return `<<set _newState = [${state}]>><div class='limb-selector'>${r}</div>${apply()}`;
 
-		function apply() {
-			let s;
-			V.AS = slave.ID;
-			if (!returnTo) {
-				s = `<<set $prostheticsConfig = "limbs", $oldLimbs = ${JSON.stringify(oldLimbs)}>>` +
-					'<<goto "Prosthetics Configuration">>';
-			} else {
-				s = `<<replace "#selector">><<= App.Desc.limbChange().reaction(getSlave(${slave.ID}), ${JSON.stringify(oldLimbs)}, "${returnTo}")>><</replace>>`;
-			}
-
-			return `<<link "Apply">><<run App.Desc.limbChange().applySelector(getSlave(${slave.ID}), _newState)>>${s}<</link>>`;
+			return f;
 		}
 	}
 
@@ -240,7 +271,7 @@ App.Desc.limbChange = function() {
 	 * @param {App.Entity.SlaveState} slave
 	 * @param {FC.LimbsState} oldLimbs
 	 * @param {string} returnTo
-	 * @returns {string}
+	 * @returns {DocumentFragment}
 	 */
 	function reaction(slave, oldLimbs, returnTo = "") {
 		let r = "";
@@ -260,14 +291,15 @@ App.Desc.limbChange = function() {
 			r += `Right leg was ${idToDescription(getRightLegID(oldLimbs))} and is now ${idToDescription(getRightLegID(slave))}. `;
 		}
 
-		r += "<br>";
+		const f = document.createDocumentFragment();
+
+		App.UI.DOM.appendNewElement("p", f, r,);
+		App.UI.DOM.appendNewElement("p", f, "Slave's reaction", "note");
 
-		r += "<span style='font-style: italic'>Slave's reaction</span>";
-		// TODO
-		// reaction based on limb change & devotion/trust
 		if (returnTo) {
-			r = `<br><br>${r}<br>[[Continue|${returnTo}]]`;
+			f.append(App.UI.DOM.passageLink("Continue", returnTo));
 		}
-		return r;
+
+		return f;
 	}
-};
+})();
diff --git a/src/npc/surgery/surgeryDegradation.js b/src/npc/surgery/surgeryDegradation.js
index 5c61c85c3f0d200043eb182324d02d38b513004b..4e3bc2bf43f1066d3dc759d496441dcde29c12a7 100644
--- a/src/npc/surgery/surgeryDegradation.js
+++ b/src/npc/surgery/surgeryDegradation.js
@@ -3485,16 +3485,12 @@ App.UI.SlaveInteract.surgeryDegradation = function(slave) {
 				break;
 			case "amp":
 				V.nextButton = " ";
-				const ampFrag = jQuery(document.createDocumentFragment());
-				ampFrag.wiki(App.Desc.limbChange().amputate(slave, V.oldLimbs, "Remote Surgery"));
-				r.push(ampFrag);
+				r.push(App.Medicine.Prosthetics.amputate(slave, V.oldLimbs, "Remote Surgery"));
 				delete V.oldLimbs;
 				break;
 			case "PLimb interface":
 				V.nextButton = " ";
-				const prosthFrag = jQuery(document.createDocumentFragment());
-				prosthFrag.wiki(App.Desc.limbChange().prosthetic(slave, V.oldLimbs, "Remote Surgery"));
-				r.push(prosthFrag);
+				r.push(App.Medicine.Prosthetics.prosthetic(slave, V.oldLimbs, "Remote Surgery"));
 				delete V.oldLimbs;
 				break;
 			/*
diff --git a/src/uncategorized/multiImplant.tw b/src/uncategorized/multiImplant.tw
index edf763e58a6842ec8f3d79c8ee84cdacb8dd70f0..2ecb17b0c920f3f006c67ab4841257494a8c51a4 100644
--- a/src/uncategorized/multiImplant.tw
+++ b/src/uncategorized/multiImplant.tw
@@ -95,34 +95,34 @@ that are ready be sent down.
 					<<set surgeryDamage(getSlave($AS), 20), $surgeryType = "ocular implant">>
 					<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
 				<<else>>
-					//Since $he has working eyes the <<= setup.prosthetics.ocular.name>> will be put into storage.//
+					//Since $he has working eyes the <<= App.Data.prosthetics.ocular.name>> will be put into storage.//
 				<</if>>
 			<<case "cochlear">>
 				<<if getSlave($AS).hears != 0>>
 					<<set getSlave($AS).earImplant = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "cochlear implant">>
 					<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
 				<<else>>
-					//Since $he has working ears the <<= setup.prosthetics.cochlear.name>> will be put into storage.//
+					//Since $he has working ears the <<= App.Data.prosthetics.cochlear.name>> will be put into storage.//
 				<</if>>
 			<<case "electrolarynx">>
 				<<if getSlave($AS).voice <= 0>>
 					<<set getSlave($AS).electrolarynx = 1, getSlave($AS).voice = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "electrolarynx">>
 					<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
 				<<else>>
-					//Since $he has a voice the <<= setup.prosthetics.electrolarynx.name>> will be put into storage.//
+					//Since $he has a voice the <<= App.Data.prosthetics.electrolarynx.name>> will be put into storage.//
 				<</if>>
 			<<case "interfaceP1">>
 				<<if hasAnyNaturalLimbs(getSlave($AS))>>
-					//Since $he has at least one healthy limb the <<= setup.prosthetics.interfaceP1.name>> will be put into storage.//
+					//Since $he has at least one healthy limb the <<= App.Data.prosthetics.interfaceP1.name>> will be put into storage.//
 				<<elseif getSlave($AS).PLimb == 2>>
-					//Since $he already has <<= addA(setup.prosthetics.interfaceP2.name)>> installed the <<= setup.prosthetics.interfaceP1.name>> will be put into storage.//
+					//Since $he already has <<= addA(App.Data.prosthetics.interfaceP2.name)>> installed the <<= App.Data.prosthetics.interfaceP1.name>> will be put into storage.//
 				<<else>>
 					<<set getSlave($AS).PLimb = 1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 20), $surgeryType = "PLimb interface1">>
 					<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
 				<</if>>
 			<<case "interfaceP2">>
 				<<if hasAllNaturalLimbs(getSlave($AS))>>
-					//Since $he has no amputated limbs the <<= setup.prosthetics.interfaceP2.name>> will be put into storage.//
+					//Since $he has no amputated limbs the <<= App.Data.prosthetics.interfaceP2.name>> will be put into storage.//
 				<<elseif getSlave($AS).PLimb == 1>>
 					<<set getSlave($AS).PLimb = 2, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS)), surgeryDamage(getSlave($AS), 5), $surgeryType = "PLimb interface3">>
 					<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
@@ -132,48 +132,48 @@ that are ready be sent down.
 				<</if>>
 			<<case "basicL" "sexL" "beautyL" "combatL" "cyberneticL">>
 				<<if getSlave($AS).fuckdoll != 0>>
-					//Since a Fuckdoll can't use prosthetic limbs the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
+					//Since a Fuckdoll can't use prosthetic limbs the <<= App.Data.prosthetics[_p.id].name>> will be put into storage.//
 				<<elseif hasAllNaturalLimbs(getSlave($AS))>>
-					//Since $he needs an amputated limb to attach prosthetics the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
+					//Since $he needs an amputated limb to attach prosthetics the <<= App.Data.prosthetics[_p.id].name>> will be put into storage.//
 				<<elseif getSlave($AS).PLimb == 0>>
-					//Since $he must have a prosthetic interface installed to attach prosthetic limbs the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
+					//Since $he must have a prosthetic interface installed to attach prosthetic limbs the <<= App.Data.prosthetics[_p.id].name>> will be put into storage.//
 				<<else>>
 					<<if _p.id == "basicL">>
-						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
+						<<set _state = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
+							<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), _state)>>
 						<<else>>
-							//Since $he already has more advanced prosthetic limbs attached the <<= setup.prosthetics.basicL.name>> will be put into storage.//
+							//Since $he already has more advanced prosthetic limbs attached the <<= App.Data.prosthetics.basicL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "sexL">>
-						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 3)>>
+						<<set _state = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 3)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
+							<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), _state)>>
 						<<else>>
-							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.sexL.name>> will be put into storage.//
+							//Since $he already has advanced prosthetic limbs attached the <<= App.Data.prosthetics.sexL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "beautyL">>
-						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 4)>>
+						<<set _state = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 4)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
+							<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), _state)>>
 						<<else>>
-							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.beautyL.name>> will be put into storage.//
+							//Since $he already has advanced prosthetic limbs attached the <<= App.Data.prosthetics.beautyL.name>> will be put into storage.//
 						<</if>>
 					<<elseif _p.id == "combatL">>
-						<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 5)>>
+						<<set _state = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 5)>>
 						<<if _change>>
-							<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
+							<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), _state)>>
 						<<else>>
-							//Since $he already has advanced prosthetic limbs attached the <<= setup.prosthetics.combatL.name>> will be put into storage.//
+							//Since $he already has advanced prosthetic limbs attached the <<= App.Data.prosthetics.combatL.name>> will be put into storage.//
 						<</if>>
 					<<else>>
 						<<if getSlave($AS).PLimb == 2>>
-							<<set _state = App.Desc.limbChange().currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
+							<<set _state = App.Medicine.Prosthetics.currentLimbs(getSlave($AS)), _change = upgradeLimbs(getSlave($AS), 2)>>
 							<<if _change>>
-								<<= App.Desc.limbChange().reaction(getSlave($AS), _state)>>
+								<<includeDOM App.Medicine.Prosthetics.reaction(getSlave($AS), _state)>>
 							<</if>>
 						<<else>>
-							//Since $he must have <<= addA(setup.prosthetics.interfaceP2.name)>> installed to attach cybernetic limbs the <<= setup.prosthetics.cyberneticL.name>> will be put into storage.//
+							//Since $he must have <<= addA(App.Data.prosthetics.interfaceP2.name)>> installed to attach cybernetic limbs the <<= App.Data.prosthetics.cyberneticL.name>> will be put into storage.//
 						<</if>>
 					<</if>>
 				<</if>>
@@ -182,11 +182,11 @@ that are ready be sent down.
 				<<includeDOM App.UI.SlaveInteract.surgeryDegradation(getSlave($AS))>>
 			<<case "modT" "sexT" "combatT">>
 				<<if getSlave($AS).PTail == 0>>
-					//Since $he must have <<= addA(setup.prosthetics.interfaceTail.name)>> installed to attach tails the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
+					//Since $he must have <<= addA(App.Data.prosthetics.interfaceTail.name)>> installed to attach tails the <<= App.Data.prosthetics[_p.id].name>> will be put into storage.//
 				<<elseif getSlave($AS).tail != "none">>
-					//Since $he currently has a tail attached the <<= setup.prosthetics[_p.id].name>> will be put into storage.//
+					//Since $he currently has a tail attached the <<= App.Data.prosthetics[_p.id].name>> will be put into storage.//
 				<<elseif _p.id == "modT">>
-					//Since installing <<= addA(setup.prosthetics.modT.name)>> is complicated it can't be automated.//
+					//Since installing <<= addA(App.Data.prosthetics.modT.name)>> is complicated it can't be automated.//
 					/*Reason: there are different designs player can choose from.*/
 				<<elseif _p.id == "combatT">>
 					<<set $prostheticsConfig = "attachTail", getSlave($AS).tail = "combat", getSlave($AS).tailColor = "jet black">>
@@ -196,7 +196,7 @@ that are ready be sent down.
 					<<include "Prosthetics Configuration">>
 				<</if>>
 			<<default>>
-				//Since there is no automated procedure to implant/attach <<= setup.prosthetics[_p.id].name>> it will be put into storage.//
+				//Since there is no automated procedure to implant/attach <<= App.Data.prosthetics[_p.id].name>> it will be put into storage.//
 			<</switch>>
 			<br>
 		<</if>>