diff --git a/src/facilities/salon/salonPassage.js b/src/facilities/salon/salonPassage.js
index bebe975fa5e5904c660702a33beb88604bbc0535..fd45b723a7ac04066addaf56e0aa8d23791faa7d 100644
--- a/src/facilities/salon/salonPassage.js
+++ b/src/facilities/salon/salonPassage.js
@@ -147,9 +147,106 @@ App.UI.salon = function(slave, cheat = false) {
 
 	function hair() {
 		const el = new DocumentFragment();
+		let option;
 		App.UI.DOM.appendNewElement("h3", el, "Hair");
+		const options = new App.UI.OptionsGroup();
+		let title;
+		let showChoices = true;
+		const hasWig = (slave.bald === 1 && slave.hStyle !== "bald");
+
+		if (slave.bald === 1) {
+			if (slave.hStyle === "bald") {
+				title = `${He} is completely bald.`;
+				showChoices = false;
+			} else {
+				title = `${His} wig is ${slave.hColor}.`;
+			}
+		} else {
+			title = `${His} hair is ${slave.hColor}.`;
+		}
+		App.UI.DOM.appendNewElement("div", el, title);
+
+		if (slave.bald === 1) {
+			options.addOption(`Use a wig`, "hStyle", slave)
+				.addValue("Enable", "neat").on()
+				.addValue("Disable", "bald").off();
+		}
+
+		if (showChoices) {
+			// Color
+			option = options.addOption("Primary color", "hColor", slave);
+			if (slave.origHColor !== slave.hColor) {
+				option.addValue("Restore natural color", slave.origHColor, billMod);
+			}
+			for (const color of App.Medicine.Modification.Color.Primary) {
+				option.addValue(capFirstChar(color.value), color.value, billMod);
+			}
+			option.pulldown();
+
+			option = options.addOption("Secondary color", "hColor", slave);
+			for (const color of App.Medicine.Modification.Color.Secondary) {
+				option.addValue(color.title, (slave.hColor + color.value), billMod);
+			}
+			option.pulldown();
 
-		el.append(App.Medicine.Salon.hair(slave, {cheat: cheat}));
+			// Style
+			if (slave.hLength > 1) {
+				title = `Style ${hasWig ? "wig" : "hair"} `;
+			} else {
+				title = `${His} ${hasWig ? "wig" : "hair"} is too short to style meaningfully`;
+			}
+			option = options.addOption(title, "hStyle", slave);
+			if (slave.hLength > 1) {
+				for (const style of App.Medicine.Modification.hairStyles.Normal) {
+					option.addValue(style.title, style.value, billMod);
+				}
+				option.pulldown();
+			}
+
+			// Style + Cut
+			if (slave.hLength > 1) {
+				option = options.addOption(`${hasWig ? "Change wig style and length" : "Cut and style hair"}`, "hStyle", slave);
+				if (slave.hLength > 1) {
+					for (const style of App.Medicine.Modification.hairStyles.Cut) {
+						option.addValue(
+							style.title,
+							style.value,
+							() => {
+								slave.hLength = style.hLength;
+								billMod();
+							});
+					}
+				}
+
+				// Length
+				option = options.addOption(`${hasWig ? "Find longer or shorter wig" : "Cut or lengthen hair"}`, "hLength", slave);
+				for (const style of App.Medicine.Modification.hairStyles.Length) {
+					if (style.hasOwnProperty("requirements") && !style.requirements(slave)) {
+						continue;
+					}
+					const length = style.hasOwnProperty("hLength") ? style.hLength : slave.hLength;
+					option.addValue(
+						style.title,
+						length,
+						() => {
+							if (style.hasOwnProperty("onApplication")) {
+								style.onApplication(slave);
+							}
+							billMod();
+						});
+				}
+				option.showTextBox();
+			}
+
+			// Maintain
+			if (!hasWig) {
+				options.addOption(`Maintain this length`, "haircuts", slave)
+					.addValue("Enable", 1).on()
+					.addValue("Disable", 0).off();
+			}
+		}
+
+		el.append(options.render());
 		return el;
 	}
 
diff --git a/src/js/salon.js b/src/js/salon.js
index 04e21f62dd86d985cdc01560d2e7a31783211afb..74916eb4ff5a80df035fa3a44c6ce96fda0e8b43 100644
--- a/src/js/salon.js
+++ b/src/js/salon.js
@@ -328,402 +328,6 @@ App.Medicine.Salon.ears = function(slave, {primaryEarColor = 0, secondaryEarColo
 	}
 };
 
-/**
- * Update hair in salon
- * @param {App.Entity.SlaveState} slave
- * @param {object} params
- * @param {number|string} [params.primaryHairColor]
- * @param {string} [params.secondaryHairColor]
- * @param {Boolean} [params.cheat=false]
- * @returns {HTMLElement}
- */
-App.Medicine.Salon.hair = function(slave, {primaryHairColor = 0, secondaryHairColor = "", cheat = false} = {}) {
-	let updatePrimary = (newVal) => { primaryHairColor = newVal.value; apply(); };
-	let updateSecondary = (newVal) => { secondaryHairColor = newVal.value; apply(); };
-	const {His, his, He, him} = getPronouns(slave);
-	const div = document.createElement("div");
-	div.id = "salon-hair";
-	div.append(contents());
-
-	return div;
-
-	function contents() {
-		const frag = new DocumentFragment();
-		if (slave.bald !== 1) {
-			frag.append(hairDye());
-			frag.append(hairStyle());
-			frag.append(hairLength());
-			frag.append(hairMaint());
-		} else {
-			// Bald
-			if (slave.hStyle === "bald") {
-				frag.append(`${He} is completely bald. `);
-			} else {
-				frag.append(wigDye());
-			}
-			frag.append(wigStyle());
-			frag.append(wigLength());
-		}
-		return frag;
-	}
-
-	function hairDye() {
-		const frag = new DocumentFragment();
-		let div;
-		let p;
-		frag.append(`${His} hair is ${slave.hColor}.`);
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		if (slave.origHColor !== slave.hColor) {
-			div.append(
-				App.UI.DOM.link(
-					"Restore natural color",
-					() => {
-						slave.hColor = slave.origHColor;
-						App.Art.refreshSlaveArt(slave, 3, "art-frame");
-						if (!cheat) {
-							cashX(forceNeg(V.modCost), "slaveMod", slave);
-						}
-						apply();
-					}
-				)
-			);
-			div.append(" or ");
-			App.UI.DOM.appendNewElement("span", div, "choose a new one: ", "note");
-		} else {
-			App.UI.DOM.appendNewElement("span", div, `Choose a dye color before dyeing ${his} hair:`, "note");
-		}
-		frag.append(div);
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(`Colors:`);
-		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
-		frag.append(div);
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(`Highlights:`);
-		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
-		frag.append(div);
-
-		if (primaryHairColor !== 0) {
-			p = document.createElement("p");
-			p.classList.add("choices");
-			p.append(
-				App.UI.DOM.link(
-					`Color ${his} hair`,
-					() => {
-						slave.hColor = (primaryHairColor + secondaryHairColor);
-						App.Art.refreshSlaveArt(slave, 3, "art-frame");
-						if (!cheat) {
-							cashX(forceNeg(V.modCost), "slaveMod", slave);
-						}
-						App.Medicine.Salon.hair(slave); // discard selections after locking them in.
-					}
-				)
-			);
-			p.append(` ${primaryHairColor}${secondaryHairColor} now?`);
-			frag.append(p);
-		}
-		return frag;
-	}
-
-	function hairStyle() {
-		const frag = new DocumentFragment();
-		let div;
-		let method;
-		div = document.createElement("div");
-		if (slave.hStyle !== "shaved") {
-			div.append(`${His} ${slave.hStyle} hair is ${lengthToEitherUnit(slave.hLength)} long. `);
-		} else {
-			div.append(`${His} hair is shaved smooth. `);
-		}
-		App.UI.DOM.appendNewElement("span", div, `General hairstyles will conform to hair length and clothing choices.`, "note");
-		frag.append(div);
-
-		// Normal styles
-		div = document.createElement("div");
-		div.classList.add("choices");
-		method = (newVal) => {
-			slave.hStyle = newVal.value;
-			if (!cheat) {
-				cashX(forceNeg(V.modCost), "slaveMod", slave);
-			}
-			apply();
-		};
-		if (slave.hLength > 1) {
-			div.append(`Style ${his} hair:`);
-			div.append(createList(App.Medicine.Modification.hairStyles.Normal, method));
-		} else {
-			App.UI.DOM.appendNewElement("span", div, `${His} hair is too short to style meaningfully`, "note");
-		}
-		frag.append(div);
-
-		// Short styles, includes cutting
-		div = document.createElement("div");
-		div.classList.add("choices");
-		method = (newVal) => {
-			slave.hStyle = newVal.value;
-			slave.hLength = newVal.hLength;
-			if (!cheat) {
-				cashX(forceNeg(V.modCost), "slaveMod", slave);
-			}
-			apply();
-		};
-		if (slave.hLength > 1) {
-			div.append(`Cut and style ${his} hair:`);
-			div.append(createList(App.Medicine.Modification.hairStyles.Cut, method));
-		}
-		frag.append(div);
-
-		return frag;
-	}
-
-	function hairLength() {
-		const frag = new DocumentFragment();
-		let div = document.createElement("div");
-		div.classList.add("choices");
-		let method = (newVal) => {
-			if (newVal.hasOwnProperty("onApplication")) {
-				newVal.onApplication(slave);
-			}
-			if (newVal.hasOwnProperty("hLength")) {
-				slave.hLength = newVal.hLength;
-			}
-			apply();
-		};
-		const oldHLength = (V.showInches === 2) ? Math.round(slave.hLength / 2.54) : slave.hLength;
-
-		App.UI.DOM.appendNewElement("span", div, `Cut or lengthen ${his} hair:`);
-		div.append(createList(App.Medicine.Modification.hairStyles.Length, method));
-		div.append(" | Custom length: ");
-		div.append(
-			App.UI.DOM.makeTextBox(
-				oldHLength,
-				v => {
-					v = Math.max(v, 0); // Positive hair length only
-					// If they entered "inches," convert
-					if (V.showInches === 2) {
-						v = Math.round(v * 2.54);
-					}
-					slave.hLength = v;
-					if (!cheat) {
-						cashX(forceNeg(V.modCost), "slaveMod", slave);
-					}
-					apply();
-				},
-				true
-			)
-		);
-		if (V.showInches === 1) {
-			div.append(`cm (${cmToInchString(slave.hLength)})`);
-		} else if (V.showInches === 2) {
-			div.append(`inches`);
-		}
-
-		frag.append(div);
-
-		return frag;
-	}
-
-	function hairMaint() {
-		let div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(`Have ${his} hair carefully maintained at its current length: `);
-		let haircuts;
-		let text;
-		if (slave.haircuts === 1) {
-			text = "Cease maintenance";
-			haircuts = 0;
-		} else {
-			text = "Begin maintenance";
-			haircuts = 1;
-		}
-		div.append(
-			App.UI.DOM.link(
-				text,
-				() => {
-					slave.haircuts = haircuts;
-					apply();
-				}
-			)
-		);
-		return div;
-	}
-
-	function wigDye() {
-		const frag = new DocumentFragment();
-		let div;
-		let p;
-		frag.append(`${His} current wig is ${slave.hColor}. `);
-
-		if (slave.hStyle !== "bald") {
-			frag.append(
-				App.UI.DOM.link(
-					"Remove wig",
-					() => {
-						slave.hStyle = "bald";
-						slave.hLength = 0;
-						// I'm not going to charge you for taking off a fucking wig.
-						apply();
-					}
-				)
-			);
-			frag.append(" or ");
-			App.UI.DOM.appendNewElement("span", frag, "choose a new one: ", "note");
-		} else {
-			App.UI.DOM.appendNewElement("span", frag, `Choose a wig color:`, "note");
-		}
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(`Colors:`);
-		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
-		frag.append(div);
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(`Highlights:`);
-		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
-		frag.append(div);
-
-		if (primaryHairColor !== 0) {
-			p = document.createElement("p");
-			p.classList.add("choices");
-			p.append(
-				App.UI.DOM.link(
-					`Change`,
-					() => {
-						slave.earTColor = (primaryHairColor + secondaryHairColor);
-						App.Art.refreshSlaveArt(slave, 3, "art-frame");
-						if (!cheat) {
-							cashX(forceNeg(V.modCost), "slaveMod", slave);
-						}
-						App.Medicine.Salon.hair(slave); // discard selections after locking them in.
-					}
-				)
-			);
-			p.append(` ${his} wig color to ${primaryHairColor}${secondaryHairColor} now?`);
-			frag.append(p);
-		}
-		return frag;
-	}
-
-	function wigLength() {
-		const frag = new DocumentFragment();
-		if (slave.hStyle === "bald") {
-			return frag;
-		}
-		let div = document.createElement("div");
-		div.classList.add("choices");
-		const array = [];
-		for (const number of [10, 30, 60, 90, 120, 150]) {
-			const obj = {};
-			obj.title = lengthToEitherUnit(number);
-			obj.hLength = number;
-			array.push(obj);
-		}
-		let method = (newVal) => {
-			slave.hLength = newVal.hLength;
-			apply();
-		};
-		const oldHLength = (V.showInches === 2) ? Math.round(slave.hLength / 2.54) : slave.hLength;
-		App.UI.DOM.appendNewElement("span", div, `Set wig length to:`, "choices");
-		div.append(createList(array, method));
-		div.append(" | Custom length: ");
-		div.append(
-			App.UI.DOM.makeTextBox(
-				oldHLength,
-				v => {
-					v = Math.max(v, 10); // Wigs must be at least 10 cm
-					// If they entered "inches," convert
-					if (V.showInches === 2) {
-						v = Math.round(v * 2.54);
-					}
-					slave.hLength = v;
-					if (!cheat) {
-						cashX(forceNeg(V.modCost), "slaveMod", slave);
-					}
-					apply();
-				},
-				true
-			)
-		);
-		if (V.showInches === 1) {
-			div.append(`cm (${cmToInchString(slave.hLength)})`);
-		} else if (V.showInches === 2) {
-			div.append(`inches`);
-		}
-
-		frag.append(div);
-
-		return frag;
-	}
-
-	function wigStyle() {
-		const frag = new DocumentFragment();
-		let div = document.createElement("div");
-		div.classList.add("choices");
-		const method = (newVal) => {
-			slave.hStyle = newVal.value;
-			if (!cheat) {
-				cashX(forceNeg(V.modCost), "slaveMod", slave);
-			}
-			apply();
-		};
-
-		if (slave.hStyle !== "bald") {
-			frag.append(`${His} ${slave.hStyle} wig is ${lengthToEitherUnit(slave.hLength)} long. `);
-		} else {
-			frag.append(`${He} is not wearing a wig. `);
-		}
-		App.UI.DOM.appendNewElement("span", frag, `General hairstyles will conform to hair length and clothing choices.`, "note");
-
-		div = document.createElement("div");
-		div.classList.add("choices");
-		if (slave.hStyle === "bald") {
-			div.append(`Give ${him} a wig:`);
-		} else {
-			div.append(`Set wig style:`);
-		}
-		div.append(createList(App.Medicine.Modification.hairStyles.Normal, method));
-		frag.append(div);
-		return frag;
-	}
-
-	function createList(array, method) {
-		const links = [];
-		for (const item of array) {
-			if (item.hasOwnProperty("requirements")) {
-				if (item.requirements(slave) === false) {
-					continue;
-				}
-			}
-			const title = item.title || capFirstChar(item.value);
-			links.push(
-				App.UI.DOM.link(
-					title,
-					() => method(item)
-				)
-			);
-		}
-		return App.UI.DOM.generateLinksStrip(links);
-	}
-
-	function apply() {
-		App.Art.refreshSlaveArt(slave, 3, "art-frame");
-		App.Medicine.Salon.hair(
-			slave,
-			{
-				primaryHairColor: primaryHairColor,
-				secondaryHairColor: secondaryHairColor,
-			}
-		);
-	}
-};
-
 /**
  * Update hair in salon
  * @param {App.Entity.SlaveState} slave