diff --git a/devTools/types/FC/arcology.d.ts b/devTools/types/FC/arcology.d.ts
index 0157430870bc8786f3515fd9c79ca6d3d3e7304c..ca632f6f171d1c35719aefae5db47bac73680c5c 100644
--- a/devTools/types/FC/arcology.d.ts
+++ b/devTools/types/FC/arcology.d.ts
@@ -248,4 +248,50 @@ declare namespace FC {
 		NEO_IMPERIAL: RevivalSocietyFreezeValue.NeoImperial,
 		ROMAN: RevivalSocietyFreezeValue.Roman,
 	}
+
+	const enum FSHumanDevelopmentVector {
+		/** Positive for mature preference, negative for young */
+		Age,
+		/** Positive for statuesque preference, negative for petite */
+		Height,
+		/** Positive for decadence preference negative for physical idealist */
+		Weight,
+		/** Positive for transformation preference, negative for body purism */
+		Modifications,
+		/** Positive for expansionist preference, negative for slim */
+		Assets,
+		/** Positive for professionalism, negative for intellectual dependency */
+		Intelligence,
+		/** Positive for radicalism, negative for fundamentalism */
+		Gender,
+		/** Positive for repopulation, negative for eugenics */
+		Breeding,
+		/** Positive for paternalism, negative for degradationism */
+		LifeQuality,
+	}
+
+	interface FSHumanDevelopmentVectorFreeze extends Record<string, FSHumanDevelopmentVector> {
+		AGE: FSHumanDevelopmentVector.Age,
+		HEIGHT: FSHumanDevelopmentVector.Height,
+		WEIGHT: FSHumanDevelopmentVector.Weight,
+		MODIFICATIONS: FSHumanDevelopmentVector.Modifications,
+		ASSETS: FSHumanDevelopmentVector.Assets,
+		INTELLIGENCE: FSHumanDevelopmentVector.Intelligence,
+		GENDER: FSHumanDevelopmentVector.Gender,
+		BREEDING: FSHumanDevelopmentVector.Breeding,
+		LIFE_QUALITY: FSHumanDevelopmentVector.LifeQuality,
+	}
+
+	interface SlaveSocialEffect {
+		/** FS */
+		FS: FutureSocietyDeco | "";
+		/** positive or negative value (a small integer, or a fraction between -1 and 1) */
+		magnitude: number;
+		/** for compact/list mode (plain text string) */
+		shortDesc: string;
+		/** for expanded mode (HTML string) */
+		longDesc: string;
+		/** true if the effect is only due to the facility she's assigned to */
+		creditFacility?: boolean;
+	}
 }
diff --git a/js/003-data/constants.js b/js/003-data/constants.js
index 0db2cd4b10e03e4508be81bf3bb4a7ba52841795..0e0e29c0b54564406b7505b007bf520875d8fb8c 100644
--- a/js/003-data/constants.js
+++ b/js/003-data/constants.js
@@ -148,3 +148,19 @@ globalThis.RevivalSociety = Object.freeze({
 	NEO_IMPERIAL: 6,
 	ROMAN: 7
 });
+
+/**
+ * @type {FC.FSHumanDevelopmentVectorFreeze}
+ * @enum {number}
+ */
+globalThis.FSHumanVector = Object.freeze({
+	AGE: 0,
+	HEIGHT: 1,
+	WEIGHT: 2,
+	MODIFICATIONS: 3,
+	ASSETS: 4,
+	INTELLIGENCE: 5,
+	GENDER: 6,
+	BREEDING: 7,
+	LIFE_QUALITY: 8,
+});
diff --git a/src/005-passages/managePassages.js b/src/005-passages/managePassages.js
index 9531718e2596d0ed4281c50667a184334487db85..17d80261a60166cf27268cfd8a73484e8e16b73a 100644
--- a/src/005-passages/managePassages.js
+++ b/src/005-passages/managePassages.js
@@ -8,7 +8,9 @@ new App.DomPassage("Main",
 	}, ["jump-to-safe", "jump-from-safe"]
 );
 
-new App.DomPassage("Future Society", () => { return App.UI.fsPassage(); }, ["jump-to-safe", "jump-from-safe"]);
+new App.DomPassage("Future Society", () => {return App.UI.fsPassage();}, ["jump-to-safe", "jump-from-safe"]);
+
+new App.DomPassage("Slave FS Conformance", () => App.FSConformance.slaveConformancePassage(), ["jump-to-safe", "jump-from-safe"]);
 
 new App.DomPassage("Manage Penthouse",
 	() => {
diff --git a/src/endWeek/saSocialEffects.js b/src/endWeek/saSocialEffects.js
index 517a0f97a26a9a8aea434512f23e6ca3d68c4283..e63336419e8854dcb4b375c4f65cc8dd4072e708 100644
--- a/src/endWeek/saSocialEffects.js
+++ b/src/endWeek/saSocialEffects.js
@@ -20,7 +20,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 		this.creditFacility = creditFacility;
 	}
 
-	/** @returns {SocialEffect[]} */
+	/** @returns {FC.SlaveSocialEffect[]} */
 	function makeSocialEffects() {
 		let t = [];
 
@@ -1126,10 +1126,15 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 		return _.differenceBy(newSocialEffects, socialEffects, s => s.shortDesc);
 	}
 
+	function effects() {
+		return socialEffects;
+	}
+
 	const socialEffects = [...makeSocialEffects(), ...makeShelterGirlEffects()];
 
 	return {
 		report,
-		newForFS
+		newForFS,
+		effects
 	};
 };
