diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js
index 6cc45818eefca0953ecba9fe649bc4f72072feea..1b8a22c2c43d716776f98bd3a54050a919083835 100644
--- a/src/js/rulesAssistantOptions.js
+++ b/src/js/rulesAssistantOptions.js
@@ -165,31 +165,22 @@ window.rulesAssistantOptions = (function() {
 	// has a short explanation (the prefix) and a value display
 	// value display can optionally be an editable text input field
 	// it can be "bound" to a variable by setting its "onchange" method
-	class List extends Element {
-		constructor(prefix, data=[], textinput=false) {
-			super(prefix + ": ", textinput);
+	class EditorWithShortcuts extends Element {
+		constructor(prefix, data=[], editor=false, ...args) {
+			super(prefix + ": ", editor, ...args);
 			this.selectedItem = null;
 			data.forEach(item => this.appendChild(new ListItem(...item)));
 		}
 
-		render(prefix, textinput) {
+		createEditor(...args) { return null; }
+
+		render(prefix, editor, ...args) {
 			const elem = document.createElement("div");
 			const label = document.createElement("span");
 			label.innerHTML = prefix;
-			let value;
-			if (textinput) {
-				value = document.createElement("input");
-				value.setAttribute("type", "text");
-				value.classList.add("rajs-value"); //
-				// call the variable binding when the input field is no longer being edited, and when the enter key is pressed
-				value.onblur = () => {this.inputEdited(); };
-				value.onkeypress = (e) => { if (returnP(e)) this.inputEdited(); };
-			} else {
-				value = document.createElement("strong");
-			}
-			this.value = value;
+			this.value = editor ? this.createEditor(...args) : document.createElement("strong");
 			elem.appendChild(label);
-			elem.appendChild(value);
+			elem.appendChild(this.value);
 			elem.classList.add("rajs-list");
 			return elem;
 		}
@@ -256,6 +247,54 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
+	class List extends EditorWithShortcuts {
+		constructor(prefix, data = [], textinput = false) {
+			super(prefix, data, textinput);
+		}
+
+		createEditor() {
+			let res = document.createElement("input");
+			res.setAttribute("type", "text");
+			res.classList.add("rajs-value"); //
+			// call the variable binding when the input field is no longer being edited, and when the enter key is pressed
+			res.onblur = () => {
+				this.inputEdited();
+			};
+			res.onkeypress = (e) => {
+				if (returnP(e)) this.inputEdited();
+			};
+			return res;
+		}
+	}
+
+	class NumberRange extends EditorWithShortcuts {
+		constructor(prefix, data = [], min, max, spinbox = false) {
+			super(prefix, data, spinbox);
+			this.min = min;
+			this.max = max;
+			this.nullValue = data.length ? data[0][1] : null;
+		}
+
+		createEditor(min, max) {
+			let res = document.createElement("input");
+			res.setAttribute("type", "number");
+			res.setAttribute("min", this.min);
+			res.setAttribute("max", this.max);
+			res.classList.add("rajs-value"); //
+			res.onblur = () => {
+				this.inputEdited();
+			};
+			res.onkeypress = (e) => {
+				if (returnP(e)) this.inputEdited();
+			};
+			return res;
+		}
+
+		parse(what) {
+			return what === "" ? this.nullValue : what;
+		}
+	}
+
 	// a way to organize lists with too many elements in subsections
 	// children are bound to the master list
 	class ListSubSection extends Element {
@@ -1595,7 +1634,7 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class BreastGrowthList extends List {
+	class BreastGrowthList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1605,13 +1644,13 @@ window.rulesAssistantOptions = (function() {
 				["Unlimited", 48000],
 				["None", 0]
 			];
-			super("Breasts", pairs, true);
+			super("Breasts", pairs, 0, 48000, true);
 			this.setValue(current_rule.set.growth_boobs);
 			this.onchange = (value) => current_rule.set.growth_boobs = value;
 		}
 	}
 
-	class ButtGrowthList extends List {
+	class ButtGrowthList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1621,13 +1660,13 @@ window.rulesAssistantOptions = (function() {
 				["Unlimited", 20],
 				["None", 0]
 			];
-			super("Butts", pairs, true);
+			super("Butts", pairs, 0, 20, true);
 			this.setValue(current_rule.set.growth_butt);
 			this.onchange = (value) => current_rule.set.growth_butt = value;
 		}
 	}
 
-	class LipGrowthList extends List {
+	class LipGrowthList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1636,13 +1675,13 @@ window.rulesAssistantOptions = (function() {
 				["Facepussy", 100],
 				["None", 0]
 			];
-			super("Lips", pairs, true);
+			super("Lips", pairs, 0, 100, true);
 			this.setValue(current_rule.set.growth_lips);
 			this.onchange = (value) => current_rule.set.growth_lips = value;
 		}
 	}
 
-	class DickGrowthList extends List {
+	class DickGrowthList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1651,13 +1690,13 @@ window.rulesAssistantOptions = (function() {
 				["Unlimited", 30],
 				["None", 0]
 			];
-			super("Dicks, if present", pairs, true);
+			super("Dicks, if present", pairs, 0, 30, true);
 			this.setValue(current_rule.set.growth_dick);
 			this.onchange = (value) => current_rule.set.growth_dick = value;
 		}
 	}
 
-	class BallGrowthList extends List {
+	class BallGrowthList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1666,7 +1705,7 @@ window.rulesAssistantOptions = (function() {
 				["Unlimited", 125],
 				["None", 0]
 			];
-			super("Balls, if present", pairs, true);
+			super("Balls, if present", pairs, 0, 125, true);
 			this.setValue(current_rule.set.growth_balls);
 			this.onchange = (value) => current_rule.set.growth_balls = value;
 		}
@@ -1951,7 +1990,7 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class MuscleList extends List {
+	class MuscleList extends NumberRange {
 		constructor() {
 			const pairs = [
 				["No default setting", "no default setting"],
@@ -1961,7 +2000,7 @@ window.rulesAssistantOptions = (function() {
 				["Massive", 100],
 				["Weak", -20]
 			];
-			super("Muscles", pairs, true);
+			super("Muscles", pairs, -20, 100, true);
 			this.setValue(current_rule.set.muscles);
 			this.onchange = (value) => current_rule.set.muscles = value;
 		}
@@ -3441,7 +3480,7 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class LipSurgeryList extends List {
+	class LipSurgeryList extends NumberRange {
 		constructor() {
 			const items = [
 				["no default setting"],
@@ -3451,13 +3490,13 @@ window.rulesAssistantOptions = (function() {
 				["huge", 70],
 				["facepussy", 95],
 			];
-			super("Lip implants", items);
+			super("Lip implants", items, 0, 95, true);
 			this.setValue(current_rule.set.surgery_lips);
 			this.onchange = (value) => current_rule.set.surgery_lips = value;
 		}
 	}
 
-	class ButtSurgeryList extends List {
+	class ButtSurgeryList extends NumberRange {
 		constructor() {
 			const items = [
 				["no default setting"],
@@ -3467,13 +3506,13 @@ window.rulesAssistantOptions = (function() {
 				["huge", 6],
 				["maximized", 9],
 			];
-			super("Buttock implants", items);
+			super("Buttock implants", items, 0, 9, true);
 			this.setValue(current_rule.set.surgery_butt);
 			this.onchange = (value) => current_rule.set.surgery_butt = value;
 		}
 	}
 
-	class BreastSurgeryList extends List {
+	class BreastSurgeryList extends NumberRange {
 		constructor() {
 			const items = [
 				["no default setting"],
@@ -3484,7 +3523,7 @@ window.rulesAssistantOptions = (function() {
 				["barely functional", 9000],
 				["maximized", 48000]
 			];
-			super("Breast implants", items);
+			super("Breast implants", items, 0, 48000, true);
 			this.setValue(current_rule.set.surgery_boobs);
 			this.onchange = (value) => current_rule.set.surgery_boobs = value;
 		}