diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index f2f41b84190e01b5452e8c73371ca206cec6c866..535142278921d3feea70174b61a062ea6eabe919 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -165,7 +165,10 @@ App.Data.defaultGameStateVariables = {
 	sortSlavesMain: 1,
 	sortSlavesOrder: "descending",
 	summaryStats: 0,
+	surnameArcology: "",
 	surnameOrder: 0,
+	surnamePCOverride: 0,
+	surnameScheme: 0,
 	/** @type {Object.<string, string>} */
 	tabChoice: {Main: "all"},
 	universalRulesAssignsSelfFacility: 0,
diff --git a/src/facilities/incubator/incubatorInteract.js b/src/facilities/incubator/incubatorInteract.js
index fcca66f3cf5a99ca732a01bad75bb0ba65feafc3..6d164645a3752e3e6fff2a1c7eb91cc8398fe525 100644
--- a/src/facilities/incubator/incubatorInteract.js
+++ b/src/facilities/incubator/incubatorInteract.js
@@ -681,7 +681,7 @@ App.UI.incubator = function() {
 					He, His,
 					he, him, his
 				} = getPronouns(V.incubator.tanks[i]);
-				r.push(App.UI.DOM.makeElement("span", V.incubator.tanks[i].slaveName, "pink"));
+				r.push(App.UI.DOM.makeElement("span", SlaveFullName(V.incubator.tanks[i]), "pink"));
 				r.push(`occupies this tank.`);
 				if (V.geneticMappingUpgrade >= 1) {
 					r.push(`${He} is a`);
diff --git a/src/interaction/siCustom.js b/src/interaction/siCustom.js
index 953681d3c1cc97a94d77355f285dc2fa39517c6d..a7567c0293c749dbe92379d9e28002cea142c996 100644
--- a/src/interaction/siCustom.js
+++ b/src/interaction/siCustom.js
@@ -295,13 +295,22 @@ App.UI.SlaveInteract.custom = function(slave) {
 				);
 			} else {
 				linkArray.push(App.UI.DOM.link(
-					` Restore ${his} birth surname`,
+					`Restore ${his} birth surname`,
 					() => {
 						slave.slaveSurname = slave.birthSurname;
 						updateName(slave, {oldName: oldName, oldSurname: oldSurname});
 					}
 				));
 			}
+			if (getSlave(slave.mother) || getSlave(slave.father) || slave.mother === -1 || slave.father === -1) {
+				linkArray.push(App.UI.DOM.link(
+					`Regenerate ${his} surname based on current Universal Rules`,
+					() => {
+						regenerateSurname(slave);
+						updateName(slave, {oldName: oldName, oldSurname: oldSurname});
+					}
+				));
+			}
 
 			if (slave.slaveSurname) {
 				linkArray.push(App.UI.DOM.link(
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index 98efa2912fe15f6e8751ab25caf46530f0698600..e5b9f78afb71cdda133990f3973ec78fbedb6419 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -1063,11 +1063,15 @@ globalThis.generateChild = function(mother, ovum, incubator = false) {
 	if (!incubator) { // does extra work for the incubator if defined, otherwise builds a simple object
 		child = new App.Facilities.Nursery.InfantState();
 		child.genes = genes.gender;
-		setSlaveName(child, genes);
-		setSurname(child, genes);
-
+		if (genes.clone !== undefined) {
+			child.clone = genes.clone;
+			child.cloneID = genes.cloneID;
+		}
 		child.mother = genes.mother;
 		child.father = genes.father;
+		setSlaveName(child, genes);
+		regenerateSurname(child);
+
 		child.nationality = genes.nationality;
 		child.race = genes.race;
 		child.intelligence = genes.intelligence;
@@ -1114,10 +1118,6 @@ globalThis.generateChild = function(mother, ovum, incubator = false) {
 			child.tailShape = "neko";
 			child.tailColor = child.hColor;
 		}
-		if (genes.clone !== undefined) {
-			child.clone = genes.clone;
-			child.cloneID = genes.cloneID;
-		}
 		if (genes.faceShape !== undefined) {
 			child.faceShape = genes.faceShape;
 		}
@@ -1140,16 +1140,17 @@ globalThis.generateChild = function(mother, ovum, incubator = false) {
 		};
 
 		child = GenerateNewSlave(genes.gender, fixedAge);
-		setSlaveName(child, genes);
-		setSurname(child, genes);
-
-		child.actualAge = 0;
+		child.slaveSurname = 0; // must default, but will be changed later
 		if (genes.clone !== undefined) {
 			child.clone = genes.clone;
 			child.cloneID = genes.cloneID;
 		}
 		child.mother = genes.mother;
 		child.father = genes.father;
+		setSlaveName(child, genes);
+		regenerateSurname(child);
+
+		child.actualAge = 0;
 		child.nationality = genes.nationality;
 		child.race = genes.race;
 		child.origRace = child.race;
@@ -1311,44 +1312,211 @@ globalThis.generateChild = function(mother, ovum, incubator = false) {
 
 /**
  * Sets the child's surname based on information on its mother and father
- * @param {object} child
- * @param {object} genes An object containing child's genetic information
+ * @param {{clone: FC.Zeroable<string>, cloneID: number, mother: number, father: number, genes: FC.GenderGenes, slaveSurname: FC.Zeroable<string>}} child
  */
-function setSurname(child, genes) {
-	child.slaveSurname = genes.surname;
-	if (genes.clone) {
-		if (genes.cloneID === -1) {
+function regenerateSurname(child) {
+	// clone case - copy surname from genetic origin
+	if (child.clone) {
+		if (child.cloneID === -1) {
 			child.slaveSurname = V.PC.slaveSurname;
 		} else {
-			let cloneSeed = getSlave(genes.cloneID);
+			const cloneSeed = getSlave(child.cloneID);
 			if (cloneSeed !== undefined) {
 				if (cloneSeed.slaveSurname !== 0 && cloneSeed.slaveSurname !== "") {
 					child.slaveSurname = cloneSeed.slaveSurname;
 				}
 			}
 		}
-	} else if (genes.mother === -1 || genes.father === -1) {
-		child.slaveSurname = V.PC.slaveSurname;
-	} else if (genes.father > 0) {
-		let currentMother = getSlave(genes.mother);
-		if (currentMother !== undefined) {
-			if (currentMother.slaveSurname !== 0 && currentMother.slaveSurname !== "") {
-				child.slaveSurname = currentMother.slaveSurname;
-			}
+		return;
+	}
+
+	// non-clone case - build surname from parent surnames according to the arcology's universal rules
+	function getMother() {
+		if (child.mother === -1) {
+			return V.PC;
+		}
+		return getSlave(child.mother);
+	}
+
+	function getFather() {
+		if (child.father === -1) {
+			return V.PC;
+		}
+		return getSlave(child.father);
+	}
+
+	/** @param {FC.Zeroable<string>} name */
+	function nameOrNull(name) {
+		// handles names that are 0 or ""
+		return name || null;
+	}
+
+	/** @param {string} surname */
+	function splitSurname(surname) {
+		if (V.surnameScheme === 6) {
+			const parts = surname.split('-', 2);
+			return {pat: parts[0], mat: parts.length > 1 ? parts[1] : null};
 		} else {
-			let currentFather = getSlave(genes.father);
-			if (currentFather !== undefined) {
-				if (currentFather.slaveSurname !== 0 && currentFather.slaveSurname !== "") {
-					child.slaveSurname = currentFather.slaveSurname;
+			const parts = surname.split(' ', 2);
+			if (V.surnameScheme === 7) {
+				return {pat: parts[0], mat: parts.length > 1 ? parts[1] : null};
+			} else if (V.surnameScheme === 8) {
+				if (parts.length === 1) {
+					return {pat: parts[0], mat: null};
+				} else {
+					return {pat: parts[1], mat: parts[0]};
 				}
 			}
 		}
-	} else {
-		let currentMother = getSlave(genes.mother);
-		if (currentMother !== undefined) {
-			if (currentMother.slaveSurname !== 0 && currentMother.slaveSurname !== "") {
-				child.slaveSurname = currentMother.slaveSurname;
+	}
+
+	const father = getFather();
+	const mother = getMother();
+	const fatherName = father ? nameOrNull(father.slaveName) : null;
+	const motherName = mother ? nameOrNull(mother.slaveName) : null;
+
+	function getPatronym() {
+		if (child.mother === -1 && V.surnamePCOverride === 1) {
+			return V.PC.slaveName;
+		} else if (motherName && (!fatherName || (child.father === -1 && V.surnamePCOverride === 2))) {
+			return motherName;
+		} else if (fatherName) {
+			return fatherName;
+		}
+	}
+
+	function getMatronym() {
+		if (child.father === -1 && V.surnamePCOverride === 1) {
+			return V.PC.slaveName;
+		} else if (fatherName && (!motherName || (child.mother === -1 && V.surnamePCOverride === 2))) {
+			return fatherName;
+		} else if (motherName) {
+			return motherName;
+		}
+	}
+
+	const fatherSurname = father ? nameOrNull(father.slaveSurname) : null;
+	const motherSurname = mother ? nameOrNull(mother.slaveSurname) : null;
+
+	function makeDoubleSurname() {
+		const dadPart = fatherSurname ? splitSurname(fatherSurname) : null;
+		const momPart = motherSurname ? splitSurname(motherSurname) : null;
+		if (dadPart && momPart && dadPart.pat && momPart.pat) {
+			// both surnames contain a patrilineal (primary) part; return them appropriately
+			if (V.surnameScheme === 6 && dadPart.pat === momPart.pat) {
+				// "Vasquez Ruiz" and "Vasquez Martinez" yielding "Vasquez Vasquez" (Hispanic/Lusitanic) is fine
+				// ...but "Smith-Jones" and "Smith-Baker" yielding "Smith-Smith" (double-barreled) is not
+				if (!dadPart.mat && momPart.mat) {
+					return momPart; // Smith (P) and Smith-Baker (M) yield Smith-Baker
+				} else {
+					return dadPart; // Smith-Jones (P) and Smith-Baker (M) yield Smith-Jones
+				}
+			}
+			return {pat: dadPart.pat, mat: momPart.pat};
+		} else if (dadPart && dadPart.pat) {
+			// mother's surname didn't contain a patrilineal (primary) part, return both parts of the father's surname
+			return dadPart;
+		} else if (momPart && momPart.pat) {
+			// father's surname didn't contain a patrilineal (primary) part, return both parts of the mother's surname
+			return momPart;
+		} else if (V.surnameArcology && !nameOrNull(child.slaveSurname)) {
+			// neither surname contained a patrilineal (primary) part, but the player will grant an arcology surname
+			return {pat: V.surnameArcology, mat: null};
+		}
+	}
+
+	const norseGenderSuffix = (child.genes === "XY" && V.allowMaleSlaveNames) ? "son" : "dóttir";
+	const patronym = getPatronym();
+	const matronym = getMatronym();
+	const doubleSurname = makeDoubleSurname();
+
+	switch (V.surnameScheme) {
+		case 0: // family, patrilineal
+			if (V.PC.slaveSurname && child.mother === -1 && V.surnamePCOverride === 1) {
+				child.slaveSurname = V.PC.slaveSurname;
+			} else if (motherSurname && (!fatherSurname || (child.father === -1 && V.surnamePCOverride === 2))) {
+				child.slaveSurname = motherSurname;
+			} else if (fatherSurname) {
+				child.slaveSurname = fatherSurname;
+			} else if (V.surnameArcology && !nameOrNull(child.slaveSurname)) {
+				child.slaveSurname = V.surnameArcology;
+			}
+			break;
+		case 1: // family, matrilineal
+			if (V.PC.slaveSurname && child.father === -1 && V.surnamePCOverride === 1) {
+				child.slaveSurname = V.PC.slaveSurname;
+			} else if (fatherSurname && (!motherSurname || (child.mother === -1 && V.surnamePCOverride === 2))) {
+				child.slaveSurname = fatherSurname;
+			} else if (motherSurname) {
+				child.slaveSurname = motherSurname;
+			} else if (V.surnameArcology && !nameOrNull(child.slaveSurname)) {
+				child.slaveSurname = V.surnameArcology;
+			}
+			break;
+		case 2: // norse, patronym
+			if (patronym) {
+				let genitive;
+				if (patronym.endsWith("i")) {
+					genitive = patronym.slice(0, -1) + "a";
+				} else {
+					genitive = patronym + "s";
+				}
+				child.slaveSurname = genitive + norseGenderSuffix;
+			}
+			break;
+		case 3: // norse, matronym
+			if (matronym) {
+				let genitive;
+				if (matronym.endsWith("a")) {
+					genitive = matronym.slice(0, -1) + "u";
+				} else {
+					genitive = matronym + "ar";
+				}
+				child.slaveSurname = genitive + norseGenderSuffix;
 			}
+			break;
+		case 4: // simple patronym (Hadesha/Somali)
+			if (patronym) {
+				child.slaveSurname = patronym;
+			}
+			break;
+		case 5: // simple matronym (Hadesha/Somali)
+			if (matronym) {
+				child.slaveSurname = matronym;
+			}
+			break;
+		case 6: // double-barreled (patrilineal inheritance)
+		{
+			if (doubleSurname.pat) {
+				if (doubleSurname.mat) {
+					child.slaveSurname = doubleSurname.pat + "-" + doubleSurname.mat;
+				} else {
+					child.slaveSurname = doubleSurname.pat;
+				}
+			}
+			break;
+		}
+		case 7: // Hispanic (patrilineal inheritance, patrilineal family name first)
+		{
+			if (doubleSurname.pat) {
+				if (doubleSurname.mat) {
+					child.slaveSurname = doubleSurname.pat + " " + doubleSurname.mat;
+				} else {
+					child.slaveSurname = doubleSurname.pat;
+				}
+			}
+			break;
+		}
+		case 8: // Lusitanic (patrilineal inheritance, matrilineal family name first)
+		{
+			if (doubleSurname.pat) {
+				if (doubleSurname.mat) {
+					child.slaveSurname = doubleSurname.mat + " " + doubleSurname.pat;
+				} else {
+					child.slaveSurname = doubleSurname.pat;
+				}
+			}
+			break;
 		}
 	}
 }
diff --git a/src/npc/generate/slaveGenerationJS.js b/src/npc/generate/slaveGenerationJS.js
index 4ccb3e962b49e54b61665f1640257ba31c8a2292..dba524cf81ef38b9813cf28cfc1f47dc16ddc669 100644
--- a/src/npc/generate/slaveGenerationJS.js
+++ b/src/npc/generate/slaveGenerationJS.js
@@ -27,8 +27,8 @@ globalThis.raceToNationality = function(slave) {
 };
 
 /**
- * @param {string | object} nationality
- * @param {string | object} race
+ * @param {string} nationality
+ * @param {FC.Race} race
  * @param {boolean} male
  * @param {(name: string) => boolean} [filter] Default: allow all
  * @returns {string}
@@ -46,11 +46,11 @@ globalThis.generateName = function(nationality, race, male, filter = _.stubTrue)
 };
 
 /**
- * @param {string | number} nationality
- * @param {any} race
- * @param {any} male
+ * @param {string} nationality
+ * @param {FC.Race} race
+ * @param {boolean} male
  * @param {(name: string) => boolean} [filter] Default: allow all
- * @returns {string}
+ * @returns {FC.Zeroable<string>}
  */
 globalThis.generateSurname = function(nationality, race, male, filter = _.stubTrue) {
 	const result = jsEither(
@@ -69,7 +69,7 @@ globalThis.generateSurname = function(nationality, race, male, filter = _.stubTr
 
 /**
  * @param {string} name
- * @param {string | object} nationality
+ * @param {string} nationality
  * @param {any} race
  * @returns {boolean}
  */
@@ -84,10 +84,20 @@ globalThis.isMaleName = function(name, nationality, race) {
  * @param {App.Entity.SlaveState} slave
  */
 globalThis.nationalityToName = function(slave) {
+	function useDoubleSurname() {
+		const hispanic = ["Spanish", "Catalan", "Andorran", "Mexican", "Costa Rican", "Salvadoran", "Guatemalan", "Honduran", "Nicaraguan", "Panamanian", "Cuban", "Dominican", "Puerto Rican", "Argentinian", "Bolivian", "Chilean", "Columbian", "Ecuadorian", "Paraguayan", "Peruvian", "Uruguayan", "Venezuelan", "Equatoguinean", "Filipina"].includes(slave.nationality);
+		const lusitanic = ["Portuguese", "Brazilian", "Angolan", "Cape Verdean", "Bissau-Guinean", "Mozambican", "São Toméan", "East Timorese"].includes(slave.nationality);
+		// keep original hispanic/lusitanian double surname if the slave probably had one, AND if the arcology uses them by convention
+		// order doesn't matter for new slaves, just grab any two appropriate surnames
+		return (hispanic || lusitanic) && (V.surnameScheme === 7 || V.surnameScheme === 8);
+	}
 	const male = (slave.genes === "XY");
 
 	slave.birthName = generateName(slave.nationality, slave.race, male);
 	slave.birthSurname = generateSurname(slave.nationality, slave.race, male);
+	if (useDoubleSurname()) {
+		slave.birthSurname += " " + generateSurname(slave.nationality, slave.race, male);
+	}
 	if (male && isMaleName(slave.birthName, slave.nationality, slave.race) && !V.allowMaleSlaveNames) {
 		slave.slaveName = generateName(slave.nationality, slave.race, false);
 	} else {
diff --git a/src/uncategorized/universalRules.tw b/src/uncategorized/universalRules.tw
index 6f31a58656e7d9d74df10f5af109048108c27ee5..16e61278dfe3296043f85378850f77e68f2a2b26 100644
--- a/src/uncategorized/universalRules.tw
+++ b/src/uncategorized/universalRules.tw
@@ -511,12 +511,60 @@ Or design your own: <<textbox "$scarDesign.primary" $scarDesign.primary "Univers
 		<<replace "#strip">>
 			Surnames taken.
 		<</replace>>
+		<<replace "#surname-style">><</replace>>
 	<</link>>
 	</span>
 <<else>>
 	[[Allow future slaves to keep their surnames|Universal Rules][$surnamesForbidden = 0]]
 <</if>>
 
+<span id="surname-style">
+<<if $surnamesForbidden == 0>>
+	<<set _options = new App.UI.OptionsGroup()>>
+	<<run _options.addOption("Surname convention", "surnameScheme")
+		.addValue("Family (Patrilineal)", 0)
+		.addValue("Family (Matrilineal)", 1)
+		.addValue("Norse (Patronymic)", 2)
+		.addValue("Norse (Matronymic)", 3)
+		.addValue("Hadesha (Patronymic)", 4)
+		.addValue("Hadesha (Matronymic)", 5)
+		.addValue("Double-barreled", 6)
+		.addValue("Hispanic", 7)
+		.addValue("Lusitanic", 8)
+		.addComment((() => {
+			const start = "If Adam Smith and Betty Jones have a daughter, Charlotte, and a son, Daniel, they will be named ";
+			switch (V.surnameScheme) {
+				case 0:
+					return start + "Charlotte Smith and Daniel Smith";
+				case 1:
+					return start + "Charlotte Jones and Daniel Jones";
+				case 2:
+					return start + "Charlotte Adamsdóttir and Daniel Adamsson";
+				case 3:
+					return start + "Charlotte Bettysdóttir and Daniel Bettysson";
+				case 4:
+					return start + "Charlotte Adam and Daniel Adam";
+				case 5:
+					return start + "Charlotte Betty and Daniel Betty";
+				case 6:
+					return start + "Charlotte Smith-Jones and Daniel Smith-Jones";
+				case 7:
+					return start + "Charlotte Smith Jones and Daniel Smith Jones";
+				case 8:
+					return start + "Charlotte Jones Smith and Daniel Jones Smith";
+			}
+		})())>>
+	<<if [0, 1, 6, 7, 8].includes($surnameScheme)>>
+		<<run _options.addOption("Grant this family name to children born without one", "surnameArcology").showTextBox()>>
+	<</if>>
+	<<if [0, 1, 2, 3, 4, 5].includes($surnameScheme)>>
+		<<run _options.addOption("Override gender preference for my own children", "surnamePCOverride")
+			.addValue("Follow conventions", 0).on().addValue("Prefer using my name", 1).addValue("Avoid using my name", 2)>>
+	<</if>>
+	<<includeDOM _options.render()>>
+<</if>>
+</span>
+
 <br>
 
 Slave nicknames are