diff --git a/src/futureSocieties/fsPassage.js b/src/futureSocieties/fsPassage.js
index bf7e69ebfb35b1eed6e49878554e7317e96a116c..f496a63cd5321f17a7f3826feeaee5ae8b7f52c5 100644
--- a/src/futureSocieties/fsPassage.js
+++ b/src/futureSocieties/fsPassage.js
@@ -171,6 +171,9 @@ App.UI.fsPassage = function() {
 		if (V.FSSpending > 10000) {
 			App.UI.DOM.appendNewElement("div", el, `Spending more than ${cashFormat(10000)} weekly is a waste`, "note");
 		}
+		if (App.FSConformance.anyVectorsDefined()) {
+			App.Events.addNode(el, [App.UI.DOM.passageLink("Check own slaves conformance", "Slave FS Conformance")]);
+		}
 		return el;
 	}
 
diff --git a/src/futureSocieties/futureSociety.js b/src/futureSocieties/futureSociety.js
index cd65e3555fb743e2f11d69cdf68e643ddaaa0e82..5179e55c847722da14a4ec327df1c7cc558ae0c2 100644
--- a/src/futureSocieties/futureSociety.js
+++ b/src/futureSocieties/futureSociety.js
@@ -31,6 +31,7 @@ globalThis.FutureSocieties = (function() {
 		isActive,
 		policyActive,
 		advance,
+		humanVector,
 	};
 
 	/** get the list of FSes active for a particular arcology
@@ -775,4 +776,32 @@ globalThis.FutureSocieties = (function() {
 		if (!FutureSocieties.isActive(fs, arc)) { return null; }
 		return arc[fs] += amount;
 	}
+
+	/**
+	 * Returns numeric value for one of the active FS policies pair, or null when both are inactive
+	 * @param {FC.FSHumanDevelopmentVector} vector the vector to test
+	 * @param {FC.ArcologyState} [arcology] Arcology to test, defaults to the PC's arcology
+	 * @returns {number|null} The number is positive or negative according to comments for FSHumanDevelopmentVector
+	 */
+	function humanVector(vector, arcology) {
+		const arc = arcology ?? V.arcologies[0];
+		/**
+		 * @param {FC.FSPolicyValue} positiveFS
+		 * @param {FC.FSPolicyValue} negativeFS
+		 * @returns {FC.FSPolicyValue}
+		 */
+		const select = (positiveFS, negativeFS) => positiveFS !== null ? positiveFS : (negativeFS !== null ? -negativeFS : null);
+
+		switch (vector) {
+			case FSHumanVector.AGE: return select(arc.FSMaturityPreferentialist, arc.FSYouthPreferentialist);
+			case FSHumanVector.HEIGHT: return select(arc.FSStatuesqueGlorification, arc.FSPetiteAdmiration);
+			case FSHumanVector.WEIGHT: return select(arc.FSHedonisticDecadence, arc.FSPhysicalIdealist);
+			case FSHumanVector.MODIFICATIONS: return select(arc.FSTransformationFetishist, arc.FSBodyPurist);
+			case FSHumanVector.ASSETS: return select(arc.FSAssetExpansionist, arc.FSSlimnessEnthusiast);
+			case FSHumanVector.INTELLIGENCE: return select(arc.FSSlaveProfessionalism, arc.FSIntellectualDependency);
+			case FSHumanVector.GENDER: return select(arc.FSGenderRadicalist, arc.FSGenderFundamentalist);
+			case FSHumanVector.BREEDING: return select(arc.FSRepopulationFocus, arc.FSRestart);
+			case FSHumanVector.LIFE_QUALITY: return select(arc.FSPaternalist, arc.FSDegradationist);
+		}
+	}
 })();
