code:
App.UI.fsPassage = function() {
	const arc = V.arcologies[0];
	V.FSReminder = 0;
	const FSCredits = FutureSocieties.availCredits();
	setup();

	V.nextButton = "Back";
	V.nextLink = "Main";
	V.encyclopedia = "Future Societies";

	const f = new DocumentFragment();

	if (V.cheatMode) {
		App.UI.DOM.appendNewElement("div", f, App.UI.DOM.passageLink(
			"Cheat Edit Future Society",
			"MOD_Edit Arcology Cheat",
			() => {
				V.cheater = 1;
			}
		), "cheat-menu");
	}

	App.UI.DOM.appendNewElement("h1", f, "Future Societies");

	f.append(overview());
	f.append(FSPerception());
	f.append(unlocks());
	f.append(spending());
	f.append(rename());
	f.append(selectFS());

	App.UI.DOM.appendNewElement("h2", f, "Facility Redecoration");

	f.append(App.UI.facilityRedecoration());
	return f;

	/**
	 * FIRST FS STORING FOR RIVALRY
	 */
	function setup() {
		if (V.rival.FS.name === "") {
			if (arc.FSSubjugationist !== "unset") {
				V.rival.FS.name = "Racial Subjugationism";
				V.rival.FS.race = arc.FSSubjugationistRace;
			}
			if (arc.FSSupremacist !== "unset") {
				V.rival.FS.name = "Racial Supremacism";
				V.rival.FS.race = arc.FSSupremacistRace;
			}
			if (arc.FSGenderRadicalist !== "unset") {
				V.rival.FS.name = "Gender Radicalism";
			} else if (arc.FSGenderFundamentalist !== "unset") {
				V.rival.FS.name = "Gender Fundamentalism";
			}
			if (arc.FSRepopulationFocus !== "unset") {
				V.rival.FS.name = "Repopulation Focus";
			} else if (arc.FSRestart !== "unset") {
				V.rival.FS.name = "Eugenics";
			}
			if (arc.FSPaternalist !== "unset") {
				V.rival.FS.name = "Paternalism";
			} else if (arc.FSDegradationist !== "unset") {
				V.rival.FS.name = "Degradationism";
			}
			if (arc.FSBodyPurist !== "unset") {
				V.rival.FS.name = "Body Purism";
			} else if (arc.FSTransformationFetishist !== "unset") {
				V.rival.FS.name = "Transformation Fetishism";
			}
			if (arc.FSYouthPreferentialist !== "unset") {
				V.rival.FS.name = "Youth Preferentialism";
			} else if (arc.FSMaturityPreferentialist !== "unset") {
				V.rival.FS.name = "Maturity Preferentialism";
			}
			if (arc.FSSlimnessEnthusiast !== "unset") {
				V.rival.FS.name = "Slimness Enthusiasm";
			} else if (arc.FSAssetExpansionist !== "unset") {
				V.rival.FS.name = "Asset Expansionism";
			}
			if (arc.FSPastoralist !== "unset") {
				V.rival.FS.name = "Pastoralism";
			} else if (arc.FSCummunism !== "unset") {
				V.rival.FS.name = "Cummunism";
			}
			if (arc.FSHedonisticDecadence !== "unset") {
				V.rival.FS.name = "Hedonistic Decadence";
			} else if (arc.FSPhysicalIdealist !== "unset") {
				V.rival.FS.name = "Physical Idealism";
			}
			if (arc.FSIntellectualDependency !== "unset") {
				V.rival.FS.name = "Intellectual Dependency";
			} else if (arc.FSSlaveProfessionalism !== "unset") {
				V.rival.FS.name = "Slave Professionalism";
			}
			if (arc.FSPetiteAdmiration !== "unset") {
				V.rival.FS.name = "Petite Admiration";
			} else if (arc.FSStatuesqueGlorification !== "unset") {
				V.rival.FS.name = "Statuesque Glorification";
			}
			if (arc.FSChattelReligionist !== "unset") {
				V.rival.FS.name = "Chattel Religionism";
			} else if (arc.FSNull !== "unset") {
				V.rival.FS.name = "Multiculturalism";
			}
			if (arc.FSRomanRevivalist !== "unset") {
				V.rival.FS.name = "Roman Revivalism";
			} else if (arc.FSNeoImperialist !== "unset") {
				V.rival.FS.name = "Neo-Imperialism";
			} else if (arc.FSAztecRevivalist !== "unset") {
				V.rival.FS.name = "Aztec Revivalism";
			} else if (arc.FSEgyptianRevivalist !== "unset") {
				V.rival.FS.name = "Egyptian Revivalism";
			} else if (arc.FSEdoRevivalist !== "unset") {
				V.rival.FS.name = "Edo Revivalism";
			} else if (arc.FSArabianRevivalist !== "unset") {
				V.rival.FS.name = "Arabian Revivalism";
			} else if (arc.FSChineseRevivalist !== "unset") {
				V.rival.FS.name = "Chinese Revivalism";
			}
		}
	}

	function overview() {
		const el = new DocumentFragment();
		const r = [];
		if (FSCredits > 0) {
			r.push(App.UI.DOM.makeElement("span", `${arc.name}'s society is ready to begin accepting a new societal direction.`, "yellow"));
		} else if (V.FSGotRepCredits >= 3) {
			r.push(`Your society is so radically changed that it is starting to take on a life of its own. The major decisions about its direction have been made.`);
		} else {
			r.push(`You must develop your reputation further for ${arc.name}'s society to be ready for a new societal direction.`);
		}
		App.Events.addNode(el, r, "div");
		return el;
	}

	function FSPerception() {
		const el = new DocumentFragment();
		for (const FS of App.Data.FutureSociety.playerFSNames) {
			if (arc[FS] !== "unset") {
				App.UI.DOM.appendNewElement("div", el, FutureSocieties.arcSupport(FS, arc));
			}
		}
		return el;
	}

	function unlocks() {
		const el = new DocumentFragment();
		const r = [];
		r.push(`You have unlocked`);
		r.push(App.UI.DOM.makeElement("span", `${num(V.FSGotRepCredits, true)} of ${num(V.FSCreditCount, true)}`, "note"));
		r.push(`possible societal customizations.`);
		App.Events.addNode(el, r, "div");
		return el;
	}

	function spending() {
		const el = new DocumentFragment();
		let r = [];
		V.FSSpending = Number(V.FSSpending) || 0;
		V.FSSpending = Math.clamp(Math.ceil(V.FSSpending / 1000) * 1000, 0, 10000);
		r.push(`You are spending ¤`);
		r.push(App.UI.DOM.makeTextBox(
			V.FSSpending,
			(v) => { V.FSSpending = v; },
			true
		));
		r.push(`each week to support your societal goals.`);
		App.Events.addNode(el, r, "div");
		if (V.FSSpending > 10000) {
			App.UI.DOM.appendNewElement("div", el, `Spending more than ${cashFormat(10000)} weekly is a waste`, "note");
		}
		return el;
	}

	function rename() {
		const el = document.createElement('span');
		el.id = "mass";
		const linkArray = [];

		if (arc.FSChattelReligionist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Give all your slaves devotional names",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.chattelReligionistSlaveNames.random();
						}
						refresh();
					}
				)
			);
		}

		if (arc.FSPastoralist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Give all your lactating slaves cow names",
					() => {
						for (const slave of V.slaves) {
							if (slave.lactation) {
								slave.slaveName = App.Data.misc.cowSlaveNames.random();
							}
						}
						refresh();
					}
				)
			);
		}

		if (arc.FSIntellectualDependency !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Give all your idiotic slaves stripper names",
					() => {
						for (const slave of V.slaves) {
							if (slave.intelligence < -15) {
								slave.slaveName = App.Data.misc.bimboSlaveNames.random();
							}
						}
						refresh();
					}
				)
			);
		}

		if (arc.FSRomanRevivalist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename all your slaves according to Roman custom",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.romanSlaveNames.random();
							slave.slaveSurname = App.Data.misc.romanSlaveSurnames.random();
						}
						refresh();
					}
				)
			);
		} else if (arc.FSAztecRevivalist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename all your slaves according to ancient Aztec custom",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.aztecSlaveNames.random();
							slave.slaveSurname = 0;
						}
						refresh();
					}
				)
			);
		} else if (arc.FSEgyptianRevivalist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename all your slaves according to ancient Egyptian custom",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.ancientEgyptianSlaveNames.random();
							slave.slaveSurname = 0;
						}
						refresh();
					}
				)
			);
		} else if (arc.FSEdoRevivalist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename all your slaves according to feudal Japanese custom",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.edoSlaveNames.random();
							slave.slaveSurname = App.Data.misc.edoSlaveSurnames.random();
						}
						refresh();
					}
				)
			);
		}
		if (arc.FSDegradationist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename all your slaves according to Degradationist custom",
					() => {
						for (const slave of V.slaves) {
							DegradingName(slave);
						}
						refresh();
					}
				)
			);
		}

		if (arc.FSPaternalist !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Rename your obedient slaves according to Paternalist custom",
					() => {
						for (const slave of V.slaves) {
							if (slave.devotion > 20 || (slave.devotion >= -20 && slave.trust < -20)) {
								if (!["Miss", "Ms.", "Mrs."].some(title => slave.slaveName.includes(title))) {
									if (slave.relationship > 4) {
										slave.slaveName = ("Mrs. " + slave.slaveName);
									} else if (slave.actualAge > 24) {
										slave.slaveName = ("Ms. " + slave.slaveName);
									} else {
										slave.slaveName = ("Miss " + slave.slaveName);
									}
								}
							}
						}
						refresh("Obedient slaves renamed.");
					}
				)
			);
		}

		if (arc.FSIntellectualDependency !== "unset") {
			linkArray.push(
				App.UI.DOM.link(
					"Give all your slaves simple bimbo names",
					() => {
						for (const slave of V.slaves) {
							slave.slaveName = App.Data.misc.bimboSlaveNames.random();
							slave.slaveSurname = 0;
						}
						refresh();
					}
				)
			);
		}

		if (linkArray.length > 0) {
			App.UI.DOM.appendNewElement("h3", el, "Names");
			el.append(App.UI.DOM.generateLinksStrip(linkArray));
		}

		return el;

		function refresh(text = "Slaves renamed.") {
			jQuery("#mass").empty().append(text);
		}
	}

	/** Test what new social effects you'd get if a new FS were added
	 *  This is a good proxy for evaluating whether you'll be able to make it stick or not
	 * @param {FC.FutureSociety} proposedFS
	 */
	function evaluation(proposedFS) {
		const effectCounts = new Map();
		for (const slave of App.SlaveAssignment.reportSlaves(V.slaves)) {
			const slaveEffects = App.SlaveAssignment.saSocialEffects(slave).newForFS(proposedFS);
			for (const effect of slaveEffects) {
				const curVal = effectCounts.get(effect.shortDesc);
				if (curVal) {
					effectCounts.set(effect.shortDesc, curVal + effect.magnitude);
				} else {
					effectCounts.set(effect.shortDesc, effect.magnitude);
				}
			}
		}

		const grid = document.createElement("div");
		grid.classList.add("grid-2columns-auto");
		let avg = 0;
		for (const [key, count] of effectCounts) {
			const className = count < 0 ? "red" : "green";
			App.UI.DOM.appendNewElement("div", grid, count.toString(), [className]);
			App.UI.DOM.appendNewElement("div", grid, key);
			avg += count;
		}
		avg /= V.slaves.length;

		const tip = document.createElement('div');
		tip.classList.add("tip-details");
		if (avg > 1.5) {
			tip.appendChild(document.createTextNode(`Adopting ${FutureSocieties.displayName(proposedFS)} is likely to be straightforward, even against social pressure from your neighbors. Your arcology is already primed to move this direction thanks to your actions, and your slaves are well-aligned with it.`));
		} else if (avg > 0.5) {
			tip.appendChild(document.createTextNode(`Adopting ${FutureSocieties.displayName(proposedFS)} should be relatively painless. Your arcology is already receptive to moving this direction thanks to your actions, and your slaves are aligned with it.`));
		} else if (avg > -0.1) {
			tip.appendChild(document.createTextNode(`Adopting ${FutureSocieties.displayName(proposedFS)} will require investment and effort. Your arcology is not opposed to moving this direction, but it's also not expecting it based on your past actions. To improve your chances, consider first aligning your slaves with ${FutureSocieties.displayAdj(proposedFS)} goals.`));
		} else {
			tip.appendChild(document.createTextNode(`Attempting to adopt ${FutureSocieties.displayName(proposedFS)} with your arcology in its current state will likely result in failure. You should strongly consider aligning your slaves with ${FutureSocieties.displayAdj(proposedFS)} goals before endorsing it.`));
		}
		tip.append(grid);

		const span = document.createElement("span");
		span.classList.add("fs-recommend");
		if (avg > 1.5) {
			span.textContent = "Primed";
			span.classList.add("fs-recommend-great");
		} else if (avg > 0.5) {
			span.textContent = "Receptive";
			span.classList.add("fs-recommend-good");
		} else if (avg > -0.1) {
			span.textContent = "Neutral";
			span.classList.add("fs-recommend-neutral");
		} else {
			span.textContent = "Resistant";
			span.classList.add("fs-recommend-bad");
		}
		span.tabIndex = 0;
		span.classList.add("has-tooltip");
		tippy(span, {
			content: tip,
			placement: "right",
		});
		return span;
	}

	function selectFS() {
		const el = new DocumentFragment();
		let r;
		let p;

		/* Race */
		p = document.createElement("p");
		r = [];
		if (arc.FSSupremacist !== "unset") {
			r.push(pursuit());
			r.push(`${arc.FSSupremacistRace} superiority.`);
			r.push(activeFS("FSSupremacist"));
		} else {
			if (FSCredits > 0) {
				r.push(
					App.UI.DOM.link(
						"Racial Supremacism",
						() => {
							arc.FSSupremacist = 4;
							App.UI.reload();
						}
					)
				);
				r.push(`is a belief in`);
				if (arc.FSSupremacistRace === 0) {
					r.push(`the superiority of a chosen race.`);
				} else {
					r.push(`${arc.FSSupremacistRace} superiority.`);
				}
				r.push(`Select race:`);
				const options = [];
				for (const race of App.Utils.getRaceArrayWithoutParamRace(arc.FSSubjugationistRace)) { // Subjugation race cannot be superior, so remove
					options.push({key: race, name: capFirstChar(race)});
				}
				r.push(App.UI.DOM.makeSelect(options, arc.FSSupremacistRace, race => {
					arc.FSSupremacistRace = /** @type {FC.Race} */ (race);
					App.UI.reload();
				}));
				r.push(evaluation("FSSupremacist"));
			} else {
				/* Racial Supremacism: a belief in the superiority of a chosen race.*/
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSSubjugationist !== "unset") {
			r.push(pursuit());
			r.push(`${arc.FSSubjugationistRace} inferiority.`);
			r.push(activeFS("FSSubjugationist"));
		} else {
			if (FSCredits > 0) {
				r.push(
					App.UI.DOM.link(
						"Racial Subjugationism",
						() => {
							arc.FSSubjugationist = 4;
							App.UI.reload();
						}
					)
				);
				r.push(`is a belief in`);
				if (arc.FSSubjugationistRace === 0) {
					r.push(`the inferiority of a chosen race.`);
				} else {
					r.push(`${arc.FSSubjugationistRace} inferiority.`);
				}
				r.push(`Select race:`);
				const options = [];
				for (const race of App.Utils.getRaceArrayWithoutParamRace(arc.FSSupremacistRace)) { // Superior race cannot be subj, so remove
					options.push({key: race, name: capFirstChar(race)});
				}
				r.push(App.UI.DOM.makeSelect(options, arc.FSSubjugationistRace, race => {
					arc.FSSubjugationistRace = /** @type {FC.Race} */ (race);
					App.UI.reload();
				}));
				r.push(evaluation("FSSubjugationist"));
			} else {
				/* Racial Subjugationism: a belief in the inferiority of a subject race.*/
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Preg vs Eugenics */
		if (V.seePreg !== 0) {
			p = document.createElement("p");
			r = [];
			if (arc.FSRestart === "unset") {
				if (arc.FSRepopulationFocus !== "unset") {
					r.push(pursuit());
					r.push(`the belief that mass breeding will save humanity.`);
					r.push(activeFS("FSRepopulationFocus", ["boughtItem.clothing.maternityLingerie", "boughtItem.clothing.maternityDress", "boughtItem.clothing.belly"]));
				} else {
					if (FSCredits > 0) {
						r.push(
							App.UI.DOM.link(
								"Repopulation Efforts",
								() => {
									arc.FSRepopulationFocus = 4 + arc.FSRepopulationFocusInterest - arc.FSEugenicsInterest;
									arc.FSRepopulationFocusPregPolicy = 0;
									arc.FSRepopulationFocusMilfPolicy = 0;
									App.UI.reload();
								}
							)
						);
						r.push(`is a focus on mass breeding in order to repopulate the future world.`);
						r.push(evaluation("FSRepopulationFocus"));
					} else {
						/* Repopulation Efforts: societal fetishization of pregnancy.*/
					}
				}
			}
			App.Events.addNode(p, r, "div");
			r = [];
			if (arc.FSRepopulationFocus === "unset") {
				if (arc.FSRestart !== "unset") {
					if (arc.FSRestartDecoration !== 100) {
						r.push(pursuit());
						r.push(`Eugenics.`);
						r.push(activeFS("FSRestart"));
					} else {
						r.push(App.UI.DOM.makeElement("span", "You have established", "bold"));
						r.push(`Eugenics.`);
						r.push(activeFS("FSRestart"));
						r.push(`The Societal Elite exist as the highest class, allowing you access to all manner of benefits.`);
					}
				} else {
					if (FSCredits > 0) {
						r.push(
							App.UI.DOM.link(
								"Eugenics",
								() => {
									arc.FSRestart = 4 + arc.FSEugenicsInterest - arc.FSRepopulationFocusInterest;
									arc.FSRepopulationFocusPregPolicy = 0;
									arc.FSRepopulationFocusMilfPolicy = 0;
									App.UI.reload();
								}
							)
						);
						r.push(`is rebuilding society using restrictive breeding programs reserved solely for society's finest.`);
						r.push(evaluation("FSRestart"));
					} else {
						/* Complete Societal Reconstruction: rebuilding society based off the elite.*/
					}
				}
			}
			App.Events.addNode(p, r, "div");
			el.append(p);
		}

		/* Pro vs Dependant */
		p = document.createElement("p");
		r = [];
		if (arc.FSSlaveProfessionalism === "unset") {
			if (arc.FSIntellectualDependency !== "unset") {
				r.push(pursuit());
				r.push(`intellectual dependency, a belief that slaves should be airheaded, horny and fully dependent on their owners.`);
				r.push(activeFS("FSIntellectualDependency", ["boughtItem.clothing.bimbo"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Intellectual Dependency",
							() => {
								arc.FSIntellectualDependency = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a belief that slaves should be airheaded, horny and fully dependent on their owners.`);
					r.push(evaluation("FSIntellectualDependency"));
				} else {
					/* Intellectual Dependency: a belief that slaves should be airheaded, horny and fully dependent on their owners.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSIntellectualDependency === "unset") {
			if (arc.FSSlaveProfessionalism !== "unset") {
				r.push(pursuit());
				r.push(`slave professionalism, a focus on smart, refined, altogether perfect slaves.`);
				r.push(activeFS("FSSlaveProfessionalism", ["boughtItem.clothing.courtesan"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Slave Professionalism",
							() => {
								arc.FSSlaveProfessionalism = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is increased interest in smart, refined, altogether perfect slaves.`);
					r.push(evaluation("FSSlaveProfessionalism"));
				} else {
					/* Slave Professionalism: increased interest in smart, refined, altogether perfect slaves.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Gender */
		p = document.createElement("p");
		r = [];
		if (V.seeDicks !== 0 || V.makeDicks === 1) {
			if (arc.FSGenderFundamentalist === "unset") {
				if (arc.FSGenderRadicalist !== "unset") {
					r.push(pursuit());
					r.push(`a radical redefinition of gender that identifies powerful people as male, and everyone else as female.`);
					r.push(activeFS("FSGenderRadicalist"));
				} else {
					if (FSCredits > 0) {
						r.push(
							App.UI.DOM.link(
								"Gender radicalism",
								() => {
									arc.FSGenderRadicalist = 4;
									App.UI.reload();
								}
							)
						);
						r.push(`is a radical redefinition of gender that identifies powerful people as male, and everyone else as female.`);
						r.push(evaluation("FSGenderRadicalist"));
					} else {
						/* Gender radicalism: a radical redefinition of gender that identifies powerful people as male, and everyone else as female.*/
					}
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSGenderRadicalist === "unset") {
			if (arc.FSGenderFundamentalist !== "unset") {
				r.push(pursuit());
				r.push(`gender traditionalism, including a societal preference for feminine slaves ${(V.seePreg !== 0) ? ` and support for slave pregnancy` : ``}.`);
				r.push(activeFS("FSGenderFundamentalist", ["boughtItem.clothing.bunny"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Gender traditionalism",
							() => {
								arc.FSGenderFundamentalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`gender traditionalism, including a societal preference for feminine slaves ${(V.seePreg !== 0) ? ` and support for slave pregnancy` : ``}.`);
					r.push(evaluation("FSGenderFundamentalist"));
				} else {
					/* Gender traditionalism: a societal preference for feminine slaves
						if (V.seePreg !== 0) {
							r.push(`and support for slave pregnancy`);
						}
						r.push(`.</span>`);*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Deg vs Paternalist */
		p = document.createElement("p");
		r = [];
		if (arc.FSDegradationist === "unset") {
			if (arc.FSPaternalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of slave improvement, including slaves' health, mental well-being, and education.`);
				r.push(activeFS("FSPaternalist", ["boughtItem.clothing.conservative"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Paternalism",
							() => {
								arc.FSPaternalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of slave improvement, including slaves' health, mental well-being, and education.`);
					r.push(evaluation("FSPaternalist"));
				} else {
					/* Paternalism: a vision of slave improvement, including slaves' health, mental well-being, and education.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSPaternalist === "unset") {
			if (arc.FSDegradationist !== "unset") {
				r.push(pursuit());
				r.push(`slave degradation, a belief that slaves are not human and should not be treated decently.`);
				r.push(activeFS("FSDegradationist", ["boughtItem.clothing.chains"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Degradationism",
							() => {
								arc.FSDegradationist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a belief that slaves are not human and should not be treated decently.`);
					r.push(evaluation("FSDegradationist"));
				} else {
					/* Degradation: a belief that slaves are not human and should not be treated decently.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* TF vs Purist */
		p = document.createElement("p");
		r = [];
		if (arc.FSTransformationFetishist === "unset") {
			if (arc.FSBodyPurist !== "unset") {
				r.push(pursuit());
				r.push(`societal disapproval of implant surgery.`);
				r.push(activeFS("FSBodyPurist"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Body Purism",
							() => {
								arc.FSBodyPurist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal disapproval of implant surgery.`);
					r.push(evaluation("FSBodyPurist"));
				} else {
					/* Body Purism: societal disapproval of implant surgery.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSBodyPurist === "unset") {
			if (arc.FSTransformationFetishist !== "unset") {
				r.push(pursuit());
				r.push(`societal fetishization of implant surgery.`);
				r.push(activeFS("FSTransformationFetishist"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Transformation Fetishism",
							() => {
								arc.FSTransformationFetishist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal fetishization of implant surgery.`);
					r.push(evaluation("FSTransformationFetishist"));
				} else {
					/* Transformation Fetishism: societal fetishization of implant surgery.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Maturity vs Youth */
		p = document.createElement("p");
		r = [];
		if (arc.FSMaturityPreferentialist === "unset") {
			if (arc.FSYouthPreferentialist !== "unset") {
				r.push(pursuit());
				r.push(`an accentuated societal preference for younger slaves.`);
				r.push(activeFS("FSYouthPreferentialist"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Youth Preferentialism",
							() => {
								arc.FSYouthPreferentialist = 4;
								if (V.idealAge >= 30) {
									V.idealAge = 29;
								}
								if (V.targetIdealAge >= 30) {
									V.targetIdealAge = 29;
								}
								if (V.targetIdealAge !== 18) {
									V.policies.idealAge = 1;
								}
								App.UI.reload();
							}
						)
					);
					r.push(`is increased interest in girls just past their majority.`);
					r.push(evaluation("FSYouthPreferentialist"));
				} else {
					/* Youth Preferentialism: increased interest in girls just past their majority.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSYouthPreferentialist === "unset") {
			if (arc.FSMaturityPreferentialist !== "unset") {
				r.push(pursuit());
				r.push(`a societal preference for older women.`);
				r.push(activeFS("FSMaturityPreferentialist"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Maturity Preferentialism",
							() => {
								arc.FSMaturityPreferentialist = 4;
								if (V.idealAge < 30) {
									V.idealAge = 30;
								}
								if (V.targetIdealAge < 30) {
									V.targetIdealAge = 30;
								}
								V.policies.idealAge = 1;
								App.UI.reload();
							}
						)
					);
					r.push(`is increased interest in mature slaves.`);
					r.push(evaluation("FSMaturityPreferentialist"));
				} else {
					/* Maturity Preferentialism: increased interest in mature slaves.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Statuesque vs Petite */
		p = document.createElement("p");
		r = [];
		if (arc.FSStatuesqueGlorification === "unset") {
			if (arc.FSPetiteAdmiration !== "unset") {
				r.push(pursuit());
				r.push(`an accentuated societal preference for short slaves.`);
				r.push(activeFS("FSPetiteAdmiration"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Petite Admiration",
							() => {
								arc.FSPetiteAdmiration = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is increased interest in short slaves.`);
					r.push(evaluation("FSPetiteAdmiration"));
				} else {
					/* Petite Admiration: increased interest in short slaves.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSPetiteAdmiration === "unset") {
			if (arc.FSStatuesqueGlorification !== "unset") {
				r.push(pursuit());
				r.push(`a societal fixation on tallness.`);
				r.push(activeFS("FSStatuesqueGlorification"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Statuesque Glorification",
							() => {
								arc.FSStatuesqueGlorification = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal fixation on tallness.`);
					r.push(evaluation("FSStatuesqueGlorification"));
				} else {
					/* Statuesque Glorification: societal fixation on tallness.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Expand vs Slim */
		p = document.createElement("p");
		r = [];
		if (arc.FSAssetExpansionist === "unset") {
			if (arc.FSSlimnessEnthusiast !== "unset") {
				r.push(App.UI.DOM.makeElement("span", "You are supporting", "bold"));
				r.push(`enthusiasm for slaves with girlish figures.`);
				r.push(activeFS("FSSlimnessEnthusiast"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Slimness Enthusiasm",
							() => {
								arc.FSSlimnessEnthusiast = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a fashion for slaves with girlish figures.`);
					r.push(evaluation("FSSlimnessEnthusiast"));
				} else {
					/* Slimness Enthusiasm: a fashion for slaves with girlish figures.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSSlimnessEnthusiast === "unset") {
			if (arc.FSAssetExpansionist !== "unset") {
				r.push(pursuit());
				r.push(`societal hunger for huge assets.`);
				r.push(activeFS("FSAssetExpansionist"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Asset Expansionism",
							() => {
								arc.FSAssetExpansionist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal hunger for huge assets of whatever origin.`);
					r.push(evaluation("FSAssetExpansionist"));
				} else {
					/* Asset Expansionism: societal hunger for huge assets of whatever origin.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Milking stands alone */
		r = [];
		if (arc.FSPastoralist !== "unset") {
			r.push(pursuit());
			r.push(`societal normalization of slave milking.`);
			r.push(activeFS("FSPastoralist", ["boughtItem.clothing.western"]));
		} else {
			if (FSCredits > 0) {
				r.push(
					App.UI.DOM.link(
						"Slave Pastoralism",
						() => {
							arc.FSPastoralist = 4;
							App.UI.reload();
						}
					)
				);
				r.push(`is societal acceptance of slave products like milk.`);
				r.push(evaluation("FSPastoralist"));
			} else {
				/* Slave Pastoralism: societal acceptance of slave products like milk.*/
			}
		}
		App.Events.addNode(el, r, "p");

		/* Physical Idealist vs Hedonist */
		p = document.createElement("p");
		r = [];
		if (arc.FSHedonisticDecadence === "unset") {
			if (arc.FSPhysicalIdealist !== "unset") {
				r.push(pursuit());
				r.push(`societal reverence for the idealized human form, including height, health and muscle.`);
				r.push(activeFS("FSPhysicalIdealist", ["boughtItem.clothing.oil"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Physical Idealism",
							() => {
								arc.FSPhysicalIdealist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal reverence for the idealized human form, including height, health and muscle.`);
					r.push(evaluation("FSPhysicalIdealist"));
				} else {
					/* Physical Idealism: societal reverence for the idealized human form, including height, health and muscle.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSPhysicalIdealist === "unset") {
			if (arc.FSHedonisticDecadence !== "unset") {
				r.push(pursuit());
				r.push(`societal normalization of overindulgence and immediate gratification. Be it food, drink, sex, drugs or whatever one's desire may be.`);
				r.push(activeFS("FSHedonisticDecadence"));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Hedonistic Decadence",
							() => {
								arc.FSHedonisticDecadence = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is societal acceptance of overindulgence and immediate gratification. Be it food, drink, sex, drugs or whatever one's desire may be.`);
					r.push(evaluation("FSHedonisticDecadence"));
				} else {
					/* HedonisticDecadence: societal acceptance of over indulgence, particularly of food, drink, sex and drugs.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		/* Chattel religionist vs Multicultural */
		p = document.createElement("p");
		r = [];
		if (arc.FSNull === "unset") {
			if (arc.FSChattelReligionist !== "unset") {
				r.push(pursuit());
				r.push(`a new strain of religion that emphasizes the slaveholding portions of religious history.`);
				r.push(activeFS("FSChattelReligionist", ["boughtItem.clothing.habit"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Chattel Religionism",
							() => {
								arc.FSChattelReligionist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a new strain of religion that emphasizes the slaveholding portions of religious history.`);
					r.push(evaluation("FSChattelReligionist"));
				} else {
					/* Chattel Religionism: a new strain of religion that emphasizes the slaveholding portions of religious history.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if (arc.FSChattelReligionist === "unset") {
			if (arc.FSNull !== "unset") {
				const freedomArray = [`You are permitting`];
				if (arc.FSNull <= 25) {
					freedomArray.push(`basic`);
				} else if (arc.FSNull <= 50) {
					freedomArray.push(`considerable`);
				} else if (arc.FSNull <= 75) {
					freedomArray.push(`advanced`);
				} else {
					freedomArray.push(`absolute`);
				}
				r.push(App.UI.DOM.makeElement("span", freedomArray.join(" "), "bold"));
				r.push(`cultural freedom in your arcology.`);
			}
			if (V.FSCreditCount === 4) {
				if (arc.FSNull !== "unset") {
					r.push(activeFS("FSChattelReligionist"));
					if (arc.FSNull <= 25) {
						r.push(abandonRel());
					} else {
						r.push(withdrawRel(25));
					}
					if (FSCredits > 0) {
						if (arc.FSNull < V.FSLockinLevel) {
							r.push(advanceRel(25));
						}
					}
				} else {
					if (FSCredits > 0) {
						r.push(MulticulturalismRel(25));
					}
				}
			} else if (V.FSCreditCount === 6) {
				if (arc.FSNull !== "unset") {
					if (arc.FSNull <= 20) {
						r.push(abandonRel());
					} else {
						r.push(withdrawRel(17));
					}
					if (FSCredits > 0) {
						if (arc.FSNull < V.FSLockinLevel) {
							r.push(advanceRel(17));
						}
					}
				} else {
					if (FSCredits > 0) {
						r.push(MulticulturalismRel(17));
					}
				}
			} else if (V.FSCreditCount === 7) {
				if (arc.FSNull !== "unset") {
					if (arc.FSNull <= 20) {
						r.push(abandonRel());
					} else {
						r.push(withdrawRel(15));
					}
					if (FSCredits > 0) {
						if (arc.FSNull < V.FSLockinLevel) {
							r.push(advanceRel(15));
						}
					}
				} else {
					if (FSCredits > 0) {
						r.push(MulticulturalismRel(15));
					}
				}
			} else {
				if (arc.FSNull !== "unset") {
					if (arc.FSNull <= 20) {
						r.push(abandonRel());
					} else {
						r.push(withdrawRel(20));
					}
					if (FSCredits > 0) {
						if (arc.FSNull < V.FSLockinLevel) {
							r.push(advanceRel(20));
						}
					}
				} else {
					if (FSCredits > 0) {
						r.push(MulticulturalismRel(20));
					}
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		function withdrawRel(num) {
			return App.UI.DOM.link(
				"Withdraw",
				() => {
					if (arc.FSNull !== "unset") {
						arc.FSNull -= num;
					}
					App.UI.reload();
				}
			);
		}

		function abandonRel() {
			return App.UI.DOM.link(
				"Abandon",
				() => {
					FutureSocieties.remove("FSNull");
					App.UI.reload();
				}
			);
		}

		function advanceRel(num) {
			return App.UI.DOM.link(
				"Advance",
				() => {
					if (arc.FSNull !== "unset") {
						arc.FSNull += num;
					}
					App.UI.reload();
				},
				[],
				"",
				"a further commitment to allow your arcology's citizens cultural freedom"
			);
		}

		function MulticulturalismRel(num) {
			const multicultural = App.UI.DOM.makeElement(
				"span",
				App.UI.DOM.link(
					"Multiculturalism",
					() => {
						arc.FSNull = num;
						App.UI.reload();
					},
					[],
					"",
					"a commitment to allow your arcology's citizens cultural freedom."
				)
			);
			multicultural.append(` is an alternative to societal advancement, and will not advance naturally.`);
			return multicultural;
		}

		/* Revival section */
		p = document.createElement("p");
		r = [];
		if ((arc.FSAztecRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSRomanRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of a new Rome.`);
				r.push(activeFS("FSRomanRevivalist", ["boughtItem.clothing.toga"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Roman Revivalism",
							() => {
								arc.FSRomanRevivalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of a new Rome.`);
					r.push(evaluation("FSRomanRevivalist"));
				} else {
					/* Roman Revivalism: a vision of a new Rome.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSAztecRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSRomanRevivalist === "unset")) {
			if (arc.FSNeoImperialist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of a new Imperial Society.`);
				r.push(activeFS("FSNeoImperialist", ["boughtItem.clothing.imperialarmor", "boughtItem.clothing.imperialsuit"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Neo-Imperialism",
							() => {
								arc.FSNeoImperialist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of a new Imperial society, integrating high technology and old-world culture under the iron fist of your absolute rule.`);
					r.push(evaluation("FSNeoImperialist"));
				} else {
					/* Neo-Imperialism: a vision of a new Imperial Society, integrating high technology and old-world culture under the iron fist of your absolute rule.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSRomanRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSAztecRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of a new Aztec Empire.`);
				r.push(activeFS("FSAztecRevivalist", ["boughtItem.clothing.huipil"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Aztec Revivalism",
							() => {
								arc.FSAztecRevivalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of a new Aztec Empire.`);
					r.push(evaluation("FSAztecRevivalist"));
				} else {
					/* Aztec Revivalism: a vision of a new Aztec Empire.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSRomanRevivalist === "unset") && (arc.FSAztecRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSEgyptianRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of Pharaoh's Egypt.`);
				r.push(activeFS("FSEgyptianRevivalist", ["boughtItem.clothing.egypt"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Egyptian Revivalism",
							() => {
								arc.FSEgyptianRevivalist = 4 + arc.FSEgyptianRevivalistInterest;
								arc.FSEgyptianRevivalistIncestPolicy = 0;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of a Pharaoh's Egypt.`);
					r.push(evaluation("FSEgyptianRevivalist"));
				} else {
					/* Egyptian Revivalism: a vision of Pharaoh's Egypt.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSRomanRevivalist === "unset") && (arc.FSAztecRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSEdoRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of Edo Japan.`);
				r.push(activeFS("FSEdoRevivalist", ["boughtItem.clothing.kimono"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Edo Revivalism",
							() => {
								arc.FSEdoRevivalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of Edo Japan.`);
					r.push(evaluation("FSEdoRevivalist"));
				} else {
					/* Edo Revivalism: a vision of Edo Japan.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSRomanRevivalist === "unset") && (arc.FSAztecRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSChineseRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSArabianRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of the Sultanate of old.`);
				r.push(activeFS("FSArabianRevivalist", ["boughtItem.clothing.harem"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Arabian Revivalism",
							() => {
								arc.FSArabianRevivalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of the Sultanate of old.`);
					r.push(evaluation("FSArabianRevivalist"));
				} else {
					/* Arabian Revivalism: a vision of the Sultanate of old.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		r = [];
		if ((arc.FSRomanRevivalist === "unset") && (arc.FSAztecRevivalist === "unset") && (arc.FSEgyptianRevivalist === "unset") && (arc.FSEdoRevivalist === "unset") && (arc.FSArabianRevivalist === "unset") && (arc.FSNeoImperialist === "unset")) {
			if (arc.FSChineseRevivalist !== "unset") {
				r.push(pursuit());
				r.push(`a vision of ancient China.`);
				r.push(activeFS("FSChineseRevivalist", ["boughtItem.clothing.qipao"]));
			} else {
				if (FSCredits > 0) {
					r.push(
						App.UI.DOM.link(
							"Chinese Revivalism",
							() => {
								arc.FSChineseRevivalist = 4;
								App.UI.reload();
							}
						)
					);
					r.push(`is a vision of ancient China.`);
					r.push(evaluation("FSChineseRevivalist"));
				} else {
					/* Chinese Revivalism: a vision of ancient China.*/
				}
			}
		}
		App.Events.addNode(p, r, "div");
		el.append(p);

		return el;

		function pursuit() {
			return App.UI.DOM.makeElement("span", "You are pursuing", "bold");
		}

		/**
		 *
		 * @param {FC.FutureSociety} FS
		 * @param {Array} [itemArray]
		 */
		function activeFS(FS, itemArray) {
			const el = new DocumentFragment();
			const r = [];

			// Abandon
			if (FS === "FSRestart" && V.eugenicsFullControl !== 1) {
				r.push(`The Societal Elite will not permit you to abandon Eugenics.`);
			} else {
				r.push(
					App.UI.DOM.link(
						"Abandon",
						() => {
							FutureSocieties.remove(FS);
							App.UI.reload();
						}
					)
				);
			}

			// Decoration
			r.push(App.UI.FSChangeDecoration(FS, itemArray));

			// Assistant
			if (V.policies.publicPA === 1) {
				if (App.Data.Assistant.appearanceForFS.get(FS).includes(V.assistant.appearance)) {
					const {hisA} = getPronouns(assistant.pronouns().main).appendSuffix('A');
					r.push(`With ${hisA} ${V.assistant.appearance} appearance,`);
					r.push(
						App.UI.DOM.passageLink(V.assistant.name, "Personal assistant options")
					);
					r.push(`is a good public mascot for this goal.`);
				}
			}

			App.Events.addNode(el, r);
			return el;
		}
	}
};


code:
globalThis.FutureSocieties = (function() {
	/** @type {Map<FC.FutureSocietyDeco, FC.FutureSociety>} */
	const DecoToFSMap = new Map();
	for (const [fsName, details] of Object.entries(App.Data.FutureSociety.records)) {
		if (details.deco) {
			// @ts-ignore - Object.entries loses type information, apparently intentionally
			DecoToFSMap.set(details.deco, fsName);
		}
	}

	return {
		activeFSes: activeFSes,
		activeCount: activeCount,
		applyBroadProgress: applyBroadProgress,
		availCredits: calcFSCredits,
		influenceSources: influenceSources,
		decorationName: decorationName,
		diplomaticFSes: diplomaticFSes,
		displayName: displayName,
		displayAdj: displayAdj,
		decay: decayFSes,
		overflowToInfluence: overflowToInfluence,
		remove: removeFS,
		validAdoptions: validAdoptions,
		DecorationCleanup: DecorationCleanup,
		DecorationBonus: FSDecorationBonus,
		Change: FSChange,
		HighestDecoration: FSHighestDecoration,
		arcSupport: arcSupport,
		researchAvailable,
		isActive,
	};

	/** get the list of FSes active for a particular arcology
	 * @param {FC.ArcologyState} arcology
	 * @returns {FC.FutureSociety[]}
	 */
	function activeFSes(arcology) {
		return App.Data.FutureSociety.fsNames.filter((fs) => Number.isFinite(arcology[fs]));
	}

	/** call as FutureSocieties.activeCount(arcology)
	 * @param {FC.ArcologyState} arcology
	 * @returns {number}
	 */
	function activeCount(arcology) {
		return activeFSes(arcology).length;
	}

	/** call as FutureSocieties.applyBroadProgress(arcologyID, progress)
	 * @param {number} arcologyID
	 * @param {number} progress
	 */
	function applyBroadProgress(arcologyID, progress) {
		/** @type {FC.ArcologyState} */
		const arcology = V.arcologies[arcologyID];
		for (const fs of activeFSes(arcology)) {
			if (fs !== "FSNull") { // does not progress this way
				// @ts-ignore // Because of activeFSes() we know it cannot be "unset", the only valid non numeric value.
				arcology[fs] += progress;
			}
		}
	}

	/** converts excess progress into influence bonus
	 * call as FutureSocieties.overflowToInfluence(arcologyID)
	 * @param {number} arcologyID
	 */
	function overflowToInfluence(arcologyID) {
		const arcology = V.arcologies[arcologyID];
		for (const fs of activeFSes(arcology)) {
			if (fs !== "FSNull") { // no conventional progress
				if (arcology[fs] > V.FSLockinLevel) {
					// @ts-ignore // Because of activeFSes() we know it cannot be "unset", the only valid non numeric value.
					arcology.influenceBonus += arcology[fs] - V.FSLockinLevel;
					arcology[fs] = V.FSLockinLevel;
				}
			}
		}
	}

	/** returns an array of FSes which the arcology has developed enough to influence others
	 * call as FutureSocieties.influenceSources(arcologyID)
	 * @param {number} arcologyID
	 */
	function influenceSources(arcologyID) {
		let fses = [];
		const arcology = V.arcologies[arcologyID];
		for (const fs of activeFSes(arcology)) {
			if (fs !== "FSNull") { // no influence from Multiculturalism?
				if (arcology[fs] > 60) {
					fses.push(fs);
				}
			}
		}
		return fses;
	}

	/** determines whether two named FSes are naturally conflicting or not
	 * @param {FC.FutureSociety} left FS
	 * @param {FC.FutureSociety} right FS
	 * @returns {boolean}
	 */
	function conflictingFSes(left, right) {
		if (left !== right) { // identical FSes are not opposed
			for (const group of App.Data.FutureSociety.mutexGroups) {
				if (group.includesAll(left, right)) {
					return true; // but any other FS in the mutex group is
				}
			}
		}
		return false;
	}

	/** returns an array of all of the FSes that would be valid for this arcology to adopt right now
	 * @param {number} arcID
	 * @returns {FC.FutureSociety[]}
	 */
	function validAdoptions(arcID) {
		const arcology = V.arcologies[arcID];
		const societies = Array.from(arcID !== 0 ? App.Data.FutureSociety.fsNames : App.Data.FutureSociety.playerFSNames);
		const arcFSes = activeFSes(arcology);

		// apply game rules
		if (!V.seeIncest) {
			societies.delete("FSIncestFetishist");
		}
		if (!V.seePreg) {
			societies.delete("FSRepopulationFocus");
		}

		// FSes already adopted by the arcology are invalid
		societies.deleteWith(fs => arcFSes.includes(fs));

		// FSes that conflict with FSes adopted by the arcology are invalid
		societies.deleteWith(fs1 => arcFSes.some(fs2 => conflictingFSes(fs1, fs2)));

		// if the government is loyal to you, FSes that conflict with FSes adopted by the player are invalid
		if (arcology.government === "your agent" || arcology.government === "your trustees") {
			const playerFSes = activeFSes(V.arcologies[0]);
			societies.deleteWith(fs1 => playerFSes.some(fs2 => conflictingFSes(fs1, fs2)));
		}

		return societies;
	}

	/** returns the set of shared FSes between two arcologies, and the set of conflicts between pairs of FSes between the arcologies
	 * relatively expensive, try not to call frequently
	 * call as FutureSocieties.diplomaticFSes(arc1, arc2)
	 * @param {FC.ArcologyState} arc1
	 * @param {FC.ArcologyState} arc2
	 * @returns {{shared: FC.FutureSociety[], conflicting: FC.FutureSociety[][]}}
	 */
	function diplomaticFSes(arc1, arc2) {
		/** @type {FC.FutureSociety[]} */
		let shared = [];
		/** @type {FC.FutureSociety[][]} */
		let conflicting = [];
		const arc1FSes = activeFSes(arc1);
		const arc2FSes = activeFSes(arc2);
		// find ordinary shared and conflicting FSes
		for (const fs1 of arc1FSes) {
			for (const fs2 of arc2FSes) {
				if (fs1 === fs2) {
					shared.push(fs1);
				} else if (conflictingFSes(fs1, fs2)) {
					conflicting.push([fs1, fs2]);
				}
			}
		}
		// special cases: racial FSes might be conflicting even when shared
		if (shared.contains("FSSupremacist")) {
			// a different race is supreme
			if (arc1.FSSupremacistRace !== arc2.FSSupremacistRace) {
				shared.delete("FSSupremacist");
				conflicting.push(["FSSupremacist", "FSSupremacist"]);
			}
			// subjugating the supreme race
			if (arc2FSes.contains("FSSubjugationist") && arc2.FSSubjugationistRace === arc1.FSSupremacistRace) {
				shared.delete("FSSupremacist");
				conflicting.push(["FSSupremacist", "FSSubjugationist"]);
			}
		}
		if (shared.contains("FSSubjugationist")) {
			// subjugating a different race
			if (arc1.FSSubjugationistRace !== arc2.FSSubjugationistRace) {
				shared.delete("FSSubjugationist");
				conflicting.push(["FSSubjugationist", "FSSubjugationist"]);
			}
			// believe the subjugated race is supreme
			if (arc2FSes.contains("FSSupremacist") && arc2.FSSupremacistRace === arc1.FSSubjugationistRace) {
				shared.delete("FSSubjugationist");
				conflicting.push(["FSSubjugationist", "FSSupremacist"]);
			}
		}
		return {shared, conflicting};
	}

	/** returns the future society display name (typically an "ism") for the given property
	 * @param {FC.FutureSociety} FSProp
	 * @returns {FC.FutureSocietyNoun}
	 */
	function displayName(FSProp) {
		return App.Data.FutureSociety.records[FSProp].noun;
	}

	/** returns the future society adjective (typically an "ist") for the given property
	 * @param {FC.FutureSociety} FSProp
	 * @returns {FC.FutureSocietyAdj}
	 */
	function displayAdj(FSProp) {
		return App.Data.FutureSociety.records[FSProp].adj;
	}

	/** returns the future society decoration name (for use in decoration properties) for the given FS property
	 * @param {FC.FutureSociety} FSProp
	 * @returns {FC.FutureSocietyDeco}
	 */
	function decorationName(FSProp) {
		return App.Data.FutureSociety.records[FSProp].deco;
	}

	/** decays all the FSes adopted by a particular arcology (for example, because of government instability)
	 * call as FutureSocieties.decay(arcologyID)
	 * @param {number} arcologyID
	 * @returns {FC.FutureSociety[]} FSes which purged completely
	 */
	function decayFSes(arcologyID) {
		const arc = V.arcologies[arcologyID];
		const fses = activeFSes(arc);
		/** @type {FC.FutureSociety[]} */
		let purged = [];
		for (const fs of fses) {
			if (fs !== "FSNull") { // exempt for some reason?
				if (arc[fs] < jsRandom(10, 150)) {
					purged.push(fs);
					arc[fs] = "unset";
				} else {
					// @ts-ignore // Because of activeFSes() we know it cannot be "unset", the only valid non numeric value.
					arc[fs] -= 10;
				}
			}
		}
		return purged;
	}

	/** Remove an FS and all associated attributes from an arcology
	 * call as FutureSocieties.remove(FS)
	 * @param {FC.FutureSociety} FS (e.g. "FSPaternalist" or "FSDegradationist")
	 */
	function removeFS(FS) {
		const arcology = V.arcologies[0];
		const FSDecoration = `${FS}Decoration`;
		const FSSMR = `${FS}SMR`;
		let FSLaw = `${FS}Law`;
		if (arcology[FS] === undefined) {
			// eslint-disable-next-line no-console
			console.log(`ERROR: bad FS reference, $arcologies[0].${FS} not defined`);
			return;
		}

		if (FS === "FSSupremacist" || FS === "FSSubjugationist") { FSLaw += "ME"; }
		if (FS !== "FSNull") { arcology[FSDecoration] = 20; }
		arcology[FS] = "unset";
		switch (FS) {
			case "FSPaternalist":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.slaveWatch = 0;
				}
				break;
			case "FSDegradationist":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.defense.liveTargets = 0;
				}
				break;
			case "FSGenderRadicalist":
				arcology.FSGenderRadicalistLawBeauty = 0;
				arcology.FSGenderRadicalistLawFuta = 0;
				break;
			case "FSGenderFundamentalist":
				arcology.FSGenderFundamentalistLawBeauty = 0;
				arcology.FSGenderFundamentalistLawBimbo = 0;
				arcology.FSGenderFundamentalistSMR = 0;
				break;
			case "FSSupremacist":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.defense.noSubhumansInArmy = 0;
				}
				break;
			case "FSTransformationFetishist":
			case "FSAssetExpansionist":
				arcology[FSSMR] = 0;
				break;
			case "FSSlimnessEnthusiast":
				arcology.FSSlimnessEnthusiastLaw = 0;
				arcology.FSSlimnessEnthusiastFoodLaw = 0;
				arcology.FSSlimnessEnthusiastSMR = 0;
				break;
			case "FSPhysicalIdealist":
				arcology.FSPhysicalIdealistLaw = 0;
				arcology.FSPhysicalIdealistSMR = 0;
				arcology.FSPhysicalIdealistStrongFat = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.defense.martialSchool = 0;
				}
				break;
			case "FSHedonisticDecadence":
				arcology.FSHedonisticDecadenceLaw = 0;
				arcology.FSHedonisticDecadenceLaw2 = 0;
				arcology.FSHedonisticDecadenceSMR = 0;
				arcology.FSHedonisticDecadenceStrongFat = 0;
				break;
			case "FSChattelReligionist":
				arcology.FSChattelReligionistLaw = 0;
				arcology.FSChattelReligionistLaw2 = 0;
				arcology.FSChattelReligionistSMR = 0;
				arcology.FSChattelReligionistCreed = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.subsidyChurch = 0;
				}
				break;
			case "FSRepopulationFocus":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				V.universalRulesChildrenBecomeBreeders = 0;
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.defense.pregExemption = 0;
				}
				break;
			case "FSRestart":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				arcology.FSRestartResearch = 0; // not really research at all; reset breeding program
				if (_.get(V, "SecExp.edicts")) {
					V.SecExp.edicts.defense.eliteOfficers = 0;
				}
				V.propOutcome = 0;
				V.failedElite = 0;
				V.playerBred = 0;
				break;
			case "FSIntellectualDependency":
				arcology.FSIntellectualDependencyLaw = 0;
				arcology.FSIntellectualDependencyLawBeauty = 0;
				arcology.FSIntellectualDependencySMR = 0;
				break;
			case "FSPetiteAdmiration":
				arcology.FSPetiteAdmirationLaw = 0;
				arcology.FSPetiteAdmirationLaw2 = 0;
				arcology.FSPetiteAdmirationSMR = 0;
				break;
			case "FSStatuesqueGlorification":
				arcology.FSStatuesqueGlorificationLaw = 0;
				arcology.FSStatuesqueGlorificationLaw2 = 0;
				arcology.FSStatuesqueGlorificationSMR = 0;
				break;
			case "FSBodyPurist":
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				arcology.FSBodyPuristCatLaw = 0;
				break;
			case "FSNeoImperialist":
				arcology.FSNeoImperialistLaw1 = 0;
				arcology.FSNeoImperialistLaw2 = 0;
				arcology.FSNeoImperialistSMR = 0;
				break;
			case "FSNull":
				break;
			default: // all others have one law and one SMR
				arcology[FSLaw] = 0;
				arcology[FSSMR] = 0;
				break;
		}

		DecorationCleanup();

		// Clean up assistant
		if (
			(V.assistant.fsAppearance === App.Data.FutureSociety.records[FS].adj.toLowerCase()) ||
			(V.assistant.fsAppearance === App.Data.FutureSociety.records[FS].noun.toLowerCase()) ||
			(FS === "FSHedonisticDecadence" && V.assistant.fsAppearance === "hedonistic decadence") ||
			(FS === "FSNeoImperialist" && V.assistant.fsAppearance === "neoimperialist")
		) {
			V.assistant.fsAppearance = "default";
		}
	}

	/** Calculate the number of FS credits that the player still has available (unspent)
	 * Call as FutureSocieties.availCredits()
	 * @returns {number}
	 */
	function calcFSCredits() {
		const arcology = V.arcologies[0];
		let activeFS = activeCount(arcology);
		if (typeof arcology.FSNull === 'number' && arcology.FSNull > 0) { // multiculturalism is accounted for separately
			activeFS -= 1; // already counted once, remove that one and count investments instead
			if (V.FSCreditCount === 4) {
				activeFS += arcology.FSNull / 25;
			} else if (V.FSCreditCount === 6) {
				activeFS += arcology.FSNull / 17;
			} else if (V.FSCreditCount === 7) {
				activeFS += arcology.FSNull / 15;
			} else {
				activeFS += arcology.FSNull / 20;
			}
		}
		return Math.max(Math.trunc(V.FSGotRepCredits - activeFS), 0);
	}

	/* call as FutureSocieties.DecorationCleanup() */
	function DecorationCleanup() {
		for (const facility of Object.values(App.Entity.facilities).filter(f => f.established && f.isDecorated)) {
			ValidateFacilityDecoration(facility);
		}
	}

	/** helper function, not callable externally
	 * @param {App.Entity.Facilities.Facility} facility
	 */
	function ValidateFacilityDecoration(facility) {
		// Backwards compatibility, do not remove
		// @ts-ignore BC
		if (facility.decoration === "Hedonism" || facility.decoration === "Hedonistic Decadence") {
			facility.decoration = "Hedonistic";
		// @ts-ignore BC
		} else if (facility.decoration === "Repopulation Focus") {
			facility.decoration = "Repopulationist";
		// @ts-ignore BC
		} else if (facility.decoration === "Neo Imperialist") {
			facility.decoration = "Neo-Imperialist";
		}

		const activeFS = DecoToFSMap.get(facility.decoration); // gets the property name

		if (facility.decoration === "standard") {
			// nothing to do
		} else if (activeFS === undefined) {
			// eslint-disable-next-line no-console
			console.log(`Error: ${facility.nameCaps} decoration is ${facility.decoration}`);
			facility.decoration = "standard";
		} else if (!Number.isFinite(V.arcologies[0][activeFS])) {
			if (V.arcologies[0][activeFS] !== "unset") {
				// eslint-disable-next-line no-console
				console.log(`Error: $arcologies[0].${activeFS} is ${V.arcologies[0][activeFS]}`);
			}
			facility.decoration = "standard";
		}
	}

	/** Apply the decoration bonus for a slave working in a facility to the FS
	 * call as FutureSocieties.DecorationBonus()
	 * @param {FC.FutureSocietyDeco} decoration - not quoted, just pass it straight in
	 * @param {number} magnitude - will be multiplied by V.FSSingleSlaveRep
	 */
	function FSDecorationBonus(decoration, magnitude) {
		if (decoration === "standard") {
			return; // no bonus
		}

		const FSProp = DecoToFSMap.get(decoration); // gets the property name
		const arc = V.arcologies[0];

		if (FSProp && Number.isFinite(arc[FSProp])) {
			/** @type {number} */
			// @ts-ignore // We know this is true because of Number.isFinite()
			const n = arc[FSProp];
			arc[FSProp] = Math.clamp(n + magnitude * V.FSSingleSlaveRep, 0, 100);
		}
	}

	/** call as FutureSocieties.Change()
	 * @param {FC.FutureSociety|FC.FutureSocietyDeco} FSString either decoration or FS property
	 * @param {number} magnitude size of change
	 * @param {number} [bonusMultiplier=1] multiplier to be applied to FS change (but NOT to rep change)
	 * @returns {number} reputation change value (for recordkeeping)
	 */
	function FSChange(FSString, magnitude, bonusMultiplier = 1) {
		const arcology = V.arcologies[0];
		// @ts-ignore - could be a decoration string; just try to convert it to a property name, and if we can't then assume it's a property name and use it as-is
		const activeFS = DecoToFSMap.get(FSString) || FSString;

		if (Number.isFinite(arcology[activeFS])) {
			let repChange = magnitude * V.FSSingleSlaveRep * (arcology[activeFS] / V.FSLockinLevel);
			if (magnitude < 0) {
				repChange /= 3; // Reducing the reputation impact of slaves that are not adhering to societal ideals properly
			}
			repX(repChange, 'futureSocieties');
			arcology[activeFS] += 0.05 * magnitude * V.FSSingleSlaveRep * bonusMultiplier;
			return repChange;
		} else {
			console.log(`Attempted to change unset FS ${activeFS}`);
			return 0;
		}
	}

	/**
	 * Returns the highest decoration level of active future societies, call as FutureSocieties.HighestDecoration()
	 * @returns {number}
	 */
	function FSHighestDecoration() {
		const arcology = V.arcologies[0];
		const decorationList = App.Data.FutureSociety.playerFSNames.map(FS => `${FS}Decoration`);
		let level = 20; // All decorations start at 20

		for (const deco of decorationList) {
			if (arcology[deco] > level) {
				level = arcology[deco];
			}
		}
		return level;
	}

	/**
	 *
	 * @param {FC.FutureSociety} FS
	 * @param {*} [arc]
	 */
	function arcSupport(FS, arc = V.arcologies[0]) {
		const arcSupportMap = new Map([
			[
				"FSSupremacist",
				[
					`${arc.name} believes implicitly in ${arc.FSSupremacistRace} superiority.`,
					`${arc.name} agrees strongly with ${arc.FSSupremacistRace} superiority.`,
					`${arc.name} is sympathetic to ${arc.FSSupremacistRace} superiority.`,
					`${arc.name} is unconvinced of ${arc.FSSupremacistRace} superiority.`
				]
			],
			[
				"FSSubjugationist",
				[
					`${arc.name} believes implicitly in the inferiority of ${arc.FSSubjugationistRace} people.`,
					`${arc.name} agrees strongly with the inferiority of ${arc.FSSubjugationistRace} people.`,
					`${arc.name} is sympathetic to the inferiority of ${arc.FSSubjugationistRace} people.`,
					`${arc.name} is unconvinced of the inferiority of ${arc.FSSubjugationistRace} people.`
				]
			],
			[
				"FSRepopulationFocus",
				[
					`${arc.name} believes implicitly that all women should be pregnant.`,
					`${arc.name} agrees strongly that all women should be pregnant.`,
					`${arc.name} is sympathetic to the idea that all women should be pregnant.`,
					`${arc.name} is unconvinced that all women should be pregnant.`,

				]
			],
			[
				"FSRestart",
				[
					`${arc.name} believes implicitly that only the elite should reproduce.`,
					`${arc.name} agrees strongly that only the elite should reproduce.`,
					`${arc.name} is sympathetic to the idea only the elite should reproduce.`,
					`${arc.name} is unconvinced that only the elite should reproduce.`
				]
			],
			[
				"FSIntellectualDependency",
				[
					`${arc.name} believes implicitly that all slaves should be mentally dependant on their owner.`,
					`${arc.name} agrees strongly that all slaves should be bimbos.`,
					`${arc.name} is sympathetic to the idea that slaves should be dumb and horny.`,
					`${arc.name} is unconvinced that all slaves should be morons.`
				]
			],
			[
				"FSSlaveProfessionalism",
				[
					`${arc.name} believes implicitly that slaves should be masters of the sexual arts.`,
					`${arc.name} agrees strongly with slavery as a profession.`,
					`${arc.name} is sympathetic to the notion of slavery as a profession.`,
					`${arc.name} is unconvinced that slaves should be highly intelligent.`
				]
			],
			[
				"FSGenderRadicalist",
				[
					`${arc.name} believes implicitly in the need to redefine gender around power.`,
					`${arc.name} agrees strongly with the need to redefine gender around power.`,
					`${arc.name} is sympathetic to the need to redefine gender around power.`,
					`${arc.name} is unconvinced of the need to redefine gender around power.`
				]
			],
			[
				"FSGenderFundamentalist",
				[
					`${arc.name} believes implicitly in the need to preserve traditional gender roles.`,
					`${arc.name} agrees strongly with the need to preserve traditional gender roles.`,
					`${arc.name} is sympathetic to the need to preserve traditional gender roles.`,
					`${arc.name} is unconvinced of the need to preserve traditional gender roles.`
				]
			],
			[
				"FSPaternalist",
				[
					`${arc.name} believes implicitly in the vision of a well-bred race of slaves.`,
					`${arc.name} agrees strongly with the vision of a well-bred race of slaves.`,
					`${arc.name} is sympathetic to the vision of a well-bred race of slaves.`,
					`${arc.name} is unconvinced of the vision of a well-bred race of slaves.`
				]
			],
			[
				"FSDegradationist",
				[
					`${arc.name} believes implicitly that slaves are not human and should be thoroughly degraded.`,
					`${arc.name} agrees strongly with the idea that slaves are not human and should be thoroughly degraded.`,
					`${arc.name} is sympathetic to the proposition that slaves are not human and should be thoroughly degraded.`,
					`${arc.name} is unconvinced that slaves are not human and should be thoroughly degraded.`
				]
			],
			[
				"FSBodyPurist",
				[
					`${arc.name} believes implicitly in the unattractive nature of implants.`,
					`${arc.name} agrees strongly with the unattractive nature of implants.`,
					`${arc.name} is sympathetic to the unattractive nature of implants.`,
					`${arc.name} is unconvinced of the unattractive nature of implants.`
				]
			],
			[
				"FSTransformationFetishist",
				[
					`${arc.name} passionately fetishizes implants.`,
					`${arc.name} strongly fetishizes implants.`,
					`${arc.name} fetishizes implants.`,
					`${arc.name} is beginning to fetishize implants.`
				]
			],
			[
				"FSMaturityPreferentialist",
				[
					`${arc.name} is passionately enthusiastic for older ladies.`,
					`${arc.name} is very enthusiastic for older ladies.`,
					`${arc.name} is enthusiastic for older ladies.`,
					`${arc.name} is beginning to be enthusiastic for older ladies.`
				]
			],
			[
				"FSYouthPreferentialist",
				[
					`${arc.name} is passionately enthusiastic for young women.`,
					`${arc.name} is very enthusiastic for young women.`,
					`${arc.name} is enthusiastic for young women.`,
					`${arc.name} is beginning to be enthusiastic for young women.`
				]
			],
			[
				"FSPetiteAdmiration",
				[
					`${arc.name} is passionately enthusiastic for short slaves.`,
					`${arc.name} is very enthusiastic for short slaves.`,
					`${arc.name} is enthusiastic for short slaves.`,
					`${arc.name} is beginning to be enthusiastic for short slaves.`
				]
			],
			[
				"FSStatuesqueGlorification",
				[
					`${arc.name} believes implicitly that the tall are superior.`,
					`${arc.name} agrees strongly with the idea that the tall are superior.`,
					`${arc.name} is sympathetic to the idea that the tall are superior.`,
					`${arc.name} is unconvinced that the tall are superior.`
				]
			],
			[
				"FSSlimnessEnthusiast",
				[
					`${arc.name} is passionately enthusiastic about slim slaves with girlish figures.`,
					`${arc.name} is very enthusiastic about slim slaves with girlish figures.`,
					`${arc.name} is enthusiastic about slim slaves with girlish figures.`,
					`${arc.name} is beginning to be enthusiastic about slim slaves with girlish figures.`
				]
			],
			[
				"FSAssetExpansionist",
				[
					`${arc.name} believes implicitly that all tits and asses should be bigger.`,
					`${arc.name} agrees strongly with the idea that all tits and asses should be bigger.`,
					`${arc.name} is sympathetic to the idea that all tits and asses should be bigger.`,
					`${arc.name} is unconvinced that all tits and asses should be bigger.`
				]
			],
			[
				"FSPastoralist",
				[
					`${arc.name} believes implicitly that slaves should be milked.`,
					`${arc.name} agrees strongly with the idea that slaves should be milked.`,
					`${arc.name} is sympathetic to the idea that slaves should be milked.`,
					`${arc.name} is unconvinced that slaves should be milked.`
				]
			],
			[
				"FSPhysicalIdealist",
				[
					`${arc.name} believes implicitly that all slaves should be tall and strong.`,
					`${arc.name} agrees strongly with the idea that all slaves should be tall and strong.`,
					`${arc.name} is sympathetic to the idea that all slaves should be tall and strong.`,
					`${arc.name} is unconvinced that all slaves should be tall and strong.`
				]
			],
			[
				"FSHedonisticDecadence",
				[
					`${arc.name} believes implicitly that all slaves should be soft and laid-back.`,
					`${arc.name} agrees strongly with the idea that all slaves should be soft and laid-back.`,
					`${arc.name} is sympathetic to the idea that all slaves should be soft and laid-back.`,
					`${arc.name} is unconvinced that all slaves should be soft and laid-back.`
				]
			],
			[
				"FSChattelReligionist",
				[
					`${arc.name} believes implicitly in a version of religion that emphasizes slaveholding traditions.`,
					`${arc.name} agrees strongly with a version of religion that emphasizes slaveholding traditions.`,
					`${arc.name} is sympathetic to a version of religion that emphasizes slaveholding traditions.`,
					`${arc.name} is unconvinced of a version of religion that emphasizes slaveholding traditions.`

				]
			],
			[
				"FSNull",
				[
					`${arc.name} is committed to affording its citizens absolute cultural freedom.`,
					`${arc.name} is committed to affording its citizens great cultural freedom.`,
					`${arc.name} is committed to affording its citizens considerable cultural freedom.`,
					`${arc.name} is committed to affording its citizens basic cultural freedom.`
				]
			],
			[
				"FSRomanRevivalist",
				[
					`${arc.name} believes implicitly that it is the new Rome.`,
					`${arc.name} agrees strongly with your project to build the new Rome.`,
					`${arc.name} is sympathetic to your project to build the new Rome.`,
					`${arc.name} is unconvinced of the wisdom of your project to build the new Rome.`
				]
			],
			[
				"FSNeoImperialist",
				[
					`${arc.name} believes implicitly that your arcology is truly a new Imperial Society.`,
					`${arc.name} agrees strongly with your project to build a new Imperial Society.`,
					`${arc.name} is sympathetic to your project to build a new Imperial Society.`,
					`${arc.name} is unconvinced of the wisdom of your project to build a new Imperial Society.`
				]
			],
			[
				"FSAztecRevivalist",
				[
					`${arc.name} believes implicitly that it is the new Aztec Empire.`,
					`${arc.name} agrees strongly with your project to build the new Aztec Empire.`,
					`${arc.name} is sympathetic to your project to build the new Aztec Empire.`,
					`${arc.name} is unconvinced of the wisdom of your project to build the new Aztec Empire.`
				]
			],
			[
				"FSEgyptianRevivalist",
				[
					`${arc.name} believes implicitly that it is the land of the Pharaohs, reborn.`,
					`${arc.name} agrees strongly with your project to build a new land of the Pharaohs.`,
					`${arc.name} is sympathetic to your project to build a new land of the Pharaohs.`,
					`${arc.name} is unconvinced of the wisdom of your project to build a new land of the Pharaohs.`
				]
			],
			[
				"FSEdoRevivalist",
				[
					`${arc.name} believes implicitly that it is the land of the Shogun, reborn.`,
					`${arc.name} agrees strongly with your project to build a new Edo Japan.`,
					`${arc.name} is sympathetic to your project to build a new Edo Japan.`,
					`${arc.name} is unconvinced of the wisdom of your project to build a new Edo Japan.`
				]
			],
			[
				"FSArabianRevivalist",
				[
					`${arc.name} believes implicitly that it is ancient Baghdad reborn.`,
					`${arc.name} agrees strongly with your project to build a new Sultanate.`,
					`${arc.name} is sympathetic to your project to build a new Sultanate.`,
					`${arc.name} is unconvinced of the wisdom of your project to build a new Sultanate.`
				]
			],
			[
				"FSChineseRevivalist",
				[
					`${arc.name} believes implicitly that possesses the Mandate of Heaven.`,
					`${arc.name} agrees strongly with your pursuit of the Mandate of Heaven.`,
					`${arc.name} is sympathetic to your pursuit of the Mandate of Heaven.`,
					`${arc.name} is unconvinced of the wisdom of your pursuit of the Mandate of Heaven.`
				]
			],
		]);
		if (arc[FS] !== "unset") {
			if (arc[FS] >= V.FSLockinLevel) {
				return arcSupportMap.get(FS)[0];
			} else if (arc[FS] >= V.FSLockinLevel * 0.6) {
				return arcSupportMap.get(FS)[1];
			} else if (arc[FS] >= V.FSLockinLevel * 0.3) {
				return arcSupportMap.get(FS)[2];
			} else {
				return arcSupportMap.get(FS)[3];
			}
		}
	}

	/**
	 *
	 * @param {FC.FSName<keyof FC.FutureSocietyWithResearchMap>} fs
	 * @param {FC.ArcologyState}[arcology] Arcology to test, defaults to the PC's arcology
	 * @returns {boolean}
	 */
	function researchAvailable(fs, arcology) {
		return (arcology || V.arcologies[0])[`FS${fs}Research`] === 1;
	}

	/**
	 * Checks if the given FS active (i.e. not "unset")
	 * @param {FC.FutureSociety} fs
	 * @param {FC.ArcologyState} [arcology] Arcology to test, defaults to the PC's arcology
	 * @returns {boolean}
	 */
	function isActive(fs, arcology) {
		return (arcology || V.arcologies[0])[fs] !== "unset";
	}
})();

repo_name: DontAskDontTell/fc

license: mit


code:
App.Encyclopedia.ui = function() {
	const f = document.createElement("div");
	const topLinks = [];
	if (V.encyclopedia !== "Table of Contents") {
		App.UI.DOM.appendNewElement("div", f, App.Encyclopedia.link("Table of Contents"), ["center"]);
	}
	App.UI.DOM.appendNewElement("h2", f, V.encyclopedia, ["center"]);
	f.append(App.Encyclopedia.renderArticle(V.encyclopedia));

	if (!["How to Play", "Table of Contents", "Credits"].includes(V.encyclopedia)) { // special pages where we don't show related links
		if (![
			"Being in Charge", "Body", "Fetishes", "Mods/Pregmod", "Inflation",
			"Leadership Positions", "Loli Mode", "Lore", "Obtaining Slaves", "Paraphilias",
			"Playing Free Cities", "Skills", "Slave Assignments", "Slave Modification", "Slaves",
			"Terrain Types", "The X-Series Arcology"
		].includes(V.encyclopedia)) { // pages asking the player to click a link for more information have no section heading
			App.UI.DOM.appendNewElement("h3", f, "Related Links", ["center"]);
		}
		f.append(App.Encyclopedia.renderCategory(V.encyclopedia));
	}
	const bottomLinks = [];
	if (V.encyclopedia !== "Table of Contents") {
		bottomLinks.push(App.Encyclopedia.link("Table of Contents"));
		if (V.encyclopedia !== "Credits") {
			bottomLinks.push(App.Encyclopedia.link("Credits"));
		}
	}
	App.UI.DOM.appendNewElement("div", f, App.UI.DOM.generateLinksStrip(bottomLinks), ["center"]);
	return f;
};

	/** Create a link to an encyclopedia dialog for a given article with the given text
	 * @param {string} text Text for link
	 * @param {string} [article] Encyclopedia article to link to (if not supplied than text is capitalized.)
	 * @param {string} [classNames] CSS Class to add to the link
	 * @returns {HTMLElement} DOM link element
	 */
	App.Encyclopedia.link = function(text, article = capFirstChar(text), classNames) {
		const link = App.UI.DOM.link(text, () => showArticleInDialog(article));
		if (!classNames) {
			return link;
		}
		// Wrap in a span for coloring, more reliable when hovering over the link
		const span = document.createElement("span");
		span.className += classNames;
		span.append(link);
		return span;

	/** Show a given encyclopedia article in the encyclopedia dialog
	 * @param {string} article
	 */
		function showArticleInDialog(article) {
			const origEncyclopedia = V.encyclopedia;
			if (Dialog.isOpen()) {
				Dialog.close();
			}
			Dialog.setup("Encyclopedia", "encyclopedia");
			V.encyclopedia = article;
			Dialog.append(App.Encyclopedia.ui());
			Dialog.open();
			V.encyclopedia = origEncyclopedia;
		}
	};