diff --git a/src/js/fsConformance.js b/src/js/fsConformance.js
new file mode 100644
index 0000000000000000000000000000000000000000..70f22b25692cd5e9024672d03776d0fb217c7e61
--- /dev/null
+++ b/src/js/fsConformance.js
@@ -0,0 +1,104 @@
+/** Utilities to list slave basing on their conformance to FS policies */
+App.FSConformance = function() {
+	/** @type {Record<FC.FSHumanDevelopmentVector, [FC.FutureSocietyDeco, FC.FutureSocietyDeco]>} */
+	const decosForVector = {
+		[FSHumanVector.AGE]: ['Maturity Preferentialist', 'Youth Preferentialist'],
+		[FSHumanVector.HEIGHT]: ['Statuesque Glorification', 'Petite Admiration'],
+		[FSHumanVector.WEIGHT]: ['Hedonistic', 'Physical Idealist'],
+		[FSHumanVector.MODIFICATIONS]: ['Transformation Fetishist', 'Body Purist'],
+		[FSHumanVector.ASSETS]: ['Asset Expansionist', 'Slimness Enthusiast'],
+		[FSHumanVector.INTELLIGENCE]: ['Slave Professionalism', 'Intellectual Dependency'],
+		[FSHumanVector.GENDER]: ['Gender Radicalist', 'Gender Fundamentalist'],
+		[FSHumanVector.BREEDING]: ['Repopulationist', 'Eugenics'],
+		[FSHumanVector.LIFE_QUALITY]: ['Paternalist', 'Degradationist'],
+	};
+
+	function anyVectorsDefined() {
+		return Object.values(FSHumanVector).some(v => FutureSocieties.humanVector(v, V.arcologies[0]) !== null);
+	}
+
+	/**
+	 * @param {{slave: FC.ReportSlave, effects: FC.SlaveSocialEffect[]}[]} slavesWithEffects
+	 * @param {FC.FutureSocietyDeco} fsDeco
+	 * @returns {{slave: FC.ReportSlave, magnitude: number}[]}
+	 */
+	function affectedSlaves(slavesWithEffects, fsDeco) {
+		return slavesWithEffects
+			.filter(item => item.effects.some(ef => fsDeco === ef.FS))
+			.map(item => {
+				const totalMagnitude = item.effects.filter(ef => fsDeco === ef.FS).reduce((total, ef) => total + ef.magnitude, 0);
+				return {slave: item.slave, magnitude: totalMagnitude};
+			});
+	}
+
+	/**
+	 * @param {{slave: FC.ReportSlave, magnitude: number}[]} slavesWithEffects
+	 * @param {FC.FutureSocietyDeco} deco
+	 * @param {boolean} top
+	 */
+	function listSlaves(slavesWithEffects, deco, top) {
+		if (top) {
+			slavesWithEffects.sort((left, right) => right.magnitude - left.magnitude);
+		} else {
+			slavesWithEffects.sort((left, right) => left.magnitude - right.magnitude);
+		}
+
+		return App.UI.SlaveList.render(
+			slavesWithEffects.slice(0, V.underperformersCount).map(item => item.slave.ID),
+			[],
+			App.UI.SlaveList.SlaveInteract.stdInteract,
+		);
+	}
+
+	function slaveConformancePassage() {
+		const node = new DocumentFragment();
+		const r = [];
+		r.push(App.UI.DOM.makeElement("div", `${properMaster()}, while you spend numerous hours and put a lot of efforts to shape the society in your arcology for the envisioned future, you might be interested to know how well your own slaves conform to that vision.`));
+
+		App.Events.addParagraph(node, r);
+
+		/** @type {{slave: FC.ReportSlave, effects: FC.SlaveSocialEffect[]}[]} */
+		const slaveSocialEffects = [];
+		for (const rpSlave of App.SlaveAssignment.reportSlaves(V.slaves)) {
+			slaveSocialEffects.push({slave: rpSlave, effects: App.SlaveAssignment.saSocialEffects(rpSlave).effects()});
+		}
+
+		const conformandDiv = App.UI.DOM.makeElement("div");
+		const conformantTabBar = new App.UI.Tabs.TabBar("fs-conformance-list");
+
+		const nonConformantDiv = App.UI.DOM.makeElement("div");
+		const nonConformantTabBar = new App.UI.Tabs.TabBar("fs-non-conformance-list");
+
+		for (const vector of Object.values(FSHumanVector)) {
+			const vectorValue = FutureSocieties.humanVector(vector, V.arcologies[0]);
+			if (vectorValue === null) {continue;}
+			const deco = vectorValue > 0 ? decosForVector[vector][0] : decosForVector[vector][1];
+
+			const slavesForDeco = affectedSlaves(slaveSocialEffects, deco);
+
+			conformantTabBar.addTab(deco, `${deco}-conform`, listSlaves(slavesForDeco.filter(item => item.magnitude > 0), deco, true));
+			nonConformantTabBar.addTab(deco, `${deco}-non-conform`, listSlaves(slavesForDeco.filter(item => item.magnitude <= 0), deco, false));
+		}
+
+		conformandDiv.append(conformantTabBar.render());
+		nonConformantDiv.append(nonConformantTabBar.render());
+
+		node.append(App.UI.DOM.accordion("Conformant", conformandDiv));
+		node.append(App.UI.DOM.accordion("Non-conformant", nonConformantDiv));
+
+		const g = new App.UI.OptionsGroup();
+		g.addOption("Maximum number of slaves", "underperformersCount")
+			.addValue("Default", 7).showTextBox();
+		node.append(g.render());
+
+		V.nextButton = "Back";
+		V.nextLink = "Future Society";
+
+		return node;
+	}
+
+	return {
+		anyVectorsDefined,
+		slaveConformancePassage,
+	};
+}();