diff --git a/FCHost/fchost/fchost_handler.cc b/FCHost/fchost/fchost_handler.cc
index 367b5f8ac471c536ec587b85929e140389934e91..b3c8cd2808282618eb3a8a700324148139fb94c8 100644
--- a/FCHost/fchost/fchost_handler.cc
+++ b/FCHost/fchost/fchost_handler.cc
@@ -190,10 +190,18 @@ bool FCHostHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
 {
 	CEF_REQUIRE_UI_THREAD();
 
-	if (event.type == cef_key_event_type_t::KEYEVENT_CHAR)
+	// for nonwindows platforms, we need to know the windows equivalent keycode, because CEF will convert to them for us
+	const int key_f = 0x46;
+	const int key_j = 0x4A;
+	const int key_plus = 0xBB;
+	const int key_numpad_plus = 0x6B;
+	const int key_minus = 0xBD;
+	const int key_numpad_minus = 0x6D;
+
+	if (event.type == cef_key_event_type_t::KEYEVENT_RAWKEYDOWN)
 	{
 		// CTRL+SHIFT+J - bring up the Javascript debugger
-		if ((event.modifiers == (cef_event_flags_t::EVENTFLAG_CONTROL_DOWN | cef_event_flags_t::EVENTFLAG_SHIFT_DOWN)) && event.unmodified_character == 10 /* j? maybe only on windows? whatever */)
+		if ((event.modifiers == (cef_event_flags_t::EVENTFLAG_CONTROL_DOWN | cef_event_flags_t::EVENTFLAG_SHIFT_DOWN)) && event.windows_key_code == key_j)
 		{
 			CefWindowInfo windowInfo;
 			CefBrowserSettings settings;
@@ -204,11 +212,23 @@ bool FCHostHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
 			return true;
 		}
 		// CTRL+F - bring up Find In Page
-		else if (event.modifiers == cef_event_flags_t::EVENTFLAG_CONTROL_DOWN && event.unmodified_character == 6 /* f? maybe only on windows? whatever */)
+		else if (event.modifiers == cef_event_flags_t::EVENTFLAG_CONTROL_DOWN && event.windows_key_code == key_f)
 		{
 			// probably can do this at some point by ripping off code from the full-fat cefclient, but damn there's a lot of it...
 			// return true;
 		}
+		// CTRL++ - zoom in
+		else if (event.modifiers == cef_event_flags_t::EVENTFLAG_CONTROL_DOWN && (event.windows_key_code == key_plus || event.windows_key_code == key_numpad_plus))
+		{
+			double curZoom = browser->GetHost()->GetZoomLevel();
+			browser->GetHost()->SetZoomLevel(curZoom + 1.0);
+		}
+		// CTRL+- - zoom out
+		else if (event.modifiers == cef_event_flags_t::EVENTFLAG_CONTROL_DOWN && (event.windows_key_code == key_minus || event.windows_key_code == key_numpad_minus))
+		{
+			double curZoom = browser->GetHost()->GetZoomLevel();
+			browser->GetHost()->SetZoomLevel(curZoom - 1.0);
+		}
 	}
 
 	return false;
diff --git a/devNotes/Useful JS Function Documentation.txt b/devNotes/Useful JS Function Documentation.txt
index e114b88f4898edd4ad5cb9d5893db1f121c1f6b8..38ab1c21a0884244f72b7f94e28bb459cef52e47 100644
--- a/devNotes/Useful JS Function Documentation.txt	
+++ b/devNotes/Useful JS Function Documentation.txt	
@@ -110,6 +110,12 @@ canHold(slave) - Returns if the slave can use both arms.
 
 canWalk(slave) - Returns if the slave can walk unassisted.
 
+canStand(slave) - Returns if the slave can stand unassisted.
+
+canMove(slave) - Returns if the slave is capable of moving themselves.
+
+isHindered(actor) - Returns if the actor's movement is impeded for any reason.
+
 canTalk(slave) - Returns if the slave can talk.
 
 canDoAnal(slave) - Returns if the slave can currently have anal sex.
@@ -138,9 +144,16 @@ isVegetable(slave) - Returns if the slave is mindbroken.
 
 overpowerCheck(slave, PC) - Returns an integer that represents the chance of a slave overpowering the player.
 
+canLift(actor1, actor2) - Returns if actor2 is capable of picking up and carrying actor1.
+
 heelLength(slave) - Returns the length of a slave's heels should she be wearing any
 
 
+Player Functions:
+
+onBedRest(actor) - Returns if the actor is on mandatory bedrest or just incapable of leaving their bed. (Mostly for player use.)
+
+
 Display Functions:
 
 properTitle() - Returns the player's proper title. (customTitle, Sir, Ma'am)
diff --git a/devNotes/colorCSS.txt b/devNotes/colorCSS.txt
index 64f6398d673e407a86100861e0bcac5374aa6c8d..b88fb4511c9c7041cef98df866b1cdac65665857 100644
--- a/devNotes/colorCSS.txt
+++ b/devNotes/colorCSS.txt
@@ -47,6 +47,10 @@ FETISH
 .fetish.loss - coral
 .fetish.inc - lightsalmon
 
+LIBIDO
+.libido.inc - violet
+.libido.dec - khaki
+
 INTELLIGENCE (WIP)
 .intelligent - deepskyblue
 .stupid - orange
diff --git a/devNotes/sugarcube stuff/sugarcube-fc-changes.patch b/devNotes/sugarcube stuff/sugarcube-fc-changes.patch
index 8fbf0a2439de9a5353e4bc9a3de53ac7ce31e675..8464ba441abca96cc6ef302589b49799981d0136 100644
--- a/devNotes/sugarcube stuff/sugarcube-fc-changes.patch	
+++ b/devNotes/sugarcube stuff/sugarcube-fc-changes.patch	
@@ -27,7 +27,7 @@ index 94f98a2..3dcb81a 100644
  
  [SugarCube](http://www.motoslave.net/sugarcube/) is a free (gratis and libre) story format for [Twine/Twee](http://twinery.org/).
 diff --git a/build.js b/build.js
-index 955b617..acd80f5 100644
+index 4d90705..79058a1 100644
 --- a/build.js
 +++ b/build.js
 @@ -30,6 +30,7 @@ const CONFIG = {
@@ -340,7 +340,7 @@ index bd211cd..712e9e1 100644
  	}
  
 diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js
-index 7383bd0..af57d75 100644
+index 610e3fe..c35b458 100644
 --- a/src/macros/macrolib.js
 +++ b/src/macros/macrolib.js
 @@ -90,7 +90,7 @@
diff --git a/devTools/javaSanityCheck/ignoredVariables b/devTools/javaSanityCheck/ignoredVariables
index 86455f56c5a0196893ec52dededc2057eb3e4350..fcfd3191353fbfa8881812b125ac5d47d6744d19 100644
--- a/devTools/javaSanityCheck/ignoredVariables
+++ b/devTools/javaSanityCheck/ignoredVariables
@@ -3,98 +3,33 @@
 AGrowth
 #
 MIN
-brokenSlaves
-toString
-toFixed
-LivingRule
-PersonalAttention
-marriages
-weeksLeft
-normalOvaMin
-normalOvaMax
-rejects
-expCheck
-interactionLink
-minDomActionDelay
-upgrade
 W;O;L
-FSGenderFundamentalistResearch;FSPaternalistResearch;FSDegradationistResearch;FSBodyPuristResearch;FSMaturityPreferentialistResearch;FSPastoralistResearch;FSPhysicalIdealistResearch;FSRepopulationFocusResearch
 0
-maleSurnamePoolSelector
-nationalityPoolSelector
-Him
-anCup
-linkHandlers
-op
-unborn
-facilityRemove
-boobsWombVolume;emptyDefaultRule;rule;removeImplant;changeImplant
-assign;commit;
-Std
-htmlFor
-Possessive;PossessivePronoun
-whip;burn;cutting;chain;exotic
-firstChild
-true
-missingLegs;missingArms;
-toy
-plural
-bimboMaleNames
-canGrow;canImplant;implantError;growOrgan;removeOrgan
-totalChildTime
-toStringExt
-northAmericaNationalities;middleEastNationalities;africaNationalities;australiaNationalities
-surnamePoolSelector
-assignmentVisible
-attendingClasses;confinement;gloryHole;milking;publicService;sexualServitude;servitude
-cx
-menialTrades;menialTransfer;fuckdollsTransfer;menialBioreactorsTransfer;menialRetirement;slaveMod;slaveSurgery;school;citizenOrphanage;environment;privateOrphanage;schoolBacking;personalLivingExpenses;PCtraining;PCmedical;PCskills;stocksTraded;fines;stocks;concubine;slavesViewOfPC;vignette;prestigiousSlave;publicServantClub;architecture;PCappearance;PCactions;PCRelationships;SlaveRelationships;multiplier
-REReductionCheckinIDs
-modestClothes
-gangCriminalPool;militaryCriminalPool;whiteCollarCriminalPool;pettyCriminalPool
-multiple;selectedOptions;
-sluttyClothes
-naturalNippleColors
-POSITIVE
-assistantPronouns;marketAssistantPronouns;ai
-bodyDesire
-Assistant;Extra1
-ArcologyNamesCummunism;ArcologyNamesIncestFetishist
-royalNationalities
-hostageGiveIn
-r
-SlaveSummaryFiler
-sacrificeType
-cellName;BaseCell;cellPath;Section;Building;ground;code
-Upkeep;SexSlaveCount;GSP;Rep
-debugModeCustomFunction
+#re-added below 11/14
+############################
+#corp
+perUnit
+DivLegal
+DivWhoreDev
+acquire
+#tabs#######################
+IntroSummary
+Assistant
+############################
 societyChanged
+totalMerc
+securityHQ
+waterwayTime
+dickSize
+bodyDesire
+Extra1
 newModelUI
 seeArcology
 dinnerParty
-showAppraisal
-IntroSummary
-two
-totalMerc
-hasFoughtMajorBattleOnce
-barren
+badOutcome
 RESSMuscles
-# PC
-criticalDamage
-marriage
-lovers
-FWBs
-BFFs
-friends
-likes
-dislikes
-hates
-loathes
-obsession
-# corporation
-canFoundCorporation;startingPrice;maintenanceSlaves;room;slaveRevenue;divisionLedger;freeDevelopment;developmentCost;maintenanceCategory;corporate;roll;divisionCategories;divisionCategoriesList;endweek;hasDividend;hasPayout;perUnit;acquire;DivLegal;DivWhoreDev
-# porn
-focusedViewershipFactor
-unfocusedViewershipFactor
-viewershipSoakingFactor
-muscle;incest
+royalNationalities
+tempEventToggle
+showAppraisal
+Him
+sacrificeType
\ No newline at end of file
diff --git a/devTools/tweeGo/storyFormats/sugarcube-2/format.js b/devTools/tweeGo/storyFormats/sugarcube-2/format.js
index be4dc1a85cc79b715e119bef7cf41fa60a1307d7..ab44b1aa724dea29af67136191d42d4362ea39a5 100644
--- a/devTools/tweeGo/storyFormats/sugarcube-2/format.js
+++ b/devTools/tweeGo/storyFormats/sugarcube-2/format.js
@@ -1 +1 @@
-window.storyFormat({"name":"SugarCube","version":"2.33.2","description":"A full featured, highly customizable story format.  See its <a href=\"http://www.motoslave.net/sugarcube/2/#documentation\" target=\"_blank\">documentation</a>.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"<!DOCTYPE html>\n<html data-init=\"no-js\">\n<head>\n<meta charset=\"UTF-8\" />\n<title>{{STORY_NAME}}</title>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<!--\n\nSugarCube (v2.33.2): A free (gratis and libre) story format.\n\nCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-->\n<script id=\"script-libraries\" type=\"text/javascript\">\nif(document.head&&document.addEventListener&&document.querySelector&&Object.create&&Object.freeze&&JSON){document.documentElement.setAttribute(\"data-init\", \"loading\");\n/*! @source http://purl.eligrey.com/github/classList.js/blob/1.2.20171210/classList.js */\n\"document\"in self&&(\"classList\"in document.createElement(\"_\")&&(!document.createElementNS||\"classList\"in document.createElementNS(\"http://www.w3.org/2000/svg\",\"g\"))||!function(t){\"use strict\";if(\"Element\"in t){var e=\"classList\",n=\"prototype\",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\\s+|\\s+$/g,\"\")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(\"\"===e)throw new c(\"SYNTAX_ERR\",\"The token must not be empty.\");if(/\\s/.test(e))throw new c(\"INVALID_CHARACTER_ERR\",\"The token must not contain space characters.\");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute(\"class\")||\"\"),n=e?e.split(/\\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute(\"class\",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+\"\")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+\"\",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+\"\",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&\"remove\":e!==!1&&\"add\";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+\"\");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(\" \")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){\"use strict\";var t=document.createElement(\"_\");if(t.classList.add(\"c1\",\"c2\"),!t.classList.contains(\"c2\")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e(\"add\"),e(\"remove\")}if(t.classList.toggle(\"c3\",!1),t.classList.contains(\"c3\")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}\"replace\"in document.createElement(\"_\").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(\" \"),i=n.indexOf(t+\"\");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());\n/*!\n * https://github.com/es-shims/es5-shim\n * @license es5-shim Copyright 2009-2020 by contributors, MIT License\n * see https://github.com/es-shims/es5-shim/blob/v4.5.14/LICENSE\n */\n(function(t,r){\"use strict\";if(typeof define===\"function\"&&define.amd){define(r)}else if(typeof exports===\"object\"){module.exports=r()}else{t.returnExports=r()}})(this,function(){var t=Array;var r=t.prototype;var e=Object;var n=e.prototype;var i=Function;var a=i.prototype;var o=String;var f=o.prototype;var u=Number;var l=u.prototype;var s=r.slice;var c=r.splice;var v=r.push;var h=r.unshift;var p=r.concat;var y=r.join;var d=a.call;var g=a.apply;var w=Math.max;var b=Math.min;var T=n.toString;var m=typeof Symbol===\"function\"&&typeof Symbol.toStringTag===\"symbol\";var D;var S=Function.prototype.toString,x=/^\\s*class /,O=function isES6ClassFn(t){try{var r=S.call(t);var e=r.replace(/\\/\\/.*\\n/g,\"\");var n=e.replace(/\\/\\*[.\\s\\S]*\\*\\//g,\"\");var i=n.replace(/\\n/gm,\" \").replace(/ {2}/g,\" \");return x.test(i)}catch(a){return false}},E=function tryFunctionObject(t){try{if(O(t)){return false}S.call(t);return true}catch(r){return false}},j=\"[object Function]\",I=\"[object GeneratorFunction]\",D=function isCallable(t){if(!t){return false}if(typeof t!==\"function\"&&typeof t!==\"object\"){return false}if(m){return E(t)}if(O(t)){return false}var r=T.call(t);return r===j||r===I};var M;var U=RegExp.prototype.exec,$=function tryRegexExec(t){try{U.call(t);return true}catch(r){return false}},F=\"[object RegExp]\";M=function isRegex(t){if(typeof t!==\"object\"){return false}return m?$(t):T.call(t)===F};var N;var C=String.prototype.valueOf,k=function tryStringObject(t){try{C.call(t);return true}catch(r){return false}},A=\"[object String]\";N=function isString(t){if(typeof t===\"string\"){return true}if(typeof t!==\"object\"){return false}return m?k(t):T.call(t)===A};var R=e.defineProperty&&function(){try{var t={};e.defineProperty(t,\"x\",{enumerable:false,value:t});for(var r in t){return false}return t.x===t}catch(n){return false}}();var P=function(t){var r;if(R){r=function(t,r,n,i){if(!i&&r in t){return}e.defineProperty(t,r,{configurable:true,enumerable:false,writable:true,value:n})}}else{r=function(t,r,e,n){if(!n&&r in t){return}t[r]=e}}return function defineProperties(e,n,i){for(var a in n){if(t.call(n,a)){r(e,a,n[a],i)}}}}(n.hasOwnProperty);var J=function isPrimitive(t){var r=typeof t;return t===null||r!==\"object\"&&r!==\"function\"};var Y=u.isNaN||function isActualNaN(t){return t!==t};var z={ToInteger:function ToInteger(t){var r=+t;if(Y(r)){r=0}else if(r!==0&&r!==1/0&&r!==-(1/0)){r=(r>0||-1)*Math.floor(Math.abs(r))}return r},ToPrimitive:function ToPrimitive(t){var r,e,n;if(J(t)){return t}e=t.valueOf;if(D(e)){r=e.call(t);if(J(r)){return r}}n=t.toString;if(D(n)){r=n.call(t);if(J(r)){return r}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError(\"can't convert \"+t+\" to object\")}return e(t)},ToUint32:function ToUint32(t){return t>>>0}};var Z=function Empty(){};P(a,{bind:function bind(t){var r=this;if(!D(r)){throw new TypeError(\"Function.prototype.bind called on incompatible \"+r)}var n=s.call(arguments,1);var a;var o=function(){if(this instanceof a){var i=g.call(r,this,p.call(n,s.call(arguments)));if(e(i)===i){return i}return this}else{return g.call(r,t,p.call(n,s.call(arguments)))}};var f=w(0,r.length-n.length);var u=[];for(var l=0;l<f;l++){v.call(u,\"$\"+l)}a=i(\"binder\",\"return function (\"+y.call(u,\",\")+\"){ return binder.apply(this, arguments); }\")(o);if(r.prototype){Z.prototype=r.prototype;a.prototype=new Z;Z.prototype=null}return a}});var G=d.bind(n.hasOwnProperty);var H=d.bind(n.toString);var W=d.bind(s);var B=g.bind(s);if(typeof document===\"object\"&&document&&document.documentElement){try{W(document.documentElement.childNodes)}catch(X){var L=W;var q=B;W=function arraySliceIE(t){var r=[];var e=t.length;while(e-- >0){r[e]=t[e]}return q(r,L(arguments,1))};B=function arraySliceApplyIE(t,r){return q(W(t),r)}}}var K=d.bind(f.slice);var Q=d.bind(f.split);var V=d.bind(f.indexOf);var _=d.bind(v);var tt=d.bind(n.propertyIsEnumerable);var rt=d.bind(r.sort);var et=t.isArray||function isArray(t){return H(t)===\"[object Array]\"};var nt=[].unshift(0)!==1;P(r,{unshift:function(){h.apply(this,arguments);return this.length}},nt);P(t,{isArray:et});var it=e(\"a\");var at=it[0]!==\"a\"||!(0 in it);var ot=function properlyBoxed(t){var r=true;var e=true;var n=false;if(t){try{t.call(\"foo\",function(t,e,n){if(typeof n!==\"object\"){r=false}});t.call([1],function(){\"use strict\";e=typeof this===\"string\"},\"x\")}catch(i){n=true}}return!!t&&!n&&r&&e};P(r,{forEach:function forEach(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=-1;var i=z.ToUint32(e.length);var a;if(arguments.length>1){a=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.forEach callback must be a function\")}while(++n<i){if(n in e){if(typeof a===\"undefined\"){t(e[n],n,r)}else{t.call(a,e[n],n,r)}}}}},!ot(r.forEach));P(r,{map:function map(r){var e=z.ToObject(this);var n=at&&N(this)?Q(this,\"\"):e;var i=z.ToUint32(n.length);var a=t(i);var o;if(arguments.length>1){o=arguments[1]}if(!D(r)){throw new TypeError(\"Array.prototype.map callback must be a function\")}for(var f=0;f<i;f++){if(f in n){if(typeof o===\"undefined\"){a[f]=r(n[f],f,e)}else{a[f]=r.call(o,n[f],f,e)}}}return a}},!ot(r.map));P(r,{filter:function filter(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.filter callback must be a function\")}for(var f=0;f<n;f++){if(f in e){a=e[f];if(typeof o===\"undefined\"?t(a,f,r):t.call(o,a,f,r)){_(i,a)}}}return i}},!ot(r.filter));P(r,{every:function every(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.every callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&!(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return false}}return true}},!ot(r.every));P(r,{some:function some(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.some callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return true}}return false}},!ot(r.some));var ft=false;if(r.reduce){ft=typeof r.reduce.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduce:function reduce(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduce callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduce of empty array with no initial value\")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in e){a=e[i++];break}if(++i>=n){throw new TypeError(\"reduce of empty array with no initial value\")}}while(true)}for(;i<n;i++){if(i in e){a=t(a,e[i],i,r)}}return a}},!ft);var ut=false;if(r.reduceRight){ut=typeof r.reduceRight.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduceRight:function reduceRight(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduceRight callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduceRight of empty array with no initial value\")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in e){i=e[a--];break}if(--a<0){throw new TypeError(\"reduceRight of empty array with no initial value\")}}while(true)}if(a<0){return i}do{if(a in e){i=t(i,e[a],a,r)}}while(a--);return i}},!ut);var lt=r.indexOf&&[0,1].indexOf(1,2)!==-1;P(r,{indexOf:function indexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=0;if(arguments.length>1){n=z.ToInteger(arguments[1])}n=n>=0?n:w(0,e+n);for(;n<e;n++){if(n in r&&r[n]===t){return n}}return-1}},lt);var st=r.lastIndexOf&&[0,1].lastIndexOf(0,-3)!==-1;P(r,{lastIndexOf:function lastIndexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=e-1;if(arguments.length>1){n=b(n,z.ToInteger(arguments[1]))}n=n>=0?n:e-Math.abs(n);for(;n>=0;n--){if(n in r&&t===r[n]){return n}}return-1}},st);var ct=function(){var t=[1,2];var r=t.splice();return t.length===2&&et(r)&&r.length===0}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}else{return c.apply(this,arguments)}}},!ct);var vt=function(){var t={};r.splice.call(t,0,0,1);return t.length===1}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}var e=arguments;this.length=w(z.ToInteger(this.length),0);if(arguments.length>0&&typeof r!==\"number\"){e=W(arguments);if(e.length<2){_(e,this.length-t)}else{e[1]=z.ToInteger(r)}}return c.apply(this,e)}},!vt);var ht=function(){var r=new t(1e5);r[8]=\"x\";r.splice(1,1);return r.indexOf(\"x\")===7}();var pt=function(){var t=256;var r=[];r[t]=\"a\";r.splice(t+1,0,\"b\");return r[t]===\"a\"}();P(r,{splice:function splice(t,r){var e=z.ToObject(this);var n=[];var i=z.ToUint32(e.length);var a=z.ToInteger(t);var f=a<0?w(i+a,0):b(a,i);var u=arguments.length===0?0:arguments.length===1?i-f:b(w(z.ToInteger(r),0),i-f);var l=0;var s;while(l<u){s=o(f+l);if(G(e,s)){n[l]=e[s]}l+=1}var c=W(arguments,2);var v=c.length;var h;if(v<u){l=f;var p=i-u;while(l<p){s=o(l+u);h=o(l+v);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l+=1}l=i;var y=i-u+v;while(l>y){delete e[l-1];l-=1}}else if(v>u){l=i-u;while(l>f){s=o(l+u-1);h=o(l+v-1);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l-=1}}l=f;for(var d=0;d<c.length;++d){e[l]=c[d];l+=1}e.length=i-u+v;return n}},!ht||!pt);var yt=r.join;var dt;try{dt=Array.prototype.join.call(\"123\",\",\")!==\"1,2,3\"}catch(X){dt=true}if(dt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(N(this)?Q(this,\"\"):this,r)}},dt)}var gt=[1,2].join(undefined)!==\"1,2\";if(gt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(this,r)}},gt)}var wt=function push(t){var r=z.ToObject(this);var e=z.ToUint32(r.length);var n=0;while(n<arguments.length){r[e+n]=arguments[n];n+=1}r.length=e+n;return e+n};var bt=function(){var t={};var r=Array.prototype.push.call(t,undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:function push(t){if(et(this)){return v.apply(this,arguments)}return wt.apply(this,arguments)}},bt);var Tt=function(){var t=[];var r=t.push(undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:wt},Tt);P(r,{slice:function(t,r){var e=N(this)?Q(this,\"\"):this;return B(e,arguments)}},at);var mt=function(){try{[1,2].sort(null)}catch(t){try{[1,2].sort({})}catch(r){return false}}return true}();var Dt=function(){try{[1,2].sort(/a/);return false}catch(t){}return true}();var St=function(){try{[1,2].sort(undefined);return true}catch(t){}return false}();P(r,{sort:function sort(t){if(typeof t===\"undefined\"){return rt(this)}if(!D(t)){throw new TypeError(\"Array.prototype.sort callback must be a function\")}return rt(this,t)}},mt||!St||!Dt);var xt=!tt({toString:null},\"toString\");var Ot=tt(function(){},\"prototype\");var Et=!G(\"x\",\"0\");var jt=function(t){var r=t.constructor;return r&&r.prototype===t};var It={$applicationCache:true,$console:true,$external:true,$frame:true,$frameElement:true,$frames:true,$innerHeight:true,$innerWidth:true,$onmozfullscreenchange:true,$onmozfullscreenerror:true,$outerHeight:true,$outerWidth:true,$pageXOffset:true,$pageYOffset:true,$parent:true,$scrollLeft:true,$scrollTop:true,$scrollX:true,$scrollY:true,$self:true,$webkitIndexedDB:true,$webkitStorageInfo:true,$window:true,$width:true,$height:true,$top:true,$localStorage:true};var Mt=function(){if(typeof window===\"undefined\"){return false}for(var t in window){try{if(!It[\"$\"+t]&&G(window,t)&&window[t]!==null&&typeof window[t]===\"object\"){jt(window[t])}}catch(r){return true}}return false}();var Ut=function(t){if(typeof window===\"undefined\"||!Mt){return jt(t)}try{return jt(t)}catch(r){return false}};var $t=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"];var Ft=$t.length;var Nt=function isArguments(t){return H(t)===\"[object Arguments]\"};var Ct=function isArguments(t){return t!==null&&typeof t===\"object\"&&typeof t.length===\"number\"&&t.length>=0&&!et(t)&&D(t.callee)};var kt=Nt(arguments)?Nt:Ct;P(e,{keys:function keys(t){var r=D(t);var e=kt(t);var n=t!==null&&typeof t===\"object\";var i=n&&N(t);if(!n&&!r&&!e){throw new TypeError(\"Object.keys called on a non-object\")}var a=[];var f=Ot&&r;if(i&&Et||e){for(var u=0;u<t.length;++u){_(a,o(u))}}if(!e){for(var l in t){if(!(f&&l===\"prototype\")&&G(t,l)){_(a,o(l))}}}if(xt){var s=Ut(t);for(var c=0;c<Ft;c++){var v=$t[c];if(!(s&&v===\"constructor\")&&G(t,v)){_(a,v)}}}return a}});var At=e.keys&&function(){return e.keys(arguments).length===2}(1,2);var Rt=e.keys&&function(){var t=e.keys(arguments);return arguments.length!==1||t.length!==1||t[0]!==1}(1);var Pt=e.keys;P(e,{keys:function keys(t){if(kt(t)){return Pt(W(t))}else{return Pt(t)}}},!At||Rt);var Jt=new Date(-0xc782b5b342b24).getUTCMonth()!==0;var Yt=new Date(-0x55d318d56a724);var zt=new Date(14496624e5);var Zt=Yt.toUTCString()!==\"Mon, 01 Jan -45875 11:59:59 GMT\";var Gt;var Ht;var Wt=Yt.getTimezoneOffset();if(Wt<-720){Gt=Yt.toDateString()!==\"Tue Jan 02 -45875\";Ht=!/^Thu Dec 10 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}else{Gt=Yt.toDateString()!==\"Mon Jan 01 -45875\";Ht=!/^Wed Dec 09 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}var Bt=d.bind(Date.prototype.getFullYear);var Xt=d.bind(Date.prototype.getMonth);var Lt=d.bind(Date.prototype.getDate);var qt=d.bind(Date.prototype.getUTCFullYear);var Kt=d.bind(Date.prototype.getUTCMonth);var Qt=d.bind(Date.prototype.getUTCDate);var Vt=d.bind(Date.prototype.getUTCDay);var _t=d.bind(Date.prototype.getUTCHours);var tr=d.bind(Date.prototype.getUTCMinutes);var rr=d.bind(Date.prototype.getUTCSeconds);var er=d.bind(Date.prototype.getUTCMilliseconds);var nr=[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];var ir=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];var ar=function daysInMonth(t,r){return Lt(new Date(r,t,0))};P(Date.prototype,{getFullYear:function getFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);if(t<0&&Xt(this)>11){return t+1}return t},getMonth:function getMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);if(t<0&&r>11){return 0}return r},getDate:function getDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);var e=Lt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e},getUTCFullYear:function getUTCFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);if(t<0&&Kt(this)>11){return t+1}return t},getUTCMonth:function getUTCMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);if(t<0&&r>11){return 0}return r},getUTCDate:function getUTCDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);var e=Qt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e}},Jt);P(Date.prototype,{toUTCString:function toUTCString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Vt(this);var r=Qt(this);var e=Kt(this);var n=qt(this);var i=_t(this);var a=tr(this);var o=rr(this);return nr[t]+\", \"+(r<10?\"0\"+r:r)+\" \"+ir[e]+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"}},Jt||Zt);P(Date.prototype,{toDateString:function toDateString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n}},Jt||Gt);if(Jt||Ht){Date.prototype.toString=function toString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();var i=this.getHours();var a=this.getMinutes();var o=this.getSeconds();var f=this.getTimezoneOffset();var u=Math.floor(Math.abs(f)/60);var l=Math.floor(Math.abs(f)%60);return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"+(f>0?\"-\":\"+\")+(u<10?\"0\"+u:u)+(l<10?\"0\"+l:l)};if(R){e.defineProperty(Date.prototype,\"toString\",{configurable:true,enumerable:false,writable:true})}}var or=-621987552e5;var fr=\"-000001\";var ur=Date.prototype.toISOString&&new Date(or).toISOString().indexOf(fr)===-1;var lr=Date.prototype.toISOString&&new Date(-1).toISOString()!==\"1969-12-31T23:59:59.999Z\";var sr=d.bind(Date.prototype.getTime);P(Date.prototype,{toISOString:function toISOString(){if(!isFinite(this)||!isFinite(sr(this))){throw new RangeError(\"Date.prototype.toISOString called on non-finite value.\")}var t=qt(this);var r=Kt(this);t+=Math.floor(r/12);r=(r%12+12)%12;var e=[r+1,Qt(this),_t(this),tr(this),rr(this)];t=(t<0?\"-\":t>9999?\"+\":\"\")+K(\"00000\"+Math.abs(t),0<=t&&t<=9999?-4:-6);for(var n=0;n<e.length;++n){e[n]=K(\"00\"+e[n],-2)}return t+\"-\"+W(e,0,2).join(\"-\")+\"T\"+W(e,2).join(\":\")+\".\"+K(\"000\"+er(this),-3)+\"Z\"}},ur||lr);var cr=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(or).toJSON().indexOf(fr)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!cr){Date.prototype.toJSON=function toJSON(t){var r=e(this);var n=z.ToPrimitive(r);if(typeof n===\"number\"&&!isFinite(n)){return null}var i=r.toISOString;if(!D(i)){throw new TypeError(\"toISOString property is not callable\")}return i.call(r)}}var vr=Date.parse(\"+033658-09-27T01:46:40.000Z\")===1e15;var hr=!isNaN(Date.parse(\"2012-04-04T24:00:00.500Z\"))||!isNaN(Date.parse(\"2012-11-31T23:59:59.000Z\"))||!isNaN(Date.parse(\"2012-12-31T23:59:60.000Z\"));var pr=isNaN(Date.parse(\"2000-01-01T00:00:00.000Z\"));if(pr||hr||!vr){var yr=Math.pow(2,31)-1;var dr=Y(new Date(1970,0,1,0,0,0,yr+1).getTime());Date=function(t){var r=function Date(e,n,i,a,f,u,l){var s=arguments.length;var c;if(this instanceof t){var v=u;var h=l;if(dr&&s>=7&&l>yr){var p=Math.floor(l/yr)*yr;var y=Math.floor(p/1e3);v+=y;h-=y*1e3}c=s===1&&o(e)===e?new t(r.parse(e)):s>=7?new t(e,n,i,a,f,v,h):s>=6?new t(e,n,i,a,f,v):s>=5?new t(e,n,i,a,f):s>=4?new t(e,n,i,a):s>=3?new t(e,n,i):s>=2?new t(e,n):s>=1?new t(e instanceof t?+e:e):new t}else{c=t.apply(this,arguments)}if(!J(c)){P(c,{constructor:r},true)}return c};var e=new RegExp(\"^\"+\"(\\\\d{4}|[+-]\\\\d{6})\"+\"(?:-(\\\\d{2})\"+\"(?:-(\\\\d{2})\"+\"(?:\"+\"T(\\\\d{2})\"+\":(\\\\d{2})\"+\"(?:\"+\":(\\\\d{2})\"+\"(?:(\\\\.\\\\d{1,}))?\"+\")?\"+\"(\"+\"Z|\"+\"(?:\"+\"([-+])\"+\"(\\\\d{2})\"+\":(\\\\d{2})\"+\")\"+\")?)?)?)?\"+\"$\");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function dayFromMonth(t,r){var e=r>1?1:0;return n[r]+Math.floor((t-1969+e)/4)-Math.floor((t-1901+e)/100)+Math.floor((t-1601+e)/400)+365*(t-1970)};var a=function toUTC(r){var e=0;var n=r;if(dr&&n>yr){var i=Math.floor(n/yr)*yr;var a=Math.floor(i/1e3);e+=a;n-=a*1e3}return u(new t(1970,0,1,0,0,e,n))};for(var f in t){if(G(t,f)){r[f]=t[f]}}P(r,{now:t.now,UTC:t.UTC},true);r.prototype=t.prototype;P(r.prototype,{constructor:r},true);var l=function parse(r){var n=e.exec(r);if(n){var o=u(n[1]),f=u(n[2]||1)-1,l=u(n[3]||1)-1,s=u(n[4]||0),c=u(n[5]||0),v=u(n[6]||0),h=Math.floor(u(n[7]||0)*1e3),p=Boolean(n[4]&&!n[8]),y=n[9]===\"-\"?1:-1,d=u(n[10]||0),g=u(n[11]||0),w;var b=c>0||v>0||h>0;if(s<(b?24:25)&&c<60&&v<60&&h<1e3&&f>-1&&f<12&&d<24&&g<60&&l>-1&&l<i(o,f+1)-i(o,f)){w=((i(o,f)+l)*24+s+d*y)*60;w=((w+c+g*y)*60+v)*1e3+h;if(p){w=a(w)}if(-864e13<=w&&w<=864e13){return w}}return NaN}return t.parse.apply(this,arguments)};P(r,{parse:l});return r}(Date)}if(!Date.now){Date.now=function now(){return(new Date).getTime()}}var gr=l.toFixed&&(8e-5.toFixed(3)!==\"0.000\"||.9.toFixed(0)!==\"1\"||1.255.toFixed(2)!==\"1.25\"||(1000000000000000128).toFixed(0)!==\"1000000000000000128\");var wr={base:1e7,size:6,data:[0,0,0,0,0,0],multiply:function multiply(t,r){var e=-1;var n=r;while(++e<wr.size){n+=t*wr.data[e];wr.data[e]=n%wr.base;n=Math.floor(n/wr.base)}},divide:function divide(t){var r=wr.size;var e=0;while(--r>=0){e+=wr.data[r];wr.data[r]=Math.floor(e/t);e=e%t*wr.base}},numToString:function numToString(){var t=wr.size;var r=\"\";while(--t>=0){if(r!==\"\"||t===0||wr.data[t]!==0){var e=o(wr.data[t]);if(r===\"\"){r=e}else{r+=K(\"0000000\",0,7-e.length)+e}}}return r},pow:function pow(t,r,e){return r===0?e:r%2===1?pow(t,r-1,e*t):pow(t*t,r/2,e)},log:function log(t){var r=0;var e=t;while(e>=4096){r+=12;e/=4096}while(e>=2){r+=1;e/=2}return r}};var br=function toFixed(t){var r,e,n,i,a,f,l,s;r=u(t);r=Y(r)?0:Math.floor(r);if(r<0||r>20){throw new RangeError(\"Number.toFixed called with invalid number of decimals\")}e=u(this);if(Y(e)){return\"NaN\"}if(e<=-1e21||e>=1e21){return o(e)}n=\"\";if(e<0){n=\"-\";e=-e}i=\"0\";if(e>1e-21){a=wr.log(e*wr.pow(2,69,1))-69;f=a<0?e*wr.pow(2,-a,1):e/wr.pow(2,a,1);f*=4503599627370496;a=52-a;if(a>0){wr.multiply(0,f);l=r;while(l>=7){wr.multiply(1e7,0);l-=7}wr.multiply(wr.pow(10,l,1),0);l=a-1;while(l>=23){wr.divide(1<<23);l-=23}wr.divide(1<<l);wr.multiply(1,1);wr.divide(2);i=wr.numToString()}else{wr.multiply(0,f);wr.multiply(1<<-a,0);i=wr.numToString()+K(\"0.00000000000000000000\",2,2+r)}}if(r>0){s=i.length;if(s<=r){i=n+K(\"0.0000000000000000000\",0,r-s+2)+i}else{i=n+K(i,0,s-r)+\".\"+K(i,s-r)}}else{i=n+i}return i};P(l,{toFixed:br},gr);var Tr=function(){try{return 1..toPrecision(undefined)===\"1\"}catch(t){return true}}();var mr=l.toPrecision;P(l,{toPrecision:function toPrecision(t){return typeof t===\"undefined\"?mr.call(this):mr.call(this,t)}},Tr);if(\"ab\".split(/(?:ab)*/).length!==2||\".\".split(/(.?)(.?)/).length!==4||\"tesst\".split(/(s)*/)[1]===\"t\"||\"test\".split(/(?:)/,-1).length!==4||\"\".split(/.?/).length||\".\".split(/()()/).length>1){(function(){var t=typeof/()??/.exec(\"\")[1]===\"undefined\";var r=Math.pow(2,32)-1;f.split=function(e,n){var i=String(this);if(typeof e===\"undefined\"&&n===0){return[]}if(!M(e)){return Q(this,e,n)}var a=[];var o=(e.ignoreCase?\"i\":\"\")+(e.multiline?\"m\":\"\")+(e.unicode?\"u\":\"\")+(e.sticky?\"y\":\"\"),f=0,u,l,s,c;var h=new RegExp(e.source,o+\"g\");if(!t){u=new RegExp(\"^\"+h.source+\"$(?!\\\\s)\",o)}var p=typeof n===\"undefined\"?r:z.ToUint32(n);l=h.exec(i);while(l){s=l.index+l[0].length;if(s>f){_(a,K(i,f,l.index));if(!t&&l.length>1){l[0].replace(u,function(){for(var t=1;t<arguments.length-2;t++){if(typeof arguments[t]===\"undefined\"){l[t]=void 0}}})}if(l.length>1&&l.index<i.length){v.apply(a,W(l,1))}c=l[0].length;f=s;if(a.length>=p){break}}if(h.lastIndex===l.index){h.lastIndex++}l=h.exec(i)}if(f===i.length){if(c||!h.test(\"\")){_(a,\"\")}}else{_(a,K(i,f))}return a.length>p?W(a,0,p):a}})()}else if(\"0\".split(void 0,0).length){f.split=function split(t,r){if(typeof t===\"undefined\"&&r===0){return[]}return Q(this,t,r)}}var Dr=f.replace;var Sr=function(){var t=[];\"x\".replace(/x(.)?/g,function(r,e){_(t,e)});return t.length===1&&typeof t[0]===\"undefined\"}();if(!Sr){f.replace=function replace(t,r){var e=D(r);var n=M(t)&&/\\)[*?]/.test(t.source);if(!e||!n){return Dr.call(this,t,r)}else{var i=function(e){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(e)||[];t.lastIndex=i;_(a,arguments[n-2],arguments[n-1]);return r.apply(this,a)};return Dr.call(this,t,i)}}}var xr=f.substr;var Or=\"\".substr&&\"0b\".substr(-1)!==\"b\";P(f,{substr:function substr(t,r){var e=t;if(t<0){e=w(this.length+t,0)}return xr.call(this,e,r)}},Or);var Er=\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\"+\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\"+\"\\u2029\\ufeff\";var jr=\"\\u200b\";var Ir=\"[\"+Er+\"]\";var Mr=new RegExp(\"^\"+Ir+Ir+\"*\");var Ur=new RegExp(Ir+Ir+\"*$\");var $r=f.trim&&(Er.trim()||!jr.trim());P(f,{trim:function trim(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}return o(this).replace(Mr,\"\").replace(Ur,\"\")}},$r);var Fr=d.bind(String.prototype.trim);var Nr=f.lastIndexOf&&\"abc\\u3042\\u3044\".lastIndexOf(\"\\u3042\\u3044\",2)!==-1;P(f,{lastIndexOf:function lastIndexOf(t){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var r=o(this);var e=o(t);var n=arguments.length>1?u(arguments[1]):NaN;var i=Y(n)?Infinity:z.ToInteger(n);var a=b(w(i,0),r.length);var f=e.length;var l=a+f;while(l>0){l=w(0,l-f);var s=V(K(r,l,a+f),e);if(s!==-1){return l+s}}return-1}},Nr);var Cr=f.lastIndexOf;P(f,{lastIndexOf:function lastIndexOf(t){return Cr.apply(this,arguments)}},f.lastIndexOf.length!==1);if(parseInt(Er+\"08\")!==8||parseInt(Er+\"0x16\")!==22){parseInt=function(t){var r=/^[-+]?0[xX]/;return function parseInt(e,n){if(typeof e===\"symbol\"){\"\"+e}var i=Fr(String(e));var a=u(n)||(r.test(i)?16:10);return t(i,a)}}(parseInt)}if(1/parseFloat(\"-0\")!==-Infinity){parseFloat=function(t){return function parseFloat(r){var e=Fr(String(r));var n=t(e);return n===0&&K(e,0,1)===\"-\"?-0:n}}(parseFloat)}if(String(new RangeError(\"test\"))!==\"RangeError: test\"){var kr=function toString(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var t=this.name;if(typeof t===\"undefined\"){t=\"Error\"}else if(typeof t!==\"string\"){t=o(t)}var r=this.message;if(typeof r===\"undefined\"){r=\"\"}else if(typeof r!==\"string\"){r=o(r)}if(!t){return r}if(!r){return t}return t+\": \"+r};Error.prototype.toString=kr}if(R){var Ar=function(t,r){if(tt(t,r)){var e=Object.getOwnPropertyDescriptor(t,r);if(e.configurable){e.enumerable=false;Object.defineProperty(t,r,e)}}};Ar(Error.prototype,\"message\");if(Error.prototype.message!==\"\"){Error.prototype.message=\"\"}Ar(Error.prototype,\"name\")}if(String(/a/gim)!==\"/a/gim\"){var Rr=function toString(){var t=\"/\"+this.source+\"/\";if(this.global){t+=\"g\"}if(this.ignoreCase){t+=\"i\"}if(this.multiline){t+=\"m\"}return t};RegExp.prototype.toString=Rr}});\n/*!\n * https://github.com/paulmillr/es6-shim\n * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)\n *   and contributors,  MIT License\n * es6-shim: v0.35.4\n * see https://github.com/paulmillr/es6-shim/blob/0.35.4/LICENSE\n * Details and documentation:\n * https://github.com/paulmillr/es6-shim/\n */\n(function(e,t){if(typeof define===\"function\"&&define.amd){define(t)}else if(typeof exports===\"object\"){module.exports=t()}else{e.returnExports=t()}})(this,function(){\"use strict\";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=Object.keys;var o=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var i=function(e){try{e();return false}catch(t){return true}};var a=function valueOrFalseIfThrows(e){try{return e()}catch(t){return false}};var u=o(i);var f=function(){return!i(function(){return Object.defineProperty({},\"x\",{get:function(){}})})};var s=!!Object.defineProperty&&f();var c=function foo(){}.name===\"foo\";var l=Function.call.bind(Array.prototype.forEach);var p=Function.call.bind(Array.prototype.reduce);var v=Function.call.bind(Array.prototype.filter);var y=Function.call.bind(Array.prototype.some);var h=function(e,t,r,n){if(!n&&t in e){return}if(s){Object.defineProperty(e,t,{configurable:true,enumerable:false,writable:true,value:r})}else{e[t]=r}};var b=function(e,t,r){l(n(t),function(n){var o=t[n];h(e,n,o,!!r)})};var g=Function.call.bind(Object.prototype.toString);var d=typeof/abc/===\"function\"?function IsCallableSlow(e){return typeof e===\"function\"&&g(e)===\"[object Function]\"}:function IsCallableFast(e){return typeof e===\"function\"};var m={getter:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}Object.defineProperty(e,t,{configurable:true,enumerable:false,get:r})},proxy:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,{configurable:n.configurable,enumerable:n.enumerable,get:function getKey(){return e[t]},set:function setKey(r){e[t]=r}})},redefine:function(e,t,r){if(s){var n=Object.getOwnPropertyDescriptor(e,t);n.value=r;Object.defineProperty(e,t,n)}else{e[t]=r}},defineByDescriptor:function(e,t,r){if(s){Object.defineProperty(e,t,r)}else if(\"value\"in r){e[t]=r.value}},preserveToString:function(e,t){if(t&&d(t.toString)){h(e,\"toString\",t.toString.bind(t),true)}}};var O=Object.create||function(e,t){var r=function Prototype(){};r.prototype=e;var o=new r;if(typeof t!==\"undefined\"){n(t).forEach(function(e){m.defineByDescriptor(o,e,t[e])})}return o};var w=function(e,t){if(!Object.setPrototypeOf){return false}return a(function(){var r=function Subclass(t){var r=new e(t);Object.setPrototypeOf(r,Subclass.prototype);return r};Object.setPrototypeOf(r,e);r.prototype=O(e.prototype,{constructor:{value:r}});return t(r)})};var j=function(){if(typeof self!==\"undefined\"){return self}if(typeof window!==\"undefined\"){return window}if(typeof global!==\"undefined\"){return global}throw new Error(\"unable to locate global object\")};var S=j();var T=S.isFinite;var I=Function.call.bind(String.prototype.indexOf);var E=Function.apply.bind(Array.prototype.indexOf);var P=Function.call.bind(Array.prototype.concat);var C=Function.call.bind(String.prototype.slice);var M=Function.call.bind(Array.prototype.push);var x=Function.apply.bind(Array.prototype.push);var N=Function.call.bind(Array.prototype.shift);var A=Math.max;var R=Math.min;var _=Math.floor;var k=Math.abs;var L=Math.exp;var F=Math.log;var D=Math.sqrt;var z=Function.call.bind(Object.prototype.hasOwnProperty);var q;var W=function(){};var G=S.Map;var H=G&&G.prototype[\"delete\"];var V=G&&G.prototype.get;var B=G&&G.prototype.has;var U=G&&G.prototype.set;var $=S.Symbol||{};var J=$.species||\"@@species\";var X=Number.isNaN||function isNaN(e){return e!==e};var K=Number.isFinite||function isFinite(e){return typeof e===\"number\"&&T(e)};var Z=d(Math.sign)?Math.sign:function sign(e){var t=Number(e);if(t===0){return t}if(X(t)){return t}return t<0?-1:1};var Y=function log1p(e){var t=Number(e);if(t<-1||X(t)){return NaN}if(t===0||t===Infinity){return t}if(t===-1){return-Infinity}return 1+t-1===0?t:t*(F(1+t)/(1+t-1))};var Q=function isArguments(e){return g(e)===\"[object Arguments]\"};var ee=function isArguments(e){return e!==null&&typeof e===\"object\"&&typeof e.length===\"number\"&&e.length>=0&&g(e)!==\"[object Array]\"&&g(e.callee)===\"[object Function]\"};var te=Q(arguments)?Q:ee;var re={primitive:function(e){return e===null||typeof e!==\"function\"&&typeof e!==\"object\"},string:function(e){return g(e)===\"[object String]\"},regex:function(e){return g(e)===\"[object RegExp]\"},symbol:function(e){return typeof S.Symbol===\"function\"&&typeof e===\"symbol\"}};var ne=function overrideNative(e,t,r){var n=e[t];h(e,t,r,true);m.preserveToString(e[t],n)};var oe=typeof $===\"function\"&&typeof $[\"for\"]===\"function\"&&re.symbol($());var ie=re.symbol($.iterator)?$.iterator:\"_es6-shim iterator_\";if(S.Set&&typeof(new S.Set)[\"@@iterator\"]===\"function\"){ie=\"@@iterator\"}if(!S.Reflect){h(S,\"Reflect\",{},true)}var ae=S.Reflect;var ue=String;var fe=typeof document===\"undefined\"||!document?null:document.all;var se=fe==null?function isNullOrUndefined(e){return e==null}:function isNullOrUndefinedAndNotDocumentAll(e){return e==null&&e!==fe};var ce={Call:function Call(t,r){var n=arguments.length>2?arguments[2]:[];if(!ce.IsCallable(t)){throw new TypeError(t+\" is not a function\")}return e(t,r,n)},RequireObjectCoercible:function(e,t){if(se(e)){throw new TypeError(t||\"Cannot call method on \"+e)}return e},TypeIsObject:function(e){if(e===void 0||e===null||e===true||e===false){return false}return typeof e===\"function\"||typeof e===\"object\"||e===fe},ToObject:function(e,t){return Object(ce.RequireObjectCoercible(e,t))},IsCallable:d,IsConstructor:function(e){return ce.IsCallable(e)},ToInt32:function(e){return ce.ToNumber(e)>>0},ToUint32:function(e){return ce.ToNumber(e)>>>0},ToNumber:function(e){if(g(e)===\"[object Symbol]\"){throw new TypeError(\"Cannot convert a Symbol value to a number\")}return+e},ToInteger:function(e){var t=ce.ToNumber(e);if(X(t)){return 0}if(t===0||!K(t)){return t}return(t>0?1:-1)*_(k(t))},ToLength:function(e){var t=ce.ToInteger(e);if(t<=0){return 0}if(t>Number.MAX_SAFE_INTEGER){return Number.MAX_SAFE_INTEGER}return t},SameValue:function(e,t){if(e===t){if(e===0){return 1/e===1/t}return true}return X(e)&&X(t)},SameValueZero:function(e,t){return e===t||X(e)&&X(t)},IsIterable:function(e){return ce.TypeIsObject(e)&&(typeof e[ie]!==\"undefined\"||te(e))},GetIterator:function(e){if(te(e)){return new q(e,\"value\")}var t=ce.GetMethod(e,ie);if(!ce.IsCallable(t)){throw new TypeError(\"value is not an iterable\")}var r=ce.Call(t,e);if(!ce.TypeIsObject(r)){throw new TypeError(\"bad iterator\")}return r},GetMethod:function(e,t){var r=ce.ToObject(e)[t];if(se(r)){return void 0}if(!ce.IsCallable(r)){throw new TypeError(\"Method not callable: \"+t)}return r},IteratorComplete:function(e){return!!e.done},IteratorClose:function(e,t){var r=ce.GetMethod(e,\"return\");if(r===void 0){return}var n,o;try{n=ce.Call(r,e)}catch(i){o=i}if(t){return}if(o){throw o}if(!ce.TypeIsObject(n)){throw new TypeError(\"Iterator's return method returned a non-object.\")}},IteratorNext:function(e){var t=arguments.length>1?e.next(arguments[1]):e.next();if(!ce.TypeIsObject(t)){throw new TypeError(\"bad iterator\")}return t},IteratorStep:function(e){var t=ce.IteratorNext(e);var r=ce.IteratorComplete(t);return r?false:t},Construct:function(e,t,r,n){var o=typeof r===\"undefined\"?e:r;if(!n&&ae.construct){return ae.construct(e,t,o)}var i=o.prototype;if(!ce.TypeIsObject(i)){i=Object.prototype}var a=O(i);var u=ce.Call(e,a,t);return ce.TypeIsObject(u)?u:a},SpeciesConstructor:function(e,t){var r=e.constructor;if(r===void 0){return t}if(!ce.TypeIsObject(r)){throw new TypeError(\"Bad constructor\")}var n=r[J];if(se(n)){return t}if(!ce.IsConstructor(n)){throw new TypeError(\"Bad @@species\")}return n},CreateHTML:function(e,t,r,n){var o=ce.ToString(e);var i=\"<\"+t;if(r!==\"\"){var a=ce.ToString(n);var u=a.replace(/\"/g,\"&quot;\");i+=\" \"+r+'=\"'+u+'\"'}var f=i+\">\";var s=f+o;return s+\"</\"+t+\">\"},IsRegExp:function IsRegExp(e){if(!ce.TypeIsObject(e)){return false}var t=e[$.match];if(typeof t!==\"undefined\"){return!!t}return re.regex(e)},ToString:function ToString(e){return ue(e)}};if(s&&oe){var le=function defineWellKnownSymbol(e){if(re.symbol($[e])){return $[e]}var t=$[\"for\"](\"Symbol.\"+e);Object.defineProperty($,e,{configurable:false,enumerable:false,writable:false,value:t});return t};if(!re.symbol($.search)){var pe=le(\"search\");var ve=String.prototype.search;h(RegExp.prototype,pe,function search(e){return ce.Call(ve,e,[this])});var ye=function search(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,pe);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(ve,t,[ce.ToString(e)])};ne(String.prototype,\"search\",ye)}if(!re.symbol($.replace)){var he=le(\"replace\");var be=String.prototype.replace;h(RegExp.prototype,he,function replace(e,t){return ce.Call(be,e,[this,t])});var ge=function replace(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,he);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(be,r,[ce.ToString(e),t])};ne(String.prototype,\"replace\",ge)}if(!re.symbol($.split)){var de=le(\"split\");var me=String.prototype.split;h(RegExp.prototype,de,function split(e,t){return ce.Call(me,e,[this,t])});var Oe=function split(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,de);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(me,r,[ce.ToString(e),t])};ne(String.prototype,\"split\",Oe)}var we=re.symbol($.match);var je=we&&function(){var e={};e[$.match]=function(){return 42};return\"a\".match(e)!==42}();if(!we||je){var Se=le(\"match\");var Te=String.prototype.match;h(RegExp.prototype,Se,function match(e){return ce.Call(Te,e,[this])});var Ie=function match(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,Se);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(Te,t,[ce.ToString(e)])};ne(String.prototype,\"match\",Ie)}}var Ee=function wrapConstructor(e,t,r){m.preserveToString(t,e);if(Object.setPrototypeOf){Object.setPrototypeOf(e,t)}if(s){l(Object.getOwnPropertyNames(e),function(n){if(n in W||r[n]){return}m.proxy(e,n,t)})}else{l(Object.keys(e),function(n){if(n in W||r[n]){return}t[n]=e[n]})}t.prototype=e.prototype;m.redefine(e.prototype,\"constructor\",t)};var Pe=function(){return this};var Ce=function(e){if(s&&!z(e,J)){m.getter(e,J,Pe)}};var Me=function(e,t){var r=t||function iterator(){return this};h(e,ie,r);if(!e[ie]&&re.symbol(ie)){e[ie]=r}};var xe=function createDataProperty(e,t,r){if(s){Object.defineProperty(e,t,{configurable:true,enumerable:true,writable:true,value:r})}else{e[t]=r}};var Ne=function createDataPropertyOrThrow(e,t,r){xe(e,t,r);if(!ce.SameValue(e[t],r)){throw new TypeError(\"property is nonconfigurable\")}};var Ae=function(e,t,r,n){if(!ce.TypeIsObject(e)){throw new TypeError(\"Constructor requires `new`: \"+t.name)}var o=t.prototype;if(!ce.TypeIsObject(o)){o=r}var i=O(o);for(var a in n){if(z(n,a)){var u=n[a];h(i,a,u,true)}}return i};if(String.fromCodePoint&&String.fromCodePoint.length!==1){var Re=String.fromCodePoint;ne(String,\"fromCodePoint\",function fromCodePoint(e){return ce.Call(Re,this,arguments)})}var _e={fromCodePoint:function fromCodePoint(e){var t=[];var r;for(var n=0,o=arguments.length;n<o;n++){r=Number(arguments[n]);if(!ce.SameValue(r,ce.ToInteger(r))||r<0||r>1114111){throw new RangeError(\"Invalid code point \"+r)}if(r<65536){M(t,String.fromCharCode(r))}else{r-=65536;M(t,String.fromCharCode((r>>10)+55296));M(t,String.fromCharCode(r%1024+56320))}}return t.join(\"\")},raw:function raw(e){var t=ce.ToObject(e,\"bad callSite\");var r=ce.ToObject(t.raw,\"bad raw value\");var n=r.length;var o=ce.ToLength(n);if(o<=0){return\"\"}var i=[];var a=0;var u,f,s,c;while(a<o){u=ce.ToString(a);s=ce.ToString(r[u]);M(i,s);if(a+1>=o){break}f=a+1<arguments.length?arguments[a+1]:\"\";c=ce.ToString(f);M(i,c);a+=1}return i.join(\"\")}};if(String.raw&&String.raw({raw:{0:\"x\",1:\"y\",length:2}})!==\"xy\"){ne(String,\"raw\",_e.raw)}b(String,_e);var ke=function repeat(e,t){if(t<1){return\"\"}if(t%2){return repeat(e,t-1)+e}var r=repeat(e,t/2);return r+r};var Le=Infinity;var Fe={repeat:function repeat(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);if(r<0||r>=Le){throw new RangeError(\"repeat count must be less than infinity and not overflow maximum string size\")}return ke(t,r)},startsWith:function startsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"startsWith\" with a regex')}var r=ce.ToString(e);var n;if(arguments.length>1){n=arguments[1]}var o=A(ce.ToInteger(n),0);return C(t,o,o+r.length)===r},endsWith:function endsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"endsWith\" with a regex')}var r=ce.ToString(e);var n=t.length;var o;if(arguments.length>1){o=arguments[1]}var i=typeof o===\"undefined\"?n:ce.ToInteger(o);var a=R(A(i,0),n);return C(t,a-r.length,a)===r},includes:function includes(e){if(ce.IsRegExp(e)){throw new TypeError('\"includes\" does not accept a RegExp')}var t=ce.ToString(e);var r;if(arguments.length>1){r=arguments[1]}return I(this,t,r)!==-1},codePointAt:function codePointAt(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);var n=t.length;if(r>=0&&r<n){var o=t.charCodeAt(r);var i=r+1===n;if(o<55296||o>56319||i){return o}var a=t.charCodeAt(r+1);if(a<56320||a>57343){return o}return(o-55296)*1024+(a-56320)+65536}}};if(String.prototype.includes&&\"a\".includes(\"a\",Infinity)!==false){ne(String.prototype,\"includes\",Fe.includes)}if(String.prototype.startsWith&&String.prototype.endsWith){var De=i(function(){return\"/a/\".startsWith(/a/)});var ze=a(function(){return\"abc\".startsWith(\"a\",Infinity)===false});if(!De||!ze){ne(String.prototype,\"startsWith\",Fe.startsWith);ne(String.prototype,\"endsWith\",Fe.endsWith)}}if(oe){var qe=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".startsWith(e)});if(!qe){ne(String.prototype,\"startsWith\",Fe.startsWith)}var We=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".endsWith(e)});if(!We){ne(String.prototype,\"endsWith\",Fe.endsWith)}var Ge=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".includes(e)});if(!Ge){ne(String.prototype,\"includes\",Fe.includes)}}b(String.prototype,Fe);var He=[\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\",\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\",\"\\u2029\\ufeff\"].join(\"\");var Ve=new RegExp(\"(^[\"+He+\"]+)|([\"+He+\"]+$)\",\"g\");var Be=function trim(){return ce.ToString(ce.RequireObjectCoercible(this)).replace(Ve,\"\")};var Ue=[\"\\x85\",\"\\u200b\",\"\\ufffe\"].join(\"\");var $e=new RegExp(\"[\"+Ue+\"]\",\"g\");var Je=/^[-+]0x[0-9a-f]+$/i;var Xe=Ue.trim().length!==Ue.length;h(String.prototype,\"trim\",Be,Xe);var Ke=function(e){return{value:e,done:arguments.length===0}};var Ze=function(e){ce.RequireObjectCoercible(e);this._s=ce.ToString(e);this._i=0};Ze.prototype.next=function(){var e=this._s;var t=this._i;if(typeof e===\"undefined\"||t>=e.length){this._s=void 0;return Ke()}var r=e.charCodeAt(t);var n,o;if(r<55296||r>56319||t+1===e.length){o=1}else{n=e.charCodeAt(t+1);o=n<56320||n>57343?1:2}this._i=t+o;return Ke(e.substr(t,o))};Me(Ze.prototype);Me(String.prototype,function(){return new Ze(this)});var Ye={from:function from(e){var r=this;var n;if(arguments.length>1){n=arguments[1]}var o,i;if(typeof n===\"undefined\"){o=false}else{if(!ce.IsCallable(n)){throw new TypeError(\"Array.from: when provided, the second argument must be a function\")}if(arguments.length>2){i=arguments[2]}o=true}var a=typeof(te(e)||ce.GetMethod(e,ie))!==\"undefined\";var u,f,s;if(a){f=ce.IsConstructor(r)?Object(new r):[];var c=ce.GetIterator(e);var l,p;s=0;while(true){l=ce.IteratorStep(c);if(l===false){break}p=l.value;try{if(o){p=typeof i===\"undefined\"?n(p,s):t(n,i,p,s)}f[s]=p}catch(v){ce.IteratorClose(c,true);throw v}s+=1}u=s}else{var y=ce.ToObject(e);u=ce.ToLength(y.length);f=ce.IsConstructor(r)?Object(new r(u)):new Array(u);var h;for(s=0;s<u;++s){h=y[s];if(o){h=typeof i===\"undefined\"?n(h,s):t(n,i,h,s)}Ne(f,s,h)}}f.length=u;return f},of:function of(){var e=arguments.length;var t=this;var n=r(t)||!ce.IsCallable(t)?new Array(e):ce.Construct(t,[e]);for(var o=0;o<e;++o){Ne(n,o,arguments[o])}n.length=e;return n}};b(Array,Ye);Ce(Array);q=function(e,t){this.i=0;this.array=e;this.kind=t};b(q.prototype,{next:function(){var e=this.i;var t=this.array;if(!(this instanceof q)){throw new TypeError(\"Not an ArrayIterator\")}if(typeof t!==\"undefined\"){var r=ce.ToLength(t.length);for(;e<r;e++){var n=this.kind;var o;if(n===\"key\"){o=e}else if(n===\"value\"){o=t[e]}else if(n===\"entry\"){o=[e,t[e]]}this.i=e+1;return Ke(o)}}this.array=void 0;return Ke()}});Me(q.prototype);var Qe=Array.of===Ye.of||function(){var e=function Foo(e){this.length=e};e.prototype=[];var t=Array.of.apply(e,[1,2]);return t instanceof e&&t.length===2}();if(!Qe){ne(Array,\"of\",Ye.of)}var et={copyWithin:function copyWithin(e,t){var r=ce.ToObject(this);var n=ce.ToLength(r.length);var o=ce.ToInteger(e);var i=ce.ToInteger(t);var a=o<0?A(n+o,0):R(o,n);var u=i<0?A(n+i,0):R(i,n);var f;if(arguments.length>2){f=arguments[2]}var s=typeof f===\"undefined\"?n:ce.ToInteger(f);var c=s<0?A(n+s,0):R(s,n);var l=R(c-u,n-a);var p=1;if(u<a&&a<u+l){p=-1;u+=l-1;a+=l-1}while(l>0){if(u in r){r[a]=r[u]}else{delete r[a]}u+=p;a+=p;l-=1}return r},fill:function fill(e){var t;if(arguments.length>1){t=arguments[1]}var r;if(arguments.length>2){r=arguments[2]}var n=ce.ToObject(this);var o=ce.ToLength(n.length);t=ce.ToInteger(typeof t===\"undefined\"?0:t);r=ce.ToInteger(typeof r===\"undefined\"?o:r);var i=t<0?A(o+t,0):R(t,o);var a=r<0?o+r:r;for(var u=i;u<o&&u<a;++u){n[u]=e}return n},find:function find(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#find: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0,a;i<n;i++){a=r[i];if(o){if(t(e,o,a,i,r)){return a}}else if(e(a,i,r)){return a}}},findIndex:function findIndex(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#findIndex: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0;i<n;i++){if(o){if(t(e,o,r[i],i,r)){return i}}else if(e(r[i],i,r)){return i}}return-1},keys:function keys(){return new q(this,\"key\")},values:function values(){return new q(this,\"value\")},entries:function entries(){return new q(this,\"entry\")}};if(Array.prototype.keys&&!ce.IsCallable([1].keys().next)){delete Array.prototype.keys}if(Array.prototype.entries&&!ce.IsCallable([1].entries().next)){delete Array.prototype.entries}if(Array.prototype.keys&&Array.prototype.entries&&!Array.prototype.values&&Array.prototype[ie]){b(Array.prototype,{values:Array.prototype[ie]});if(re.symbol($.unscopables)){Array.prototype[$.unscopables].values=true}}if(c&&Array.prototype.values&&Array.prototype.values.name!==\"values\"){var tt=Array.prototype.values;ne(Array.prototype,\"values\",function values(){return ce.Call(tt,this,arguments)});h(Array.prototype,ie,Array.prototype.values,true)}b(Array.prototype,et);if(1/[true].indexOf(true,-0)<0){h(Array.prototype,\"indexOf\",function indexOf(e){var t=E(this,arguments);if(t===0&&1/t<0){return 0}return t},true)}Me(Array.prototype,function(){return this.values()});if(Object.getPrototypeOf){Me(Object.getPrototypeOf([].values()))}var rt=function(){return a(function(){return Array.from({length:-1}).length===0})}();var nt=function(){var e=Array.from([0].entries());return e.length===1&&r(e[0])&&e[0][0]===0&&e[0][1]===0}();if(!rt||!nt){ne(Array,\"from\",Ye.from)}var ot=function(){return a(function(){return Array.from([0],void 0)})}();if(!ot){var it=Array.from;ne(Array,\"from\",function from(e){if(arguments.length>1&&typeof arguments[1]!==\"undefined\"){return ce.Call(it,this,arguments)}else{return t(it,this,e)}})}var at=-(Math.pow(2,32)-1);var ut=function(e,r){var n={length:at};n[r?(n.length>>>0)-1:0]=true;return a(function(){t(e,n,function(){throw new RangeError(\"should not reach here\")},[]);return true})};if(!ut(Array.prototype.forEach)){var ft=Array.prototype.forEach;ne(Array.prototype,\"forEach\",function forEach(e){return ce.Call(ft,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.map)){var st=Array.prototype.map;ne(Array.prototype,\"map\",function map(e){return ce.Call(st,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.filter)){var ct=Array.prototype.filter;ne(Array.prototype,\"filter\",function filter(e){return ce.Call(ct,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.some)){var lt=Array.prototype.some;ne(Array.prototype,\"some\",function some(e){return ce.Call(lt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.every)){var pt=Array.prototype.every;ne(Array.prototype,\"every\",function every(e){return ce.Call(pt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduce)){var vt=Array.prototype.reduce;ne(Array.prototype,\"reduce\",function reduce(e){return ce.Call(vt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduceRight,true)){var yt=Array.prototype.reduceRight;ne(Array.prototype,\"reduceRight\",function reduceRight(e){return ce.Call(yt,this.length>=0?this:[],arguments)},true)}var ht=Number(\"0o10\")!==8;var bt=Number(\"0b10\")!==2;var gt=y(Ue,function(e){return Number(e+0+e)===0});if(ht||bt||gt){var dt=Number;var mt=/^0b[01]+$/i;var Ot=/^0o[0-7]+$/i;var wt=mt.test.bind(mt);var jt=Ot.test.bind(Ot);var St=function(e){var t;if(typeof e.valueOf===\"function\"){t=e.valueOf();if(re.primitive(t)){return t}}if(typeof e.toString===\"function\"){t=e.toString();if(re.primitive(t)){return t}}throw new TypeError(\"No default value\")};var Tt=$e.test.bind($e);var It=Je.test.bind(Je);var Et=function(){var e=function Number(t){var r;if(arguments.length>0){r=re.primitive(t)?t:St(t,\"number\")}else{r=0}if(typeof r===\"string\"){r=ce.Call(Be,r);if(wt(r)){r=parseInt(C(r,2),2)}else if(jt(r)){r=parseInt(C(r,2),8)}else if(Tt(r)||It(r)){r=NaN}}var n=this;var o=a(function(){dt.prototype.valueOf.call(n);return true});if(n instanceof e&&!o){return new dt(r)}return dt(r)};return e}();Ee(dt,Et,{});b(Et,{NaN:dt.NaN,MAX_VALUE:dt.MAX_VALUE,MIN_VALUE:dt.MIN_VALUE,NEGATIVE_INFINITY:dt.NEGATIVE_INFINITY,POSITIVE_INFINITY:dt.POSITIVE_INFINITY});Number=Et;m.redefine(S,\"Number\",Et)}var Pt=Math.pow(2,53)-1;b(Number,{MAX_SAFE_INTEGER:Pt,MIN_SAFE_INTEGER:-Pt,EPSILON:2.220446049250313e-16,parseInt:S.parseInt,parseFloat:S.parseFloat,isFinite:K,isInteger:function isInteger(e){return K(e)&&ce.ToInteger(e)===e},isSafeInteger:function isSafeInteger(e){return Number.isInteger(e)&&k(e)<=Number.MAX_SAFE_INTEGER},isNaN:X});h(Number,\"parseInt\",S.parseInt,Number.parseInt!==S.parseInt);if([,1].find(function(){return true})===1){ne(Array.prototype,\"find\",et.find)}if([,1].findIndex(function(){return true})!==0){ne(Array.prototype,\"findIndex\",et.findIndex)}var Ct=Function.bind.call(Function.bind,Object.prototype.propertyIsEnumerable);var Mt=function ensureEnumerable(e,t){if(s&&Ct(e,t)){Object.defineProperty(e,t,{enumerable:false})}};var xt=function sliceArgs(){var e=Number(this);var t=arguments.length;var r=t-e;var n=new Array(r<0?0:r);for(var o=e;o<t;++o){n[o-e]=arguments[o]}return n};var Nt=function assignTo(e){return function assignToSource(t,r){t[r]=e[r];return t}};var At=function(e,t){var r=n(Object(t));var o;if(ce.IsCallable(Object.getOwnPropertySymbols)){o=v(Object.getOwnPropertySymbols(Object(t)),Ct(t))}return p(P(r,o||[]),Nt(t),e)};var Rt={assign:function(e,t){var r=ce.ToObject(e,\"Cannot convert undefined or null to object\");return p(ce.Call(xt,1,arguments),At,r)},is:function is(e,t){return ce.SameValue(e,t)}};var _t=Object.assign&&Object.preventExtensions&&function(){var e=Object.preventExtensions({1:2});try{Object.assign(e,\"xy\")}catch(t){return e[1]===\"y\"}}();if(_t){ne(Object,\"assign\",Rt.assign)}b(Object,Rt);if(s){var kt={setPrototypeOf:function(e,r){var n;var o=function(e,t){if(!ce.TypeIsObject(e)){throw new TypeError(\"cannot set prototype on a non-object\")}if(!(t===null||ce.TypeIsObject(t))){throw new TypeError(\"can only set prototype to an object or null\"+t)}};var i=function(e,r){o(e,r);t(n,e,r);return e};try{n=e.getOwnPropertyDescriptor(e.prototype,r).set;t(n,{},null)}catch(a){if(e.prototype!=={}[r]){return}n=function(e){this[r]=e};i.polyfill=i(i({},null),e.prototype)instanceof e}return i}(Object,\"__proto__\")};b(Object,kt)}if(Object.setPrototypeOf&&Object.getPrototypeOf&&Object.getPrototypeOf(Object.setPrototypeOf({},null))!==null&&Object.getPrototypeOf(Object.create(null))===null){(function(){var e=Object.create(null);var t=Object.getPrototypeOf;var r=Object.setPrototypeOf;Object.getPrototypeOf=function(r){var n=t(r);return n===e?null:n};Object.setPrototypeOf=function(t,n){var o=n===null?e:n;return r(t,o)};Object.setPrototypeOf.polyfill=false})()}var Lt=!i(function(){return Object.keys(\"foo\")});if(!Lt){var Ft=Object.keys;ne(Object,\"keys\",function keys(e){return Ft(ce.ToObject(e))});n=Object.keys}var Dt=i(function(){return Object.keys(/a/g)});if(Dt){var zt=Object.keys;ne(Object,\"keys\",function keys(e){if(re.regex(e)){var t=[];for(var r in e){if(z(e,r)){M(t,r)}}return t}return zt(e)});n=Object.keys}if(Object.getOwnPropertyNames){var qt=!i(function(){return Object.getOwnPropertyNames(\"foo\")});if(!qt){var Wt=typeof window===\"object\"?Object.getOwnPropertyNames(window):[];var Gt=Object.getOwnPropertyNames;ne(Object,\"getOwnPropertyNames\",function getOwnPropertyNames(e){var t=ce.ToObject(e);if(g(t)===\"[object Window]\"){try{return Gt(t)}catch(r){return P([],Wt)}}return Gt(t)})}}if(Object.getOwnPropertyDescriptor){var Ht=!i(function(){return Object.getOwnPropertyDescriptor(\"foo\",\"bar\")});if(!Ht){var Vt=Object.getOwnPropertyDescriptor;ne(Object,\"getOwnPropertyDescriptor\",function getOwnPropertyDescriptor(e,t){return Vt(ce.ToObject(e),t)})}}if(Object.seal){var Bt=!i(function(){return Object.seal(\"foo\")});if(!Bt){var Ut=Object.seal;ne(Object,\"seal\",function seal(e){if(!ce.TypeIsObject(e)){return e}return Ut(e)})}}if(Object.isSealed){var $t=!i(function(){return Object.isSealed(\"foo\")});if(!$t){var Jt=Object.isSealed;ne(Object,\"isSealed\",function isSealed(e){if(!ce.TypeIsObject(e)){return true}return Jt(e)})}}if(Object.freeze){var Xt=!i(function(){return Object.freeze(\"foo\")});if(!Xt){var Kt=Object.freeze;ne(Object,\"freeze\",function freeze(e){if(!ce.TypeIsObject(e)){return e}return Kt(e)})}}if(Object.isFrozen){var Zt=!i(function(){return Object.isFrozen(\"foo\")});if(!Zt){var Yt=Object.isFrozen;ne(Object,\"isFrozen\",function isFrozen(e){if(!ce.TypeIsObject(e)){return true}return Yt(e)})}}if(Object.preventExtensions){var Qt=!i(function(){return Object.preventExtensions(\"foo\")});if(!Qt){var er=Object.preventExtensions;ne(Object,\"preventExtensions\",function preventExtensions(e){if(!ce.TypeIsObject(e)){return e}return er(e)})}}if(Object.isExtensible){var tr=!i(function(){return Object.isExtensible(\"foo\")});if(!tr){var rr=Object.isExtensible;ne(Object,\"isExtensible\",function isExtensible(e){if(!ce.TypeIsObject(e)){return false}return rr(e)})}}if(Object.getPrototypeOf){var nr=!i(function(){return Object.getPrototypeOf(\"foo\")});if(!nr){var or=Object.getPrototypeOf;ne(Object,\"getPrototypeOf\",function getPrototypeOf(e){return or(ce.ToObject(e))})}}var ir=s&&function(){var e=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\");return e&&ce.IsCallable(e.get)}();if(s&&!ir){var ar=function flags(){if(!ce.TypeIsObject(this)){throw new TypeError(\"Method called on incompatible type: must be an object.\")}var e=\"\";if(this.global){e+=\"g\"}if(this.ignoreCase){e+=\"i\"}if(this.multiline){e+=\"m\"}if(this.unicode){e+=\"u\"}if(this.sticky){e+=\"y\"}return e};m.getter(RegExp.prototype,\"flags\",ar)}var ur=s&&a(function(){return String(new RegExp(/a/g,\"i\"))===\"/a/i\"});var fr=oe&&s&&function(){var e=/./;e[$.match]=false;return RegExp(e)===e}();var sr=a(function(){return RegExp.prototype.toString.call({source:\"abc\"})===\"/abc/\"});var cr=sr&&a(function(){return RegExp.prototype.toString.call({source:\"a\",flags:\"b\"})===\"/a/b\"});if(!sr||!cr){var lr=RegExp.prototype.toString;h(RegExp.prototype,\"toString\",function toString(){var e=ce.RequireObjectCoercible(this);if(re.regex(e)){return t(lr,e)}var r=ue(e.source);var n=ue(e.flags);return\"/\"+r+\"/\"+n},true);m.preserveToString(RegExp.prototype.toString,lr)}if(s&&(!ur||fr)){var pr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\").get;var vr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"source\")||{};var yr=function(){return this.source};var hr=ce.IsCallable(vr.get)?vr.get:yr;var br=RegExp;var gr=function(){return function RegExp(e,t){var r=ce.IsRegExp(e);var n=this instanceof RegExp;if(!n&&r&&typeof t===\"undefined\"&&e.constructor===RegExp){return e}var o=e;var i=t;if(re.regex(e)){o=ce.Call(hr,e);i=typeof t===\"undefined\"?ce.Call(pr,e):t;return new RegExp(o,i)}else if(r){o=e.source;i=typeof t===\"undefined\"?e.flags:t}return new br(e,t)}}();Ee(br,gr,{$input:true});RegExp=gr;m.redefine(S,\"RegExp\",gr)}if(s){var dr={input:\"$_\",lastMatch:\"$&\",lastParen:\"$+\",leftContext:\"$`\",rightContext:\"$'\"};l(n(dr),function(e){if(e in RegExp&&!(dr[e]in RegExp)){m.getter(RegExp,dr[e],function get(){return RegExp[e]})}})}Ce(RegExp);var mr=1/Number.EPSILON;var Or=function roundTiesToEven(e){return e+mr-mr};var wr=Math.pow(2,-23);var jr=Math.pow(2,127)*(2-wr);var Sr=Math.pow(2,-126);var Tr=Math.E;var Ir=Math.LOG2E;var Er=Math.LOG10E;var Pr=Number.prototype.clz;delete Number.prototype.clz;var Cr={acosh:function acosh(e){var t=Number(e);if(X(t)||e<1){return NaN}if(t===1){return 0}if(t===Infinity){return t}var r=1/(t*t);if(t<2){return Y(t-1+D(1-r)*t)}var n=t/2;return Y(n+D(1-r)*n-1)+1/Ir},asinh:function asinh(e){var t=Number(e);if(t===0||!T(t)){return t}var r=k(t);var n=r*r;var o=Z(t);if(r<1){return o*Y(r+n/(D(n+1)+1))}return o*(Y(r/2+D(1+1/n)*r/2-1)+1/Ir)},atanh:function atanh(e){var t=Number(e);if(t===0){return t}if(t===-1){return-Infinity}if(t===1){return Infinity}if(X(t)||t<-1||t>1){return NaN}var r=k(t);return Z(t)*Y(2*r/(1-r))/2},cbrt:function cbrt(e){var t=Number(e);if(t===0){return t}var r=t<0;var n;if(r){t=-t}if(t===Infinity){n=Infinity}else{n=L(F(t)/3);n=(t/(n*n)+2*n)/3}return r?-n:n},clz32:function clz32(e){var t=Number(e);var r=ce.ToUint32(t);if(r===0){return 32}return Pr?ce.Call(Pr,r):31-_(F(r+.5)*Ir)},cosh:function cosh(e){var t=Number(e);if(t===0){return 1}if(X(t)){return NaN}if(!T(t)){return Infinity}var r=L(k(t)-1);return(r+1/(r*Tr*Tr))*(Tr/2)},expm1:function expm1(e){var t=Number(e);if(t===-Infinity){return-1}if(!T(t)||t===0){return t}if(k(t)>.5){return L(t)-1}var r=t;var n=0;var o=1;while(n+r!==n){n+=r;o+=1;r*=t/o}return n},hypot:function hypot(e,t){var r=0;var n=0;for(var o=0;o<arguments.length;++o){var i=k(Number(arguments[o]));if(n<i){r*=n/i*(n/i);r+=1;n=i}else{r+=i>0?i/n*(i/n):i}}return n===Infinity?Infinity:n*D(r)},log2:function log2(e){return F(e)*Ir},log10:function log10(e){return F(e)*Er},log1p:Y,sign:Z,sinh:function sinh(e){var t=Number(e);if(!T(t)||t===0){return t}var r=k(t);if(r<1){var n=Math.expm1(r);return Z(t)*n*(1+1/(n+1))/2}var o=L(r-1);return Z(t)*(o-1/(o*Tr*Tr))*(Tr/2)},tanh:function tanh(e){var t=Number(e);if(X(t)||t===0){return t}if(t>=20){return 1}if(t<=-20){return-1}return(Math.expm1(t)-Math.expm1(-t))/(L(t)+L(-t))},trunc:function trunc(e){var t=Number(e);return t<0?-_(-t):_(t)},imul:function imul(e,t){var r=ce.ToUint32(e);var n=ce.ToUint32(t);var o=r>>>16&65535;var i=r&65535;var a=n>>>16&65535;var u=n&65535;return i*u+(o*u+i*a<<16>>>0)|0},fround:function fround(e){var t=Number(e);if(t===0||t===Infinity||t===-Infinity||X(t)){return t}var r=Z(t);var n=k(t);if(n<Sr){return r*Or(n/Sr/wr)*Sr*wr}var o=(1+wr/Number.EPSILON)*n;var i=o-(o-n);if(i>jr||X(i)){return r*Infinity}return r*i}};var Mr=function withinULPDistance(e,t,r){return k(1-e/t)/Number.EPSILON<(r||8)};b(Math,Cr);h(Math,\"sinh\",Cr.sinh,Math.sinh(710)===Infinity);h(Math,\"cosh\",Cr.cosh,Math.cosh(710)===Infinity);h(Math,\"log1p\",Cr.log1p,Math.log1p(-1e-17)!==-1e-17);h(Math,\"asinh\",Cr.asinh,Math.asinh(-1e7)!==-Math.asinh(1e7));h(Math,\"asinh\",Cr.asinh,Math.asinh(1e300)===Infinity);h(Math,\"atanh\",Cr.atanh,Math.atanh(1e-300)===0);h(Math,\"tanh\",Cr.tanh,Math.tanh(-2e-17)!==-2e-17);\nh(Math,\"acosh\",Cr.acosh,Math.acosh(Number.MAX_VALUE)===Infinity);h(Math,\"acosh\",Cr.acosh,!Mr(Math.acosh(1+Number.EPSILON),Math.sqrt(2*Number.EPSILON)));h(Math,\"cbrt\",Cr.cbrt,!Mr(Math.cbrt(1e-300),1e-100));h(Math,\"sinh\",Cr.sinh,Math.sinh(-2e-17)!==-2e-17);var xr=Math.expm1(10);h(Math,\"expm1\",Cr.expm1,xr>22025.465794806718||xr<22025.465794806718);var Nr=Math.round;var Ar=Math.round(.5-Number.EPSILON/4)===0&&Math.round(-.5+Number.EPSILON/3.99)===1;var Rr=mr+1;var _r=2*mr-1;var kr=[Rr,_r].every(function(e){return Math.round(e)===e});h(Math,\"round\",function round(e){var t=_(e);var r=t===-1?-0:t+1;return e-t<.5?t:r},!Ar||!kr);m.preserveToString(Math.round,Nr);var Lr=Math.imul;if(Math.imul(4294967295,5)!==-5){Math.imul=Cr.imul;m.preserveToString(Math.imul,Lr)}if(Math.imul.length!==2){ne(Math,\"imul\",function imul(e,t){return ce.Call(Lr,Math,arguments)})}var Fr=function(){var e=S.setTimeout;if(typeof e!==\"function\"&&typeof e!==\"object\"){return}ce.IsPromise=function(e){if(!ce.TypeIsObject(e)){return false}if(typeof e._promise===\"undefined\"){return false}return true};var r=function(e){if(!ce.IsConstructor(e)){throw new TypeError(\"Bad promise constructor\")}var t=this;var r=function(e,r){if(t.resolve!==void 0||t.reject!==void 0){throw new TypeError(\"Bad Promise implementation!\")}t.resolve=e;t.reject=r};t.resolve=void 0;t.reject=void 0;t.promise=new e(r);if(!(ce.IsCallable(t.resolve)&&ce.IsCallable(t.reject))){throw new TypeError(\"Bad promise constructor\")}};var n;if(typeof window!==\"undefined\"&&ce.IsCallable(window.postMessage)){n=function(){var e=[];var t=\"zero-timeout-message\";var r=function(r){M(e,r);window.postMessage(t,\"*\")};var n=function(r){if(r.source===window&&r.data===t){r.stopPropagation();if(e.length===0){return}var n=N(e);n()}};window.addEventListener(\"message\",n,true);return r}}var o=function(){var e=S.Promise;var t=e&&e.resolve&&e.resolve();return t&&function(e){return t.then(e)}};var i=ce.IsCallable(S.setImmediate)?S.setImmediate:typeof process===\"object\"&&process.nextTick?process.nextTick:o()||(ce.IsCallable(n)?n():function(t){e(t,0)});var a=function(e){return e};var u=function(e){throw e};var f=0;var s=1;var c=2;var l=0;var p=1;var v=2;var y={};var h=function(e,t,r){i(function(){g(e,t,r)})};var g=function(e,t,r){var n,o;if(t===y){return e(r)}try{n=e(r);o=t.resolve}catch(i){n=i;o=t.reject}o(n)};var d=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.fulfillReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+l],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=s;r.reactionLength=0};var m=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.rejectReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+p],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=c;r.reactionLength=0};var O=function(e){var t=false;var r=function(r){var n;if(t){return}t=true;if(r===e){return m(e,new TypeError(\"Self resolution\"))}if(!ce.TypeIsObject(r)){return d(e,r)}try{n=r.then}catch(o){return m(e,o)}if(!ce.IsCallable(n)){return d(e,r)}i(function(){j(e,r,n)})};var n=function(r){if(t){return}t=true;return m(e,r)};return{resolve:r,reject:n}};var w=function(e,r,n,o){if(e===I){t(e,r,n,o,y)}else{t(e,r,n,o)}};var j=function(e,t,r){var n=O(e);var o=n.resolve;var i=n.reject;try{w(r,t,o,i)}catch(a){i(a)}};var T,I;var E=function(){var e=function Promise(t){if(!(this instanceof e)){throw new TypeError('Constructor Promise requires \"new\"')}if(this&&this._promise){throw new TypeError(\"Bad construction\")}if(!ce.IsCallable(t)){throw new TypeError(\"not a valid resolver\")}var r=Ae(this,e,T,{_promise:{result:void 0,state:f,reactionLength:0,fulfillReactionHandler0:void 0,rejectReactionHandler0:void 0,reactionCapability0:void 0}});var n=O(r);var o=n.reject;try{t(n.resolve,o)}catch(i){o(i)}return r};return e}();T=E.prototype;var P=function(e,t,r,n){var o=false;return function(i){if(o){return}o=true;t[e]=i;if(--n.count===0){var a=r.resolve;a(t)}}};var C=function(e,t,r){var n=e.iterator;var o=[];var i={count:1};var a,u;var f=0;while(true){try{a=ce.IteratorStep(n);if(a===false){e.done=true;break}u=a.value}catch(s){e.done=true;throw s}o[f]=void 0;var c=t.resolve(u);var l=P(f,o,r,i);i.count+=1;w(c.then,c,l,r.reject);f+=1}if(--i.count===0){var p=r.resolve;p(o)}return r.promise};var x=function(e,t,r){var n=e.iterator;var o,i,a;while(true){try{o=ce.IteratorStep(n);if(o===false){e.done=true;break}i=o.value}catch(u){e.done=true;throw u}a=t.resolve(i);w(a.then,a,r.resolve,r.reject)}return r.promise};b(E,{all:function all(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return C(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},race:function race(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return x(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},reject:function reject(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}var n=new r(t);var o=n.reject;o(e);return n.promise},resolve:function resolve(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}if(ce.IsPromise(e)){var n=e.constructor;if(n===t){return e}}var o=new r(t);var i=o.resolve;i(e);return o.promise}});b(T,{\"catch\":function(e){return this.then(null,e)},then:function then(e,t){var n=this;if(!ce.IsPromise(n)){throw new TypeError(\"not a promise\")}var o=ce.SpeciesConstructor(n,E);var i;var b=arguments.length>2&&arguments[2]===y;if(b&&o===E){i=y}else{i=new r(o)}var g=ce.IsCallable(e)?e:a;var d=ce.IsCallable(t)?t:u;var m=n._promise;var O;if(m.state===f){if(m.reactionLength===0){m.fulfillReactionHandler0=g;m.rejectReactionHandler0=d;m.reactionCapability0=i}else{var w=3*(m.reactionLength-1);m[w+l]=g;m[w+p]=d;m[w+v]=i}m.reactionLength+=1}else if(m.state===s){O=m.result;h(g,i,O)}else if(m.state===c){O=m.result;h(d,i,O)}else{throw new TypeError(\"unexpected Promise state\")}return i.promise}});y=new r(E);I=T.then;return E}();if(S.Promise){delete S.Promise.accept;delete S.Promise.defer;delete S.Promise.prototype.chain}if(typeof Fr===\"function\"){b(S,{Promise:Fr});var Dr=w(S.Promise,function(e){return e.resolve(42).then(function(){})instanceof e});var zr=!i(function(){return S.Promise.reject(42).then(null,5).then(null,W)});var qr=i(function(){return S.Promise.call(3,W)});var Wr=function(e){var t=e.resolve(5);t.constructor={};var r=e.resolve(t);try{r.then(null,W).then(null,W)}catch(n){return true}return t===r}(S.Promise);var Gr=s&&function(){var e=0;var t=Object.defineProperty({},\"then\",{get:function(){e+=1}});Promise.resolve(t);return e===1}();var Hr=function BadResolverPromise(e){var t=new Promise(e);e(3,function(){});this.then=t.then;this.constructor=BadResolverPromise};Hr.prototype=Promise.prototype;Hr.all=Promise.all;var Vr=a(function(){return!!Hr.all([1,2])});if(!Dr||!zr||!qr||Wr||!Gr||Vr){Promise=Fr;ne(S,\"Promise\",Fr)}if(Promise.all.length!==1){var Br=Promise.all;ne(Promise,\"all\",function all(e){return ce.Call(Br,this,arguments)})}if(Promise.race.length!==1){var Ur=Promise.race;ne(Promise,\"race\",function race(e){return ce.Call(Ur,this,arguments)})}if(Promise.resolve.length!==1){var $r=Promise.resolve;ne(Promise,\"resolve\",function resolve(e){return ce.Call($r,this,arguments)})}if(Promise.reject.length!==1){var Jr=Promise.reject;ne(Promise,\"reject\",function reject(e){return ce.Call(Jr,this,arguments)})}Mt(Promise,\"all\");Mt(Promise,\"race\");Mt(Promise,\"resolve\");Mt(Promise,\"reject\");Ce(Promise)}var Xr=function(e){var t=n(p(e,function(e,t){e[t]=true;return e},{}));return e.join(\":\")===t.join(\":\")};var Kr=Xr([\"z\",\"a\",\"bb\"]);var Zr=Xr([\"z\",1,\"a\",\"3\",2]);if(s){var Yr=function fastkey(e,t){if(!t&&!Kr){return null}if(se(e)){return\"^\"+ce.ToString(e)}else if(typeof e===\"string\"){return\"$\"+e}else if(typeof e===\"number\"){if(!Zr){return\"n\"+e}return e}else if(typeof e===\"boolean\"){return\"b\"+e}return null};var Qr=function emptyObject(){return Object.create?Object.create(null):{}};var en=function addIterableToMap(e,n,o){if(r(o)||re.string(o)){l(o,function(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"Iterator value \"+e+\" is not an entry object\")}n.set(e[0],e[1])})}else if(o instanceof e){t(e.prototype.forEach,o,function(e,t){n.set(t,e)})}else{var i,a;if(!se(o)){a=n.set;if(!ce.IsCallable(a)){throw new TypeError(\"bad map\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{if(!ce.TypeIsObject(f)){throw new TypeError(\"Iterator value \"+f+\" is not an entry object\")}t(a,n,f[0],f[1])}catch(s){ce.IteratorClose(i,true);throw s}}}}};var tn=function addIterableToSet(e,n,o){if(r(o)||re.string(o)){l(o,function(e){n.add(e)})}else if(o instanceof e){t(e.prototype.forEach,o,function(e){n.add(e)})}else{var i,a;if(!se(o)){a=n.add;if(!ce.IsCallable(a)){throw new TypeError(\"bad set\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{t(a,n,f)}catch(s){ce.IteratorClose(i,true);throw s}}}}};var rn={Map:function(){var e={};var r=function MapEntry(e,t){this.key=e;this.value=t;this.next=null;this.prev=null};r.prototype.isRemoved=function isRemoved(){return this.key===e};var n=function isMap(e){return!!e._es6map};var o=function requireMapSlot(e,t){if(!ce.TypeIsObject(e)||!n(e)){throw new TypeError(\"Method Map.prototype.\"+t+\" called on incompatible receiver \"+ce.ToString(e))}};var i=function MapIterator(e,t){o(e,\"[[MapIterator]]\");this.head=e._head;this.i=this.head;this.kind=t};i.prototype={isMapIterator:true,next:function next(){if(!this.isMapIterator){throw new TypeError(\"Not a MapIterator\")}var e=this.i;var t=this.kind;var r=this.head;if(typeof this.i===\"undefined\"){return Ke()}while(e.isRemoved()&&e!==r){e=e.prev}var n;while(e.next!==r){e=e.next;if(!e.isRemoved()){if(t===\"key\"){n=e.key}else if(t===\"value\"){n=e.value}else{n=[e.key,e.value]}this.i=e;return Ke(n)}}this.i=void 0;return Ke()}};Me(i.prototype);var a;var u=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}if(this&&this._es6map){throw new TypeError(\"Bad construction\")}var e=Ae(this,Map,a,{_es6map:true,_head:null,_map:G?new G:null,_size:0,_storage:Qr()});var t=new r(null,null);t.next=t.prev=t;e._head=t;if(arguments.length>0){en(Map,e,arguments[0])}return e};a=u.prototype;m.getter(a,\"size\",function(){if(typeof this._size===\"undefined\"){throw new TypeError(\"size method called on incompatible Map\")}return this._size});b(a,{get:function get(e){o(this,\"get\");var t;var r=Yr(e,true);if(r!==null){t=this._storage[r];if(t){return t.value}else{return}}if(this._map){t=V.call(this._map,e);if(t){return t.value}else{return}}var n=this._head;var i=n;while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){return i.value}}},has:function has(e){o(this,\"has\");var t=Yr(e,true);if(t!==null){return typeof this._storage[t]!==\"undefined\"}if(this._map){return B.call(this._map,e)}var r=this._head;var n=r;while((n=n.next)!==r){if(ce.SameValueZero(n.key,e)){return true}}return false},set:function set(e,t){o(this,\"set\");var n=this._head;var i=n;var a;var u=Yr(e,true);if(u!==null){if(typeof this._storage[u]!==\"undefined\"){this._storage[u].value=t;return this}else{a=this._storage[u]=new r(e,t);i=n.prev}}else if(this._map){if(B.call(this._map,e)){V.call(this._map,e).value=t}else{a=new r(e,t);U.call(this._map,e,a);i=n.prev}}while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){i.value=t;return this}}a=a||new r(e,t);if(ce.SameValue(-0,e)){a.key=+0}a.next=this._head;a.prev=this._head.prev;a.prev.next=a;a.next.prev=a;this._size+=1;return this},\"delete\":function(t){o(this,\"delete\");var r=this._head;var n=r;var i=Yr(t,true);if(i!==null){if(typeof this._storage[i]===\"undefined\"){return false}n=this._storage[i].prev;delete this._storage[i]}else if(this._map){if(!B.call(this._map,t)){return false}n=V.call(this._map,t).prev;H.call(this._map,t)}while((n=n.next)!==r){if(ce.SameValueZero(n.key,t)){n.key=e;n.value=e;n.prev.next=n.next;n.next.prev=n.prev;this._size-=1;return true}}return false},clear:function clear(){o(this,\"clear\");this._map=G?new G:null;this._size=0;this._storage=Qr();var t=this._head;var r=t;var n=r.next;while((r=n)!==t){r.key=e;r.value=e;n=r.next;r.next=r.prev=t}t.next=t.prev=t},keys:function keys(){o(this,\"keys\");return new i(this,\"key\")},values:function values(){o(this,\"values\");return new i(this,\"value\")},entries:function entries(){o(this,\"entries\");return new i(this,\"key+value\")},forEach:function forEach(e){o(this,\"forEach\");var r=arguments.length>1?arguments[1]:null;var n=this.entries();for(var i=n.next();!i.done;i=n.next()){if(r){t(e,r,i.value[1],i.value[0],this)}else{e(i.value[1],i.value[0],this)}}}});Me(a,a.entries);return u}(),Set:function(){var e=function isSet(e){return e._es6set&&typeof e._storage!==\"undefined\"};var r=function requireSetSlot(t,r){if(!ce.TypeIsObject(t)||!e(t)){throw new TypeError(\"Set.prototype.\"+r+\" called on incompatible receiver \"+ce.ToString(t))}};var o;var i=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}if(this&&this._es6set){throw new TypeError(\"Bad construction\")}var e=Ae(this,Set,o,{_es6set:true,\"[[SetData]]\":null,_storage:Qr()});if(!e._es6set){throw new TypeError(\"bad set\")}if(arguments.length>0){tn(Set,e,arguments[0])}return e};o=i.prototype;var a=function(e){var t=e;if(t===\"^null\"){return null}else if(t===\"^undefined\"){return void 0}else{var r=t.charAt(0);if(r===\"$\"){return C(t,1)}else if(r===\"n\"){return+C(t,1)}else if(r===\"b\"){return t===\"btrue\"}}return+t};var u=function ensureMap(e){if(!e[\"[[SetData]]\"]){var t=new rn.Map;e[\"[[SetData]]\"]=t;l(n(e._storage),function(e){var r=a(e);t.set(r,r)});e[\"[[SetData]]\"]=t}e._storage=null};m.getter(i.prototype,\"size\",function(){r(this,\"size\");if(this._storage){return n(this._storage).length}u(this);return this[\"[[SetData]]\"].size});b(i.prototype,{has:function has(e){r(this,\"has\");var t;if(this._storage&&(t=Yr(e))!==null){return!!this._storage[t]}u(this);return this[\"[[SetData]]\"].has(e)},add:function add(e){r(this,\"add\");var t;if(this._storage&&(t=Yr(e))!==null){this._storage[t]=true;return this}u(this);this[\"[[SetData]]\"].set(e,e);return this},\"delete\":function(e){r(this,\"delete\");var t;if(this._storage&&(t=Yr(e))!==null){var n=z(this._storage,t);return delete this._storage[t]&&n}u(this);return this[\"[[SetData]]\"][\"delete\"](e)},clear:function clear(){r(this,\"clear\");if(this._storage){this._storage=Qr()}if(this[\"[[SetData]]\"]){this[\"[[SetData]]\"].clear()}},values:function values(){r(this,\"values\");u(this);return new f(this[\"[[SetData]]\"].values())},entries:function entries(){r(this,\"entries\");u(this);return new f(this[\"[[SetData]]\"].entries())},forEach:function forEach(e){r(this,\"forEach\");var n=arguments.length>1?arguments[1]:null;var o=this;u(o);this[\"[[SetData]]\"].forEach(function(r,i){if(n){t(e,n,i,i,o)}else{e(i,i,o)}})}});h(i.prototype,\"keys\",i.prototype.values,true);Me(i.prototype,i.prototype.values);var f=function SetIterator(e){this.it=e};f.prototype={isSetIterator:true,next:function next(){if(!this.isSetIterator){throw new TypeError(\"Not a SetIterator\")}return this.it.next()}};Me(f.prototype);return i}()};var nn=S.Set&&!Set.prototype[\"delete\"]&&Set.prototype.remove&&Set.prototype.items&&Set.prototype.map&&Array.isArray((new Set).keys);if(nn){S.Set=rn.Set}if(S.Map||S.Set){var on=a(function(){return new Map([[1,2]]).get(1)===2});if(!on){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,S.Map.prototype);return e};S.Map.prototype=O(G.prototype);h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var an=new Map;var un=function(){var e=new Map([[1,0],[2,0],[3,0],[4,0]]);e.set(-0,e);return e.get(0)===e&&e.get(-0)===e&&e.has(0)&&e.has(-0)}();var fn=an.set(1,2)===an;if(!un||!fn){ne(Map.prototype,\"set\",function set(e,r){t(U,this,e===0?0:e,r);return this})}if(!un){b(Map.prototype,{get:function get(e){return t(V,this,e===0?0:e)},has:function has(e){return t(B,this,e===0?0:e)}},true);m.preserveToString(Map.prototype.get,V);m.preserveToString(Map.prototype.has,B)}var sn=new Set;var cn=Set.prototype[\"delete\"]&&Set.prototype.add&&Set.prototype.has&&function(e){e[\"delete\"](0);e.add(-0);return!e.has(0)}(sn);var ln=sn.add(1)===sn;if(!cn||!ln){var pn=Set.prototype.add;Set.prototype.add=function add(e){t(pn,this,e===0?0:e);return this};m.preserveToString(Set.prototype.add,pn)}if(!cn){var vn=Set.prototype.has;Set.prototype.has=function has(e){return t(vn,this,e===0?0:e)};m.preserveToString(Set.prototype.has,vn);var yn=Set.prototype[\"delete\"];Set.prototype[\"delete\"]=function SetDelete(e){return t(yn,this,e===0?0:e)};m.preserveToString(Set.prototype[\"delete\"],yn)}var hn=w(S.Map,function(e){var t=new e([]);t.set(42,42);return t instanceof e});var bn=Object.setPrototypeOf&&!hn;var gn=function(){try{return!(S.Map()instanceof S.Map)}catch(e){return e instanceof TypeError}}();if(S.Map.length!==0||bn||!gn){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Map.prototype);return e};S.Map.prototype=G.prototype;h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var dn=w(S.Set,function(e){var t=new e([]);t.add(42,42);return t instanceof e});var mn=Object.setPrototypeOf&&!dn;var On=function(){try{return!(S.Set()instanceof S.Set)}catch(e){return e instanceof TypeError}}();if(S.Set.length!==0||mn||!On){var wn=S.Set;S.Set=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}var e=new wn;if(arguments.length>0){tn(Set,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Set.prototype);return e};S.Set.prototype=wn.prototype;h(S.Set.prototype,\"constructor\",S.Set,true);m.preserveToString(S.Set,wn)}var jn=new S.Map;var Sn=!a(function(){return jn.keys().next().done});if(typeof S.Map.prototype.clear!==\"function\"||(new S.Set).size!==0||jn.size!==0||typeof S.Map.prototype.keys!==\"function\"||typeof S.Set.prototype.keys!==\"function\"||typeof S.Map.prototype.forEach!==\"function\"||typeof S.Set.prototype.forEach!==\"function\"||u(S.Map)||u(S.Set)||typeof jn.keys().next!==\"function\"||Sn||!hn){b(S,{Map:rn.Map,Set:rn.Set},true)}if(S.Set.prototype.keys!==S.Set.prototype.values){h(S.Set.prototype,\"keys\",S.Set.prototype.values,true)}Me(Object.getPrototypeOf((new S.Map).keys()));Me(Object.getPrototypeOf((new S.Set).keys()));if(c&&S.Set.prototype.has.name!==\"has\"){var Tn=S.Set.prototype.has;ne(S.Set.prototype,\"has\",function has(e){return t(Tn,this,e)})}}b(S,rn);Ce(S.Map);Ce(S.Set)}var In=function throwUnlessTargetIsObject(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"target must be an object\")}};var En={apply:function apply(){return ce.Call(ce.Call,null,arguments)},construct:function construct(e,t){if(!ce.IsConstructor(e)){throw new TypeError(\"First argument must be a constructor.\")}var r=arguments.length>2?arguments[2]:e;if(!ce.IsConstructor(r)){throw new TypeError(\"new.target must be a constructor.\")}return ce.Construct(e,t,r,\"internal\")},deleteProperty:function deleteProperty(e,t){In(e);if(s){var r=Object.getOwnPropertyDescriptor(e,t);if(r&&!r.configurable){return false}}return delete e[t]},has:function has(e,t){In(e);return t in e}};if(Object.getOwnPropertyNames){Object.assign(En,{ownKeys:function ownKeys(e){In(e);var t=Object.getOwnPropertyNames(e);if(ce.IsCallable(Object.getOwnPropertySymbols)){x(t,Object.getOwnPropertySymbols(e))}return t}})}var Pn=function ConvertExceptionToBoolean(e){return!i(e)};if(Object.preventExtensions){Object.assign(En,{isExtensible:function isExtensible(e){In(e);return Object.isExtensible(e)},preventExtensions:function preventExtensions(e){In(e);return Pn(function(){return Object.preventExtensions(e)})}})}if(s){var Cn=function get(e,t,r){var n=Object.getOwnPropertyDescriptor(e,t);if(!n){var o=Object.getPrototypeOf(e);if(o===null){return void 0}return Cn(o,t,r)}if(\"value\"in n){return n.value}if(n.get){return ce.Call(n.get,r)}return void 0};var Mn=function set(e,r,n,o){var i=Object.getOwnPropertyDescriptor(e,r);if(!i){var a=Object.getPrototypeOf(e);if(a!==null){return Mn(a,r,n,o)}i={value:void 0,writable:true,enumerable:true,configurable:true}}if(\"value\"in i){if(!i.writable){return false}if(!ce.TypeIsObject(o)){return false}var u=Object.getOwnPropertyDescriptor(o,r);if(u){return ae.defineProperty(o,r,{value:n})}else{return ae.defineProperty(o,r,{value:n,writable:true,enumerable:true,configurable:true})}}if(i.set){t(i.set,o,n);return true}return false};Object.assign(En,{defineProperty:function defineProperty(e,t,r){In(e);return Pn(function(){return Object.defineProperty(e,t,r)})},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(e,t){In(e);return Object.getOwnPropertyDescriptor(e,t)},get:function get(e,t){In(e);var r=arguments.length>2?arguments[2]:e;return Cn(e,t,r)},set:function set(e,t,r){In(e);var n=arguments.length>3?arguments[3]:e;return Mn(e,t,r,n)}})}if(Object.getPrototypeOf){var xn=Object.getPrototypeOf;En.getPrototypeOf=function getPrototypeOf(e){In(e);return xn(e)}}if(Object.setPrototypeOf&&En.getPrototypeOf){var Nn=function(e,t){var r=t;while(r){if(e===r){return true}r=En.getPrototypeOf(r)}return false};Object.assign(En,{setPrototypeOf:function setPrototypeOf(e,t){In(e);if(t!==null&&!ce.TypeIsObject(t)){throw new TypeError(\"proto must be an object or null\")}if(t===ae.getPrototypeOf(e)){return true}if(ae.isExtensible&&!ae.isExtensible(e)){return false}if(Nn(e,t)){return false}Object.setPrototypeOf(e,t);return true}})}var An=function(e,t){if(!ce.IsCallable(S.Reflect[e])){h(S.Reflect,e,t)}else{var r=a(function(){S.Reflect[e](1);S.Reflect[e](NaN);S.Reflect[e](true);return true});if(r){ne(S.Reflect,e,t)}}};Object.keys(En).forEach(function(e){An(e,En[e])});var Rn=S.Reflect.getPrototypeOf;if(c&&Rn&&Rn.name!==\"getPrototypeOf\"){ne(S.Reflect,\"getPrototypeOf\",function getPrototypeOf(e){return t(Rn,S.Reflect,e)})}if(S.Reflect.setPrototypeOf){if(a(function(){S.Reflect.setPrototypeOf(1,{});return true})){ne(S.Reflect,\"setPrototypeOf\",En.setPrototypeOf)}}if(S.Reflect.defineProperty){if(!a(function(){var e=!S.Reflect.defineProperty(1,\"test\",{value:1});var t=typeof Object.preventExtensions!==\"function\"||!S.Reflect.defineProperty(Object.preventExtensions({}),\"test\",{});return e&&t})){ne(S.Reflect,\"defineProperty\",En.defineProperty)}}if(S.Reflect.construct){if(!a(function(){var e=function F(){};return S.Reflect.construct(function(){},[],e)instanceof e})){ne(S.Reflect,\"construct\",En.construct)}}if(String(new Date(NaN))!==\"Invalid Date\"){var _n=Date.prototype.toString;var kn=function toString(){var e=+this;if(e!==e){return\"Invalid Date\"}return ce.Call(_n,this)};ne(Date.prototype,\"toString\",kn)}var Ln={anchor:function anchor(e){return ce.CreateHTML(this,\"a\",\"name\",e)},big:function big(){return ce.CreateHTML(this,\"big\",\"\",\"\")},blink:function blink(){return ce.CreateHTML(this,\"blink\",\"\",\"\")},bold:function bold(){return ce.CreateHTML(this,\"b\",\"\",\"\")},fixed:function fixed(){return ce.CreateHTML(this,\"tt\",\"\",\"\")},fontcolor:function fontcolor(e){return ce.CreateHTML(this,\"font\",\"color\",e)},fontsize:function fontsize(e){return ce.CreateHTML(this,\"font\",\"size\",e)},italics:function italics(){return ce.CreateHTML(this,\"i\",\"\",\"\")},link:function link(e){return ce.CreateHTML(this,\"a\",\"href\",e)},small:function small(){return ce.CreateHTML(this,\"small\",\"\",\"\")},strike:function strike(){return ce.CreateHTML(this,\"strike\",\"\",\"\")},sub:function sub(){return ce.CreateHTML(this,\"sub\",\"\",\"\")},sup:function sub(){return ce.CreateHTML(this,\"sup\",\"\",\"\")}};l(Object.keys(Ln),function(e){var r=String.prototype[e];var n=false;if(ce.IsCallable(r)){var o=t(r,\"\",' \" ');var i=P([],o.match(/\"/g)).length;n=o!==o.toLowerCase()||i>2}else{n=true}if(n){ne(String.prototype,e,Ln[e])}});var Fn=function(){if(!oe){return false}var e=typeof JSON===\"object\"&&typeof JSON.stringify===\"function\"?JSON.stringify:null;if(!e){return false}if(typeof e($())!==\"undefined\"){return true}if(e([$()])!==\"[null]\"){return true}var t={a:$()};t[$()]=true;if(e(t)!==\"{}\"){return true}return false}();var Dn=a(function(){if(!oe){return true}return JSON.stringify(Object($()))===\"{}\"&&JSON.stringify([Object($())])===\"[{}]\"});if(Fn||!Dn){var zn=JSON.stringify;ne(JSON,\"stringify\",function stringify(e){if(typeof e===\"symbol\"){return}var n;if(arguments.length>1){n=arguments[1]}var o=[e];if(!r(n)){var i=ce.IsCallable(n)?n:null;var a=function(e,r){var n=i?t(i,this,e,r):r;if(typeof n!==\"symbol\"){if(re.symbol(n)){return Nt({})(n)}else{return n}}};o.push(a)}else{o.push(n)}if(arguments.length>2){o.push(arguments[2])}return zn.apply(this,o)})}return S});\n/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(C,e){\"use strict\";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return\"function\"==typeof e&&\"number\"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement(\"script\");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?n[o.call(e)]||\"object\":typeof e}var f=\"3.5.1\",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&\"length\"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&(\"array\"===n||0===t||\"number\"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for(\"boolean\"==typeof a&&(l=a,a=arguments[s]||{},s++),\"object\"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],\"__proto__\"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:\"jQuery\"+(f+Math.random()).replace(/\\D/g,\"\"),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||\"[object Object]\"!==o.call(e))&&(!(t=r(e))||\"function\"==typeof(n=v.call(t,\"constructor\")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,\"string\"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),\"function\"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){n[\"[object \"+t+\"]\"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S=\"sizzle\"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R=\"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",M=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",I=\"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",W=\"\\\\[\"+M+\"*(\"+I+\")(?:\"+M+\"*([*^$|!~]?=)\"+M+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+I+\"))|)\"+M+\"*\\\\]\",F=\":(\"+I+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+W+\")*)|.*)\\\\)|)\",B=new RegExp(M+\"+\",\"g\"),$=new RegExp(\"^\"+M+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+M+\"+$\",\"g\"),_=new RegExp(\"^\"+M+\"*,\"+M+\"*\"),z=new RegExp(\"^\"+M+\"*([>+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp(F),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+F),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+M+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+M+\"*(?:([+-]|)\"+M+\"*(\\\\d+)|))\"+M+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+R+\")$\",\"i\"),needsContext:new RegExp(\"^\"+M+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+M+\"*((?:-\\\\d)?\\\\d*)\"+M+\"*\\\\)|)(?=[^-]|$)\",\"i\")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),ne=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?\"\\0\"===e?\"\\ufffd\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&\"fieldset\"===e.nodeName.toLowerCase()},{dir:\"parentNode\",next:\"legend\"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],\"string\"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?\"#\"+s:\":scope\")+\" \"+xe(l[o]);c=l.join(\",\")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute(\"id\")}}}return g(t.replace($,\"$1\"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+\" \")>b.cacheLength&&delete e[r.shift()],e[t+\" \"]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split(\"|\"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return\"input\"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return(\"input\"===t||\"button\"===t)&&e.type===n}}function ge(t){return function(e){return\"form\"in e?e.parentNode&&!1===e.disabled?\"label\"in e?\"label\"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:\"label\"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||\"HTML\")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement(\"div\")),\"undefined\"!=typeof e.querySelectorAll&&!e.querySelectorAll(\":scope fieldset div\").length}),d.attributes=ce(function(e){return e.className=\"i\",!e.getAttribute(\"className\")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute(\"id\")===t}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=\"undefined\"!=typeof e.getAttributeNode&&e.getAttributeNode(\"id\");return t&&t.value===n}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(\"undefined\"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=\"<a id='\"+S+\"'></a><select id='\"+S+\"-\\r\\\\' msallowcapture=''><option selected=''></option></select>\",e.querySelectorAll(\"[msallowcapture^='']\").length&&v.push(\"[*^$]=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\"[selected]\").length||v.push(\"\\\\[\"+M+\"*(?:value|\"+R+\")\"),e.querySelectorAll(\"[id~=\"+S+\"-]\").length||v.push(\"~=\"),(t=C.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||v.push(\"\\\\[\"+M+\"*name\"+M+\"*=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+S+\"+*\").length||v.push(\".#.+[+~]\"),e.querySelectorAll(\"\\\\\\f\"),v.push(\"[\\\\r\\\\n\\\\f]\")}),ce(function(e){e.innerHTML=\"<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>\";var t=C.createElement(\"input\");t.setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),e.querySelectorAll(\"[name=d]\").length&&v.push(\"name\"+M+\"*[*^$|!~]?=\"),2!==e.querySelectorAll(\":enabled\").length&&v.push(\":enabled\",\":disabled\"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&v.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),v.push(\",.*:\")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,\"*\"),c.call(e,\"[s!='']:x\"),s.push(\"!=\",F)}),v=v.length&&new RegExp(v.join(\"|\")),s=s.length&&new RegExp(s.join(\"|\")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+\" \"]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+\"\").replace(re,ie)},se.error=function(e){throw new Error(\"Syntax error, unrecognized expression: \"+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||\"\").replace(te,ne),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&m(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?\"!=\"===r:!r||(t+=\"\",\"=\"===r?t===i:\"!=\"===r?t!==i:\"^=\"===r?i&&0===t.indexOf(i):\"*=\"===r?i&&-1<t.indexOf(i):\"$=\"===r?i&&t.slice(-i.length)===i:\"~=\"===r?-1<(\" \"+t.replace(B,\" \")+\" \").indexOf(i):\"|=\"===r&&(t===i||t.slice(0,i.length+1)===i+\"-\"))}},CHILD:function(h,e,t,g,v){var y=\"nth\"!==h.slice(0,3),m=\"last\"!==h.slice(-4),x=\"of-type\"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?\"nextSibling\":\"previousSibling\",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l=\"only\"===h&&!u&&\"nextSibling\"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error(\"unsupported pseudo: \"+e);return a[S]?a(o):1<a.length?(t=[e,e,\"\",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,\"$1\"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||\"\")||se.error(\"unsupported lang: \"+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute(\"xml:lang\")||e.getAttribute(\"lang\"))return(t=t.toLowerCase())===n||0===t.indexOf(n+\"-\")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||\"text\"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r=\"\";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&\"parentNode\"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||\"*\",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[\" \"],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:\" \"===e[s-2].type?\"*\":\"\"})).replace($,\"$1\"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+\" \"];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($,\" \")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+\" \"];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l=\"0\",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG(\"*\",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l=\"function\"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&\"ID\"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split(\"\").sort(D).join(\"\")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement(\"fieldset\"))}),ce(function(e){return e.innerHTML=\"<a href='#'></a>\",\"#\"===e.firstChild.getAttribute(\"href\")})||fe(\"type|href|height|width\",function(e,t,n){if(!n)return e.getAttribute(t,\"type\"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML=\"<input/>\",e.firstChild.setAttribute(\"value\",\"\"),\"\"===e.firstChild.getAttribute(\"value\")})||fe(\"value\",function(e,t,n){if(!n&&\"input\"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute(\"disabled\")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[\":\"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,\"string\"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a=\"string\"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,\"parentNode\")},parentsUntil:function(e,t,n){return h(e,\"parentNode\",n)},next:function(e){return O(e,\"nextSibling\")},prev:function(e){return O(e,\"previousSibling\")},nextAll:function(e){return h(e,\"nextSibling\")},prevAll:function(e){return h(e,\"previousSibling\")},nextUntil:function(e,t,n){return h(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return h(e,\"previousSibling\",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,\"template\")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return\"Until\"!==r.slice(-5)&&(t=e),t&&\"string\"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\\x20\\t\\r\\n\\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r=\"string\"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:\"\")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&\"string\"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t=\"\",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=\"\"),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[[\"notify\",\"progress\",S.Callbacks(\"memory\"),S.Callbacks(\"memory\"),2],[\"resolve\",\"done\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),1,\"rejected\"]],i=\"pending\",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},\"catch\":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+\"With\"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError(\"Thenable self-resolution\");t=e&&(\"object\"==typeof e||\"function\"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+\"With\"](this===s?void 0:this,arguments),this},s[t[0]+\"With\"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),\"pending\"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn(\"jQuery.Deferred exception: \"+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener(\"DOMContentLoaded\",B),C.removeEventListener(\"load\",B),S.ready()}S.fn.ready=function(e){return F.then(e)[\"catch\"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,\"complete\"===E.readyState||\"loading\"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener(\"DOMContentLoaded\",B),C.addEventListener(\"load\",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if(\"object\"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,\"ms-\").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if(\"string\"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&\"string\"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(K,\"-$&\").toLowerCase(),\"string\"==typeof(n=e.getAttribute(r))){try{n=\"true\"===(i=n)||\"false\"!==i&&(\"null\"===i?null:i===+i+\"\"?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,\"hasDataAttrs\"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf(\"data-\")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,\"hasDataAttrs\",!0)}return i}return\"object\"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks(\"once memory\").add(function(){Y.remove(e,[t+\"queue\",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return\"string\"!=typeof t&&(n=t,t=\"fx\",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),\"fx\"===t&&\"inprogress\"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};\"string\"!=typeof e&&(t=e,e=void 0),e=e||\"fx\";while(a--)(n=Y.get(o[a],e+\"queueHooks\"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,te=new RegExp(\"^(?:([+-])=|)(\"+ee+\")([a-z%]*)$\",\"i\"),ne=[\"Top\",\"Right\",\"Bottom\",\"Left\"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return\"none\"===(e=t||e).style.display||\"\"===e.style.display&&ie(e)&&\"none\"===S.css(e,\"display\")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,\"\")},u=s(),l=n&&n[3]||(S.cssNumber[t]?\"\":\"px\"),c=e.nodeType&&(S.cssNumber[t]||\"px\"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?(\"none\"===n&&(l[c]=Y.get(r,\"display\")||null,l[c]||(r.style.display=\"\")),\"\"===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,\"display\"),o.parentNode.removeChild(o),\"none\"===u&&(u=\"block\"),ue[s]=u)))):\"none\"!==n&&(l[c]=\"none\",Y.set(r,\"display\",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(fe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),fe.setAttribute(\"checked\",\"checked\"),fe.setAttribute(\"name\",\"t\"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML=\"<textarea>x</textarea>\",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=\"<option></option>\",y.option=!!ce.lastChild;var ge={thead:[1,\"<table>\",\"</table>\"],col:[2,\"<table><colgroup>\",\"</colgroup></table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:[0,\"\",\"\"]};function ve(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],\"globalEval\",!t||Y.get(t[n],\"globalEval\"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,\"<select multiple='multiple'>\",\"</select>\"]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if(\"object\"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement(\"div\")),s=(de.exec(o)||[\"\",\"\"])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=\"\"}else p.push(t.createTextNode(o));f.textContent=\"\",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),\"script\"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||\"\")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==(\"focus\"===t)}function ke(e,t,n,r,i,o){var a,s;if(\"object\"==typeof t){for(s in\"string\"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&(\"string\"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return\"undefined\"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||\"\").match(P)||[\"\"]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(\".\")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||\"\").match(P)||[\"\"]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&(\"**\"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,\"events\")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!(\"click\"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(\"click\"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+\" \"]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Y.get(t,\"click\")||A(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,\"char\":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+\".\"+r.namespace:r.origType,r.selector,r.handler),this;if(\"object\"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&\"function\"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\\s*(?:[^=]|=\\s*.checked.)/i,je=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;function qe(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&S(e).children(\"tbody\")[0]||e}function Le(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function He(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&\"string\"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,\"script\"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,\"script\"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||\"\")&&!Y.access(u,\"globalEval\")&&S.contains(l,u)&&(u.src&&\"module\"!==(u.type||\"\").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute(\"nonce\")},l):b(u.textContent.replace(je,\"\"),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,\"script\")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,\"input\"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:\"input\"!==l&&\"textarea\"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,\"script\")).length&&ye(a,!f&&ve(e,\"script\")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp(\"^(\"+ee+\")(?!px)[a-z%]+$\",\"i\"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join(\"|\"),\"i\");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(\"\"!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+\"\":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText=\"position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0\",l.style.cssText=\"position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%\",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n=\"1%\"!==e.top,s=12===t(e.marginLeft),l.style.right=\"60%\",o=36===t(e.right),r=36===t(e.width),l.style.position=\"absolute\",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement(\"div\"),l=E.createElement(\"div\");l.style&&(l.style.backgroundClip=\"content-box\",l.cloneNode(!0).style.backgroundClip=\"\",y.clearCloneStyle=\"content-box\"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement(\"table\"),t=E.createElement(\"tr\"),n=E.createElement(\"div\"),e.style.cssText=\"position:absolute;left:-11111px\",t.style.height=\"1px\",n.style.height=\"9px\",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=[\"Webkit\",\"Moz\",\"ms\"],ze=E.createElement(\"div\").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Qe={letterSpacing:\"0\",fontWeight:\"400\"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ke(e,t,n,r,i,o){var a=\"width\"===t?1:0,s=0,u=0;if(n===(r?\"border\":\"content\"))return 0;for(;a<4;a+=2)\"margin\"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?(\"content\"===n&&(u-=S.css(e,\"padding\"+ne[a],!0,i)),\"margin\"!==n&&(u-=S.css(e,\"border\"+ne[a]+\"Width\",!0,i))):(u+=S.css(e,\"padding\"+ne[a],!0,i),\"padding\"!==n?u+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i):s+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,r),o=i,a=Be(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a=\"auto\"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===S.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===S.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?\"\":\"px\")),y.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),\"normal\"===i&&t in Qe&&(i=Qe[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each([\"height\",\"width\"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&\"absolute\"===i.position,a=(o||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e[\"offset\"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,\"border\",!1,i)-.5)),s&&(r=te.exec(t))&&\"px\"!==(r[3]||\"px\")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,\"marginLeft\"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+\"px\"}),S.each({margin:\"\",padding:\"\",border:\"Width\"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r=\"string\"==typeof e?e.split(\" \"):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},\"margin\"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?\"\":\"px\")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=ne[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners[\"*\"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&\"expand\"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=[\"*\"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f=\"width\"in t||\"height\"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,\"fxshow\");for(r in n.queue||(null==(a=S._queueHooks(e,\"fx\")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,\"fx\").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||\"toggle\"===i,i===(g?\"hide\":\"show\")){if(\"show\"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,\"display\")),\"none\"===(c=S.css(e,\"display\"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,\"display\"),le([e]))),(\"inline\"===c||\"inline-block\"===c&&null!=l)&&\"none\"===S.css(e,\"float\")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l=\"none\"===c?\"\":c)),h.display=\"inline-block\")),n.overflow&&(h.overflow=\"hidden\",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?\"hidden\"in v&&(g=v.hidden):v=Y.access(e,\"fxshow\",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,\"fxshow\"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&\"object\"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,\"finish\"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return\"string\"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||\"fx\",[]),this.each(function(){var e=!0,t=null!=i&&i+\"queueHooks\",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||\"fx\"),this.each(function(){var e,t=Y.get(this),n=t[a+\"queue\"],r=t[a+\"queueHooks\"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each([\"toggle\",\"show\",\"hide\"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||\"boolean\"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt(\"show\"),slideUp:lt(\"hide\"),slideToggle:lt(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||\"fx\",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement(\"input\"),it=E.createElement(\"select\").appendChild(E.createElement(\"option\")),rt.type=\"checkbox\",y.checkOn=\"\"!==rt.value,y.optSelected=it.selected,(rt=E.createElement(\"input\")).value=\"t\",rt.type=\"radio\",y.radioValue=\"t\"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return\"undefined\"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&\"radio\"===t&&A(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(\" \")}function yt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function mt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,\"tabindex\");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr(\"class\",\"\");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])while(-1<r.indexOf(\" \"+o+\" \"))r=r.replace(\" \"+o+\" \",\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},toggleClass:function(i,t){var o=typeof i,a=\"string\"===o||Array.isArray(i);return\"boolean\"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&\"boolean\"!==o||((e=yt(this))&&Y.set(this,\"__className__\",e),this.setAttribute&&this.setAttribute(\"class\",e||!1===i?\"\":Y.get(this,\"__className__\")||\"\"))})},hasClass:function(e){var t,n,r=0;t=\" \"+e+\" \";while(n=this[r++])if(1===n.nodeType&&-1<(\" \"+vt(yt(n))+\" \").indexOf(t))return!0;return!1}});var xt=/\\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t=\"\":\"number\"==typeof t?t+=\"\":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?\"\":e+\"\"})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&\"set\"in r&&void 0!==r.set(this,t,\"value\")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&\"get\"in r&&void 0!==(e=r.get(t,\"value\"))?e:\"string\"==typeof(e=t.value)?e.replace(xt,\"\"):null==e?\"\":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,\"value\");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,\"optgroup\"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each([\"radio\",\"checkbox\"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}),y.focusin=\"onfocusin\"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,\"type\")?e.type:e,h=v.call(e,\"namespace\")?e.namespace.split(\".\"):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(\".\")&&(d=(h=d.split(\".\")).shift(),h.sort()),u=d.indexOf(\":\")<0&&\"on\"+d,(e=e[S.expando]?e:new S.Event(d,\"object\"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join(\".\"),e.rnamespace=e.namespace?new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,\"events\")||Object.create(null))[e.type]&&Y.get(o,\"handle\"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:\"focusin\",blur:\"focusout\"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\\?/;S.parseXML=function(e){var t;if(!e||\"string\"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,\"text/xml\")}catch(e){t=void 0}return t&&!t.getElementsByTagName(\"parsererror\").length||S.error(\"Invalid XML: \"+e),t};var St=/\\[\\]$/,kt=/\\r?\\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+\"[\"+(\"object\"==typeof t&&null!=t?e:\"\")+\"]\",t,r,i)});else if(r||\"object\"!==w(e))i(n,e);else for(t in e)Dt(n+\"[\"+t+\"]\",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join(\"&\")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,\"elements\");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(\":disabled\")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,\"\\r\\n\")}}):{name:t.name,value:n.replace(kt,\"\\r\\n\")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\\/\\//,Rt={},Mt={},It=\"*/\".concat(\"*\"),Wt=E.createElement(\"a\");function Ft(o){return function(e,t){\"string\"!=typeof e&&(t=e,e=\"*\");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])\"+\"===n[0]?(n=n.slice(1)||\"*\",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return\"string\"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s[\"*\"]&&l(\"*\")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":It,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){\"object\"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks(\"once memory\"),w=v.statusCode||{},a={},s={},u=\"canceled\",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+\" \"]=(n[t[1].toLowerCase()+\" \"]||[]).concat(t[2])}t=n[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+\"\").replace(Pt,Tt.protocol+\"//\"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||\"*\").toLowerCase().match(P)||[\"\"],null==v.crossDomain){r=E.createElement(\"a\");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+\"//\"+Wt.host!=r.protocol+\"//\"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&\"string\"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger(\"ajaxStart\"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,\"\"),v.hasContent?v.data&&v.processData&&0===(v.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(v.data=v.data.replace(jt,\"+\")):(o=v.url.slice(f.length),v.data&&(v.processData||\"string\"==typeof v.data)&&(f+=(Et.test(f)?\"&\":\"?\")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,\"$1\"),o=(Et.test(f)?\"&\":\"?\")+\"_=\"+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader(\"If-Modified-Since\",S.lastModified[f]),S.etag[f]&&T.setRequestHeader(\"If-None-Match\",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader(\"Content-Type\",v.contentType),T.setRequestHeader(\"Accept\",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+(\"*\"!==v.dataTypes[0]?\", \"+It+\"; q=0.01\":\"\"):v.accepts[\"*\"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u=\"abort\",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger(\"ajaxSend\",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort(\"timeout\")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,\"No Transport\");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||\"\",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while(\"*\"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray(\"script\",v.dataTypes)&&(v.converters[\"text script\"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e[\"throws\"])t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader(\"Last-Modified\"))&&(S.lastModified[f]=u),(u=T.getResponseHeader(\"etag\"))&&(S.etag[f]=u)),204===e||\"HEAD\"===v.type?l=\"nocontent\":304===e?l=\"notmodified\":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l=\"error\",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+\"\",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?\"ajaxSuccess\":\"ajaxError\",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger(\"ajaxComplete\",[T,v]),--S.active||S.event.trigger(\"ajaxStop\")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,\"json\")},getScript:function(e,t){return S.get(e,void 0,t,\"script\")}}),S.each([\"get\",\"post\"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&\"withCredentials\"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e[\"X-Requested-With\"]||(e[\"X-Requested-With\"]=\"XMLHttpRequest\"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,\"abort\"===e?r.abort():\"error\"===e?\"number\"!=typeof r.status?t(0,\"error\"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,\"text\"!==(r.responseType||\"text\")||\"string\"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o(\"error\"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o(\"abort\");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")}),S.ajaxTransport(\"script\",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S(\"<script>\").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on(\"load error\",i=function(e){r.remove(),i=null,e&&t(\"error\"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=Xt.pop()||S.expando+\"_\"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Vt.test(e.data)&&\"data\");if(a||\"jsonp\"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,\"$1\"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||S.error(r+\" was not called\"),o[0]},e.dataTypes[0]=\"json\",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"<form></form><form></form>\",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return\"string\"!=typeof e?[]:(\"boolean\"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument(\"\")).createElement(\"base\")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&\"object\"==typeof t&&(i=\"POST\"),0<a.length&&S.ajax({url:e,type:i||\"GET\",dataType:\"html\",data:t}).done(function(e){o=arguments,a.html(r?S(\"<div>\").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,\"position\"),c=S(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=S.css(e,\"top\"),u=S.css(e,\"left\"),(\"absolute\"===l||\"fixed\"===l)&&-1<(o+u).indexOf(\"auto\")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),\"using\"in t?t.using.call(e,f):(\"number\"==typeof f.top&&(f.top+=\"px\"),\"number\"==typeof f.left&&(f.left+=\"px\"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if(\"fixed\"===S.css(r,\"position\"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&\"static\"===S.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,\"borderTopWidth\",!0),i.left+=S.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-S.css(r,\"marginTop\",!0),left:t.left-i.left-S.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===S.css(e,\"position\"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each([\"top\",\"left\"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+\"px\":t})}),S.each({Height:\"height\",Width:\"width\"},function(a,s){S.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||\"boolean\"!=typeof e),i=r||(!0===e||!0===t?\"margin\":\"border\");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf(\"outer\")?e[\"inner\"+a]:e.document.documentElement[\"client\"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body[\"scroll\"+a],r[\"scroll\"+a],e.body[\"offset\"+a],r[\"offset\"+a],r[\"client\"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?\"\":(e+\"\").replace(Gt,\"\")},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},\"undefined\"==typeof e&&(C.jQuery=C.$=S),S});\n/*\n * jQuery throttle / debounce - v1.1 - 3/7/2010\n * http://benalman.com/projects/jquery-throttle-debounce-plugin/\n * \n * Copyright (c) 2010 \"Cowboy\" Ben Alman\n * Dual licensed under the MIT and GPL licenses.\n * http://benalman.com/about/license/\n */\n(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!==\"boolean\"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);\n/*!\n * imagesLoaded PACKAGED v4.1.4\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n!function(e,t){\"function\"==typeof define&&define.amd?define(\"ev-emitter/ev-emitter\",t):\"object\"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}(\"undefined\"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o<i.length;o++){var r=i[o],s=n&&n[r];s&&(this.off(e,r),delete n[r]),r.apply(this,t)}return this}},t.allOff=function(){delete this._events,delete this._onceEvents},e}),function(e,t){\"use strict\";\"function\"==typeof define&&define.amd?define([\"ev-emitter/ev-emitter\"],function(i){return t(e,i)}):\"object\"==typeof module&&module.exports?module.exports=t(e,require(\"ev-emitter\")):e.imagesLoaded=t(e,e.EvEmitter)}(\"undefined\"!=typeof window?window:this,function(e,t){function i(e,t){for(var i in t)e[i]=t[i];return e}function n(e){if(Array.isArray(e))return e;var t=\"object\"==typeof e&&\"number\"==typeof e.length;return t?d.call(e):[e]}function o(e,t,r){if(!(this instanceof o))return new o(e,t,r);var s=e;return\"string\"==typeof e&&(s=document.querySelectorAll(e)),s?(this.elements=n(s),this.options=i({},this.options),\"function\"==typeof t?r=t:i(this.options,t),r&&this.on(\"always\",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(this.check.bind(this))):void a.error(\"Bad element for imagesLoaded \"+(s||e))}function r(e){this.img=e}function s(e,t){this.url=e,this.element=t,this.img=new Image}var h=e.jQuery,a=e.console,d=Array.prototype.slice;o.prototype=Object.create(t.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(e){\"IMG\"==e.nodeName&&this.addImage(e),this.options.background===!0&&this.addElementBackgroundImages(e);var t=e.nodeType;if(t&&u[t]){for(var i=e.querySelectorAll(\"img\"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if(\"string\"==typeof this.options.background){var r=e.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var u={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(e){var t=getComputedStyle(e);if(t)for(var i=/url\\((['\"])?(.*?)\\1\\)/gi,n=i.exec(t.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,e),n=i.exec(t.backgroundImage)}},o.prototype.addImage=function(e){var t=new r(e);this.images.push(t)},o.prototype.addBackground=function(e,t){var i=new s(e,t);this.images.push(i)},o.prototype.check=function(){function e(e,i,n){setTimeout(function(){t.progress(e,i,n)})}var t=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(t){t.once(\"progress\",e),t.check()}):void this.complete()},o.prototype.progress=function(e,t,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emitEvent(\"progress\",[this,e,t]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,e),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log(\"progress: \"+i,e,t)},o.prototype.complete=function(){var e=this.hasAnyBroken?\"fail\":\"done\";if(this.isComplete=!0,this.emitEvent(e,[this]),this.emitEvent(\"always\",[this]),this.jqDeferred){var t=this.hasAnyBroken?\"reject\":\"resolve\";this.jqDeferred[t](this)}},r.prototype=Object.create(t.prototype),r.prototype.check=function(){var e=this.getIsImageComplete();return e?void this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"):(this.proxyImage=new Image,this.proxyImage.addEventListener(\"load\",this),this.proxyImage.addEventListener(\"error\",this),this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},r.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.img,t])},r.prototype.handleEvent=function(e){var t=\"on\"+e.type;this[t]&&this[t](e)},r.prototype.onload=function(){this.confirm(!0,\"onload\"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,\"onerror\"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener(\"load\",this),this.proxyImage.removeEventListener(\"error\",this),this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),this.img.src=this.url;var e=this.getIsImageComplete();e&&(this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.element,t])},o.makeJQueryPlugin=function(t){t=t||e.jQuery,t&&(h=t,h.fn.imagesLoaded=function(e,t){var i=new o(this,e,t);return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o});\n/*! lz-string-1.3.3-min.js | (c) 2013 Pieroxy | Licensed under a WTFPL license */\nvar LZString={_keyStr:\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",_f:String.fromCharCode,compressToBase64:function(e){if(e==null)return\"\";var t=\"\";var n,r,i,s,o,u,a;var f=0;e=LZString.compress(e);while(f<e.length*2){if(f%2==0){n=e.charCodeAt(f/2)>>8;r=e.charCodeAt(f/2)&255;if(f/2+1<e.length)i=e.charCodeAt(f/2+1)>>8;else i=NaN}else{n=e.charCodeAt((f-1)/2)&255;if((f+1)/2<e.length){r=e.charCodeAt((f+1)/2)>>8;i=e.charCodeAt((f+1)/2)&255}else r=i=NaN}f+=3;s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+LZString._keyStr.charAt(s)+LZString._keyStr.charAt(o)+LZString._keyStr.charAt(u)+LZString._keyStr.charAt(a)}return t},decompressFromBase64:function(e){if(e==null)return\"\";var t=\"\",n=0,r,i,s,o,u,a,f,l,c=0,h=LZString._f;e=e.replace(/[^A-Za-z0-9\\+\\/\\=]/g,\"\");while(c<e.length){u=LZString._keyStr.indexOf(e.charAt(c++));a=LZString._keyStr.indexOf(e.charAt(c++));f=LZString._keyStr.indexOf(e.charAt(c++));l=LZString._keyStr.indexOf(e.charAt(c++));i=u<<2|a>>4;s=(a&15)<<4|f>>2;o=(f&3)<<6|l;if(n%2==0){r=i<<8;if(f!=64){t+=h(r|s)}if(l!=64){r=o<<8}}else{t=t+h(r|i);if(f!=64){r=s<<8}if(l!=64){t+=h(r|o)}}n+=3}return LZString.decompress(t)},compressToUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i,s=0,o=LZString._f;e=LZString.compress(e);for(n=0;n<e.length;n++){r=e.charCodeAt(n);switch(s++){case 0:t+=o((r>>1)+32);i=(r&1)<<14;break;case 1:t+=o(i+(r>>2)+32);i=(r&3)<<13;break;case 2:t+=o(i+(r>>3)+32);i=(r&7)<<12;break;case 3:t+=o(i+(r>>4)+32);i=(r&15)<<11;break;case 4:t+=o(i+(r>>5)+32);i=(r&31)<<10;break;case 5:t+=o(i+(r>>6)+32);i=(r&63)<<9;break;case 6:t+=o(i+(r>>7)+32);i=(r&127)<<8;break;case 7:t+=o(i+(r>>8)+32);i=(r&255)<<7;break;case 8:t+=o(i+(r>>9)+32);i=(r&511)<<6;break;case 9:t+=o(i+(r>>10)+32);i=(r&1023)<<5;break;case 10:t+=o(i+(r>>11)+32);i=(r&2047)<<4;break;case 11:t+=o(i+(r>>12)+32);i=(r&4095)<<3;break;case 12:t+=o(i+(r>>13)+32);i=(r&8191)<<2;break;case 13:t+=o(i+(r>>14)+32);i=(r&16383)<<1;break;case 14:t+=o(i+(r>>15)+32,(r&32767)+32);s=0;break}}return t+o(i+32)},decompressFromUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i=0,s=0,o=LZString._f;while(s<e.length){r=e.charCodeAt(s)-32;switch(i++){case 0:n=r<<1;break;case 1:t+=o(n|r>>14);n=(r&16383)<<2;break;case 2:t+=o(n|r>>13);n=(r&8191)<<3;break;case 3:t+=o(n|r>>12);n=(r&4095)<<4;break;case 4:t+=o(n|r>>11);n=(r&2047)<<5;break;case 5:t+=o(n|r>>10);n=(r&1023)<<6;break;case 6:t+=o(n|r>>9);n=(r&511)<<7;break;case 7:t+=o(n|r>>8);n=(r&255)<<8;break;case 8:t+=o(n|r>>7);n=(r&127)<<9;break;case 9:t+=o(n|r>>6);n=(r&63)<<10;break;case 10:t+=o(n|r>>5);n=(r&31)<<11;break;case 11:t+=o(n|r>>4);n=(r&15)<<12;break;case 12:t+=o(n|r>>3);n=(r&7)<<13;break;case 13:t+=o(n|r>>2);n=(r&3)<<14;break;case 14:t+=o(n|r>>1);n=(r&1)<<15;break;case 15:t+=o(n|r);i=0;break}s++}return LZString.decompress(t)},compress:function(e){if(e==null)return\"\";var t,n,r={},i={},s=\"\",o=\"\",u=\"\",a=2,f=3,l=2,c=\"\",h=0,p=0,d,v=LZString._f;for(d=0;d<e.length;d+=1){s=e.charAt(d);if(!Object.prototype.hasOwnProperty.call(r,s)){r[s]=f++;i[s]=true}o=u+s;if(Object.prototype.hasOwnProperty.call(r,o)){u=o}else{if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}r[o]=f++;u=String(s)}}if(u!==\"\"){if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}}n=2;for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}while(true){h=h<<1;if(p==15){c+=v(h);break}else p++}return c},decompress:function(e){if(e==null)return\"\";if(e==\"\")return null;var t=[],n,r=4,i=4,s=3,o=\"\",u=\"\",a,f,l,c,h,p,d,v=LZString._f,m={string:e,val:e.charCodeAt(0),position:32768,index:1};for(a=0;a<3;a+=1){t[a]=a}l=0;h=Math.pow(2,2);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(n=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 2:return\"\"}t[3]=d;f=u=d;while(true){if(m.index>m.string.length){return\"\"}l=0;h=Math.pow(2,s);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(d=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 2:return u}if(r==0){r=Math.pow(2,s);s++}if(t[d]){o=t[d]}else{if(d===i){o=f+f.charAt(0)}else{return null}}u+=o;t[i++]=f+o.charAt(0);r--;f=o;if(r==0){r=Math.pow(2,s);s++}}}};if(typeof module!==\"undefined\"&&module!=null){module.exports=LZString}\n/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */\nvar saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||function(e){\"use strict\";var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=e.URL||e.webkitURL||e,i=t.createElementNS(\"http://www.w3.org/1999/xhtml\",\"a\"),s=\"download\"in i,o=function(n){var r=t.createEvent(\"MouseEvents\");r.initMouseEvent(\"click\",true,false,e,0,0,0,0,0,false,false,false,false,0,null);n.dispatchEvent(r)},u=e.webkitRequestFileSystem,a=e.requestFileSystem||u||e.mozRequestFileSystem,f=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},l=\"application/octet-stream\",c=0,h=[],p=function(){var e=h.length;while(e--){var t=h[e];if(typeof t===\"string\"){r.revokeObjectURL(t)}else{t.remove()}}h.length=0},d=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var i=e[\"on\"+t[r]];if(typeof i===\"function\"){try{i.call(e,n||e)}catch(s){f(s)}}}},v=function(t,r){var f=this,p=t.type,v=false,m,g,y=function(){var e=n().createObjectURL(t);h.push(e);return e},b=function(){d(f,\"writestart progress write writeend\".split(\" \"))},w=function(){if(v||!m){m=y(t)}if(g){g.location.href=m}else{window.open(m,\"_blank\")}f.readyState=f.DONE;b()},E=function(e){return function(){if(f.readyState!==f.DONE){return e.apply(this,arguments)}}},S={create:true,exclusive:false},x;f.readyState=f.INIT;if(!r){r=\"download\"}if(s){m=y(t);i.href=m;i.download=r;o(i);f.readyState=f.DONE;b();return}if(e.chrome&&p&&p!==l){x=t.slice||t.webkitSlice;t=x.call(t,0,t.size,l);v=true}if(u&&r!==\"download\"){r+=\".download\"}if(p===l||u){g=e}if(!a){w();return}c+=t.size;a(e.TEMPORARY,c,E(function(e){e.root.getDirectory(\"saved\",S,E(function(e){var n=function(){e.getFile(r,S,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){g.location.href=e.toURL();h.push(e);f.readyState=f.DONE;d(f,\"writeend\",t)};n.onerror=function(){var e=n.error;if(e.code!==e.ABORT_ERR){w()}};\"writestart progress write abort\".split(\" \").forEach(function(e){n[\"on\"+e]=f[\"on\"+e]});n.write(t);f.abort=function(){n.abort();f.readyState=f.DONE};f.readyState=f.WRITING}),w)}),w)};e.getFile(r,{create:false},E(function(e){e.remove();n()}),E(function(e){if(e.code===e.NOT_FOUND_ERR){n()}else{w()}}))}),w)}),w)},m=v.prototype,g=function(e,t){return new v(e,t)};m.abort=function(){var e=this;e.readyState=e.DONE;d(e,\"abort\")};m.readyState=m.INIT=0;m.WRITING=1;m.DONE=2;m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null;e.addEventListener(\"unload\",p,false);return g}(self)\n/*! seedrandom.js v2.3.3 | (c) 2013 David Bau, all rights reserved. | Licensed under a BSD-style license */\n!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=r&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=r&f+1],c=c*d+h[r&(h[f]=h[g=r&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&\"object\"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:\"string\"==e?a:a+\"\\0\"}function l(a,b){for(var c,d=a+\"\",e=0;e<d.length;)b[r&e]=r&(c^=19*b[r&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return a.crypto.getRandomValues(c=new Uint8Array(d)),n(c)}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o=c.pow(d,e),p=c.pow(2,f),q=2*p,r=d-1,s=c[\"seed\"+i]=function(a,f,g){var h=[],r=l(k(f?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=o,c=0;p>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=q;)a/=2,b/=2,c>>>=1;return(a+c)/b},r,this==c)};l(c[i](),b),g&&g.exports?g.exports=s:h&&h.amd&&h(function(){return s})}(this,[],Math,256,6,52,\"object\"==typeof module&&module,\"function\"==typeof define&&define,\"random\");\n/*! console_hack.js | (c) 2015 Thomas Michael Edwards | Licensed under SugarCube's Simple BSD license */\n!function(){for(var methods=[\"assert\",\"clear\",\"count\",\"debug\",\"dir\",\"dirxml\",\"error\",\"exception\",\"group\",\"groupCollapsed\",\"groupEnd\",\"info\",\"log\",\"markTimeline\",\"profile\",\"profileEnd\",\"table\",\"time\",\"timeEnd\",\"timeline\",\"timelineEnd\",\"timeStamp\",\"trace\",\"warn\"],length=methods.length,noop=function(){},console=window.console=window.console||{};length--;){var method=methods[length];console[method]||(console[method]=noop)}}();\n}else{document.documentElement.setAttribute(\"data-init\", \"lacking\");}\n</script>\n<style id=\"style-normalize\" type=\"text/css\">/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS and IE text size adjust after device orientation change,\n *    without disabling user zoom.\n */\n\nhtml {\n  font-family: sans-serif; /* 1 */\n  -ms-text-size-adjust: 100%; /* 2 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n  margin: 0;\n}\n\n/* HTML5 display definitions\n   ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; /* 1 */\n  vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n/* Links\n   ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n  background-color: transparent;\n}\n\n/**\n * Improve readability of focused elements when they are also in an\n * active/hover state.\n */\n\na:active,\na:hover {\n  outline: 0;\n}\n\n/* Text-level semantics\n   ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n  font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n  font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n/* Embedded content\n   ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n  border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n/* Grouping content\n   ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n  margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n  overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n/* Forms\n   ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n *    Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; /* 1 */\n  font: inherit; /* 2 */\n  margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n  overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n *    and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n *    `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; /* 2 */\n  cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n  line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n */\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  box-sizing: content-box; /* 2 */\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n  border: 0; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n  overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n  font-weight: bold;\n}\n\n/* Tables\n   ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n</style>\n<style id=\"style-init-screen\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/init-screen.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@-webkit-keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@-o-keyframes init-loading-spin {\n\t0%   { -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n#init-screen {\n\tdisplay: none;\n\tz-index: 500000;\n\tposition: fixed;\n\ttop: 0px;\n\tleft: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tfont: 28px/1 Helmet, Freesans, sans-serif;\n\tfont-weight: bold;\n\tcolor: #eee;\n\tbackground-color: #111;\n\ttext-align: center;\n}\n#init-screen > div {\n\tdisplay: none;\n\tposition: relative;\n\tmargin: 0 auto;\n\tmax-width: 1136px;\n\ttop: 25%;\n}\nhtml[data-init=\"no-js\"] #init-screen, html[data-init=\"lacking\"] #init-screen, html[data-init=\"loading\"] #init-screen {\n\tdisplay: block;\n}\nhtml[data-init=\"no-js\"] #init-no-js, html[data-init=\"lacking\"] #init-lacking {\n\tdisplay: block;\n\tpadding: 0 1em;\n}\nhtml[data-init=\"no-js\"] #init-no-js {\n\tcolor: red;\n}\nhtml[data-init=\"loading\"] #init-loading {\n\tdisplay: block;\n\tborder: 24px solid transparent;\n\tborder-radius: 50%;\n\tborder-top-color: #7f7f7f;\n\tborder-bottom-color: #7f7f7f;\n\twidth: 100px;\n\theight: 100px;\n\t-webkit-animation: init-loading-spin 2s linear infinite;\n\t     -o-animation: init-loading-spin 2s linear infinite;\n\t        animation: init-loading-spin 2s linear infinite;\n}\nhtml[data-init=\"loading\"] #init-loading > div {\n\ttext-indent: 9999em;\n\toverflow: hidden;\n\twhite-space: nowrap;\n}\nhtml[data-init=\"loading\"] #ui-bar, html[data-init=\"loading\"] #passages {\n\tdisplay: none;\n}\n</style>\n<style id=\"style-font\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/font.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@font-face {\n\t/*\n\t\ttme-fa-icons (2020-03-27)\n\n\t\t`tme-fa-icons` is a subset of the Font Awesome font v4.7.0 by Dave Gandy (http://fontawesome.com)\n\t\tand is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL).\n\t*/\n\tfont-family: \"tme-fa-icons\";\n\tsrc: url('data:application/octet-stream;base64,d09GRgABAAAAADLAAA8AAAAAWHgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+IEkIY21hcAAAAdgAAAG8AAAF3rob9jFjdnQgAAADlAAAABMAAAAgBtX/BGZwZ20AAAOoAAAFkAAAC3CKkZBZZ2FzcAAACTgAAAAIAAAACAAAABBnbHlmAAAJQAAAI6gAADv+gJOpzGhlYWQAACzoAAAAMwAAADYY1IZaaGhlYQAALRwAAAAgAAAAJAfCBClobXR4AAAtPAAAAJEAAAFMBfb/0WxvY2EAAC3QAAAAqAAAAKhjiHI5bWF4cAAALngAAAAgAAAAIAFjDA9uYW1lAAAumAAAAY0AAAL94+zEpHBvc3QAADAoAAACHAAAA11cG/YjcHJlcAAAMkQAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZNZgnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF4EMgf9z2KIYg5imAYUZgTJAQDSIQumAHic7dTVbltRAETR7dpNCikzQ8rMzMxt/M39mHnsU1/TfZz5jFpaV7pXJunMDLAZmOqaZjD5y4Tx+uPTyeL5lG2L5zN+L94zG8+ztr7ulXH1fra4bvK9M79xiWW2sNXPbWeFHexkF7vZw172sZ8DHOQQhznCUY5xnBOc5BSnOcNZVjnHeS5wkUtc5gpX/f3r3OAmt7jNHe5yj/s84CGPeMwTnvKM57zgJa94zRve8o73fOAjn/jMF77yje/84Ce/WGPun1zi/2tlXKbp3Xyc44bFyZanSWokJDXOOjXSk/LUSXn+pEwCKTNBaqQqZU5IjX+XMjukTBEp80TKZJEyY6RMGylzR8oEkjKLpEwlKfNJyqSSMrOkTC8pc0zKRJMy26RMOSnzTsrkk7IDpGwDKXtByoaQsiukbA0p+0PKJpGyU6RsFyl7RmosQcrukbKFpOwjKZtJyo6Ssq2k7C0pG0zKLpOy1aTsNymbTsrOk7L9pNwBUi4CKbeBlCtByr0g5XKQckNIuSak3BVSLgwpt4aUq0PK/SHlEpFyk0i5TqTcKVIuFim3i5QrRso9I+WykXLjXOYNzP8BuAPUwHicY2BAAxIQyBz0PwuEARJsA90AeJytVml300YUHXlJnIQsJQstamHExGmwRiZswYAJQbJjIF2crZWgixQ76b7xid/gX/Nk2nPoN35a7xsvJJC053Cak6N3583VzNtlElqS2AvrkZSbL8XU1iaN7DwJ6YZNy1F8KDt7IWWKyd8FURCtltq3HYdERCJQta6wRBD7HlmaZHzoUUbLtqRXTcotPekuW+NBvVXffho6yrE7oaRmM3RoPbIlVRhVokimPVLSpmWo+itJK7y/wsxXzVDCiE4iabwZxtBI3htntMpoNbbjKIpsstwoUiSa4UEUeZTVEufkigkMygfNkPLKpxHlw/yIrNijnFawS7bT/L4vead3OT+xX29RtuRAH8iO7ODsdCVfhFtbYdy0k+0oVBF213dCbNnsVP9mj/KaRgO3KzK90IxgqXyFECs/ocz+IVktnE/5kkejWrKRE0HrZU7sSz6B1uOIKXHNGFnQ3dEJEdT9kjMM9pg+Hvzx3imWCxMCeBzLekclnAgTKWFzNEnaMHJgJWWLKqn1rpg45XVaxFvCfu3a0ZfOaONQd2I8Ww8dWzlRyfFoUqeZTJ3aSc2jKQ2ilHQmeMyvAyg/oklebWM1iZVH0zhmxoREIgIt3EtTQSw7saQpBM2jGb25G6a5di1apMkD9dyj9/TmVri501PaDvSzRn9Wp2I62AvT6WnkL/Fp2uUiRen66Rl+TOJB1gIykS02w5SDB2/9DtLL15YchdcG2O7t8yuofdZE8KQB+xvQHk/VKQlMhZhViFZAYq1rWZbJ1awWqcjUd0OaVr6s0wSKchwXx76Mcf1fMzOWmBK+34nTsyMuPXPtSwjTHHybdT2a16nFcgFxZnlOp1mW7+s0x/IDneZZntfpCEtbp6MsP9RpgeVHOh1jeUELmnTfwZCLMOQCDpAwhKUDQ1hegiEsFQxhuQhDWBZhCMslGMLyYxjCchmGsLysZdXUU0nj2plYBmxCYGKOHrnMReVqKrlUQrtoVGpDnhJulVQUz6p/ZaBePPKGObAWSJfIml8xzpWPRuX41hUtbxo7V8Cx6m8fjvY58VLWi4U/Bf/V1lQlvWLNw5Or8BuGnmwnqjapeHRNl89VPbr+X1RUWAv0G0iFWCjKsmxwZyKEjzqdhmqglUPMbMw8tOt1y5qfw/03MUIWUP34NxQaC9yDTllJWe3grNXX27LcO4NyOBMsSTE38/pW+CIjs9J+kVnKno98HnAFjEpl2GoDrRW82ScxD5neJM8EcVtRNkja2M4EiQ0c84B5850EJmHqqg3kTuGGDfgFYW7BeSdconqjLIfuRezzKKT8W6fiRPaoaIzAs9kbYa/vQspvcQwkNPmlfgxUFaGpGDUV0DRSbqgGX8bZum1Cxg70Iyp2w7Ks4sPHFveVkm0ZhHykiNWjo5/WXqJOqtx+ZhSX752+BcEgNTF/e990cZDKu1rJMkdtA1O3GpVT15pD41WH6uZR9b3j7BM5a5puuiceel/TqtvBxVwssPZtDtJSJhfU9WGFDaLLxaVQ6mU0Se+4BxgWGNDvUIqN/6v62HyeK1WF0XEk307Ut9HnYAz8D9h/R/UD0Pdj6HINLs/3mhOfbvThbJmuohfrp+g3MGutuVm6BtzQdAPiIUetjrjKDXynBnF6pLkc6SHgY90V4gHAJoDF4BPdtYzmUwCj+Yw5PsDnzGHQZA6DLeYw2GbOGsAOcxjsMofBHnMYfMGcdYAvmcMgZA6DiDkMnjAnAHjKHAZfMYfB18xh8A1z7gN8yxwGMXMYJMxhsK/p1jDMLV7QXaC2QVWgA1NPWNzD4lBTZcj+jheG/b1BzP7BIKb+qOn2kPoTLwz1Z4OY+otBTP1V050h9TdeGOrvBjH1D4OY+ky/GMtlBr+MfJcKB5RdbD7n74n3D9vFQLkAAQAB//8AD3icrXsLcFzlleZ//v+++nb37dfte/VotVrdrW5JltuKpO6WJVluPyXbsmOEbGRjO4KVjWPZlsM4QIU4IYGlgCE2yxqKIQkJg2GreKQCTCZb1CZkiswjzOwOmSTATNVWTZLdDUwSMrVDsomD23vOf69assFAqgbk+773P4//nPOdc/5mwNjFl8WjosbaWblmxiOmIlTGYXzb11M7Z2ohAMbZCcZ5iG9prZl4whfwGju05xtttitUdwXYEUgkbQtWga5lC+XBaqJI285qpb8dVEc8Gnm5L5QM/f58yAlB399a7dD0mWAmdAqaMvBmKPKX9TdDwSjod9yhx03FAPcvI6Gk2lV33XoXUtKgL8C6Wa0WSzXbVjhg6JoqIPShCK0FOvOuE48K1V4B1VUQAVevugmP7Fz2CmTzW5/8+ZGP/+Kp7h/8oI4MuOZ7M5B9IvujH2Wf+PnCAjzn8ZK6AieMKRcvXnxWWSWCzGARlPcqtqs21WZzEBFgYIVDAYWzVJIrXBlH+hXGlWNM00GAJmYZ8sKBzTJFVZVppijqDFMVdTIWLa0o5JrdaHusPZGIG1IjFhQGK2mAZEe56kJnR1bTY7ZT7eivFGODBTdma3pHtlCNDVbwmgMHx/aO4R8ffeet5/ZCG6TfuV03IaSJU3oIzKsGO9+5PV+BwU5xqnOQx1aO8fW71yvD9fPn55/fA22PmsaFvfSgwZ8wzPiFvZ2DUMnzJ2hHVDMmHucPsSRrq7UQo4DMAV/AW7CANw/Zru3NIx20bBEpXwukArlxxOPReibaG63/SyQyiftzcBy3k1HuOHgjEgFHnkYfh4VodDJC4138Ff8hv5PlWbrWmm2O6grNEgGNCWGnbFtRm1Z04gzQPPWvkqMWF4eWU8DB267Df+gNe84bBvfRpfNI5Ny5yAmHDh5/PPLuByMlekDS9DsRR73nWHetwBShSO5PqCBQONO4E2wGlSvYZG6gMzeQ09SWFZC0tWION3oOVVXGTbGM+hrAzQj0O25yoN8R8YzzRsaZdzLwhpsGPEm783hAJ9+kq286eDX5pn/VydDjvk7i8EuchU21JJ3DNOqFzeABmywPcNXxhu/wB+3whrqwVw7Cn1j8vPfdDH6O7PR3fDV/Cb85wua/waSit309grZJHHOhHEQ9CA7zTGVCUb0ZvXzc1lrRe5Af+4An0aSL9op0IU9y6vRFtAYGKy7KJGFbQrdg2dXqGAwWilld0zUy9xLqd6A/zeFuy7gzYFmBOwOhZ6NNhZakm8YTI7S1pyM1mM032V26qevXGlzZ8+TKvROlB/BBkO9ACDalB7OZuBnuC5tRcAItpalENNOfhajVH1A2a1HjbHZ4N4r54oWLz4qPoe4jrMo2sonapm5QRQBQ3XycKVxwRRxjQuVCnWc6mrzOZ5FvBiraOmia5FubYRpok8n0ioRTKOQMNbWiszxYWAFZrQ1sB+dDJTFYghzyiBN3oB857ndIAha4Se/+YGUtjAkXXUO2xPE2eoe3TWPeMOXmzNi+j9w1HghvVbSAmu4c6nFac6MgbzXFU2baDr1241+98TfHtU/9t7df+MzU4msmfPYj06Wbw8Gqohda0/FkSyiyvtPGG/FsMKq1pLqmPvndkye/+y+08eYIHEJZpFmJra2Nop/VWuKcCwPvgRhnGlOFps6iIdAEmFVAKh53jblZyBU6nAFdbfX03tGwioZ9oGnEyDSWTQCyFzhoR+s/jdjgWLmc3L2K21zEmXCsM5aDm4g9Ny5v0PZVb+dY9Rcd/sl5eTiPj0nTaeiU+BhhW2qbuzJcUy2Nc2gCrjBkxWCqZiArGgOuwSzjAlDb5L91XfpvfYbpij6ZRIUWYoVcLqC2+UqNLddsMrekY3GZztVl2uQjpl4xjedIM+Uybc/r5viShuHgcgXuwQtpuoMHz8utCe/s8S+Y/7xMaYu2DX1yDrez3lp3cyyKdop+HLcLi878Uh9SKOSlj73cOIUFRVKVKHhaSS9JP452RCcYoJ3w85bz8Ckp9VN4xeZj9atpH4FnPCV4OtiDNIVYCrWwsbauK81VxUlaQnA+riFBisoUsiU0LbQpASCmmRAwg1MNJoH19uQ6WpriUV1jIQjphA5QjosiTfiy1tF+kPJKMTFYpAu6lkx40odnTv7VjYtC/QtTJyGH9HmMnKamGrcaqmaagRsMUwR9WeLmwsQREvIRevSvYa8uVFXo9Sc0w5A8/Rp5+jcxhXLuYAOIx/pbY+ggFp0FV8lNoLNAEQvyDkwBZR9T1ZC6xR0qFgsybix3DJcR7iIGwCsCHHQM0nYKVSBXQZPnqG7KDZiqrn8K524gpN9ghAx42k4GsvF3nohnA0kbnglkC9mrl7h4Db2TommKcVE1QOPRd97K5WJxsKO5nIjHbHtx/og3kK8iWys1he/EkKlqCbFOB6iKQp5Q5Yp6DB0AsjmPvHEFSGsoFraPaVpI2zLc2lmudErjJ/eeQwbKPoRBZulcwhwXYc6i1SdiEpiCF+KrFOIrAzKaHdhQ79tw4MAGuJv4rt8sQQu80jloGnnDfNVJBa+vn1WjSg1d8NHrg44FbTgdJ5+T77yy/gDI5wY7633yTbI7+CdyMDyEL2paDd0BvZjyfIYXH2/H+aqjDfXUisgnk3rFYMePoyZBegWanQpM5hKdlUSU1JnoQH2iG1d90IYADi0JPV2/oxMEeB7apm+aBngFw/ObMjzHzv7dgzyOh48fHZnmO9c8Wv+2RAGwHiP20UNnzx46mvYxyaMixDpZX22lQqQgRDqG4kYcMo92cpypAOo0Ti+iSYXJRGc5l8wvQhPfqpE0SYa7nDwkeQyh26OOlbecqVumoCwJ8+mDm+9/5T4eOyOt+4wk8WjavYTIG+7nD5Li2cWv8zGkMYoz51q2u3Y1JQSwfcumSn9JUxW0CZIWQ5isqKCo8zoGFPybx/lyHD2w4AaCBw7Ap1HIxAaHyZlr1tXG1owMt7h5Ox5QmxF4Iv6vDhaQoTFAYADuYIlnLa7b7RwtiOBDtVK1dbyCMXXxn5azOIIKfLFKaQP9K3HKIMYEQosvhkIWH23TQ9wIpCq9M4WxycnJsQIUYrEJ/bPGuOZohfHVzdmMaAmHm418c7DU3xdoyYPebFktPJtpHu7fefjw4R0VHiNTa06ZUTPe09a1sdTUVNrYtbo3nth11VW7tBa1d/U1a1t71rdG2u1IJNkWDYdbUs0pnnFT+OloWzISsdsjqVpvy9prqrNjed41POfPx2+J7TIXaWP5Wgf5bvToBEvpppQYOXPOJoda4gTQE6TsNK8sAvUSSHBFygfnfM9wL+8aK/Bddv0tZ8SufyKZ7ml7s20iCWdsPpPu4YVaXuur/2M6WX8riReTE21vtPUAnn4iyTxdf0vJ+/QM0pwsUL4D5BmUBQEeXpaU4W6RtExzN1In7eT9qHPpIoUdqAzSrdyHIPo1JNRps1uioCWJyIm2U/JG8oO4aZMXozF8b4RupRd5JJv7Nn8ZeUyxjlrakhJHcwO2gHMUDgGLR0MmS0GrIoMRWos0K81LUnPZYgmkw6sM8HtCoQTaT9y0Wu3f/MZuCYfijhMPhUVQNdL2hY8kMroS/8UvEqqeSfC/xzPV07k3vsqyrL+2ykYnhJjEz45OLIdbu4ncrbrWhImzltU7oqqaXAEdMWiHxPsSdvuF70K6/xf6FejjL13YBvFVP+UXrkwnyelZjBcltoLypQIoEmsoiDUw4C2QFS/I/J6yx5ybWOOqaMad0kJlKo+ElAdLqvRTMqOsgucuM64j3hgHU1H1mIkQ084Oje3eXT1lZwL1nwaD0BZMNfFTcHpv+sf7v6LEo4oZMlRbFNqH9tb60nEN0UkQ0mYawZNpR878eFuD1lWsl/Jb9J5iMbPlDKnkh3raBmS66U1M9Jm5rKUmMel1dKLOcxrVSgf5dJqU4o0g0pJyTlV37x4bytpCATOG8VUT4+m9cPoURiakE34ZMes/wbB0Roun+2p7h9oLSlwzQqZq2eIr+xe2/Rhp5QF8hHn5Hh/FfM9ibs32UrNG+l22ZbrnJ8N+fERAcD6YCp6nuPc2Aq/vWXiMfzKaye/BDfzTLPwe33MT8ns4P/y0uiLBdyZw3qRvNvGZegjF6H+escXvreOnkD7zm7IkADTbJEkU4sdw1jmwzpTj42smHLB82ijenjfx9YsXkccR+B5+I1azGtQk+4maTpnLlsBL75E7O1IPea+mzfPmUwQt00FJoG+nPxN/zruZzZprTljyx2GpXiHdYWBZtcINeB+2xVP16/GT9euDwf00TbqgK5gK7QvCmfp/wPn15WDa3BcM1l/Hy8F9wZQ31nf4Q2IjjrXyG+BnzXnMmqXiTuB5CLa01gINjvb8mesSTwHuSAEtzm8anp+uvwbdprkfhUs0wCNIxH6TP1l/vf6aPDThK0TXI5I+tjj+SX/8wIcaPxWX4y+CqsCiEIiAIzhsKrgfh+6qv+4L4RETPl6/zqMKukki9AA96OseZb3Zk7UKVKxZKt65tpR1J9p1sTGgP5Z4ah+KEzl73R/xEfr+I8H5fchlN/Jr0n0c3fSGkrz+vbhd5m2ZWltThDeKUaKh3WTZXWauS+WomBdMkjGvOCRuT9YfcIZxk0x24/5cT3q8redxe8TpTsLn03b9LIaFo/I0eQ7uxtjQm6rffI4elnTcJfbwX/qRFyG+Vw+SaJdyFIpugk0WXCcnaZFo1323viNAWHZPvk1+ugfjTf2sbcPR5LDT442bhwPj6Z5z9qi9wr8B85Ji51yXb3tISxlpSUtaZEYnFX8ZbHI6C8vk4ll2h5/YdRQbM0CUbSmXHsd5rH5zWz7fBnc/5iA1NLANwyQZ2+5JjiYfQ4mle+BxJA1prT9g+770dv4zTz9RnTPp+JlY4DJKoroOucmyT4fnsGIeFC3H9EtmB+qnGyP6413tPvs4BhIwipskDKakeIg+KKTgbl92JBkkHn1J3c8t+9ia2vBKhMFkGBJtEjrGDIzKE1SeAgTJiDAVRYpKmSGwMlksJor5gUWgjKlXYS0gurS4neaYflXJnaWBUjDKNAVhkWoF1WhHz7uJ2PapE8NHJkulySPD62/qVmLapMq10a997JqvnphQarc8dO3UQ2smYr38pfOWszK6fTs+eBKfHy4j8t2uWNrWnbDx5CNfe+TkxrHVE/EEW4ynxM9H2FhtpAeE2tnGFUH1J4xXChxD5hACLNXbLgOA3WU353rI37Y44SipbwdxwED/mEC0rOmuIzmlGkw7SBzNxZ6NN3519+zXRhV1Uosp3TdtGD68s4eXJo8uzHVtjyXc85gC9MYmRh+euuaRk+vhAG43Tm3RLGW7Clp52Oesq3N7dKVjnW9KxCdWjyFvi/nUs+Ja5CnPxtn+2t4NnVwLrELw76JqDMz1MacMGJoR0I5RVsA1lR/DLEdonLJLQWnOMcSamKlrszLnWWZ3mzcVOjsrnYWynTfVNmQ6aQFxrWtL9SQtgjqsojbxf5lp+qquSF1rVGyk02qZZEEKRkPdlv9ff3LVQ6MTFMas8xSft3fNVbd+vqg1KaF5w7RwBsirUye24UVXDS3oIcj/nz+56mF6qQlUAQ++gGoNytcxFG7P98DWMXMoHIL/6l/Z7p1riv8ko2oiyiru129WsSrmUjO1XdtWc0Pr7miOoWOV9YUQ07WQPmsCisWYDge5puAUAI3NIk6EQACmaQ+BGRaAwOSemV1TH90+vnlDrZBNFOi/nEUlLL96lYx5VZLqB5zDQLFQzGm6KuUX89L6YqxRuaPsCwXYTlJE1EVJt9ycWTo8bereoW7Wv38ewfOzmgI/N42Kn53LatjTxUCv85zbEyg+Y5hTcDddq99M2ysc8/51lABfjZ++8KvSxvUlnpCj7U+mIG3vR8yhXSbXEbaB3cDmatdds4lrhi9Z8hskNo2hkI+RQHWN6fMIUwKGFZiNhDmCNq6BoR1gejCoTzNdD86woB6cPDh33YFr91w99dHJLePr1tp525NylKZkzJtthLplN+ADzhOxjpiN1trRPwb/vhKfqIcMg8Mr3DDqd38o4cM36y9KWa+Tsn7v4/ocj134Vcg2TZsf/ABFKH4tYwrtObZYYxoF3QiQC+PjATwUho5+W8MkQlOOqUDxnlMNjZpsfB8zjJCxZe2afKeTjXeuboqT2XcOlsACB6XROFhWch7AeNy/VsZfx4fO5Aj8EjTVRPjLdtrmTS1NX7Azce6kmjZnnHf+1iuBiG0duzsmQTiZPzfjEoTGAqZ7xqt/nmmai8gXOWb0/sG9z8vyyPNOZjKDf9DlRgmQR93keVlHOU/9RTkfH8W8ieTQzWpsc21DGRM0Xw4soAUWDEDPtMB0oS9I5qeXC0PhMySPyTWjuYFctn9JEgWLp6FSXdz71TeSgzuQBmnXVHPXGqWhIhXkF+H2lQXxVqiSO5OthN9CQQSazmBOhdycwSgoZRJvQ28Yz8SVltDiwd3PUxMLN9De1dWehinH5783RvA9xhpyoHgnWAfGvE3sqtqOFT25rGIoMB4GBa0NAaepg2KYyiwVijQqFKGtopEeUFFYgQCboj0jZ8cCk7U1Q2W3MBBLjMRi0SCKxO0od6gD6Muon6Q35JGLDZS9DEpfLNZTA4aqrmrD0rwHCAO8Cs/Ur4a3J0Lql9WU4RfBJiYySfg+cviqaczLvipt59LuhbjXflPdSuRLUUd/9VV422jRv6SF/NbehYrcQ1p2AZ6jd80L5+kSRwE3WV+OVGTs9GvnU6yLDbJsrZ2aE+iijskqPtuHgTAktgz0r+xtaUaslZRRH6FKlRJrBwO9bK+lef8YXipg7j8GSJjM+GRPmSApRv6n//T4VrH3qqbRaNxoqoxSNMfwD6MV18yPulftrX+xZ7gXeka7vMCPgf2aQ8+N4bPuaKz7lg2LIGjjTT3x4T4jvubPYKL+cFtPTxscwm0DA+yVNaMjbFNt/cE9k+sUpoxgas8Gu1qjhG7GqUu+oAFel8B2gTDcgt+54If2XXv1VVsmVvRkM4m4TnnrYCGLtt5foc4F2rWO/NrIbxENvBHUy0WJBYqEg8hbyklAxoATvupfHMCZ3wAHaASEAFz/Y7pUPh+ZumWK7z65G1KGftgMJro0NbIzrOvbm1sCuhL9tBGKtrof1aLaZkdRjS4zYhzSDTDVw4bldnrPGtubWgKGiH0adR1JuR9VI/qErSgB72HMk0emp2+anr6F7kfTydZ+zdKSO0EdDRuTqaip3xAIjapaLa1aWqg/kmqNQEiXzza3ZFbqId3euezR4Iiqbkj5j7ZEIeTX7X4n9vLvLmKL2mAXILVUp0WPoyLCVJVGQ+/yNlGhjP8PNLq4y9vNy9rPrn+eW36OaPmdX0nTF7EIpu9XPlvW1XMgOiHbSHIL1ji1nsbpBdbAyXtlTRwjSHuCqyJvkjfAVEiMe317oeLEURlNKa/CI7tfVLxLp5qb4tFwUFVYJ3R6/STHqy1Xi1R9RLvvd6lSPIba14rZoo6zwa3w/7L18OGzRwC+N7B52+HD2zYPfA8OP3iIH9kyjkd4Fdwj9x85skUPzfXhQd9cSN96mB+97yjgoYUXvZ7kxYu/UW7mL7Eo+rwKK9byTKWsSWWz6N8VBaZxB5ShgDJZHerscm0J6L1at2fNGPH4CqQNZ+gAGTeaPZVNBB52IJ7nt/uI3N/B0707Do+8vmEH37rpdTLX8eEDd47Xr564Y3aIj+67azM8Q4dwYHjpHbJoOu1/8OkH++lk4s59Y2Lo+tsevG1ukA/N3uHb9f9TbkFebNRET61I/GEuOEtdCkxKcdfISpPptmSn01kdVNF4Y4NjHF1rWnh0cxWZKImsJdKcKPPIkZR5RIoYkcJ7dh665dDOHqV/4jgc2ILXkYz77zgwypGsH1zKst/L+ir6nJVsPdtR2zaC4aQTZC9CR4p0jigDQ6gmexJcUeeXdbS0xY7WslzDdpPF8uryAPX0L+tqoVNFD5QrFi7rbKGXwYnkJGCpwra4XMZe1th6JZcOCL0VgV445HepqL+l5jVdKMHP1VeH89a/WtYaK2/9Z/g4noyFYduzjf6WpSS0FIKDRovrC4aaQ9ZArQ9b1r/K58P0Yhi/4Msl4WPhFbUuzCTBb1OitsjmCVsAn+Qs3eokTJ1FeEQlI1nGXNlfnbS8tcc/sYz4wU8vsbd+lr/UII5u7mncOYBJ+WKc+5jsETe/L03AXCfmN4IlTVnCOYiVSxyWr5h45up7d/Lpu568c7ey4zRcu6yjzk9P3Xvu3im5qb9yaf98ab2AwZIsy0Zrq6nXxkG22zi121ScGiC71IoiZwcZqlAmzUCmvaU5GgkkzaQfoNCjlKimWnxvGhtR5b4r0roYFX59BZLFsvVB5NMRIVAGQ65bJoBsRuaFbLJM/0n3nZCLlZaWYKj+efWy88WezKv+yiG5dKnN23mX2uUJbi5bY+Rc4XgxBrHXGnlYb637gwLPEuXVBmD1ViiIyzgRfl5f9NddvfoB9NDxw0clWD9KV0FbdhMMjzfc+H7uJXEG8fo6Ns4O1q5fU+KCCq16PhXG3JuJcczFw6GwEaLSBXkQwNwF4DgLs1AgHDqA2FToAXEgCDpj+jTudDaDgUmnGsaG9bWx1UOVctJGzBlDf2HJWgZyh1HHkeV53RK5WC6Gc8fr45AzKSByxwlWHShTsxydSgfVLhALUZsXOiS8w0TlVOdJ006bC4ZayA43j7cN9WCqeCgYDTvGJzKnKG0Jn7ou6KSC18Frs8FUk2Jch1frv65/kWLdMMbf2fWfDKac4HFdaYpb8HY9ZDXZhnEylEgHP7t2L6YMcO46M22b111HA113zoFBDJQUpy/WLx6B36K+MxQdmlHBKerm0BIJKqUu68GgoLY0VSpexdChJiDC1WpC9qwK1cQYpS7U0BW0JAzeMrX6P+hRI2Dy4z/hqqmb4gS3jG8GLT77P1Ue5E4wfOFTFoioAS8NYVYZhv9umJapaPV6hct6wHdkHiowcqVYnvUivt5Qa+3vK/X2dBXy2Uy6pclBE4pR4XkwxWHTtq93vH+tv0l2U6pFvbNRePWrv51uBFbBWmgHbw/upXv+0LnRc1AxL/RjLrVgmvx/yP0Fq1KJxarV2A+PHct2HDvWwbvxJIYX60/THfzHrcdGH7shQm/iC2l6E/fXRumtaPU/ybeyx+p34UkVL0LJv9PAUHOomzL1w8q9uZaYoV3SIOoujHBKKii60eKMxhJHXUMgTQW0InVqqErq22AbYN6BHOMkFCMtlpWLDDc/0NM23tYLZ1uGMX5ZrWfPtkQj+chQ61lZiH+gZSiai0Sbz4JhDbeswXd2PSVr8E/twqtr8KXdu690Q2KpI6jHMMuhBjdRdbG9iQslEae6YVhDV7gBdOiHgK4iKtSERkv4yOpAP8ZMpimmNqtSRuUFG50FDD2AeaYRNrasGS0P2LTwuGDncpRJNtZ5Fpev85SVhMY6T3ewBJrtjAFIs8XZi95eSXOXLJkW75wKaXmE7afkst1TsjBDJxP3v3I//kG6Z9R+ce7WnfcfrvHRo6fPnT46CpteTMJZ7yXKMb2XTlHieMpspgUYLz+k3UsJV/LFTWNH7vvT08eHlfWHHtx+69yLSbZMRhHWhDn2SG0oYCC3qE/B0qAKuZqPg+b5XkErlwWmYkKlMkNY2eLmUAqF3OJinmKjrPIhuK1/W/IJ6z8kh5K1D2aK+/UTWqOzkz1fC4xVOgKKKqgjZqKV9nhTGGOgUGnZjiK4Mq8jJFKOMwJ0UxSLdlO/Ymvrtq8H3/UG+nBNcG0e7Vh79xt/yOf37KklDMPYaeyc3LZ1y8hwT671KoP8BApooL9agOqYUm2FATeRptTcdeQGxexmC3pWyw2u5ZSr4l9xsLCKW+DaaeqxVskXZql+XdTggY9NDrcHk331MoTzqZSj3fGlCe3GxJQT6IsGjeBkQOGQO53v+VKSb9E1EVMQ5vKs2/R7axiimWAmiVCh4/MZ1eYrecvvMWx9oa71KppmNkVhBs6G6m+veHkw8amOFi0QFY4pTI7RrikRxSd1jgBaCeytDEHmYSsUN/HTEEyqQVQ6ppY4937ER/nPmMXaWK6W8TvQy1cj+s28wcIlS70LHqQsSoS5rB18abP70ubwv0VMqrRhJALnb2SJXZ76vg7pAPc9++oF2+s8XzLyZUMt/3ajhy35encPGy4n+lIy4Xvvou1JERMJFmTa8zqHldSwlaBa9k6VC63heDzM/3cYttfndDMiKlbIwCN7eV2F8FS61toUNZhCzHltUoKChwg/yah6WVnr8jIXJer+ultoizj14CWn/GfvvEU5uIjTdtnx8nw86K1TlzWF5QsXBhYXGlw+3rLk/5IB/G8+gzZuYUagPa8ByoUaWfiRIuA35I4CrJOMVariLtOCaKD+Ryq3gvWTwSDci1AAfVtQi7zzsmWE4F5Vq/+RPKDe8714vX5SVSVGuXjxa2K/iCyN42qyOkbLkXwt6FIjmt5sRsHC11VB2yANZQl1HL+GWjFC9Em4Vx7Q2oKTeB2+oKkeL3xEkBU015zWlri4VEBrpICIlwZ7Eso2mgSVKs8GLeJN5YsLnmn7Lh7/45KS6ncSg5I/Por8NcaWi7UW27Te2JeyTGPqPg3VS9j2CzJyeznjn1qaGXCrpsq8ro655n7+1zi3WwkBRkGuRl5e3eMyi6L0TvDJcnYwTnM14fWG5Xyh5nXVLya56BmNJRUi25oYoiGRJBGXdaO+Rb1Y9R861mlZYzodsf21K8/wZ1mC8I75Hr9viTsF+fsW5BzxTSO1IFQNz6DJvnC/lPv9L6Ab4KcvfJU6gC94y9NfMP31s7i5XY5RIn6LJqKRxjAKDuOVppiQA65ycUhaEvVeQya8dVuF6uCYKClVFADVGd5FyGt9RTWAAgg50ahiNFm2YkcCarHv3QTW/6Jna0TY0VA0FEpnMkbciKJ0RGSrrMN/XzzKfy7p3saOsq21cUl7FVQ2Dboqxg8D38S4hnxooL2bGRU1qurqAtP1Q1M7xzd3Sd4MKlx9aN7cxtWq/1OQAi25ohrK0s0ilVHwbgmKVNGWD1T/IMmcmd2u68h5MhPsLpW6Mf7ZEUPfceC+0zfhdXy9uTm5cQffujnZrMQFzmZdv+n0HyDOnl33Z4RjOcFoIL338N50IIpBxBEd9+2+7fV+vGGHQ5Y18NCTDw1EwkITYRu/JwZe9fPMI+IfxFUsyjbSqvKhAcwt168pdedsTBbTzZxTnRzRxQyV/if8uggP8y2VcpqWw7iyTmgJOy3GuEu5oK35P5xC0L6GZFag308hopD9/6r3KyoE7rKG5Tqi3D9z8s6TM/3+7mEeeCxiPDanxdWDjxmRxwKIeebmVFVePajGtTl5VaWLcGDdLdMVpbTvxD0n9pWUyvQtew1RfioQFOU/1vU/Lotg4KmyMEz9nnuM2OINTVu8ETPuuUeXPuNZUcFYEmd9rFRbYSxfVUjLiN9dFerqyvd2yKWFBEEB548Fup3m1PWnBQHUERnjyKYur0O1v1pBfvl30u2Hnj4EwydOw/CBOyd23vd4+YefpuUbvHb84elmO9HXD1P3Tq1f48YM5VZ17msH5/d1fPtmWQndeOwTd1EnZNeXbtwsoBRbcbJ29T3T0GbGDO+3J95aen6KxViWDVCHL8i5wsPAxPLfE1FDb55RmQnmCYgrEoijOyX+6KeDoE66Tt5ONjkSgheKBA9LqL6qpqchU0E7QaSYtJ3+irSVimrrmpLJU3ej0gsK6vTRXbdlEXtnb9u17Z9B+Un9m9Hg5rmoE93YF4zCPwZ31H9b/6f6b3cEgzvAgAIYO4IwfMe64Q03nOX3fXzD8Lo7brzrLtiCz85tCkajwb6N0b9LJD738MOfSxTs2x7mj3yGsEgjzzBYB9Xw2hGCYAZOzYWG2uR6nFlvxlJeEZfO732ziNsX84be4ffMGz7/gRmQb1dyTX8Q/dtArU8Hb8Wybmgod53NBlQOilxbg9mPCIstK3sTAzE7O5BMJuTKkvJgwfulBskXM0CBMymXLQrpkSoDMVmJ6aA2QjGG4SJkRk38g5sd61dtYGoQ4St+jHY+U23r4aVWOEjtseoMnDgvfwCEm2+hp6r/X91AK4zYkU3HMVEaxnR3qBfqPzn+/wHPnsOreJxjYGRgYABi1vIsoXh+m68M3MwvgCIMt5YoxkDp2P9f/2exVDAHAbkcDEwgUQAz1Qu/AHicY2BkYGAO+p/FwMBS9v/r/68sFQxAERQQDACh8QbyeJxdT0sVwzAMy49AkAxAkbT3UQiAITGAISmAYQiA9th4ttUs7Q568UeSlVidiwSkB3PUPg+ESd6ZD/+8v7HyrtzwghY89ZB6BcyrYqc6RZhw48YhaA1Wc/v1PQtdeXJ/HppUmFM597nvBVK7D+Z+E8/uQZLBgDyaz3Llvxx02S3c/Hv813JrznryZP4F+6lSfQAAAAAAAAAAUAC2ATABaAGyAfoCJAKwAzYDmgQSBFwExgUyBbQF/AZOBvwHRAe2B/YISgigCPIJGglCCWQJignACgAKQAp2CroLAAtGC4oL8gxcDPINng5iDuYPag/6EF4RIBGGEeQSShKYEyQTbhOyFAoUYhS+FVoVphYoFooXHBeGGFoYnhjGGOwZChlOGXgZrBneGhwaWhqgGtIbLhvyHHQc2B1QHZ4d/wABAAAAUwBtAAYAAAAAAAIAIAAwAHMAAAB2C3AAAAAAeJx1kN9q2zAUh39q0441YxcbjN3tXJWWEcc1lEGvWkLbXZeSu8JUV/6T2VKQlY48w95ifYa9zt6jd/vFESUUYiP5O5/O8ZEE4AP+QWH9nHKsWeEdozXv4A0uIu/Sf488IN9G3sMQPyLv0/+MfICv+BV5iI/4wz+owVtGM/yNrPBZfYm8g/fqW+Rd+svIA/Jd5D18UovI+/S/Ix9gqp4iD3GoniduvvR1WQU5mhxLlmap3C/FUdVWN6IXoXK+k3MpnA2maVySuza0ZlToUZ07292YctFov6k2eWp8VzsrJ0m6qa+NNV4H87Dq1j2WWQiFFN61chX7yNy7mclDUoUwPxuPN/tjAoc5lvCoUaJCgOCI9pjfDGk/BPfMEGaus2pYaDQ0GgtWVP1Kx/ico2BkaQ0zGnKCnHNL09KNuK451721rLqhLfmfht5vzdrmp7Sr3nUfC07YL92afU1r+wrd7/Dh5WwdHrmLjDawanUK3+9acPXqPML7Wq3NaHL6pL+1QHuGMd8t5/8PvPGO3QAAAHicbZLnlpswEIV9DRiwvZtseu89Ib333vvmBWRZYMVC0pHEEufpg8BO/kTncOfTaLgMOtPr97o17P1/baKPACEiDBAjQYohRhhjDevYhu3YwA7sxC7sxh7sxT7sxwEcxCEcxhEcxTEcxwmcxCmcxhmcxTmcxwVcxCVkuIwruIpruI4buIlbuI07uIt7uI8HeIhHeIwneIpneI4XeIlXeI03eIt3eI8P+IhP+Iwv+Ipv+I5N/OiF1hEz9JKxUrtFrDl1lWF9NR9QIikToRaVjUouKxvOmNBjLxnlhgo2DbnM1djLKrNGnGPScSUzItzGv93yPP2bSQSX84z9cqFQdJ56yZRmMhW8mLlJJSaBI0XYPDaZKDUviZmvr6DrNjJMi0WcK1MTM02mqpbZlJtEsNx5SI238jSodJtoS7qv+BpPw67IY9xU+dg5TXjROTWwdGrIOzWhT+uA0jolxqjaZrSOnCF2Nmq16651EYpMm1fakAul9SJQeR5QVYQlk1VkZ8SwoVNFIVjWnKQrlBGdMToftdoZjrs77DajqXKrS02YEFxbbtdWkG0x44JJVUS5aBqKSlJwmhDrmOF2Hv9Wqsy4TNqoKhfmSrrQKuNSL5nvPG6p0s0AkEWkSWVZMy1Kx3ljk03qLuZ14lTmB8gNGmByGrGfjLrhlhJV2f7SaIneNF1ypQNbybBUSgZswQaWEUNngeay1/sD4l/60HicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZWJ02MTAyaIEYm7mYGDkgLD4GMIvNaRfTAaA0J5DN7rSLwQHCZmZw2ajC2BEYscGhI2Ijc4rLRjUQbxdHAwMji0NHckgESEkkEGzmYWLk0drB+L91A0vvRiYGFwAMdiP0AAA=') format('woff');\n}\n</style>\n<style id=\"style-core\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\nhtml {\n\t/*\n\t\tWe define the base font size and line height here as they affect the layout\n\t\tof the base page elements (i.e. `#ui-bar`, `#ui-dialog`, and `#story`).\n\t*/\n\tfont: 16px/1 Helmet, Freesans, sans-serif;\n}\n\n/* Story data styling. */\n#store-area, tw-storydata {\n\tdisplay: none !important;\n\tz-index: 0;\n}\n\n/* Special no transition styling. */\n.no-transition {\n\t-webkit-transition: none !important;\n\t-o-transition: none !important;\n\ttransition: none !important;\n}\n\n\n/*\n\tFullscreen appearance styles.\n*/\n*:-webkit-full-screen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:-ms-fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\nbody::-ms-backdrop { /* Prevent IE 11 from hiding the `body` element's background. */\n\tbackground: none;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n*:focus {\n\toutline: thin dotted;\n}\n*:disabled {\n\tcursor: not-allowed !important;\n}\nbody {\n\tcolor: #eee;\n\tbackground-color: #111;\n\toverflow: auto;\n}\na {\n\tcursor: pointer;\n\tcolor: #68d;\n\ttext-decoration: none;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\na:hover {\n\tcolor: #8af;\n\ttext-decoration: underline;\n}\na.link-broken {\n\tcolor: #c22;\n}\na.link-broken:hover {\n\tcolor: #e44;\n}\na[disabled], span.link-disabled {\n\tcolor: #aaa;\n\tcursor: not-allowed !important;\n\t/*\n\t\tNOTE: Do not use `pointer-events` here as it disables\n\t\tthe display of a cursor in some browsers.\n\n\t\tpointer-events: none;\n\t*/\n\ttext-decoration: none;\n}\narea {\n\tcursor: pointer;\n}\nbutton {\n\tcursor: pointer;\n\tcolor: #eee;\n\tbackground-color: #35a;\n\tborder: 1px solid #57c;\n\tline-height: normal;\n\tpadding: 0.4em;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\nbutton:hover {\n\tbackground-color: #57c;\n\tborder-color: #79e;\n}\nbutton:disabled {\n\tbackground-color: #444;\n\tborder: 1px solid #666;\n}\ninput, select, textarea {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\nselect {\n\tpadding: 0.34em 0.4em;\n}\ninput[type=\"text\"] {\n\tmin-width: 18em;\n}\ntextarea {\n\tmin-width: 30em;\n\tresize: vertical;\n}\ninput[type=\"checkbox\"], input[type=\"file\"], input[type=\"radio\"], select {\n\tcursor: pointer;\n}\n/* BEGIN: input[type=\"range\"] */\ninput[type=\"range\"] {\n\t-webkit-appearance: none;\n\tmin-height: 1.2em;\n}\ninput[type=\"range\"]:focus {\n\toutline: none;\n}\ninput[type=\"range\"]::-webkit-slider-runnable-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n\t-webkit-appearance: none;\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\t/*\n\t\tNOTE: Ideally, `margin-top` should be `0` for Edge/Spartan (ca. v17), but\n\t\treal WebKit/Blink-based browsers need it.  Since there's more of them and\n\t\tEdge is co-opting the prefix anyway, we cater to them.  Edge will simply\n\t\thave to look ever so slightly off.\n\t*/\n\tmargin-top: -5px;\n\twidth: 33px;\n}\ninput[type=\"range\"]:focus::-webkit-slider-runnable-track {\n\tbackground: #222;\n}\ninput[type=\"range\"]::-moz-range-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\twidth: 33px;\n}\ninput[type=\"range\"]::-ms-track {\n\tbackground: transparent;\n\tborder-color: transparent;\n\tcolor: transparent;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: calc(100% - 1px);\n}\ninput[type=\"range\"]::-ms-fill-lower {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-fill-upper {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 16px;\n\twidth: 33px;\n}\n/* END: input[type=\"range\"] */\ninput:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,\ninput:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\nhr {\n\tdisplay: block;\n\theight: 1px;\n\tborder: none;\n\tborder-top: 1px solid #eee;\n\tmargin: 1em 0;\n\tpadding: 0;\n}\naudio, canvas, progress, video {\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.error-view {\n\tbackground-color: #511;\n\tborder-left: 0.5em solid #c22;\n\tdisplay: inline-block;\n\tmargin: 0.1em;\n\tmax-width: 100%;\n\tpadding: 0 0.25em;\n\tposition: relative;\n}\n.error-view > .error-toggle {\n\tbackground-color: transparent;\n\tborder: none;\n\tline-height: inherit;\n\tleft: 0;\n\tpadding: 0;\n\tposition: absolute;\n\ttop: 0;\n\twidth: 1.75em;\n}\n.error-view > .error {\n\tdisplay: inline-block;\n\tmargin-left: 0.25em;\n}\n.error-view > .error-toggle + .error {\n\tmargin-left: 1.5em;\n}\n.error-view > .error-source[hidden] {\n\tdisplay: none;\n}\n.error-view > .error-source:not([hidden]) {\n\tbackground-color: rgba(0, 0, 0, 0.2);\n\tdisplay: block;\n\tmargin: 0 0 0.25em;\n\toverflow-x: auto;\n\tpadding: 0.25em;\n}\n\n.highlight, .marked {\n\tcolor: yellow;\n\tfont-weight: bold;\n\tfont-style: italic;\n}\n.nobr {\n\twhite-space: nowrap;\n}\n\n[data-icon]:before,\n[data-icon-before]:before,\n[data-icon-after]:after,\n.error-view > .error-toggle:before,\n.error-view > .error:before,\na.link-external:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n[data-icon]:before {\n\tcontent: attr(data-icon);\n}\n[data-icon-before]:before {\n\tcontent: attr(data-icon-before) \"\\00a0\\00a0\";\n}\n[data-icon-after]:after {\n\tcontent: \"\\00a0\\00a0\" attr(data-icon-after);\n}\n.error-view > .error-toggle:before {\n\tcontent: \"\\e81a\";\n}\n.error-view > .error-toggle.enabled:before {\n\tcontent: \"\\e818\";\n}\n.error-view > .error:before {\n\tcontent: \"\\e80d\\00a0\\00a0\";\n}\na.link-external:after {\n\tcontent: \"\\00a0\\e80e\";\n}\n</style>\n<style id=\"style-core-display\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-display.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n#story {\n\tz-index: 10;\n\tmargin: 2.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-right: 1.5em;\n\t}\n}\n#passages {\n\tmax-width: 54em;\n\tmargin: 0 auto;\n}\n</style>\n<style id=\"style-core-passage\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-passage.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n.passage {\n\tline-height: 1.75;\n\ttext-align: left;\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.passage-in {\n\topacity: 0;\n}\n.passage ul, .passage ol {\n\tmargin-left: 0.5em;\n\tpadding-left: 1.5em;\n}\n.passage table {\n\tmargin: 1em 0;\n\tborder-collapse: collapse;\n\tfont-size: 100%;\n}\n.passage tr, .passage th, .passage td, .passage caption {\n\tpadding: 3px;\n}\n</style>\n<style id=\"style-core-macro\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-macro.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n@-webkit-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@-o-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n\n.macro-linkappend-insert,\n.macro-linkprepend-insert,\n.macro-linkreplace-insert,\n.macro-append-insert,\n.macro-prepend-insert,\n.macro-replace-insert,\n.macro-repeat-insert,\n.macro-timed-insert {\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.macro-linkappend-in,\n.macro-linkprepend-in,\n.macro-linkreplace-in,\n.macro-append-in,\n.macro-prepend-in,\n.macro-replace-in,\n.macro-repeat-in,\n.macro-timed-in {\n\topacity: 0;\n}\n\n.macro-type-cursor:after {\n\t-webkit-animation: cursor-blink 1s infinite;\n\t     -o-animation: cursor-blink 1s infinite;\n\t        animation: cursor-blink 1s infinite;\n\tcontent: \"\\2590\"; /* \"\\2588\" */\n\topacity: 1;\n}\n</style>\n<style id=\"style-ui-dialog\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-dialog.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\nhtml[data-dialog] body {\n\toverflow: hidden;\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-overlay.open {\n\tvisibility: visible;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n#ui-overlay:not(.open) {\n\t-webkit-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\t-o-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\ttransition: visibility 200ms step-end, opacity 200ms ease-in;\n}\n#ui-overlay {\n\tvisibility: hidden;\n\topacity: 0;\n\tz-index: 100000;\n\tposition: fixed;\n\t/*\n\ttop: -50vh;\n\tleft: -50vw;\n\theight: 200vh;\n\twidth: 200vw;\n\t*/\n\ttop: -50%;\n\tleft: -50%;\n\theight: 200%;\n\twidth: 200%;\n}\n#ui-dialog.open {\n\tdisplay: block;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n/*\n\tWe do not animate `#ui-dialog:not(.open)` for various reasons.  Chief among\n\tthem, however, is so that the dialog isn't in the middle of its animation\n\twhen other page updates happen.\n\n\te.g. The restoration of `overflow` on `body` would cause the still animating\n\t     dialog to jump around a little if a scrollbar were to pop in.\n\n\t     Any dialog action which performs a task which has its own animations\n\t     (e.g. passage display) or causes the page to reload in addition to\n\t     closing the dialog could cause display shenanigans.\n*/\n#ui-dialog {\n\tdisplay: none;\n\topacity: 0;\n\tz-index: 100100;\n\tposition: fixed;\n\ttop: 50px;\n\tmargin: 0;\n\tpadding: 0;\n}\n#ui-dialog > * {\n\t-webkit-box-sizing: border-box;\n\t        box-sizing: border-box;\n}\n#ui-dialog-titlebar {\n\tposition: relative;\n}\n#ui-dialog-close {\n\tdisplay: block;\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\twhite-space: nowrap;\n}\n#ui-dialog-body {\n\toverflow: auto;\n\tmin-width: 280px;\n\theight: 92%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.1em); /* parent - title(2.1em) */\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-overlay {\n\tbackground-color: #000;\n}\n#ui-overlay.open {\n\topacity: 0.8;\n}\n#ui-dialog {\n\tmax-width: 66em;\n}\n#ui-dialog.open {\n\topacity: 1;\n}\n#ui-dialog-titlebar {\n\tbackground-color: #444;\n\tmin-height: 24px;\n}\n#ui-dialog-title {\n\tmargin: 0;\n\tpadding: 0.2em 3.5em 0.2em 0.5em;\n\tfont-size: 1.5em;\n\ttext-align: center;\n\ttext-transform: uppercase;\n}\n#ui-dialog-close {\n\tcursor: pointer;\n\tfont-size: 120%;\n\tmargin: 0;\n\tpadding: 0;\n\twidth: 3.6em;\n\theight: 92%;\n\tbackground-color: transparent;\n\tborder: 1px solid transparent;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\n#ui-dialog-close:hover {\n\tbackground-color: #b44;\n\tborder-color: #d66;\n}\n#ui-dialog-body {\n\tbackground-color: #111;\n\tborder: 1px solid #444;\n\ttext-align: left;\n\tline-height: 1.5;\n\tpadding: 1em;\n}\n#ui-dialog-body > *:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body hr {\n\tbackground-color: #444;\n}\n\n/* Default dialog button bar styling. */\n#ui-dialog-body ul.buttons {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n#ui-dialog-body ul.buttons li {\n\tdisplay: inline-block;\n\tmargin: 0;\n\tpadding: 0.4em 0.4em 0 0;\n}\n#ui-dialog-body ul.buttons > li + li > button {\n\tmargin-left: 1em;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-close {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-close {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n</style>\n<style id=\"style-ui\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n/* Settings dialog styling. */\n#ui-dialog-body.settings [id|=\"setting-body\"] > div:first-child {\n\tdisplay: table;\n\twidth: 100%;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] {\n\tdisplay: table-cell;\n\tpadding: 0.4em 2em 0.4em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] + div {\n\tdisplay: table-cell;\n\tmin-width: 8em;\n\ttext-align: right;\n\tvertical-align: middle;\n\twhite-space: nowrap;\n}\n\n\n/*\n\tBuilt-in dialog appearance styles.\n*/\n/* List-based dialog styling (primarily for the Jumpto & Share dialogs). */\n#ui-dialog-body.list {\n\tpadding: 0;\n}\n#ui-dialog-body.list ul {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid transparent;\n}\n#ui-dialog-body.list li {\n\tmargin: 0;\n}\n#ui-dialog-body.list li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.list li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-decoration: none;\n}\n#ui-dialog-body.list li a:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n/* Saves dialog styling. */\n#ui-dialog-body.saves {\n\tpadding: 0 0 1px; /* Webkit/Blink need 1px bottom padding or they'll trigger the scroll bar */\n}\n#ui-dialog-body.saves > *:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves table {\n\tborder-spacing: 0;\n\twidth: 100%;\n}\n#ui-dialog-body.saves tr:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves td {\n\tpadding: 0.33em 0.33em;\n}\n#ui-dialog-body.saves td:first-child {\n\tmin-width: 1.5em;\n\ttext-align: center;\n}\n#ui-dialog-body.saves td:nth-child(3) {\n\tline-height: 1.2;\n}\n#ui-dialog-body.saves td:last-child {\n\ttext-align: right;\n}\n#ui-dialog-body.saves .empty {\n\tcolor: #999;\n\tspeak: none;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n#ui-dialog-body.saves .datestamp {\n\tfont-size: 75%;\n\tmargin-left: 1em;\n}\n#ui-dialog-body.saves ul.buttons li {\n\tpadding: 0.4em;\n}\n#ui-dialog-body.saves ul.buttons > li + li > button {\n\tmargin-left: 0.2em;\n}\n#ui-dialog-body.saves ul.buttons li:last-child {\n\t/*\n\t\tUsing `position:absolute;right:0;` here can produce poor results,\n\t\tso we use `float:right` instead.\n\t*/\n\tfloat: right;\n}\n\n/* Settings dialog styling. */\n#ui-dialog-body.settings div[id|=\"header-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:not(:first-child) {\n\tborder-top: 1px solid #444;\n\tpadding-top: 1em;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"] > * {\n\tmargin: 0;\n}\n#ui-dialog-body.settings h2[id|=\"header-heading\"] {\n\tfont-size: 1.375em;\n}\n#ui-dialog-body.settings p[id|=\"header-desc\"],\n#ui-dialog-body.settings p[id|=\"setting-desc\"] {\n\tfont-size: 87.5%;\n\tmargin: 0 0 0 0.5em;\n}\n#ui-dialog-body.settings div[id|=\"setting-body\"] + div[id|=\"setting-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-control\"] {\n\twhite-space: nowrap;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"] {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n#ui-dialog-body.settings input[type=\"range\"][id|=\"setting-control\"] {\n\tmax-width: 35vw;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-body.list a,\n#ui-dialog-body.settings span[id|=\"setting-input\"] {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-body.saves button[id=\"saves-export\"]:before,\n#ui-dialog-body.saves button[id=\"saves-import\"]:before,\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before,\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after,\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-dialog-body.saves button[id=\"saves-export\"]:before {\n\tcontent: \"\\e829\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-import\"]:before {\n\tcontent: \"\\e82a\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before {\n\tcontent: \"\\e827\\00a0\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n</style>\n<style id=\"style-ui-bar\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-bar.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\n#story {\n\tmargin-left: 20em;\n\t-webkit-transition: margin-left 200ms ease-in;\n\t-o-transition: margin-left 200ms ease-in;\n\ttransition: margin-left 200ms ease-in;\n}\n#ui-bar.stowed ~ #story {\n\tmargin-left: 4.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-left: 19em;\n\t}\n\t#ui-bar.stowed ~ #story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n/*\n\tAt very narrow viewport widths, set `#story{margin-left}` equal to the value\n\tof `#ui-bar.stowed~#story{margin-left}`, so that `#ui-bar` will side over top\n\tof `#story` when unstowed, rather than shoving it over.\n*/\n@media screen and (max-width: 768px) {\n\t#story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-bar {\n\tposition: fixed;\n\tz-index: 50;\n\ttop: 0;\n\tleft: 0;\n\twidth: 17.5em;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\t-webkit-transition: left 200ms ease-in;\n\t-o-transition: left 200ms ease-in;\n\ttransition: left 200ms ease-in;\n}\n#ui-bar.stowed {\n\tleft: -15.5em;\n}\n#ui-bar-tray {\n\tposition: absolute;\n\ttop: 0.2em;\n\tleft: 0;\n\tright: 0;\n}\n#ui-bar-body {\n\theight: 90%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.5em);\n\tmargin: 2.5em 0;\n\tpadding: 0 1.5em;\n}\n#ui-bar.stowed #ui-bar-history,\n#ui-bar.stowed #ui-bar-body {\n\tvisibility: hidden;\n\t-webkit-transition: visibility 200ms step-end;\n\t-o-transition: visibility 200ms step-end;\n\ttransition: visibility 200ms step-end;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-bar {\n\tbackground-color: #222;\n\tborder-right: 1px solid #444;\n\ttext-align: center;\n}\n#ui-bar a {\n\ttext-decoration: none;\n}\n#ui-bar hr {\n\tborder-color: #444;\n}\n#ui-bar-toggle,\n#ui-bar-history [id|=\"history\"] {\n\tfont-size: 1.2em;\n\tline-height: inherit;\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n}\n#ui-bar-toggle {\n\tdisplay: block;\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tborder-right: none;\n\tpadding: 0.3em 0.45em 0.25em;\n}\n#ui-bar.stowed #ui-bar-toggle {\n\tpadding: 0.3em 0.35em 0.25em 0.55em;\n}\n#ui-bar-toggle:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history {\n\tmargin: 0 auto;\n}\n#ui-bar-history [id|=\"history\"] {\n\tpadding: 0.2em 0.45em 0.35em;\n}\n#ui-bar-history #history-jumpto {\n\tpadding: 0.2em 0.665em 0.35em;\n}\n#ui-bar-history [id|=\"history\"]:not(:first-child) {\n\tmargin-left: 1.2em;\n}\n#ui-bar-history [id|=\"history\"]:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history [id|=\"history\"]:disabled {\n\tcolor: #444;\n\tbackground-color: transparent;\n\tborder-color: #444;\n}\n#ui-bar-body {\n\tline-height: 1.5;\n\toverflow: auto;\n}\n#ui-bar-body > :not(:first-child) {\n\tmargin-top: 2em;\n}\n#story-title {\n\tmargin: 0;\n\tfont-size: 162.5%;\n}\n#story-author {\n\tmargin-top: 2em;\n\tfont-weight: bold;\n}\n#menu ul {\n\tmargin: 1em 0 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid #444;\n}\n#menu ul:empty {\n\tdisplay: none;\n}\n#menu li {\n\tmargin: 0;\n}\n#menu li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#menu li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-transform: uppercase;\n}\n#menu li a:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-bar-history [id|=\"history\"],\n#ui-bar-toggle,\n#menu a {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-bar-toggle:before,\n#ui-bar-history [id|=\"history\"],\n#menu-core li[id|=\"menu-item\"] a:before {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-bar-toggle:before {\n\tcontent: \"\\e81d\";\n}\n#ui-bar.stowed #ui-bar-toggle:before {\n\tcontent: \"\\e81e\";\n}\n#menu-item-saves a:before {\n\tcontent: \"\\e82b\\00a0\";\n}\n#menu-item-settings a:before {\n\tcontent: \"\\e82d\\00a0\";\n}\n#menu-item-restart a:before {\n\tcontent: \"\\e82c\\00a0\";\n}\n#menu-item-share a:before {\n\tcontent: \"\\e82f\\00a0\";\n}\n</style>\n<style id=\"style-ui-debug\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-debug.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault debug bar styles.\n*/\n#debug-bar {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 0;\n\tmargin: 0;\n\tmax-height: 75%;\n\t/* max-width: 28em;\n\tmin-width: 22em; */\n\tpadding: 0.5em;\n\tposition: fixed;\n\tright: 0;\n\tz-index: 99900;\n}\n#debug-bar > div:not([id]) + div {\n\tmargin-top: 0.5em;\n}\n#debug-bar > div > label {\n\tmargin-right: 0.5em;\n}\n#debug-bar > div > input[type=\"text\"] {\n\tmin-width: 0;\n\twidth: 8em;\n}\n#debug-bar > div > select {\n\twidth: 15em;\n}\n\n#debug-bar-toggle {\n\tcolor: #eee;\n\tbackground-color: #222;\n\tborder: 1px solid #444;\n\theight: 101%; /* fallback for browsers without support for calc() */\n\theight: calc(100% + 1px);\n\tleft: -2em; /* fallback for browsers without support for calc() */\n\tleft: calc(-2em - 1px);\n\tposition: absolute;\n\ttop: -1px;\n\twidth: 2em;\n}\n#debug-bar-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n#debug-bar-hint {\n\tbottom: 0.175em;\n\tfont-size: 4.5em;\n\topacity: 0.33;\n\tpointer-events: none;\n\tposition: fixed;\n\tright: 0.6em;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n\twhite-space: nowrap;\n}\n\n#debug-bar-watch {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 102%; /* fallback for browsers without support for calc() */\n\tbottom: calc(100% + 1px);\n\tfont-size: 0.9em;\n\tleft: -1px;\n\tmax-height: 650%; /* fallback for browsers without support for vh units */\n\tmax-height: 65vh;\n\tposition: absolute;\n\toverflow-x: hidden;\n\toverflow-y: scroll;\n\tright: 0;\n\tz-index: 99800;\n}\n#debug-bar-watch[hidden] {\n\tdisplay: none;\n}\n#debug-bar-watch div {\n\tcolor: #999;\n\tfont-style: italic;\n\tmargin: 1em auto;\n\ttext-align: center;\n}\n#debug-bar-watch table {\n\twidth: 100%;\n}\n#debug-bar-watch tr:nth-child(2n) {\n\tbackground-color: rgba(127, 127, 127, 0.15);\n}\n#debug-bar-watch td {\n\tpadding: 0.2em 0;\n}\n#debug-bar-watch td:first-child + td {\n\tpadding: 0.2em 0.3em 0.2em 0.1em;\n}\n#debug-bar-watch .watch-delete {\n\tbackground-color: transparent;\n\tborder: none;\n\tcolor: #c00;\n}\n#debug-bar-watch-all,\n#debug-bar-watch-none {\n\tmargin-left: 0.5em;\n}\n#debug-bar-watch-toggle,\n#debug-bar-views-toggle {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tmargin-right: 1em;\n\tpadding: 0.4em;\n}\n#debug-bar-watch-toggle:hover,\n#debug-bar-views-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle,\nhtml[data-debug-view] #debug-bar-views-toggle {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:hover,\nhtml[data-debug-view] #debug-bar-views-toggle:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n\n#debug-bar-toggle:before,\n#debug-bar-hint:after,\n#debug-bar-watch .watch-delete:before,\n#debug-bar-watch-add:before,\n#debug-bar-watch-all:before,\n#debug-bar-watch-none:before,\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#debug-bar-toggle:before {\n\tcontent: \"\\e838\";\n}\n#debug-bar-hint:after {\n\tcontent: \"\\e838\\202f\\e822\";\n}\n#debug-bar-watch .watch-delete:before {\n\tcontent: \"\\e804\";\n}\n#debug-bar-watch-add:before {\n\tcontent: \"\\e805\";\n}\n#debug-bar-watch-all:before {\n\tcontent: \"\\e83a\";\n}\n#debug-bar-watch-none:before {\n\tcontent: \"\\e827\";\n}\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:after,\nhtml[data-debug-view] #debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n\n\n/*\n\tDefault debug view styles.\n*/\nhtml[data-debug-view] .debug {\n\tpadding: 0.25em;\n\tbackground-color: #234; /* #541, #151 */\n}\nhtml[data-debug-view] .debug[title] {\n\tcursor: help;\n}\nhtml[data-debug-view] .debug.block {\n\tdisplay: inline-block;\n\tvertical-align: middle;\n}\nhtml[data-debug-view] .debug.invalid {\n\ttext-decoration: line-through;\n}\nhtml[data-debug-view] .debug.hidden,\nhtml[data-debug-view] .debug.hidden .debug {\n\tbackground-color: #555;\n}\nhtml:not([data-debug-view]) .debug.hidden {\n\tdisplay: none;\n}\n\nhtml[data-debug-view] .debug[data-name][data-type]:before,\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:after {\n\tbackground-color: rgba(0,0,0,0.25);\n\tfont-family: monospace, monospace;\n\twhite-space: pre;\n}\nhtml[data-debug-view] .debug[data-name][data-type]:before {\n\tcontent: attr(data-name);\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"]:before {\n\tcontent: \"<<\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"].nonvoid:after {\n\tcontent: \"<</\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"]:before {\n\tcontent: \"<\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"].nonvoid:after {\n\tcontent: \"</\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type]:not(:empty):before {\n\tmargin-right: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:not(:empty):after {\n\tmargin-left: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"],\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"]:before {\n\tdisplay: block;\n}\n</style>\n</head>\n<body>\n\t<div id=\"init-screen\">\n\t\t<div id=\"init-no-js\"><noscript>JavaScript is required. Please enable it to continue.</noscript></div>\n\t\t<div id=\"init-lacking\">Your browser lacks required capabilities. Please upgrade it or switch to another to continue.</div>\n\t\t<div id=\"init-loading\"><div>Loading&hellip;</div></div>\n\t</div>\n\t{{STORY_DATA}}\n\t<script id=\"script-sugarcube\" type=\"text/javascript\">\n\t/*! SugarCube JS */\n\tif(document.documentElement.getAttribute(\"data-init\")===\"loading\"){window.TWINE1=false;\nwindow.DEBUG=false;\n(function (window, document, jQuery, undefined) {\n\"use strict\";\n\n/***********************************************************************************************************************\n\n\tlib/alert.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: This regular expression should be elsewhere.\n\n\tError prologs by engine/browser: (ca. 2018)\n\t\tChrome, Opera, & Vivaldi → `Uncaught \\w*Error: …`\n\t\tEdge & IE                → `…`\n\t\tFirefox                  → `Error: …`\n\t\tOpera (Presto)           → `Uncaught exception: \\w*(?:Error|Exception): …`\n\t\tSafari (ca. v5.1)        → `\\w*(?:Error|_ERR): …`\n*/\nvar errorPrologRegExp = /^(?:(?:uncaught\\s+(?:exception:\\s+)?)?\\w*(?:error|exception|_err):\\s+)+/i; // eslint-disable-line no-unused-vars, no-var\n\nvar Alert = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tError Functions.\n\t*******************************************************************************************************************/\n\tfunction _alertMesg(type, where, what, error) {\n\t\tconst isFatal = type === 'fatal';\n\t\tlet mesg = `Apologies! ${isFatal ? 'A fatal' : 'An'} error has occurred.`;\n\n\t\tif (isFatal) {\n\t\t\tmesg += ' Aborting.';\n\t\t}\n\t\telse {\n\t\t\tmesg += ' You may be able to continue, but some parts may not work properly.';\n\t\t}\n\n\t\tif (where != null || what != null) { // lazy equality for null\n\t\t\tmesg += '\\n\\nError';\n\n\t\t\tif (where != null) { // lazy equality for null\n\t\t\t\tmesg += ` [${where}]`;\n\t\t\t}\n\n\t\t\tif (what != null) { // lazy equality for null\n\t\t\t\tmesg += `: ${what.replace(errorPrologRegExp, '')}.`;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmesg += ': unknown error.';\n\t\t\t}\n\t\t}\n\n\t\tif (typeof error === 'object' && error !== null && error.stack) {\n\t\t\tmesg += `\\n\\nStack Trace:\\n${error.stack}`;\n\t\t}\n\n\t\twindow.alert(mesg); // eslint-disable-line no-alert\n\t}\n\n\tfunction alertError(where, what, error) {\n\t\t_alertMesg(null, where, what, error);\n\t}\n\n\tfunction alertFatal(where, what, error) {\n\t\t_alertMesg('fatal', where, what, error);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tError Event.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSet up a global error handler for uncaught exceptions.\n\t*/\n\t(origOnError => {\n\t\twindow.onerror = function (what, source, lineNum, colNum, error) {\n\t\t\t// Uncaught exceptions during play may be recoverable/ignorable.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\talertError(null, what, error);\n\t\t\t}\n\n\t\t\t// Uncaught exceptions during startup should be fatal.\n\t\t\telse {\n\t\t\t\talertFatal(null, what, error);\n\t\t\t\twindow.onerror = origOnError;\n\n\t\t\t\tif (typeof window.onerror === 'function') {\n\t\t\t\t\twindow.onerror.apply(this, arguments);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})(window.onerror);\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\terror : { value : alertError },\n\t\tfatal : { value : alertFatal }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/patterns.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tTODO: Move all markup patterns into here.\n*/\n\nvar Patterns = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPatterns.\n\t*******************************************************************************************************************/\n\t/*\n\t\tWhitespace patterns.\n\n\t\tSpace class (equivalent to `\\s`):\n\t\t\t[\\u0020\\f\\n\\r\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]\n\t\tSpace class, sans line terminators:\n\t\t\t[\\u0020\\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]\n\t\tLine Terminator class:\n\t\t\t[\\n\\r\\u2028\\u2029]\n\t*/\n\tconst space = (() => {\n\t\t/*\n\t\t\tSome browsers still supported by SugarCube have faulty space classes (`\\s`).\n\t\t\tWe check for that lossage here and, if necessary, build our own class from\n\t\t\tthe component pieces.\n\t\t*/\n\t\tconst wsMap = new Map([\n\t\t\t['\\u0020', '\\\\u0020'],\n\t\t\t['\\f', '\\\\f'],\n\t\t\t['\\n', '\\\\n'],\n\t\t\t['\\r', '\\\\r'],\n\t\t\t['\\t', '\\\\t'],\n\t\t\t['\\v', '\\\\v'],\n\t\t\t['\\u00a0', '\\\\u00a0'],\n\t\t\t['\\u1680', '\\\\u1680'],\n\t\t\t['\\u180e', '\\\\u180e'],\n\t\t\t['\\u2000', '\\\\u2000'],\n\t\t\t['\\u2001', '\\\\u2001'],\n\t\t\t['\\u2002', '\\\\u2002'],\n\t\t\t['\\u2003', '\\\\u2003'],\n\t\t\t['\\u2004', '\\\\u2004'],\n\t\t\t['\\u2005', '\\\\u2005'],\n\t\t\t['\\u2006', '\\\\u2006'],\n\t\t\t['\\u2007', '\\\\u2007'],\n\t\t\t['\\u2008', '\\\\u2008'],\n\t\t\t['\\u2009', '\\\\u2009'],\n\t\t\t['\\u200a', '\\\\u200a'],\n\t\t\t['\\u2028', '\\\\u2028'],\n\t\t\t['\\u2029', '\\\\u2029'],\n\t\t\t['\\u202f', '\\\\u202f'],\n\t\t\t['\\u205f', '\\\\u205f'],\n\t\t\t['\\u3000', '\\\\u3000'],\n\t\t\t['\\ufeff', '\\\\ufeff']\n\t\t]);\n\t\tconst wsRe = /^\\s$/;\n\t\tlet missing = '';\n\n\t\twsMap.forEach((pat, char) => {\n\t\t\tif (!wsRe.test(char)) {\n\t\t\t\tmissing += pat;\n\t\t\t}\n\t\t});\n\n\t\treturn missing ? `[\\\\s${missing}]` : '\\\\s';\n\t})();\n\tconst spaceNoTerminator = '[\\\\u0020\\\\f\\\\t\\\\v\\\\u00a0\\\\u1680\\\\u180e\\\\u2000-\\\\u200a\\\\u202f\\\\u205f\\\\u3000\\\\ufeff]';\n\tconst lineTerminator    = '[\\\\n\\\\r\\\\u2028\\\\u2029]';\n\tconst notSpace          = space === '\\\\s' ? '\\\\S' : space.replace(/^\\[/, '[^');\n\n\t/*\n\t\tCharacter patterns.\n\t*/\n\tconst anyChar = `(?:.|${lineTerminator})`;\n\n\t/*\n\t\tLetter patterns.\n\n\t\tFIXME:\n\t\t\t1. The existing set, which is a TiddlyWiki holdover, should probably\n\t\t\t   encompass a significantly greater range of BMP code points.\n\t\t\t2. Should we include the surrogate pair code units (\\uD800-\\uDBFF &\n\t\t\t   \\uDC00-\\uDFFF) to handle non-BMP code points?  Further, should we\n\t\t\t   simply be checking for the code units themselves or checking for\n\t\t\t   properly mated pairs?\n\t*/\n\tconst anyLetter       = '[0-9A-Z_a-z\\\\-\\\\u00c0-\\\\u00d6\\\\u00d8-\\\\u00f6\\\\u00f8-\\\\u00ff\\\\u0150\\\\u0170\\\\u0151\\\\u0171]';\n\tconst anyLetterStrict = anyLetter.replace('\\\\-', ''); // anyLetter sans hyphen\n\n\t/*\n\t\tIdentifier patterns.\n\n\t\tNOTE: Since JavaScript's RegExp syntax does not support Unicode character\n\t\tclasses, the correct regular expression to match a valid identifier name,\n\t\twithin the scope of our needs, would be on the order of approximately 5–6\n\t\tor 11–16 KiB, depending on how the pattern was built.  That being the case,\n\t\tfor the moment we restrict valid TwineScript identifiers to US-ASCII.\n\n\t\tFIXME: Fix this to, at least, approximate the correct range.\n\t*/\n\tconst identifierFirstChar = '[$A-Z_a-z]';\n\tconst identifierNextChar  = '[$0-9A-Z_a-z]';\n\tconst identifier          = `${identifierFirstChar}${identifierNextChar}*`;\n\n\t// Variable patterns.\n\tconst variableSigil = '[$_]';\n\tconst variable      = variableSigil + identifier;\n\n\t// Macro name pattern.\n\tconst macroName = '[A-Za-z][\\\\w-]*|[=-]';\n\n\t// Template name pattern.\n\tconst templateName = '[A-Za-z][\\\\w-]*';\n\n\t// CSS ID or class sigil pattern.\n\tconst cssIdOrClassSigil = '[#.]';\n\n\t// CSS image transclusion template pattern.\n\t//\n\t// NOTE: The alignment syntax isn't supported, but removing it might break uses\n\t// of the template in the wild, so we leave it alone for now.\n\tconst cssImage = '\\\\[[<>]?[Ii][Mm][Gg]\\\\[(?:\\\\s|\\\\S)*?\\\\]\\\\]+';\n\n\t// Inline CSS pattern.\n\tconst inlineCss = (() => {\n\t\t/* legacy */\n\t\tconst twStyle   = `(${anyLetter}+)\\\\(([^\\\\)\\\\|\\\\n]+)\\\\):`;\n\t\t/* /legacy */\n\t\tconst cssStyle  = `${spaceNoTerminator}*(${anyLetter}+)${spaceNoTerminator}*:([^;\\\\|\\\\n]+);`;\n\t\tconst idOrClass = `${spaceNoTerminator}*((?:${cssIdOrClassSigil}${anyLetter}+${spaceNoTerminator}*)+);`;\n\n\t\t// [1,2] = style(value):\n\t\t// [3,4] = style:value;\n\t\t// [5]   = #id.className;\n\t\treturn `${twStyle}|${cssStyle}|${idOrClass}`;\n\t})();\n\n\t// URL pattern.\n\tconst url = '(?:file|https?|mailto|ftp|javascript|irc|news|data):[^\\\\s\\'\"]+';\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze({\n\t\tspace,\n\t\tspaceNoTerminator,\n\t\tlineTerminator,\n\t\tnotSpace,\n\t\tanyChar,\n\t\tanyLetter,\n\t\tanyLetterStrict,\n\t\tidentifierFirstChar,\n\t\tidentifierNextChar,\n\t\tidentifier,\n\t\tvariableSigil,\n\t\tvariable,\n\t\tmacroName,\n\t\ttemplateName,\n\t\tcssIdOrClassSigil,\n\t\tcssImage,\n\t\tinlineCss,\n\t\turl\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/extensions.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\n/*\n\tJavaScript Polyfills.\n\n\tNOTE: The ES5 and ES6 polyfills come from the vendored `es5-shim.js` and `es6-shim.js` libraries.\n*/\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tTrims whitespace from either the start or end of the given string.\n\t*/\n\tconst _trimString = (() => {\n\t\t// Whitespace regular expressions.\n\t\tconst startWSRe = new RegExp(`^${Patterns.space}${Patterns.space}*`);\n\t\tconst endWSRe   = new RegExp(`${Patterns.space}${Patterns.space}*$`);\n\n\t\tfunction trimString(str, where) {\n\t\t\tconst val = String(str);\n\n\t\t\tif (!val) {\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tswitch (where) {\n\t\t\tcase 'start':\n\t\t\t\treturn startWSRe.test(val) ? val.replace(startWSRe, '') : val;\n\n\t\t\tcase 'end':\n\t\t\t\treturn endWSRe.test(val) ? val.replace(endWSRe, '') : val;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`_trimString called with incorrect where parameter value: \"${where}\"`);\n\t\t\t}\n\t\t}\n\n\t\treturn trimString;\n\t})();\n\n\t/*\n\t\tGenerates a pad string based upon the given string and length.\n\t*/\n\tfunction _createPadString(length, padding) {\n\t\tconst targetLength = Number.parseInt(length, 10) || 0;\n\n\t\tif (targetLength < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet padString = typeof padding === 'undefined' ? '' : String(padding);\n\n\t\tif (padString === '') {\n\t\t\tpadString = ' ';\n\t\t}\n\n\t\twhile (padString.length < targetLength) {\n\t\t\tconst curPadLength    = padString.length;\n\t\t\tconst remainingLength = targetLength - curPadLength;\n\n\t\t\tpadString += curPadLength > remainingLength\n\t\t\t\t? padString.slice(0, remainingLength)\n\t\t\t\t: padString;\n\t\t}\n\n\t\tif (padString.length > targetLength) {\n\t\t\tpadString = padString.slice(0, targetLength);\n\t\t}\n\n\t\treturn padString;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPolyfills.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[ES2019] Returns a new array consisting of the source array with all sub-array elements\n\t\tconcatenated into it recursively up to the given depth.\n\t*/\n\tif (!Array.prototype.flat) {\n\t\tObject.defineProperty(Array.prototype, 'flat', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\t\t\tvalue        : (() => {\n\t\t\t\tfunction flat(/* depth */) {\n\t\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\t\tthrow new TypeError('Array.prototype.flat called on null or undefined');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst depth = arguments.length === 0 ? 1 : Number(arguments[0]) || 0;\n\n\t\t\t\t\tif (depth < 1) {\n\t\t\t\t\t\treturn Array.prototype.slice.call(this);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Array.prototype.reduce.call(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\t(acc, cur) => {\n\t\t\t\t\t\t\tif (cur instanceof Array) {\n\t\t\t\t\t\t\t\t// acc.push.apply(acc, flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t\tacc.push(...flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc.push(cur);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn flat;\n\t\t\t})()\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new array consisting of the result of calling the given mapping function\n\t\ton every element in the source array and then concatenating all sub-array elements into it\n\t\trecursively up to a depth of `1`.  Identical to calling `<Array>.map(fn).flat()`.\n\t*/\n\tif (!Array.prototype.flatMap) {\n\t\tObject.defineProperty(Array.prototype, 'flatMap', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* callback [, thisArg] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.flatMap called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.map.apply(this, arguments).flat();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2016] Returns whether the given element was found within the array.\n\t*/\n\tif (!Array.prototype.includes) {\n\t\tObject.defineProperty(Array.prototype, 'includes', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.includes called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst length = this.length >>> 0;\n\n\t\t\t\tif (length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst needle = arguments[0];\n\t\t\t\tlet i = Number(arguments[1]) || 0;\n\n\t\t\t\tif (i < 0) {\n\t\t\t\t\ti = Math.max(0, length + i);\n\t\t\t\t}\n\n\t\t\t\tfor (/* empty */; i < length; ++i) {\n\t\t\t\t\tconst value = this[i];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property/value\n\t\tpairs as `[key, value]` arrays.\n\t*/\n\tif (!Object.entries) {\n\t\tObject.defineProperty(Object, 'entries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.entries object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => [key, obj[key]]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new generic object consisting of the given list's key/value pairs.\n\t*/\n\tif (!Object.fromEntries) {\n\t\tObject.defineProperty(Object, 'fromEntries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(iter) {\n\t\t\t\treturn Array.from(iter).reduce(\n\t\t\t\t\t(acc, pair) => {\n\t\t\t\t\t\tif (Object(pair) !== pair) {\n\t\t\t\t\t\t\tthrow new TypeError('Object.fromEntries iterable parameter must yield objects');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (pair[0] in acc) {\n\t\t\t\t\t\t\tObject.defineProperty(acc, pair[0], {\n\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\tvalue        : pair[1]\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tacc[pair[0]] = pair[1]; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns all own property descriptors of the given object.\n\t*/\n\tif (!Object.getOwnPropertyDescriptors) {\n\t\tObject.defineProperty(Object, 'getOwnPropertyDescriptors', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (obj == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Object.getOwnPropertyDescriptors object parameter is null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst O = Object(obj);\n\n\t\t\t\treturn Reflect.ownKeys(O).reduce(\n\t\t\t\t\t(acc, key) => {\n\t\t\t\t\t\tconst desc = Object.getOwnPropertyDescriptor(O, key);\n\n\t\t\t\t\t\tif (typeof desc !== 'undefined') {\n\t\t\t\t\t\t\tif (key in acc) {\n\t\t\t\t\t\t\t\tObject.defineProperty(acc, key, {\n\t\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\t\tvalue        : desc\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc[key] = desc; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property values.\n\t*/\n\tif (!Object.values) {\n\t\tObject.defineProperty(Object, 'values', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.values object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => obj[key]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the start of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padStart) {\n\t\tObject.defineProperty(String.prototype, 'padStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn _createPadString(targetLength - baseLength, padding) + baseString;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the end of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padEnd) {\n\t\tObject.defineProperty(String.prototype, 'padEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn baseString + _createPadString(targetLength - baseLength, padding);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the start of the string.\n\t*/\n\tif (!String.prototype.trimStart) {\n\t\tObject.defineProperty(String.prototype, 'trimStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimLeft) {\n\t\tObject.defineProperty(String.prototype, 'trimLeft', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimLeft called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the end of the string.\n\t*/\n\tif (!String.prototype.trimEnd) {\n\t\tObject.defineProperty(String.prototype, 'trimEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimRight) {\n\t\tObject.defineProperty(String.prototype, 'trimRight', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimRight called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n})();\n\n\n/*\n\tJavaScript Extensions.\n*/\n(() => {\n\t'use strict';\n\n\tconst _nativeMathRandom = Math.random;\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the given bounds.\n\t*/\n\tfunction _random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('_random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = arguments[0];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = arguments[0];\n\t\t\tmax = arguments[1];\n\t\t\tbreak;\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(_nativeMathRandom() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a randomly selected index within the given length and bounds.\n\t\tBounds may be negative.\n\t*/\n\tfunction _randomIndex(length, boundsArgs) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (boundsArgs.length) {\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = length - 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(boundsArgs[1]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(boundsArgs[1]);\n\t\t\tmax = Math.trunc(boundsArgs[2]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min)) {\n\t\t\tmin = 0;\n\t\t}\n\t\telse if (!Number.isFinite(min) || min >= length) {\n\t\t\tmin = length - 1;\n\t\t}\n\t\telse if (min < 0) {\n\t\t\tmin = length + min;\n\n\t\t\tif (min < 0) {\n\t\t\t\tmin = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (Number.isNaN(max)) {\n\t\t\tmax = 0;\n\t\t}\n\t\telse if (!Number.isFinite(max) || max >= length) {\n\t\t\tmax = length - 1;\n\t\t}\n\t\telse if (max < 0) {\n\t\t\tmax = length + max;\n\n\t\t\tif (max < 0) {\n\t\t\t\tmax = length - 1;\n\t\t\t}\n\t\t}\n\n\t\treturn _random(min, max);\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Will throw exceptions on invalid surrogate pairs.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction _getCodePointStartAndEnd(str, pos) {\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : pos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos) + str.charAt(nextPos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : nextPos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\treturn {\n\t\t\tchar  : str.charAt(prevPos) + str.charAt(pos),\n\t\t\tstart : prevPos,\n\t\t\tend   : pos\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, General.\n\t*******************************************************************************************************************/\n\t/*\n\t\tRandomly selects an element from the given array, or array-like object, and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(array /* DEPRECATED: [, [min ,] max] */) {\n\t\t\tif (\n\t\t\t\t   typeof array !== 'object'\n\t\t\t\t|| array === null\n\t\t\t\t|| !Object.prototype.hasOwnProperty.call(array, 'length')\n\t\t\t) {\n\t\t\t\tthrow new TypeError('Array.random array parameter must be an array or array-lke object');\n\t\t\t}\n\n\t\t\tconst length = array.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, Array.prototype.slice.call(arguments, 1));\n\n\t\t\treturn array[index];\n\t\t}\n\t});\n\n\t/*\n\t\tConcatenates one or more unique elements to the end of the base array\n\t\tand returns the result as a new array.  Elements which are arrays will\n\t\tbe merged—i.e. their elements will be concatenated, rather than the\n\t\tarray itself.\n\t*/\n\tObject.defineProperty(Array.prototype, 'concatUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.concatUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst result = Array.from(this);\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst items   = Array.prototype.reduce.call(arguments, (prev, cur) => prev.concat(cur), []);\n\t\t\tconst addSize = items.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = items[i];\n\n\t\t\t\tif (indexOf.call(result, value) === -1) {\n\t\t\t\t\tpush.call(result, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst needle  = arguments[0];\n\t\t\tlet pos   = Number(arguments[1]) || 0;\n\t\t\tlet count = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\t++pos;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the given elements from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'delete', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.delete called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst needles       = Array.prototype.concat.apply([], arguments);\n\t\t\tconst needlesLength = needles.length;\n\t\t\tconst indices       = [];\n\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tconst value = this[i];\n\n\t\t\t\tfor (let j = 0; j < needlesLength; ++j) {\n\t\t\t\t\tconst needle = needles[j];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\tindices.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0, iend = indices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[indices[i]];\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements at the given indices from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteAt', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* indices */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteAt called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice     = Array.prototype.splice;\n\t\t\tconst cpyIndices = [\n\t\t\t\t...new Set(\n\t\t\t\t\tArray.prototype.concat.apply([], arguments)\n\t\t\t\t\t\t// Map negative indices to their positive counterparts,\n\t\t\t\t\t\t// so the Set can properly filter out duplicates.\n\t\t\t\t\t\t.map(x => x < 0 ? Math.max(0, length + x) : x)\n\t\t\t\t).values()\n\t\t\t];\n\t\t\tconst delIndices = [...cpyIndices].sort((a, b) => b - a);\n\t\t\tconst result     = [];\n\n\t\t\t// Copy the elements (in originally specified order).\n\t\t\tfor (let i = 0, iend = cpyIndices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[cpyIndices[i]];\n\t\t\t}\n\n\t\t\t// Delete the elements (in descending numeric order).\n\t\t\tfor (let i = 0, iend = delIndices.length; i < iend; ++i) {\n\t\t\t\tsplice.call(this, delIndices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements that pass the test implemented\n\t\tby the given predicate function from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteWith', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(predicate, thisArg) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteWith called on null or undefined');\n\t\t\t}\n\t\t\tif (typeof predicate !== 'function') {\n\t\t\t\tthrow new Error('Array.prototype.deleteWith predicate parameter must be a function');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice  = Array.prototype.splice;\n\t\t\tconst indices = [];\n\t\t\tconst result  = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tif (predicate.call(thisArg, this[i], i, this)) {\n\t\t\t\t\tresult.push(this[i]);\n\t\t\t\t\tindices.push(i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[0];\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAll called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAll.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\t!Array.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAny called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAny.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\tArray.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[length - 1];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluck', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluck called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn Array.prototype.splice.call(this, index, 1)[0];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes the given number of unique elements from the base array\n\t\tand returns the removed elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluckMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluckMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.pluckMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\t\t\tconst result = [];\n\t\t\tlet max = length - 1;\n\n\t\t\tdo {\n\t\t\t\tresult.push(splice.call(this, _random(0, max--), 1)[0]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tAppends one or more unique elements to the end of the base array and\n\t\treturns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pushUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pushUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tpush.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.random called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn this[index];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects the given number of unique elements from the base array\n\t\tand returns the selected elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'randomMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.randomMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.randomMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst picked = new Map();\n\t\t\tconst result = [];\n\t\t\tconst max    = length - 1;\n\n\t\t\tdo {\n\t\t\t\tlet i;\n\t\t\t\tdo {\n\t\t\t\t\ti = _random(0, max);\n\t\t\t\t} while (picked.has(i));\n\t\t\t\tpicked.set(i, true);\n\t\t\t\tresult.push(this[i]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly shuffles the array and returns it.\n\t*/\n\tObject.defineProperty(Array.prototype, 'shuffle', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.shuffle called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tfor (let i = length - 1; i > 0; --i) {\n\t\t\t\tconst j = Math.floor(_nativeMathRandom() * (i + 1));\n\n\t\t\t\tif (i === j) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// [this[i], this[j]] = [this[j], this[i]];\n\t\t\t\tconst swap = this[i];\n\t\t\t\tthis[i] = this[j];\n\t\t\t\tthis[j] = swap;\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t});\n\n\t/*\n\t\tPrepends one or more unique elements to the beginning of the base array\n\t\tand returns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'unshiftUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.unshiftUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst unshift = Array.prototype.unshift;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tunshift.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a bound function that supplies the given arguments to the base\n\t\tfunction, followed by the arguments are supplied to the bound function,\n\t\twhenever it is called.\n\t*/\n\tObject.defineProperty(Function.prototype, 'partial', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Function.prototype.partial called on null or undefined');\n\t\t\t}\n\n\t\t\tconst slice = Array.prototype.slice;\n\t\t\tconst fn    = this;\n\t\t\tconst bound = slice.call(arguments, 0);\n\n\t\t\treturn function () {\n\t\t\t\tconst applied = [];\n\t\t\t\tlet argc = 0;\n\n\t\t\t\tfor (let i = 0; i < bound.length; ++i) {\n\t\t\t\t\tapplied.push(bound[i] === undefined ? arguments[argc++] : bound[i]);\n\t\t\t\t}\n\n\t\t\t\treturn fn.apply(this, applied.concat(slice.call(arguments, argc)));\n\t\t\t};\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the given numerical clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Math, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num, min, max) {\n\t\t\tconst value = Number(num);\n\t\t\treturn Number.isNaN(value) ? NaN : value.clamp(min, max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a decimal number eased from 0 to 1.\n\n\t\tNOTE: The magnitude of the returned value decreases if num < 0.5 or increases if num > 0.5.\n\t*/\n\tObject.defineProperty(Math, 'easeInOut', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num) {\n\t\t\treturn 1 - (Math.cos(Number(num) * Math.PI) + 1) / 2;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Number.prototype, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* min, max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Number.prototype.clamp called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length !== 2) {\n\t\t\t\tthrow new Error('Number.prototype.clamp called with an incorrect number of parameters');\n\t\t\t}\n\n\t\t\tlet min = Number(arguments[0]);\n\t\t\tlet max = Number(arguments[1]);\n\n\t\t\tif (min > max) {\n\t\t\t\t[min, max] = [max, min];\n\t\t\t}\n\n\t\t\treturn Math.min(Math.max(this, min), max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the given string with all RegExp metacharacters escaped.\n\t*/\n\tif (!RegExp.escape) {\n\t\t(() => {\n\t\t\tconst _regExpMetaCharsRe    = /[\\\\^$*+?.()|[\\]{}]/g;\n\t\t\tconst _hasRegExpMetaCharsRe = new RegExp(_regExpMetaCharsRe.source); // to drop the global flag\n\n\t\t\tObject.defineProperty(RegExp, 'escape', {\n\t\t\t\tconfigurable : true,\n\t\t\t\twritable     : true,\n\n\t\t\t\tvalue(str) {\n\t\t\t\t\tconst val = String(str);\n\t\t\t\t\treturn val && _hasRegExpMetaCharsRe.test(val)\n\t\t\t\t\t\t? val.replace(_regExpMetaCharsRe, '\\\\$&')\n\t\t\t\t\t\t: val;\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t}\n\n\t/*\n\t\tReturns a formatted string, after replacing each format item in the given\n\t\tformat string with the text equivalent of the corresponding argument's value.\n\t*/\n\t(() => {\n\t\tconst _formatRegExp    = /{(\\d+)(?:,([+-]?\\d+))?}/g;\n\t\tconst _hasFormatRegExp = new RegExp(_formatRegExp.source); // to drop the global flag\n\n\t\tObject.defineProperty(String, 'format', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(format) {\n\t\t\t\tfunction padString(str, align, pad) {\n\t\t\t\t\tif (!align) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst plen = Math.abs(align) - str.length;\n\n\t\t\t\t\tif (plen < 1) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\t// const padding = Array(plen + 1).join(pad);\n\t\t\t\t\tconst padding = String(pad).repeat(plen);\n\t\t\t\t\treturn align < 0 ? str + padding : padding + str;\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length < 2) {\n\t\t\t\t\treturn arguments.length === 0 ? '' : format;\n\t\t\t\t}\n\n\t\t\t\tconst args = arguments.length === 2 && Array.isArray(arguments[1])\n\t\t\t\t\t? [...arguments[1]]\n\t\t\t\t\t: Array.prototype.slice.call(arguments, 1);\n\n\t\t\t\tif (args.length === 0) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\tif (!_hasFormatRegExp.test(format)) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t\t_formatRegExp.lastIndex = 0;\n\n\t\t\t\treturn format.replace(_formatRegExp, (match, index, align) => {\n\t\t\t\t\tlet retval = args[index];\n\n\t\t\t\t\tif (retval == null) { // lazy equality for null\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\n\t\t\t\t\twhile (typeof retval === 'function') {\n\t\t\t\t\t\tretval = retval();\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (typeof retval) {\n\t\t\t\t\tcase 'string': /* no-op */ break;\n\t\t\t\t\tcase 'object': retval = JSON.stringify(retval); break;\n\t\t\t\t\tdefault:       retval = String(retval); break;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn padString(retval, !align ? 0 : Number.parseInt(align, 10), ' ');\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t})();\n\n\t/*\n\t\tReturns whether the given string was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn String.prototype.indexOf.apply(this, arguments) !== -1;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given substring was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst needle = String(arguments[0] || '');\n\n\t\t\tif (needle === '') {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tconst indexOf = String.prototype.indexOf;\n\t\t\tconst step    = needle.length;\n\t\t\tlet pos     = Number(arguments[1]) || 0;\n\t\t\tlet count   = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\tpos += step;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the last code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, str.length - 1);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with `delCount` characters replaced with\n\t\t`replacement`, starting at `startAt`.\n\t*/\n\tObject.defineProperty(String.prototype, 'splice', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(startAt, delCount, replacement) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splice called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tlet start = Number(startAt);\n\n\t\t\tif (!Number.isSafeInteger(start)) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\telse if (start < 0) {\n\t\t\t\tstart += length;\n\n\t\t\t\tif (start < 0) {\n\t\t\t\t\tstart = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (start > length) {\n\t\t\t\tstart = length;\n\t\t\t}\n\n\t\t\tlet count = Number(delCount);\n\n\t\t\tif (!Number.isSafeInteger(count) || count < 0) {\n\t\t\t\tcount = 0;\n\t\t\t}\n\n\t\t\tlet res = this.slice(0, start);\n\n\t\t\tif (typeof replacement !== 'undefined') {\n\t\t\t\tres += replacement;\n\t\t\t}\n\n\t\t\tif (start + count < length) {\n\t\t\t\tres += this.slice(start + count);\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns an array of strings, split from the string, or an empty array if the\n\t\tstring is empty.\n\t*/\n\tObject.defineProperty(String.prototype, 'splitOrEmpty', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* [ separator [, limit ]] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splitOrEmpty called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tif (String(this) === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\treturn String.prototype.split.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased,\n\t\taccording to any locale-specific rules.\n\t*/\n\tObject.defineProperty(String.prototype, 'toLocaleUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toLocaleUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toLocaleUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased.\n\t*/\n\tObject.defineProperty(String.prototype, 'toUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, JSON.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDefine `toJSON()` methods on each prototype we wish to support.\n\t*/\n\tObject.defineProperty(Date.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:date)', this.toISOString()];\n\t\t}\n\t});\n\tObject.defineProperty(Function.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\t/*\n\t\t\t\tThe enclosing parenthesis here are necessary to force the function expression code\n\t\t\t\tstring, returned by `this.toString()`, to be evaluated as an expression during\n\t\t\t\trevival.  Without them, the function expression, which is likely nameless, will be\n\t\t\t\tevaluated as a function definition—which will throw a syntax error exception, since\n\t\t\t\tfunction definitions must have a name.\n\t\t\t*/\n\t\t\treturn ['(revive:eval)', `(${this.toString()})`];\n\t\t}\n\t});\n\tObject.defineProperty(Map.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:map)', [...this]];\n\t\t}\n\t});\n\tObject.defineProperty(RegExp.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:eval)', this.toString()];\n\t\t}\n\t});\n\tObject.defineProperty(Set.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:set)', [...this]];\n\t\t}\n\t});\n\n\t/*\n\t\tUtility method to allow users to easily wrap their code in the revive wrapper.\n\t*/\n\tObject.defineProperty(JSON, 'reviveWrapper', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(code, data) {\n\t\t\tif (typeof code !== 'string') {\n\t\t\t\tthrow new TypeError('JSON.reviveWrapper code parameter must be a string');\n\t\t\t}\n\n\t\t\treturn ['(revive:eval)', [code, data]];\n\t\t}\n\t});\n\n\t/*\n\t\tBackup the original `JSON.parse()` and replace it with a revive wrapper aware version.\n\t*/\n\tObject.defineProperty(JSON, '_real_parse', {\n\t\tvalue : JSON.parse\n\t});\n\tObject.defineProperty(JSON, 'parse', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(text, reviver) {\n\t\t\treturn JSON._real_parse(text, (key, val) => {\n\t\t\t\tlet value = val;\n\n\t\t\t\t/*\n\t\t\t\t\tAttempt to revive wrapped values.\n\t\t\t\t*/\n\t\t\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\t\t\tswitch (value[0]) {\n\t\t\t\t\tcase '(revive:set)':\n\t\t\t\t\t\tvalue = new Set(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:map)':\n\t\t\t\t\t\tvalue = new Map(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:date)':\n\t\t\t\t\t\tvalue = new Date(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:eval)':\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/* eslint-disable no-eval */\n\t\t\t\t\t\t\t// For post-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\tif (Array.isArray(value[1])) {\n\t\t\t\t\t\t\t\tconst $ReviveData$ = value[1][1]; // eslint-disable-line no-unused-vars\n\t\t\t\t\t\t\t\tvalue = eval(value[1][0]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// For regular expressions, functions, and pre-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvalue = eval(value[1]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* eslint-enable no-eval */\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* legacy */\n\t\t\t\telse if (typeof value === 'string' && value.slice(0, 10) === '@@revive@@') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = eval(value.slice(10)); // eslint-disable-line no-eval\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\t\t\t\t/* /legacy */\n\n\t\t\t\t/*\n\t\t\t\t\tCall the custom reviver, if specified.\n\t\t\t\t*/\n\t\t\t\tif (typeof reviver === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = reviver(key, value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\n\t\t\t\treturn value;\n\t\t\t});\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, Deprecated.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns whether the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAll called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAll.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAny called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAny.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns a new array consisting of the flattened source array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'flatten', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.flatten called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.flat.call(this, Infinity);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns an array of link titles, parsed from the string.\n\n\t\tNOTE: Unused in SugarCube, only included for compatibility.\n\t*/\n\tObject.defineProperty(String.prototype, 'readBracketedList', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.readBracketedList called on null or undefined');\n\t\t\t}\n\n\t\t\t// RegExp groups: Double-square-bracket quoted | Unquoted.\n\t\t\tconst re    = new RegExp('(?:\\\\[\\\\[((?:\\\\s|\\\\S)*?)\\\\]\\\\])|([^\"\\'\\\\s]\\\\S*)', 'gm');\n\t\t\tconst names = [];\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.exec(this)) !== null) {\n\t\t\t\tif (match[1]) { // double-square-bracket quoted\n\t\t\t\t\tnames.push(match[1]);\n\t\t\t\t}\n\t\t\t\telse if (match[2]) { // unquoted\n\t\t\t\t\tnames.push(match[2]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn names;\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/browser.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Browser = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable max-len */\n\tconst userAgent = navigator.userAgent.toLowerCase();\n\n\tconst winPhone = userAgent.includes('windows phone');\n\tconst isMobile = Object.freeze({\n\t\tAndroid    : !winPhone && userAgent.includes('android'),\n\t\tBlackBerry : /blackberry|bb10/.test(userAgent),\n\t\tiOS        : !winPhone && /ip(?:hone|ad|od)/.test(userAgent),\n\t\tOpera      : !winPhone && (typeof window.operamini === 'object' || userAgent.includes('opera mini')),\n\t\tWindows    : winPhone || /iemobile|wpdesktop/.test(userAgent),\n\n\t\tany() {\n\t\t\treturn isMobile.Android || isMobile.BlackBerry || isMobile.iOS || isMobile.Opera || isMobile.Windows;\n\t\t}\n\t});\n\n\tconst isGecko = !isMobile.Windows && !/khtml|trident|edge/.test(userAgent) && userAgent.includes('gecko');\n\n\tconst isIE      = !userAgent.includes('opera') && /msie|trident/.test(userAgent);\n\tconst ieVersion = isIE\n\t\t? (() => {\n\t\t\tconst ver = /(?:msie\\s+|rv:)(\\d+\\.\\d)/.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\t// opera <= 12: \"opera/9.80 (windows nt 6.1; wow64) presto/2.12.388 version/12.16\"\n\t// opera >= 15: \"mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/28.0.1500.52 safari/537.36 opr/15.0.1147.130\"\n\tconst isOpera      = userAgent.includes('opera') || userAgent.includes(' opr/');\n\tconst operaVersion = isOpera\n\t\t? (() => {\n\t\t\tconst re  = new RegExp(`${/khtml|chrome/.test(userAgent) ? 'opr' : 'version'}\\\\/(\\\\d+\\\\.\\\\d+)`);\n\t\t\tconst ver = re.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\tconst isVivaldi = userAgent.includes('vivaldi');\n\t/* eslint-enable max-len */\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\tuserAgent,\n\t\tisMobile,\n\t\tisGecko,\n\t\tisIE,\n\t\tieVersion,\n\t\tisOpera,\n\t\toperaVersion,\n\t\tisVivaldi\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/has.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Has = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tNOTE: The aggressive try/catch feature tests are necessitated by implementation\n\t\tbugs in various browsers.\n\t*/\n\n\t// Is the `HTMLAudioElement` API available?\n\tconst hasAudioElement = (() => {\n\t\ttry {\n\t\t\treturn typeof document.createElement('audio').canPlayType === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `File` API available?\n\tconst hasFile = (() => {\n\t\ttry {\n\t\t\treturn 'Blob' in window &&\n\t\t\t\t'File' in window &&\n\t\t\t\t'FileList' in window &&\n\t\t\t\t'FileReader' in window &&\n\t\t\t\t!Browser.isMobile.any() &&\n\t\t\t\t(!Browser.isOpera || Browser.operaVersion >= 15);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `geolocation` API available?\n\tconst hasGeolocation = (() => {\n\t\ttry {\n\t\t\treturn 'geolocation' in navigator &&\n\t\t\t\ttypeof navigator.geolocation.getCurrentPosition === 'function' &&\n\t\t\t\ttypeof navigator.geolocation.watchPosition === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `MutationObserver` API available?\n\tconst hasMutationObserver = (() => {\n\t\ttry {\n\t\t\treturn 'MutationObserver' in window &&\n\t\t\t\ttypeof window.MutationObserver === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `performance` API available?\n\tconst hasPerformance = (() => {\n\t\ttry {\n\t\t\treturn 'performance' in window &&\n\t\t\t\ttypeof window.performance.now === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the platform a touch device?\n\tconst hasTouch = (() => {\n\t\ttry {\n\t\t\treturn 'ontouchstart' in window ||\n\t\t\t\t!!window.DocumentTouch &&\n\t\t\t\tdocument instanceof window.DocumentTouch ||\n\t\t\t\t!!navigator.maxTouchPoints ||\n\t\t\t\t!!navigator.msMaxTouchPoints;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the transition end event available and by what name?\n\tconst hasTransitionEndEvent = (() => {\n\t\ttry {\n\t\t\tconst teMap = new Map([\n\t\t\t\t['transition',       'transitionend'],\n\t\t\t\t['MSTransition',     'msTransitionEnd'],\n\t\t\t\t['WebkitTransition', 'webkitTransitionEnd'],\n\t\t\t\t['MozTransition',    'transitionend']\n\t\t\t]);\n\t\t\tconst teKeys = [...teMap.keys()];\n\t\t\tconst el     = document.createElement('div');\n\n\t\t\tfor (let i = 0; i < teKeys.length; ++i) {\n\t\t\t\tif (el.style[teKeys[i]] !== undefined) {\n\t\t\t\t\treturn teMap.get(teKeys[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\taudio              : hasAudioElement,\n\t\tfileAPI            : hasFile,\n\t\tgeolocation        : hasGeolocation,\n\t\tmutationObserver   : hasMutationObserver,\n\t\tperformance        : hasPerformance,\n\t\ttouch              : hasTouch,\n\t\ttransitionEndEvent : hasTransitionEndEvent\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/visibility.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Visibility = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tThere are two versions of the Page Visibility API: First Edition and, the current,\n\t\tSecond Edition (i.e. \"Level 2\").  First Edition is mentioned here only because some\n\t\tolder browsers implement it, rather than the current specification.\n\n\t\tSEE:\n\t\t\tSecond Edition : https://www.w3.org/TR/page-visibility/\n\t\t\tFirst Edition  : https://www.w3.org/TR/2013/REC-page-visibility-20130514/\n\n\t\tNOTE: Generally, all supported browsers change the visibility state when either switching tabs\n\t\twithin the browser or minimizing the browser window.  Exceptions are noted below:\n\t\t\t* IE 9 doesn't support either version of the Page Visibility API.\n\t\t\t* Opera 12 (Presto) doesn't change the visibility state when the browser is minimized.\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'hidden',          // boolean; historical in 2nd edition\n\t\t\t\t\tstateProperty  : 'visibilityState', // string, values: 'hidden', 'visible'; 1st edition had more values\n\t\t\t\t\tchangeEvent    : 'visibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink & WebKit.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'webkitHidden',\n\t\t\t\t\tstateProperty  : 'webkitVisibilityState',\n\t\t\t\t\tchangeEvent    : 'webkitvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'mozHidden',\n\t\t\t\t\tstateProperty  : 'mozVisibilityState',\n\t\t\t\t\tchangeEvent    : 'mozvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 10.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'msHidden',\n\t\t\t\t\tstateProperty  : 'msVisibilityState',\n\t\t\t\t\tchangeEvent    : 'msvisibilitychange'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.hiddenProperty in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getVisibility() {\n\t\treturn vendor && document[vendor.stateProperty] || 'visible';\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor);\n\t}\n\n\tfunction isHidden() {\n\t\t// return Boolean(vendor && document[vendor.stateProperty] === 'hidden');\n\t\treturn Boolean(vendor && document[vendor.hiddenProperty]); // NOTE: Historical, but probably better for 1st edition.\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Functions.\n\t\tvendor    : { get : getVendor },\n\t\tstate     : { get : getVisibility },\n\t\tisEnabled : { value : isEnabled },\n\t\tisHidden  : { value : isHidden },\n\n\t\t// Properties.\n\t\thiddenProperty : { value : vendor && vendor.hiddenProperty },\n\t\tstateProperty  : { value : vendor && vendor.stateProperty },\n\t\tchangeEvent    : { value : vendor && vendor.changeEvent }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/fullscreen.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Fullscreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tSEE:\n\t\t\thttps://fullscreen.spec.whatwg.org\n\t\t\thttps://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'fullscreenEnabled',\n\t\t\t\t\telement     : 'fullscreenElement',\n\t\t\t\t\trequestFn   : 'requestFullscreen',\n\t\t\t\t\texitFn      : 'exitFullscreen',\n\t\t\t\t\tchangeEvent : 'fullscreenchange', // prop: onfullscreenchange\n\t\t\t\t\terrorEvent  : 'fullscreenerror'   // prop: onfullscreenerror\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink, WebKit, & Edge.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'webkitFullscreenEnabled',\n\t\t\t\t\telement     : 'webkitFullscreenElement',\n\t\t\t\t\trequestFn   : 'webkitRequestFullscreen',\n\t\t\t\t\texitFn      : 'webkitExitFullscreen',\n\t\t\t\t\tchangeEvent : 'webkitfullscreenchange',\n\t\t\t\t\terrorEvent  : 'webkitfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'mozFullScreenEnabled',\n\t\t\t\t\telement     : 'mozFullScreenElement',\n\t\t\t\t\trequestFn   : 'mozRequestFullScreen',\n\t\t\t\t\texitFn      : 'mozCancelFullScreen',\n\t\t\t\t\tchangeEvent : 'mozfullscreenchange',\n\t\t\t\t\terrorEvent  : 'mozfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 11.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'msFullscreenEnabled',\n\t\t\t\t\telement     : 'msFullscreenElement',\n\t\t\t\t\trequestFn   : 'msRequestFullscreen',\n\t\t\t\t\texitFn      : 'msExitFullscreen',\n\t\t\t\t\tchangeEvent : 'MSFullscreenChange',\n\t\t\t\t\terrorEvent  : 'MSFullscreenError'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.isEnabled in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************/\n\n\t// Return whether the request and exit fullscreen methods return a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _returnsPromise = (function () {\n\t\t// Cache of whether the request and exit methods return a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _returnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (vendor) {\n\t\t\t\ttry {\n\t\t\t\t\tconst value = document.exitFullscreen();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since we shouldn't be in fullscreen yet,\n\t\t\t\t\t// and we don't actually care about the error, since we just want the return\n\t\t\t\t\t// value, so we consign it to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _returnsPromise;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _selectElement(requestedEl) {\n\t\tlet selectedEl = requestedEl || document.documentElement;\n\n\t\t// Document element scrolling workaround for older browsers.\n\t\tif (\n\t\t\t   selectedEl === document.documentElement\n\t\t\t&& (\n\t\t\t\t   vendor.requestFn === 'msRequestFullscreen'   // IE 11\n\t\t\t\t|| Browser.isOpera && Browser.operaVersion < 15 // Opera 12 (Presto)\n\t\t\t)\n\t\t) {\n\t\t\tselectedEl = document.body;\n\t\t}\n\n\t\treturn selectedEl;\n\t}\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getElement() {\n\t\treturn (vendor || null) && document[vendor.element];\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor && document[vendor.isEnabled]);\n\t}\n\n\tfunction isFullscreen() {\n\t\treturn Boolean(vendor && document[vendor.element]);\n\t}\n\n\tfunction requestFullscreen(options, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (typeof element[vendor.requestFn] !== 'function') {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\t\tif (isFullscreen()) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn element[vendor.requestFn](options);\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_requestFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(element)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen request error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\telement[vendor.requestFn](options);\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction exitFullscreen() {\n\t\tif (!vendor || typeof document[vendor.exitFn] !== 'function') {\n\t\t\treturn Promise.reject(new TypeError('fullscreen not supported'));\n\t\t}\n\t\tif (!isFullscreen()) {\n\t\t\treturn Promise.reject(new TypeError('fullscreen mode not active'));\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn document[vendor.exitFn]();\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_exitFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen exit error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tdocument[vendor.exitFn]();\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction toggleFullscreen(options, requestedEl) {\n\t\treturn isFullscreen() ? exitFullscreen() : requestFullscreen(options, requestedEl);\n\t}\n\n\tfunction onChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.changeEvent, handlerFn);\n\t}\n\n\tfunction offChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.changeEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.changeEvent);\n\t\t}\n\t}\n\n\tfunction onError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.errorEvent, handlerFn);\n\t}\n\n\tfunction offError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.errorEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.errorEvent);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tvendor       : { get : getVendor },\n\t\telement      : { get : getElement },\n\t\tisEnabled    : { value : isEnabled },\n\t\tisFullscreen : { value : isFullscreen },\n\t\trequest      : { value : requestFullscreen },\n\t\texit         : { value : exitFullscreen },\n\t\ttoggle       : { value : toggleFullscreen },\n\t\tonChange     : { value : onChange },\n\t\toffChange    : { value : offChange },\n\t\tonError      : { value : onError },\n\t\toffError     : { value : offError }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/helpers.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, State, Story, Util, Wikifier */\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tclone,\n\tconvertBreaks,\n\tsafeActiveElement,\n\tsetDisplayTitle,\n\tsetPageElement,\n\tthrowError,\n\ttoStringOrDefault\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _getTextContent(source) {\n\t\tconst copy = source.cloneNode(true);\n\t\tconst frag = document.createDocumentFragment();\n\t\tlet node;\n\n\t\twhile ((node = copy.firstChild) !== null) {\n\t\t\t// Insert spaces before various elements.\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tswitch (node.nodeName.toUpperCase()) {\n\t\t\t\tcase 'BR':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'P':\n\t\t\t\t\tfrag.appendChild(document.createTextNode(' '));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfrag.appendChild(node);\n\t\t}\n\n\t\treturn frag.textContent;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a deep copy of the given object.\n\n\t\tNOTE:\n\t\t\t1. `clone()` does not clone functions, however, since function definitions\n\t\t\t   are immutable, the only issues are with expando properties and scope.\n\t\t\t   The former really should not be done.  The latter is problematic either\n\t\t\t   way—damned if you do, damned if you don't.\n\t\t\t2. `clone()` does not maintain referential relationships—e.g. multiple\n\t\t\t   references to the same object will, post-cloning, refer to different\n\t\t\t   equivalent objects; i.e. each reference will receive its own clone\n\t\t\t   of the original object.\n\t*/\n\tfunction clone(orig) {\n\t\t/*\n\t\t\tImmediately return the primitives and functions.\n\t\t*/\n\t\tif (typeof orig !== 'object' || orig === null) {\n\t\t\treturn orig;\n\t\t}\n\n\t\t/*\n\t\t\tUnbox instances of the primitive exemplar objects.\n\t\t*/\n\t\tif (orig instanceof String) {\n\t\t\treturn String(orig);\n\t\t}\n\t\tif (orig instanceof Number) {\n\t\t\treturn Number(orig);\n\t\t}\n\t\tif (orig instanceof Boolean) {\n\t\t\treturn Boolean(orig);\n\t\t}\n\n\t\t/*\n\t\t\tHonor native clone methods.\n\t\t*/\n\t\tif (typeof orig.clone === 'function') {\n\t\t\treturn orig.clone(true);\n\t\t}\n\t\tif (orig.nodeType && typeof orig.cloneNode === 'function') {\n\t\t\treturn orig.cloneNode(true);\n\t\t}\n\n\t\t/*\n\t\t\tCreate a copy of the original object.\n\n\t\t\tNOTE: Each non-generic object that we wish to support must be\n\t\t\texplicitly handled below.\n\t\t*/\n\t\tlet copy;\n\n\t\t// Handle instances of the core supported object types.\n\t\tif (orig instanceof Array) {\n\t\t\tcopy = new Array(orig.length);\n\t\t}\n\t\telse if (orig instanceof Date) {\n\t\t\tcopy = new Date(orig.getTime());\n\t\t}\n\t\telse if (orig instanceof Map) {\n\t\t\tcopy = new Map();\n\t\t\torig.forEach((val, key) => copy.set(key, clone(val)));\n\t\t}\n\t\telse if (orig instanceof RegExp) {\n\t\t\tcopy = new RegExp(orig);\n\t\t}\n\t\telse if (orig instanceof Set) {\n\t\t\tcopy = new Set();\n\t\t\torig.forEach(val => copy.add(clone(val)));\n\t\t}\n\n\t\t// Handle instances of unknown or generic objects.\n\t\telse {\n\t\t\t// We try to ensure that the returned copy has the same prototype as\n\t\t\t// the original, but this will probably produce less than satisfactory\n\t\t\t// results on non-generics.\n\t\t\tcopy = Object.create(Object.getPrototypeOf(orig));\n\t\t}\n\n\t\t/*\n\t\t\tDuplicate the original object's own enumerable properties, which will\n\t\t\tinclude expando properties on non-generic objects.\n\n\t\t\tNOTE: This preserves neither symbol properties nor ES5 property attributes.\n\t\t\tNeither does the delta coding or serialization code, however, so it's not\n\t\t\treally an issue at the moment.\n\t\t*/\n\t\tObject.keys(orig).forEach(name => copy[name] = clone(orig[name]));\n\n\t\treturn copy;\n\t}\n\n\t/*\n\t\tConverts <br> elements to <p> elements within the given node tree.\n\t*/\n\tfunction convertBreaks(source) {\n\t\tconst output = document.createDocumentFragment();\n\t\tlet para = document.createElement('p');\n\t\tlet node;\n\n\t\twhile ((node = source.firstChild) !== null) {\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase 'BR':\n\t\t\t\t\tif (\n\t\t\t\t\t\t   node.nextSibling !== null\n\t\t\t\t\t\t&& node.nextSibling.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t\t&& node.nextSibling.nodeName.toUpperCase() === 'BR'\n\t\t\t\t\t) {\n\t\t\t\t\t\tsource.removeChild(node.nextSibling);\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!para.hasChildNodes()) {\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'ADDRESS':\n\t\t\t\tcase 'ARTICLE':\n\t\t\t\tcase 'ASIDE':\n\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\tcase 'CENTER':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'DL':\n\t\t\t\tcase 'FIGURE':\n\t\t\t\tcase 'FOOTER':\n\t\t\t\tcase 'FORM':\n\t\t\t\tcase 'H1':\n\t\t\t\tcase 'H2':\n\t\t\t\tcase 'H3':\n\t\t\t\tcase 'H4':\n\t\t\t\tcase 'H5':\n\t\t\t\tcase 'H6':\n\t\t\t\tcase 'HEADER':\n\t\t\t\tcase 'HR':\n\t\t\t\tcase 'MAIN':\n\t\t\t\tcase 'NAV':\n\t\t\t\tcase 'OL':\n\t\t\t\tcase 'P':\n\t\t\t\tcase 'PRE':\n\t\t\t\tcase 'SECTION':\n\t\t\t\tcase 'TABLE':\n\t\t\t\tcase 'UL':\n\t\t\t\t\tif (para.hasChildNodes()) {\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.appendChild(node);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpara.appendChild(node);\n\t\t}\n\n\t\tif (para.hasChildNodes()) {\n\t\t\toutput.appendChild(para);\n\t\t}\n\n\t\tsource.appendChild(output);\n\t}\n\n\t/*\n\t\tReturns `document.activeElement` or `null`.\n\t*/\n\tfunction safeActiveElement() {\n\t\t/*\n\t\t\tIE9 contains a bug where trying to access the active element of an iframe's\n\t\t\tparent document (i.e. `window.parent.document.activeElement`) will throw an\n\t\t\texception, so we must allow for an exception to be thrown.\n\n\t\t\tWe could simply return `undefined` here, but since the API's default behavior\n\t\t\tshould be to return `document.body` or `null` when there is no selection, we\n\t\t\tchoose to return `null` in all non-element cases (i.e. whether it returns\n\t\t\t`null` or throws an exception).  Just a bit of normalization.\n\t\t*/\n\t\ttry {\n\t\t\treturn document.activeElement || null;\n\t\t}\n\t\tcatch (ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/*\n\t\tSets the display title.\n\t*/\n\tfunction setDisplayTitle(title) {\n\t\tif (typeof title !== 'string') {\n\t\t\tthrow new TypeError(`story display title must be a string (received: ${Util.getType(title)})`);\n\t\t}\n\n\t\tconst render = document.createDocumentFragment();\n\t\tnew Wikifier(render, title);\n\n\t\tconst text = _getTextContent(render).trim();\n\n\t\t// if (text === '') {\n\t\t// \tthrow new Error('story display title must not render to an empty string or consist solely of whitespace');\n\t\t// }\n\n\t\tdocument.title = Config.passages.displayTitles && State.passage !== '' && State.passage !== Config.passages.start\n\t\t\t? `${State.passage} | ${text}`\n\t\t\t: text;\n\n\t\tconst storyTitle = document.getElementById('story-title');\n\n\t\tif (storyTitle !== null) {\n\t\t\tjQuery(storyTitle).empty().append(render);\n\t\t}\n\t}\n\n\t/*\n\t\tWikifies a passage into a DOM element corresponding to the passed ID and returns the element.\n\t*/\n\tfunction setPageElement(idOrElement, titles, defaultText) {\n\t\tconst el = typeof idOrElement === 'object'\n\t\t\t? idOrElement\n\t\t\t: document.getElementById(idOrElement);\n\n\t\tif (el == null) { // lazy equality for null\n\t\t\treturn null;\n\t\t}\n\n\t\tconst ids = Array.isArray(titles) ? titles : [titles];\n\n\t\tjQuery(el).empty();\n\n\t\tfor (let i = 0, iend = ids.length; i < iend; ++i) {\n\t\t\tif (Story.has(ids[i])) {\n\t\t\t\tnew Wikifier(el, Story.get(ids[i]).processText().trim());\n\t\t\t\treturn el;\n\t\t\t}\n\t\t}\n\n\t\tif (defaultText != null) { // lazy equality for null\n\t\t\tconst text = String(defaultText).trim();\n\n\t\t\tif (text !== '') {\n\t\t\t\tnew Wikifier(el, text);\n\t\t\t}\n\t\t}\n\n\t\treturn el;\n\t}\n\n\t/*\n\t\tAppends an error view to the passed DOM element.\n\t*/\n\tfunction throwError(place, message, source, stack) {\n\t\tconst $wrapper = jQuery(document.createElement('div'));\n\t\tconst $toggle  = jQuery(document.createElement('button'));\n\t\tconst $source  = jQuery(document.createElement('pre'));\n\t\tconst mesg     = `${L10n.get('errorTitle')}: ${message || 'unknown error'} ${Config.saves.version}`;\n\n\t\t$toggle\n\t\t\t.addClass('error-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('errorToggle')\n\t\t\t}, () => {\n\t\t\t\tif ($toggle.hasClass('enabled')) {\n\t\t\t\t\t$toggle.removeClass('enabled');\n\t\t\t\t\t$source.attr({\n\t\t\t\t\t\t'aria-hidden' : true,\n\t\t\t\t\t\thidden        : 'hidden'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$toggle.addClass('enabled');\n\t\t\t\t\t$source.removeAttr('aria-hidden hidden');\n\t\t\t\t}\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('span'))\n\t\t\t.addClass('error')\n\t\t\t.text(mesg)\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('code'))\n\t\t\t.text(source)\n\t\t\t.appendTo($source);\n\t\t$source\n\t\t\t.addClass('error-source')\n\t\t\t.attr({\n\t\t\t\t'aria-hidden' : true,\n\t\t\t\thidden        : 'hidden'\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tif (stack) {\n\t\t\tconst lines = stack.split('\\n');\n\t\t\tfor (const ll of lines) {\n\t\t\t\tconst div = document.createElement('div');\n\t\t\t\tdiv.append(ll.replace(/file:.*\\//, '<path>/'));\n\t\t\t\t$source.append(div);\n\t\t\t}\n\t\t}\n\t\t$wrapper\n\t\t\t.addClass('error-view')\n\t\t\t.appendTo(place);\n\n\t\tconsole.warn(`${mesg}\\n\\t${source.replace(/\\n/g, '\\n\\t')}`);\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the simple string representation of the passed value or, if there is none,\n\t\tthe passed default value.\n\t*/\n\tfunction toStringOrDefault(value, defValue) {\n\t\tconst tSOD = toStringOrDefault;\n\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\t// TODO: Perhaps NaN should be printed instead?\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\tif (value === null) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\telse if (Array.isArray(value)) {\n\t\t\t\treturn value.map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Set) {\n\t\t\t\treturn [...value].map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Map) {\n\t\t\t\tconst result = [...value].map(([key, val]) => `${tSOD(key, defValue)} \\u2192 ${tSOD(val, defValue)}`);\n\t\t\t\treturn `{\\u202F${result.join(', ')}\\u202F}`;\n\t\t\t}\n\t\t\telse if (value instanceof Date) {\n\t\t\t\treturn value.toLocaleString();\n\t\t\t}\n\t\t\telse if (typeof value.toString === 'function') {\n\t\t\t\treturn value.toString();\n\t\t\t}\n\t\t\treturn Object.prototype.toString.call(value);\n\n\t\tcase 'function':\n\t\tcase 'undefined':\n\t\t\treturn defValue;\n\t\t}\n\n\t\treturn String(value);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tclone             : { value : clone },\n\t\tconvertBreaks     : { value : convertBreaks },\n\t\tsafeActiveElement : { value : safeActiveElement },\n\t\tsetDisplayTitle   : { value : setDisplayTitle },\n\t\tsetPageElement    : { value : setPageElement },\n\t\tthrowError        : { value : throwError },\n\t\ttoStringOrDefault : { value : toStringOrDefault }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/jquery-plugins.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Wikifier, errorPrologRegExp, safeActiveElement */\n\n/*\n\tWAI-ARIA methods plugin.\n\n\t`<jQuery>.ariaClick([options,] handler)`\n\t    Makes the target element(s) WAI-ARIA compatible clickables.\n\n\t`<jQuery>.ariaDisabled(state)`\n\t    Changes the disabled state of the target WAI-ARIA-compatible clickable element(s).\n\n\t`<jQuery>.ariaIsDisabled()`\n\t    Checks the disabled status of the target WAI-ARIA-compatible clickable element(s).\n*/\n(() => {\n\t'use strict';\n\n\t/*\n\t\tEvent handler & utility functions.\n\n\t\tNOTE: Do not replace the anonymous functions herein with arrow functions.\n\t*/\n\tfunction onKeypressFn(ev) {\n\t\t// 13 is Enter/Return, 32 is Space.\n\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\tev.preventDefault();\n\n\t\t\t// To allow delegation, attempt to trigger the event on `document.activeElement`,\n\t\t\t// if possible, elsewise on `this`.\n\t\t\tjQuery(safeActiveElement() || this).trigger('click');\n\t\t}\n\t}\n\n\tfunction onClickFnWrapper(fn) {\n\t\treturn function () {\n\t\t\tconst $this = jQuery(this);\n\n\t\t\tconst dataPassage = $this.attr('data-passage');\n\t\t\tconst initialDataPassage = window && window.SugarCube && window.SugarCube.State && window.SugarCube.State.passage;\n\t\t\tconst savedYOffset = window.pageYOffset;\n\n\t\t\t// Toggle \"aria-pressed\" status, if the attribute exists.\n\t\t\tif ($this.is('[aria-pressed]')) {\n\t\t\t\t$this.attr('aria-pressed', $this.attr('aria-pressed') === 'true' ? 'false' : 'true');\n\t\t\t}\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\n\t\t\tconst doJump = function(){ window.scrollTo(0, savedYOffset); }\n\t\t\tif ( dataPassage && (window.lastDataPassageLink === dataPassage || initialDataPassage === dataPassage))\n\t\t\t\tdoJump();\n\t\t\twindow.lastDataPassageLink = dataPassage;\n\t\t};\n\t}\n\n\tfunction oneClickFnWrapper(fn) {\n\t\treturn onClickFnWrapper(function () {\n\t\t\t// Remove both event handlers (keypress & click) and the other components.\n\t\t\tjQuery(this)\n\t\t\t\t.off('.aria-clickable')\n\t\t\t\t.removeAttr('tabindex aria-controls aria-pressed')\n\t\t\t\t.not('a,button')\n\t\t\t\t.removeAttr('role')\n\t\t\t\t.end()\n\t\t\t\t.filter('button')\n\t\t\t\t.prop('disabled', true);\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\t\t});\n\t}\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaClick()` method.\n\t\t*/\n\t\tariaClick(options, handler) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tlet opts = options;\n\t\t\tlet fn   = handler;\n\n\t\t\tif (fn == null) { // lazy equality for null\n\t\t\t\tfn   = opts;\n\t\t\t\topts = undefined;\n\t\t\t}\n\n\t\t\topts = jQuery.extend({\n\t\t\t\tnamespace : undefined,\n\t\t\t\tone       : false,\n\t\t\t\tselector  : undefined,\n\t\t\t\tdata      : undefined,\n\t\t\t\tcontrols  : undefined,\n\t\t\t\tpressed   : undefined,\n\t\t\t\tlabel     : undefined\n\t\t\t}, opts);\n\n\t\t\tif (typeof opts.namespace !== 'string') {\n\t\t\t\topts.namespace = '';\n\t\t\t}\n\t\t\telse if (opts.namespace[0] !== '.') {\n\t\t\t\topts.namespace = `.${opts.namespace}`;\n\t\t\t}\n\n\t\t\tif (typeof opts.pressed === 'boolean') {\n\t\t\t\topts.pressed = opts.pressed ? 'true' : 'false';\n\t\t\t}\n\n\t\t\t// Set `type` to `button` to suppress \"submit\" semantics, for <button> elements.\n\t\t\tthis.filter('button').prop('type', 'button');\n\n\t\t\t// Set `role` to `button`, for non-<a>/-<button> elements.\n\t\t\tthis.not('a,button').attr('role', 'button');\n\n\t\t\t// Set `tabindex` to `0` to make them focusable (unnecessary on <button> elements, but it doesn't hurt).\n\t\t\tthis.attr('tabindex', 0);\n\n\t\t\t// Set `aria-controls`.\n\t\t\tif (opts.controls != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-controls', opts.controls);\n\t\t\t}\n\n\t\t\t// Set `aria-pressed`.\n\t\t\tif (opts.pressed != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-pressed', opts.pressed);\n\t\t\t}\n\n\t\t\t// Set `aria-label` and `title`.\n\t\t\tif (opts.label != null) { // lazy equality for null\n\t\t\t\tthis.attr({\n\t\t\t\t\t'aria-label' : opts.label,\n\t\t\t\t\ttitle        : opts.label\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Set the keypress handlers, for non-<button> elements.\n\t\t\t// NOTE: For the single-use case, the click handler will also remove this handler.\n\t\t\tthis.not('button').on(\n\t\t\t\t`keypress.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\tonKeypressFn\n\t\t\t);\n\n\t\t\t// Set the click handlers.\n\t\t\t// NOTE: To ensure both handlers are properly removed, `one()` must not be used here.\n\t\t\tthis.on(\n\t\t\t\t`click.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\topts.data,\n\t\t\t\topts.one ? oneClickFnWrapper(fn) : onClickFnWrapper(fn)\n\t\t\t);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaDisabled()` method.\n\t\t*/\n\t\tariaDisabled(disable) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE: We use `<jQuery>.each()` callbacks to invoke the `<Element>.setAttribute()`\n\t\t\t\tmethods in the following because the `<jQuery>.attr()` method does not allow you\n\t\t\t\tto set a content attribute without a value, which is recommended for boolean\n\t\t\t\tcontent attributes by the HTML specification.\n\t\t\t*/\n\n\t\t\tconst $nonDisableable = this.not('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\t\t\tconst $disableable    = this.filter('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\n\t\t\tif (disable) {\n\t\t\t\t// Add boolean content attribute `disabled` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.setAttribute('disabled', '');\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `true` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = true;\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Remove content attributes `disabled` and `aria-disabled`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.removeAttribute('disabled');\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `false` and remove content attribute `aria-disabled`,\n\t\t\t\t// for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = false;\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaIsDisabled()` method.\n\t\t*/\n\t\tariaIsDisabled() {\n\t\t\t// Check content attribute `disabled`.\n\t\t\t//\n\t\t\t// NOTE: We simply check the `disabled` content attribute for all elements\n\t\t\t// since we have to check it for non-disableable elements and it may also\n\t\t\t// be used for disableable elements since their `disabled` IDL attribute\n\t\t\t// is required to reflect the status of their `disabled` content attribute,\n\t\t\t// and vice versa, by the HTML specification.\n\t\t\t// return this.toArray().some(el => el.hasAttribute('disabled'));\n\t\t\treturn this.is('[disabled]');\n\t\t}\n\t});\n})();\n\n/*\n\tWikifier methods plugin.\n\n\t`jQuery.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s), as directed by the given options.\n\n\t`jQuery.wiki(sources…)`\n\t    Wikifies the given content source(s).\n\n\t`<jQuery>.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s), as directed by the given options.\n\n\t`<jQuery>.wiki(sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s).\n*/\n(() => {\n\t'use strict';\n\n\tjQuery.extend({\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out, if there are no content sources.\n\t\t\tif (sources.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\tthis.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out if there are no target element(s) or content sources.\n\t\t\tif (this.length === 0 || sources.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Append the fragment to the target element(s).\n\t\t\tthis.append(frag);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\treturn this.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/util.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, Scripting */\n\nvar Util = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tType Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value yielded by `typeof` (for primitives), the `@@toStringTag`\n\t\tinternal property (for objects), and `'null'` for `null`.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot for objects.\n\t*/\n\tfunction utilGetType(obj) {\n\t\tif (obj === null) { return 'null'; }\n\n\t\tconst baseType = typeof obj;\n\t\treturn baseType === 'object'\n\t\t\t? Object.prototype.toString.call(obj).slice(8, -1)\n\t\t\t: baseType;\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a boolean or one of the strings \"true\"\n\t\tor \"false\".\n\t*/\n\tfunction utilIsBoolean(obj) {\n\t\treturn typeof obj === 'boolean' || typeof obj === 'string' && (obj === 'true' || obj === 'false');\n\t}\n\n\t/*\n\t\tReturns whether the passed value is iterable.\n\t*/\n\tfunction utilIsIterable(obj) {\n\t\treturn obj != null && typeof obj[Symbol.iterator] === 'function'; // lazy equality for null\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a finite number or a numeric string which\n\t\tyields a finite number when parsed.\n\t*/\n\tfunction utilIsNumeric(obj) {\n\t\tlet num;\n\n\t\tswitch (typeof obj) {\n\t\tcase 'number':\n\t\t\tnum = obj;\n\t\t\tbreak;\n\n\t\tcase 'string':\n\t\t\tnum = Number(obj);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !Number.isNaN(num) && Number.isFinite(num);\n\t}\n\n\t/*\n\t\tReturns whether the passed values pass a SameValueZero comparison.\n\n\t\tSEE: http://ecma-international.org/ecma-262/8.0/#sec-samevaluezero\n\t*/\n\tfunction utilSameValueZero(a, b) {\n\t\t/*\n\t\t\tNOTE: This comparison could also be implemented thus:\n\n\t\t\t\t```\n\t\t\t\ta === b ||\n\t\t\t\ttypeof a === 'number' && typeof b === 'number' &&\n\t\t\t\tNumber.isNaN(a) && Number.isNaN(b)\n\t\t\t\t```\n\n\t\t\tThat's needlessly verbose, however, as `NaN` is the only value in\n\t\t\tthe language which is not reflexive.\n\t\t*/\n\t\treturn a === b || a !== a && b !== b;\n\t}\n\n\t/*\n\t\tReturns a pseudo-enumeration created from the given Array, Map, Set, or generic object.\n\t*/\n\tfunction utilToEnum(obj) {\n\t\tconst pEnum = Object.create(null);\n\n\t\tif (obj instanceof Array) {\n\t\t\tobj.forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Set) {\n\t\t\t// NOTE: Use `<Array>.forEach()` here rather than `<Set>.forEach()`\n\t\t\t// as the latter does not provide the indices we require.\n\t\t\tArray.from(obj).forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Map) {\n\t\t\tobj.forEach((val, key) => pEnum[String(key)] = val);\n\t\t}\n\t\telse if (\n\t\t\t   typeof obj === 'object'\n\t\t\t&& obj !== null\n\t\t\t&& Object.getPrototypeOf(obj) === Object.prototype\n\t\t) {\n\t\t\tObject.assign(pEnum, obj);\n\t\t}\n\t\telse {\n\t\t\tthrow new TypeError('Util.toEnum obj parameter must be an Array, Map, Set, or generic object');\n\t\t}\n\n\t\treturn Object.freeze(pEnum);\n\t}\n\n\t/*\n\t\tReturns the value of the `@@toStringTag` property of the given object.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot.\n\t*/\n\tfunction utilToStringTag(obj) {\n\t\treturn Object.prototype.toString.call(obj).slice(8, -1);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tString Encoding Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a trimmed and encoded slug of the passed string that should be safe\n\t\tfor use as a DOM ID or class name.\n\n\t\tNOTE: The range of illegal characters consists of: C0 controls, space, exclamation,\n\t\tdouble quote, number, dollar, percent, ampersand, single quote, left paren, right\n\t\tparen, asterisk, plus, comma, hyphen, period, forward slash, colon, semi-colon,\n\t\tless-than, equals, greater-than, question, at, left bracket, backslash, right\n\t\tbracket, caret, backquote/grave, left brace, pipe/vertical-bar, right brace, tilde,\n\t\tdelete, C1 controls.\n\t*/\n\tconst _illegalSlugCharsRe = /[\\x00-\\x20!-/:-@[-^`{-\\x9f]+/g; // eslint-disable-line no-control-regex\n\t/* legacy */\n\tconst _isInvalidSlugRe = /^-*$/; // Matches the empty string or one comprised solely of hyphens.\n\t/* /legacy */\n\n\tfunction utilSlugify(str) {\n\t\tconst base = String(str).trim();\n\n\t\t/* legacy */\n\t\tconst _legacy = base\n\t\t\t.replace(/[^\\w\\s\\u2013\\u2014-]+/g, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-')\n\t\t\t.toLocaleLowerCase();\n\n\t\tif (!_isInvalidSlugRe.test(_legacy)) {\n\t\t\treturn _legacy;\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn base\n\t\t\t.replace(_illegalSlugCharsRe, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-');\n\n\t\t// For v3.\n\t\t// return base.replace(_illegalSlugCharsRe, '-');\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Escapes the five primary HTML special characters, the backquote,\n\t\tand SugarCube markup metacharacters.\n\t*/\n\tconst _markupCharsRe    = /[!\"#$&'*\\-/<=>?@[\\\\\\]^_`{|}~]/g;\n\tconst _hasMarkupCharsRe = new RegExp(_markupCharsRe.source); // to drop the global flag\n\tconst _markupCharsMap   = utilToEnum({\n\t\t/* eslint-disable quote-props */\n\t\t'!'  : '&#33;',\n\t\t'\"'  : '&quot;',\n\t\t'#'  : '&#35;',\n\t\t'$'  : '&#36;',\n\t\t'&'  : '&amp;',\n\t\t\"'\"  : '&#39;',\n\t\t'*'  : '&#42;',\n\t\t'-'  : '&#45;',\n\t\t'/'  : '&#47;',\n\t\t'<'  : '&lt;',\n\t\t'='  : '&#61;',\n\t\t'>'  : '&gt;',\n\t\t'?'  : '&#63;',\n\t\t'@'  : '&#64;',\n\t\t'['  : '&#91;',\n\t\t'\\\\' : '&#92;',\n\t\t']'  : '&#93;',\n\t\t'^'  : '&#94;',\n\t\t'_'  : '&#95;',\n\t\t'`'  : '&#96;',\n\t\t'{'  : '&#123;',\n\t\t'|'  : '&#124;',\n\t\t'}'  : '&#125;',\n\t\t'~'  : '&#126;'\n\t\t/* eslint-enable quote-props */\n\t});\n\n\tfunction utilEscapeMarkup(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasMarkupCharsRe.test(val)\n\t\t\t? val.replace(_markupCharsRe, ch => _markupCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Only escapes the five primary special characters and the backquote.\n\t*/\n\tconst _htmlCharsRe    = /[&<>\"'`]/g;\n\tconst _hasHtmlCharsRe = new RegExp(_htmlCharsRe.source); // to drop the global flag\n\tconst _htmlCharsMap   = utilToEnum({\n\t\t'&' : '&amp;',\n\t\t'<' : '&lt;',\n\t\t'>' : '&gt;',\n\t\t'\"' : '&quot;',\n\t\t\"'\" : '&#39;',\n\t\t'`' : '&#96;'\n\t});\n\n\tfunction utilEscape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasHtmlCharsRe.test(val)\n\t\t\t? val.replace(_htmlCharsRe, ch => _htmlCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns a decoded version of the passed entity encoded string.\n\n\t\tNOTE: The extended replacement set here, in contrast to `utilEscape()`,\n\t\tis required due to observed stupidity from various sources.\n\t*/\n\tconst _escapedHtmlRe    = /&(?:amp|#38|#x26|lt|#60|#x3c|gt|#62|#x3e|quot|#34|#x22|apos|#39|#x27|#96|#x60);/gi;\n\tconst _hasEscapedHtmlRe = new RegExp(_escapedHtmlRe.source, 'i'); // to drop the global flag\n\tconst _escapedHtmlMap   = utilToEnum({\n\t\t'&amp;'  : '&', // ampersand (HTML character entity, XML predefined entity)\n\t\t'&#38;'  : '&', // ampersand (decimal numeric character reference)\n\t\t'&#x26;' : '&', // ampersand (hexadecimal numeric character reference)\n\t\t'&lt;'   : '<', // less-than (HTML character entity, XML predefined entity)\n\t\t'&#60;'  : '<', // less-than (decimal numeric character reference)\n\t\t'&#x3c;' : '<', // less-than (hexadecimal numeric character reference)\n\t\t'&gt;'   : '>', // greater-than (HTML character entity, XML predefined entity)\n\t\t'&#62;'  : '>', // greater-than (decimal numeric character reference)\n\t\t'&#x3e;' : '>', // greater-than (hexadecimal numeric character reference)\n\t\t'&quot;' : '\"', // double quote (HTML character entity, XML predefined entity)\n\t\t'&#34;'  : '\"', // double quote (decimal numeric character reference)\n\t\t'&#x22;' : '\"', // double quote (hexadecimal numeric character reference)\n\t\t'&apos;' : \"'\", // apostrophe (XML predefined entity)\n\t\t'&#39;'  : \"'\", // apostrophe (decimal numeric character reference)\n\t\t'&#x27;' : \"'\", // apostrophe (hexadecimal numeric character reference)\n\t\t'&#96;'  : '`', // backquote (decimal numeric character reference)\n\t\t'&#x60;' : '`'  // backquote (hexadecimal numeric character reference)\n\t});\n\n\tfunction utilUnescape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasEscapedHtmlRe.test(val)\n\t\t\t? val.replace(_escapedHtmlRe, entity => _escapedHtmlMap[entity.toLowerCase()])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Returns the individual code units of invalid surrogate pairs as-is.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction utilCharAndPosAt(text, position) {\n\t\tconst str  = String(text);\n\t\tconst pos  = Math.trunc(position);\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\tconst retval = {\n\t\t\tchar  : str.charAt(pos),\n\t\t\tstart : pos,\n\t\t\tend   : pos\n\t\t};\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tretval.char = retval.char + str.charAt(nextPos);\n\t\t\tretval.end = nextPos;\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tretval.char = str.charAt(prevPos) + retval.char;\n\t\tretval.start = prevPos;\n\t\treturn retval;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTime Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of milliseconds elapsed since a reference epoch.\n\n\t\tNOTE: Use the Performance API, if available, elsewise use Date as a\n\t\tfailover.  The Performance API is preferred for its monotonic clock—\n\t\tmeaning, it's not subject to the vagaries of timezone changes and leap\n\t\tperiods, as is Date.\n\t*/\n\tconst _nowSource = Has.performance ? performance : Date;\n\n\tfunction utilNow() {\n\t\treturn _nowSource.now();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tConversion Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of miliseconds represented by the passed CSS time string.\n\t*/\n\tconst _cssTimeRe = /^([+-]?(?:\\d*\\.)?\\d+)([Mm]?[Ss])$/;\n\n\tfunction utilFromCssTime(cssTime) {\n\t\tconst match = _cssTimeRe.exec(String(cssTime));\n\n\t\tif (match === null) {\n\t\t\tthrow new SyntaxError(`invalid time value syntax: \"${cssTime}\"`);\n\t\t}\n\n\t\tlet msec = Number(match[1]);\n\n\t\tif (match[2].length === 1) {\n\t\t\tmsec *= 1000;\n\t\t}\n\n\t\tif (Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tthrow new RangeError(`invalid time value: \"${cssTime}\"`);\n\t\t}\n\n\t\treturn msec;\n\t}\n\n\t/*\n\t\tReturns the CSS time string represented by the passed number of milliseconds.\n\t*/\n\tfunction utilToCssTime(msec) {\n\t\tif (typeof msec !== 'number' || Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tlet what;\n\n\t\t\tswitch (typeof msec) {\n\t\t\tcase 'string':\n\t\t\t\twhat = `\"${msec}\"`;\n\t\t\t\tbreak;\n\n\t\t\tcase 'number':\n\t\t\t\twhat = String(msec);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\twhat = utilToStringTag(msec);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow new Error(`invalid milliseconds: ${what}`);\n\t\t}\n\n\t\treturn `${msec}ms`;\n\t}\n\n\t/*\n\t\tReturns the DOM property name represented by the passed CSS property name.\n\t*/\n\tfunction utilFromCssProperty(cssName) {\n\t\tif (!cssName.includes('-')) {\n\t\t\tswitch (cssName) {\n\t\t\tcase 'bgcolor': return 'backgroundColor';\n\t\t\tcase 'float':   return 'cssFloat';\n\t\t\tdefault:        return cssName;\n\t\t\t}\n\t\t}\n\n\t\t// Strip the leading hyphen from the `-ms-` vendor prefix, so it stays lowercased.\n\t\tconst normalized = cssName.slice(0, 4) === '-ms-' ? cssName.slice(1) : cssName;\n\n\t\treturn normalized\n\t\t\t.split('-')\n\t\t\t.map((part, i) => i === 0 ? part : part.toUpperFirst())\n\t\t\t.join('');\n\t}\n\n\t/*\n\t\tReturns an object containing the component properties parsed from the passed URL.\n\t*/\n\tfunction utilParseUrl(url) {\n\t\tconst el       = document.createElement('a');\n\t\tconst queryObj = Object.create(null);\n\n\t\t// Let the `<a>` element parse the URL.\n\t\tel.href = url;\n\n\t\t// Populate the `queryObj` object with the query string attributes.\n\t\tif (el.search) {\n\t\t\tel.search\n\t\t\t\t.replace(/^\\?/, '')\n\t\t\t\t.splitOrEmpty(/(?:&(?:amp;)?|;)/)\n\t\t\t\t.forEach(query => {\n\t\t\t\t\tconst [key, value] = query.split('=');\n\t\t\t\t\tqueryObj[key] = value;\n\t\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tCaveats by browser:\n\t\t\t\tEdge and Internet Explorer (≥8) do not support authentication\n\t\t\t\tinformation within a URL at all and will throw a security exception\n\t\t\t\ton *any* property access if it's included.\n\n\t\t\t\tInternet Explorer does not include the leading forward slash on\n\t\t\t\t`pathname` when required.\n\n\t\t\t\tOpera (Presto) strips the authentication information from `href`\n\t\t\t\tand does not supply `username` or `password`.\n\n\t\t\t\tSafari (ca. v5.1.x) does not supply `username` or `password` and\n\t\t\t\tpeforms URI decoding on `pathname`.\n\t\t*/\n\n\t\t// Patch for IE not including the leading slash on `pathname` when required.\n\t\tconst pathname = el.host && el.pathname[0] !== '/' ? `/${el.pathname}` : el.pathname;\n\n\t\treturn {\n\t\t\t// The full URL that was originally parsed.\n\t\t\thref : el.href,\n\n\t\t\t// The request protocol, lowercased.\n\t\t\tprotocol : el.protocol,\n\n\t\t\t// // The full authentication information.\n\t\t\t// auth : el.username || el.password // eslint-disable-line no-nested-ternary\n\t\t\t// \t? `${el.username}:${el.password}`\n\t\t\t// \t: typeof el.username === 'string' ? '' : undefined,\n\t\t\t//\n\t\t\t// // The username portion of the auth info.\n\t\t\t// username : el.username,\n\t\t\t//\n\t\t\t// // The password portion of the auth info.\n\t\t\t// password : el.password,\n\n\t\t\t// The full host information, including port number, lowercased.\n\t\t\thost : el.host,\n\n\t\t\t// The hostname portion of the host info, lowercased.\n\t\t\thostname : el.hostname,\n\n\t\t\t// The port number portion of the host info.\n\t\t\tport : el.port,\n\n\t\t\t// The full path information, including query info.\n\t\t\tpath : `${pathname}${el.search}`,\n\n\t\t\t// The pathname portion of the path info.\n\t\t\tpathname,\n\n\t\t\t// The query string portion of the path info, including the leading question mark.\n\t\t\tquery  : el.search,\n\t\t\tsearch : el.search,\n\n\t\t\t// The attributes portion of the query string, parsed into an object.\n\t\t\tqueries  : queryObj,\n\t\t\tsearches : queryObj,\n\n\t\t\t// The fragment string, including the leading hash/pound sign.\n\t\t\thash : el.hash\n\t\t};\n\t}\n\n\t/*\n\t\tReturns a new exception based on the given exception.\n\n\t\tNOTE: Mostly useful for making a standard JavaScript exception type copy\n\t\tof a host exception type—e.g. `DOMException` → `Error`.\n\t*/\n\tfunction utilNewExceptionFrom(original, exceptionType, override) {\n\t\tif (typeof original !== 'object' || original === null) {\n\t\t\tthrow new Error('Util.newExceptionFrom original parameter must be an object');\n\t\t}\n\t\tif (typeof exceptionType !== 'function') {\n\t\t\tthrow new Error('Util.newExceptionFrom exceptionType parameter must be an error type constructor');\n\t\t}\n\n\t\tconst ex = new exceptionType(original.message); // eslint-disable-line new-cap\n\n\t\tif (typeof original.name !== 'undefined') {\n\t\t\tex.name = original.name;\n\t\t}\n\t\tif (typeof original.code !== 'undefined') {\n\t\t\tex.code = original.code;\n\t\t}\n\t\tif (typeof original.columnNumber !== 'undefined') {\n\t\t\tex.columnNumber = original.columnNumber;\n\t\t}\n\t\tif (typeof original.description !== 'undefined') {\n\t\t\tex.description = original.description;\n\t\t}\n\t\tif (typeof original.fileName !== 'undefined') {\n\t\t\tex.fileName = original.fileName;\n\t\t}\n\t\tif (typeof original.lineNumber !== 'undefined') {\n\t\t\tex.lineNumber = original.lineNumber;\n\t\t}\n\t\tif (typeof original.number !== 'undefined') {\n\t\t\tex.number = original.number;\n\t\t}\n\t\tif (typeof original.stack !== 'undefined') {\n\t\t\tex.stack = original.stack;\n\t\t}\n\n\t\tconst overrideType = typeof override;\n\n\t\tif (overrideType !== 'undefined') {\n\t\t\tif (overrideType === 'object' && override !== null) {\n\t\t\t\tObject.assign(ex, override);\n\t\t\t}\n\t\t\telse if (overrideType === 'string') {\n\t\t\t\tex.message = override;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('Util.newExceptionFrom override parameter must be an object or string');\n\t\t\t}\n\t\t}\n\n\t\treturn ex;\n\t}\n\n\t/*\n\t\tReturns a sanitized version of the passed `KeyboardEvent.key` value from\n\t\tprevious incarnations of the specification that should better reflect the\n\t\tcurrent incarnation.\n\t*/\n\tconst utilScrubEventKey = (() => {\n\t\tlet separatorKey;\n\t\tlet decimalKey;\n\n\t\t// Attempt to determine the player's 'Separator' and 'Decimal' key values\n\t\t// based on their current locale.\n\t\tif (typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function') {\n\t\t\tconst match = new Intl.NumberFormat().format(111111.5).match(/(\\D*)\\d+(\\D*)/);\n\n\t\t\tif (match) {\n\t\t\t\tseparatorKey = match[1];\n\t\t\t\tdecimalKey   = match[2];\n\t\t\t}\n\t\t}\n\n\t\t// Failover to US-centric values, if using `Intl.NumberFormat` failed.\n\t\tif (!separatorKey && !decimalKey) {\n\t\t\tseparatorKey = ',';\n\t\t\tdecimalKey   = '.';\n\t\t}\n\n\t\t// Maps older `KeyboardEvent.key` values to more current/correct ones.\n\t\tfunction utilScrubEventKey(key) {\n\t\t\tswitch (key) {\n\t\t\t// case 'OS':                 return 'Meta'; // Unreliable.\n\t\t\tcase 'Scroll':             return 'ScrollLock';\n\t\t\tcase 'Spacebar':           return '\\x20';\n\t\t\tcase 'Left':               return 'ArrowLeft';\n\t\t\tcase 'Right':              return 'ArrowRight';\n\t\t\tcase 'Up':                 return 'ArrowUp';\n\t\t\tcase 'Down':               return 'ArrowDown';\n\t\t\tcase 'Del':                return 'Delete';\n\t\t\tcase 'Crsel':              return 'CrSel';\n\t\t\tcase 'Exsel':              return 'ExSel';\n\t\t\tcase 'Esc':                return 'Escape';\n\t\t\tcase 'Apps':               return 'ContextMenu';\n\t\t\tcase 'Nonconvert':         return 'NonConvert';\n\t\t\tcase 'MediaNextTrack':     return 'MediaTrackNext';\n\t\t\tcase 'MediaPreviousTrack': return 'MediaTrackPrevious';\n\t\t\tcase 'VolumeUp':           return 'AudioVolumeUp';\n\t\t\tcase 'VolumeDown':         return 'AudioVolumeDown';\n\t\t\tcase 'VolumeMute':         return 'AudioVolumeMute';\n\t\t\tcase 'Zoom':               return 'ZoomToggle';\n\t\t\tcase 'SelectMedia':        /* see below */\n\t\t\tcase 'MediaSelect':        return 'LaunchMediaPlayer';\n\t\t\tcase 'Add':                return '+';\n\t\t\tcase 'Divide':             return '/';\n\t\t\tcase 'Multiply':           return '*';\n\t\t\tcase 'Subtract':           return '-';\n\t\t\tcase 'Decimal':            return decimalKey;\n\t\t\tcase 'Separator':          return separatorKey;\n\t\t\t}\n\n\t\t\treturn key;\n\t\t}\n\n\t\treturn utilScrubEventKey;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tType Functions.\n\t\t*/\n\t\tgetType       : { value : utilGetType },\n\t\tisBoolean     : { value : utilIsBoolean },\n\t\tisIterable    : { value : utilIsIterable },\n\t\tisNumeric     : { value : utilIsNumeric },\n\t\tsameValueZero : { value : utilSameValueZero },\n\t\ttoEnum        : { value : utilToEnum },\n\t\ttoStringTag   : { value : utilToStringTag },\n\n\t\t/*\n\t\t\tString Encoding Functions.\n\t\t*/\n\t\tslugify      : { value : utilSlugify },\n\t\tescapeMarkup : { value : utilEscapeMarkup },\n\t\tescape       : { value : utilEscape },\n\t\tunescape     : { value : utilUnescape },\n\t\tcharAndPosAt : { value : utilCharAndPosAt },\n\n\t\t/*\n\t\t\tTime Functions.\n\t\t*/\n\t\tnow : { value : utilNow },\n\n\t\t/*\n\t\t\tConversion Functions.\n\t\t*/\n\t\tfromCssTime      : { value : utilFromCssTime },\n\t\ttoCssTime        : { value : utilToCssTime },\n\t\tfromCssProperty  : { value : utilFromCssProperty },\n\t\tparseUrl         : { value : utilParseUrl },\n\t\tnewExceptionFrom : { value : utilNewExceptionFrom },\n\t\tscrubEventKey    : { value : utilScrubEventKey },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\trandom         : { value : Math.random },\n\t\tentityEncode   : { value : utilEscape },\n\t\tentityDecode   : { value : utilUnescape },\n\t\tevalExpression : { value : (...args) => Scripting.evalJavaScript(...args) }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) }  // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/simplestore.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar SimpleStore = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// In-order list of database adapters.\n\tconst _adapters = [];\n\n\t// The initialized adapter.\n\tlet _initialized = null;\n\n\n\t/*******************************************************************************************************************\n\t\tSimpleStore Functions.\n\t*******************************************************************************************************************/\n\tfunction storeCreate(storageId, persistent) {\n\t\tif (_initialized) {\n\t\t\treturn _initialized.create(storageId, persistent);\n\t\t}\n\n\t\t// Return the first adapter which successfully initializes, elsewise throw an exception.\n\t\tfor (let i = 0; i < _adapters.length; ++i) {\n\t\t\tif (_adapters[i].init(storageId, persistent)) {\n\t\t\t\t_initialized = _adapters[i];\n\t\t\t\treturn _initialized.create(storageId, persistent);\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error('no valid storage adapters found');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tAdapters List.\n\n\t\t\tTODO: This should probably have a getter, rather than being exported directly.\n\t\t*/\n\t\tadapters : { value : _adapters },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tcreate : { value : storeCreate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/FCHost.Storage.js\n\n\tCopyright © 2013–2019 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_FCHostStorageAdapter Class.\n        Note that FCHost is only intended for a single document, so we ignore both prefixing and storageID\n\t*******************************************************************************************************************/\n\tclass _FCHostStorageAdapter {\n\t\tconstructor(persistent) {\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.FCHostPersistent;\n\t\t\t\tname   = 'FCHostPersistent';\n\t\t\t}\n\t\t\telse {\n\t\t\t    engine = window.FCHostSession;\n\t\t\t\tname   = 'FCHostSession';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n                \n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\treturn this._engine.keys();\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn this._engine.has(key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.get(key);\n\n\t\t\treturn value == null ? null : _FCHostStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.set(key, _FCHostStorageAdapter._serialize(value));\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.remove(key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tthis._engine.clear();\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(str);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// FCHost feature test.\n\t\tfunction hasFCHostStorage() {\n\t\t\ttry {\n\t\t\t    if (typeof window.FCHostPersistent !== 'undefined')\n\t\t\t        return true;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t_ok = hasFCHostStorage();\n\t\t\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _FCHostStorageAdapter(persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/webstorage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_WebStorageAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _WebStorageAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}.`;\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.localStorage;\n\t\t\t\tname   = 'localStorage';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tengine = window.sessionStorage;\n\t\t\t\tname   = 'sessionStorage';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tconst keys = [];\n\n\t\t\tfor (let i = 0; i < this._engine.length; ++i) {\n\t\t\t\tconst key = this._engine.key(i);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// // FIXME: This method should probably check for the key, rather than comparing its value.\n\t\t\t// return this._engine.getItem(this._prefix + key) != null; // lazy equality for null\n\n\t\t\treturn this._engine.hasOwnProperty(this._prefix + key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.getItem(this._prefix + key);\n\n\t\t\treturn value == null ? null : _WebStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value, compression = true) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis._engine.setItem(this._prefix + key,\n\t\t\t\t\t_WebStorageAdapter._serialize(value, this.persistent && compression));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t/*\n\t\t\t\t\tIf the exception is a quota exceeded error, massage it into something\n\t\t\t\t\ta bit nicer for the player.\n\n\t\t\t\t\tNOTE: Ideally, we could simply do something like checking `ex.code`, but\n\t\t\t\t\tit's a non-standard property and not supported in all browsers.  Thus,\n\t\t\t\t\twe have to resort to pattern matching the name and message—the latter being\n\t\t\t\t\trequired by Opera (Presto).  I hate the parties responsible for this snafu\n\t\t\t\t\tso much.\n\t\t\t\t*/\n\t\t\t\tif (/quota.?(?:exceeded|reached)/i.test(ex.name + ex.message)) {\n\t\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `${this.name} quota exceeded`);\n\t\t\t\t}\n\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.removeItem(this._prefix + key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// return this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj, compression) {\n\t\t\tif (compression) {\n\t\t\t\treturn LZString.compressToUTF16(JSON.stringify(obj));\n\t\t\t}\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse((!str || str[0] == \"{\") ? str : LZString.decompressFromUTF16(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// Web Storage feature test.\n\t\tfunction hasWebStorage(storeId) {\n\t\t\ttry {\n\t\t\t\tconst store = window[storeId];\n\t\t\t\tconst tid   = `_sc_${String(Date.now())}`;\n\t\t\t\tstore.setItem(tid, tid);\n\t\t\t\tconst result = store.getItem(tid) === tid;\n\t\t\t\tstore.removeItem(tid);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t\tJust to be safe, we feature test for both `localStorage` and `sessionStorage`,\n\t\t\tas you never know what browser implementation bugs you're going to run into.\n\t\t*/\n\t\t_ok = hasWebStorage('localStorage') && hasWebStorage('sessionStorage');\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _WebStorageAdapter(storageId, persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/cookie.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Expiry constants.\n\tconst _MAX_EXPIRY = 'Tue, 19 Jan 2038 03:14:07 GMT'; // (new Date((Math.pow(2, 31) - 1) * 1000)).toUTCString()\n\tconst _MIN_EXPIRY = 'Thu, 01 Jan 1970 00:00:00 GMT'; // (new Date(0)).toUTCString()\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_CookieAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _CookieAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}${persistent ? '!' : '*'}.`;\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : 'cookie'\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tif (document.cookie === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\t\t\tconst keys    = [];\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should omit such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\tif (value !== '') {\n\t\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn _CookieAdapter._getCookie(this._prefix + key) !== null;\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = _CookieAdapter._getCookie(this._prefix + key);\n\n\t\t\treturn value === null ? null : _CookieAdapter._deserialize(value);\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\t\t\t\t\t_CookieAdapter._serialize(value),\n\n\t\t\t\t\t// An undefined expiry denotes a session cookie.\n\t\t\t\t\tthis.persistent ? _MAX_EXPIRY : undefined\n\t\t\t\t);\n\n\t\t\t\tif (!this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during set');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\t/*\n\t\t\t\tAttempting to delete a cookie implies setting it, so we test for its existence\n\t\t\t\tbeforehand, to avoid creating it in the event that it does not already exist.\n\t\t\t*/\n\t\t\tif (typeof key !== 'string' || !key || !this.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\n\t\t\t\t\t// Use `undefined` as the value.\n\t\t\t\t\tundefined,\n\n\t\t\t\t\t// Use the epoch as the expiry.\n\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t);\n\n\t\t\t\tif (this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during delete');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _getCookie(prefixedKey) {\n\t\t\tif (!prefixedKey || document.cookie === '') {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (prefixedKey === key) {\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should yield `null` for such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\treturn value || null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic _setCookie(prefixedKey, value, expiry) {\n\t\t\tif (!prefixedKey) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet payload = `${encodeURIComponent(prefixedKey)}=`;\n\n\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\tpayload += encodeURIComponent(value);\n\t\t\t}\n\n\t\t\tif (expiry != null) { // lazy equality for null\n\t\t\t\tpayload += `; expires=${expiry}`;\n\t\t\t}\n\n\t\t\tpayload += '; path=/';\n\t\t\tdocument.cookie = payload;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn LZString.compressToBase64(JSON.stringify(obj));\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(LZString.decompressFromBase64(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit(\n\t\t// Only used for stores updates.\n\t\tstorageId\n\t) {\n\t\t// Cookie feature test.\n\t\ttry {\n\t\t\tconst tid = `_sc_${String(Date.now())}`;\n\n\t\t\t// We only test a session cookie as that should suffice.\n\t\t\t_CookieAdapter._setCookie(tid, _CookieAdapter._serialize(tid), undefined);\n\t\t\t_ok = _CookieAdapter._deserialize(_CookieAdapter._getCookie(tid)) === tid;\n\t\t\t_CookieAdapter._setCookie(tid, undefined, _MIN_EXPIRY);\n\t\t}\n\t\tcatch (ex) {\n\t\t\t_ok = false;\n\t\t}\n\n\t\t/* legacy */\n\t\t// Attempt to update the cookie stores, if necessary.  This should happen only during initialization.\n\t\tif (_ok) {\n\t\t\t_updateCookieStores(storageId);\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _CookieAdapter(storageId, persistent);\n\t}\n\n\t/* legacy */\n\t// Updates old non-segmented cookie stores into segmented stores.\n\tfunction _updateCookieStores(storageId) {\n\t\tif (document.cookie === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldPrefix     = `${storageId}.`;\n\t\tconst oldPrefixRe   = new RegExp(`^${RegExp.escape(oldPrefix)}`);\n\t\tconst persistPrefix = `${storageId}!.`;\n\t\tconst sessionPrefix = `${storageId}*.`;\n\t\tconst sessionTestRe = /\\.(?:state|rcWarn)$/;\n\t\tconst cookies       = document.cookie.split(/;\\s*/);\n\n\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\tif (oldPrefixRe.test(key)) {\n\t\t\t\t/*\n\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\tnot a serialized empty string, so we should skip processing such pairs.\n\t\t\t\t*/\n\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\tif (value !== '') {\n\t\t\t\t\tconst persist = !sessionTestRe.test(key);\n\n\t\t\t\t\t// Delete the old k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t\t);\n\n\t\t\t\t\t// Set the new k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey.replace(oldPrefixRe, () => persist ? persistPrefix : sessionPrefix),\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tpersist ? _MAX_EXPIRY : undefined\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t/* /legacy */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/debugview.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: Make this use jQuery throughout.\n*/\nvar DebugView = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDebugView Class.\n\t*******************************************************************************************************************/\n\tclass DebugView {\n\t\tconstructor(parent, type, name, title) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : parent\n\t\t\t\t},\n\n\t\t\t\tview : {\n\t\t\t\t\tvalue : document.createElement('span')\n\t\t\t\t},\n\n\t\t\t\tbreak : {\n\t\t\t\t\tvalue : document.createElement('wbr')\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up the wrapper (`<span>`) element.\n\t\t\tjQuery(this.view)\n\t\t\t\t.attr({\n\t\t\t\t\ttitle,\n\t\t\t\t\t'aria-label' : title,\n\t\t\t\t\t'data-type'  : type != null ? type : '', // lazy equality for null\n\t\t\t\t\t'data-name'  : name != null ? name : ''  // lazy equality for null\n\t\t\t\t})\n\t\t\t\t.addClass('debug');\n\n\t\t\t// Set up the word break (`<wbr>`) element.\n\t\t\tjQuery(this.break).addClass('debug hidden');\n\n\t\t\t// Add the wrapper (`<span>`) and word break (`<wbr>`) elements to the `parent` element.\n\t\t\tthis.parent.appendChild(this.view);\n\t\t\tthis.parent.appendChild(this.break);\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this.view;\n\t\t}\n\n\t\tget type() {\n\t\t\treturn this.view.getAttribute('data-type');\n\t\t}\n\t\tset type(type) {\n\t\t\tthis.view.setAttribute('data-type', type != null ? type : ''); // lazy equality for null\n\t\t}\n\n\t\tget name() {\n\t\t\treturn this.view.getAttribute('data-name');\n\t\t}\n\t\tset name(name) {\n\t\t\tthis.view.setAttribute('data-name', name != null ? name : ''); // lazy equality for null\n\t\t}\n\n\t\tget title() {\n\t\t\treturn this.view.title;\n\t\t}\n\t\tset title(title) {\n\t\t\tthis.view.title = title;\n\t\t}\n\n\t\tappend(el) {\n\t\t\tjQuery(this.view).append(el);\n\t\t\treturn this;\n\t\t}\n\n\t\tmodes(options) {\n\t\t\tif (options == null) { // lazy equality for null\n\t\t\t\tconst current = {};\n\n\t\t\t\tthis.view.className.splitOrEmpty(/\\s+/).forEach(name => {\n\t\t\t\t\tif (name !== 'debug') {\n\t\t\t\t\t\tcurrent[name] = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\telse if (typeof options === 'object') {\n\t\t\t\tObject.keys(options).forEach(function (name) {\n\t\t\t\t\tthis[options[name] ? 'addClass' : 'removeClass'](name);\n\t\t\t\t}, jQuery(this.view));\n\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tthrow new Error('DebugView.prototype.modes options parameter must be an object or null/undefined');\n\t\t}\n\n\t\tremove() {\n\t\t\tconst $view = jQuery(this.view);\n\n\t\t\tif (this.view.hasChildNodes()) {\n\t\t\t\t$view.contents().appendTo(this.parent);\n\t\t\t}\n\n\t\t\t$view.remove();\n\t\t\tjQuery(this.break).remove();\n\t\t}\n\n\t\tstatic isEnabled() {\n\t\t\treturn jQuery(document.documentElement).attr('data-debug-view') === 'enabled';\n\t\t}\n\n\t\tstatic enable() {\n\t\t\tjQuery(document.documentElement).attr('data-debug-view', 'enabled');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic disable() {\n\t\t\tjQuery(document.documentElement).removeAttr('data-debug-view');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic toggle() {\n\t\t\tif (jQuery(document.documentElement).attr('data-debug-view') === 'enabled') {\n\t\t\t\tDebugView.disable();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDebugView.enable();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn DebugView;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/nodetyper.js\n\n\tCopyright © 2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar NodeTyper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tNodeTyper Class.\n\t*******************************************************************************************************************/\n\tclass NodeTyper {\n\t\tconstructor(config) {\n\t\t\tif (typeof config !== 'object' || config === null) {\n\t\t\t\tthrow new Error(`config parameter must be an object (received: ${Util.getType(config)})`);\n\t\t\t}\n\t\t\tif (!config.hasOwnProperty('targetNode') || !(config.targetNode instanceof Node)) {\n\t\t\t\tthrow new Error('config parameter object \"targetNode\" property must be a node');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tnode : {\n\t\t\t\t\tvalue : config.targetNode\n\t\t\t\t},\n\n\t\t\t\tchildNodes : {\n\t\t\t\t\tvalue : []\n\t\t\t\t},\n\n\t\t\t\tnodeValue : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t},\n\n\t\t\t\tappendTo : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.parentNode || null\n\t\t\t\t},\n\n\t\t\t\tclassNames : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.classNames || null\n\t\t\t\t},\n\n\t\t\t\tfinished : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst node = this.node;\n\n\t\t\tif (node.nodeValue) {\n\t\t\t\tthis.nodeValue = node.nodeValue;\n\t\t\t\tnode.nodeValue = '';\n\t\t\t}\n\n\t\t\tlet childNode;\n\n\t\t\twhile ((childNode = node.firstChild) !== null) {\n\t\t\t\tthis.childNodes.push(new NodeTyper({\n\t\t\t\t\ttargetNode : childNode,\n\t\t\t\t\tparentNode : node,\n\t\t\t\t\tclassNames : this.classNames\n\t\t\t\t}));\n\n\t\t\t\tnode.removeChild(childNode);\n\t\t\t}\n\t\t}\n\n\t\tfinish() {\n\t\t\twhile (this.type(true)) /* no-op */;\n\t\t\treturn false;\n\t\t}\n\n\t\ttype(flush) {\n\t\t\tif (this.finished) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (this.appendTo) {\n\t\t\t\tthis.appendTo.appendChild(this.node);\n\t\t\t\tthis.appendTo = null;\n\n\t\t\t\t// Immediately finish typing this node if….\n\t\t\t\tif (\n\t\t\t\t\t// …it's neither a element or text node.\n\t\t\t\t\tthis.node.nodeType !== Node.ELEMENT_NODE && this.node.nodeType !== Node.TEXT_NODE\n\n\t\t\t\t\t// …or the computed value of its parent node's `display` property is `'none'`.\n\t\t\t\t\t|| jQuery(this.node.parentNode).css('display') === 'none'\n\t\t\t\t) {\n\t\t\t\t\treturn this.finish();\n\t\t\t\t}\n\n\t\t\t\tif (this.node.parentNode && this.classNames) {\n\t\t\t\t\tjQuery(this.node.parentNode).addClass(this.classNames);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.nodeValue) {\n\t\t\t\tif (flush) {\n\t\t\t\t\t// Concatenate here in case we've already done some processing.\n\t\t\t\t\tthis.node.nodeValue += this.nodeValue;\n\t\t\t\t\tthis.nodeValue = '';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Use `Util.charAndPosAt()` here to properly handle Unicode code points\n\t\t\t\t\t// that are comprised of surrogate pairs.\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(this.nodeValue, 0);\n\t\t\t\t\tthis.node.nodeValue += char;\n\t\t\t\t\tthis.nodeValue = this.nodeValue.slice(1 + end - start);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (this.classNames) {\n\t\t\t\tjQuery(this.node.parentNode).removeClass(this.classNames);\n\t\t\t\tthis.classNames = null;\n\t\t\t}\n\n\t\t\tconst childNodes = this.childNodes;\n\n\t\t\twhile (childNodes.length > 0) {\n\t\t\t\tif (childNodes[0].type()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tchildNodes.shift();\n\t\t\t}\n\n\t\t\tthis.finished = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn NodeTyper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/prngwrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar PRNGWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPRNGWrapper Class.\n\t*******************************************************************************************************************/\n\tclass PRNGWrapper {\n\t\tconstructor(seed, useEntropy) {\n\t\t\t/* eslint-disable new-cap */\n\t\t\tObject.defineProperties(this, new Math.seedrandom(seed, useEntropy, (prng, seed) => ({\n\t\t\t\t_prng : {\n\t\t\t\t\tvalue : prng\n\t\t\t\t},\n\n\t\t\t\tseed : {\n\t\t\t\t\t/*\n\t\t\t\t\t\tTODO: Make this non-writable.\n\t\t\t\t\t*/\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : seed\n\t\t\t\t},\n\n\t\t\t\tpull : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\trandom : {\n\t\t\t\t\tvalue() {\n\t\t\t\t\t\t++this.pull;\n\t\t\t\t\t\treturn this._prng();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})));\n\t\t\t/* eslint-enable new-cap */\n\t\t}\n\n\t\tstatic marshal(prng) {\n\t\t\tif (!prng || !prng.hasOwnProperty('seed') || !prng.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG is missing required data');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tseed : prng.seed,\n\t\t\t\tpull : prng.pull\n\t\t\t};\n\t\t}\n\n\t\tstatic unmarshal(prngObj) {\n\t\t\tif (!prngObj || !prngObj.hasOwnProperty('seed') || !prngObj.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG object is missing required data');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCreate a new PRNG using the original seed and pull values from it until it\n\t\t\t\thas reached the original pull count.\n\t\t\t*/\n\t\t\tconst prng = new PRNGWrapper(prngObj.seed, false);\n\n\t\t\tfor (let i = prngObj.pull; i > 0; --i) {\n\t\t\t\tprng.random();\n\t\t\t}\n\n\t\t\treturn prng;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn PRNGWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/stylewrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Story, Wikifier */\n\nvar StyleWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _imageMarkupRe    = new RegExp(Patterns.cssImage, 'g');\n\tconst _hasImageMarkupRe = new RegExp(Patterns.cssImage);\n\n\n\t/*******************************************************************************************************************\n\t\tStyleWrapper Class.\n\t*******************************************************************************************************************/\n\tclass StyleWrapper {\n\t\tconstructor(style) {\n\t\t\tif (style == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('StyleWrapper style parameter must be an HTMLStyleElement object');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tstyle : {\n\t\t\t\t\tvalue : style\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tisEmpty() {\n\t\t\t// This should work in all supported browsers.\n\t\t\treturn this.style.cssRules.length === 0;\n\t\t}\n\n\t\tset(rawCss) {\n\t\t\tthis.clear();\n\t\t\tthis.add(rawCss);\n\t\t}\n\n\t\tadd(rawCss) {\n\t\t\tlet css = rawCss;\n\n\t\t\t// Check for wiki image transclusion.\n\t\t\tif (_hasImageMarkupRe.test(css)) {\n\t\t\t\t/*\n\t\t\t\t\tThe JavaScript specifications, since at least ES3, say that `<String>.replace()`\n\t\t\t\t\tshould reset a global-flagged regular expression's `lastIndex` property to `0`\n\t\t\t\t\tupon invocation.  Buggy browser versions exist, however, which do not reset\n\t\t\t\t\t`lastIndex`, so we should do so manually to support those browsers.\n\n\t\t\t\t\tNOTE: I do not think this is actually necessary, since `_imageMarkupRe` is\n\t\t\t\t\tscoped to this module—meaning users should not be able to access it.  That\n\t\t\t\t\tbeing the case, and since we search to exhaustion which should also cause\n\t\t\t\t\t`lastIndex` to be reset, there should never be an instance where we invoke\n\t\t\t\t\t`css.replace()` and `_imageMarkupRe.lastIndex` is not already `0`.  Still,\n\t\t\t\t\tconsidering the other bug, better safe than sorry.\n\t\t\t\t*/\n\t\t\t\t_imageMarkupRe.lastIndex = 0;\n\n\t\t\t\tcss = css.replace(_imageMarkupRe, wikiImage => {\n\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t});\n\n\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t*/\n\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For IE ≤ 10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText += css;\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥ 11).\n\t\t\telse {\n\t\t\t\tthis.style.appendChild(document.createTextNode(css));\n\t\t\t}\n\t\t}\n\n\t\tclear() {\n\t\t\t// For IE ≤10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText = '';\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥11).\n\t\t\telse {\n\t\t\t\tjQuery(this.style).empty();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn StyleWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/diff.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, clone */\n\nvar Diff = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDiff Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDiff operations object (pseudo-enumeration).\n\t*/\n\tconst Op = Util.toEnum({\n\t\tDelete      : 0,\n\t\tSpliceArray : 1,\n\t\tCopy        : 2,\n\t\tCopyDate    : 3\n\t});\n\n\t/*\n\t\tReturns a difference object generated from comparing the orig and dest objects.\n\t*/\n\tfunction diff(orig, dest) /* diff object */ {\n\t\tconst objToString = Object.prototype.toString;\n\t\tconst origIsArray = orig instanceof Array;\n\t\tconst keys        = []\n\t\t\t.concat(Object.keys(orig), Object.keys(dest))\n\t\t\t.sort()\n\t\t\t.filter((val, i, arr) => i === 0 || arr[i - 1] !== val);\n\t\tconst diffed      = {};\n\t\tlet aOpRef;\n\n\t\tconst keyIsAOpRef = key => key === aOpRef;\n\n\t\t/* eslint-disable max-depth */\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key   = keys[i];\n\t\t\tconst origP = orig[key];\n\t\t\tconst destP = dest[key];\n\n\t\t\tif (orig.hasOwnProperty(key)) {\n\t\t\t\t// Key exists in both.\n\t\t\t\tif (dest.hasOwnProperty(key)) {\n\t\t\t\t\t// Values are exactly the same, so do nothing.\n\t\t\t\t\tif (origP === destP) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Values are of the same basic type.\n\t\t\t\t\tif (typeof origP === typeof destP) { // eslint-disable-line valid-typeof\n\t\t\t\t\t\t// Values are functions.\n\t\t\t\t\t\tif (typeof origP === 'function') {\n\t\t\t\t\t\t\t/* diffed[key] = [Op.Copy, destP]; */\n\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are primitives.\n\t\t\t\t\t\telse if (typeof origP !== 'object' || origP === null) {\n\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are objects.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst origPType = objToString.call(origP);\n\t\t\t\t\t\t\tconst destPType = objToString.call(destP);\n\n\t\t\t\t\t\t\t// Values are objects of the same reported type.\n\t\t\t\t\t\t\tif (origPType === destPType) {\n\t\t\t\t\t\t\t\t// Various special cases to handle supported non-generic objects.\n\t\t\t\t\t\t\t\tif (origP instanceof Date) {\n\t\t\t\t\t\t\t\t\tif (Number(origP) !== Number(destP)) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Map) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof RegExp) {\n\t\t\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Set) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Unknown non-generic objects (custom or unsupported natives).\n\t\t\t\t\t\t\t\telse if (origPType !== '[object Object]') {\n\t\t\t\t\t\t\t\t\t// We cannot know how to process these objects,\n\t\t\t\t\t\t\t\t\t// so we simply accept them as-is.\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Generic objects.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tconst recurse = diff(origP, destP);\n\n\t\t\t\t\t\t\t\t\tif (recurse !== null) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = recurse;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Values are objects of different reported types.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Values are of different types.\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = [\n\t\t\t\t\t\t\tOp.Copy,\n\t\t\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Key only exists in orig.\n\t\t\t\telse {\n\t\t\t\t\tif (origIsArray && Util.isNumeric(key)) {\n\t\t\t\t\t\tconst nKey = Number(key);\n\n\t\t\t\t\t\tif (!aOpRef) {\n\t\t\t\t\t\t\taOpRef = '';\n\n\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\taOpRef += '~';\n\t\t\t\t\t\t\t} while (keys.some(keyIsAOpRef));\n\n\t\t\t\t\t\t\tdiffed[aOpRef] = [Op.SpliceArray, nKey, nKey];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey < diffed[aOpRef][1]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][1] = nKey;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey > diffed[aOpRef][2]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][2] = nKey;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = Op.Delete;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Key only exists in dest.\n\t\t\telse {\n\t\t\t\tdiffed[key] = [\n\t\t\t\t\tOp.Copy,\n\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\t/* eslint-enable max-depth */\n\n\t\treturn Object.keys(diffed).length > 0 ? diffed : null;\n\t}\n\n\t/*\n\t\tReturns the object resulting from updating the orig object with the diffed object.\n\t*/\n\tfunction patch(orig, diffed) /* patched object */ {\n\t\tconst keys    = Object.keys(diffed || {});\n\t\tconst patched = clone(orig);\n\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key     = keys[i];\n\t\t\tconst diffedP = diffed[key];\n\n\t\t\tif (diffedP === Op.Delete) {\n\t\t\t\tdelete patched[key];\n\t\t\t}\n\t\t\telse if (diffedP instanceof Array) {\n\t\t\t\tswitch (diffedP[0]) {\n\t\t\t\tcase Op.SpliceArray:\n\t\t\t\t\tpatched.splice(diffedP[1], 1 + (diffedP[2] - diffedP[1]));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.Copy:\n\t\t\t\t\tpatched[key] = clone(diffedP[1]);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.CopyDate:\n\t\t\t\t\tpatched[key] = new Date(diffedP[1]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpatched[key] = patch(patched[key], diffedP);\n\t\t\t}\n\t\t}\n\n\t\treturn patched;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tOp    : { value : Op },\n\t\tdiff  : { value : diff },\n\t\tpatch : { value : patch }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/l10n.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global l10nStrings, strings */\n\nvar L10n = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Replacement pattern regular expressions.\n\tconst _patternRe    = /\\{\\w+\\}/g;\n\tconst _hasPatternRe = new RegExp(_patternRe.source); // to drop the global flag\n\n\n\t/*******************************************************************************************************************\n\t\tLocalization Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nInit() {\n\t\t/* legacy */\n\t\t_mapStringsToL10nStrings();\n\t\t/* /legacy */\n\t}\n\n\t/*******************************************************************************************************************\n\t\tLocalized String Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nGet(ids, overrides) {\n\t\tif (!ids) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst id = (idList => {\n\t\t\tlet selectedId;\n\t\t\tidList.some(id => {\n\t\t\t\tif (l10nStrings.hasOwnProperty(id)) {\n\t\t\t\t\tselectedId = id;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\treturn selectedId;\n\t\t})(Array.isArray(ids) ? ids : [ids]);\n\n\t\tif (!id) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst maxIterations = 50;\n\t\tlet processed = l10nStrings[id];\n\t\tlet iteration = 0;\n\n\t\twhile (_hasPatternRe.test(processed)) {\n\t\t\tif (++iteration > maxIterations) {\n\t\t\t\tthrow new Error('L10n.get exceeded maximum replacement iterations, probable infinite loop');\n\t\t\t}\n\n\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t_patternRe.lastIndex = 0;\n\n\t\t\tprocessed = processed.replace(_patternRe, pat => {\n\t\t\t\tconst subId = pat.slice(1, -1);\n\n\t\t\t\tif (overrides && overrides.hasOwnProperty(subId)) {\n\t\t\t\t\treturn overrides[subId];\n\t\t\t\t}\n\t\t\t\telse if (l10nStrings.hasOwnProperty(subId)) {\n\t\t\t\t\treturn l10nStrings[subId];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn processed;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tAttempt to map legacy `strings` object properties to the `l10nStrings` object.\n\t*/\n\tfunction _mapStringsToL10nStrings() {\n\t\tif (strings && Object.keys(strings).length > 0) {\n\t\t\tObject.keys(l10nStrings).forEach(id => {\n\t\t\t\ttry {\n\t\t\t\t\tlet value;\n\n\t\t\t\t\tswitch (id) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tGeneral.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'identity': value = strings.identity; break;\n\t\t\t\t\tcase 'aborting': value = strings.aborting; break;\n\t\t\t\t\tcase 'cancel':   value = strings.cancel; break;\n\t\t\t\t\tcase 'close':    value = strings.close; break;\n\t\t\t\t\tcase 'ok':       value = strings.ok; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tErrors.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'errorTitle':              value = strings.errors.title; break;\n\t\t\t\t\tcase 'errorNonexistentPassage': value = strings.errors.nonexistentPassage; break;\n\t\t\t\t\tcase 'errorSaveMissingData':    value = strings.errors.saveMissingData; break;\n\t\t\t\t\tcase 'errorSaveIdMismatch':     value = strings.errors.saveIdMismatch; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tWarnings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'warningDegraded': value = strings.warnings.degraded; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tDebug View.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'debugViewTitle':  value = strings.debugView.title; break;\n\t\t\t\t\tcase 'debugViewToggle': value = strings.debugView.toggle; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tUI bar.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'uiBarToggle':   value = strings.uiBar.toggle; break;\n\t\t\t\t\tcase 'uiBarBackward': value = strings.uiBar.backward; break;\n\t\t\t\t\tcase 'uiBarForward':  value = strings.uiBar.forward; break;\n\t\t\t\t\tcase 'uiBarJumpto':   value = strings.uiBar.jumpto; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tJump To.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'jumptoTitle':       value = strings.jumpto.title; break;\n\t\t\t\t\tcase 'jumptoTurn':        value = strings.jumpto.turn; break;\n\t\t\t\t\tcase 'jumptoUnavailable': value = strings.jumpto.unavailable; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSaves.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'savesTitle':       value = strings.saves.title; break;\n\t\t\t\t\tcase 'savesDisallowed':  value = strings.saves.disallowed; break;\n\t\t\t\t\tcase 'savesIncapable':   value = strings.saves.incapable; break;\n\t\t\t\t\tcase 'savesLabelAuto':   value = strings.saves.labelAuto; break;\n\t\t\t\t\tcase 'savesLabelDelete': value = strings.saves.labelDelete; break;\n\t\t\t\t\tcase 'savesLabelExport': value = strings.saves.labelExport; break;\n\t\t\t\t\tcase 'savesLabelImport': value = strings.saves.labelImport; break;\n\t\t\t\t\tcase 'savesLabelLoad':   value = strings.saves.labelLoad; break;\n\t\t\t\t\tcase 'savesLabelClear':  value = strings.saves.labelClear; break;\n\t\t\t\t\tcase 'savesLabelSave':   value = strings.saves.labelSave; break;\n\t\t\t\t\tcase 'savesLabelSlot':   value = strings.saves.labelSlot; break;\n\t\t\t\t\tcase 'savesUnavailable': value = strings.saves.unavailable; break;\n\t\t\t\t\tcase 'savesUnknownDate': value = strings.saves.unknownDate; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSettings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'settingsTitle': value = strings.settings.title; break;\n\t\t\t\t\tcase 'settingsOff':   value = strings.settings.off; break;\n\t\t\t\t\tcase 'settingsOn':    value = strings.settings.on; break;\n\t\t\t\t\tcase 'settingsReset': value = strings.settings.reset; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tRestart.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'restartTitle':  value = strings.restart.title; break;\n\t\t\t\t\tcase 'restartPrompt': value = strings.restart.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tShare.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'shareTitle': value = strings.share.title; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAlert.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'alertTitle': /* none */ break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAutoload.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'autoloadTitle':  value = strings.autoload.title; break;\n\t\t\t\t\tcase 'autoloadCancel': value = strings.autoload.cancel; break;\n\t\t\t\t\tcase 'autoloadOk':     value = strings.autoload.ok; break;\n\t\t\t\t\tcase 'autoloadPrompt': value = strings.autoload.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tMacros.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'macroBackText':   value = strings.macros.back.text; break;\n\t\t\t\t\tcase 'macroReturnText': value = strings.macros.return.text; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tl10nStrings[id] = value.replace(/%\\w+%/g, pat => `{${pat.slice(1, -1)}}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tLocalization Functions.\n\t\t*/\n\t\tinit : { value : l10nInit },\n\n\t\t/*\n\t\t\tLocalized String Functions.\n\t\t*/\n\t\tget : { value : l10nGet }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/legacy.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\t[DEPRECATED] The `strings` object is deprecated and should no longer be used.\n\tAll new or updated translations should be based upon the `l10nStrings` object\n\t(see: `l10n/strings.js`).\n\n\tLegacy/existing uses of the `strings` object will be mapped to the `l10nStrings`\n\tobject after user script evaluation.\n*/\nvar strings = { // eslint-disable-line no-unused-vars, no-var\n\terrors    : {},\n\twarnings  : {},\n\tdebugView : {},\n\tuiBar     : {},\n\tjumpto    : {},\n\tsaves     : {},\n\tsettings  : {},\n\trestart   : {},\n\tshare     : {},\n\tautoload  : {},\n\tmacros    : {\n\t\tback   : {},\n\t\treturn : {}\n\t}\n};\n\n/***********************************************************************************************************************\n\n\tl10n/strings.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* eslint-disable max-len, prefer-template */\n\n/*\n\tATTENTION TRANSLATORS\n\n\tPlease use the `locale/l10n-template.js` file, from the root of the repository,\n\tas the template for your translation rather than this file.\n\n\tSEE: https://github.com/tmedwards/sugarcube-2/tree/develop/locale\n*/\nvar l10nStrings = { // eslint-disable-line no-unused-vars, no-var\n\t/*\n\t\tGeneral.\n\t*/\n\tidentity : 'game',\n\taborting : 'Aborting',\n\tcancel   : 'Cancel',\n\tclose    : 'Close',\n\tok       : 'OK',\n\n\t/*\n\t\tErrors.\n\t*/\n\terrorTitle              : 'Error',\n\terrorToggle             : 'Toggle the error view',\n\terrorNonexistentPassage : 'the passage \"{passage}\" does not exist', // NOTE: `passage` is supplied locally\n\terrorSaveMissingData    : 'save is missing required data. Either the loaded file is not a save or the save has become corrupted',\n\terrorSaveIdMismatch     : 'save is from the wrong {identity}',\n\n\t/*\n\t\tWarnings.\n\t*/\n\t_warningIntroLacking  : 'Your browser either lacks or has disabled',\n\t_warningOutroDegraded : ', so this {identity} is running in a degraded mode. You may be able to continue, however, some parts may not work properly.',\n\twarningNoWebStorage   : '{_warningIntroLacking} the Web Storage API{_warningOutroDegraded}',\n\twarningDegraded       : '{_warningIntroLacking} some of the capabilities required by this {identity}{_warningOutroDegraded}',\n\n\t/*\n\t\tDebug bar.\n\t*/\n\tdebugBarToggle      : 'Toggle the debug bar',\n\tdebugBarNoWatches   : '\\u2014 no watches set \\u2014',\n\tdebugBarAddWatch    : 'Add watch',\n\tdebugBarDeleteWatch : 'Delete watch',\n\tdebugBarWatchAll    : 'Watch all',\n\tdebugBarWatchNone   : 'Delete all',\n\tdebugBarLabelAdd    : 'Add',\n\tdebugBarLabelWatch  : 'Watch',\n\tdebugBarLabelTurn   : 'Turn', // (noun) chance to act (in a game), moment, period\n\tdebugBarLabelViews  : 'Views',\n\tdebugBarViewsToggle : 'Toggle the debug views',\n\tdebugBarWatchToggle : 'Toggle the watch panel',\n\n\t/*\n\t\tUI bar.\n\t*/\n\tuiBarToggle   : 'Toggle the UI bar',\n\tuiBarBackward : 'Go backward within the {identity} history',\n\tuiBarForward  : 'Go forward within the {identity} history',\n\tuiBarJumpto   : 'Jump to a specific point within the {identity} history',\n\n\t/*\n\t\tJump To.\n\t*/\n\tjumptoTitle       : 'Jump To',\n\tjumptoTurn        : 'Turn', // (noun) chance to act (in a game), moment, period\n\tjumptoUnavailable : 'No jump points currently available\\u2026',\n\n\t/*\n\t\tSaves.\n\t*/\n\tsavesTitle       : 'Saves',\n\tsavesDisallowed  : 'Saving has been disallowed on this passage.',\n\tsavesIncapable   : '{_warningIntroLacking} the capabilities required to support saves, so saves have been disabled for this session.',\n\tsavesLabelAuto   : 'Autosave',\n\tsavesLabelDelete : 'Delete',\n\tsavesLabelExport : 'Save to Disk\\u2026',\n\tsavesLabelImport : 'Load from Disk\\u2026',\n\tsavesLabelLoad   : 'Load',\n\tsavesLabelClear  : 'Delete All',\n\tsavesLabelSave   : 'Save',\n\tsavesLabelSlot   : 'Slot',\n\tsavesUnavailable : 'No save slots found\\u2026',\n\tsavesUnknownDate : 'unknown',\n\n\t/*\n\t\tSettings.\n\t*/\n\tsettingsTitle : 'Settings',\n\tsettingsOff   : 'Off',\n\tsettingsOn    : 'On',\n\tsettingsReset : 'Reset to Defaults',\n\n\t/*\n\t\tRestart.\n\t*/\n\trestartTitle  : 'Restart',\n\trestartPrompt : 'Are you sure that you want to restart? Unsaved progress will be lost.',\n\n\t/*\n\t\tShare.\n\t*/\n\tshareTitle : 'Share',\n\n\t/*\n\t\tAlert.\n\t*/\n\talertTitle : 'Alert',\n\n\t/*\n\t\tAutoload.\n\t*/\n\tautoloadTitle  : 'Autoload',\n\tautoloadCancel : 'Go to start',\n\tautoloadOk     : 'Load autosave',\n\tautoloadPrompt : 'An autosave exists. Load it now or go to the start?',\n\n\t/*\n\t\tMacros.\n\t*/\n\tmacroBackText   : 'Back',  // (verb) rewind, revert\n\tmacroReturnText : 'Return' // (verb) go/send back\n};\n\n/***********************************************************************************************************************\n\n\tconfig.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar Config = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// General settings.\n\tlet _debug                 = false;\n\tlet _addVisitedLinkClass   = false;\n\tlet _cleanupWikifierOutput = false;\n\tlet _loadDelay             = 0;\n\n\t// Audio settings.\n\tlet _audioPauseOnFadeToZero = true;\n\tlet _audioPreloadMetadata   = true;\n\n\t// State history settings.\n\tlet _historyControls  = true;\n\tlet _historyMaxStates = 100;\n\n\t// Macros settings.\n\tlet _macrosIfAssignmentError   = true;\n\tlet _macrosMaxLoopIterations   = 1000;\n\tlet _macrosTypeSkipKey         = '\\x20'; // Space\n\tlet _macrosTypeVisitedPassages = true;\n\n\t// Navigation settings.\n\tlet _navigationOverride;\n\n\t// Passages settings.\n\tlet _passagesDescriptions;\n\tlet _passagesDisplayTitles = false;\n\tlet _passagesNobr          = false;\n\tlet _passagesStart; // set by `Story.load()`\n\tlet _passagesOnProcess;\n\tlet _passagesTransitionOut;\n\n\t// Saves settings.\n\tlet _savesAutoload;\n\tlet _savesAutosave;\n\tlet _savesId        = 'untitled-story';\n\tlet _savesIsAllowed;\n\tlet _savesOnLoad;\n\tlet _savesOnSave;\n\tlet _savesSlots     = 8;\n\tlet _savesVersion;\n\n\t// UI settings.\n\tlet _uiStowBarInitially    = 800;\n\tlet _uiUpdateStoryElements = true;\n\n\n\t/*******************************************************************************\n\t\tError Constants.\n\t*******************************************************************************/\n\n\tconst _errHistoryModeDeprecated     = 'Config.history.mode has been deprecated and is no longer used by SugarCube, please remove it from your code';\n\tconst _errHistoryTrackingDeprecated = 'Config.history.tracking has been deprecated, use Config.history.maxStates instead';\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze({\n\t\t/*\n\t\t\tGeneral settings.\n\t\t*/\n\t\tget debug() { return _debug; },\n\t\tset debug(value) { _debug = Boolean(value); },\n\n\t\tget addVisitedLinkClass() { return _addVisitedLinkClass; },\n\t\tset addVisitedLinkClass(value) { _addVisitedLinkClass = Boolean(value); },\n\n\t\tget cleanupWikifierOutput() { return _cleanupWikifierOutput; },\n\t\tset cleanupWikifierOutput(value) { _cleanupWikifierOutput = Boolean(value); },\n\n\t\tget loadDelay() { return _loadDelay; },\n\t\tset loadDelay(value) {\n\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\tthrow new RangeError('Config.loadDelay must be a non-negative integer');\n\t\t\t}\n\n\t\t\t_loadDelay = value;\n\t\t},\n\n\t\t/*\n\t\t\tAudio settings.\n\t\t*/\n\t\taudio : Object.freeze({\n\t\t\tget pauseOnFadeToZero() { return _audioPauseOnFadeToZero; },\n\t\t\tset pauseOnFadeToZero(value) { _audioPauseOnFadeToZero = Boolean(value); },\n\n\t\t\tget preloadMetadata() { return _audioPreloadMetadata; },\n\t\t\tset preloadMetadata(value) { _audioPreloadMetadata = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tState history settings.\n\t\t*/\n\t\thistory : Object.freeze({\n\t\t\t// TODO: (v3) This should be under UI settings → `Config.ui.historyControls`.\n\t\t\tget controls() { return _historyControls; },\n\t\t\tset controls(value) {\n\t\t\t\tconst controls = Boolean(value);\n\n\t\t\t\tif (_historyMaxStates === 1 && controls) {\n\t\t\t\t\tthrow new Error('Config.history.controls must be false when Config.history.maxStates is 1');\n\t\t\t\t}\n\n\t\t\t\t_historyControls = controls;\n\t\t\t},\n\n\t\t\tget maxStates() { return _historyMaxStates; },\n\t\t\tset maxStates(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.history.maxStates must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_historyMaxStates = value;\n\n\t\t\t\t// Force `Config.history.controls` to `false`, when limited to `1` moment.\n\t\t\t\tif (_historyControls && value === 1) {\n\t\t\t\t\t_historyControls = false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// legacy\n\t\t\t// Die if deprecated state history settings are accessed.\n\t\t\tget mode()  { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tset mode(_) { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tget tracking()  { throw new Error(_errHistoryTrackingDeprecated); },\n\t\t\tset tracking(_) { throw new Error(_errHistoryTrackingDeprecated); }\n\t\t\t// /legacy\n\t\t}),\n\n\t\t/*\n\t\t\tMacros settings.\n\t\t*/\n\t\tmacros : Object.freeze({\n\t\t\tget ifAssignmentError() { return _macrosIfAssignmentError; },\n\t\t\tset ifAssignmentError(value) { _macrosIfAssignmentError = Boolean(value); },\n\n\t\t\tget maxLoopIterations() { return _macrosMaxLoopIterations; },\n\t\t\tset maxLoopIterations(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.macros.maxLoopIterations must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_macrosMaxLoopIterations = value;\n\t\t\t},\n\n\t\t\tget typeSkipKey() { return _macrosTypeSkipKey; },\n\t\t\tset typeSkipKey(value) { _macrosTypeSkipKey = String(value); },\n\n\t\t\tget typeVisitedPassages() { return _macrosTypeVisitedPassages; },\n\t\t\tset typeVisitedPassages(value) { _macrosTypeVisitedPassages = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tNavigation settings.\n\t\t*/\n\t\tnavigation : Object.freeze({\n\t\t\tget override() { return _navigationOverride; },\n\t\t\tset override(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.navigation.override must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_navigationOverride = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tPassages settings.\n\t\t*/\n\t\tpassages : Object.freeze({\n\t\t\tget descriptions() { return _passagesDescriptions; },\n\t\t\tset descriptions(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'Object' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.descriptions must be a boolean, object, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesDescriptions = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.updateTitle`.\n\t\t\tget displayTitles() { return _passagesDisplayTitles; },\n\t\t\tset displayTitles(value) { _passagesDisplayTitles = Boolean(value); },\n\n\t\t\tget nobr() { return _passagesNobr; },\n\t\t\tset nobr(value) { _passagesNobr = Boolean(value); },\n\n\t\t\tget onProcess() { return _passagesOnProcess; },\n\t\t\tset onProcess(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.onProcess must be a function or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesOnProcess = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.(start|startingPassage)`.\n\t\t\tget start() { return _passagesStart; },\n\t\t\tset start(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.start must be a string or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesStart = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.transitionOut`.\n\t\t\tget transitionOut() { return _passagesTransitionOut; },\n\t\t\tset transitionOut(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'string'\n\t\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.transitionOut must be a string, non-negative integer, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesTransitionOut = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tSaves settings.\n\t\t*/\n\t\tsaves : Object.freeze({\n\t\t\tget autoload() { return _savesAutoload; },\n\t\t\tset autoload(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'string' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autoload must be a boolean, string, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutoload = value;\n\t\t\t},\n\n\t\t\tget autosave() { return _savesAutosave; },\n\t\t\tset autosave(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\t// legacy\n\t\t\t\t\t// Convert a string value to an Array of string.\n\t\t\t\t\tif (valueType === 'string') {\n\t\t\t\t\t\t_savesAutosave = [value];\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// /legacy\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t\t&& (valueType !== 'Array' || !value.every(item => typeof item === 'string'))\n\t\t\t\t\t\t&& valueType !== 'function'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autosave must be a boolean, Array of strings, function, or null/undefined (received: ${valueType}${valueType === 'Array' ? ' of mixed' : ''})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutosave = value;\n\t\t\t},\n\n\t\t\tget id() { return _savesId; },\n\t\t\tset id(value) {\n\t\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\t\tthrow new TypeError(`Config.saves.id must be a non-empty string (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesId = value;\n\t\t\t},\n\n\t\t\tget isAllowed() { return _savesIsAllowed; },\n\t\t\tset isAllowed(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.isAllowed must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesIsAllowed = value;\n\t\t\t},\n\n\t\t\tget onLoad() { return _savesOnLoad; },\n\t\t\tset onLoad(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onLoad must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnLoad = value;\n\t\t\t},\n\n\t\t\tget onSave() { return _savesOnSave; },\n\t\t\tset onSave(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onSave must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnSave = value;\n\t\t\t},\n\n\t\t\tget slots() { return _savesSlots; },\n\t\t\tset slots(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new TypeError(`Config.saves.slots must be a non-negative integer (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesSlots = value;\n\t\t\t},\n\n\t\t\tget version() { return _savesVersion; },\n\t\t\tset version(value) { _savesVersion = value; }\n\t\t}),\n\n\t\t/*\n\t\t\tUI settings.\n\t\t*/\n\t\tui : Object.freeze({\n\t\t\tget stowBarInitially() { return _uiStowBarInitially; },\n\t\t\tset stowBarInitially(value) {\n\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\tif (\n\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError(`Config.ui.stowBarInitially must be a boolean or non-negative integer (received: ${valueType})`);\n\t\t\t\t}\n\n\t\t\t\t_uiStowBarInitially = value;\n\t\t\t},\n\n\t\t\tget updateStoryElements() { return _uiUpdateStoryElements; },\n\t\t\tset updateStoryElements(value) { _uiUpdateStoryElements = Boolean(value); }\n\t\t})\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tsimpleaudio.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser, Config, Has, LoadScreen, Story, Util, Visibility, clone */\n\nvar SimpleAudio = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tEvents that count as user activation—i.e. \"user gestures\", \"activation behavior\".\n\n\t\tNOTE (ca. Dec, 2018): This not an exhaustive list and varies significantly by browser.\n\t\tProposals for a specification/standard are still very much in flux at this point.\n\n\t\tTODO (ca. Dec, 2018): Revisit this topic.\n\n\t\tSEE: (too many to list)\n\t\t\thttps://github.com/whatwg/html/issues/3849\n\t\t\thttps://github.com/whatwg/html/issues/1903\n\t\t\thttps://html.spec.whatwg.org/#activation\n\t\t\thttps://docs.google.com/spreadsheets/d/1DGXjhQ6D3yZXIePOMo0dsd2agz0t5W7rYH1NwJ-QGJo/edit#gid=0\n\t*/\n\tconst _gestureEventNames = Object.freeze(['click', 'contextmenu', 'dblclick', 'keyup', 'mouseup', 'pointerup', 'touchend']);\n\n\t// Special group IDs.\n\tconst _specialIds = Object.freeze([':not', ':all', ':looped', ':muted', ':paused', ':playing']);\n\n\t// Format specifier regular expression.\n\tconst _formatSpecRe = /^([\\w-]+)\\s*\\|\\s*(\\S.*)$/; // e.g. 'mp3|https://audiohost.tld/id'\n\n\t// ID verification regular expressions.\n\tconst _badIdRe = /[:\\s]/;\n\n\t// Tracks collection.\n\tconst _tracks = new Map();\n\n\t// Groups collection.\n\tconst _groups = new Map();\n\n\t// Playlists collection.\n\tconst _lists = new Map();\n\n\t// Subscriber collection.\n\tconst _subscribers = new Map();\n\n\t// Master playback rate.\n\tlet _masterRate = 1;\n\n\t// Master playback volume.\n\tlet _masterVolume = 1;\n\n\t// Master mute state.\n\tlet _masterMute = false;\n\n\t// Master mute on tab/window visibility state.\n\tlet _masterMuteOnHidden = false;\n\n\n\t/*******************************************************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************************************************/\n\t// Return whether the `<HTMLAudioElement>.play()` method returns a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _playReturnsPromise = (function () {\n\t\t// Cache of whether `<HTMLAudioElement>.play()` returns a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _playReturnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (Has.audio) {\n\t\t\t\ttry {\n\t\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t\t// NOTE (ca. Jan 01, 2020): Firefox will still log an \"Autoplay is only allowed\n\t\t\t\t\t// when […] media is muted.\" message to the console when attempting the test\n\t\t\t\t\t// below, even though the audio has been muted.  Stay classy, Firefox.\n\t\t\t\t\t//\n\t\t\t\t\t// QUESTION (ca. Jan 01, 2020): Keep this?  It's only here to appease Firefox,\n\t\t\t\t\t// but doesn't seem to work as Firefox seems to ignore mute in violation of the\n\t\t\t\t\t// `HTMLAudioElement` specification—willfully or simply a bug, I can't say.\n\t\t\t\t\taudio.muted = true;\n\n\t\t\t\t\tconst value = audio.play();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since there's no source, and we don't actually\n\t\t\t\t\t// care about the error, since we just want the return value, so we consign it\n\t\t\t\t\t// to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _playReturnsPromise;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tAudioTrack Class.\n\t*******************************************************************************************************************/\n\tclass AudioTrack {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of sources or AudioTrack object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioTrack) {\n\t\t\t\tthis._copy(obj);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('sources parameter must be either an array, of URIs or source objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(sourceList) {\n\t\t\tconst dataUriRe   = /^data:\\s*audio\\/([^;,]+)\\s*[;,]/i;\n\t\t\tconst extRe       = /\\.([^./\\\\]+)$/;\n\t\t\tconst getType     = AudioTrack.getType;\n\t\t\tconst usedSources = [];\n\t\t\t/*\n\t\t\t\tHTMLAudioElement: DOM factory method vs. constructor\n\n\t\t\t\tUse of the DOM factory method, `document.createElement('audio')`, should be\n\t\t\t\tpreferred over use of the constructor, `new Audio()`.  The reason being that\n\t\t\t\tobjects created by the latter are, erroneously, treated differently, often\n\t\t\t\tunfavorably, by certain browser engines—e.g. within some versions of the iOS\n\t\t\t\tbrowser core.\n\n\t\t\t\tNotably, the only difference between the two, per the specification, is that\n\t\t\t\tobjects created via the constructor should have their `preload` property\n\t\t\t\tautomatically set to 'auto'.  Thus, there's no technical reason to prefer\n\t\t\t\tusage of the constructor, even discounting buggy browser implementations.\n\t\t\t*/\n\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t// Initially set the `preload` attribute to `'none'`.\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Process the array of sources, adding any valid sources to the `usedSources`\n\t\t\t// array and to the audio element as source elements.\n\t\t\tsourceList.forEach(src => {\n\t\t\t\tlet srcObj = null;\n\n\t\t\t\tswitch (typeof src) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t{\n\t\t\t\t\t\tlet match;\n\n\t\t\t\t\t\tif (src.slice(0, 5) === 'data:') {\n\t\t\t\t\t\t\tmatch = dataUriRe.exec(src);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source data URI missing media type');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tmatch = extRe.exec(Util.parseUrl(src).pathname);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source URL missing file extension');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(match[1]);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\t{\n\t\t\t\t\t\tif (src === null) {\n\t\t\t\t\t\t\tthrow new Error('source object cannot be null');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('src')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"src\" property');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('format')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"format\" property');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst type = getType(src.format);\n\n\t\t\t\t\t\tif (type !== null) {\n\t\t\t\t\t\t\tsrcObj = { src : src.src, type };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`invalid source value (type: ${typeof src})`);\n\t\t\t\t}\n\n\t\t\t\tif (srcObj !== null) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tOpera (Blink; ca. Jul 2017) fails to play audio from some sources\n\t\t\t\t\t\twith MIME-types containing a `codecs` parameter, despite the fact\n\t\t\t\t\t\tthat `canPlayType()` blessed the full MIME-type including `codecs`.\n\n\t\t\t\t\t\tBizarrely, this only affects some MIME-types—e.g. MP3s are affected,\n\t\t\t\t\t\twhile WAVEs are not.\n\t\t\t\t\t\t\tFails: 'audio/mpeg; codecs=\"mp3\"'\n\t\t\t\t\t\t\tPlays: 'audio/mpeg'\n\t\t\t\t\t\t\tPlays: 'audio/wav; codecs=\"1\"'\n\n\t\t\t\t\t\tTo workaround this we remove all parameters from the MIME-type in\n\t\t\t\t\t\tOpera.\n\t\t\t\t\t*/\n\t\t\t\t\tif (Browser.isOpera) {\n\t\t\t\t\t\tsrcObj.type =  srcObj.type.replace(/;.*$/, '');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\taudio.appendChild(source);\n\t\t\t\t\tusedSources.push(srcObj);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (audio.hasChildNodes()) {\n\t\t\t\t// Set the `preload` attribute to `'metadata'`, unless preloading has been disabled.\n\t\t\t\tif (Config.audio.preloadMetadata) {\n\t\t\t\t\taudio.preload = 'metadata';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._finalize(audio, usedSources, clone(sourceList));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(\n\t\t\t\tobj.audio.cloneNode(true), // deep clone of the audio element & its children\n\t\t\t\tclone(obj.sources),\n\t\t\t\tclone(obj.originals)\n\t\t\t);\n\t\t}\n\n\t\t_finalize(audio, sources, originals) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\taudio : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : audio\n\t\t\t\t},\n\n\t\t\t\tsources : {\n\t\t\t\t\tvalue : Object.freeze(sources)\n\t\t\t\t},\n\n\t\t\t\toriginals : {\n\t\t\t\t\tvalue : Object.freeze(originals)\n\t\t\t\t},\n\n\t\t\t\t_error : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_faderId : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up event handlers on the audio and source elements.\n\t\t\tjQuery(this.audio)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving a `loadstart` event on the audio element, set `_error` to\n\t\t\t\t\t`false`.\n\t\t\t\t*/\n\t\t\t\t.on('loadstart.AudioTrack', () => this._error = false)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the audio element, set `_error` to\n\t\t\t\t\t`true`.\n\n\t\t\t\t\tCaveats by browser:\n\t\t\t\t\t\tEdge violates the specification by triggering `error` events from source\n\t\t\t\t\t\telements on their parent media element, rather than the source element.\n\t\t\t\t\t\tTo enable error handling in all browsers, we set the error handler on the\n\t\t\t\t\t\taudio element and have the final source element forward its `error` event.\n\n\t\t\t\t\t\tIE does not trigger, at least some, `error` events from source elements at\n\t\t\t\t\t\tall, not on the source element or its parent media element.  AFAIK, nothing\n\t\t\t\t\t\tcan be done about this lossage.\n\t\t\t\t*/\n\t\t\t\t.on('error.AudioTrack', () => this._error = true)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the final source element (if any), trigger\n\t\t\t\t\tan `error` event on the audio element—that being necessary because the source\n\t\t\t\t\t`error` event does not bubble.\n\t\t\t\t*/\n\t\t\t\t.find('source:last-of-type')\n\t\t\t\t.on('error.AudioTrack', () => this._trigger('error'));\n\n\t\t\t// Subscribe to command messages.\n\t\t\tsubscribe(this, mesg => {\n\t\t\t\tif (!this.audio) {\n\t\t\t\t\tunsubscribe(this);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tswitch (mesg) {\n\t\t\t\tcase 'loadwithscreen':\n\t\t\t\t\tif (this.hasSource()) {\n\t\t\t\t\t\tconst lockId = LoadScreen.lock();\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t// NOTE: Do not use an arrow function here.\n\t\t\t\t\t\t\t.one(\n\t\t\t\t\t\t\t\t'canplaythrough.AudioTrack_loadwithscreen error.AudioTrack_loadwithscreen',\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\tjQuery(this).off('.AudioTrack_loadwithscreen');\n\t\t\t\t\t\t\t\t\tLoadScreen.unlock(lockId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.load();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'load':   this.load();               break;\n\t\t\t\tcase 'mute':   this._updateAudioMute();   break;\n\t\t\t\tcase 'rate':   this._updateAudioRate();   break;\n\t\t\t\tcase 'stop':   this.stop();               break;\n\t\t\t\tcase 'volume': this._updateAudioVolume(); break;\n\t\t\t\tcase 'unload': this.unload();             break;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Synchronize with the current master audio settings.\n\t\t\tthis._updateAudioMute();\n\t\t\tthis._updateAudioRate();\n\t\t\tthis._updateAudioVolume();\n\t\t}\n\n\t\t_trigger(eventName) {\n\t\t\t// Do not use `trigger()` here as we do not want these events to bubble.\n\t\t\tjQuery(this.audio).triggerHandler(eventName);\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.  That said, since the audio element contains\n\t\t\t\tdata buffers for the selected audio source, which may be quite large, manually\n\t\t\t\tpurging them as soon as we know that they're no longer needed is not a bad idea.\n\t\t\t*/\n\t\t\tunsubscribe(this);\n\n\t\t\tif (!this.audio) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(this.audio).off();\n\t\t\tthis.unload();\n\t\t\tthis._error = true;\n\n\t\t\t// Delete the audio element property.\n\t\t\tdelete this.audio;\n\t\t}\n\n\t\tclone() {\n\t\t\treturn new AudioTrack(this);\n\t\t}\n\n\t\tload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.audio.pause();\n\n\t\t\tif (!this.audio.hasChildNodes()) {\n\t\t\t\tif (this.sources.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.sources.forEach(srcObj => {\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src  = srcObj.src;\n\t\t\t\t\tsource.type = srcObj.type;\n\t\t\t\t\tthis.audio.appendChild(source);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tif (!this.isLoading()) {\n\t\t\t\tthis.audio.load();\n\t\t\t}\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.stop();\n\n\t\t\tconst audio = this.audio;\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Remove all source elements.\n\t\t\twhile (audio.hasChildNodes()) {\n\t\t\t\taudio.removeChild(audio.firstChild);\n\t\t\t}\n\n\t\t\t// Force the audio element to drop any existing data buffers.\n\t\t\taudio.load();\n\t\t}\n\n\t\tplay() {\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tconst namespace = '.AudioTrack_play';\n\n\t\t\treturn _playReturnsPromise()\n\t\t\t\t? this.audio.play()\n\t\t\t\t: new Promise((resolve, reject) => {\n\t\t\t\t\tif (this.isPlaying()) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t\t\t.off(namespace)\n\t\t\t\t\t\t\t.one(`error${namespace} playing${namespace} timeupdate${namespace}`, ev => {\n\t\t\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\t\t\tif (ev.type === 'error') {\n\t\t\t\t\t\t\t\t\treject(new Error('unknown audio play error'));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.audio.play();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioTrack_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioTrack_playWhenAllowed');\n\t\t\t\t\tthis.audio.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tthis.audio.pause();\n\t\t}\n\n\t\tstop() {\n\t\t\tthis.audio.pause();\n\t\t\tthis.time(0);\n\t\t\tthis._trigger(':stopped');\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tthis.fadeStop();\n\n\t\t\tconst from = Math.clamp(fromVol == null ? this.volume() : fromVol, 0, 1); // lazy equality for null\n\t\t\tconst to   = Math.clamp(toVol, 0, 1);\n\n\t\t\tif (from === to) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.volume(from);\n\n\t\t\t/*\n\t\t\t\tWe listen for the `timeupdate` event here, rather than `playing`, because\n\t\t\t\tvarious browsers (notably, mobile browsers) are poor at firing media events\n\t\t\t\tin a timely fashion, so we use `timeupdate` to ensure that we don't start\n\t\t\t\tthe fade until the track is actually progressing.\n\t\t\t*/\n\t\t\tjQuery(this.audio)\n\t\t\t\t.off('timeupdate.AudioTrack_fade')\n\t\t\t\t.one('timeupdate.AudioTrack_fade', () => {\n\t\t\t\t\tlet min;\n\t\t\t\t\tlet max;\n\n\t\t\t\t\t// Fade in.\n\t\t\t\t\tif (from < to) {\n\t\t\t\t\t\tmin = from;\n\t\t\t\t\t\tmax = to;\n\t\t\t\t\t}\n\t\t\t\t\t// Fade out.\n\t\t\t\t\telse {\n\t\t\t\t\t\tmin = to;\n\t\t\t\t\t\tmax = from;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst time     = Math.max(duration, 1);\n\t\t\t\t\tconst interval = 25; // in milliseconds\n\t\t\t\t\tconst delta    = (to - from) / (time / (interval / 1000));\n\n\t\t\t\t\tthis._trigger(':fading');\n\t\t\t\t\tthis._faderId = setInterval(() => {\n\t\t\t\t\t\tif (!this.isPlaying()) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWhile it may seem like a good idea to also set the track volume\n\t\t\t\t\t\t\t\tto the `to` value here, we should not do so.  We cannot know why\n\t\t\t\t\t\t\t\tthe track is no longer playing, nor if the volume has been modified\n\t\t\t\t\t\t\t\tin the interim, so doing so now may clobber an end-user set volume.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.volume(Math.clamp(this.volume() + delta, min, max));\n\n\t\t\t\t\t\tif (Config.audio.pauseOnFadeToZero && this.volume() === 0) {\n\t\t\t\t\t\t\tthis.pause();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.volume() === to) {\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\tthis._trigger(':faded');\n\t\t\t\t\t\t}\n\t\t\t\t\t}, interval);\n\t\t\t\t});\n\n\t\t\treturn this.play();\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this._faderId !== null) {\n\t\t\t\tclearInterval(this._faderId);\n\t\t\t\tthis._faderId = null;\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this.audio.loop;\n\t\t\t}\n\n\t\t\tthis.audio.loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\t\t\tthis._updateAudioMute();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioMute() {\n\t\t\tthis.audio.muted = this._mute || _masterMute;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tClamp the playback rate to sane values—some browsers also do this to varying degrees.\n\n\t\t\t\tNOTE (ca. Aug 2016): The specification allows negative values for reverse playback,\n\t\t\t\thowever, most browsers either completely ignore negative values or clamp them to\n\t\t\t\tsome positive value.  In some (notably, IE & Edge), setting a negative playback\n\t\t\t\trate breaks the associated controls, if displayed.\n\t\t\t*/\n\t\t\t/*\n\t\t\tthis._rate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\t\tthis._updateAudioRate();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioRate() {\n\t\t\t/*\n\t\t\tconst rate = this._rate * _masterRate;\n\t\t\tthis.audio.playbackRate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis.audio.playbackRate = Math.clamp(this._rate * _masterRate, 0.2, 5); // clamp to 5× slower & faster\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\treturn this.audio.currentTime;\n\t\t\t}\n\n\t\t\tif (typeof time !== 'number') {\n\t\t\t\tthrow new TypeError('time parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE (historic): If we try to modify the audio clip's `.currentTime` property\n\t\t\t\tbefore its metadata has been loaded, it will throw an `InvalidStateError`\n\t\t\t\t(since it doesn't know its duration, allowing `.currentTime` to be set would\n\t\t\t\tbe undefined behavior), so in case an exception is thrown we provide a fallback\n\t\t\t\tusing the `loadedmetadata` event.\n\n\t\t\t\tNOTE (ca. 2016): This workaround should no longer be necessary in most browsers.\n\t\t\t\tThat said, it will still be required for some time to service legacy browsers.\n\n\t\t\t\tNOTE (ca. Dec 09, 2018): Firefox will still log an `InvalidStateError` to the\n\t\t\t\tconsole when attempting to modify the clip's `.currentTime` property before its\n\t\t\t\tmetadata has been loaded, even though it handles the situation properly—by waiting\n\t\t\t\tfor the metadata, as all browsers do now.  To prevent this spurious logging, we\n\t\t\t\tmust now manually check for the existence of the metadata and always failover to\n\t\t\t\tan event regardless of if the browser needs it or not—because I don't want to\n\t\t\t\tintroduce a browser check here.  Stay classy, Firefox.\n\t\t\t*/\n\t\t\tif (this.hasMetadata()) {\n\t\t\t\tthis.audio.currentTime = time;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t.off('loadedmetadata.AudioTrack_time')\n\t\t\t\t\t.one('loadedmetadata.AudioTrack_time', () => this.audio.currentTime = time);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\t\tthis._updateAudioVolume();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioVolume() {\n\t\t\tthis.audio.volume = Math.clamp(this._volume * _masterVolume, 0, 1);\n\t\t}\n\n\t\tduration() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration;\n\t\t}\n\n\t\tremaining() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration - this.audio.currentTime;\n\t\t}\n\n\t\tisFailed() {\n\t\t\treturn this._error;\n\t\t}\n\n\t\tisLoading() {\n\t\t\treturn this.audio.networkState === HTMLMediaElement.NETWORK_LOADING;\n\t\t}\n\n\t\tisUnloaded() {\n\t\t\treturn !this.audio.hasChildNodes();\n\t\t}\n\n\t\tisUnavailable() {\n\t\t\treturn !this.hasSource() || this.isUnloaded() || this.isFailed();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\t// NOTE: The `this.hasSomeData()` check is probably no longer necessary.\n\t\t\treturn !this.audio.paused && this.hasSomeData();\n\t\t}\n\n\t\tisPaused() {\n\t\t\t/*\n\t\t\t\tIf the selected audio resource is a stream, `currentTime` may return a non-zero\n\t\t\t\tvalue even at the earliest available position within the stream as the browser\n\t\t\t\tmay have dropped the earliest chunks of buffered data or the stream may have a\n\t\t\t\ttimeline which does not start at zero.\n\n\t\t\t\tIn an attempt to guard against these possiblities, as best as we can, we test\n\t\t\t\t`duration` against `Infinity` first, which should yield true for actual streams.\n\t\t\t*/\n\t\t\treturn this.audio.paused\n\t\t\t\t&& (this.audio.duration === Infinity || this.audio.currentTime > 0)\n\t\t\t\t&& !this.audio.ended;\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.audio.paused && this.audio.currentTime === 0;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.audio.ended;\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this._faderId !== null;\n\t\t}\n\n\t\tisSeeking() {\n\t\t\treturn this.audio.seeking;\n\t\t}\n\n\t\thasSource() {\n\t\t\treturn this.sources.length > 0;\n\t\t}\n\n\t\thasNoData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_NOTHING;\n\t\t}\n\n\t\thasMetadata() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_METADATA;\n\t\t}\n\n\t\thasSomeData() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA;\n\t\t}\n\n\t\thasData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tjQuery.fn.on.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tjQuery.fn.one.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tjQuery.fn.off.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\t/*\n\t\t\tVerifies that the browser supports the given MIME-type and then retuns either\n\t\t\tthe MIME-type, if it is supported, or `null`, if it is not.\n\t\t*/\n\t\tstatic _verifyType(type) {\n\t\t\tif (!type || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cache = AudioTrack._types;\n\n\t\t\tif (!cache.hasOwnProperty(type)) {\n\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t// Some early implementations return 'no' instead of the empty string.\n\t\t\t\tcache[type] = audio.canPlayType(type).replace(/^no$/i, '') !== '';\n\t\t\t}\n\n\t\t\treturn cache[type] ? type : null;\n\t\t}\n\n\t\t/*\n\t\t\tRetuns the MIME-type associated with the given format-ID, if it is supported,\n\t\t\telsewise `null`.\n\t\t*/\n\t\tstatic getType(format) {\n\t\t\tif (!format || !Has.audio) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst known = AudioTrack.formats;\n\t\t\tconst id    = format.toLowerCase();\n\t\t\tconst type  = known.hasOwnProperty(id) ? known[id] : `audio/${id}`;\n\n\t\t\treturn AudioTrack._verifyType(type);\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a format.\n\t\t*/\n\t\tstatic canPlayFormat(format) {\n\t\t\treturn AudioTrack.getType(format) !== null;\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the browser potentially supports a MIME-type.\n\t\t*/\n\t\tstatic canPlayType(type) {\n\t\t\treturn AudioTrack._verifyType(type) !== null;\n\t\t}\n\t}\n\n\t// Attach the static data members.\n\tObject.defineProperties(AudioTrack, {\n\t\t/*\n\t\t\tFormat-ID to MIME-type mappings for common audio types.\n\n\t\t\tIn most cases, the codecs property should not be included with the MIME-type,\n\t\t\tas we have no way of knowing which codec was used—and the browser will figure\n\t\t\tit out.  Conversely, in cases where the relationship relationship between a\n\t\t\tformat-ID and a specific codec is strong, we should include the codecs property.\n\n\t\t\tNOTE: Caveats by browser/engine:\n\t\t\t\tOpera ≤12 (Presto) will return a false-negative if the codecs value is quoted\n\t\t\t\twith single quotes, requiring the use of either double quotes or no quotes.\n\n\t\t\t\tBlink-based browsers (e.g. Chrome, Opera ≥15) will return a false-negative\n\t\t\t\tfor WAVE audio if the preferred MIME-type of 'audio/wave' is specified,\n\t\t\t\trequiring the use of 'audio/wav' instead.\n\t\t*/\n\t\tformats : {\n\t\t\tvalue : { // Leave this object extensible for users.\n\t\t\t\t// AAC — MPEG-2 AAC audio; specific profiles vary, but commonly \"AAC-LC\".\n\t\t\t\taac : 'audio/aac',\n\n\t\t\t\t// CAF — Codecs vary.\n\t\t\t\tcaf     : 'audio/x-caf',\n\t\t\t\t'x-caf' : 'audio/x-caf',\n\n\t\t\t\t// MP3 — MPEG-1/-2 Layer-III audio.\n\t\t\t\tmp3  : 'audio/mpeg; codecs=\"mp3\"',\n\t\t\t\tmpeg : 'audio/mpeg; codecs=\"mp3\"',\n\n\t\t\t\t// MP4 — Codecs vary, but commonly \"mp4a.40.2\" (a.k.a. \"AAC-LC\").\n\t\t\t\tm4a     : 'audio/mp4',\n\t\t\t\tmp4     : 'audio/mp4',\n\t\t\t\t'x-m4a' : 'audio/mp4',\n\t\t\t\t'x-mp4' : 'audio/mp4',\n\n\t\t\t\t// OGG — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\toga : 'audio/ogg',\n\t\t\t\togg : 'audio/ogg',\n\n\t\t\t\t// OPUS — Opus audio in an Ogg container.\n\t\t\t\topus : 'audio/ogg; codecs=\"opus\"',\n\n\t\t\t\t// WAVE — Codecs vary, but commonly \"1\" (1 is the FourCC for PCM/LPCM).\n\t\t\t\twav  : 'audio/wav',\n\t\t\t\twave : 'audio/wav',\n\n\t\t\t\t// WEBM — Codecs vary, but commonly \"vorbis\" and, recently, \"opus\".\n\t\t\t\tweba : 'audio/webm',\n\t\t\t\twebm : 'audio/webm'\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tCache of supported MIME-types.\n\t\t*/\n\t\t_types : {\n\t\t\tvalue : {}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudioList Class.\n\t*******************************************************************************************************************/\n\tclass AudioList {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of track objects or AudioList object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioList) {\n\t\t\t\tthis._copy(obj);\n\t\t\t\t// this._create(obj.tracks);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('tracks parameter must be either an array, of track objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(trackList) {\n\t\t\t// Map the array of tracks to playlist track objects.\n\t\t\tthis._finalize(trackList.map(trackObj => {\n\t\t\t\tif (typeof trackObj !== 'object') { // lazy equality for null\n\t\t\t\t\tthrow new Error('tracks parameter array members must be objects');\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\tlet rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (trackObj instanceof AudioTrack) {\n\t\t\t\t\town    = true;\n\t\t\t\t\trate   = trackObj.rate();\n\t\t\t\t\ttrack  = trackObj.clone();\n\t\t\t\t\tvolume = trackObj.volume();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (!trackObj.hasOwnProperty('track')) {\n\t\t\t\t\t\tthrow new Error('track object missing required \"track\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (!(trackObj.track instanceof AudioTrack)) {\n\t\t\t\t\t\tthrow new Error('track object\\'s \"track\" property must be an AudioTrack object');\n\t\t\t\t\t}\n\t\t\t\t\t// else if (!trackObj.hasOwnProperty('volume')) {\n\t\t\t\t\t// \tthrow new Error('track object missing required \"volume\" property');\n\t\t\t\t\t// }\n\n\t\t\t\t\town    = trackObj.hasOwnProperty('own') && trackObj.own;\n\t\t\t\t\trate   = trackObj.hasOwnProperty('rate') ? trackObj.rate : trackObj.track.rate();\n\t\t\t\t\ttrack  = trackObj.track;\n\t\t\t\t\tvolume = trackObj.hasOwnProperty('volume') ? trackObj.volume : trackObj.track.volume();\n\t\t\t\t}\n\n\t\t\t\ttrack.stop();\n\t\t\t\ttrack.loop(false);\n\t\t\t\ttrack.mute(false);\n\t\t\t\ttrack.rate(rate);\n\t\t\t\ttrack.volume(volume);\n\t\t\t\ttrack.on('ended.AudioList', () => this._onEnd());\n\n\t\t\t\treturn { own, track, volume, rate };\n\t\t\t}));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(clone(obj.tracks));\n\t\t}\n\n\t\t_finalize(tracks) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttracks : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : Object.freeze(tracks)\n\t\t\t\t},\n\n\t\t\t\tqueue : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : []\n\t\t\t\t},\n\n\t\t\t\tcurrent : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_loop : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_shuffle : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.\n\t\t\t*/\n\t\t\t// Stop playback.\n\t\t\tthis.stop();\n\n\t\t\t// Destroy all owned tracks.\n\t\t\tthis.tracks\n\t\t\t\t.filter(trackObj => trackObj.own)\n\t\t\t\t.forEach(trackObj => trackObj.track._destroy());\n\n\t\t\t// Delete the reference-type properties.\n\t\t\tdelete this.tracks;\n\t\t\tdelete this.queue;\n\t\t}\n\n\t\tload() {\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.load());\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.stop();\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.unload());\n\t\t}\n\n\t\tplay() {\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (this.queue.length === 0) {\n\t\t\t\t\tthis._fillQueue();\n\t\t\t\t}\n\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn Promise.reject(new Error('no tracks were available'));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.current.track.play();\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioList_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioList_playWhenAllowed');\n\t\t\t\t\tthis.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.pause();\n\t\t\t}\n\t\t}\n\n\t\tstop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tthis._drainQueue();\n\t\t}\n\n\t\tskip() {\n\t\t\tif (this._next()) {\n\t\t\t\tthis.current.track.play();\n\t\t\t}\n\t\t\telse if (this._loop) {\n\t\t\t\tthis.play();\n\t\t\t}\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adjToVol = Math.clamp(toVol, 0, 1) * this.current.volume;\n\t\t\tlet adjFromVol;\n\n\t\t\tif (fromVol != null) { // lazy equality for null\n\t\t\t\tadjFromVol = Math.clamp(fromVol, 0, 1) * this.current.volume;\n\t\t\t}\n\n\t\t\tthis._volume = toVol; // NOTE: Kludgey, but necessary.\n\n\t\t\treturn this.current.track.fade(duration, adjToVol, adjFromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.fadeStop();\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this._loop;\n\t\t\t}\n\n\t\t\tthis._loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.mute(this._mute);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tshuffle(shuffle) {\n\t\t\tif (shuffle == null) { // lazy equality for null\n\t\t\t\treturn this._shuffle;\n\t\t\t}\n\n\t\t\tthis._shuffle = !!shuffle;\n\n\t\t\tif (this.queue.length > 0) {\n\t\t\t\tthis._fillQueue();\n\n\t\t\t\t// Try not to immediately replay the last track when not shuffling.\n\t\t\t\tif (!this._shuffle && this.current !== null && this.queue.length > 1) {\n\t\t\t\t\tconst firstIdx = this.queue.findIndex(trackObj => trackObj === this.current);\n\n\t\t\t\t\tif (firstIdx !== -1) {\n\t\t\t\t\t\tthis.queue.push(...this.queue.splice(0, firstIdx + 1));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tduration() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('duration takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.tracks\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\t\t}\n\n\t\tremaining() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('remaining takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\tlet remainingTime = this.queue\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tremainingTime += this.current.track.remaining();\n\t\t\t}\n\n\t\t\treturn remainingTime;\n\t\t}\n\n\t\ttime() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('time takes no parameters');\n\t\t\t}\n\n\t\t\treturn this.duration() - this.remaining();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\treturn this.current !== null && this.current.track.isPlaying();\n\t\t}\n\n\t\tisPaused() {\n\t\t\treturn this.current === null || this.current.track.isPaused();\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.queue.length === 0 && this.current === null;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.queue.length === 0 && (this.current === null || this.current.track.isEnded());\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this.current !== null && this.current.track.isFading();\n\t\t}\n\n\t\t_next() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tlet nextTrack;\n\n\t\t\twhile ((nextTrack = this.queue.shift())) {\n\t\t\t\tif (!nextTrack.track.isUnavailable()) {\n\t\t\t\t\tthis.current = nextTrack;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.current === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.current.track.mute(this._mute);\n\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\n\t\t\t// Attempt to protect against the `loop` state being reenabled\n\t\t\t// outside of the playlist.  Mostly for unowned tracks.\n\t\t\t//\n\t\t\t// TODO: Should we reapply the `ended` event handler too?\n\t\t\tthis.current.track.loop(false);\n\n\t\t\treturn true;\n\t\t}\n\n\t\t_onEnd() {\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tif (!this._loop) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (!this._next()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.current.track.play();\n\t\t}\n\n\t\t_drainQueue() {\n\t\t\tthis.queue.splice(0);\n\t\t}\n\n\t\t_fillQueue() {\n\t\t\tthis._drainQueue();\n\t\t\tthis.queue.push(...this.tracks.filter(trackObj => !trackObj.track.isUnavailable()));\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this._shuffle) {\n\t\t\t\tthis.queue.shuffle();\n\n\t\t\t\t// Try not to immediately replay the last track when shuffling.\n\t\t\t\tif (this.queue.length > 1 && this.queue[0] === this.current) {\n\t\t\t\t\tthis.queue.push(this.queue.shift());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAudioRunner Class.\n\t*******************************************************************************************************************/\n\tclass AudioRunner {\n\t\tconstructor(list) {\n\t\t\tif (!(list instanceof Set || list instanceof AudioRunner)) {\n\t\t\t\tthrow new TypeError('list parameter must be a Set or a AudioRunner instance');\n\t\t\t}\n\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttrackIds : {\n\t\t\t\t\tvalue : new Set(list instanceof AudioRunner ? list.trackIds : list)\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.load);\n\t\t}\n\n\t\tunload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.unload);\n\t\t}\n\n\t\tplay() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.play);\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.playWhenAllowed);\n\t\t}\n\n\t\tpause() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.pause);\n\t\t}\n\n\t\tstop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.stop);\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (duration == null || toVol == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fade requires parameters');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fade, duration, toVol, fromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeIn requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeIn, duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeOut requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeOut, duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeStop);\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\tthrow new Error('loop requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.loop, loop);\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\tthrow new Error('mute requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.mute, mute);\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\tthrow new Error('rate requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.rate, rate);\n\t\t\treturn this;\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\tthrow new Error('time requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.time, time);\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\tthrow new Error('volume requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.volume, volume);\n\t\t\treturn this;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.on, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.one, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.off, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tstatic _run(ids, fn, ...args) {\n\t\t\tids.forEach(id => {\n\t\t\t\tconst track = _tracks.get(id);\n\n\t\t\t\tif (track) {\n\t\t\t\t\tfn.apply(track, args);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTrack Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.tracks.add(trackId, sources…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.tracks.add(\n\t\t\t\t'over_the_top',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.mp3',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.ogg'\n\t\t\t);\n\t*/\n\tfunction trackAdd(/* trackId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('track ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('sources'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `track ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\tthrow new Error(`invalid ${what}: track IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst sources = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet track;\n\n\t\ttry {\n\t\t\ttrack = _newTrack(sources);\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during track initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If in Test Mode and no supported sources were specified, throw an error.\n\t\tif (Config.debug && !track.hasSource()) {\n\t\t\tthrow new Error(`${what}: no supported audio sources found`);\n\t\t}\n\n\t\t// If a track by the given ID already exists, destroy it.\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// Add the track to the cache.\n\t\t_tracks.set(id, track);\n\t}\n\n\tfunction trackDelete(id) {\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// TODO: Should this also remove references to the track from groups and playlists?\n\n\t\treturn _tracks.delete(id);\n\t}\n\n\tfunction trackClear() {\n\t\t_tracks.forEach(track => track._destroy());\n\t\t_tracks.clear();\n\t}\n\n\tfunction trackHas(id) {\n\t\treturn _tracks.has(id);\n\t}\n\n\tfunction trackGet(id) {\n\t\treturn _tracks.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tGroup Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.groups.add(groupId, trackIds…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.groups.add(':ui', 'beep', 'boop', 'boing');\n\t*/\n\tfunction groupAdd(/* groupId , trackIds… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('group ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `group ID \"${id}\"`;\n\n\t\tif (id[0] !== ':' || _badIdRe.test(id.slice(1))) {\n\t\t\tthrow new Error(`invalid ${what}: group IDs must start with a colon and must not contain colons or whitespace`);\n\t\t}\n\n\t\tif (_specialIds.includes(id)) {\n\t\t\tthrow new Error(`cannot clobber special ${what}`);\n\t\t}\n\n\t\tconst trackIds = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet group;\n\n\t\ttry {\n\t\t\tgroup = new Set(trackIds.map(trackId => {\n\t\t\t\tif (!_tracks.has(trackId)) {\n\t\t\t\t\tthrow new Error(`track \"${trackId}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\treturn trackId;\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during group initialization: ${ex.message}`);\n\t\t}\n\n\t\t// Add the group to the cache.\n\t\t_groups.set(id, Object.freeze(Array.from(group)));\n\t}\n\n\tfunction groupDelete(id) {\n\t\treturn _groups.delete(id);\n\t}\n\n\tfunction groupClear() {\n\t\t_groups.clear();\n\t}\n\n\tfunction groupHas(id) {\n\t\treturn _groups.has(id);\n\t}\n\n\tfunction groupGet(id) {\n\t\treturn _groups.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPlaylist Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.lists.add(listId, sources…);\n\t\t\tWhere `sources` may be either a track ID or descriptor (object).\n\t\t\tTrack descriptors are either { id, [own], [rate], [volume] } or { sources, [rate], [volume] }.\n\n\t\tNOTE: Rate properties are currently unsupported due to poor browser support.\n\n\t\tE.g.\n\t\t\tSimpleAudio.lists.add(\n\t\t\t\t'bgm',\n\t\t\t\t'over_the_top',\n\t\t\t\t{\n\t\t\t\t\tid     : 'heavens_a_lie',\n\t\t\t\t\tvolume : 0.5,\n\t\t\t\t\town    : true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tsources : [\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.mp3',\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.ogg'\n\t\t\t\t\t],\n\t\t\t\t\tvolume  : 0.75\n\t\t\t\t}\n\t\t\t);\n\t*/\n\tfunction listAdd(/* listId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('list ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `list ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\treturn this.error(`invalid ${what}: list IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst descriptors = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet list;\n\n\t\ttry {\n\t\t\tlist = new AudioList(descriptors.map(desc => {\n\t\t\t\tif (desc === null) {\n\t\t\t\t\tthrow new Error('track descriptor must be a string or object (type: null)');\n\t\t\t\t}\n\n\t\t\t\tswitch (typeof desc) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t// Simply a track ID, so convert it into an object.\n\t\t\t\t\tdesc = { id : desc }; // eslint-disable-line no-param-reassign\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (!desc.hasOwnProperty('id') && !desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain one of either an \"id\" or a \"sources\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (desc.hasOwnProperty('id') && desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain either an \"id\" or a \"sources\" property, not both');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`track descriptor must be a string or object (type: ${typeof desc})`);\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\t// let rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (desc.hasOwnProperty('id')) {\n\t\t\t\t\tif (typeof desc.id !== 'string') {\n\t\t\t\t\t\tthrow new Error('\"id\" property must be a string');\n\t\t\t\t\t}\n\t\t\t\t\tif (!_tracks.has(desc.id)) {\n\t\t\t\t\t\tthrow new Error(`track \"${desc.id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\ttrack = _tracks.get(desc.id);\n\t\t\t\t}\n\t\t\t\telse if (desc.hasOwnProperty('sources')) {\n\t\t\t\t\tif (!Array.isArray(desc.sources) || desc.sources.length === 0) {\n\t\t\t\t\t\tthrow new Error('\"sources\" property must be a non-empty array');\n\t\t\t\t\t}\n\t\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\t\tthrow new Error('\"own\" property is not allowed with the \"sources\" property');\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttrack = _newTrack(desc.sources);\n\t\t\t\t\t\town = true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`error during track initialization: ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\t\tif (Config.debug && !track.hasSource()) {\n\t\t\t\t\t\tthrow new Error('no supported audio sources found');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\tif (typeof desc.own !== 'boolean') {\n\t\t\t\t\t\tthrow new Error('\"own\" property must be a boolean');\n\t\t\t\t\t}\n\n\t\t\t\t\town = desc.own;\n\n\t\t\t\t\tif (own) {\n\t\t\t\t\t\ttrack = track.clone();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if (desc.hasOwnProperty('rate')) {\n\t\t\t\t// \tif (\n\t\t\t\t// \t\t   typeof desc.rate !== 'number'\n\t\t\t\t// \t\t|| Number.isNaN(desc.rate)\n\t\t\t\t// \t\t|| !Number.isFinite(desc.rate)\n\t\t\t\t// \t) {\n\t\t\t\t// \t\tthrow new Error('\"rate\" property must be a finite number');\n\t\t\t\t// \t}\n\t\t\t\t//\n\t\t\t\t// \trate = desc.rate;\n\t\t\t\t// }\n\n\t\t\t\tif (desc.hasOwnProperty('volume')) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t   typeof desc.volume !== 'number'\n\t\t\t\t\t\t|| Number.isNaN(desc.volume)\n\t\t\t\t\t\t|| !Number.isFinite(desc.volume)\n\t\t\t\t\t\t|| desc.volume < 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error('\"volume\" property must be a non-negative finite number');\n\t\t\t\t\t}\n\n\t\t\t\t\tvolume = desc.volume;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\town    : own != null ? own : false, // lazy equality for null,\n\t\t\t\t\t// rate   : rate != null ? rate : track.rate(), // lazy equality for null,\n\t\t\t\t\ttrack,\n\t\t\t\t\tvolume : volume != null ? volume : track.volume() // lazy equality for null\n\t\t\t\t};\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during playlist initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If a playlist by the given ID already exists, destroy it.\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\t// Add the playlist to the cache.\n\t\t_lists.set(id, list);\n\t}\n\n\tfunction listDelete(id) {\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\treturn _lists.delete(id);\n\t}\n\n\tfunction listClear() {\n\t\t_lists.forEach(list => list._destroy());\n\t\t_lists.clear();\n\t}\n\n\tfunction listHas(id) {\n\t\treturn _lists.has(id);\n\t}\n\n\tfunction listGet(id) {\n\t\treturn _lists.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tRunner Functions.\n\t*******************************************************************************************************************/\n\tconst _runnerParseSelector = (() => {\n\t\tconst notWsRe = /\\S/g;\n\t\tconst parenRe = /[()]/g;\n\n\t\tfunction processNegation(str, startPos) {\n\t\t\tlet match;\n\n\t\t\tnotWsRe.lastIndex = startPos;\n\t\t\tmatch = notWsRe.exec(str);\n\n\t\t\tif (match === null || match[0] !== '(') {\n\t\t\t\tthrow new Error('invalid \":not()\" syntax: missing parentheticals');\n\t\t\t}\n\n\t\t\tparenRe.lastIndex = notWsRe.lastIndex;\n\t\t\tconst start  = notWsRe.lastIndex;\n\t\t\tconst result = { str : '', nextMatch : -1 };\n\t\t\tlet depth = 1;\n\n\t\t\twhile ((match = parenRe.exec(str)) !== null) {\n\t\t\t\tif (match[0] === '(') {\n\t\t\t\t\t++depth;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t--depth;\n\t\t\t\t}\n\n\t\t\t\tif (depth < 1) {\n\t\t\t\t\tresult.nextMatch = parenRe.lastIndex;\n\t\t\t\t\tresult.str = str.slice(start, result.nextMatch - 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tfunction parseSelector(idArg) {\n\t\t\tconst ids  = [];\n\t\t\tconst idRe = /:?[^\\s:()]+/g;\n\t\t\tlet match;\n\n\t\t\twhile ((match = idRe.exec(idArg)) !== null) {\n\t\t\t\tconst id = match[0];\n\n\t\t\t\t// Group negation.\n\t\t\t\tif (id === ':not') {\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error('invalid negation: no group ID preceded \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst parent = ids[ids.length - 1];\n\n\t\t\t\t\tif (parent.id[0] !== ':') {\n\t\t\t\t\t\tthrow new Error(`invalid negation of track \"${parent.id}\": only groups may be negated with \":not()\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst negation = processNegation(idArg, idRe.lastIndex);\n\n\t\t\t\t\tif (negation.nextMatch === -1) {\n\t\t\t\t\t\tthrow new Error('unknown error parsing \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tidRe.lastIndex = negation.nextMatch;\n\t\t\t\t\tparent.not = parseSelector(negation.str);\n\t\t\t\t}\n\n\t\t\t\t// Group or track ID.\n\t\t\t\telse {\n\t\t\t\t\tids.push({ id });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ids;\n\t\t}\n\n\t\treturn parseSelector;\n\t})();\n\n\t/*\n\t\tSimpleAudio.select(selector).…;\n\n\t\tE.g.\n\t\t\tSimpleAudio.select(':ui').…\n\t\t\tSimpleAudio.select(':ui:not(boop)').…\n\t\t\tSimpleAudio.select('boop beep').…\n\t\t\tSimpleAudio.select(':ui :sfx').…\n\t\t\tSimpleAudio.select(':ui:not(boop) :sfx overthetop').…\n\t*/\n\tfunction runnerGet(/* selector */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('no track selector specified');\n\t\t}\n\n\t\tconst selector = String(arguments[0]).trim();\n\t\tconst trackIds = new Set();\n\n\t\ttry {\n\t\t\tconst allIds = Array.from(_tracks.keys());\n\n\t\t\tfunction renderIds(idObj) {\n\t\t\t\tconst id = idObj.id;\n\t\t\t\tlet ids;\n\n\t\t\t\tswitch (id) {\n\t\t\t\tcase ':all':     ids = allIds; break;\n\t\t\t\tcase ':looped':  ids = allIds.filter(id => _tracks.get(id).loop()); break;\n\t\t\t\tcase ':muted':   ids = allIds.filter(id => _tracks.get(id).mute()); break;\n\t\t\t\tcase ':paused':  ids = allIds.filter(id => _tracks.get(id).isPaused()); break;\n\t\t\t\tcase ':playing': ids = allIds.filter(id => _tracks.get(id).isPlaying()); break;\n\t\t\t\tdefault:         ids = id[0] === ':' ? _groups.get(id) : [id]; break;\n\t\t\t\t}\n\n\t\t\t\tif (idObj.hasOwnProperty('not')) {\n\t\t\t\t\tconst negated = idObj.not.map(idObj => renderIds(idObj)).flat(Infinity);\n\t\t\t\t\tids = ids.filter(id => !negated.includes(id));\n\t\t\t\t}\n\n\t\t\t\treturn ids;\n\t\t\t}\n\n\t\t\t_runnerParseSelector(selector).forEach(idObj => renderIds(idObj).forEach(id => {\n\t\t\t\tif (!_tracks.has(id)) {\n\t\t\t\t\tthrow new Error(`track \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\ttrackIds.add(id);\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`error during runner initialization: ${ex.message}`);\n\t\t}\n\n\t\treturn new AudioRunner(trackIds);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMaster Audio Functions.\n\t*******************************************************************************************************************/\n\tfunction masterLoad() {\n\t\tpublish('load');\n\t}\n\n\tfunction masterLoadWithScreen() {\n\t\tpublish('loadwithscreen');\n\t}\n\n\tfunction masterMute(mute) {\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMute;\n\t\t}\n\n\t\t_masterMute = !!mute;\n\t\tpublish('mute', _masterMute);\n\t}\n\n\tfunction masterMuteOnHidden(mute) {\n\t\t// NOTE: Some older browsers—notably: IE 9—do not support the Page Visibility API.\n\t\tif (!Visibility.isEnabled()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMuteOnHidden;\n\t\t}\n\n\t\t_masterMuteOnHidden = !!mute;\n\n\t\tconst namespace = '.SimpleAudio_masterMuteOnHidden';\n\n\t\tif (_masterMuteOnHidden) {\n\t\t\tconst visibilityChange = `${Visibility.changeEvent}${namespace}`;\n\t\t\tjQuery(document)\n\t\t\t\t.off(namespace)\n\t\t\t\t.on(visibilityChange, () => masterMute(Visibility.isHidden()));\n\n\t\t\t// Only change the mute state initially if hidden.\n\t\t\tif (Visibility.isHidden()) {\n\t\t\t\tmasterMute(true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tjQuery(document).off(namespace);\n\t\t}\n\t}\n\n\tfunction masterRate(rate) {\n\t\tif (rate == null) { // lazy equality for null\n\t\t\treturn _masterRate;\n\t\t}\n\n\t\tif (typeof rate !== 'number' || Number.isNaN(rate) || !Number.isFinite(rate)) {\n\t\t\tthrow new Error('rate must be a finite number');\n\t\t}\n\n\t\t_masterRate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\tpublish('rate', _masterRate);\n\t}\n\n\tfunction masterStop() {\n\t\tpublish('stop');\n\t}\n\n\tfunction masterUnload() {\n\t\tpublish('unload');\n\t}\n\n\tfunction masterVolume(volume) {\n\t\tif (volume == null) { // lazy equality for null\n\t\t\treturn _masterVolume;\n\t\t}\n\n\t\tif (typeof volume !== 'number' || Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\tthrow new Error('volume must be a finite number');\n\t\t}\n\n\t\t_masterVolume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\tpublish('volume', _masterVolume);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSubscription Functions.\n\t*******************************************************************************************************************/\n\tfunction subscribe(id, callback) {\n\t\tif (typeof callback !== 'function') {\n\t\t\tthrow new Error('callback parameter must be a function');\n\t\t}\n\n\t\t_subscribers.set(id, callback);\n\t}\n\n\tfunction unsubscribe(id) {\n\t\t_subscribers.delete(id);\n\t}\n\n\tfunction publish(mesg, data) {\n\t\t_subscribers.forEach(fn => fn(mesg, data));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _newTrack(sources) {\n\t\treturn new AudioTrack(sources.map(source => {\n\t\t\t// Handle audio passages.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.audio')) {\n\t\t\t\t\treturn passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle URIs—possibly prefixed with a format specifier.\n\t\t\tconst match = _formatSpecRe.exec(source);\n\t\t\treturn match === null ? source : {\n\t\t\t\tformat : match[1],\n\t\t\t\tsrc    : match[2]\n\t\t\t};\n\t\t}));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Track Functions.\n\t\ttracks : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : trackAdd },\n\t\t\t\tdelete : { value : trackDelete },\n\t\t\t\tclear  : { value : trackClear },\n\t\t\t\thas    : { value : trackHas },\n\t\t\t\tget    : { value : trackGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Group Functions.\n\t\tgroups : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : groupAdd },\n\t\t\t\tdelete : { value : groupDelete },\n\t\t\t\tclear  : { value : groupClear },\n\t\t\t\thas    : { value : groupHas },\n\t\t\t\tget    : { value : groupGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Playlist Functions.\n\t\tlists : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : listAdd },\n\t\t\t\tdelete : { value : listDelete },\n\t\t\t\tclear  : { value : listClear },\n\t\t\t\thas    : { value : listHas },\n\t\t\t\tget    : { value : listGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Runner Functions.\n\t\tselect : { value : runnerGet },\n\n\t\t// Master Audio Functions.\n\t\tload           : { value : masterLoad },\n\t\tloadWithScreen : { value : masterLoadWithScreen },\n\t\tmute           : { value : masterMute },\n\t\tmuteOnHidden   : { value : masterMuteOnHidden },\n\t\trate           : { value : masterRate },\n\t\tstop           : { value : masterStop },\n\t\tunload         : { value : masterUnload },\n\t\tvolume         : { value : masterVolume }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstate.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Diff, Engine, PRNGWrapper, Scripting, clone, session, storage */\n\nvar State = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// History moment stack.\n\tlet _history = [];\n\n\t// Currently active/played moment.\n\tlet _active = momentCreate();\n\n\t// Currently active/played moment index.\n\tlet _activeIndex = -1;\n\n\t// Titles of all moments which have expired (i.e. fallen off the bottom of the stack).\n\tlet _expired = [];\n\n\t// (optional) Seedable PRNG object.\n\tlet _prng = null;\n\n\t// Temporary variables object.\n\tlet _tempVariables = {};\n\n\n\t/*******************************************************************************************************************\n\t\tState Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tResets the story state.\n\t*/\n\tfunction stateReset() {\n\t\tif (DEBUG) { console.log('[State/stateReset()]'); }\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tsession.delete('state');\n\n\t\t/*\n\t\t\tReset the properties.\n\t\t*/\n\t\t_history     = [];\n\t\t_active      = momentCreate();\n\t\t_activeIndex = -1;\n\t\t_expired     = [];\n\t\t_prng        = _prng === null ? null : new PRNGWrapper(_prng.seed, false);\n\t}\n\n\t/*\n\t\tRestores the story state from the active session.\n\t*/\n\tfunction stateRestore() {\n\t\tif (DEBUG) { console.log('[State/stateRestore()]'); }\n\n\t\t/*\n\t\t\tAttempt to restore an active session.\n\t\t*/\n\t\tif (session.has('state')) {\n\t\t\t/*\n\t\t\t\tRetrieve the session.\n\t\t\t*/\n\t\t\tconst stateObj = session.get('state');\n\n\t\t\tif (DEBUG) { console.log('\\tsession state:', stateObj); }\n\n\t\t\tif (stateObj == null) { // lazy equality for null\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tRestore the session.\n\t\t\t*/\n\t\t\tstateUnmarshal(stateObj);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a serializable object.\n\t*/\n\tfunction stateMarshal(noDelta) {\n\t\t/*\n\t\t\tGather the properties.\n\t\t*/\n\t\tconst stateObj = {\n\t\t\tindex : _activeIndex\n\t\t};\n\n\t\tif (noDelta) {\n\t\t\tstateObj.history = clone(_history);\n\t\t}\n\t\telse {\n\t\t\tstateObj.delta = historyDeltaEncode(_history);\n\t\t}\n\n\t\tif (_expired.length > 0) {\n\t\t\tstateObj.expired = [];\n\t\t}\n\n\t\tif (_prng !== null) {\n\t\t\tstateObj.seed = _prng.seed;\n\t\t}\n\n\t\treturn stateObj;\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled story state serialization object.\n\t*/\n\tfunction stateUnmarshal(stateObj, noDelta) {\n\t\tif (stateObj == null) { // lazy equality for null\n\t\t\tthrow new Error('state object is null or undefined');\n\t\t}\n\n\t\tif (\n\t\t\t   !stateObj.hasOwnProperty(noDelta ? 'history' : 'delta')\n\t\t\t|| stateObj[noDelta ? 'history' : 'delta'].length === 0\n\t\t) {\n\t\t\tthrow new Error('state object has no history or history is empty');\n\t\t}\n\n\t\tif (!stateObj.hasOwnProperty('index')) {\n\t\t\tthrow new Error('state object has no index');\n\t\t}\n\n\t\tif (_prng !== null && !stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has no seed, but PRNG is enabled');\n\t\t}\n\n\t\tif (_prng === null && stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has seed, but PRNG is disabled');\n\t\t}\n\n\t\t/*\n\t\t\tRestore the properties.\n\t\t*/\n\t\t_history     = noDelta ? clone(stateObj.history) : historyDeltaDecode(stateObj.delta);\n\t\t_activeIndex = stateObj.index;\n\t\t_expired     = stateObj.hasOwnProperty('expired') ? [...stateObj.expired] : [];\n\n\t\tif (stateObj.hasOwnProperty('seed')) {\n\t\t\t/*\n\t\t\t\tWe only need to restore the PRNG's seed here as `momentActivate()` will handle\n\t\t\t\tfully restoring the PRNG to its proper state.\n\t\t\t*/\n\t\t\t_prng.seed = stateObj.seed;\n\t\t}\n\n\t\t/*\n\t\t\tActivate the current moment (do this only after all properties have been restored).\n\t\t*/\n\t\tmomentActivate(_activeIndex);\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a save-compatible serializable object.\n\t*/\n\tfunction stateMarshalForSave() {\n\t\treturn stateMarshal(true);\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled save-compatible story state serialization object.\n\t*/\n\tfunction stateUnmarshalForSave(stateObj) {\n\t\treturn stateUnmarshal(stateObj, true);\n\t}\n\n\t/*\n\t\tReturns the titles of expired moments.\n\t*/\n\tfunction stateExpired() {\n\t\treturn _expired;\n\t}\n\n\t/*\n\t\tReturns the total number of played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTurns() {\n\t\treturn _expired.length + historyLength();\n\t}\n\n\t/*\n\t\tReturns the passage titles of all played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTitles() {\n\t\treturn _expired.concat(_history.slice(0, historyLength()).map(moment => moment.title));\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title has been played (expired + in-play history moments).\n\t*/\n\tfunction stateHasPlayed(title) {\n\t\tif (title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tif (_expired.includes(title)) {\n\t\t\treturn true;\n\t\t}\n\t\telse if (_history.slice(0, historyLength()).some(moment => moment.title === title)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMoment Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a new moment object created from the given passage title and variables object.\n\t*/\n\tfunction momentCreate(title, variables) {\n\t\treturn {\n\t\t\ttitle     : title == null ? '' : String(title), // lazy equality for null\n\t\t\tvariables : variables == null ? {} : variables   // lazy equality for null\n\t\t};\n\t}\n\n\t/*\n\t\tReturns the active (present) moment.\n\t*/\n\tfunction momentActive() {\n\t\treturn _active;\n\t}\n\n\t/*\n\t\tReturns the index within the history of the active (present) moment.\n\t*/\n\tfunction momentActiveIndex() {\n\t\treturn _activeIndex;\n\t}\n\n\t/*\n\t\tReturns the title from the active (present) moment.\n\t*/\n\tfunction momentActiveTitle() {\n\t\treturn _active.title;\n\t}\n\n\t/*\n\t\tReturns the variables from the active (present) moment.\n\t*/\n\tfunction momentActiveVariables() {\n\t\treturn _active.variables;\n\t}\n\n\t/*\n\t\tReturns the active (present) moment after setting it to either the given moment object\n\t\tor the moment object at the given history index.  Additionally, updates the active session\n\t\tand triggers a history update event.\n\t*/\n\tfunction momentActivate(moment) {\n\t\tif (moment == null) { // lazy equality for null\n\t\t\tthrow new Error('moment activation attempted with null or undefined');\n\t\t}\n\n\t\t/*\n\t\t\tSet the active moment.\n\t\t*/\n\t\tswitch (typeof moment) {\n\t\tcase 'object':\n\t\t\t_active = clone(moment);\n\t\t\tbreak;\n\n\t\tcase 'number':\n\t\t\tif (historyIsEmpty()) {\n\t\t\t\tthrow new Error('moment activation attempted with index on empty history');\n\t\t\t}\n\n\t\t\tif (moment < 0 || moment >= historySize()) {\n\t\t\t\tthrow new RangeError(`moment activation attempted with out-of-bounds index; need [0, ${historySize() - 1}], got ${moment}`);\n\t\t\t}\n\n\t\t\t_active = clone(_history[moment]);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new TypeError(`moment activation attempted with a \"${typeof moment}\"; must be an object or valid history stack index`);\n\t\t}\n\n\t\t/*\n\t\t\tRestore the seedable PRNG.\n\n\t\t\tNOTE: We cannot simply set `_prng.pull` to `_active.pull` as that would\n\t\t\tnot properly mutate the PRNG's internal state.\n\t\t*/\n\t\tif (_prng !== null) {\n\t\t\t_prng = PRNGWrapper.unmarshal({\n\t\t\t\tseed : _prng.seed,\n\t\t\t\tpull : _active.pull\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tUpdate the active session.\n\t\t*/\n\t\tsession.set('state', stateMarshal());\n\n\t\t/*\n\t\t\tTrigger a global `:historyupdate` event.\n\n\t\t\tNOTE: We do this here because setting a new active moment is a core component\n\t\t\tof, virtually, all history updates.\n\t\t*/\n\t\tjQuery.event.trigger(':historyupdate');\n\n\t\treturn _active;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHistory Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the moment history.\n\t*/\n\tfunction historyGet() {\n\t\treturn _history;\n\t}\n\n\t/*\n\t\tReturns the number of active history moments (past only).\n\t*/\n\tfunction historyLength() {\n\t\treturn _activeIndex + 1;\n\t}\n\n\t/*\n\t\tReturns the total number of history moments (past + future).\n\t*/\n\tfunction historySize() {\n\t\treturn _history.length;\n\t}\n\n\t/*\n\t\tReturns whether the history is empty.\n\t*/\n\tfunction historyIsEmpty() {\n\t\treturn _history.length === 0;\n\t}\n\n\t/*\n\t\tReturns the current (pre-play version of the active) moment within the history.\n\t*/\n\tfunction historyCurrent() {\n\t\treturn _history.length > 0 ? _history[_activeIndex] : null;\n\t}\n\n\t/*\n\t\tReturns the topmost (most recent) moment within the history.\n\t*/\n\tfunction historyTop() {\n\t\treturn _history.length > 0 ? _history[_history.length - 1] : null;\n\t}\n\n\t/*\n\t\tReturns the bottommost (least recent) moment within the history.\n\t*/\n\tfunction historyBottom() {\n\t\treturn _history.length > 0 ? _history[0] : null;\n\t}\n\n\t/*\n\t\tReturns the moment at the given index within the history.\n\t*/\n\tfunction historyIndex(index) {\n\t\tif (historyIsEmpty() || index < 0 || index > _activeIndex) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[index];\n\t}\n\n\t/*\n\t\tReturns the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyPeek(offset) {\n\t\tif (historyIsEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst lengthOffset = 1 + (offset ? Math.abs(offset) : 0);\n\n\t\tif (lengthOffset > historyLength()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[historyLength() - lengthOffset];\n\t}\n\n\t/*\n\t\tReturns whether a moment with the given title exists within the history.\n\t*/\n\tfunction historyHas(title) {\n\t\tif (historyIsEmpty() || title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = _activeIndex; i >= 0; --i) {\n\t\t\tif (_history[i].title === title) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tCreates a new moment and pushes it onto the history, discarding future moments if necessary.\n\t*/\n\tfunction historyCreate(title) {\n\t\tif (DEBUG) { console.log(`[State/historyCreate(title: \"${title}\")]`); }\n\n\t\t/*\n\t\t\tTODO: It might be good to have some assertions about the passage title here.\n\t\t*/\n\n\t\t/*\n\t\t\tIf we're not at the top of the stack, discard the future moments.\n\t\t*/\n\t\tif (historyLength() < historySize()) {\n\t\t\tif (DEBUG) { console.log(`\\tnon-top push; discarding ${historySize() - historyLength()} future moments`); }\n\n\t\t\t_history.splice(historyLength(), historySize() - historyLength());\n\t\t}\n\n\t\t/*\n\t\t\tPush the new moment onto the history stack.\n\t\t*/\n\t\t_history.push(momentCreate(title, _active.variables));\n\n\t\tif (_prng) {\n\t\t\thistoryTop().pull = _prng.pull;\n\t\t}\n\n\t\t/*\n\t\t\tTruncate the history, if necessary, by discarding moments from the bottom.\n\t\t*/\n\t\tif (Config.history.maxStates > 0) {\n\t\t\twhile (historySize() > Config.history.maxStates) {\n\t\t\t\t_expired.push(_history.shift().title);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tActivate the new top moment.\n\t\t*/\n\t\t_activeIndex = historySize() - 1;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn historyLength();\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the history.\n\t*/\n\tfunction historyGoTo(index) {\n\t\tif (DEBUG) { console.log(`[State/historyGoTo(index: ${index})]`); }\n\n\t\tif (\n\t\t\t   index == null /* lazy equality for null */\n\t\t\t|| index < 0\n\t\t\t|| index >= historySize()\n\t\t\t|| index === _activeIndex\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t_activeIndex = index;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyGo(offset) {\n\t\tif (DEBUG) { console.log(`[State/historyGo(offset: ${offset})]`); }\n\n\t\tif (offset == null || offset === 0) { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\treturn historyGoTo(_activeIndex + offset);\n\t}\n\n\t/*\n\t\tReturns the delta encoded form of the given history array.\n\t*/\n\tfunction historyDeltaEncode(historyArr) {\n\t\tif (!Array.isArray(historyArr)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (historyArr.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst delta = [historyArr[0]];\n\n\t\tfor (let i = 1, iend = historyArr.length; i < iend; ++i) {\n\t\t\tdelta.push(Diff.diff(historyArr[i - 1], historyArr[i]));\n\t\t}\n\n\t\treturn delta;\n\t}\n\n\t/*\n\t\tReturns a history array from the given delta encoded history array.\n\t*/\n\tfunction historyDeltaDecode(delta) {\n\t\tif (!Array.isArray(delta)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (delta.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst historyArr = [clone(delta[0])];\n\n\t\tfor (let i = 1, iend = delta.length; i < iend; ++i) {\n\t\t\thistoryArr.push(Diff.patch(historyArr[i - 1], delta[i]));\n\t\t}\n\n\t\treturn historyArr;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPRNG Functions.\n\t*******************************************************************************************************************/\n\tfunction prngInit(seed, useEntropy) {\n\t\tif (DEBUG) { console.log(`[State/prngInit(seed: ${seed}, useEntropy: ${useEntropy})]`); }\n\n\t\tif (!historyIsEmpty()) {\n\t\t\tlet scriptSection;\n\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tscriptSection = 'a script-tagged passage';\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tscriptSection = 'the Story JavaScript';\n\t\t\t}\n\n\t\t\tthrow new Error(`State.initPRNG must be called during initialization, within either ${scriptSection} or the StoryInit special passage`);\n\t\t}\n\n\t\t_prng = new PRNGWrapper(seed, useEntropy);\n\t\t_active.pull = _prng.pull;\n\t}\n\n\tfunction prngIsEnabled() {\n\t\treturn _prng !== null;\n\t}\n\n\tfunction prngPull() {\n\t\treturn _prng ? _prng.pull : NaN;\n\t}\n\n\tfunction prngSeed() {\n\t\treturn _prng ? _prng.seed : null;\n\t}\n\n\tfunction prngRandom() {\n\t\tif (DEBUG) { console.log('[State/prngRandom()]'); }\n\n\t\treturn _prng ? _prng.random() : Math.random();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTemporary Variables Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tClear the temporary variables.\n\t*/\n\tfunction tempVariablesClear() {\n\t\tif (DEBUG) { console.log('[State/tempVariablesReset()]'); }\n\n\t\t_tempVariables = {};\n\n\t\t/* legacy */\n\t\tTempVariables = _tempVariables; // eslint-disable-line no-undef\n\t\t/* /legacy */\n\t}\n\n\t/*\n\t\tReturns the current temporary variables.\n\t*/\n\tfunction tempVariables() {\n\t\treturn _tempVariables;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tVariable Chain Parsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value of the given story/temporary variable.\n\t*/\n\tfunction variableGet(varExpression) {\n\t\ttry {\n\t\t\treturn Scripting.evalTwineScript(varExpression);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\t}\n\n\t/*\n\t\tSets the value of the given story/temporary variable.\n\t*/\n\tfunction variableSet(varExpression, value) {\n\t\ttry {\n\t\t\tScripting.evalTwineScript(`${varExpression} = evalTwineScript$Data$`, null, value);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tStory Metadata Functions.\n\t*******************************************************************************************************************/\n\tconst _METADATA_STORE = 'metadata';\n\n\tfunction metadataClear() {\n\t\tstorage.delete(_METADATA_STORE);\n\t}\n\n\tfunction metadataDelete(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.delete key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\n\t\tif (store && store.hasOwnProperty(key)) {\n\t\t\tif (Object.keys(store).length === 1) {\n\t\t\t\tstorage.delete(_METADATA_STORE);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelete store[key];\n\t\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction metadataGet(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.get key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key) ? store[key] : undefined;\n\t}\n\n\tfunction metadataHas(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.has key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key);\n\t}\n\n\tfunction metadataSet(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.set key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tif (typeof value === 'undefined') {\n\t\t\tmetadataDelete(key);\n\t\t}\n\t\telse {\n\t\t\tconst store = storage.get(_METADATA_STORE) || {};\n\t\t\tstore[key] = value;\n\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t}\n\t}\n\n\tfunction metadataSize() {\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store ? Object.keys(store).length : 0;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tState Functions.\n\t\t*/\n\t\treset            : { value : stateReset },\n\t\trestore          : { value : stateRestore },\n\t\tmarshalForSave   : { value : stateMarshalForSave },\n\t\tunmarshalForSave : { value : stateUnmarshalForSave },\n\t\texpired          : { get : stateExpired },\n\t\tturns            : { get : stateTurns },\n\t\tpassages         : { get : stateTitles },\n\t\thasPlayed        : { value : stateHasPlayed },\n\n\t\t/*\n\t\t\tMoment Functions.\n\t\t*/\n\t\tactive      : { get : momentActive },\n\t\tactiveIndex : { get : momentActiveIndex },\n\t\tpassage     : { get : momentActiveTitle },     // shortcut for `State.active.title`\n\t\tvariables   : { get : momentActiveVariables }, // shortcut for `State.active.variables`\n\n\t\t/*\n\t\t\tHistory Functions.\n\t\t*/\n\t\thistory     : { get : historyGet },\n\t\tlength      : { get : historyLength },\n\t\tsize        : { get : historySize },\n\t\tisEmpty     : { value : historyIsEmpty },\n\t\tcurrent     : { get : historyCurrent },\n\t\ttop         : { get : historyTop },\n\t\tbottom      : { get : historyBottom },\n\t\tindex       : { value : historyIndex },\n\t\tpeek        : { value : historyPeek },\n\t\thas         : { value : historyHas },\n\t\tcreate      : { value : historyCreate },\n\t\tgoTo        : { value : historyGoTo },\n\t\tgo          : { value : historyGo },\n\t\tdeltaEncode : { value : historyDeltaEncode },\n\t\tdeltaDecode : { value : historyDeltaDecode },\n\n\t\t/*\n\t\t\tPRNG Functions.\n\t\t*/\n\t\tprng : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tinit      : { value : prngInit },\n\t\t\t\tisEnabled : { value : prngIsEnabled },\n\t\t\t\tpull      : { get : prngPull },\n\t\t\t\tseed      : { get : prngSeed }\n\t\t\t}))\n\t\t},\n\t\trandom : { value : prngRandom },\n\n\t\t/*\n\t\t\tTemporary Variables Functions.\n\t\t*/\n\t\tclearTemporary : { value : tempVariablesClear },\n\t\ttemporary      : { get : tempVariables },\n\n\t\t/*\n\t\t\tVariable Chain Parsing Functions.\n\t\t*/\n\t\tgetVar : { value : variableGet },\n\t\tsetVar : { value : variableSet },\n\n\t\t/*\n\t\t\tStory Metadata Functions.\n\t\t*/\n\t\tmetadata : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tclear  : { value : metadataClear },\n\t\t\t\tdelete : { value : metadataDelete },\n\t\t\t\tget    : { value : metadataGet },\n\t\t\t\thas    : { value : metadataHas },\n\t\t\t\tset    : { value : metadataSet },\n\t\t\t\tsize   : { get : metadataSize }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tinitPRNG : { value : prngInit },\n\t\trestart  : { value : () => Engine.restart() },\n\t\tbackward : { value : () => Engine.backward() },\n\t\tforward  : { value : () => Engine.forward() },\n\t\tdisplay  : { value : (...args) => Engine.display(...args) },\n\t\tshow     : { value : (...args) => Engine.show(...args) },\n\t\tplay     : { value : (...args) => Engine.play(...args) }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/scripting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Engine, Patterns, State, Story, Util */\n\nvar Scripting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable no-unused-vars */\n\n\t/*******************************************************************************************************************\n\t\tDeprecated Legacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns the jQuery-wrapped target element(s) after making them accessible\n\t\tclickables (ARIA compatibility).\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction addAccessibleClickHandler(targets, selector, handler, one, namespace) {\n\t\tif (arguments.length < 2) {\n\t\t\tthrow new Error('addAccessibleClickHandler insufficient number of parameters');\n\t\t}\n\n\t\tlet fn;\n\t\tlet opts;\n\n\t\tif (typeof selector === 'function') {\n\t\t\tfn = selector;\n\t\t\topts = {\n\t\t\t\tnamespace : one,\n\t\t\t\tone       : !!handler\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tfn = handler;\n\t\t\topts = {\n\t\t\t\tnamespace,\n\t\t\t\tone : !!one,\n\t\t\t\tselector\n\t\t\t};\n\t\t}\n\n\t\tif (typeof fn !== 'function') {\n\t\t\tthrow new TypeError('addAccessibleClickHandler handler parameter must be a function');\n\t\t}\n\n\t\treturn jQuery(targets).ariaClick(opts, fn);\n\t}\n\n\t/*\n\t\t[DEPRECATED] Returns a new DOM element, optionally appending it to the passed DOM element, if any.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertElement(place, type, id, classNames, text, title) { // eslint-disable-line max-params\n\t\tconst $el = jQuery(document.createElement(type));\n\n\t\t// Add attributes/properties.\n\t\tif (id) {\n\t\t\t$el.attr('id', id);\n\t\t}\n\n\t\tif (classNames) {\n\t\t\t$el.addClass(classNames);\n\t\t}\n\n\t\tif (title) {\n\t\t\t$el.attr('title', title);\n\t\t}\n\n\t\t// Add text content.\n\t\tif (text) {\n\t\t\t$el.text(text);\n\t\t}\n\n\t\t// Append it to the given node.\n\t\tif (place) {\n\t\t\t$el.appendTo(place);\n\t\t}\n\n\t\treturn $el[0];\n\t}\n\n\t/*\n\t\t[DEPRECATED] Creates a new text node and appends it to the passed DOM element.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertText(place, text) {\n\t\tjQuery(place).append(document.createTextNode(text));\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes all children from the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeChildren(node) {\n\t\tjQuery(node).empty();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeElement(node) {\n\t\tjQuery(node).remove();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Fades a DOM element in or out.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction fade(el, options) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tconst direction = options.fade === 'in' ? 1 : -1;\n\t\tlet current;\n\t\tlet proxy      = el.cloneNode(true);\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tcurrent += 0.05 * direction;\n\t\t\tsetOpacity(proxy, Math.easeInOut(current));\n\n\t\t\tif (direction === 1 && current >= 1 || direction === -1 && current <= 0) {\n\t\t\t\tel.style.visibility = options.fade === 'in' ? 'visible' : 'hidden';\n\t\t\t\tproxy.parentNode.replaceChild(el, proxy);\n\t\t\t\tproxy = null;\n\t\t\t\twindow.clearInterval(intervalId);\n\n\t\t\t\tif (options.onComplete) {\n\t\t\t\t\toptions.onComplete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction setOpacity(el, opacity) {\n\t\t\t// Old IE.\n\t\t\tel.style.zoom = 1;\n\t\t\tel.style.filter = `alpha(opacity=${Math.floor(opacity * 100)})`;\n\n\t\t\t// CSS.\n\t\t\tel.style.opacity = opacity;\n\t\t}\n\n\t\tel.parentNode.replaceChild(proxy, el);\n\n\t\tif (options.fade === 'in') {\n\t\t\tcurrent = 0;\n\t\t\tproxy.style.visibility = 'visible';\n\t\t}\n\t\telse {\n\t\t\tcurrent = 1;\n\t\t}\n\n\t\tsetOpacity(proxy, current);\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\t/*\n\t\t[DEPRECATED] Scrolls the browser window to ensure that a DOM element is in view.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction scrollWindowTo(el, incrementBy) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tlet increment = incrementBy != null ? Number(incrementBy) : 0.1; // lazy equality for null\n\n\t\tif (Number.isNaN(increment) || !Number.isFinite(increment) || increment < 0) {\n\t\t\tincrement = 0.1;\n\t\t}\n\t\telse if (increment > 1) {\n\t\t\tincrement = 1;\n\t\t}\n\n\t\tconst start     = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\tconst end       = ensureVisible(el);\n\t\tconst distance  = Math.abs(start - end);\n\t\tconst direction = start > end ? -1 : 1;\n\t\tlet progress   = 0;\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tprogress += increment;\n\t\t\twindow.scroll(0, start + direction * (distance * Math.easeInOut(progress)));\n\n\t\t\tif (progress >= 1) {\n\t\t\t\twindow.clearInterval(intervalId);\n\t\t\t}\n\t\t}\n\n\t\tfunction findPosY(el) { // eslint-disable-line no-shadow\n\t\t\tlet curtop = 0;\n\n\t\t\twhile (el.offsetParent) {\n\t\t\t\tcurtop += el.offsetTop;\n\t\t\t\tel = el.offsetParent;\n\t\t\t}\n\n\t\t\treturn curtop;\n\t\t}\n\n\t\tfunction ensureVisible(el) { // eslint-disable-line no-shadow\n\t\t\tconst posTop    = findPosY(el);\n\t\t\tconst posBottom = posTop + el.offsetHeight;\n\t\t\tconst winTop    = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\t\tconst winHeight = window.innerHeight ? window.innerHeight : document.body.clientHeight;\n\t\t\tconst winBottom = winTop + winHeight;\n\n\t\t\treturn posTop >= winTop && posBottom > winBottom && el.offsetHeight < winHeight\n\t\t\t\t? posTop - (winHeight - el.offsetHeight) + 20\n\t\t\t\t: posTop;\n\t\t}\n\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUser Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a random value from its given arguments.\n\t*/\n\tfunction either(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn Array.prototype.concat.apply([], arguments).random();\n\t}\n\n\t/*\n\t\tRemoves the given key, and its value, from the story metadata store.\n\t*/\n\tfunction forget(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`forget key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.delete(key);\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the logical-AND\n\t\taggregate of the set.\n\t*/\n\tfunction hasVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('hasVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend; ++i) {\n\t\t\tif (!played.includes(needles[i])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tReturns the number of turns that have passed since the last instance of the given passage\n\t\toccurred within the story history or `-1` if it does not exist.  If multiple passages are\n\t\tgiven, returns the lowest count (which can be `-1`).\n\t*/\n\tfunction lastVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('lastVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\t\tconst uBound  = played.length - 1;\n\t\tlet turns = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && turns > -1; ++i) {\n\t\t\tconst lastIndex = played.lastIndexOf(needles[i]);\n\t\t\tturns = Math.min(turns, lastIndex === -1 ? -1 : uBound - lastIndex);\n\t\t}\n\n\t\treturn turns;\n\t}\n\n\t/*\n\t\tSets the given key/value pair within the story metadata store.\n\t*/\n\tfunction memorize(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`memorize key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.set(key, value);\n\t}\n\n\t/*\n\t\tReturns the title of the current passage.\n\t*/\n\tfunction passage() {\n\t\treturn State.passage;\n\t}\n\n\t/*\n\t\tReturns the title of a previous passage, either the most recent one whose title does not\n\t\tmatch that of the active passage or the one at the optional offset, or an empty string,\n\t\tif there is no such passage.\n\t*/\n\tfunction previous(/* legacy: offset */) {\n\t\tconst passages = State.passages;\n\n\t\t/* legacy: behavior with an offset */\n\t\tif (arguments.length > 0) {\n\t\t\tconst offset = Number(arguments[0]);\n\n\t\t\tif (!Number.isSafeInteger(offset) || offset < 1) {\n\t\t\t\tthrow new RangeError('previous offset parameter must be a positive integer greater than zero');\n\t\t\t}\n\n\t\t\treturn passages.length > offset ? passages[passages.length - 1 - offset] : '';\n\t\t}\n\t\t/* /legacy */\n\n\t\tfor (let i = passages.length - 2; i >= 0; --i) {\n\t\t\tif (passages[i] !== State.passage) {\n\t\t\t\treturn passages[i];\n\t\t\t}\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the range of the given bounds.\n\t*/\n\tfunction random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(arguments[0]);\n\t\t\tmax = Math.trunc(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!Number.isInteger(min)) {\n\t\t\tthrow new Error('random min parameter must be an integer');\n\t\t}\n\t\tif (!Number.isInteger(max)) {\n\t\t\tthrow new Error('random max parameter must be an integer');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(State.random() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a pseudo-random real number (floating-point) within the range of the given bounds.\n\n\t\tNOTE: Unlike with its sibling function `random()`, the `max` parameter\n\t\tis exclusive, not inclusive—i.e. the range goes to, but does not include,\n\t\tthe given value.\n\t*/\n\tfunction randomFloat(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('randomFloat called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0.0;\n\t\t\tmax = Number(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Number(arguments[0]);\n\t\t\tmax = Number(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min) || !Number.isFinite(min)) {\n\t\t\tthrow new Error('randomFloat min parameter must be a number');\n\t\t}\n\t\tif (Number.isNaN(max) || !Number.isFinite(max)) {\n\t\t\tthrow new Error('randomFloat max parameter must be a number');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn State.random() * (max - min) + min;\n\t}\n\n\t/*\n\t\tReturns the value of the given key from the story metadata store\n\t\tor the given default value if the key does not exist.\n\t*/\n\tfunction recall(key, defaultValue) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`recall key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\treturn State.metadata.has(key) ? State.metadata.get(key) : defaultValue;\n\t}\n\n\t/*\n\t\tReturns a new array consisting of all of the tags of the given passages.\n\t*/\n\tfunction tags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn Story.get(State.passage).tags.slice(0);\n\t\t}\n\n\t\tconst passages = Array.prototype.concat.apply([], arguments);\n\t\tlet tags = [];\n\n\t\tfor (let i = 0, iend = passages.length; i < iend; ++i) {\n\t\t\ttags = tags.concat(Story.get(passages[i]).tags);\n\t\t}\n\n\t\treturn tags;\n\t}\n\n\t/*\n\t\tReturns a reference to the current temporary _variables store.\n\t*/\n\tfunction temporary() {\n\t\treturn State.temporary;\n\t}\n\n\t/*\n\t\tReturns the number of milliseconds which have passed since the current passage was rendered.\n\t*/\n\tfunction time() {\n\t\treturn Engine.lastPlay === null ? 0 : Util.now() - Engine.lastPlay;\n\t}\n\n\t/*\n\t\tReturns the number of passages that the player has visited.\n\n\t\tNOTE: Passages which were visited but have been undone—e.g. via the backward\n\t\tbutton or the `<<back>>` macro—are no longer part of the in-play story\n\t\thistory and thus are not tallied.  Passages which were visited but have\n\t\texpired from the story history, on the other hand, are tallied.\n\t*/\n\tfunction turns() {\n\t\treturn State.turns;\n\t}\n\n\t/*\n\t\tReturns a reference to the current story $variables store.\n\t*/\n\tfunction variables() {\n\t\treturn State.variables;\n\t}\n\n\t/*\n\t\tReturns the number of times that the passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the lowest count.\n\t*/\n\tfunction visited(/* variadic */) {\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments.length === 0 ? [State.passage] : arguments);\n\t\tconst played  = State.passages;\n\t\tlet count = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && count > 0; ++i) {\n\t\t\tcount = Math.min(count, played.count(needles[i]));\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/*\n\t\tReturns the number of passages within the story history which are tagged with all of the given tags.\n\t*/\n\tfunction visitedTags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('visitedTags called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst nLength = needles.length;\n\t\tconst played  = State.passages;\n\t\tconst seen    = new Map();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0, iend = played.length; i < iend; ++i) {\n\t\t\tconst title = played[i];\n\n\t\t\tif (seen.has(title)) {\n\t\t\t\tif (seen.get(title)) {\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst tags = Story.get(title).tags;\n\n\t\t\t\tif (tags.length > 0) {\n\t\t\t\t\tlet found = 0;\n\n\t\t\t\t\tfor (let j = 0; j < nLength; ++j) {\n\t\t\t\t\t\tif (tags.includes(needles[j])) {\n\t\t\t\t\t\t\t++found;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (found === nLength) {\n\t\t\t\t\t\t++count;\n\t\t\t\t\t\tseen.set(title, true);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tseen.set(title, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/* eslint-enable no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tImport Functions.\n\t*******************************************************************************************************************/\n\tvar { // eslint-disable-line no-var\n\t\t/* eslint-disable no-unused-vars */\n\t\timportScripts,\n\t\timportStyles\n\t\t/* eslint-enable no-unused-vars */\n\t} = (() => {\n\t\t// Slugify the given URL.\n\t\tfunction slugifyUrl(url) {\n\t\t\treturn Util.parseUrl(url).path\n\t\t\t\t.replace(/^[^\\w]+|[^\\w]+$/g, '')\n\t\t\t\t.replace(/[^\\w]+/g, '-')\n\t\t\t\t.toLocaleLowerCase();\n\t\t}\n\n\t\t// Add a <script> element which will load the script from the given URL.\n\t\tfunction addScript(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('script'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importScripts failed to load the script \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `script-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\ttype : 'text/javascript',\n\t\t\t\t\t\tsrc  : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Add a <link> element which will load the stylesheet from the given URL.\n\t\tfunction addStyle(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('link'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importStyles failed to load the stylesheet \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `style-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\trel  : 'stylesheet',\n\t\t\t\t\t\thref : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Turn a list of callbacks into a sequential chain of `Promise` objects.\n\t\tfunction sequence(callbacks) {\n\t\t\treturn callbacks.reduce((seq, fn) => seq = seq.then(fn), Promise.resolve()); // eslint-disable-line no-param-reassign\n\t\t}\n\n\t\t/*\n\t\t\tImport scripts from a URL.\n\t\t*/\n\t\tfunction importScripts(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addScript(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addScript(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t/*\n\t\t\tImport stylesheets from a URL.\n\t\t*/\n\t\tfunction importStyles(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addStyle(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addStyle(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t// Exports.\n\t\treturn {\n\t\t\timportScripts,\n\t\t\timportStyles\n\t\t};\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tParsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the given string after converting all TwineScript syntactical sugars to\n\t\ttheir native JavaScript counterparts.\n\t*/\n\tconst parse = (() => {\n\t\tconst tokenTable = Util.toEnum({\n\t\t\t/* eslint-disable quote-props */\n\t\t\t// Story $variable sigil-prefix.\n\t\t\t'$'     : 'State.variables.',\n\t\t\t// Temporary _variable sigil-prefix.\n\t\t\t'_'     : 'State.temporary.',\n\t\t\t// Assignment operators.\n\t\t\t'to'    : '=',\n\t\t\t// Equality operators.\n\t\t\t'eq'    : '==',\n\t\t\t'neq'   : '!=',\n\t\t\t'is'    : '===',\n\t\t\t'isnot' : '!==',\n\t\t\t// Relational operators.\n\t\t\t'gt'    : '>',\n\t\t\t'gte'   : '>=',\n\t\t\t'lt'    : '<',\n\t\t\t'lte'   : '<=',\n\t\t\t// Logical operators.\n\t\t\t'and'   : '&&',\n\t\t\t'or'    : '||',\n\t\t\t// Unary operators.\n\t\t\t'not'   : '!',\n\t\t\t'def'   : '\"undefined\" !== typeof',\n\t\t\t'ndef'  : '\"undefined\" === typeof'\n\t\t\t/* eslint-enable quote-props */\n\t\t});\n\t\tconst parseRe = new RegExp([\n\t\t\t'(\"\"|\\'\\')',                                          // 1=Empty quotes\n\t\t\t'(\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\")',                            // 2=Double quoted, non-empty\n\t\t\t\"('(?:\\\\\\\\.|[^'\\\\\\\\])+')\",                            // 3=Single quoted, non-empty\n\t\t\t'([=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}]+)',       // 4=Operator delimiters\n\t\t\t'([^\"\\'=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}\\\\s]+)' // 5=Barewords\n\t\t].join('|'), 'g');\n\t\tconst notSpaceRe      = /\\S/;\n\t\tconst varTest         = new RegExp(`^${Patterns.variable}`);\n\t\tconst withColonTestRe = /^\\s*:/;\n\t\tconst withNotTestRe   = /^\\s+not\\b/;\n\n\t\tfunction parse(rawCodeString) {\n\t\t\tif (parseRe.lastIndex !== 0) {\n\t\t\t\tthrow new RangeError('Scripting.parse last index is non-zero at start');\n\t\t\t}\n\n\t\t\tlet code  = rawCodeString;\n\t\t\tlet match;\n\n\t\t\twhile ((match = parseRe.exec(code)) !== null) {\n\t\t\t\t// no-op: Empty quotes | Double quoted | Single quoted | Operator delimiters\n\n\t\t\t\t// Barewords.\n\t\t\t\tif (match[5]) {\n\t\t\t\t\tlet token = match[5];\n\n\t\t\t\t\t// If the token is simply a dollar-sign or underscore, then it's either\n\t\t\t\t\t// just the raw character or, probably, a function alias, so skip it.\n\t\t\t\t\tif (token === '$' || token === '_') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is a story $variable or temporary _variable, reset it\n\t\t\t\t\t// to just its sigil—for later mapping.\n\t\t\t\t\telse if (varTest.test(token)) {\n\t\t\t\t\t\ttoken = token[0];\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is `is`, check to see if it's followed by `not`, if so,\n\t\t\t\t\t// convert them into the `isnot` operator.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: This is a safety feature, since `$a is not $b` probably sounds\n\t\t\t\t\t// reasonable to most users.\n\t\t\t\t\telse if (token === 'is') {\n\t\t\t\t\t\tconst start = parseRe.lastIndex;\n\t\t\t\t\t\tconst ahead = code.slice(start);\n\n\t\t\t\t\t\tif (withNotTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcode = code.splice(start, ahead.search(notSpaceRe));\n\t\t\t\t\t\t\ttoken = 'isnot';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is followed by a colon, then it's likely to be an object\n\t\t\t\t\t// property, so skip it.\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst ahead = code.slice(parseRe.lastIndex);\n\n\t\t\t\t\t\tif (withColonTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the finalized token has a mapping, replace it within the code string\n\t\t\t\t\t// with its counterpart.\n\t\t\t\t\tif (tokenTable[token]) {\n\t\t\t\t\t\tcode = code.splice(\n\t\t\t\t\t\t\tmatch.index,      // starting index\n\t\t\t\t\t\t\ttoken.length,     // replace how many\n\t\t\t\t\t\t\ttokenTable[token] // replacement string\n\t\t\t\t\t\t);\n\t\t\t\t\t\tparseRe.lastIndex += tokenTable[token].length - token.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn code;\n\t\t}\n\n\t\treturn parse;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tEval Functions.\n\t*******************************************************************************************************************/\n\t/* eslint-disable no-eval, no-extra-parens, no-unused-vars */\n\t/*\n\t\tEvaluates the given JavaScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalJavaScript(code, output, data) {\n\t\treturn (function (code, output, evalJavaScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, String(code), output, data);\n\t}\n\n\t/*\n\t\tEvaluates the given TwineScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalTwineScript(code, output, data) {\n\t\t// NOTE: Do not move the dollar sign to the front of `evalTwineScript$Data$`,\n\t\t// as `parse()` will break references to it within the code string.\n\t\treturn (function (code, output, evalTwineScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, parse(String(code)), output, data);\n\t}\n\t/* eslint-enable no-eval, no-extra-parens, no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tparse           : { value : parse },\n\t\tevalJavaScript  : { value : evalJavaScript },\n\t\tevalTwineScript : { value : evalTwineScript }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/lexer.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tEOF,\n\tLexer\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\t// End of file (string, actually).\n\tconst EOF = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tLexer Class.\n\t*******************************************************************************************************************/\n\tclass Lexer {\n\t\tconstructor(source, initialState) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer constructor called with too few parameters (source:string , initialState:function)');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tthis.source  → the string to be scanned\n\t\t\t\tthis.initial → initial state\n\t\t\t\tthis.state   → current state\n\t\t\t\tthis.start   → start position of an item\n\t\t\t\tthis.pos     → current position in the source string\n\t\t\t\tthis.depth   → current brace/bracket/parenthesis nesting depth\n\t\t\t\tthis.items   → scanned item queue\n\t\t\t\tthis.data    → lexing data\n\t\t\t*/\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : source\n\t\t\t\t},\n\n\t\t\t\tinitial : {\n\t\t\t\t\tvalue : initialState\n\t\t\t\t},\n\n\t\t\t\tstate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : initialState\n\t\t\t\t},\n\n\t\t\t\tstart : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tpos : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tdepth : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\titems : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : []\n\t\t\t\t},\n\n\t\t\t\tdata : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : {}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treset() {\n\t\t\tthis.state  = this.initial;\n\t\t\tthis.start  = 0;\n\t\t\tthis.pos    = 0;\n\t\t\tthis.depth  = 0;\n\t\t\tthis.items  = [];\n\t\t\tthis.data   = {};\n\t\t}\n\n\t\trun() {\n\t\t\t// scan the source string until no states remain\n\t\t\twhile (this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the array of items\n\t\t\treturn this.items;\n\t\t}\n\n\t\tnextItem() {\n\t\t\t// scan the source string until we have an item or no states remain\n\t\t\twhile (this.items.length === 0 && this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the current item\n\t\t\treturn this.items.shift();\n\t\t}\n\n\t\tnext() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos++];\n\t\t}\n\n\t\tpeek() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos];\n\t\t}\n\n\t\tbackup(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos -= num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t--this.pos;\n\t\t\t// }\n\t\t\tthis.pos -= num || 1;\n\t\t}\n\n\t\tforward(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos += num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t++this.pos;\n\t\t\t// }\n\t\t\tthis.pos += num || 1;\n\t\t}\n\n\t\tignore() {\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\taccept(valid) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (valid.includes(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRe(validRe) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (validRe.test(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRun(valid) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!valid.includes(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\tacceptRunRe(validRe) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!validRe.test(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\temit(type) {\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\terror(type, message) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer.prototype.error called with too few parameters (type:number , message:string)');\n\t\t\t}\n\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\tmessage,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic enumFromNames(names) {\n\t\t\tconst obj = names.reduce((obj, name, i) => {\n\t\t\t\tobj[name] = i; // eslint-disable-line no-param-reassign\n\t\t\t\treturn obj;\n\t\t\t}, {});\n\t\t\treturn Object.freeze(Object.assign(Object.create(null), obj));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn {\n\t\tEOF,\n\t\tLexer\n\t};\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/wikifier.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, EOF, Engine, Lexer, Patterns, Scripting, State, Story, TempState, Util, convertBreaks,\n\t       errorPrologRegExp\n*/\n\n/*\n\tTODO: The Wikifier, and associated code, could stand to receive a serious refactoring.\n*/\n/* eslint-disable max-len */\nvar Wikifier = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Wikifier call depth.\n\tlet _callDepth = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tWikifier Class.\n\t*******************************************************************************************************************/\n\tclass Wikifier {\n\t\tconstructor(destination, source, options) {\n\t\t\tif (Wikifier.Parser.Profile.isEmpty()) {\n\t\t\t\tWikifier.Parser.Profile.compile();\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// General Wikifier properties.\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : String(source)\n\t\t\t\t},\n\n\t\t\t\toptions : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Object.assign({\n\t\t\t\t\t\tprofile : 'all'\n\t\t\t\t\t}, options)\n\t\t\t\t},\n\n\t\t\t\tnextMatch : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\toutput : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t// Macro parser ('macro') related properties.\n\t\t\t\t_rawArgs : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// No destination specified.  Create a fragment to act as the output buffer.\n\t\t\tif (destination == null) { // lazy equality for null\n\t\t\t\tthis.output = document.createDocumentFragment();\n\t\t\t}\n\n\t\t\t// jQuery-wrapped destination.  Grab the first element.\n\t\t\telse if (destination.jquery) { // cannot use `hasOwnProperty()` here as `jquery` is from jQuery's prototype\n\t\t\t\tthis.output = destination[0];\n\t\t\t}\n\n\t\t\t// Normal destination.\n\t\t\telse {\n\t\t\t\tthis.output = destination;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tWikify the source into the output buffer element, possibly converting line\n\t\t\t\tbreaks into paragraphs.\n\n\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\tto ensure that the call depth is properly restored in the event that an\n\t\t\t\tuncaught exception is thrown during the call to `subWikify()`.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\t++_callDepth;\n\n\t\t\t\tthis.subWikify(this.output);\n\n\t\t\t\t// Limit line break conversion to non-recursive calls.\n\t\t\t\tif (_callDepth === 1 && Config.cleanupWikifierOutput) {\n\t\t\t\t\tconvertBreaks(this.output);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t--_callDepth;\n\t\t\t}\n\t\t}\n\n\t\tsubWikify(output, terminator, options) {\n\t\t\t// Cache and temporarily replace the current output buffer.\n\t\t\tconst oldOutput = this.output;\n\t\t\tthis.output = output;\n\n\t\t\tlet newOptions;\n\t\t\tlet oldOptions;\n\n\t\t\t// Parser option overrides.\n\t\t\tif (Wikifier.Option.length > 0) {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, Wikifier.Option.options);\n\t\t\t}\n\t\t\t// Local parameter option overrides.\n\t\t\tif (options !== null && typeof options === 'object') {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, options);\n\t\t\t}\n\t\t\t// If new options exist, cache and temporarily replace the current options.\n\t\t\tif (newOptions) {\n\t\t\t\toldOptions = this.options;\n\t\t\t\tthis.options = Object.assign({}, this.options, newOptions);\n\t\t\t}\n\n\t\t\tconst parsersProfile   = Wikifier.Parser.Profile.get(this.options.profile);\n\t\t\tconst terminatorRegExp = terminator\n\t\t\t\t? new RegExp(`(?:${terminator})`, this.options.ignoreTerminatorCase ? 'gim' : 'gm')\n\t\t\t\t: null;\n\t\t\tlet terminatorMatch;\n\t\t\tlet parserMatch;\n\n\t\t\tdo {\n\t\t\t\t// Prepare the RegExp match positions.\n\t\t\t\tparsersProfile.parserRegExp.lastIndex = this.nextMatch;\n\n\t\t\t\tif (terminatorRegExp) {\n\t\t\t\t\tterminatorRegExp.lastIndex = this.nextMatch;\n\t\t\t\t}\n\n\t\t\t\t// Get the first matches.\n\t\t\t\tparserMatch     = parsersProfile.parserRegExp.exec(this.source);\n\t\t\t\tterminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;\n\n\t\t\t\t// Try for a terminator match, unless there's a closer parser match.\n\t\t\t\tif (terminatorMatch && (!parserMatch || terminatorMatch.index <= parserMatch.index)) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (terminatorMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, terminatorMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = terminatorMatch.index;\n\t\t\t\t\tthis.matchLength = terminatorMatch[0].length;\n\t\t\t\t\tthis.matchText   = terminatorMatch[0];\n\t\t\t\t\tthis.nextMatch   = terminatorRegExp.lastIndex;\n\n\t\t\t\t\t// Restore the original output buffer and options.\n\t\t\t\t\tthis.output = oldOutput;\n\n\t\t\t\t\tif (oldOptions) {\n\t\t\t\t\t\tthis.options = oldOptions;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Exit.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try for a parser match.\n\t\t\t\telse if (parserMatch) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (parserMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, parserMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = parserMatch.index;\n\t\t\t\t\tthis.matchLength = parserMatch[0].length;\n\t\t\t\t\tthis.matchText   = parserMatch[0];\n\t\t\t\t\tthis.nextMatch   = parsersProfile.parserRegExp.lastIndex;\n\n\t\t\t\t\t// Figure out which parser matched.\n\t\t\t\t\tlet matchingParser;\n\n\t\t\t\t\tfor (let i = 1, iend = parserMatch.length; i < iend; ++i) {\n\t\t\t\t\t\tif (parserMatch[i]) {\n\t\t\t\t\t\t\tmatchingParser = i - 1;\n\t\t\t\t\t\t\tbreak; // stop once we've found the matching parser\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call the parser.\n\t\t\t\t\tparsersProfile.parsers[matchingParser].handler(this);\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (terminatorMatch || parserMatch);\n\n\t\t\t// Output any text after the last match.\n\t\t\tif (TempState.break == null) { // lazy equality for null\n\t\t\t\tif (this.nextMatch < this.source.length) {\n\t\t\t\t\tthis.outputText(this.output, this.nextMatch, this.source.length);\n\t\t\t\t\tthis.nextMatch = this.source.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In case of <<break>>/<<continue>>, remove the last <br>.\n\t\t\telse if (\n\t\t\t\t   this.output.lastChild\n\t\t\t\t&& this.output.lastChild.nodeType === Node.ELEMENT_NODE\n\t\t\t\t&& this.output.lastChild.nodeName.toUpperCase() === 'BR'\n\t\t\t) {\n\t\t\t\tjQuery(this.output.lastChild).remove();\n\t\t\t}\n\n\t\t\t// Restore the original output buffer and options.\n\t\t\tthis.output = oldOutput;\n\n\t\t\tif (oldOptions) {\n\t\t\t\tthis.options = oldOptions;\n\t\t\t}\n\t\t}\n\n\t\toutputText(destination, startPos, endPos) {\n\t\t\tdestination.appendChild(document.createTextNode(this.source.substring(startPos, endPos)));\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the raw, unprocessed\n\t\t\ttext given to the currently executing macro.\n\t\t*/\n\t\trawArgs() {\n\t\t\treturn this._rawArgs;\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the text given to\n\t\t\tthe currently executing macro after doing TwineScript-to-JavaScript transformations.\n\t\t*/\n\t\tfullArgs() {\n\t\t\treturn Scripting.parse(this._rawArgs);\n\t\t}\n\n\t\t/*\n\t\t\tReturns the output generated by wikifying the given text, throwing if there were errors.\n\t\t*/\n\t\tstatic wikifyEval(text) {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\tnew Wikifier(output, text);\n\n\t\t\tconst errors = output.querySelector('.error');\n\n\t\t\tif (errors !== null) {\n\t\t\t\tthrow new Error(errors.textContent.replace(errorPrologRegExp, ''));\n\t\t\t}\n\n\t\t\treturn output;\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an internal link.\n\t\t*/\n\t\tstatic createInternalLink(destination, passage, text, callback) {\n\t\t\tconst $link = jQuery(document.createElement('a'));\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\n\t\t\t\t$link.ariaClick({ one : true }, () => {\n\t\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\n\t\t\t\t\tEngine.play(passage);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (text) {\n\t\t\t\t$link.append(document.createTextNode(text));\n\t\t\t}\n\n\t\t\tif (destination) {\n\t\t\t\t$link.appendTo(destination);\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an external link.\n\t\t*/\n\t\tstatic createExternalLink(destination, url, text) {\n\t\t\tconst $link = jQuery(document.createElement('a'))\n\t\t\t\t.attr('target', '_blank')\n\t\t\t\t.addClass('link-external')\n\t\t\t\t.text(text)\n\t\t\t\t.appendTo(destination);\n\n\t\t\tif (url != null) { // lazy equality for null\n\t\t\t\t$link.attr({\n\t\t\t\t\thref     : url,\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the given link source is external (probably).\n\t\t*/\n\t\tstatic isExternalLink(link) {\n\t\t\tif (Story.has(link)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst urlRegExp = new RegExp(`^${Patterns.url}`, 'gim');\n\t\t\treturn urlRegExp.test(link) || /[/.?#]/.test(link);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tOption Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Option', {\n\t\tvalue : (() => {\n\t\t\t// Options array (stack).\n\t\t\tlet _optionsStack = [];\n\n\n\t\t\t/*\n\t\t\t\tGlobalOption Functions.\n\t\t\t*/\n\t\t\tfunction optionLength() {\n\t\t\t\treturn _optionsStack.length;\n\t\t\t}\n\n\t\t\tfunction optionGetter() {\n\t\t\t\treturn Object.assign({}, ..._optionsStack);\n\t\t\t}\n\n\t\t\tfunction optionClear() {\n\t\t\t\t_optionsStack = [];\n\t\t\t}\n\n\t\t\tfunction optionGet(idx) {\n\t\t\t\treturn _optionsStack[idx];\n\t\t\t}\n\n\t\t\tfunction optionPop() {\n\t\t\t\treturn _optionsStack.pop();\n\t\t\t}\n\n\t\t\tfunction optionPush(options) {\n\t\t\t\tif (typeof options !== 'object' || options === null) {\n\t\t\t\t\tthrow new TypeError(`Wikifier.Option.push options parameter must be an object (received: ${Util.getType(options)})`);\n\t\t\t\t}\n\n\t\t\t\treturn _optionsStack.push(options);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\tlength  : { get : optionLength },\n\t\t\t\toptions : { get : optionGetter },\n\t\t\t\tclear   : { value : optionClear },\n\t\t\t\tget     : { value : optionGet },\n\t\t\t\tpop     : { value : optionPop },\n\t\t\t\tpush    : { value : optionPush }\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tParser Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Parser', {\n\t\tvalue : (() => {\n\t\t\t// Parser definition array.  Ordering matters, so this must be an ordered list.\n\t\t\tconst _parsers = [];\n\n\t\t\t// Parser profiles object.\n\t\t\tlet _profiles;\n\n\n\t\t\t/*\n\t\t\t\tParser Functions.\n\t\t\t*/\n\t\t\tfunction parsersGetter() {\n\t\t\t\treturn _parsers;\n\t\t\t}\n\n\t\t\tfunction parsersAdd(parser) {\n\t\t\t\t// Parser object sanity checks.\n\t\t\t\tif (typeof parser !== 'object') {\n\t\t\t\t\tthrow new Error('Wikifier.Parser.add parser parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('name')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"name\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.name !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"name\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('match')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"match\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.match !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"match\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('handler')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"handler\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.handler !== 'function') {\n\t\t\t\t\tthrow new Error('parser object \"handler\" property must be a function');\n\t\t\t\t}\n\n\t\t\t\tif (parser.hasOwnProperty('profiles') && !Array.isArray(parser.profiles)) {\n\t\t\t\t\tthrow new Error('parser object \"profiles\" property must be an array');\n\t\t\t\t}\n\n\t\t\t\t// Check for an existing parser with the same name.\n\t\t\t\tif (parsersHas(parser.name)) {\n\t\t\t\t\tthrow new Error(`cannot clobber existing parser \"${parser.name}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Add the parser to the end of the array.\n\t\t\t\t_parsers.push(parser);\n\t\t\t}\n\n\t\t\tfunction parsersDelete(name) {\n\t\t\t\tconst parser = _parsers.find(parser => parser.name === name);\n\n\t\t\t\tif (parser) {\n\t\t\t\t\t_parsers.delete(parser);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction parsersIsEmpty() {\n\t\t\t\treturn _parsers.length === 0;\n\t\t\t}\n\n\t\t\tfunction parsersHas(name) {\n\t\t\t\treturn !!_parsers.find(parser => parser.name === name);\n\t\t\t}\n\n\t\t\tfunction parsersGet(name) {\n\t\t\t\treturn _parsers.find(parser => parser.name === name) || null;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tParser Profile Functions.\n\t\t\t*/\n\t\t\tfunction profilesGetter() {\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesCompile() {\n\t\t\t\tif (DEBUG) { console.log('[Wikifier.Parser/profilesCompile()]'); }\n\n\t\t\t\tconst all  = _parsers;\n\t\t\t\tconst core = all.filter(parser => !Array.isArray(parser.profiles) || parser.profiles.includes('core'));\n\n\t\t\t\t_profiles = Object.freeze({\n\t\t\t\t\tall : {\n\t\t\t\t\t\tparsers      : all,\n\t\t\t\t\t\tparserRegExp : new RegExp(all.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t},\n\t\t\t\t\tcore : {\n\t\t\t\t\t\tparsers      : core,\n\t\t\t\t\t\tparserRegExp : new RegExp(core.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesIsEmpty() {\n\t\t\t\treturn typeof _profiles !== 'object' || Object.keys(_profiles).length === 0;\n\t\t\t}\n\n\t\t\tfunction profilesGet(profile) {\n\t\t\t\tif (typeof _profiles !== 'object' || !_profiles.hasOwnProperty(profile)) {\n\t\t\t\t\tthrow new Error(`nonexistent parser profile \"${profile}\"`);\n\t\t\t\t}\n\n\t\t\t\treturn _profiles[profile];\n\t\t\t}\n\n\t\t\tfunction profilesHas(profile) {\n\t\t\t\treturn typeof _profiles === 'object' && _profiles.hasOwnProperty(profile);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\t/*\n\t\t\t\t\tParser Containers.\n\t\t\t\t*/\n\t\t\t\tparsers : { get : parsersGetter },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Functions.\n\t\t\t\t*/\n\t\t\t\tadd     : { value : parsersAdd },\n\t\t\t\tdelete  : { value : parsersDelete },\n\t\t\t\tisEmpty : { value : parsersIsEmpty },\n\t\t\t\thas     : { value : parsersHas },\n\t\t\t\tget     : { value : parsersGet },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Profile.\n\t\t\t\t*/\n\t\t\t\tProfile : {\n\t\t\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Containers.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tprofiles : { get : profilesGetter },\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Functions.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tcompile : { value : profilesCompile },\n\t\t\t\t\t\tisEmpty : { value : profilesIsEmpty },\n\t\t\t\t\t\thas     : { value : profilesHas },\n\t\t\t\t\t\tget     : { value : profilesGet }\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAdditional Static Properties.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier, {\n\t\thelpers : { value : {} },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tgetValue       : { value : State.getVar },              // SEE: `state.js`.\n\t\tsetValue       : { value : State.setVar },              // SEE: `state.js`.\n\t\tparse          : { value : Scripting.parse },           // SEE: `markup/scripting.js`.\n\t\tevalExpression : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\ttextPrimitives : { value : Patterns }                   // SEE: `lib/patterns.js`.\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Static Methods.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier.helpers, {\n\t\tinlineCss : {\n\t\t\tvalue : (() => {\n\t\t\t\tconst lookaheadRe = new RegExp(Patterns.inlineCss, 'gm');\n\t\t\t\tconst idOrClassRe = new RegExp(`(${Patterns.cssIdOrClassSigil})(${Patterns.anyLetter}+)`, 'g');\n\n\t\t\t\tfunction helperInlineCss(w) {\n\t\t\t\t\tconst css = { classes : [], id : '', styles : {} };\n\t\t\t\t\tlet matched;\n\n\t\t\t\t\tdo {\n\t\t\t\t\t\tlookaheadRe.lastIndex = w.nextMatch;\n\n\t\t\t\t\t\tconst match = lookaheadRe.exec(w.source);\n\n\t\t\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\tif (match[1]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[1])] = match[2].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[3]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[3])] = match[4].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[5]) {\n\t\t\t\t\t\t\t\tlet subMatch;\n\n\t\t\t\t\t\t\t\tidOrClassRe.lastIndex = 0; // NOTE: Guard against buggy implementations.\n\n\t\t\t\t\t\t\t\twhile ((subMatch = idOrClassRe.exec(match[5])) !== null) {\n\t\t\t\t\t\t\t\t\tif (subMatch[1] === '.') {\n\t\t\t\t\t\t\t\t\t\tcss.classes.push(subMatch[2]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tcss.id = subMatch[2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tw.nextMatch = lookaheadRe.lastIndex; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (matched);\n\n\t\t\t\t\treturn css;\n\t\t\t\t}\n\n\t\t\t\treturn helperInlineCss;\n\t\t\t})()\n\t\t},\n\n\t\tevalText : {\n\t\t\tvalue(text) {\n\t\t\t\tlet result;\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = Scripting.evalTwineScript(text);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAttempt to prevent the leakage of auto-globals by enforcing that\n\t\t\t\t\t\tthe resultant value be either a string or a number.\n\n\t\t\t\t\t\tNOTE: This is not a foolproof solution to the problem of auto-global\n\t\t\t\t\t\tleakage.  Various auto-globals, which return strings or numbers, can\n\t\t\t\t\t\tstill leak through—e.g. `window.status` → string.\n\t\t\t\t\t*/\n\t\t\t\t\tswitch (typeof result) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tif (result.trim() === '') {\n\t\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tresult = String(result);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tresult = text;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t},\n\n\t\tevalPassageId : {\n\t\t\tvalue(passage) {\n\t\t\t\tif (passage == null || Story.has(passage)) { // lazy equality for null; `0` is a valid name, so we cannot simply evaluate `passage`\n\t\t\t\t\treturn passage;\n\t\t\t\t}\n\n\t\t\t\treturn Wikifier.helpers.evalText(passage);\n\t\t\t}\n\t\t},\n\n\t\thasBlockContext : {\n\t\t\tvalue(nodes) {\n\t\t\t\tconst hasGCS = typeof window.getComputedStyle === 'function';\n\n\t\t\t\tfor (let i = nodes.length - 1; i >= 0; --i) {\n\t\t\t\t\tconst node = nodes[i];\n\n\t\t\t\t\tswitch (node.nodeType) {\n\t\t\t\t\tcase Node.ELEMENT_NODE:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\t\t\t\tif (tagName === 'BR') {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst styles = hasGCS ? window.getComputedStyle(node, null) : node.currentStyle;\n\n\t\t\t\t\t\t\tif (styles && styles.display) {\n\t\t\t\t\t\t\t\tif (styles.display === 'none') {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn styles.display === 'block';\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWebKit/Blink-based browsers do not attach any computed style\n\t\t\t\t\t\t\t\tinformation to elements until they're inserted into the DOM\n\t\t\t\t\t\t\t\t(and probably visible), not even the default browser styles\n\t\t\t\t\t\t\t\tand any user styles.  So, we make an assumption based on the\n\t\t\t\t\t\t\t\telement.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\t\tcase 'ADDRESS':\n\t\t\t\t\t\t\tcase 'ARTICLE':\n\t\t\t\t\t\t\tcase 'ASIDE':\n\t\t\t\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\t\t\t\tcase 'CENTER':\n\t\t\t\t\t\t\tcase 'DIV':\n\t\t\t\t\t\t\tcase 'DL':\n\t\t\t\t\t\t\tcase 'FIGURE':\n\t\t\t\t\t\t\tcase 'FOOTER':\n\t\t\t\t\t\t\tcase 'FORM':\n\t\t\t\t\t\t\tcase 'H1':\n\t\t\t\t\t\t\tcase 'H2':\n\t\t\t\t\t\t\tcase 'H3':\n\t\t\t\t\t\t\tcase 'H4':\n\t\t\t\t\t\t\tcase 'H5':\n\t\t\t\t\t\t\tcase 'H6':\n\t\t\t\t\t\t\tcase 'HEADER':\n\t\t\t\t\t\t\tcase 'HR':\n\t\t\t\t\t\t\tcase 'MAIN':\n\t\t\t\t\t\t\tcase 'NAV':\n\t\t\t\t\t\t\tcase 'OL':\n\t\t\t\t\t\t\tcase 'P':\n\t\t\t\t\t\t\tcase 'PRE':\n\t\t\t\t\t\t\tcase 'SECTION':\n\t\t\t\t\t\t\tcase 'TABLE':\n\t\t\t\t\t\t\tcase 'UL':\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tcase Node.COMMENT_NODE:\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\n\t\tcreateShadowSetterCallback : {\n\t\t\tvalue : (() => {\n\t\t\t\tlet macroParser = null;\n\n\t\t\t\tfunction cacheMacroParser() {\n\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\tmacroParser = Wikifier.Parser.get('macro');\n\n\t\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\t\tthrow new Error('cannot find \"macro\" parser');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn macroParser;\n\t\t\t\t}\n\n\t\t\t\tfunction getMacroContextShadowView() {\n\t\t\t\t\tconst macro = macroParser || cacheMacroParser();\n\t\t\t\t\tconst view  = new Set();\n\n\t\t\t\t\tfor (let context = macro.context; context !== null; context = context.parent) {\n\t\t\t\t\t\tif (context._shadows) {\n\t\t\t\t\t\t\tcontext._shadows.forEach(name => view.add(name));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [...view];\n\t\t\t\t}\n\n\t\t\t\tfunction helperCreateShadowSetterCallback(code) {\n\t\t\t\t\tconst shadowStore = {};\n\n\t\t\t\t\tgetMacroContextShadowView().forEach(varName => {\n\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t\t});\n\n\t\t\t\t\treturn function () {\n\t\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\t\tevaluation.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Evaluate the JavaScript.\n\t\t\t\t\t\t\treturn Scripting.evalJavaScript(code);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn helperCreateShadowSetterCallback;\n\t\t\t})()\n\t\t},\n\n\t\tparseSquareBracketedMarkup : {\n\t\t\tvalue : (() => {\n\t\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t\t'Error',     // error\n\t\t\t\t\t'DelimLTR',  // '|' or '->'\n\t\t\t\t\t'DelimRTL',  // '<-'\n\t\t\t\t\t'InnerMeta', // ']['\n\t\t\t\t\t'ImageMeta', // '[img[', '[<img[', or '[>img['\n\t\t\t\t\t'LinkMeta',  // '[['\n\t\t\t\t\t'Link',      // link destination\n\t\t\t\t\t'RightMeta', // ']]'\n\t\t\t\t\t'Setter',    // setter expression\n\t\t\t\t\t'Source',    // image source\n\t\t\t\t\t'Text'       // link text or image alt text\n\t\t\t\t]);\n\t\t\t\tconst Delim = Lexer.enumFromNames([ // delimiter state object (pseudo-enumeration)\n\t\t\t\t\t'None', // no delimiter encountered\n\t\t\t\t\t'LTR',  // '|' or '->'\n\t\t\t\t\t'RTL'   // '<-'\n\t\t\t\t]);\n\n\t\t\t\t// Lexing functions.\n\t\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\t\tloop: for (;;) {\n\t\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* falls through */\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t\t}\n\n\t\t\t\t\treturn lexer.pos;\n\t\t\t\t}\n\n\t\t\t\tfunction lexLeftMeta(lexer) {\n\t\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t}\n\n\t\t\t\t\t// Is link markup.\n\t\t\t\t\tif (lexer.accept('[')) {\n\t\t\t\t\t\tlexer.data.isLink = true;\n\t\t\t\t\t\tlexer.emit(Item.LinkMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\t// May be image markup.\n\t\t\t\t\telse {\n\t\t\t\t\t\tlexer.accept('<>'); // aligner syntax\n\n\t\t\t\t\t\tif (!lexer.accept('Ii') || !lexer.accept('Mm') || !lexer.accept('Gg') || !lexer.accept('[')) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlexer.data.isLink = false;\n\t\t\t\t\t\tlexer.emit(Item.ImageMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\t\t\t\t\treturn lexCoreComponents;\n\t\t\t\t}\n\n\t\t\t\tfunction lexCoreComponents(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\t\t\t\t\tlet delim = Delim.None;\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '|': // possible pipe ('|') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None) {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward();\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '-': // possible right arrow ('->') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '>') {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '<': // possible left arrow ('<-') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '-') {\n\t\t\t\t\t\t\t\tdelim = Delim.RTL;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimRTL);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexer.data.isLink ? lexSetter : lexImageLink;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexImageLink(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup link component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexSetter;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexSetter(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated single quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tif (lexer.peek() !== ']') {\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Setter);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Parse function.\n\t\t\t\tfunction parseSquareBracketedMarkup(w) {\n\t\t\t\t\t// Initialize the lexer.\n\t\t\t\t\tconst lexer  = new Lexer(w.source, lexLeftMeta);\n\n\t\t\t\t\t// Set the initial positions within the source string.\n\t\t\t\t\tlexer.start = lexer.pos = w.matchStart;\n\n\t\t\t\t\t// Lex the raw argument string.\n\t\t\t\t\tconst markup = {};\n\t\t\t\t\tconst items  = lexer.run();\n\t\t\t\t\tconst last   = items.last();\n\n\t\t\t\t\tif (last && last.type === Item.Error) {\n\t\t\t\t\t\tmarkup.error = last.message;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\titems.forEach(item => {\n\t\t\t\t\t\t\tconst text = item.text.trim();\n\n\t\t\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\t\t\tcase Item.ImageMeta:\n\t\t\t\t\t\t\t\tmarkup.isImage = true;\n\n\t\t\t\t\t\t\t\tif (text[1] === '<') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'left';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (text[1] === '>') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'right';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.LinkMeta:\n\t\t\t\t\t\t\t\tmarkup.isLink = true;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Link:\n\t\t\t\t\t\t\t\tif (text[0] === '~') {\n\t\t\t\t\t\t\t\t\tmarkup.forceInternal = true;\n\t\t\t\t\t\t\t\t\tmarkup.link = text.slice(1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tmarkup.link = text;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Setter:\n\t\t\t\t\t\t\t\tmarkup.setter = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Source:\n\t\t\t\t\t\t\t\tmarkup.source = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Text:\n\t\t\t\t\t\t\t\tmarkup.text = text;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tmarkup.pos = lexer.pos;\n\t\t\t\t\treturn markup;\n\t\t\t\t}\n\n\t\t\t\treturn parseSquareBracketedMarkup;\n\t\t\t\t/* eslint-enable no-param-reassign */\n\t\t\t})()\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Wikifier;\n})();\n/* eslint-enable max-len */\n\n/***********************************************************************************************************************\n\n\tmarkup/parserlib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, EOF, Engine, Lexer, Macro, MacroContext, Patterns, Scripting, State, Story, Template,\n\t       Wikifier, toStringOrDefault, throwError\n*/\n/* eslint \"no-param-reassign\": [ 2, { \"props\" : false } ] */\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _verbatimTagHandler(w) {\n\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\tconst match = this.lookahead.exec(w.source);\n\n\t\tif (match && match.index === w.matchStart) {\n\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(match[1])\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tParsers.\n\t*******************************************************************************************************************/\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByBlock',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^<<<\\\\n',\n\t\tterminator : '^<<<\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t.appendTo(w.output)\n\t\t\t\t\t.get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByLine',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^>+',\n\t\tlookahead  : /^>+/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curLevel = 0;\n\t\t\tlet newLevel = w.matchLength;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcurLevel = newLevel;\n\t\t\t\tw.subWikify(destStack[destStack.length - 1], this.terminator);\n\t\t\t\tjQuery(document.createElement('br')).appendTo(destStack[destStack.length - 1]);\n\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tnewLevel = match[0].length;\n\t\t\t\t\tw.nextMatch += match[0].length;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'macro',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<<',\n\t\tlookahead : new RegExp(`<<(/?${Patterns.macroName})(?:\\\\s*)((?:(?:\\`(?:\\\\\\\\.|[^\\`\\\\\\\\])*\\`)|(?:\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")|(?:'(?:\\\\\\\\.|[^'\\\\\\\\])*')|(?:\\\\[(?:[<>]?[Ii][Mm][Gg])?\\\\[[^\\\\r\\\\n]*?\\\\]\\\\]+)|[^>]|(?:>(?!>)))*)>>`, 'gm'),\n\t\tworking   : { source : '', name : '', arguments : '', index : 0 }, // the working parse object\n\t\tcontext   : null, // last execution context object (top-level macros, hierarchically, have a null context)\n\n\t\thandler(w) {\n\t\t\tconst matchStart = this.lookahead.lastIndex = w.matchStart;\n\n\t\t\tif (this.parseTag(w)) {\n\t\t\t\t/*\n\t\t\t\t\tIf `parseBody()` is called below, it will modify the current working\n\t\t\t\t\tvalues, so we must cache them now.\n\t\t\t\t*/\n\t\t\t\tconst nextMatch = w.nextMatch;\n\t\t\t\tconst name      = this.working.name;\n\t\t\t\tconst rawArgs   = this.working.arguments;\n\t\t\t\tlet macro;\n\n\t\t\t\ttry {\n\t\t\t\t\tmacro = Macro.get(name);\n\n\t\t\t\t\tif (macro) {\n\t\t\t\t\t\tlet payload = null;\n\n\t\t\t\t\t\tif (typeof macro.tags !== 'undefined') {\n\t\t\t\t\t\t\tpayload = this.parseBody(w, macro);\n\n\t\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\t\tw.nextMatch = nextMatch; // we must reset `w.nextMatch` here, as `parseBody()` modifies it\n\t\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t\t`cannot find a closing tag for macro <<${name}>>`,\n\t\t\t\t\t\t\t\t\t`${w.source.slice(matchStart, w.nextMatch)}\\u2026`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (typeof macro.handler === 'function') {\n\t\t\t\t\t\t\tconst args = !payload\n\t\t\t\t\t\t\t\t? this.createArgs(rawArgs, this.skipArgs(macro, macro.name))\n\t\t\t\t\t\t\t\t: payload[0].args;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tNew-style macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (typeof macro._MACRO_API !== 'undefined') {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tAdd the macro's execution context to the context chain.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tthis.context = new MacroContext({\n\t\t\t\t\t\t\t\t\tmacro,\n\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\t\t\tsource : w.source.slice(matchStart, w.nextMatch),\n\t\t\t\t\t\t\t\t\tparent : this.context,\n\t\t\t\t\t\t\t\t\tparser : w\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the execution context is properly restored in the event\n\t\t\t\t\t\t\t\t\tthat an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler.call(this.context);\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\tQUESTION: Swap to the following, which passes macro arguments in\n\t\t\t\t\t\t\t\t\t\tas parameters to the handler function, in addition to them being\n\t\t\t\t\t\t\t\t\t\tavailable on its `this`?  If so, it might still be something to\n\t\t\t\t\t\t\t\t\t\thold off on until v3, when the legacy macro API is removed.\n\n\t\t\t\t\t\t\t\t\t\tmacro.handler.apply(this.context, this.context.args);\n\t\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tthis.context = this.context.parent;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t[DEPRECATED] Old-style/legacy macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tSet up the raw arguments string.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tconst prevRawArgs = w._rawArgs;\n\t\t\t\t\t\t\t\tw._rawArgs = rawArgs;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the previous raw arguments string is properly restored in\n\t\t\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler(w.output, name, args, w, payload);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tw._rawArgs = prevRawArgs;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`macro <<${name}>> handler function ${typeof macro.handler === 'undefined' ? 'does not exist' : 'is not a function'}`,\n\t\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (Macro.tags.has(name)) {\n\t\t\t\t\t\tconst tags = Macro.tags.get(name);\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`child tag <<${name}>> was found outside of a call to its parent macro${tags.length === 1 ? '' : 's'} <<${tags.join('>>, <<')}>>`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`macro <<${name}>> does not exist`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute ${macro && macro.isWidget ? 'widget' : 'macro'} <<${name}>>: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.working.source    = '';\n\t\t\t\t\tthis.working.name      = '';\n\t\t\t\t\tthis.working.arguments = '';\n\t\t\t\t\tthis.working.index     = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t}\n\t\t},\n\n\t\tparseTag(w) {\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart && match[1]) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tthis.working.source    = w.source.slice(match.index, this.lookahead.lastIndex);\n\t\t\t\tthis.working.name      = match[1];\n\t\t\t\tthis.working.arguments = match[2];\n\t\t\t\tthis.working.index     = match.index;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseBody(w, macro) {\n\t\t\tconst openTag  = this.working.name;\n\t\t\tconst closeTag = `/${openTag}`;\n\t\t\tconst closeAlt = `end${openTag}`;\n\t\t\tconst bodyTags = Array.isArray(macro.tags) ? macro.tags : false;\n\t\t\tconst payload  = [];\n\t\t\tlet end          = -1;\n\t\t\tlet opened       = 1;\n\t\t\tlet curSource    = this.working.source;\n\t\t\tlet curTag       = this.working.name;\n\t\t\tlet curArgument  = this.working.arguments;\n\t\t\tlet contentStart = w.nextMatch;\n\n\t\t\twhile ((w.matchStart = w.source.indexOf(this.match, w.nextMatch)) !== -1) {\n\t\t\t\tif (!this.parseTag(w)) {\n\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = w.matchStart + this.match.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst tagSource = this.working.source;\n\t\t\t\tconst tagName   = this.working.name;\n\t\t\t\tconst tagArgs   = this.working.arguments;\n\t\t\t\tconst tagBegin  = this.working.index;\n\t\t\t\tconst tagEnd    = w.nextMatch;\n\t\t\t\tconst hasArgs   = tagArgs.trim() !== '';\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase openTag:\n\t\t\t\t\t++opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase closeAlt:\n\t\t\t\tcase closeTag:\n\t\t\t\t\tif (hasArgs) {\n\t\t\t\t\t\t// Skip over malformed closing tags and throw.\n\t\t\t\t\t\tw.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tthrow new Error(`malformed closing tag: \"${tagSource}\"`);\n\t\t\t\t\t}\n\t\t\t\t\t--opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (hasArgs && (tagName.startsWith('/') || tagName.startsWith('end'))) {\n\t\t\t\t\t\t// Skip over malformed alien closing tags.\n\t\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (opened === 1 && bodyTags) {\n\t\t\t\t\t\tfor (let i = 0, iend = bodyTags.length; i < iend; ++i) {\n\t\t\t\t\t\t\tif (tagName === bodyTags[i]) {\n\t\t\t\t\t\t\t\tpayload.push({\n\t\t\t\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tcurSource    = tagSource;\n\t\t\t\t\t\t\t\tcurTag       = tagName;\n\t\t\t\t\t\t\t\tcurArgument  = tagArgs;\n\t\t\t\t\t\t\t\tcontentStart = tagEnd;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (opened === 0) {\n\t\t\t\t\tpayload.push({\n\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t});\n\t\t\t\t\tend = tagEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (end !== -1) {\n\t\t\t\tw.nextMatch = end;\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tcreateArgs(rawArgsString, skipArgs) {\n\t\t\tconst args = skipArgs ? [] : this.parseArgs(rawArgsString);\n\n\t\t\t// Extend the args array with the raw and full argument strings.\n\t\t\tObject.defineProperties(args, {\n\t\t\t\traw : {\n\t\t\t\t\tvalue : rawArgsString\n\t\t\t\t},\n\t\t\t\tfull : {\n\t\t\t\t\tvalue : Scripting.parse(rawArgsString)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn args;\n\t\t},\n\n\t\tskipArgs(macro, tagName) {\n\t\t\tif (typeof macro.skipArgs !== 'undefined') {\n\t\t\t\tconst sa = macro.skipArgs;\n\n\t\t\t\treturn typeof sa === 'boolean' && sa || Array.isArray(sa) && sa.includes(tagName);\n\t\t\t}\n\t\t\t/* legacy */\n\t\t\telse if (typeof macro.skipArg0 !== 'undefined') {\n\t\t\t\treturn macro.skipArg0 && macro.name === tagName;\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseArgs : (() => {\n\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t'Error',        // error\n\t\t\t\t'Bareword',     // bare identifier\n\t\t\t\t'Expression',   // expression (backquoted)\n\t\t\t\t'String',       // quoted string (single or double)\n\t\t\t\t'SquareBracket' // [[…]] or [img[…]]\n\t\t\t]);\n\t\t\tconst spaceRe    = new RegExp(Patterns.space);\n\t\t\tconst notSpaceRe = new RegExp(Patterns.notSpace);\n\t\t\tconst varTest    = new RegExp(`^${Patterns.variable}`);\n\n\t\t\t// Lexing functions.\n\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\treturn lexer.pos;\n\t\t\t}\n\n\t\t\tfunction lexSpace(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(notSpaceRe);\n\n\t\t\t\tif (offset === EOF) {\n\t\t\t\t\t// no non-whitespace characters, so bail\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\telse if (offset !== 0) {\n\t\t\t\t\tlexer.pos += offset;\n\t\t\t\t\tlexer.ignore();\n\t\t\t\t}\n\n\t\t\t\t// determine what the next state is\n\t\t\t\tswitch (lexer.next()) {\n\t\t\t\tcase '`':\n\t\t\t\t\treturn lexExpression;\n\t\t\t\tcase '\"':\n\t\t\t\t\treturn lexDoubleQuote;\n\t\t\t\tcase \"'\":\n\t\t\t\t\treturn lexSingleQuote;\n\t\t\t\tcase '[':\n\t\t\t\t\treturn lexSquareBracket;\n\t\t\t\tdefault:\n\t\t\t\t\treturn lexBareword;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction lexExpression(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '`') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated backquote expression');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.Expression);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexDoubleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated double quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSingleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated single quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSquareBracket(lexer) {\n\t\t\t\tconst imgMeta = '<>IiMmGg';\n\t\t\t\tlet what;\n\n\t\t\t\tif (lexer.accept(imgMeta)) {\n\t\t\t\t\twhat = 'image';\n\t\t\t\t\tlexer.acceptRun(imgMeta);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\twhat = 'link';\n\t\t\t\t}\n\n\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t}\n\n\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\tcase '[':\n\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ']':\n\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\tif (lexer.depth < 0) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, \"unexpected right square bracket ']'\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\tif (lexer.next() === ']') {\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.SquareBracket);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexBareword(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(spaceRe);\n\t\t\t\tlexer.pos = offset === EOF ? lexer.source.length : lexer.pos + offset;\n\t\t\t\tlexer.emit(Item.Bareword);\n\t\t\t\treturn offset === EOF ? null : lexSpace;\n\t\t\t}\n\n\t\t\t// Parse function.\n\t\t\tfunction parseMacroArgs(rawArgsString) {\n\t\t\t\t// Initialize the lexer.\n\t\t\t\tconst lexer = new Lexer(rawArgsString, lexSpace);\n\t\t\t\tconst args  = [];\n\n\t\t\t\t// Lex the raw argument string.\n\t\t\t\tlexer.run().forEach(item => {\n\t\t\t\t\tlet arg = item.text;\n\n\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\tcase Item.Error:\n\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${item.message}`);\n\n\t\t\t\t\tcase Item.Bareword:\n\t\t\t\t\t\t// A variable, so substitute its value.\n\t\t\t\t\t\tif (varTest.test(arg)) {\n\t\t\t\t\t\t\targ = State.getVar(arg);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Property access on the settings or setup objects, so try to evaluate it.\n\t\t\t\t\t\telse if (/^(?:settings|setup)[.[]/.test(arg)) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(arg);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Null literal, so convert it into null.\n\t\t\t\t\t\telse if (arg === 'null') {\n\t\t\t\t\t\t\targ = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Undefined literal, so convert it into undefined.\n\t\t\t\t\t\telse if (arg === 'undefined') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean true literal, so convert it into true.\n\t\t\t\t\t\telse if (arg === 'true') {\n\t\t\t\t\t\t\targ = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean false literal, so convert it into false.\n\t\t\t\t\t\telse if (arg === 'false') {\n\t\t\t\t\t\t\targ = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// NaN literal, so convert it into NaN.\n\t\t\t\t\t\telse if (arg === 'NaN') {\n\t\t\t\t\t\t\targ = NaN;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Attempt to convert it into a number, in case it's a numeric literal.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst argAsNum = Number(arg);\n\n\t\t\t\t\t\t\tif (!Number.isNaN(argAsNum)) {\n\t\t\t\t\t\t\t\targ = argAsNum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.Expression:\n\t\t\t\t\t\targ = arg.slice(1, -1).trim(); // remove the backquotes and trim the expression\n\n\t\t\t\t\t\t// Empty backquotes.\n\t\t\t\t\t\tif (arg === '') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Evaluate the expression.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tThe enclosing parenthesis here are necessary to force a code string\n\t\t\t\t\t\t\t\t\tconsisting solely of an object literal to be evaluated as such, rather\n\t\t\t\t\t\t\t\t\tthan as a code block.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(`(${arg})`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument expression \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.String:\n\t\t\t\t\t\t// Evaluate the string to handle escaped characters.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targ = Scripting.evalJavaScript(arg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument string \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.SquareBracket:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\t\tsource     : arg,\n\t\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${markup.error}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (markup.pos < arg.length) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": unexpected character(s) \"${arg.slice(markup.pos)}\" (pos: ${markup.pos})`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Convert to a link or image object.\n\t\t\t\t\t\t\tif (markup.isLink) {\n\t\t\t\t\t\t\t\t// .isLink, [.text], [.forceInternal], .link, [.setter]\n\t\t\t\t\t\t\t\targ = { isLink : true };\n\t\t\t\t\t\t\t\targ.count    = markup.hasOwnProperty('text') ? 2 : 1;\n\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\targ.text     = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : arg.link;\n\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\targ.setFn    = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (markup.isImage) {\n\t\t\t\t\t\t\t\t// .isImage, [.align], [.title], .source, [.forceInternal], [.link], [.setter]\n\t\t\t\t\t\t\t\targ = (source => {\n\t\t\t\t\t\t\t\t\tconst imgObj = {\n\t\t\t\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t\t\t\tisImage : true\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t// Check for Twine 1.4 Base64 image passage transclusion.\n\t\t\t\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\t\t\t\timgObj.source  = passage.text;\n\t\t\t\t\t\t\t\t\t\t\timgObj.passage = passage.title;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn imgObj;\n\t\t\t\t\t\t\t\t})(Wikifier.helpers.evalPassageId(markup.source));\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\t\t\t\t\t\targ.align = markup.align;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\t\t\t\t\t\targ.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\targ.setFn = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\targs.push(arg);\n\t\t\t\t});\n\n\t\t\t\treturn args;\n\t\t\t}\n\n\t\t\treturn parseMacroArgs;\n\t\t})()\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'link',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[\\\\[[^[]',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// text=(text), forceInternal=(~), link=link, setter=(setter)\n\t\t\tconst link  = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\tconst text  = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : link;\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\n\t\t\t// Debug view setup.\n\t\t\tconst output = (Config.debug\n\t\t\t\t? new DebugView(w.output, 'link-markup', '[[link]]', w.source.slice(w.matchStart, w.nextMatch))\n\t\t\t\t: w\n\t\t\t).output;\n\n\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\tWikifier.createInternalLink(output, link, text, setFn);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tWikifier.createExternalLink(output, link, text);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'urlLink',\n\t\tprofiles : ['core'],\n\t\tmatch    : Patterns.url,\n\n\t\thandler(w) {\n\t\t\tw.outputText(Wikifier.createExternalLink(w.output, w.matchText), w.matchStart, w.nextMatch);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'image',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[[<>]?[Ii][Mm][Gg]\\\\[',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// Debug view setup.\n\t\t\tlet debugView;\n\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tw.output,\n\t\t\t\t\t'image-markup',\n\t\t\t\t\tmarkup.hasOwnProperty('link') ? '[img[][link]]' : '[img[]]',\n\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// align=(left|right), title=(title), source=source, forceInternal=(~), link=(link), setter=(setter)\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\t\t\tlet el     = (Config.debug ? debugView : w).output;\n\t\t\tlet source;\n\n\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\tconst link = Wikifier.helpers.evalPassageId(markup.link);\n\n\t\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\t\tel = Wikifier.createInternalLink(el, link, null, setFn);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tel = Wikifier.createExternalLink(el, link);\n\t\t\t\t}\n\n\t\t\t\tel.classList.add('link-image');\n\t\t\t}\n\n\t\t\tel = jQuery(document.createElement('img'))\n\t\t\t\t.appendTo(el)\n\t\t\t\t.get(0);\n\t\t\tsource = Wikifier.helpers.evalPassageId(markup.source);\n\n\t\t\t// Check for image passage transclusion.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\tel.setAttribute('data-passage', passage.title);\n\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tel.src = source;\n\n\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\tel.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t}\n\n\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\tel.align = markup.align;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'monospacedByBlock',\n\t\tprofiles  : ['block'],\n\t\tmatch     : '^\\\\{\\\\{\\\\{\\\\n',\n\t\tlookahead : /^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tconst pre = jQuery(document.createElement('pre'));\n\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t.text(match[1])\n\t\t\t\t\t.appendTo(pre);\n\t\t\t\tpre.appendTo(w.output);\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'formatByChar',\n\t\tprofiles : ['core'],\n\t\tmatch    : \"''|//|__|\\\\^\\\\^|~~|==|\\\\{\\\\{\\\\{\",\n\n\t\thandler(w) {\n\t\t\tswitch (w.matchText) {\n\t\t\tcase \"''\":\n\t\t\t\tw.subWikify(jQuery(document.createElement('strong')).appendTo(w.output).get(0), \"''\");\n\t\t\t\tbreak;\n\n\t\t\tcase '//':\n\t\t\t\tw.subWikify(jQuery(document.createElement('em')).appendTo(w.output).get(0), '//');\n\t\t\t\tbreak;\n\n\t\t\tcase '__':\n\t\t\t\tw.subWikify(jQuery(document.createElement('u')).appendTo(w.output).get(0), '__');\n\t\t\t\tbreak;\n\n\t\t\tcase '^^':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sup')).appendTo(w.output).get(0), '\\\\^\\\\^');\n\t\t\t\tbreak;\n\n\t\t\tcase '~~':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sub')).appendTo(w.output).get(0), '~~');\n\t\t\t\tbreak;\n\n\t\t\tcase '==':\n\t\t\t\tw.subWikify(jQuery(document.createElement('s')).appendTo(w.output).get(0), '==');\n\t\t\t\tbreak;\n\n\t\t\tcase '{{{':\n\t\t\t\t{\n\t\t\t\t\tconst lookahead = /\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}/gm;\n\n\t\t\t\t\tlookahead.lastIndex = w.matchStart;\n\n\t\t\t\t\tconst match = lookahead.exec(w.source);\n\n\t\t\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t\t\t.text(match[1])\n\t\t\t\t\t\t\t.appendTo(w.output);\n\t\t\t\t\t\tw.nextMatch = lookahead.lastIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'customStyle',\n\t\tprofiles   : ['core'],\n\t\tmatch      : '@@',\n\t\tterminator : '@@',\n\t\tblockRe    : /\\s*\\n/gm,\n\n\t\thandler(w) {\n\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\n\t\t\tthis.blockRe.lastIndex = w.nextMatch; // must follow the call to `inlineCss()`\n\n\t\t\tconst blockMatch = this.blockRe.exec(w.source);\n\t\t\tconst blockLevel = blockMatch && blockMatch.index === w.nextMatch;\n\t\t\tconst $el        = jQuery(document.createElement(blockLevel ? 'div' : 'span'))\n\t\t\t\t.appendTo(w.output);\n\n\t\t\tif (css.classes.length === 0 && css.id === '' && Object.keys(css.styles).length === 0) {\n\t\t\t\t$el.addClass('marked');\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcss.classes.forEach(className => $el.addClass(className));\n\n\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t$el.attr('id', css.id);\n\t\t\t\t}\n\n\t\t\t\t$el.css(css.styles);\n\t\t\t}\n\n\t\t\tif (blockLevel) {\n\t\t\t\t// Skip the leading and, if it exists, trailing newlines.\n\t\t\t\tw.nextMatch += blockMatch[0].length;\n\t\t\t\tw.subWikify($el[0], `\\\\n?${this.terminator}`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.subWikify($el[0], this.terminator);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimText',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '\"{3}|<[Nn][Oo][Ww][Ii][Kk][Ii]>',\n\t\tlookahead : /(?:\"{3}((?:.|\\n)*?)\"{3})|(?:<[Nn][Oo][Ww][Ii][Kk][Ii]>((?:.|\\n)*?)<\\/[Nn][Oo][Ww][Ii][Kk][Ii]>)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('verbatim')\n\t\t\t\t\t.text(match[1] || match[2])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'horizontalRule',\n\t\tprofiles : ['core'],\n\t\tmatch    : '^----+$\\\\n?|<[Hh][Rr]\\\\s*/?>\\\\n?',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createElement('hr')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'emdash',\n\t\tprofiles : ['core'],\n\t\tmatch    : '--',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('\\u2014')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'doubleDollarSign',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\${2}', // eslint-disable-line no-template-curly-in-string\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('$')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tSupported syntax:\n\t\t\t\t$variable\n\t\t\t\t$variable.property\n\t\t\t\t$variable[numericIndex]\n\t\t\t\t$variable[\"property\"]\n\t\t\t\t$variable['property']\n\t\t\t\t$variable[$indexOrPropertyVariable]\n\t\t*/\n\t\tname     : 'nakedVariable',\n\t\tprofiles : ['core'],\n\t\tmatch    : `${Patterns.variable}(?:(?:\\\\.${Patterns.identifier})|(?:\\\\[\\\\d+\\\\])|(?:\\\\[\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\"\\\\])|(?:\\\\['(?:\\\\\\\\.|[^'\\\\\\\\])+'\\\\])|(?:\\\\[${Patterns.variable}\\\\]))*`,\n\n\t\thandler(w) {\n\t\t\tconst result = toStringOrDefault(State.getVar(w.matchText), null);\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'variable', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'template',\n\t\tprofiles : ['core'],\n\t\tmatch    : `\\\\?${Patterns.templateName}`,\n\n\t\thandler(w) {\n\t\t\tconst name = w.matchText.slice(1);\n\t\t\tlet template = Template.get(name);\n\t\t\tlet result   = null;\n\n\t\t\t// If we have an array of templates, randomly choose one.\n\t\t\tif (template instanceof Array) {\n\t\t\t\ttemplate = template.random();\n\t\t\t}\n\n\t\t\tswitch (typeof template) {\n\t\t\tcase 'function':\n\t\t\t\ttry {\n\t\t\t\t\tresult = toStringOrDefault(template.call({ name }), null);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute function template ?${name}: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tresult = template;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'template', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'heading',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^!{1,6}',\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement(`h${w.matchLength}`)).appendTo(w.output).get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'table',\n\t\tprofiles       : ['block'],\n\t\tmatch          : '^\\\\|(?:[^\\\\n]*)\\\\|(?:[fhck]?)$',\n\t\tlookahead      : /^\\|([^\\n]*)\\|([fhck]?)$/gm,\n\t\trowTerminator  : '\\\\|(?:[cfhk]?)$\\\\n?',\n\t\tcellPattern    : '(?:\\\\|([^\\\\n\\\\|]*)\\\\|)|(\\\\|[cfhk]?$\\\\n?)',\n\t\tcellTerminator : '(?:\\\\u0020*)\\\\|',\n\t\trowTypes       : { c : 'caption', f : 'tfoot', h : 'thead', '' : 'tbody' }, // eslint-disable-line id-length\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst table       = jQuery(document.createElement('table')).appendTo(w.output).get(0);\n\t\t\tconst prevColumns = [];\n\t\t\tlet curRowType    = null;\n\t\t\tlet $rowContainer = null;\n\t\t\tlet rowCount      = 0;\n\t\t\tlet matched;\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst nextRowType = match[2];\n\n\t\t\t\t\tif (nextRowType === 'k') {\n\t\t\t\t\t\ttable.className = match[1];\n\t\t\t\t\t\tw.nextMatch += match[0].length + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (nextRowType !== curRowType) {\n\t\t\t\t\t\t\tcurRowType = nextRowType;\n\t\t\t\t\t\t\t$rowContainer = jQuery(document.createElement(this.rowTypes[nextRowType]))\n\t\t\t\t\t\t\t\t.appendTo(table);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (curRowType === 'c') {\n\t\t\t\t\t\t\t$rowContainer.css('caption-side', rowCount === 0 ? 'top' : 'bottom');\n\t\t\t\t\t\t\tw.nextMatch += 1;\n\t\t\t\t\t\t\tw.subWikify($rowContainer[0], this.rowTerminator);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.rowHandler(\n\t\t\t\t\t\t\t\tw,\n\t\t\t\t\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t\t\t\t\t.appendTo($rowContainer)\n\t\t\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\t\t\tprevColumns\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t++rowCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t},\n\n\t\trowHandler(w, rowEl, prevColumns) {\n\t\t\tconst cellRe = new RegExp(this.cellPattern, 'gm');\n\t\t\tlet col         = 0;\n\t\t\tlet curColCount = 1;\n\t\t\tlet matched;\n\n\t\t\tdo {\n\t\t\t\tcellRe.lastIndex = w.nextMatch;\n\n\t\t\t\tconst cellMatch = cellRe.exec(w.source);\n\n\t\t\t\tmatched = cellMatch && cellMatch.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tif (cellMatch[1] === '~') {\n\t\t\t\t\t\tconst last = prevColumns[col];\n\n\t\t\t\t\t\tif (last) {\n\t\t\t\t\t\t\t++last.rowCount;\n\t\t\t\t\t\t\tlast.$element\n\t\t\t\t\t\t\t\t.attr('rowspan', last.rowCount)\n\t\t\t\t\t\t\t\t.css('vertical-align', 'middle');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[1] === '>') {\n\t\t\t\t\t\t++curColCount;\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[2]) {\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t++w.nextMatch;\n\n\t\t\t\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\t\t\t\t\t\tlet spaceLeft  = false;\n\t\t\t\t\t\tlet spaceRight = false;\n\t\t\t\t\t\tlet $cell;\n\n\t\t\t\t\t\twhile (w.source.substr(w.nextMatch, 1) === ' ') {\n\t\t\t\t\t\t\tspaceLeft = true;\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (w.source.substr(w.nextMatch, 1) === '!') {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('th')).appendTo(rowEl);\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('td')).appendTo(rowEl);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevColumns[col] = {\n\t\t\t\t\t\t\trowCount : 1,\n\t\t\t\t\t\t\t$element : $cell\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (curColCount > 1) {\n\t\t\t\t\t\t\t$cell.attr('colspan', curColCount);\n\t\t\t\t\t\t\tcurColCount = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.subWikify($cell[0], this.cellTerminator);\n\n\t\t\t\t\t\tif (w.matchText.substr(w.matchText.length - 2, 1) === ' ') {\n\t\t\t\t\t\t\tspaceRight = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcss.classes.forEach(className => $cell.addClass(className));\n\n\t\t\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t\t\t$cell.attr('id', css.id);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (spaceLeft && spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'center';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceLeft) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'right';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'left';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$cell.css(css.styles);\n\n\t\t\t\t\t\tw.nextMatch = w.nextMatch - 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'list',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^(?:(?:\\\\*+)|(?:#+))',\n\t\tlookahead  : /^(?:(\\*+)|(#+))/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curType  = null;\n\t\t\tlet curLevel = 0;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst newType  = match[2] ? 'ol' : 'ul';\n\t\t\t\t\tconst newLevel = match[0].length;\n\n\t\t\t\t\tw.nextMatch += match[0].length;\n\n\t\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel === curLevel && newType !== curType) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tcurLevel = newLevel;\n\t\t\t\t\tcurType = newType;\n\t\t\t\t\tw.subWikify(\n\t\t\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\tthis.terminator\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'commentByBlock',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '(?:/(?:%|\\\\*))|(?:<!--)',\n\t\tlookahead : /(?:\\/(%|\\*)(?:(?:.|\\n)*?)\\1\\/)|(?:<!--(?:(?:.|\\n)*?)-->)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineContinuation',\n\t\tprofiles : ['core'],\n\n\t\t// WARNING: The ordering here is important: end-of-line, start-of-line, end-of-string, start-of-string.\n\t\tmatch : `\\\\\\\\${Patterns.spaceNoTerminator}*\\\\n|\\\\n${Patterns.spaceNoTerminator}*\\\\\\\\|\\\\n?\\\\\\\\${Patterns.spaceNoTerminator}*$|^${Patterns.spaceNoTerminator}*\\\\\\\\\\\\n?`,\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineBreak',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\n|<[Bb][Rr]\\\\s*/?>',\n\n\t\thandler(w) {\n\t\t\tif (!w.options.nobr) {\n\t\t\t\tjQuery(document.createElement('br')).appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'htmlCharacterReference',\n\t\tprofiles : ['core'],\n\t\tmatch    : '(?:(?:&#?[0-9A-Za-z]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9A-Fa-f]|1D[C-Fc-f][0-9A-Fa-f]|20[D-Fd-f][0-9A-Fa-f]|FE2[0-9A-Fa-f])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[0-9A-Za-z]{2,8};)',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(w.matchText)\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'xmlProlog',\n\t\tprofiles : ['core'],\n\t\tmatch    : '<\\\\?[Xx][Mm][Ll][^>]*\\\\?>',\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimHtml',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Hh][Tt][Mm][Ll]>',\n\t\tlookahead : /<[Hh][Tt][Mm][Ll]>((?:.|\\n)*?)<\\/[Hh][Tt][Mm][Ll]>/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimScriptTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Cc][Rr][Ii][Pp][Tt][^>]*>',\n\t\tlookahead : /(<[Ss][Cc][Rr][Ii][Pp][Tt]*>(?:.|\\n)*?<\\/[Ss][Cc][Rr][Ii][Pp][Tt]>)/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'styleTag',\n\t\tprofiles       : ['core'],\n\t\tmatch          : '<[Ss][Tt][Yy][Ll][Ee][^>]*>',\n\t\tlookahead      : /(<[Ss][Tt][Yy][Ll][Ee]*>)((?:.|\\n)*?)(<\\/[Ss][Tt][Yy][Ll][Ee]>)/gm,\n\t\timageMarkup    : new RegExp(Patterns.cssImage, 'g'),\n\t\thasImageMarkup : new RegExp(Patterns.cssImage),\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tlet css = match[2];\n\n\t\t\t\t// Check for wiki image transclusion.\n\t\t\t\tif (this.hasImageMarkup.test(css)) {\n\t\t\t\t\tthis.imageMarkup.lastIndex = 0;\n\n\t\t\t\t\tcss = css.replace(this.imageMarkup, wikiImage => {\n\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\tsource = passage.text;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t\t.append(match[1] + css + match[3])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'svgTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Vv][Gg][^>]*>',\n\t\tlookahead : /(<[Ss][Vv][Gg][^>]*>(?:.|\\n)*?<\\/[Ss][Vv][Gg]>)/gm,\n\t\tnamespace : 'http://www.w3.org/2000/svg',\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tconst $frag = jQuery(document.createDocumentFragment()).append(match[1]);\n\n\t\t\t\t// Postprocess the relevant SVG element nodes.\n\t\t\t\t$frag.find('a[data-passage],image[data-passage]').each((_, el) => {\n\t\t\t\t\tconst tagName = el.tagName.toLowerCase();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`svg|<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$frag.appendTo(w.output);\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// '<image>' element, so attempt media passage transclusion.\n\t\t\t\tif (tagName === 'image') {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t// NOTE: SVG `.href` IDL attribute is read-only,\n\t\t\t\t\t\t\t// so set its `href` content attribute instead.\n\t\t\t\t\t\t\tel.setAttribute('href', passage.text.trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>'.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tNOTE: This parser MUST come after any parser which handles HTML tag-\n\t\t\tlike constructs—e.g. 'verbatimText', 'horizontalRule', 'lineBreak',\n\t\t\t'xmlProlog', 'verbatimHtml', 'verbatimSvgTag', 'verbatimScriptTag',\n\t\t\tand 'styleTag'.\n\t\t*/\n\t\tname      : 'htmlTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<\\\\w+(?:\\\\s+[^\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\s\"\\'>\\\\/=]+(?:\\\\s*=\\\\s*(?:\"[^\"]*?\"|\\'[^\\']*?\\'|[^\\\\s\"\\'=<>`]+))?)*\\\\s*\\\\/?>',\n\t\ttagRe     : /^<(\\w+)/,\n\t\tmediaTags : ['audio', 'img', 'source', 'track', 'video'], // NOTE: The `<picture>` element should not be in this list.\n\t\tnobrTags  : ['audio', 'colgroup', 'datalist', 'dl', 'figure', 'meter', 'ol', 'optgroup', 'picture', 'progress', 'ruby', 'select', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'ul', 'video'],\n\t\tvoidTags  : ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],\n\n\t\thandler(w) {\n\t\t\tconst tagMatch = this.tagRe.exec(w.matchText);\n\t\t\tconst tag      = tagMatch && tagMatch[1];\n\t\t\tconst tagName  = tag && tag.toLowerCase();\n\n\t\t\tif (tagName) {\n\t\t\t\tconst isVoid = this.voidTags.includes(tagName) || w.matchText.endsWith('/>');\n\t\t\t\tconst isNobr = this.nobrTags.includes(tagName);\n\t\t\t\tlet terminator;\n\t\t\t\tlet terminatorMatch;\n\n\t\t\t\tif (!isVoid) {\n\t\t\t\t\tterminator = `<\\\\/${tagName}\\\\s*>`;\n\n\t\t\t\t\tconst terminatorRe = new RegExp(terminator, 'gim'); // ignore case during match\n\n\t\t\t\t\tterminatorRe.lastIndex = w.matchStart;\n\t\t\t\t\tterminatorMatch = terminatorRe.exec(w.source);\n\t\t\t\t}\n\n\t\t\t\tif (isVoid || terminatorMatch) {\n\t\t\t\t\tlet output    = w.output;\n\t\t\t\t\tlet el        = document.createElement(w.output.tagName);\n\t\t\t\t\tlet debugView;\n\n\t\t\t\t\tel.innerHTML = w.matchText;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of a `while` statement here is curious, however,\n\t\t\t\t\t\tI'm hesitant to change it for fear of breaking some edge case.\n\t\t\t\t\t*/\n\t\t\t\t\twhile (el.firstChild) {\n\t\t\t\t\t\tel = el.firstChild;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\n\t\t\t\t\t\t// Debug view setup.\n\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`html-${tagName}`,\n\t\t\t\t\t\t\t\ttagName,\n\t\t\t\t\t\t\t\tw.matchText\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tdebugView.modes({\n\t\t\t\t\t\t\t\tblock   : tagName === 'img',\n\t\t\t\t\t\t\t\tnonvoid : terminatorMatch\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toutput = debugView.output;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (terminatorMatch) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists\n\t\t\t\t\t\t\tsolely to ensure that the options stack is properly restored in\n\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the call to\n\t\t\t\t\t\t\t`subWikify()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tWikifier.Option.push({ nobr : isNobr });\n\t\t\t\t\t\t\tw.subWikify(el, terminator, { ignoreTerminatorCase : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tWikifier.Option.pop();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tDebug view modification.  If the current element has any debug\n\t\t\t\t\t\t\tview descendants who have \"block\" mode set, then set its debug\n\t\t\t\t\t\t\tview to the same.  It just makes things look a bit nicer.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif (debugView && jQuery(el).find('.debug.block').length > 0) {\n\t\t\t\t\t\t\tdebugView.modes({ block : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of `cloneNode(true)` here for `<track>` elements\n\t\t\t\t\t\tis necessary to workaround a poorly understood rehoming issue.\n\t\t\t\t\t*/\n\t\t\t\t\toutput.appendChild(tagName === 'track' ? el.cloneNode(true) : el);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot find a closing tag for HTML <${tag}>`,\n\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// Media element, so attempt media passage transclusion.\n\t\t\t\tif (this.mediaTags.includes(tagName)) {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tlet parentName;\n\t\t\t\t\t\tlet twineTag;\n\n\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\tcase 'audio':\n\t\t\t\t\t\tcase 'video':\n\t\t\t\t\t\t\ttwineTag = `Twine.${tagName}`;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'img':\n\t\t\t\t\t\t\ttwineTag = 'Twine.image';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'track':\n\t\t\t\t\t\t\ttwineTag = 'Twine.vtt';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'source':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst $parent = $(el).closest('audio,picture,video');\n\n\t\t\t\t\t\t\t\tif ($parent.length) {\n\t\t\t\t\t\t\t\t\tparentName = $parent.get(0).tagName.toLowerCase();\n\t\t\t\t\t\t\t\t\ttwineTag = `Twine.${parentName === 'picture' ? 'image' : parentName}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (passage.tags.includes(twineTag)) {\n\t\t\t\t\t\t\tel[parentName === 'picture' ? 'srcset' : 'src'] = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>', '<area>', '<button>', etc.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/template.js\n\n\tCopyright © 2019–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\nvar Template = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Template definitions.\n\tconst _templates = new Map();\n\n\t// Valid template name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.templateName})$`);\n\n\t// Valid template type predicate.\n\tconst _validType = template => {\n\t\tconst templateType = typeof template;\n\t\treturn templateType === 'function' || templateType === 'string';\n\t};\n\n\n\t/*******************************************************************************\n\t\tTemplate Functions.\n\t*******************************************************************************/\n\n\tfunction templateAdd(name, template) {\n\t\tif (\n\t\t\t   !_validType(template)\n\t\t\t&& !(template instanceof Array && template.length > 0 && template.every(_validType))\n\t\t) {\n\t\t\tthrow new TypeError(`invalid template type (${name}); templates must be: functions, strings, or an array of either`);\n\t\t}\n\n\t\t(name instanceof Array ? name : [name]).forEach(name => {\n\t\t\tif (!_validNameRe.test(name)) {\n\t\t\t\tthrow new Error(`invalid template name \"${name}\"`);\n\t\t\t}\n\t\t\tif (_templates.has(name)) {\n\t\t\t\tthrow new Error(`cannot clobber existing template ?${name}`);\n\t\t\t}\n\n\t\t\t_templates.set(name, template);\n\t\t});\n\t}\n\n\tfunction templateDelete(name) {\n\t\t(name instanceof Array ? name : [name]).forEach(name => _templates.delete(name));\n\t}\n\n\tfunction templateGet(name) {\n\t\treturn _templates.has(name) ? _templates.get(name) : null;\n\t}\n\n\tfunction templateHas(name) {\n\t\treturn _templates.has(name);\n\t}\n\n\tfunction templateSize() {\n\t\treturn _templates.size;\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tadd    : { value : templateAdd },\n\t\tdelete : { value : templateDelete },\n\t\tget    : { value : templateGet },\n\t\thas    : { value : templateHas },\n\t\tsize   : { get : templateSize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macro.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Scripting, macros */\n\nvar Macro = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Macro definitions.\n\tconst _macros = {};\n\n\t// Map of all macro tags and their parents (key: 'tag name' => value: ['list of parent names']).\n\tconst _tags = {};\n\n\t// Valid macro name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.macroName})$`);\n\n\n\t/*******************************************************************************************************************\n\t\tMacros Functions.\n\t*******************************************************************************************************************/\n\tfunction macrosAdd(name, def) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosAdd(name, def));\n\t\t\treturn;\n\t\t}\n\n\t\tif (!_validNameRe.test(name)) {\n\t\t\tthrow new Error(`invalid macro name \"${name}\"`);\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing macro <<${name}>>`);\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber child tag <<${name}>> of parent macro${_tags[name].length === 1 ? '' : 's'} <<${_tags[name].join('>>, <<')}>>`);\n\t\t}\n\n\t\ttry {\n\t\t\tif (typeof def === 'object') {\n\t\t\t\t// Add the macro definition.\n\t\t\t\t//\n\t\t\t\t// NOTE: Since `macrosGet()` may return legacy macros, we add the `_MACRO_API`\n\t\t\t\t// flag to (modern) API macros, so that the macro formatter will know how to\n\t\t\t\t// call the macro.  This should be removed in v3.\n\t\t\t\t_macros[name] = Object.assign(Object.create(null), def, { _MACRO_API : true });\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Add the macro alias.\n\t\t\t\tif (macrosHas(def)) {\n\t\t\t\t\t_macros[name] = Object.create(_macros[def], {\n\t\t\t\t\t\t_ALIAS_OF : {\n\t\t\t\t\t\t\tenumerable : true,\n\t\t\t\t\t\t\tvalue      : def\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`cannot create alias of nonexistent macro <<${def}>>`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(_macros, name, { writable : false });\n\t\t}\n\t\tcatch (ex) {\n\t\t\tif (ex.name === 'TypeError') {\n\t\t\t\tthrow new Error(`cannot clobber protected macro <<${name}>>`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`unknown error when attempting to add macro <<${name}>>: [${ex.name}] ${ex.message}`);\n\t\t\t}\n\t\t}\n\n\t\t// Tags post-processing.\n\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\tif (_macros[name].tags == null) { // lazy equality for null\n\t\t\t\ttagsRegister(name);\n\t\t\t}\n\t\t\telse if (Array.isArray(_macros[name].tags)) {\n\t\t\t\ttagsRegister(name, _macros[name].tags);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`bad value for \"tags\" property of macro <<${name}>>`);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction macrosDelete(name) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosDelete(name));\n\t\t\treturn;\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\t// Tags pre-processing.\n\t\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\t\ttagsUnregister(name);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Remove the macro definition.\n\t\t\t\tObject.defineProperty(_macros, name, { writable : true });\n\t\t\t\tdelete _macros[name];\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tthrow new Error(`unknown error removing macro <<${name}>>: ${ex.message}`);\n\t\t\t}\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot remove child tag <<${name}>> of parent macro <<${_tags[name]}>>`);\n\t\t}\n\t}\n\n\tfunction macrosIsEmpty() {\n\t\treturn Object.keys(_macros).length === 0;\n\t}\n\n\tfunction macrosHas(name) {\n\t\treturn _macros.hasOwnProperty(name);\n\t}\n\n\tfunction macrosGet(name) {\n\t\tlet macro = null;\n\n\t\tif (macrosHas(name) && typeof _macros[name].handler === 'function') {\n\t\t\tmacro = _macros[name];\n\t\t}\n\t\t/* legacy macro support */\n\t\telse if (macros.hasOwnProperty(name) && typeof macros[name].handler === 'function') {\n\t\t\tmacro = macros[name];\n\t\t}\n\t\t/* /legacy macro support */\n\n\t\treturn macro;\n\t}\n\n\tfunction macrosInit(handler = 'init') { // eslint-disable-line no-unused-vars\n\t\tObject.keys(_macros).forEach(name => {\n\t\t\tif (typeof _macros[name][handler] === 'function') {\n\t\t\t\t_macros[name][handler](name);\n\t\t\t}\n\t\t});\n\n\t\t/* legacy macro support */\n\t\tObject.keys(macros).forEach(name => {\n\t\t\tif (typeof macros[name][handler] === 'function') {\n\t\t\t\tmacros[name][handler](name);\n\t\t\t}\n\t\t});\n\t\t/* /legacy macro support */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTags Functions.\n\t*******************************************************************************************************************/\n\tfunction tagsRegister(parent, bodyTags) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tconst endTags = [`/${parent}`, `end${parent}`]; // automatically create the closing tags\n\t\tconst allTags = [].concat(endTags, Array.isArray(bodyTags) ? bodyTags : []);\n\n\t\tfor (let i = 0; i < allTags.length; ++i) {\n\t\t\tconst tag = allTags[i];\n\n\t\t\tif (macrosHas(tag)) {\n\t\t\t\tthrow new Error('cannot register tag for an existing macro');\n\t\t\t}\n\n\t\t\tif (tagsHas(tag)) {\n\t\t\t\tif (!_tags[tag].includes(parent)) {\n\t\t\t\t\t_tags[tag].push(parent);\n\t\t\t\t\t_tags[tag].sort();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_tags[tag] = [parent];\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction tagsUnregister(parent) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tObject.keys(_tags).forEach(tag => {\n\t\t\tconst i = _tags[tag].indexOf(parent);\n\n\t\t\tif (i !== -1) {\n\t\t\t\tif (_tags[tag].length === 1) {\n\t\t\t\t\tdelete _tags[tag];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_tags[tag].splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction tagsHas(name) {\n\t\treturn _tags.hasOwnProperty(name);\n\t}\n\n\tfunction tagsGet(name) {\n\t\treturn tagsHas(name) ? _tags[name] : null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tMacro Functions.\n\t\t*/\n\t\tadd     : { value : macrosAdd },\n\t\tdelete  : { value : macrosDelete },\n\t\tisEmpty : { value : macrosIsEmpty },\n\t\thas     : { value : macrosHas },\n\t\tget     : { value : macrosGet },\n\t\tinit    : { value : macrosInit },\n\n\t\t/*\n\t\t\tTags Functions.\n\t\t*/\n\t\ttags : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tregister   : { value : tagsRegister },\n\t\t\t\tunregister : { value : tagsUnregister },\n\t\t\t\thas        : { value : tagsHas },\n\t\t\t\tget        : { value : tagsGet }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) } // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrocontext.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, DebugView, Patterns, State, Wikifier, throwError */\n\nvar MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tMacroContext Class.\n\t*******************************************************************************************************************/\n\tclass MacroContext {\n\t\tconstructor(contextData) {\n\t\t\tconst context = Object.assign({\n\t\t\t\tparent      : null,\n\t\t\t\tmacro       : null,\n\t\t\t\tname        : '',\n\t\t\t\tdisplayName : '',\n\t\t\t\targs        : null,\n\t\t\t\tpayload     : null,\n\t\t\t\tparser      : null,\n\t\t\t\tsource      : ''\n\t\t\t}, contextData);\n\n\t\t\tif (context.macro === null || context.name === '' || context.parser === null) {\n\t\t\t\tthrow new TypeError('context object missing required properties');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tself : {\n\t\t\t\t\tvalue : context.macro\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : typeof context.macro._ALIAS_OF === 'undefined' ? context.name : context.macro._ALIAS_OF\n\t\t\t\t},\n\n\t\t\t\tdisplayName : {\n\t\t\t\t\tvalue : context.name\n\t\t\t\t},\n\n\t\t\t\targs : {\n\t\t\t\t\tvalue : context.args\n\t\t\t\t},\n\n\t\t\t\tpayload : {\n\t\t\t\t\tvalue : context.payload\n\t\t\t\t},\n\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : context.source\n\t\t\t\t},\n\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : context.parent\n\t\t\t\t},\n\n\t\t\t\tparser : {\n\t\t\t\t\tvalue : context.parser\n\t\t\t\t},\n\n\t\t\t\t_output : {\n\t\t\t\t\tvalue : context.parser.output\n\t\t\t\t},\n\n\t\t\t\t_shadows : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugView : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugViewEnabled : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Config.debug\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this._debugViewEnabled ? this.debugView.output : this._output;\n\t\t}\n\n\t\tget shadows() {\n\t\t\treturn [...this._shadows];\n\t\t}\n\n\t\tget shadowView() {\n\t\t\tconst view = new Set();\n\t\t\tthis.contextSelectAll(ctx => ctx._shadows)\n\t\t\t\t.forEach(ctx => ctx._shadows.forEach(name => view.add(name)));\n\t\t\treturn [...view];\n\t\t}\n\n\t\tget debugView() {\n\t\t\tif (this._debugViewEnabled) {\n\t\t\t\treturn this._debugView !== null ? this._debugView : this.createDebugView();\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextHas(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tcontextSelect(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextSelectAll(filter) {\n\t\t\tconst result = [];\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\tresult.push(context);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\taddShadow(...names) {\n\t\t\tif (!this._shadows) {\n\t\t\t\tthis._shadows = new Set();\n\t\t\t}\n\n\t\t\tconst varRe = new RegExp(`^${Patterns.variable}$`);\n\n\t\t\tnames\n\t\t\t\t.flat(Infinity)\n\t\t\t\t.forEach(name => {\n\t\t\t\t\tif (typeof name !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`variable name must be a string; type: ${typeof name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!varRe.test(name)) {\n\t\t\t\t\t\tthrow new Error(`invalid variable name \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._shadows.add(name);\n\t\t\t\t});\n\t\t}\n\n\t\tcreateShadowWrapper(callback, doneCallback, startCallback) {\n\t\t\tconst shadowContext = this;\n\t\t\tlet shadowStore;\n\n\t\t\tif (typeof callback === 'function') {\n\t\t\t\tshadowStore = {};\n\t\t\t\tthis.shadowView.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn function (...args) {\n\t\t\t\tif (typeof startCallback === 'function') {\n\t\t\t\t\tstartCallback.apply(this, args);\n\t\t\t\t}\n\n\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\t\t\t\t\tconst macroParser = Wikifier.Parser.get('macro');\n\t\t\t\t\tlet contextCache;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\tcallback.\n\t\t\t\t\t*/\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Cache the existing macro execution context and assign the shadow context.\n\t\t\t\t\t\tcontextCache = macroParser.context;\n\t\t\t\t\t\tmacroParser.context = shadowContext;\n\n\t\t\t\t\t\t// Call the callback function.\n\t\t\t\t\t\tcallback.apply(this, args);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\t// Revert the macro execution context shadowing.\n\t\t\t\t\t\tif (contextCache !== undefined) {\n\t\t\t\t\t\t\tmacroParser.context = contextCache;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (typeof doneCallback === 'function') {\n\t\t\t\t\tdoneCallback.apply(this, args);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tcreateDebugView(name, title) {\n\t\t\tthis._debugView = new DebugView(\n\t\t\t\tthis._output,\n\t\t\t\t'macro',\n\t\t\t\tname ? name : this.displayName,\n\t\t\t\ttitle ? title : this.source\n\t\t\t);\n\n\t\t\tif (this.payload !== null && this.payload.length > 0) {\n\t\t\t\tthis._debugView.modes({ nonvoid : true });\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = true;\n\t\t\treturn this._debugView;\n\t\t}\n\n\t\tremoveDebugView() {\n\t\t\tif (this._debugView !== null) {\n\t\t\t\tthis._debugView.remove();\n\t\t\t\tthis._debugView = null;\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = false;\n\t\t}\n\n\t\terror(message, source, stack) {\n\t\t\treturn throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source, stack);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn MacroContext;\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrolib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, Engine, Has, L10n, Macro, NodeTyper, Patterns, Scripting, SimpleAudio, State,\n\t       Story, TempState, Util, Wikifier, postdisplay, prehistory, storage, toStringOrDefault\n*/\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tVariables Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<capture>>\n\t*/\n\tMacro.add('capture', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\t\ttsVarRe  : new RegExp(`(${Patterns.variable})`,'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.raw.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst valueCache = {};\n\n\t\t\t/*\n\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t`Wikifier` call.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\tconst tsVarRe = this.self.tsVarRe;\n\t\t\t\tlet match;\n\n\t\t\t\t/*\n\t\t\t\t\tCache the existing values of the variables and add a shadow.\n\t\t\t\t*/\n\t\t\t\twhile ((match = tsVarRe.exec(this.args.raw)) !== null) {\n\t\t\t\t\tconst varName = match[1];\n\t\t\t\t\tconst varKey  = varName.slice(1);\n\t\t\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.addShadow(varName);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier(this.output, this.payload[0].contents);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t// Revert the variable shadowing.\n\t\t\t\tthis.shadows.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<set>>\n\t*/\n\tMacro.add('set', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<unset>>\n\t*/\n\tMacro.add('unset', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(\n\t\t\t`State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})`,\n\t\t\t'g'\n\t\t),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst jsVarRe = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst store = State[match[1]];\n\t\t\t\tconst name  = match[2];\n\n\t\t\t\tif (store.hasOwnProperty(name)) {\n\t\t\t\t\tdelete store[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remember>>\n\t*/\n\tMacro.add('remember', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember') || {};\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\t\t\t\tremember[name] = State.variables[name];\n\t\t\t}\n\n\t\t\tif (!storage.set('remember', remember)) {\n\t\t\t\treturn this.error(`unknown error, cannot remember: ${this.args.raw}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t},\n\n\t\tinit() {\n\t\t\tconst remember = storage.get('remember');\n\n\t\t\tif (remember) {\n\t\t\t\tObject.keys(remember).forEach(name => State.variables[name] = remember[name]);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<forget>>\n\t*/\n\tMacro.add('forget', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story variable list specified');\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember');\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\t\t\tlet needStore = false;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\n\t\t\t\tif (State.variables.hasOwnProperty(name)) {\n\t\t\t\t\tdelete State.variables[name];\n\t\t\t\t}\n\n\t\t\t\tif (remember && remember.hasOwnProperty(name)) {\n\t\t\t\t\tneedStore = true;\n\t\t\t\t\tdelete remember[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needStore) {\n\t\t\t\tif (Object.keys(remember).length === 0) {\n\t\t\t\t\tif (!storage.delete('remember')) {\n\t\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!storage.set('remember', remember)) {\n\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tScripting Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<run>>\n\t*/\n\tMacro.add('run', 'set'); // add <<run>> as an alias of <<set>>\n\n\t/*\n\t\t<<script>>\n\t*/\n\tMacro.add('script', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.payload[0].contents, output);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.createDebugView();\n\t\t\t}\n\n\t\t\tif (output.hasChildNodes()) {\n\t\t\t\tthis.output.appendChild(output);\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDisplay Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<include>>\n\t*/\n\tMacro.add('include', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tpassage = Story.get(passage);\n\t\t\tlet $el;\n\n\t\t\tif (this.args[1]) {\n\t\t\t\t$el = jQuery(document.createElement(this.args[1]))\n\t\t\t\t\t.addClass(`${passage.domId} macro-${this.name}`)\n\t\t\t\t\t.attr('data-passage', passage.title)\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(this.output);\n\t\t\t}\n\n\t\t\t$el.wiki(passage.processText());\n\t\t}\n\t});\n\n\t/*\n\t\t<<nobr>>\n\t*/\n\tMacro.add('nobr', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\t/*\n\t\t\t\tWikify the contents, after removing all leading & trailing newlines and compacting\n\t\t\t\tall internal sequences of newlines into single spaces.\n\t\t\t*/\n\t\t\tnew Wikifier(this.output, this.payload[0].contents.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' '));\n\t\t}\n\t});\n\n\t/*\n\t\t<<print>>, <<=>>, & <<->>\n\t*/\n\tMacro.add(['print', '=', '-'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = toStringOrDefault(Scripting.evalJavaScript(this.args.full), null);\n\n\t\t\t\tif (result !== null) {\n\t\t\t\t\tnew Wikifier(this.output, this.name === '-' ? Util.escape(result) : result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<silently>>\n\t*/\n\tMacro.add('silently', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.payload[0].contents.trim());\n\n\t\t\tif (Config.debug) {\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tthis.debugView.modes({ block : true, hidden : true });\n\t\t\t\tthis.output.appendChild(frag);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Discard the output, unless there were errors.\n\t\t\t\tconst errList = [...frag.querySelectorAll('.error')].map(errEl => errEl.textContent);\n\n\t\t\t\tif (errList.length > 0) {\n\t\t\t\t\treturn this.error(`error${errList.length === 1 ? '' : 's'} within contents (${errList.join('; ')})`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<type speed [start delay] [class classes] [element tag] [id ID] [keep|none] [skipkey key]>>\n\t*/\n\tMacro.add('type', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttypeId  : 0,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no speed specified');\n\t\t\t}\n\n\t\t\tconst speed = Util.fromCssTime(this.args[0]); // in milliseconds\n\n\t\t\tif (speed < 0) {\n\t\t\t\treturn this.error(`speed time value must be non-negative (received: ${this.args[0]})`);\n\t\t\t}\n\n\t\t\tlet cursor;\n\t\t\tlet elClass = '';\n\t\t\tlet elId    = '';\n\t\t\tlet elTag   = 'div';\n\t\t\tlet skipKey = Config.macros.typeSkipKey;\n\t\t\tlet start   = 400; // in milliseconds\n\n\t\t\t// Process optional arguments.\n\t\t\tconst options = this.args.slice(1);\n\n\t\t\twhile (options.length > 0) {\n\t\t\t\tconst option = options.shift();\n\n\t\t\t\tswitch (option) {\n\t\t\t\tcase 'class': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('class option missing required class name(s)');\n\t\t\t\t\t}\n\n\t\t\t\t\telClass = options.shift();\n\n\t\t\t\t\tif (elClass === '') {\n\t\t\t\t\t\tthrow new Error('class option class name(s) must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'element': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('element option missing required element tag name');\n\t\t\t\t\t}\n\n\t\t\t\t\telTag = options.shift();\n\n\t\t\t\t\tif (elTag === '') {\n\t\t\t\t\t\tthrow new Error('element option tag name must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'id': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('id option missing required ID');\n\t\t\t\t\t}\n\n\t\t\t\t\telId = options.shift();\n\n\t\t\t\t\tif (elId === '') {\n\t\t\t\t\t\tthrow new Error('id option ID must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'keep':\n\t\t\t\t\tcursor = 'keep';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'none':\n\t\t\t\t\tcursor = 'none';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'skipkey': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('skipkey option missing required key value');\n\t\t\t\t\t}\n\n\t\t\t\t\tskipKey = options.shift();\n\n\t\t\t\t\tif (skipKey === '') {\n\t\t\t\t\t\tthrow new Error('skipkey option key value must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'start': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('start option missing required time value');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = options.shift();\n\t\t\t\t\tstart = Util.fromCssTime(value);\n\n\t\t\t\t\tif (start < 0) {\n\t\t\t\t\t\tthrow new Error(`start option time value must be non-negative (received: ${value})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.error(`unknown option: ${option}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contents = this.payload[0].contents;\n\n\t\t\t// Do nothing if there's no content to type out.\n\t\t\tif (contents.trim() === '') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// Set up our base class name and event namespace.\n\t\t\tconst className = `macro-${this.name}`;\n\t\t\tconst namespace = `.${className}`;\n\n\t\t\t// Create a target to be later replaced by the typing wrapper.\n\t\t\tconst $target = jQuery(document.createElement(elTag))\n\t\t\t\t.addClass(`${className} ${className}-target`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Initialize the queue and clean up handlers.\n\t\t\tif (!TempState.macroTypeQueue) {\n\t\t\t\t// Set up the typing handler queue for all invocations.\n\t\t\t\tTempState.macroTypeQueue = [];\n\n\t\t\t\t// Immediately clear any existing handlers from our namespace and set up a\n\t\t\t\t// `:passageinit` event handler to clean up after navigation.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`:passageinit${namespace}`, () => $(document).off(namespace));\n\t\t\t}\n\n\t\t\t// If the queue is empty at this point, set the start typing flag.\n\t\t\tconst startTyping = TempState.macroTypeQueue.length === 0;\n\n\t\t\t// Generate our unique ID.\n\t\t\tconst selfId = ++this.self.typeId;\n\n\t\t\t// Push our typing handler onto the queue.\n\t\t\tTempState.macroTypeQueue.push({\n\t\t\t\tid : selfId,\n\n\t\t\t\thandler() {\n\t\t\t\t\tconst $wrapper = jQuery(document.createElement(elTag))\n\t\t\t\t\t\t.addClass(className);\n\n\t\t\t\t\t// Add the user ID, if any.\n\t\t\t\t\tif (elId) {\n\t\t\t\t\t\t$wrapper.attr('id', elId);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add the user class(es), if any.\n\t\t\t\t\tif (elClass) {\n\t\t\t\t\t\t$wrapper.addClass(elClass);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wikify the contents into `$wrapper`.\n\t\t\t\t\tnew Wikifier($wrapper, contents);\n\n\t\t\t\t\t// Cache info about the current turn.\n\t\t\t\t\tconst passage = State.passage;\n\t\t\t\t\tconst turn    = State.turns;\n\n\t\t\t\t\t// Skip typing if….\n\t\t\t\t\tif (\n\t\t\t\t\t\t// …we've visited the passage before.\n\t\t\t\t\t\t!Config.macros.typeVisitedPassages\n\t\t\t\t\t\t&& State.passages.slice(0, -1).some(title => title === passage)\n\n\t\t\t\t\t\t// …there were any content errors.\n\t\t\t\t\t\t|| $wrapper.find('.error').length > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t\t// Run the next typing handler in the queue, if any.\n\t\t\t\t\t\tif (TempState.macroTypeQueue.length > 0) {\n\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Exit.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create a new `NodeTyper` instance for the wrapper's contents and\n\t\t\t\t\t// replace the target with the typing wrapper.\n\t\t\t\t\tconst typer = new NodeTyper({\n\t\t\t\t\t\ttargetNode : $wrapper.get(0),\n\t\t\t\t\t\tclassNames : cursor === 'none' ? null : `${className}-cursor`\n\t\t\t\t\t});\n\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t// Set up event IDs.\n\t\t\t\t\tconst typingCompleteId = ':typingcomplete';\n\t\t\t\t\tconst typingStartId    = ':typingstart';\n\t\t\t\t\tconst typingStopId     = ':typingstop';\n\t\t\t\t\tconst keydownAndNS     = `keydown${namespace}`;\n\t\t\t\t\tconst typingStopAndNS  = `${typingStopId}${namespace}`;\n\n\t\t\t\t\t// Set up handlers for spacebar aborting and continuations.\n\t\t\t\t\t$(document)\n\t\t\t\t\t\t.off(keydownAndNS)\n\t\t\t\t\t\t.on(keydownAndNS, ev => {\n\t\t\t\t\t\t\t// Finish typing if the player aborts via the skip key.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tUtil.scrubEventKey(ev.key) === skipKey\n\t\t\t\t\t\t\t\t&& (ev.target === document.body || ev.target === document.documentElement)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t\t$(document).off(keydownAndNS);\n\t\t\t\t\t\t\t\ttyper.finish();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.one(typingStopAndNS, () => {\n\t\t\t\t\t\t\tif (TempState.macroTypeQueue) {\n\t\t\t\t\t\t\t\t// If the queue is empty, fire the typing complete event.\n\t\t\t\t\t\t\t\tif (TempState.macroTypeQueue.length === 0) {\n\t\t\t\t\t\t\t\t\tjQuery.event.trigger(typingCompleteId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Elsewise, run the next typing handler in the queue.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Set up the typing interval and start/stop event firing.\n\t\t\t\t\tconst typeNode = function typeNode() {\n\t\t\t\t\t\t// Fire the typing start event.\n\t\t\t\t\t\t$wrapper.trigger(typingStartId);\n\n\t\t\t\t\t\tconst typeNodeId = setInterval(() => {\n\t\t\t\t\t\t\t// Stop typing if….\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t// …we've navigated away.\n\t\t\t\t\t\t\t\tState.passage !== passage\n\t\t\t\t\t\t\t\t|| State.turns !== turn\n\n\t\t\t\t\t\t\t\t// …we're done typing.\n\t\t\t\t\t\t\t\t|| !typer.type()\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Terminate the timer.\n\t\t\t\t\t\t\t\tclearInterval(typeNodeId);\n\n\t\t\t\t\t\t\t\t// Remove this handler from the queue, if the queue still exists and the\n\t\t\t\t\t\t\t\t// handler IDs match.\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.length > 0\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.first().id === selfId\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Fire the typing stop event.\n\t\t\t\t\t\t\t\t$wrapper.trigger(typingStopId);\n\n\t\t\t\t\t\t\t\t// Add the done class to the wrapper.\n\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-done`);\n\n\t\t\t\t\t\t\t\t// Add the cursor class to the wrapper, if we're keeping it.\n\t\t\t\t\t\t\t\tif (cursor === 'keep') {\n\t\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-cursor`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, speed);\n\t\t\t\t\t};\n\n\t\t\t\t\t// Kick off typing the node.\n\t\t\t\t\tif (start) {\n\t\t\t\t\t\tsetTimeout(typeNode, start);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\ttypeNode();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we're to start typing, then either set up a `:passageend` event handler\n\t\t\t// to do so or start it immediately, depending on the engine state.\n\t\t\tif (startTyping) {\n\t\t\t\tif (Engine.isPlaying()) {\n\t\t\t\t\t$(document).one(`:passageend${namespace}`, () => TempState.macroTypeQueue.first().handler());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<display>>\n\t*/\n\tMacro.add('display', 'include'); // add <<display>> as an alias of <<include>>\n\n\n\t/*******************************************************************************************************************\n\t\tControl Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<if>>, <<elseif>>, & <<else>>\n\t*/\n\tMacro.add('if', {\n\t\tskipArgs   : true,\n\t\ttags       : ['elseif', 'else'],\n\t\telseifWsRe : /^\\s*if\\b/i,\n\t\tifAssignRe : /[^!=&^|<>*/%+-]=[^=>]/,\n\n\t\thandler() {\n\t\t\tlet i;\n\n\t\t\ttry {\n\t\t\t\tconst len = this.payload.length;\n\n\t\t\t\t// Sanity checks.\n\t\t\t\tconst elseifWsRe = this.self.elseifWsRe;\n\t\t\t\tconst ifAssignRe = this.self.ifAssignRe;\n\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t/* eslint-disable prefer-template */\n\t\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\t\tcase 'else':\n\t\t\t\t\t\tif (this.payload[i].args.raw.length > 0) {\n\t\t\t\t\t\t\tif (elseifWsRe.test(this.payload[i].args.raw)) {\n\t\t\t\t\t\t\t\treturn this.error(`whitespace is not allowed between the \"else\" and \"if\" in <<elseif>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn this.error(`<<else>> does not accept a conditional expression (perhaps you meant to use <<elseif>>), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\t\treturn this.error('<<else>> must be the final clause');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (this.payload[i].args.full.length === 0) {\n\t\t\t\t\t\t\treturn this.error(`no conditional expression specified for <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t   Config.macros.ifAssignmentError\n\t\t\t\t\t\t\t&& ifAssignRe.test(this.payload[i].args.full)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn this.error(`assignment operator found within <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''} (perhaps you meant to use an equality operator: ==, ===, eq, is), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable prefer-template */\n\t\t\t\t}\n\n\t\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\t\tlet success = false;\n\n\t\t\t\t// Evaluate the clauses.\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t// Custom debug view setup for the current clause.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Conditional test.\n\t\t\t\t\tif (this.payload[i].name === 'else' || !!evalJavaScript(this.payload[i].args.full)) {\n\t\t\t\t\t\tsuccess = true;\n\t\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t\t// Custom debug view setup for a failed conditional.\n\t\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup for the remaining clauses.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tFake a debug view for `<</if>>`.  We do this to aid the checking of nesting\n\t\t\t\t\t\tand as a quick indicator of if any of the clauses matched.\n\t\t\t\t\t*/\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : !success,\n\t\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression in <<${i === 0 ? 'if' : 'elseif'}>> clause${i > 0 ? ' (#' + i + ')' : ''}: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack); // eslint-disable-line prefer-template\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<switch>>, <<case>>, & <<default>>\n\t*/\n\tMacro.add('switch', {\n\t\tskipArgs : ['switch'],\n\t\ttags     : ['case', 'default'],\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\tconst len = this.payload.length;\n\n\t\t\t// if (len === 1 || !this.payload.some(p => p.name === 'case')) {\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no cases specified');\n\t\t\t}\n\n\t\t\tlet i;\n\n\t\t\t// Sanity checks.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\tcase 'default':\n\t\t\t\t\tif (this.payload[i].args.length > 0) {\n\t\t\t\t\t\treturn this.error(`<<default>> does not accept values, invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\treturn this.error('<<default>> must be the final case');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no value(s) specified for <<${this.payload[i].name}>> (#${i})`);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet result;\n\n\t\t\ttry {\n\t\t\t\tresult = Scripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst debugView = this.debugView; // cache it now, to be modified later\n\t\t\tlet success = false;\n\n\t\t\t// Initial debug view setup for `<<switch>>`.\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Evaluate the clauses.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\t// Custom debug view setup for the current case.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t}\n\n\t\t\t\t// Case test(s).\n\t\t\t\tif (this.payload[i].name === 'default' || this.payload[i].args.some(val => val === result)) {\n\t\t\t\t\tsuccess = true;\n\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t// Custom debug view setup for a failed case.\n\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup for the remaining cases.\n\t\t\tif (Config.debug) {\n\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t\tFinalize the debug view for `<<switch>>` and fake a debug view for `<</switch>>`.\n\t\t\t\t\tWe do both as a quick indicator of if any of the cases matched and the latter\n\t\t\t\t\tto aid the checking of nesting.\n\t\t\t\t*/\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t\tthis\n\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<for>>, <<break>>, & <<continue>>\n\t*/\n\tMacro.add('for', {\n\t\t/* eslint-disable max-len */\n\t\tskipArgs    : true,\n\t\ttags        : null,\n\t\thasRangeRe  : new RegExp(`^\\\\S${Patterns.anyChar}*?\\\\s+range\\\\s+\\\\S${Patterns.anyChar}*?$`),\n\t\trangeRe     : new RegExp(`^(?:State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s*,\\\\s*)?State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s+range\\\\s+(\\\\S${Patterns.anyChar}*?)$`),\n\t\tthreePartRe : /^([^;]*?)\\s*;\\s*([^;]*?)\\s*;\\s*([^;]*?)$/,\n\t\tforInRe     : /^\\S+\\s+in\\s+\\S+/i,\n\t\tforOfRe     : /^\\S+\\s+of\\s+\\S+/i,\n\t\t/* eslint-enable max-len */\n\n\t\thandler() {\n\t\t\tconst argsStr = this.args.full.trim();\n\t\t\tconst payload = this.payload[0].contents.replace(/\\n$/, '');\n\n\t\t\t// Empty form.\n\t\t\tif (argsStr.length === 0) {\n\t\t\t\tthis.self.handleFor.call(this, payload, null, true, null);\n\t\t\t}\n\n\t\t\t// Range form.\n\t\t\telse if (this.self.hasRangeRe.test(argsStr)) {\n\t\t\t\tconst parts = argsStr.match(this.self.rangeRe);\n\n\t\t\t\tif (parts === null) {\n\t\t\t\t\treturn this.error('invalid range form syntax, format: [index ,] value range collection');\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleForRange.call(\n\t\t\t\t\tthis,\n\t\t\t\t\tpayload,\n\t\t\t\t\t{ type : parts[1], name : parts[2] },\n\t\t\t\t\t{ type : parts[3], name : parts[4] },\n\t\t\t\t\tparts[5]\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Conditional forms.\n\t\t\telse {\n\t\t\t\tlet init;\n\t\t\t\tlet condition;\n\t\t\t\tlet post;\n\n\t\t\t\t// Conditional-only form.\n\t\t\t\tif (argsStr.indexOf(';') === -1) {\n\t\t\t\t\t// Sanity checks.\n\t\t\t\t\tif (this.self.forInRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…in is not supported; see: for…range');\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.self.forOfRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…of is not supported; see: for…range');\n\t\t\t\t\t}\n\n\t\t\t\t\tcondition = argsStr;\n\t\t\t\t}\n\n\t\t\t\t// 3-part conditional form.\n\t\t\t\telse {\n\t\t\t\t\tconst parts = argsStr.match(this.self.threePartRe);\n\n\t\t\t\t\tif (parts === null) {\n\t\t\t\t\t\treturn this.error('invalid 3-part conditional form syntax, format: [init] ; [condition] ; [post]');\n\t\t\t\t\t}\n\n\t\t\t\t\tinit      = parts[1];\n\t\t\t\t\tcondition = parts[2].trim();\n\t\t\t\t\tpost      = parts[3];\n\n\t\t\t\t\tif (condition.length === 0) {\n\t\t\t\t\t\tcondition = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleFor.call(this, payload, init, condition, post);\n\t\t\t}\n\t\t},\n\n\t\thandleFor(payload, init, condition, post) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet first  = true;\n\t\t\tlet safety = Config.macros.maxLoopIterations;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tif (init) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tevalJavaScript(init);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad init expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile (evalJavaScript(condition)) {\n\t\t\t\t\tif (--safety < 0) {\n\t\t\t\t\t\treturn this.error(`exceeded configured maximum loop iterations (${Config.macros.maxLoopIterations})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (post) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevalJavaScript(post);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\treturn this.error(`bad post expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\thandleForRange(payload, indexVar, valueVar, rangeExp) {\n\t\t\tlet first     = true;\n\t\t\tlet rangeList;\n\n\t\t\ttry {\n\t\t\t\trangeList = this.self.toRangeList(rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tfor (let i = 0; i < rangeList.length; ++i) {\n\t\t\t\t\tif (indexVar.name) {\n\t\t\t\t\t\tState[indexVar.type][indexVar.name] = rangeList[i][0];\n\t\t\t\t\t}\n\n\t\t\t\t\tState[valueVar.type][valueVar.name] = rangeList[i][1];\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\ttoRangeList(rangeExp) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet value;\n\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t*/\n\t\t\t\tvalue = evalJavaScript(rangeExp[0] === '{' ? `(${rangeExp})` : rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tif (typeof ex !== 'object') {\n\t\t\t\t\tthrow new Error(`bad range expression: ${ex}`);\n\t\t\t\t}\n\n\t\t\t\tex.message = `bad range expression: ${ex.message}`;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tlet list;\n\n\t\t\tswitch (typeof value) {\n\t\t\tcase 'string':\n\t\t\t\tlist = [];\n\t\t\t\tfor (let i = 0; i < value.length; /* empty */) {\n\t\t\t\t\tconst obj = Util.charAndPosAt(value, i);\n\t\t\t\t\tlist.push([i, obj.char]);\n\t\t\t\t\ti = 1 + obj.end;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'object':\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tlist = value.map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Set) {\n\t\t\t\t\tlist = [...value].map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Map) {\n\t\t\t\t\tlist = [...value.entries()];\n\t\t\t\t}\n\t\t\t\telse if (Util.toStringTag(value) === 'Object') {\n\t\t\t\t\tlist = Object.keys(value).map(key => [key, value[key]]);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`unsupported range expression type: ${Util.toStringTag(value)}`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`unsupported range expression type: ${typeof value}`);\n\t\t\t}\n\n\t\t\treturn list;\n\t\t}\n\t});\n\tMacro.add(['break', 'continue'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.contextHas(ctx => ctx.name === 'for')) {\n\t\t\t\tTempState.break = this.name === 'continue' ? 1 : 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<for>>');\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tInteractive Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<button>> & <<link>>\n\t*/\n\tMacro.add(['button', 'link'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error(`no ${this.name === 'button' ? 'button' : 'link'} text specified`);\n\t\t\t}\n\n\t\t\tconst $link = jQuery(document.createElement(this.name === 'button' ? 'button' : 'a'));\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\tconst $image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t.attr('src', this.args[0].source)\n\t\t\t\t\t\t.appendTo($link);\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t}\n\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t$link.append(document.createTextNode(this.args[0].text));\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the link text.\n\t\t\t\t$link.wikiWithOptions({ profile : 'core' }, this.args[0]);\n\t\t\t\tpassage = this.args.length > 1 ? this.args[1] : undefined;\n\t\t\t}\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$link.addClass('link-internal');\n\t\t\t}\n\n\t\t\t$link\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : passage != null // lazy equality for null\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\tthis.payload[0].contents !== ''\n\t\t\t\t\t\t? () => Wikifier.wikifyEval(this.payload[0].contents.trim())\n\t\t\t\t\t\t: null,\n\t\t\t\t\tpassage != null // lazy equality for null\n\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t: null\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<checkbox>>\n\t*/\n\tMacro.add('checkbox', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 3) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('unchecked value'); }\n\t\t\t\tif (this.args.length < 3) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst uncheckValue = this.args[1];\n\t\t\tconst checkValue   = this.args[2];\n\t\t\tconst el           = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'checkbox',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.checked ? checkValue : uncheckValue);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and input element to the appropriate value and state, as requested.\n\t\t\t*/\n\t\t\tswitch (this.args[3]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<cycle>>, <<listbox>>, <<option>>, & <<optionsfrom>>\n\t*/\n\tMacro.add(['cycle', 'listbox'], {\n\t\tisAsync  : true,\n\t\tskipArgs : ['optionsfrom'],\n\t\ttags     : ['option', 'optionsfrom'],\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no variable name specified');\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst len   = this.payload.length;\n\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no options specified');\n\t\t\t}\n\n\t\t\tconst autoselect = this.args.length > 1 && this.args[1] === 'autoselect';\n\t\t\tconst options    = [];\n\t\t\tconst tagCount   = { option : 0, optionsfrom : 0 };\n\t\t\tlet selectedIdx = -1;\n\n\t\t\t// Get the options and selected index, if any.\n\t\t\tfor (let i = 1; i < len; ++i) {\n\t\t\t\tconst payload = this.payload[i];\n\n\t\t\t\t// <<option label value [selected]>>\n\t\t\t\tif (payload.name === 'option') {\n\t\t\t\t\t++tagCount.option;\n\n\t\t\t\t\tif (payload.args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no arguments specified for <<${payload.name}>> (#${tagCount.option})`);\n\t\t\t\t\t}\n\n\t\t\t\t\toptions.push({\n\t\t\t\t\t\tlabel : String(payload.args[0]),\n\t\t\t\t\t\tvalue : payload.args.length === 1 ? payload.args[0] : payload.args[1]\n\t\t\t\t\t});\n\n\t\t\t\t\tif (payload.args.length > 2 && payload.args[2] === 'selected') {\n\t\t\t\t\t\tif (autoselect) {\n\t\t\t\t\t\t\treturn this.error('cannot specify both the autoselect and selected keywords');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (selectedIdx !== -1) {\n\t\t\t\t\t\t\treturn this.error(`multiple selected keywords specified for <<${payload.name}>> (#${selectedIdx + 1} & #${tagCount.option})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tselectedIdx = options.length - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// <<optionsfrom expression>>\n\t\t\t\telse {\n\t\t\t\t\t++tagCount.optionsfrom;\n\n\t\t\t\t\tif (payload.args.full.length === 0) {\n\t\t\t\t\t\treturn this.error(`no expression specified for <<${payload.name}>> (#${tagCount.optionsfrom})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tconst exp = payload.args.full;\n\t\t\t\t\t\tresult = Scripting.evalJavaScript(exp[0] === '{' ? `(${exp})` : exp);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (typeof result !== 'object' || result === null) {\n\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (type: ${result === null ? 'null' : typeof result})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result instanceof Array || result instanceof Set) {\n\t\t\t\t\t\tresult.forEach(val => options.push({ label : String(val), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse if (result instanceof Map) {\n\t\t\t\t\t\tresult.forEach((val, key) => options.push({ label : String(key), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst oType = Util.toStringTag(result);\n\n\t\t\t\t\t\tif (oType !== 'Object') {\n\t\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (object type: ${oType})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tObject.keys(result).forEach(key => options.push({ label : key, value : result[key] }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No options were selected by the user, so we must select one.\n\t\t\tif (selectedIdx === -1) {\n\t\t\t\t// Attempt to automatically select an option by matching the variable's current value.\n\t\t\t\tif (autoselect) {\n\t\t\t\t\t// NOTE: This will usually fail for objects due to a variety of reasons.\n\t\t\t\t\tconst sameValueZero = Util.sameValueZero;\n\t\t\t\t\tconst curValue      = State.getVar(varName);\n\t\t\t\t\tconst curValueIdx   = options.findIndex(opt => sameValueZero(opt.value, curValue));\n\t\t\t\t\tselectedIdx = curValueIdx === -1 ? 0 : curValueIdx;\n\t\t\t\t}\n\n\t\t\t\t// Simply select the first option.\n\t\t\t\telse {\n\t\t\t\t\tselectedIdx = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set up and append the appropriate element to the output buffer.\n\t\t\tif (this.name === 'cycle') {\n\t\t\t\tlet cycleIdx = selectedIdx;\n\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t.wikiWithOptions({ profile : 'core' }, options[selectedIdx].label)\n\t\t\t\t\t.attr('id', `${this.name}-${varId}`)\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () {\n\t\t\t\t\t\tcycleIdx = (cycleIdx + 1) % options.length;\n\t\t\t\t\t\t$(this).empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label);\n\t\t\t\t\t\tState.setVar(varName, options[cycleIdx].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse { // this.name === 'listbox'\n\t\t\t\tconst $select = jQuery(document.createElement('select'));\n\n\t\t\t\toptions.forEach((opt, i) => {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(opt.label)\n\t\t\t\t\t\t.appendTo($select);\n\t\t\t\t});\n\n\t\t\t\t$select\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t\t})\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.val(selectedIdx)\n\t\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\t\tState.setVar(varName, options[Number(this.value)].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\n\t\t\t// Set the variable to the appropriate value, as requested.\n\t\t\tState.setVar(varName, options[selectedIdx].value);\n\t\t}\n\t});\n\n\t/*\n\t\t<<linkappend>>, <<linkprepend>>, & <<linkreplace>>\n\t*/\n\tMacro.add(['linkappend', 'linkprepend', 'linkreplace'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no link text specified');\n\t\t\t}\n\n\t\t\tconst $link      = jQuery(document.createElement('a'));\n\t\t\tconst $insert    = jQuery(document.createElement('span'));\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\n\t\t\t$link\n\t\t\t\t.wikiWithOptions({ profile : 'core' }, this.args[0])\n\t\t\t\t.addClass(`link-internal macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : true\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tif (this.name === 'linkreplace') {\n\t\t\t\t\t\t\t$link.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$link\n\t\t\t\t\t\t\t\t.wrap(`<span class=\"macro-${this.name}\"></span>`)\n\t\t\t\t\t\t\t\t.replaceWith(() => $link.html());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\t\t\t\t\t\t\t$insert.append(frag);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (transition) {\n\t\t\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t$insert.addClass(`macro-${this.name}-insert`);\n\n\t\t\tif (transition) {\n\t\t\t\t$insert.addClass(`macro-${this.name}-in`);\n\t\t\t}\n\n\t\t\tif (this.name === 'linkprepend') {\n\t\t\t\t$insert.insertBefore($link);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$insert.insertAfter($link);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<numberbox>> & <<textbox>>\n\t*/\n\tMacro.add(['numberbox', 'textbox'], {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst asNumber     = this.name === 'numberbox';\n\t\t\tconst defaultValue = asNumber ? Number(this.args[1]) : this.args[1];\n\n\t\t\tif (asNumber && Number.isNaN(defaultValue)) {\n\t\t\t\treturn this.error(`default value \"${this.args[1]}\" is neither a number nor can it be parsed into a number`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst el    = document.createElement('input');\n\t\t\tlet autofocus = false;\n\t\t\tlet passage;\n\n\t\t\tif (this.args.length > 3) {\n\t\t\t\tpassage   = this.args[2];\n\t\t\t\tautofocus = this.args[3] === 'autofocus';\n\t\t\t}\n\t\t\telse if (this.args.length > 2) {\n\t\t\t\tif (this.args[2] === 'autofocus') {\n\t\t\t\t\tautofocus = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpassage = this.args[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (typeof passage === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = passage.link;\n\t\t\t}\n\n\t\t\t// Set up and append the input element to the output buffer.\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : asNumber ? 'number' : 'text',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\t\t\t\t}))\n\t\t\t\t.on('keypress.macros', this.createShadowWrapper(function (ev) {\n\t\t\t\t\t// If Return/Enter is pressed, set the variable and, optionally, forward to another passage.\n\t\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\n\t\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Set the variable and input element to the default value.\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\tel.value = defaultValue;\n\n\t\t\t// Autofocus the input element, if requested.\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<radiobutton>>\n\t*/\n\tMacro.add('radiobutton', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId      = Util.slugify(varName);\n\t\t\tconst checkValue = this.args[1];\n\t\t\tconst el         = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and initialize the group counter.\n\t\t\t*/\n\t\t\tif (!TempState.hasOwnProperty(this.name)) {\n\t\t\t\tTempState[this.name] = {};\n\t\t\t}\n\n\t\t\tif (!TempState[this.name].hasOwnProperty(varId)) {\n\t\t\t\tTempState[this.name][varId] = 0;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}-${TempState[this.name][varId]++}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'radio',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tif (this.checked) {\n\t\t\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable to the checked value and the input element to checked, if requested.\n\t\t\t*/\n\t\t\tswitch (this.args[2]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<textarea>>\n\t*/\n\tMacro.add('textarea', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst defaultValue = this.args[1];\n\t\t\tconst autofocus    = this.args[2] === 'autofocus';\n\t\t\tconst el           = document.createElement('textarea');\n\n\t\t\t/*\n\t\t\t\tSet up and append the textarea element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\trows     : 4,\n\t\t\t\t\t// cols     : 68, // instead of setting \"cols\" we set the `min-width` in CSS\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.value);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and textarea element to the default value.\n\t\t\t*/\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\t// Ideally, we should be setting `.defaultValue` here, but IE doesn't support it,\n\t\t\t// so we have to use `.textContent`, which is equivalent.\n\t\t\tel.textContent = defaultValue;\n\n\t\t\t/*\n\t\t\t\tAutofocus the textarea element, if requested.\n\t\t\t*/\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<click>>\n\t*/\n\tMacro.add('click', 'link'); // add <<click>> as an alias of <<link>>\n\n\n\t/*******************************************************************************************************************\n\t\tLinks Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<actions>>\n\t*/\n\tMacro.add('actions', {\n\t\thandler() {\n\t\t\tconst $list = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass(this.name)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\tfor (let i = 0; i < this.args.length; ++i) {\n\t\t\t\tlet passage;\n\t\t\t\tlet text;\n\t\t\t\tlet $image;\n\t\t\t\tlet setFn;\n\n\t\t\t\tif (typeof this.args[i] === 'object') {\n\t\t\t\t\tif (this.args[i].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[i].source);\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[i].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[i].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[i].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[i].text;\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[i];\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t   State.variables.hasOwnProperty('#actions')\n\t\t\t\t\t&& State.variables['#actions'].hasOwnProperty(passage)\n\t\t\t\t\t&& State.variables['#actions'][passage]\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tjQuery(Wikifier.createInternalLink(\n\t\t\t\t\tjQuery(document.createElement('li')).appendTo($list),\n\t\t\t\t\tpassage,\n\t\t\t\t\tnull,\n\t\t\t\t\t((passage, fn) => () => {\n\t\t\t\t\t\tif (!State.variables.hasOwnProperty('#actions')) {\n\t\t\t\t\t\t\tState.variables['#actions'] = {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tState.variables['#actions'][passage] = true;\n\n\t\t\t\t\t\tif (typeof fn === 'function') {\n\t\t\t\t\t\t\tfn();\n\t\t\t\t\t\t}\n\t\t\t\t\t})(passage, setFn)\n\t\t\t\t))\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.append($image || document.createTextNode(text));\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<back>> & <<return>>\n\t*/\n\tMacro.add(['back', 'return'], {\n\t\thandler() {\n\t\t\t/* legacy */\n\t\t\tif (this.args.length > 1) {\n\t\t\t\treturn this.error('too many arguments specified, check the documentation for details');\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\tlet momentIndex = -1;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('link')) {\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\tif (this.args[0].count === 1) {\n\t\t\t\t\t\t\t// Simple link syntax: `[[...]]`.\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Pretty link syntax: `[[...|...]]`.\n\t\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (this.args.length === 1) {\n\t\t\t\t\t// Argument was simply the link text.\n\t\t\t\t\ttext = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\t/*\n\t\t\t\t\tFind the index and title of the most recent moment whose title does not match\n\t\t\t\t\tthat of the active (present) moment's.\n\t\t\t\t*/\n\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\tif (State.history[i].title !== State.passage) {\n\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\tpassage = State.history[i].title;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we failed to find a passage and we're `<<return>>`, fallback to `State.expired`.\n\t\t\t\tif (passage == null && this.name === 'return') { // lazy equality for null\n\t\t\t\t\tfor (let i = State.expired.length - 1; i >= 0; --i) {\n\t\t\t\t\t\tif (State.expired[i] !== State.passage) {\n\t\t\t\t\t\t\tpassage = State.expired[i];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tif (this.name === 'back') {\n\t\t\t\t\t/*\n\t\t\t\t\t\tFind the index of the most recent moment whose title matches that of the\n\t\t\t\t\t\tspecified passage.\n\t\t\t\t\t*/\n\t\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\t\tif (State.history[i].title === passage) {\n\t\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (momentIndex === -1) {\n\t\t\t\t\t\treturn this.error(`cannot find passage \"${passage}\" in the current story history`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn this.error('cannot find passage');\n\t\t\t}\n\n\t\t\t// if (this.name === \"back\" && momentIndex === -1) {\n\t\t\t// \t// no-op; we're already at the first passage in the current story history\n\t\t\t// \treturn;\n\t\t\t// }\n\n\t\t\tlet $el;\n\n\t\t\tif (this.name !== 'back' || momentIndex !== -1) {\n\t\t\t\t$el = jQuery(document.createElement('a'))\n\t\t\t\t\t.addClass('link-internal')\n\t\t\t\t\t.ariaClick(\n\t\t\t\t\t\t{ one : true },\n\t\t\t\t\t\tthis.name === 'return'\n\t\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t\t: () => Engine.goTo(momentIndex)\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('link-disabled');\n\t\t\t}\n\n\t\t\t$el\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text || L10n.get(`macro${this.name.toUpperFirst()}Text`)))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<choice>>\n\t*/\n\tMacro.add('choice', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tconst choiceId = State.passage;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\t\t\tlet setFn;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// NOTE: The arguments here are backwards.\n\t\t\t\tpassage = this.args[0];\n\t\t\t\ttext    = this.args[1];\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t   State.variables.hasOwnProperty('#choice')\n\t\t\t\t&& State.variables['#choice'].hasOwnProperty(choiceId)\n\t\t\t\t&& State.variables['#choice'][choiceId]\n\t\t\t) {\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass(`link-disabled macro-${this.name}`)\n\t\t\t\t\t.attr('tabindex', -1)\n\t\t\t\t\t.append($image || document.createTextNode(text))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(Wikifier.createInternalLink(this.output, passage, null, () => {\n\t\t\t\tif (!State.variables.hasOwnProperty('#choice')) {\n\t\t\t\t\tState.variables['#choice'] = {};\n\t\t\t\t}\n\n\t\t\t\tState.variables['#choice'][choiceId] = true;\n\n\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\tsetFn();\n\t\t\t\t}\n\t\t\t}))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text));\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDOM Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<addclass>> & <<toggleclass>>\n\t*/\n\tMacro.add(['addclass', 'toggleclass'], {\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('selector'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('class names'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tswitch (this.name) {\n\t\t\tcase 'addclass':\n\t\t\t\t$targets.addClass(this.args[1].trim());\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggleclass':\n\t\t\t\t$targets.toggleClass(this.args[1].trim());\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<removeclass>>\n\t*/\n\tMacro.add('removeclass', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.args.length > 1) {\n\t\t\t\t$targets.removeClass(this.args[1].trim());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$targets.removeClass();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<copy>>\n\t*/\n\tMacro.add('copy', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tjQuery(this.output).append($targets.html());\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<append>>, <<prepend>>, & <<replace>>\n\t*/\n\tMacro.add(['append', 'prepend', 'replace'], {\n\t\ttags  : null,\n\t\tt8nRe : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\t\tlet $insert;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$insert = jQuery(document.createElement('span'));\n\t\t\t\t\t$insert.addClass(`macro-${this.name}-insert macro-${this.name}-in`);\n\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$insert = jQuery(document.createDocumentFragment());\n\t\t\t\t}\n\n\t\t\t\t$insert.wiki(this.payload[0].contents);\n\n\t\t\t\tswitch (this.name) {\n\t\t\t\tcase 'replace':\n\t\t\t\t\t$targets.empty();\n\t\t\t\t\t/* falls through */\n\n\t\t\t\tcase 'append':\n\t\t\t\t\t$targets.append($insert);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'prepend':\n\t\t\t\t\t$targets.prepend($insert);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (this.name === 'replace') {\n\t\t\t\t$targets.empty();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remove>>\n\t*/\n\tMacro.add('remove', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\t$targets.remove();\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudio Macros.\n\t*******************************************************************************************************************/\n\tif (Has.audio) {\n\t\tconst errorOnePlaybackAction = (cur, prev) => `only one playback action allowed per invocation, \"${cur}\" cannot be combined with \"${prev}\"`;\n\n\t\t/*\n\t\t\t<<audio>>\n\t\t*/\n\t\tMacro.add('audio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track and/or group IDs'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tlet selected;\n\n\t\t\t\t// Process the track and/or group IDs.\n\t\t\t\ttry {\n\t\t\t\t\tselected = SimpleAudio.select(this.args[0]);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(1);\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet passage;\n\t\t\t\tlet time;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'time':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('time missing required seconds value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\ttime = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(time) || !Number.isFinite(time)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse time: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'goto':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('goto missing required passage title');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\n\t\t\t\t\t\tif (typeof raw === 'object') {\n\t\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\t\tpassage = raw.link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\t\t\tpassage = raw;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tselected.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (time != null) { // lazy equality for null\n\t\t\t\t\t\tselected.time(time);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tselected.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tselected.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\tconst nsEnded = `ended.macros.macro-${this.name}_goto`;\n\t\t\t\t\t\tselected\n\t\t\t\t\t\t\t.off(nsEnded)\n\t\t\t\t\t\t\t.one(nsEnded, () => {\n\t\t\t\t\t\t\t\tselected.off(nsEnded);\n\t\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tselected.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tselected.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tselected.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tselected.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tselected.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tselected.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<cacheaudio track_id source_list>>\n\t\t*/\n\t\tMacro.add('cacheaudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track ID'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('sources'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tconst id       = String(this.args[0]).trim();\n\t\t\t\tconst oldFmtRe = /^format:\\s*([\\w-]+)\\s*;\\s*/i;\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.tracks.add(id, this.args.slice(1).map(source => {\n\t\t\t\t\t\t/* legacy */\n\t\t\t\t\t\t// Transform an old format specifier into the new style.\n\t\t\t\t\t\tif (oldFmtRe.test(source)) {\n\t\t\t\t\t\t\t// If in Test Mode, return an error.\n\t\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\t\treturn this.error(`track ID \"${id}\": format specifier migration required, \"format:formatId;\" \\u2192 \"formatId|\"`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsource = source.replace(oldFmtRe, '$1|'); // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn source;\n\t\t\t\t\t\t/* /legacy */\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\tif (Config.debug && !SimpleAudio.tracks.get(id).hasSource()) {\n\t\t\t\t\treturn this.error(`track ID \"${id}\": no supported audio sources found`);\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createaudiogroup group_id>>\n\t\t\t\t<<track track_id>>\n\t\t\t\t…\n\t\t\t<</createaudiogroup>>\n\t\t*/\n\t\tMacro.add('createaudiogroup', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst groupId  = String(this.args[0]).trim();\n\t\t\t\tconst trackIds = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length < 1) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackIds.push(String(this.payload[i].args[0]).trim());\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.groups.add(groupId, trackIds);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createplaylist list_id>>\n\t\t\t\t<<track track_id action_list>>\n\t\t\t\t…\n\t\t\t<</createplaylist>>\n\t\t*/\n\t\tMacro.add('createplaylist', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'createplaylist') {\n\t\t\t\t\treturn this.error('a playlist has already been defined with <<setplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst listId    = String(this.args[0]).trim();\n\t\t\t\tconst trackObjs = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst trackObj = { id : String(this.payload[i].args[0]).trim() };\n\t\t\t\t\tconst args     = this.payload[i].args.slice(1);\n\n\t\t\t\t\t// Process arguments.\n\t\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\t\tlet raw;\n\t\t\t\t\t\tlet parsed;\n\n\t\t\t\t\t\tswitch (arg) {\n\t\t\t\t\t\tcase 'copy': // [DEPRECATED]\n\t\t\t\t\t\tcase 'own':\n\t\t\t\t\t\t\ttrackObj.own = true;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'rate':\n\t\t\t\t\t\t\t// if (args.length === 0) {\n\t\t\t\t\t\t\t// \treturn this.error('rate missing required speed value');\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// raw = args.shift();\n\t\t\t\t\t\t\t// parsed = Number.parseFloat(raw);\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// if (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t// \treturn this.error(`cannot parse rate: ${raw}`);\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// trackObj.rate = parsed;\n\t\t\t\t\t\t\tif (args.length > 0) {\n\t\t\t\t\t\t\t\targs.shift();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\t\tparsed = Number.parseFloat(raw);\n\n\t\t\t\t\t\t\tif (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttrackObj.volume = parsed;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackObjs.push(trackObj);\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add(listId, trackObjs);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'createplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<masteraudio action_list>>\n\t\t*/\n\t\tMacro.add('masteraudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(0);\n\t\t\t\tlet action;\n\t\t\t\tlet mute;\n\t\t\t\tlet muteOnHide;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'muteonhide':\n\t\t\t\t\tcase 'nomuteonhide':\n\t\t\t\t\t\tmuteOnHide = arg === 'muteonhide';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (muteOnHide != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.muteOnHidden(muteOnHide);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tSimpleAudio.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tSimpleAudio.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tSimpleAudio.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<playlist list_id action_list>>  ← <<createplaylist>> syntax\n\t\t\t<<playlist action_list>>          ← <<setplaylist>> syntax\n\t\t*/\n\t\tMacro.add('playlist', {\n\t\t\tfrom : null,\n\n\t\t\thandler() {\n\t\t\t\tconst from = this.self.from;\n\n\t\t\t\tif (from === null) {\n\t\t\t\t\treturn this.error('no playlists have been created');\n\t\t\t\t}\n\n\t\t\t\tlet list;\n\t\t\t\tlet args;\n\n\t\t\t\tif (from === 'createplaylist') {\n\t\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\tif (this.args.length < 1) { errors.push('list ID'); }\n\t\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get(id);\n\t\t\t\t\targs = this.args.slice(1);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get('setplaylist');\n\t\t\t\t\targs = this.args.slice(0);\n\t\t\t\t}\n\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet shuffle;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'shuffle':\n\t\t\t\t\tcase 'unshuffle':\n\t\t\t\t\t\tshuffle = arg === 'shuffle';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tlist.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tlist.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tlist.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (shuffle != null) { // lazy equality for null\n\t\t\t\t\t\tlist.shuffle(shuffle);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tlist.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tlist.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tlist.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tlist.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\t\tlist.skip();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tlist.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tlist.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeaudiogroup group_id>>\n\t\t*/\n\t\tMacro.add('removeaudiogroup', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.groups.has(id)) {\n\t\t\t\t\treturn this.error(`group \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.groups.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeplaylist list_id>>\n\t\t*/\n\t\tMacro.add('removeplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.lists.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<waitforaudio>>\n\t\t*/\n\t\tMacro.add('waitforaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.loadWithScreen();\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<setplaylist track_id_list>>\n\t\t*/\n\t\tMacro.add('setplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no track ID(s) specified');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'setplaylist') {\n\t\t\t\t\treturn this.error('playlists have already been defined with <<createplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Create the new playlist.\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add('setplaylist', this.args.slice(0));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'setplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<stopallaudio>>\n\t\t*/\n\t\tMacro.add('stopallaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.select(':all').stop();\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\telse {\n\t\t/* The HTML5 <audio> API appears to be missing or disabled, set up no-op macros. */\n\t\tMacro.add([\n\t\t\t'audio',\n\t\t\t'cacheaudio',\n\t\t\t'createaudiogroup',\n\t\t\t'createplaylist',\n\t\t\t'masteraudio',\n\t\t\t'playlist',\n\t\t\t'removeaudiogroup',\n\t\t\t'removeplaylist',\n\t\t\t'waitforaudio',\n\n\t\t\t// Deprecated.\n\t\t\t'setplaylist',\n\t\t\t'stopallaudio'\n\t\t], {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\t/* no-op */\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMiscellaneous Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<goto>>\n\t*/\n\tMacro.add('goto', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCall `Engine.play()` asynchronously.\n\n\t\t\t\tNOTE: This does not terminate the current Wikifier call chain,\n\t\t\t\tthough, ideally, it should.  Doing so would not be trivial, however,\n\t\t\t\tand there's also the question of whether that behavior would be\n\t\t\t\tunwanted by users, who are used to the current behavior from\n\t\t\t\tsimilar macros and constructs.\n\t\t\t*/\n\t\t\tsetTimeout(() => Engine.play(passage), Engine.minDomActionDelay);\n\t\t}\n\t});\n\n\t/*\n\t\t<<repeat>> & <<stop>>\n\t*/\n\tMacro.add('repeat', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified');\n\t\t\t}\n\n\t\t\tlet delay;\n\n\t\t\ttry {\n\t\t\t\tdelay = Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0]));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerInterval(this.createShadowWrapper(() => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-repeat-insert macro-repeat-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-repeat-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), delay);\n\t\t},\n\n\t\tregisterInterval(callback, delay) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId = null;\n\n\t\t\t// Set up the interval.\n\t\t\ttimerId = setInterval(() => {\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\tclearInterval(timerId);\n\t\t\t\t\ttimers.delete(timerId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet timerIdCache;\n\t\t\t\t/*\n\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t`Wikifier` call.\n\t\t\t\t*/\n\t\t\t\ttry {\n\t\t\t\t\tTempState.break = null;\n\n\t\t\t\t\t// Set up the `repeatTimerId` value, caching the existing value, if necessary.\n\t\t\t\t\tif (TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\t\t\ttimerIdCache = TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.repeatTimerId = timerId;\n\n\t\t\t\t\t// Execute the callback.\n\t\t\t\t\tcallback.call(this);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\t// Teardown the `repeatTimerId` property, restoring the cached value, if necessary.\n\t\t\t\t\tif (typeof timerIdCache !== 'undefined') {\n\t\t\t\t\t\tTempState.repeatTimerId = timerIdCache;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.break = null;\n\t\t\t\t}\n\t\t\t}, delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#repeat-timers-cleanup')) {\n\t\t\t\tprehistory['#repeat-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearInterval(timerId));\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\tMacro.add('stop', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (!TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<repeat>>');\n\t\t\t}\n\n\t\t\tconst timers  = Macro.get('repeat').timers;\n\t\t\tconst timerId = TempState.repeatTimerId;\n\t\t\tclearInterval(timerId);\n\t\t\ttimers.delete(timerId);\n\t\t\tTempState.break = 2;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<timed>> & <<next>>\n\t*/\n\tMacro.add('timed', {\n\t\tisAsync : true,\n\t\ttags    : ['next'],\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified in <<timed>>');\n\t\t\t}\n\n\t\t\tconst items = [];\n\n\t\t\ttry {\n\t\t\t\titems.push({\n\t\t\t\t\tname    : this.name,\n\t\t\t\t\tsource  : this.source,\n\t\t\t\t\tdelay   : Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0])),\n\t\t\t\t\tcontent : this.payload[0].contents\n\t\t\t\t});\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`${ex.message} in <<timed>>`);\n\t\t\t}\n\n\t\t\tif (this.payload.length > 1) {\n\t\t\t\tlet i;\n\n\t\t\t\ttry {\n\t\t\t\t\tlet len;\n\n\t\t\t\t\tfor (i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tname   : this.payload[i].name,\n\t\t\t\t\t\t\tsource : this.payload[i].source,\n\t\t\t\t\t\t\tdelay  : this.payload[i].args.length === 0\n\t\t\t\t\t\t\t\t? items[items.length - 1].delay\n\t\t\t\t\t\t\t\t: Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.payload[i].args[0])),\n\t\t\t\t\t\t\tcontent : this.payload[i].contents\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`${ex.message} in <<next>> (#${i})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerTimeout(this.createShadowWrapper(item => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, item.content);\n\n\t\t\t\t// Output.\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\t// Custom debug view setup for `<<next>>`.\n\t\t\t\tif (Config.debug && item.name === 'next') {\n\t\t\t\t\t$output = jQuery(new DebugView( // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t$output[0],\n\t\t\t\t\t\t'macro',\n\t\t\t\t\t\titem.name,\n\t\t\t\t\t\titem.source\n\t\t\t\t\t).output);\n\t\t\t\t}\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-timed-insert macro-timed-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-timed-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), items);\n\t\t},\n\n\t\tregisterTimeout(callback, items) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId  = null;\n\t\t\tlet nextItem = items.shift();\n\n\t\t\tconst worker = function () {\n\t\t\t\t// Bookkeeping.\n\t\t\t\ttimers.delete(timerId);\n\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the current item and set up the next worker, if any.\n\t\t\t\tconst curItem = nextItem;\n\n\t\t\t\tif ((nextItem = items.shift()) != null) { // lazy equality for null\n\t\t\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\t\t\ttimers.add(timerId);\n\t\t\t\t}\n\n\t\t\t\t// Execute the callback.\n\t\t\t\tcallback.call(this, curItem);\n\t\t\t};\n\n\t\t\t// Setup the timeout.\n\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#timed-timers-cleanup')) {\n\t\t\t\tprehistory['#timed-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearTimeout(timerId)); // eslint-disable-line no-shadow\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<widget>>\n\t*/\n\tMacro.add('widget', {\n\t\ttags : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no widget name specified');\n\t\t\t}\n\n\t\t\tconst widgetName = this.args[0];\n\n\t\t\tif (Macro.has(widgetName)) {\n\t\t\t\tif (!Macro.get(widgetName).isWidget) {\n\t\t\t\t\treturn this.error(`cannot clobber existing macro \"${widgetName}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Delete the existing widget.\n\t\t\t\tMacro.delete(widgetName);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMacro.add(widgetName, {\n\t\t\t\t\tisWidget : true,\n\t\t\t\t\thandler  : (function (contents) {\n\t\t\t\t\t\treturn function () {\n\t\t\t\t\t\t\tlet argsCache;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Cache the existing value of the `$args` variable, if necessary.\n\t\t\t\t\t\t\t\tif (State.variables.hasOwnProperty('args')) {\n\t\t\t\t\t\t\t\t\targsCache = State.variables.args;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set up the widget `$args` variable and add a shadow.\n\t\t\t\t\t\t\t\tState.variables.args = [...this.args];\n\t\t\t\t\t\t\t\tState.variables.args.raw = this.args.raw;\n\t\t\t\t\t\t\t\tState.variables.args.full = this.args.full;\n\t\t\t\t\t\t\t\tthis.addShadow('$args');\n\n\t\t\t\t\t\t\t\t// Set up the error trapping variables.\n\t\t\t\t\t\t\t\tconst resFrag = document.createDocumentFragment();\n\t\t\t\t\t\t\t\tconst errList = [];\n\n\t\t\t\t\t\t\t\t// Wikify the widget contents.\n\t\t\t\t\t\t\t\tnew Wikifier(resFrag, contents);\n\n\t\t\t\t\t\t\t\t// Carry over the output, unless there were errors.\n\t\t\t\t\t\t\t\tArray.from(resFrag.querySelectorAll('.error')).forEach(errEl => {\n\t\t\t\t\t\t\t\t\terrList.push(errEl.textContent);\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tif (errList.length === 0) {\n\t\t\t\t\t\t\t\t\tthis.output.appendChild(resFrag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\treturn this.error(`error${errList.length > 1 ? 's' : ''} within widget contents (${errList.join('; ')})`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot execute widget: ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t// Revert the `$args` variable shadowing.\n\t\t\t\t\t\t\t\tif (typeof argsCache !== 'undefined') {\n\t\t\t\t\t\t\t\t\tState.variables.args = argsCache;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete State.variables.args;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t})(this.payload[0].contents)\n\t\t\t\t});\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`cannot create widget macro \"${widgetName}\": ${ex.message}`);\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tdialog.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, L10n, safeActiveElement */\n\nvar Dialog = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Dialog element caches.\n\tlet _$overlay       = null;\n\tlet _$dialog        = null;\n\tlet _$dialogTitle   = null;\n\tlet _$dialogBody    = null;\n\n\t// The last active/focused non-dialog element.\n\tlet _lastActive     = null;\n\n\t// The width of the browser's scrollbars.\n\tlet _scrollbarWidth = 0;\n\n\t// Dialog mutation resize handler.\n\tlet _dialogObserver = null;\n\n\n\t/*******************************************************************************\n\t\tDialog Functions.\n\t*******************************************************************************/\n\n\t/*\n\t\t[DEPRECATED] Adds a click hander to the target element(s) which opens the dialog modal.\n\t*/\n\tfunction dialogAddClickHandler(targets, options, startFn, doneFn, closeFn) {\n\t\treturn jQuery(targets).ariaClick(ev => {\n\t\t\tev.preventDefault();\n\n\t\t\t// Call the start function.\n\t\t\tif (typeof startFn === 'function') {\n\t\t\t\tstartFn(ev);\n\t\t\t}\n\n\t\t\t// Open the dialog.\n\t\t\tdialogOpen(options, closeFn);\n\n\t\t\t// Call the done function.\n\t\t\tif (typeof doneFn === 'function') {\n\t\t\t\tdoneFn(ev);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction dialogBodyAppend(...args) {\n\t\t_$dialogBody.append(...args);\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogBody() {\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogClose(ev) {\n\t\t// Trigger a `:dialogclosing` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogclosing');\n\n\t\t// Largely reverse the actions taken in `dialogOpen()`.\n\t\tjQuery(document)\n\t\t\t.off('.dialog-close');\n\t\tif (_dialogObserver) {\n\t\t\t_dialogObserver.disconnect();\n\t\t\t_dialogObserver = null;\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.off('.dialog-resize');\n\t\t}\n\t\tjQuery(window)\n\t\t\t.off('.dialog-resize');\n\t\t_$dialog\n\t\t\t.removeClass('open')\n\t\t\t.css({ left : '', right : '', top : '', bottom : '' });\n\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex=-2]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.attr('tabindex', 0);\n\t\tjQuery('body>[tabindex=-3]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.removeAttr('tabindex');\n\n\t\t_$overlay\n\t\t\t.removeClass('open');\n\t\tjQuery(document.documentElement)\n\t\t\t.removeAttr('data-dialog');\n\n\t\t// Clear the dialog's content.\n\t\t_$dialogTitle\n\t\t\t.empty();\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\t// Attempt to restore focus to whichever element had it prior to opening the dialog.\n\t\tif (_lastActive !== null) {\n\t\t\tjQuery(_lastActive).focus();\n\t\t\t_lastActive = null;\n\t\t}\n\n\t\t// Call the given \"on close\" callback function, if any.\n\t\tif (ev && ev.data && typeof ev.data.closeFn === 'function') {\n\t\t\tev.data.closeFn(ev);\n\t\t}\n\n\t\t// Trigger a `:dialogclosed` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogclose');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogclosed');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogInit() {\n\t\tif (DEBUG) { console.log('[Dialog/dialogInit()]'); }\n\n\t\tif (document.getElementById('ui-dialog')) {\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t\tCalculate and cache the width of scrollbars.\n\t\t*/\n\t\t_scrollbarWidth = (() => {\n\t\t\tlet scrollbarWidth;\n\n\t\t\ttry {\n\t\t\t\tconst inner = document.createElement('p');\n\t\t\t\tconst outer = document.createElement('div');\n\n\t\t\t\tinner.style.width      = '100%';\n\t\t\t\tinner.style.height     = '200px';\n\t\t\t\touter.style.position   = 'absolute';\n\t\t\t\touter.style.left       = '0px';\n\t\t\t\touter.style.top        = '0px';\n\t\t\t\touter.style.width      = '100px';\n\t\t\t\touter.style.height     = '100px';\n\t\t\t\touter.style.visibility = 'hidden';\n\t\t\t\touter.style.overflow   = 'hidden';\n\n\t\t\t\touter.appendChild(inner);\n\t\t\t\tdocument.body.appendChild(outer);\n\n\t\t\t\tconst w1 = inner.offsetWidth;\n\t\t\t\t/*\n\t\t\t\t\tThe `overflow: scroll` style property value does not work consistently\n\t\t\t\t\twith scrollbars which are styled with `::-webkit-scrollbar`, so we use\n\t\t\t\t\t`overflow: auto` with dimensions guaranteed to force a scrollbar.\n\t\t\t\t*/\n\t\t\t\touter.style.overflow = 'auto';\n\t\t\t\tlet w2 = inner.offsetWidth;\n\n\t\t\t\tif (w1 === w2) {\n\t\t\t\t\tw2 = outer.clientWidth;\n\t\t\t\t}\n\n\t\t\t\tdocument.body.removeChild(outer);\n\n\t\t\t\tscrollbarWidth = w1 - w2;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn scrollbarWidth || 17; // 17px is a reasonable failover\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate the dialog elements.\n\t\t*/\n\t\tconst $elems = jQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"ui-overlay\" class=\"ui-close\"></div>'\n\t\t\t\t+ '<div id=\"ui-dialog\" tabindex=\"0\" role=\"dialog\" aria-labelledby=\"ui-dialog-title\">'\n\t\t\t\t+     '<div id=\"ui-dialog-titlebar\">'\n\t\t\t\t+         '<h1 id=\"ui-dialog-title\"></h1>'\n\t\t\t\t+         `<button id=\"ui-dialog-close\" class=\"ui-close\" tabindex=\"0\" aria-label=\"${L10n.get('close')}\">\\uE804</button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div id=\"ui-dialog-body\"></div>'\n\t\t\t\t+ '</div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t/*\n\t\t\tCache the dialog elements, since they're going to be used often.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$overlay     = jQuery($elems.find('#ui-overlay').get(0));\n\t\t_$dialog      = jQuery($elems.find('#ui-dialog').get(0));\n\t\t_$dialogTitle = jQuery($elems.find('#ui-dialog-title').get(0));\n\t\t_$dialogBody  = jQuery($elems.find('#ui-dialog-body').get(0));\n\n\t\t/*\n\t\t\tInsert the dialog elements into the page before the main script.\n\t\t*/\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t}\n\n\tfunction dialogIsOpen(classNames) {\n\t\treturn _$dialog.hasClass('open')\n\t\t\t&& (classNames ? classNames.splitOrEmpty(/\\s+/).every(cn => _$dialogBody.hasClass(cn)) : true);\n\t}\n\n\tfunction dialogOpen(options, closeFn) {\n\t\t// Trigger a `:dialogopening` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogopening');\n\n\t\t// Grab the options we care about.\n\t\tconst { top } = jQuery.extend({ top : 50 }, options);\n\n\t\t// Record the last active/focused non-dialog element.\n\t\tif (!dialogIsOpen()) {\n\t\t\t_lastActive = safeActiveElement();\n\t\t}\n\n\t\t// Add the `data-dialog` attribute to <html> (mostly used to style <body>).\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-dialog', 'open');\n\n\t\t// Display the overlay.\n\t\t_$overlay\n\t\t\t.addClass('open');\n\n\t\t/*\n\t\t\tAdd the imagesLoaded handler to the dialog body, if necessary.\n\n\t\t\tNOTE: We use `querySelector()` here as jQuery has no simple way to\n\t\t\tcheck if, and only if, at least one element of the specified type\n\t\t\texists.  The best that jQuery offers is analogous to `querySelectorAll()`,\n\t\t\twhich enumerates all elements of the specified type.\n\t\t*/\n\t\tif (_$dialogBody[0].querySelector('img') !== null) {\n\t\t\t_$dialogBody\n\t\t\t\t.imagesLoaded()\n\t\t\t\t.always(() => _resizeHandler({ data : { top } }));\n\t\t}\n\n\t\t// Add `aria-hidden=true` to all direct non-dialog-children of <body> to\n\t\t// hide the underlying page from screen readers while the dialog is open.\n\t\tjQuery('body>:not(script,#store-area,tw-storydata,#ui-bar,#ui-overlay,#ui-dialog)')\n\t\t\t.attr('tabindex', -3)\n\t\t\t.attr('aria-hidden', true);\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex]:not([tabindex^=-])')\n\t\t\t.attr('tabindex', -2)\n\t\t\t.attr('aria-hidden', true);\n\n\t\t// Display the dialog.\n\t\t_$dialog\n\t\t\t.css(_calcPosition(top))\n\t\t\t.addClass('open')\n\t\t\t.focus();\n\n\t\t// Add the UI resize handler.\n\t\tjQuery(window)\n\t\t\t.on('resize.dialog-resize', null, { top }, jQuery.throttle(40, _resizeHandler));\n\n\t\t// Add the dialog mutation resize handler.\n\t\tif (Has.mutationObserver) {\n\t\t\t_dialogObserver = new MutationObserver(mutations => {\n\t\t\t\tfor (let i = 0; i < mutations.length; ++i) {\n\t\t\t\t\tif (mutations[i].type === 'childList') {\n\t\t\t\t\t\t_resizeHandler({ data : { top } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t_dialogObserver.observe(_$dialogBody[0], {\n\t\t\t\tchildList : true,\n\t\t\t\tsubtree   : true\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.on(\n\t\t\t\t\t'DOMNodeInserted.dialog-resize DOMNodeRemoved.dialog-resize',\n\t\t\t\t\tnull,\n\t\t\t\t\t{ top },\n\t\t\t\t\tjQuery.throttle(40, _resizeHandler)\n\t\t\t\t);\n\t\t}\n\n\t\t// Set up the delegated UI close handler.\n\t\tjQuery(document)\n\t\t\t.one('click.dialog-close', '.ui-close', { closeFn }, ev => {\n\t\t\t\t// NOTE: Do not allow this event handler to return the `Dialog` static object,\n\t\t\t\t// as doing so causes Edge (ca. 18) to throw a \"Number expected\" exception due\n\t\t\t\t// to `Dialog` not having a prototype.\n\t\t\t\tdialogClose(ev);\n\t\t\t\t/* implicit `return undefined;` */\n\t\t\t})\n\t\t\t.one('keypress.dialog-close', '.ui-close', function (ev) {\n\t\t\t\t// 13 is Enter/Return, 32 is Space.\n\t\t\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\t\t\tjQuery(this).trigger('click');\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Trigger a `:dialogopened` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogopen');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogopened');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogResize(data) {\n\t\treturn _resizeHandler(typeof data === 'object' ? { data } : undefined);\n\t}\n\n\tfunction dialogSetup(title, classNames) {\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\tif (classNames != null) { // lazy equality for null\n\t\t\t_$dialogBody.addClass(classNames);\n\t\t}\n\n\t\t_$dialogTitle\n\t\t\t.empty()\n\t\t\t.append((title != null ? String(title) : '') || '\\u00A0'); // lazy equality for null\n\n\t\t// TODO: In v3 this should return `Dialog` for chaining.\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogBodyWiki(...args) {\n\t\t_$dialogBody.wiki(...args);\n\t\treturn Dialog;\n\t}\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _calcPosition(topPos) {\n\t\tconst top       = topPos != null ? topPos : 50; // lazy equality for null\n\t\tconst $parent   = jQuery(window);\n\t\tconst dialogPos = { left : '', right : '', top : '', bottom : '' };\n\n\t\t// Unset the dialog's positional properties before checking its dimensions.\n\t\t_$dialog.css(dialogPos);\n\n\t\tlet horzSpace = $parent.width() - _$dialog.outerWidth(true) - 1;   // -1 to address a Firefox issue\n\t\tlet vertSpace = $parent.height() - _$dialog.outerHeight(true) - 1; // -1 to address a Firefox issue\n\n\t\tif (horzSpace <= 32 + _scrollbarWidth) {\n\t\t\tvertSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (vertSpace <= 32 + _scrollbarWidth) {\n\t\t\thorzSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (horzSpace <= 32) {\n\t\t\tdialogPos.left = dialogPos.right = 16;\n\t\t}\n\t\telse {\n\t\t\tdialogPos.left = dialogPos.right = horzSpace / 2 >> 0;\n\t\t}\n\n\t\tif (vertSpace <= 32) {\n\t\t\tdialogPos.top = dialogPos.bottom = 16;\n\t\t}\n\t\telse {\n\t\t\tif (vertSpace / 2 > top) {\n\t\t\t\tdialogPos.top = top;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdialogPos.top = dialogPos.bottom = vertSpace / 2 >> 0;\n\t\t\t}\n\t\t}\n\n\t\tObject.keys(dialogPos).forEach(key => {\n\t\t\tif (dialogPos[key] !== '') {\n\t\t\t\tdialogPos[key] += 'px';\n\t\t\t}\n\t\t});\n\n\t\treturn dialogPos;\n\t}\n\n\tfunction _resizeHandler(ev) {\n\t\tconst top = ev && ev.data && typeof ev.data.top !== 'undefined' ? ev.data.top : 50;\n\n\t\tif (_$dialog.css('display') === 'block') {\n\t\t\t// Stow the dialog.\n\t\t\t_$dialog.css({ display : 'none' });\n\n\t\t\t// Restore the dialog with its new positional properties.\n\t\t\t_$dialog.css(jQuery.extend({ display : '' }, _calcPosition(top)));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tappend : { value : dialogBodyAppend },\n\t\tbody   : { value : dialogBody },\n\t\tclose  : { value : dialogClose },\n\t\tinit   : { value : dialogInit },\n\t\tisOpen : { value : dialogIsOpen },\n\t\topen   : { value : dialogOpen },\n\t\tresize : { value : dialogResize },\n\t\tsetup  : { value : dialogSetup },\n\t\twiki   : { value : dialogBodyWiki },\n\n\t\t// Legacy Functions.\n\t\taddClickHandler : { value : dialogAddClickHandler }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tengine.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Config, DebugView, Dialog, Has, LoadScreen, Save, State, Story, StyleWrapper, UI, UIBar, Util,\n\t       Wikifier, postdisplay, postrender, predisplay, prehistory, prerender, setDisplayTitle\n*/\n\nvar Engine = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Engine state types object (pseudo-enumeration).\n\tconst States = Util.toEnum({\n\t\tIdle      : 'idle',\n\t\tPlaying   : 'playing',\n\t\tRendering : 'rendering'\n\t});\n\n\t// Minimum delay for DOM actions (in milliseconds).\n\tconst minDomActionDelay = 40;\n\n\t// Current state of the engine (default: `Engine.States.Idle`).\n\tlet _state = States.Idle;\n\n\t// Last time `enginePlay()` was called (in milliseconds).\n\tlet _lastPlay = null;\n\n\t// Cache of the debug view for the StoryInit special passage.\n\tlet _storyInitDebugView = null;\n\n\t// Cache of the outline patching <style> element (`StyleWrapper`-wrapped).\n\tlet _outlinePatch = null;\n\n\t// List of objects describing `StoryInterface` elements to update via passages during navigation.\n\tlet _updating = null;\n\n\n\t/*******************************************************************************************************************\n\t\tEngine Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize the core story elements and perform some bookkeeping.\n\t*/\n\tfunction engineInit() {\n\t\tif (DEBUG) { console.log('[Engine/engineInit()]'); }\n\n\t\t/*\n\t\t\tRemove #init-no-js & #init-lacking from #init-screen.\n\t\t*/\n\t\tjQuery('#init-no-js,#init-lacking').remove();\n\n\t\t/*\n\t\t\tGenerate the core story elements and insert them into the page before the store area.\n\t\t*/\n\t\t(() => {\n\t\t\tconst $elems = jQuery(document.createDocumentFragment());\n\t\t\tconst markup = Story.has('StoryInterface') && Story.get('StoryInterface').text.trim();\n\n\t\t\tif (markup) {\n\t\t\t\t// Remove the UI bar, its styles, and events.\n\t\t\t\tUIBar.destroy();\n\n\t\t\t\t// Remove the core display area styles.\n\t\t\t\tjQuery(document.head).find('#style-core-display').remove();\n\n\t\t\t\t$elems.append(markup);\n\n\t\t\t\tif ($elems.find('#passages').length === 0) {\n\t\t\t\t\tthrow new Error('no element with ID \"passages\" found within \"StoryInterface\" special passage');\n\t\t\t\t}\n\n\t\t\t\tconst updating = [];\n\n\t\t\t\t$elems.find('[data-passage]').each((i, el) => {\n\t\t\t\t\tif (el.id === 'passages') {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} id=\"passages\"> must not contain a \"data-passage\" content attribute`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst passage = el.getAttribute('data-passage').trim();\n\n\t\t\t\t\tif (el.firstElementChild !== null) {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} data-passage=\"${passage}\"> contains child elements`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tupdating.push({\n\t\t\t\t\t\t\tpassage,\n\t\t\t\t\t\t\telement : el\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (updating.length > 0) {\n\t\t\t\t\t_updating = updating;\n\t\t\t\t}\n\n\t\t\t\tConfig.ui.updateStoryElements = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$elems.append('<div id=\"story\" role=\"main\"><div id=\"passages\"></div></div>');\n\t\t\t}\n\n\t\t\t// Insert the core UI elements into the page before the main script.\n\t\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate and cache the ARIA outlines <style> element (`StyleWrapper`-wrapped)\n\t\t\tand set up the handler to manipulate the outlines.\n\n\t\t\tIDEA: http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/\n\t\t*/\n\t\t_outlinePatch = new StyleWrapper((\n\t\t\t() => jQuery(document.createElement('style'))\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-aria-outlines',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t})\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.get(0) // return the <style> element itself\n\t\t)());\n\t\t_hideOutlines(); // initially hide outlines\n\t\tlet _lastOutlineEvent;\n\t\tjQuery(document).on(\n\t\t\t'mousedown.aria-outlines keydown.aria-outlines',\n\t\t\tev => {\n\t\t\t\tif (ev.type !== _lastOutlineEvent) {\n\t\t\t\t\t_lastOutlineEvent = ev.type;\n\n\t\t\t\t\tif (ev.type === 'keydown') {\n\t\t\t\t\t\t_showOutlines();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t_hideOutlines();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\n\t/*\n\t\tStarts the story.\n\t*/\n\tfunction engineStart() {\n\t\tif (DEBUG) { console.log('[Engine/engineStart()]'); }\n\n\t\t/*\n\t\t\tExecute the StoryInit special passage.\n\t\t*/\n\t\tif (Story.has('StoryInit')) {\n\t\t\ttry {\n\t\t\t\tconst debugBuffer = Wikifier.wikifyEval(Story.get('StoryInit').text);\n\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tconst debugView = new DebugView(\n\t\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t\t'special',\n\t\t\t\t\t\t'StoryInit',\n\t\t\t\t\t\t'StoryInit'\n\t\t\t\t\t);\n\t\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\t\tdebugView.append(debugBuffer);\n\t\t\t\t\t_storyInitDebugView = debugView.output;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('StoryInit', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Sanity checks.\n\t\tif (Config.passages.start == null) { // lazy equality for null\n\t\t\tthrow new Error('starting passage not selected');\n\t\t}\n\t\tif (!Story.has(Config.passages.start)) {\n\t\t\tthrow new Error(`starting passage (\"${Config.passages.start}\") not found`);\n\t\t}\n\n\t\t// Focus the document element initially.\n\t\tjQuery(document.documentElement).focus();\n\n\t\t/*\n\t\t\tAttempt to restore an active session.  Failing that, attempt to autoload the autosave,\n\t\t\tif requested.  Failing that, display the starting passage.\n\t\t*/\n\t\tif (State.restore()) {\n\t\t\tengineShow();\n\t\t}\n\t\telse {\n\t\t\tlet loadStart = true;\n\n\t\t\tswitch (typeof Config.saves.autoload) {\n\t\t\tcase 'boolean':\n\t\t\t\tif (Config.saves.autoload && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tif (Config.saves.autoload === 'prompt' && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tloadStart = false;\n\t\t\t\t\tUI.buildAutoload();\n\t\t\t\t\tDialog.open();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'function':\n\t\t\t\tif (Save.autosave.ok() && Save.autosave.has() && !!Config.saves.autoload()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (loadStart) {\n\t\t\t\tif (DEBUG) { console.log(`\\tstarting passage: \"${Config.passages.start}\"`); }\n\n\t\t\t\tenginePlay(Config.passages.start);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t\tRestarts the story.\n\t*/\n\tfunction engineRestart() {\n\t\tif (DEBUG) { console.log('[Engine/engineRestart()]'); }\n\n\t\t/*\n\t\t\tShow the loading screen to hide any unsightly rendering shenanigans during the\n\t\t\tpage reload.\n\t\t*/\n\t\tLoadScreen.show();\n\n\t\t/*\n\t\t\tScroll the window to the top.\n\n\t\t\tThis is required by most browsers for the starting passage or it will remain at\n\t\t\twhatever its current scroll position is after the page reload.  We do it generally,\n\t\t\trather than only for the currently set starting passage, since the starting passage\n\t\t\tmay be dynamically manipulated.\n\t\t*/\n\t\twindow.scroll(0, 0);\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tState.reset();\n\n\t\t/*\n\t\t\tTrigger an ':enginerestart' event.\n\t\t*/\n\t\tjQuery.event.trigger(':enginerestart');\n\n\t\t/*\n\t\t\tReload the page.\n\t\t*/\n\t\twindow.location.reload();\n\t}\n\n\t/*\n\t\tReturns the current state of the engine.\n\t*/\n\tfunction engineState() {\n\t\treturn _state;\n\t}\n\n\t/*\n\t\tReturns whether the engine is idle.\n\t*/\n\tfunction engineIsIdle() {\n\t\treturn _state === States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is playing.\n\t*/\n\tfunction engineIsPlaying() {\n\t\treturn _state !== States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is rendering.\n\t*/\n\tfunction engineIsRendering() {\n\t\treturn _state === States.Rendering;\n\t}\n\n\t/*\n\t\tReturns a timestamp representing the last time `Engine.play()` was called.\n\t*/\n\tfunction engineLastPlay() {\n\t\treturn _lastPlay;\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the state history and show it.\n\t*/\n\tfunction engineGoTo(idx) {\n\t\tconst succeded = State.goTo(idx);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the state history\n\t\tand show it.\n\t*/\n\tfunction engineGo(offset) {\n\t\tconst succeded = State.go(offset);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tGo to the moment which directly precedes the active moment and show it.\n\t*/\n\tfunction engineBackward() {\n\t\treturn engineGo(-1);\n\t}\n\n\t/*\n\t\tGo to the moment which directly follows the active moment and show it.\n\t*/\n\tfunction engineForward() {\n\t\treturn engineGo(1);\n\t}\n\n\t/*\n\t\tRenders and displays the active (present) moment's associated passage without adding\n\t\ta new moment to the history.\n\t*/\n\tfunction engineShow() {\n\t\treturn enginePlay(State.passage, true);\n\t}\n\n\t/*\n\t\tRenders and displays the passage referenced by the given title, optionally without\n\t\tadding a new moment to the history.\n\t*/\n\tfunction enginePlay(title, noHistory) {\n\t\tif (DEBUG) { console.log(`[Engine/enginePlay(title: \"${title}\", noHistory: ${noHistory})]`); }\n\n\t\tlet passageTitle = title;\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Reset the temporary state and variables objects.\n\t\tTempState = {}; // eslint-disable-line no-undef\n\t\tState.clearTemporary();\n\n\t\t// Debug view setup.\n\t\tlet passageReadyOutput;\n\t\tlet passageDoneOutput;\n\n\t\t// Execute the navigation override callback.\n\t\tif (typeof Config.navigation.override === 'function') {\n\t\t\ttry {\n\t\t\t\tconst overrideTitle = Config.navigation.override(passageTitle);\n\n\t\t\t\tif (overrideTitle) {\n\t\t\t\t\tpassageTitle = overrideTitle;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\t\t}\n\n\t\t// Retrieve the passage by the given title.\n\t\t//\n\t\t// NOTE: The values of the `title` parameter and `passageTitle` variable\n\t\t// may be empty, strings, or numbers (though using a number as reference\n\t\t// to a numeric title should be discouraged), so after loading the passage,\n\t\t// always refer to `passage.title` and never to the others.\n\t\tconst passage = Story.get(passageTitle);\n\n\t\t// Execute the pre-history events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype : ':passageinit',\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prehistory).forEach(task => {\n\t\t\tif (typeof prehistory[task] === 'function') {\n\t\t\t\tprehistory[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Create a new entry in the history.\n\t\tif (!noHistory) {\n\t\t\tState.create(passage.title);\n\t\t}\n\n\t\t// Clear the document body's classes.\n\t\tif (document.body.className) {\n\t\t\tdocument.body.className = '';\n\t\t}\n\n\t\t// Update the last play time.\n\t\t//\n\t\t// NOTE: This is mostly for event, task, and special passage code,\n\t\t// though the likelihood of it being needed this early is low.  This\n\t\t// will be updated again later at the end.\n\t\t_lastPlay = Util.now();\n\n\t\t// Execute pre-display tasks and the `PassageReady` special passage.\n\t\tObject.keys(predisplay).forEach(task => {\n\t\t\tif (typeof predisplay[task] === 'function') {\n\t\t\t\tpredisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\tif (Story.has('PassageReady')) {\n\t\t\ttry {\n\t\t\t\tpassageReadyOutput = Wikifier.wikifyEval(Story.get('PassageReady').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageReady', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Update the engine state.\n\t\t_state = States.Rendering;\n\n\t\t// Get the passage's tags as a string, or `null` if there aren't any.\n\t\tconst dataTags = passage.tags.length > 0 ? passage.tags.join(' ') : null;\n\n\t\t// Create and set up the incoming passage element.\n\t\tconst passageEl = document.createElement('div');\n\t\tjQuery(passageEl)\n\t\t\t.attr({\n\t\t\t\tid             : passage.domId,\n\t\t\t\t'data-passage' : passage.title,\n\t\t\t\t'data-tags'    : dataTags\n\t\t\t})\n\t\t\t.addClass(`passage ${passage.className}`);\n\n\t\t// Add the passage's classes and tags to the document body.\n\t\tjQuery(document.body)\n\t\t\t.attr('data-tags', dataTags)\n\t\t\t.addClass(passage.className);\n\n\t\t// Add the passage's tags to the document element.\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-tags', dataTags);\n\n\t\t// Execute pre-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagestart',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prerender).forEach(task => {\n\t\t\tif (typeof prerender[task] === 'function') {\n\t\t\t\tprerender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Render the `PassageHeader` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageHeader')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageHeader').processText());\n\t\t}\n\n\t\t// Render the passage into its element.\n\t\tpassageEl.appendChild(passage.render());\n\n\t\t// Render the `PassageFooter` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageFooter')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageFooter').processText());\n\t\t}\n\n\t\t// Execute post-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagerender',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postrender).forEach(task => {\n\t\t\tif (typeof postrender[task] === 'function') {\n\t\t\t\tpostrender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Cache the passage container.\n\t\tconst containerEl = document.getElementById('passages');\n\n\t\t// Empty the passage container.\n\t\tif (containerEl.hasChildNodes()) {\n\t\t\tif (\n\t\t\t\t   typeof Config.passages.transitionOut === 'number'\n\t\t\t\t|| typeof Config.passages.transitionOut === 'string'\n\t\t\t\t&& Config.passages.transitionOut !== ''\n\t\t\t\t&& Has.transitionEndEvent\n\t\t\t) {\n\t\t\t\t[...containerEl.childNodes].forEach(outgoing => {\n\t\t\t\t\tconst $outgoing = jQuery(outgoing);\n\n\t\t\t\t\tif (outgoing.nodeType === Node.ELEMENT_NODE && $outgoing.hasClass('passage')) {\n\t\t\t\t\t\tif ($outgoing.hasClass('passage-out')) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$outgoing\n\t\t\t\t\t\t\t.attr('id', `out-${$outgoing.attr('id')}`)\n\t\t\t\t\t\t\t.addClass('passage-out');\n\n\t\t\t\t\t\tif (typeof Config.passages.transitionOut === 'string') {\n\t\t\t\t\t\t\t$outgoing.on(Has.transitionEndEvent, ev => {\n\t\t\t\t\t\t\t\tif (ev.originalEvent.propertyName === Config.passages.transitionOut) {\n\t\t\t\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t\t() => $outgoing.remove(),\n\t\t\t\t\t\t\t\tMath.max(minDomActionDelay, Config.passages.transitionOut)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(containerEl).empty();\n\t\t\t}\n\t\t}\n\n\t\t// Append the passage element to the passage container and set up its transition.\n\t\tjQuery(passageEl)\n\t\t\t.addClass('passage-in')\n\t\t\t.appendTo(containerEl);\n\t\tsetTimeout(() => jQuery(passageEl).removeClass('passage-in'), minDomActionDelay);\n\n\t\t// Update the story display title, if necessary.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\t// NOTE: We don't have an `else` here because that case will be handled later (below).\n\t\t\tif (_updating !== null || !Config.ui.updateStoryElements) {\n\t\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t\t}\n\t\t}\n\t\telse if (Config.passages.displayTitles && passage.title !== Config.passages.start) {\n\t\t\tdocument.title = `${passage.title} | ${Story.title}`;\n\t\t}\n\n\t\t// Scroll the window to the top.\n\t\twindow.scroll(0, 0);\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Execute post-display events, tasks, and the `PassageDone` special passage.\n\t\tif (Story.has('PassageDone')) {\n\t\t\ttry {\n\t\t\t\tpassageDoneOutput = Wikifier.wikifyEval(Story.get('PassageDone').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageDone', ex.message);\n\t\t\t}\n\t\t}\n\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagedisplay',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postdisplay).forEach(task => {\n\t\t\tif (typeof postdisplay[task] === 'function') {\n\t\t\t\tpostdisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Update the other interface elements, if necessary.\n\t\tif (_updating !== null) {\n\t\t\t_updating.forEach(pair => {\n\t\t\t\tjQuery(pair.element).empty();\n\t\t\t\tnew Wikifier(pair.element, Story.get(pair.passage).processText().trim());\n\t\t\t});\n\t\t}\n\t\telse if (Config.ui.updateStoryElements) {\n\t\t\tUIBar.update();\n\t\t}\n\n\t\t// Add the completed debug views for `StoryInit`, `PassageReady`, and `PassageDone`\n\t\t// to the incoming passage element.\n\t\tif (Config.debug) {\n\t\t\tlet debugView;\n\n\t\t\t// Prepend the `PassageReady` debug view.\n\t\t\tif (passageReadyOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageReady',\n\t\t\t\t\t'PassageReady'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageReadyOutput);\n\t\t\t\tjQuery(passageEl).prepend(debugView.output);\n\t\t\t}\n\n\t\t\t// Append the `PassageDone` debug view.\n\t\t\tif (passageDoneOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageDone',\n\t\t\t\t\t'PassageDone'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageDoneOutput);\n\t\t\t\tjQuery(passageEl).append(debugView.output);\n\t\t\t}\n\n\t\t\t// Prepend the cached `StoryInit` debug view, if we're showing the first moment/turn.\n\t\t\tif (State.turns === 1 && _storyInitDebugView != null) { // lazy equality for null\n\t\t\t\tjQuery(passageEl).prepend(_storyInitDebugView);\n\t\t\t}\n\t\t}\n\n\t\t// Last second post-processing for accessibility and other things.\n\t\tjQuery('#story')\n\t\t\t// Add `link-external` to all `href` bearing `<a>` elements which don't have it.\n\t\t\t.find('a[href]:not(.link-external)')\n\t\t\t.addClass('link-external')\n\t\t\t.end()\n\t\t\t// Add `tabindex=0` to all interactive elements which don't have it.\n\t\t\t.find('a,link,button,input,select,textarea')\n\t\t\t.not('[tabindex]')\n\t\t\t.attr('tabindex', 0);\n\n\t\t// Handle autosaves.\n\t\tswitch (typeof Config.saves.autosave) {\n\t\tcase 'boolean':\n\t\t\tif (Config.saves.autosave) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'object':\n\t\t\tif (passage.tags.some(tag => Config.saves.autosave.includes(tag))) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'function':\n\t\t\tif (Config.saves.autosave()) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// Execute post-play events.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passageend',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\n\t\t// Reset the engine state.\n\t\t_state = States.Idle;\n\n\t\t// Update the last play time.\n\t\t_lastPlay = Util.now();\n\n\t\treturn passageEl;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Play the given passage, optionally without altering the history.\n\t*/\n\tfunction engineDisplay(title, link, option) {\n\t\tif (DEBUG) { console.log('[Engine/engineDisplay()]'); }\n\n\t\tlet noHistory = false;\n\n\t\t// Process the option parameter.\n\t\tswitch (option) {\n\t\tcase undefined:\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'replace':\n\t\tcase 'back':\n\t\t\tnoHistory = true;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`Engine.display option parameter called with obsolete value \"${option}\"; please notify the developer`);\n\t\t}\n\n\t\tenginePlay(title, noHistory);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _hideOutlines() {\n\t\t_outlinePatch.set('*:focus{outline:none;}');\n\t}\n\n\tfunction _showOutlines() {\n\t\t_outlinePatch.clear();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tConstants.\n\t\t*/\n\t\tStates            : { value : States },\n\t\tminDomActionDelay : { value : minDomActionDelay },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tinit        : { value : engineInit },\n\t\tstart       : { value : engineStart },\n\t\trestart     : { value : engineRestart },\n\t\tstate       : { get : engineState },\n\t\tisIdle      : { value : engineIsIdle },\n\t\tisPlaying   : { value : engineIsPlaying },\n\t\tisRendering : { value : engineIsRendering },\n\t\tlastPlay    : { get : engineLastPlay },\n\t\tgoTo        : { value : engineGoTo },\n\t\tgo          : { value : engineGo },\n\t\tbackward    : { value : engineBackward },\n\t\tforward     : { value : engineForward },\n\t\tshow        : { value : engineShow },\n\t\tplay        : { value : enginePlay },\n\n\t\t/*\n\t\t\tLegacy Functions.\n\t\t*/\n\t\tdisplay : { value : engineDisplay }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tpassage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, Util, Wikifier */\n\nvar Passage = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tlet _tagsToSkip;\n\tlet _twine1Unescape;\n\n\t/*\n\t\tTags which should not be transformed into classes:\n\t\t\tdebug      → special tag\n\t\t\tnobr       → special tag\n\t\t\tpassage    → the default class\n\t\t\tscript     → special tag (only in Twine 1)\n\t\t\tstylesheet → special tag (only in Twine 1)\n\t\t\ttwine.*    → special tag\n\t\t\twidget     → special tag\n\t*/\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|script|stylesheet|widget|twine\\..*)$/i;\n\t}\n\t// For Twine 2\n\telse {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|widget|twine\\..*)$/i;\n\t}\n\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t/*\n\t\t\tReturns a decoded version of the passed Twine 1 passage store encoded string.\n\t\t*/\n\t\tconst _twine1EscapesRe    = /(?:\\\\n|\\\\t|\\\\s|\\\\|\\r)/g;\n\t\tconst _hasTwine1EscapesRe = new RegExp(_twine1EscapesRe.source); // to drop the global flag\n\t\tconst _twine1EscapesMap   = Object.freeze({\n\t\t\t'\\\\n' : '\\n',\n\t\t\t'\\\\t' : '\\t',\n\t\t\t'\\\\s' : '\\\\',\n\t\t\t'\\\\'  : '\\\\',\n\t\t\t'\\r'  : ''\n\t\t});\n\n\t\t_twine1Unescape = function (str) {\n\t\t\tif (str == null) { // lazy equality for null\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst val = String(str);\n\t\t\treturn val && _hasTwine1EscapesRe.test(val)\n\t\t\t\t? val.replace(_twine1EscapesRe, esc => _twine1EscapesMap[esc])\n\t\t\t\t: val;\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Class.\n\t*******************************************************************************************************************/\n\tclass Passage {\n\t\tconstructor(title, el) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage title/ID.\n\t\t\t\ttitle : {\n\t\t\t\t\tvalue : Util.unescape(title)\n\t\t\t\t},\n\n\t\t\t\t// Passage data element (within the story data element; i.e. T1: '[tiddler]', T2: 'tw-passagedata').\n\t\t\t\telement : {\n\t\t\t\t\tvalue : el || null\n\t\t\t\t},\n\n\t\t\t\t// Passage tags array (sorted and unique).\n\t\t\t\ttags : {\n\t\t\t\t\tvalue : Object.freeze(el && el.hasAttribute('tags')\n\t\t\t\t\t\t? el.getAttribute('tags')\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.splitOrEmpty(/\\s+/)\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.filter((tag, i, aref) => i === 0 || aref[i - 1] !== tag)\n\t\t\t\t\t\t: [])\n\t\t\t\t},\n\n\t\t\t\t// Passage excerpt.  Used by the `description()` method.\n\t\t\t\t_excerpt : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Properties dependant upon the above set.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage DOM-compatible ID.\n\t\t\t\tdomId : {\n\t\t\t\t\tvalue : `passage-${Util.slugify(this.title)}`\n\t\t\t\t},\n\n\t\t\t\t// Passage classes array (sorted and unique).\n\t\t\t\tclasses : {\n\t\t\t\t\tvalue : Object.freeze(this.tags.length === 0 ? [] : (() =>\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tReturn the sorted list of unique classes.\n\n\t\t\t\t\t\t\tNOTE: The `this.tags` array is already sorted and unique,\n\t\t\t\t\t\t\tso we only need to filter and map here.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tthis.tags\n\t\t\t\t\t\t\t.filter(tag => !_tagsToSkip.test(tag))\n\t\t\t\t\t\t\t.map(tag => Util.slugify(tag))\n\t\t\t\t\t)())\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Getters.\n\t\tget className() {\n\t\t\treturn this.classes.join(' ');\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get source`.\n\t\tget text() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\tconst passage = Util.escapeMarkup(this.title);\n\t\t\t\tconst mesg    = `${L10n.get('errorTitle')}: ${L10n.get('errorNonexistentPassage', { passage })}`;\n\t\t\t\treturn `<div class=\"error-view\"><span class=\"error\">${mesg}</span></div>`;\n\t\t\t}\n\n\t\t\t// For Twine 1\n\t\t\tif (TWINE1) {\n\t\t\t\treturn _twine1Unescape(this.element.textContent);\n\t\t\t}\n\t\t\t// For Twine 2\n\t\t\telse { // eslint-disable-line no-else-return\n\t\t\t\treturn this.element.textContent.replace(/\\r/g, '');\n\t\t\t}\n\t\t}\n\n\t\tdescription() {\n\t\t\tconst descriptions = Config.passages.descriptions;\n\n\t\t\tif (descriptions != null) { // lazy equality for null\n\t\t\t\tswitch (typeof descriptions) {\n\t\t\t\tcase 'boolean':\n\t\t\t\t\tif (descriptions) {\n\t\t\t\t\t\treturn this.title;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (descriptions instanceof Map && descriptions.has(this.title)) {\n\t\t\t\t\t\treturn descriptions.get(this.title);\n\t\t\t\t\t}\n\t\t\t\t\telse if (descriptions.hasOwnProperty(this.title)) {\n\t\t\t\t\t\treturn descriptions[this.title];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'function':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst result = descriptions.call(this);\n\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new TypeError('Config.passages.descriptions must be a boolean, object, or function');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Initialize the excerpt cache from the raw passage text, if necessary.\n\t\t\tif (this._excerpt === null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromText(this.text);\n\t\t\t}\n\n\t\t\treturn this._excerpt;\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get text`.\n\t\tprocessText() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\treturn this.text;\n\t\t\t}\n\n\t\t\t// Handle image passage transclusion.\n\t\t\tif (this.tags.includes('Twine.image')) {\n\t\t\t\treturn `[img[${this.text}]]`;\n\t\t\t}\n\n\t\t\tlet processed = this.text;\n\n\t\t\t// Handle `Config.passages.onProcess`.\n\t\t\tif (Config.passages.onProcess) {\n\t\t\t\tprocessed = Config.passages.onProcess.call(null, {\n\t\t\t\t\ttitle : this.title,\n\t\t\t\t\ttags  : this.tags,\n\t\t\t\t\ttext  : processed\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle `Config.passages.nobr` and the `nobr` tag.\n\t\t\tif (Config.passages.nobr || this.tags.includes('nobr')) {\n\t\t\t\t// Remove all leading & trailing newlines and compact all internal sequences\n\t\t\t\t// of newlines into single spaces.\n\t\t\t\tprocessed = processed.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' ');\n\t\t\t}\n\n\t\t\treturn processed;\n\t\t}\n\n\t\trender(options) {\n\t\t\t// Wikify the passage into a document fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.processText(), options);\n\n\t\t\t// Update the excerpt cache to reflect the rendered text, if we need it for the passage description\n\t\t\tif (Config.passages.descriptions == null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromNode(frag);\n\t\t\t}\n\n\t\t\treturn frag;\n\t\t}\n\n\t\tstatic getExcerptFromNode(node, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromNode(node=…, count=${count})]`, node); }\n\n\t\t\tif (!node.hasChildNodes()) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t// WARNING: es5-shim's `<String>.trim()` can cause \"too much recursion\" errors\n\t\t\t// here on very large strings (e.g., ≥40 KiB), at least in Firefox, for unknown\n\t\t\t// reasons.\n\t\t\t//\n\t\t\t// To fix the issue, we're removed `\\u180E` from es5-shim's whitespace pattern\n\t\t\t// to prevent it from erroneously shimming `<String>.trim()` in the first place.\n\t\t\tlet excerpt = node.textContent.trim();\n\n\t\t\tif (excerpt !== '') {\n\t\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\t\texcerpt = excerpt\n\t\t\t\t\t// Compact whitespace.\n\t\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t\t.match(excerptRe);\n\t\t\t}\n\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\n\t\tstatic getExcerptFromText(text, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromText(text=…, count=${count})]`, text); }\n\n\t\t\tif (text === '') {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\tconst excerpt   = text\n\t\t\t\t// Strip macro tags (replace with a space).\n\t\t\t\t.replace(/<<.*?>>/g, ' ')\n\t\t\t\t// Strip html tags (replace with a space).\n\t\t\t\t.replace(/<.*?>/g, ' ')\n\t\t\t\t// The above might have left problematic whitespace, so trim.\n\t\t\t\t.trim()\n\t\t\t\t// Strip table markup.\n\t\t\t\t.replace(/^\\s*\\|.*\\|.*?$/gm, '')\n\t\t\t\t// Strip image markup.\n\t\t\t\t.replace(/\\[[<>]?img\\[[^\\]]*\\]\\]/g, '')\n\t\t\t\t// Clean link markup (remove all but the link text).\n\t\t\t\t.replace(/\\[\\[([^|\\]]*?)(?:(?:\\||->|<-)[^\\]]*)?\\]\\]/g, '$1')\n\t\t\t\t// Clean heading markup.\n\t\t\t\t.replace(/^\\s*!+(.*?)$/gm, '$1')\n\t\t\t\t// Clean bold/italic/underline/highlight styles.\n\t\t\t\t.replace(/'{2}|\\/{2}|_{2}|@{2}/g, '')\n\t\t\t\t// A final trim.\n\t\t\t\t.trim()\n\t\t\t\t// Compact whitespace.\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t.match(excerptRe);\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Passage;\n})();\n\n/***********************************************************************************************************************\n\n\tsave.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Dialog, Engine, L10n, State, Story, UI, Util, storage */\n\nvar Save = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Save operation type pseudo-enumeration.\n\tconst Type = Util.toEnum({\n\t\tAutosave  : 'autosave',\n\t\tDisk      : 'disk',\n\t\tSerialize : 'serialize',\n\t\tSlot      : 'slot'\n\t});\n\n\t// The upper bound of the saves slots.\n\tlet _slotsUBound = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tSaves Functions.\n\t*******************************************************************************************************************/\n\tfunction savesInit() {\n\t\tif (DEBUG) { console.log('[Save/savesInit()]'); }\n\n\t\t// Disable save slots and the autosave when Web Storage is unavailable.\n\t\tif (storage.name === 'cookie') {\n\t\t\tsavesObjClear();\n\t\t\tConfig.saves.autoload = undefined;\n\t\t\tConfig.saves.autosave = undefined;\n\t\t\tConfig.saves.slots = 0;\n\t\t\treturn false;\n\t\t}\n\n\n\t\t// convert the single big saves object into the new smaller objects\n\t\tconst saves = storage.get('saves');\n\t\tif (saves !== null) {\n\t\t\tstorage.delete('saves'); // Make sure we have enough space to store the saves in the new format\n\t\t\tconst container = Dialog.setup('Backup');\n\t\t\tconst message = document.createElement('span');\n\t\t\tmessage.className = 'red';\n\t\t\tmessage.append('Due to changes to the saves system your existing saves were converted. Backup all saves' +\n\t\t\t\t' that are important to you as they may have been lost during conversion. Once you close this dialog' +\n\t\t\t\t' it will be IMPOSSIBLE to get your old saves back.');\n\t\t\tcontainer.append(message);\n\t\t\tconst index = indexGet();\n\t\t\t// we store the index every time we store a save, so in case we exceed local storage only the\n\t\t\t// last save is lost\n\t\t\tif (saves.autosave !== null) {\n\t\t\t\tcontainer.append(savesLink('autosave', saves.autosave));\n\t\t\t\tindex.autosave = { title : saves.autosave.title, date : saves.autosave.date };\n\t\t\t\ttry {\n\t\t\t\t\tstorage.set('autosave', saves.autosave);\n\t\t\t\t\tindexSave(index);\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tstorage.delete('autosave');\n\t\t\t\t\tindex.autosave = null;\n\t\t\t\t\tindexSave(index);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (let i = 0; i < saves.slots.length; i++) {\n\t\t\t\tif (saves.slots[i] !== null) {\n\t\t\t\t\tcontainer.append(savesLink(`slot${i}`, saves.slots[i]));\n\t\t\t\t\tindex.slots[i] = { title : saves.slots[i].title, date : saves.slots[i].date };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstorage.set(`slot${i}`, saves.slots[i]);\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tstorage.delete(`slot${i}`);\n\t\t\t\t\t\tindex.slots[i] = null;\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tDialog.open();\n\t\t}\n\n\t\tfunction savesLink(name, save) {\n\t\t\tconst div = document.createElement('div');\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.append(`Backup ${name}`);\n\t\t\ta.onclick = () => {\n\t\t\t\texportToDisk(`backup-${name}`, {}, save);\n\t\t\t};\n\t\t\tdiv.append(a);\n\t\t\treturn div;\n\t\t}\n\n\t\t/* All SC2 compatibility converters are disabled, too much work */\n\n\t\t/* legacy */\n\t\t// Convert an ancient saves array into a new saves object.\n\t\t/* if (Array.isArray(saves)) {\n\t\t\tsaves = {\n\t\t\t\tautosave : null,\n\t\t\t\tslots    : saves\n\t\t\t};\n\t\t\tupdated = true;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Handle the author changing the number of save slots.\n\t\t/*\n\t\tif (Config.saves.slots !== saves.slots.length) {\n\t\t\tif (Config.saves.slots < saves.slots.length) {\n\t\t\t\t// Attempt to decrease the number of slots; this will only compact\n\t\t\t\t// the slots array, by removing empty slots, no saves will be deleted.\n\t\t\t\tsaves.slots.reverse();\n\n\t\t\t\tsaves.slots = saves.slots.filter(function (val) {\n\t\t\t\t\tif (val === null && this.count > 0) {\n\t\t\t\t\t\t--this.count;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}, { count : saves.slots.length - Config.saves.slots });\n\n\t\t\t\tsaves.slots.reverse();\n\t\t\t}\n\t\t\telse if (Config.saves.slots > saves.slots.length) {\n\t\t\t\t// Attempt to increase the number of slots.\n\t\t\t\t_appendSlots(saves.slots, Config.saves.slots - saves.slots.length);\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t */\n\n\t\t/* legacy */\n\t\t// Update saves with old/obsolete properties.\n\t\t/* if (_savesObjUpdate(saves.autosave)) {\n\t\t\tupdated = true;\n\t\t}\n\n\t\tfor (let i = 0; i < saves.slots.length; ++i) {\n\t\t\tif (_savesObjUpdate(saves.slots[i])) {\n\t\t\t\tupdated = true;\n\t\t\t}\n\t\t}\n\n\t\t// Remove save stores which are empty.\n\t\tif (_savesObjIsEmpty(saves)) {\n\t\t\tstorage.delete('saves');\n\t\t\tupdated = false;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// If the saves object was updated, then update the store.\n\t\t/* if (updated) {\n\t\t\t_savesObjSave(saves);\n\t\t}\n\t\t*/\n\n\t\t_slotsUBound = indexGet().slots.length - 1;\n\n\t\treturn true;\n\t}\n\n\tfunction indexCreate() {\n\t\treturn {\n\t\t\tautosave : null,\n\t\t\tslots    : _appendSlots([], Config.saves.slots)\n\t\t};\n\t}\n\n\tfunction indexGet() {\n\t\tconst index = storage.get('index');\n\t\treturn index === null ? indexCreate() : index;\n\t}\n\n\tfunction indexSave(index) {\n\t\treturn storage.set('index', index);\n\t}\n\n\tfunction savesObjClear() {\n\t\tstorage.delete('autosave');\n\t\tconst index = indexGet();\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tstorage.delete(`slot${i}`);\n\t\t}\n\t\tstorage.delete('index');\n\t\treturn true;\n\t}\n\n\tfunction savesOk() {\n\t\treturn autosaveOk() || slotsOk();\n\t}\n\n\t/*******************************************************************************************************************\n\t\tAutosave Functions.\n\t*******************************************************************************************************************/\n\tfunction autosaveOk() {\n\t\treturn storage.name !== 'cookie' && typeof Config.saves.autosave !== 'undefined';\n\t}\n\n\tfunction autosaveHas() {\n\t\treturn indexGet().autosave !== null;\n\t}\n\n\tfunction autosaveGet() {\n\t\treturn storage.get('autosave');\n\t}\n\n\tfunction autosaveLoad() {\n\t\tconst autosave = autosaveGet();\n\n\t\tif (autosave === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn _unmarshal(autosave);\n\t}\n\n\tfunction autosaveSave(title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle : title || Story.get(State.passage).description(),\n\t\t\tdate  : Date.now()\n\t\t};\n\t\tconst index = indexGet();\n\t\tindex.autosave = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set('autosave', _marshal(supplemental, { type : Type.Autosave }), false);\n\t\t} catch (ex) {\n\t\t\tindex.autosave = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction autosaveDelete() {\n\t\tconst index = indexGet();\n\t\tindex.autosave = null;\n\t\tindexSave(index);\n\t\treturn storage.delete('autosave');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSlots Functions.\n\t*******************************************************************************************************************/\n\tfunction slotsOk() {\n\t\treturn storage.name !== 'cookie' && _slotsUBound !== -1;\n\t}\n\n\tfunction slotsLength() {\n\t\treturn _slotsUBound + 1;\n\t}\n\n\tfunction slotsCount() {\n\t\tif (!slotsOk()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst index = indexGet();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tif (index.slots[i] !== null) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\tfunction slotsIsEmpty() {\n\t\treturn slotsCount() === 0;\n\t}\n\n\tfunction slotsHas(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length || index.slots[slot] === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction slotsGet(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn storage.get(`slot${slot}`);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tfunction slotsLoad(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn _unmarshal(storage.get(`slot${slot}`));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction slotsSave(slot, title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle: title || Story.get(State.passage).description(),\n\t\t\tdate: Date.now()\n\t\t};\n\n\t\tindex.slots[slot] = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set(`slot${slot}`, _marshal(supplemental, { type : Type.Slot }));\n\t\t} catch (ex) {\n\t\t\tindex.slots[slot] = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction slotsDelete(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tindex.slots[slot] = null;\n\t\tindexSave(index);\n\n\t\treturn storage.delete(`slot${slot}`);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDisk Import/Export Functions.\n\t*******************************************************************************************************************/\n\tfunction exportToDisk(filename, metadata, marshaledSave) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tfunction datestamp() {\n\t\t\tconst now = new Date();\n\t\t\tlet MM = now.getMonth() + 1;\n\t\t\tlet DD = now.getDate();\n\t\t\tlet hh = now.getHours();\n\t\t\tlet mm = now.getMinutes();\n\t\t\tlet ss = now.getSeconds();\n\n\t\t\tif (MM < 10) { MM = `0${MM}`; }\n\t\t\tif (DD < 10) { DD = `0${DD}`; }\n\t\t\tif (hh < 10) { hh = `0${hh}`; }\n\t\t\tif (mm < 10) { mm = `0${mm}`; }\n\t\t\tif (ss < 10) { ss = `0${ss}`; }\n\n\t\t\treturn `${now.getFullYear()}${MM}${DD}-${hh}${mm}${ss}`;\n\t\t}\n\n\t\tfunction legalizeName(str) {\n\t\t\t/*\n\t\t\t\tNOTE: The range of illegal characters consists of: C0 controls, double quote,\n\t\t\t\tnumber, dollar, percent, ampersand, single quote, asterisk, plus, comma,\n\t\t\t\tforward slash, colon, semi-colon, less-than, equals, greater-than, question,\n\t\t\t\tbackslash, caret, backquote/grave, pipe/vertical-bar, delete, C1 controls.\n\t\t\t*/\n\t\t\treturn String(str).trim()\n\t\t\t\t.replace(/[\\x00-\\x1f\"#$%&'*+,/:;<=>?\\\\^`|\\x7f-\\x9f]+/g, '') // eslint-disable-line no-control-regex\n\t\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-'); // legacy\n\t\t}\n\n\t\tconst baseName     = filename == null ? Story.domId : legalizeName(filename); // lazy equality for null\n\t\tconst saveName     = `${baseName}-${datestamp()}.save`;\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\tconst saveObj      = LZString.compressToBase64(JSON.stringify(\n\t\t\tmarshaledSave == null ? _marshal(supplemental, { type : Type.Disk }) : marshaledSave\n\t\t));\n\t\tsaveAs(new Blob([saveObj], { type : 'text/plain;charset=UTF-8' }), saveName);\n\t}\n\n\tfunction importFromDisk(event) {\n\t\tconst file   = event.target.files[0];\n\t\tconst reader = new FileReader();\n\n\t\t// Add the handler that will capture the file information once the load is finished.\n\t\tjQuery(reader).on('load', ev => {\n\t\t\tconst target = ev.currentTarget;\n\n\t\t\tif (!target.result) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet saveObj;\n\n\t\t\ttry {\n\t\t\t\tsaveObj = JSON.parse(\n\t\t\t\t\t/* legacy */ /\\.json$/i.test(file.name) || /^\\{/.test(target.result)\n\t\t\t\t\t\t? target.result\n\t\t\t\t\t\t: /* /legacy */ LZString.decompressFromBase64(target.result)\n\t\t\t\t);\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\t\t_unmarshal(saveObj);\n\t\t});\n\n\t\t// Initiate the file load.\n\t\treader.readAsText(file);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSerialization Functions.\n\t*******************************************************************************************************************/\n\tfunction serialize(metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\treturn LZString.compressToBase64(JSON.stringify(_marshal(supplemental, { type : Type.Serialize })));\n\t}\n\n\tfunction deserialize(base64Str) {\n\t\t/*\n\t\t\tNOTE: We purposefully do not attempt to catch parameter shenanigans\n\t\t\there, instead relying on `_unmarshal()` to do the heavy lifting.\n\t\t*/\n\n\t\tlet saveObj;\n\n\t\ttry {\n\t\t\tsaveObj = JSON.parse(LZString.decompressFromBase64(base64Str));\n\t\t}\n\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\tif (!_unmarshal(saveObj)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn saveObj.metadata;\n\t}\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _storageAlert() {\n\t\tif (Dialog.isOpen()) {\n\t\t\t$(document).one(':dialogclosed', () => UI.alert('Local storage full, delete saves or increase local storage'));\n\t\t} else {\n\t\t\tUI.alert('Local storage full, delete saves or increase local storage');\n\t\t}\n\t}\n\n\tfunction _appendSlots(array, num) {\n\t\tfor (let i = 0; i < num; ++i) {\n\t\t\tarray.push(null);\n\t\t}\n\n\t\treturn array;\n\t}\n\n\tfunction _savesObjUpdate(saveObj) {\n\t\tif (saveObj == null || typeof saveObj !== \"object\") { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tlet updated = false;\n\n\t\t/* eslint-disable no-param-reassign */\n\t\tif (\n\t\t\t   !saveObj.hasOwnProperty('state')\n\t\t\t|| !saveObj.state.hasOwnProperty('delta')\n\t\t\t|| !saveObj.state.hasOwnProperty('index')\n\t\t) {\n\t\t\tif (saveObj.hasOwnProperty('data')) {\n\t\t\t\tdelete saveObj.mode;\n\t\t\t\tsaveObj.state = {\n\t\t\t\t\tdelta : State.deltaEncode(saveObj.data)\n\t\t\t\t};\n\t\t\t\tdelete saveObj.data;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('delta')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\t\t\tdelete saveObj.state.history;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('index')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t}\n\n\t\t\tsaveObj.state.index = saveObj.state.delta.length - 1;\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (saveObj.state.hasOwnProperty('rseed')) {\n\t\t\tsaveObj.state.seed = saveObj.state.rseed;\n\t\t\tdelete saveObj.state.rseed;\n\n\t\t\tsaveObj.state.delta.forEach((_, i, delta) => {\n\t\t\t\tif (delta[i].hasOwnProperty('rcount')) {\n\t\t\t\t\tdelta[i].pull = delta[i].rcount;\n\t\t\t\t\tdelete delta[i].rcount;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (\n\t\t\t   saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number'\n\t\t\t||  saveObj.state.hasOwnProperty('unique')\n\t\t\t||  saveObj.state.hasOwnProperty('last')\n\t\t) {\n\t\t\tif (saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number') {\n\t\t\t\tdelete saveObj.state.expired;\n\t\t\t}\n\n\t\t\tif (saveObj.state.hasOwnProperty('unique') || saveObj.state.hasOwnProperty('last')) {\n\t\t\t\tsaveObj.state.expired = [];\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('unique')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.unique);\n\t\t\t\t\tdelete saveObj.state.unique;\n\t\t\t\t}\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('last')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.last);\n\t\t\t\t\tdelete saveObj.state.last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t/* eslint-enable no-param-reassign */\n\n\t\treturn updated;\n\t}\n\n\tfunction _marshal(supplemental, details) {\n\t\tif (DEBUG) { console.log(`[Save/_marshal(…, { type : '${details.type}' })]`); }\n\n\t\tif (supplemental != null && typeof supplemental !== 'object') { // lazy equality for null\n\t\t\tthrow new Error('supplemental parameter must be an object');\n\t\t}\n\n\t\tconst saveObj = Object.assign({}, supplemental, {\n\t\t\tid    : Config.saves.id,\n\t\t\tstate : State.marshalForSave()\n\t\t});\n\n\t\tif (Config.saves.version) {\n\t\t\tsaveObj.version = Config.saves.version;\n\t\t}\n\n\t\tif (typeof Config.saves.onSave === 'function') {\n\t\t\tConfig.saves.onSave(saveObj, details);\n\t\t}\n\n\t\t// Delta encode the state history and delete the non-encoded property.\n\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\tdelete saveObj.state.history;\n\n\t\treturn saveObj;\n\t}\n\n\tfunction _unmarshal(saveObj) {\n\t\tif (DEBUG) { console.log('[Save/_unmarshal()]'); }\n\n\t\ttry {\n\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t/* legacy */\n\t\t\t// Update saves with old/obsolete properties.\n\t\t\t_savesObjUpdate(saveObj);\n\t\t\t/* /legacy */\n\n\t\t\tif (!saveObj || !saveObj.hasOwnProperty('id') || !saveObj.hasOwnProperty('state')) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveMissingData'));\n\t\t\t}\n\n\t\t\t// Delta decode the state history and delete the encoded property.\n\t\t\tsaveObj.state.history = State.deltaDecode(saveObj.state.delta);\n\t\t\tdelete saveObj.state.delta;\n\n\t\t\tif (typeof Config.saves.onLoad === 'function') {\n\t\t\t\tConfig.saves.onLoad(saveObj);\n\t\t\t}\n\n\t\t\tif (saveObj.id !== Config.saves.id) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveIdMismatch'));\n\t\t\t}\n\n\t\t\t// Restore the state.\n\t\t\tState.unmarshalForSave(saveObj.state); // may also throw exceptions\n\n\t\t\t// Show the active moment.\n\t\t\tEngine.show();\n\t\t\t/* eslint-enable no-param-reassign */\n\t\t}\n\t\tcatch (ex) {\n\t\t\tUI.alert(`${ex.message.toUpperFirst()}.</p><p>${L10n.get('aborting')}.`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tSave Functions.\n\t\t*/\n\t\tinit  : { value : savesInit },\n\t\tclear : { value : savesObjClear },\n\t\tok    : { value : savesOk },\n\t\tindex : { value : indexGet },\n\n\t\t/*\n\t\t\tAutosave Functions.\n\t\t*/\n\t\tautosave : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok     : { value : autosaveOk },\n\t\t\t\thas    : { value : autosaveHas },\n\t\t\t\tget    : { value : autosaveGet },\n\t\t\t\tload   : { value : autosaveLoad },\n\t\t\t\tsave   : { value : autosaveSave },\n\t\t\t\tdelete : { value : autosaveDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tSlots Functions.\n\t\t*/\n\t\tslots : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok      : { value : slotsOk },\n\t\t\t\tlength  : { get : slotsLength },\n\t\t\t\tisEmpty : { value : slotsIsEmpty },\n\t\t\t\tcount   : { value : slotsCount },\n\t\t\t\thas     : { value : slotsHas },\n\t\t\t\tget     : { value : slotsGet },\n\t\t\t\tload    : { value : slotsLoad },\n\t\t\t\tsave    : { value : slotsSave },\n\t\t\t\tdelete  : { value : slotsDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tDisk Import/Export Functions.\n\t\t*/\n\t\texport : { value : exportToDisk },\n\t\timport : { value : importFromDisk },\n\n\t\t/*\n\t\t\tSerialization Functions.\n\t\t*/\n\t\tserialize   : { value : serialize },\n\t\tdeserialize : { value : deserialize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsetting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, settings:true, storage */\n\nvar Setting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Setting control types object (pseudo-enumeration).\n\tconst Types = Util.toEnum({\n\t\tHeader : 0,\n\t\tToggle : 1,\n\t\tList   : 2,\n\t\tRange  : 3\n\t});\n\n\t// Setting definition array.\n\tconst _definitions = [];\n\n\n\t/*******************************************************************************************************************\n\t\tSettings Functions.\n\t*******************************************************************************************************************/\n\tfunction settingsInit() {\n\t\tif (DEBUG) { console.log('[Setting/settingsInit()]'); }\n\n\t\t/* legacy */\n\t\t// Attempt to migrate an existing `options` store to `settings`.\n\t\tif (storage.has('options')) {\n\t\t\tconst old = storage.get('options');\n\n\t\t\tif (old !== null) {\n\t\t\t\twindow.SugarCube.settings = settings = Object.assign(settingsCreate(), old);\n\t\t\t}\n\n\t\t\tsettingsSave();\n\t\t\tstorage.delete('options');\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Load existing settings.\n\t\tsettingsLoad();\n\n\t\t// Execute `onInit` callbacks.\n\t\t_definitions.forEach(def => {\n\t\t\tif (def.hasOwnProperty('onInit')) {\n\t\t\t\tconst thisArg = {\n\t\t\t\t\tname    : def.name,\n\t\t\t\t\tvalue   : settings[def.name],\n\t\t\t\t\tdefault : def.default\n\t\t\t\t};\n\n\t\t\t\tif (def.hasOwnProperty('list')) {\n\t\t\t\t\tthisArg.list = def.list;\n\t\t\t\t}\n\n\t\t\t\tdef.onInit.call(thisArg);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction settingsCreate() {\n\t\treturn Object.create(null);\n\t}\n\n\tfunction settingsSave() {\n\t\tconst savedSettings = settingsCreate();\n\n\t\tif (Object.keys(settings).length > 0) {\n\t\t\t_definitions\n\t\t\t\t.filter(def => def.type !== Types.Header && settings[def.name] !== def.default)\n\t\t\t\t.forEach(def => savedSettings[def.name] = settings[def.name]);\n\t\t}\n\n\t\tif (Object.keys(savedSettings).length === 0) {\n\t\t\tstorage.delete('settings');\n\t\t\treturn true;\n\t\t}\n\n\t\treturn storage.set('settings', savedSettings);\n\t}\n\n\tfunction settingsLoad() {\n\t\tconst defaultSettings = settingsCreate();\n\t\tconst loadedSettings  = storage.get('settings') || settingsCreate();\n\n\t\t// Load the defaults.\n\t\t_definitions\n\t\t\t.filter(def => def.type !== Types.Header)\n\t\t\t.forEach(def => defaultSettings[def.name] = def.default);\n\n\t\t// Assign to the `settings` object while overwriting the defaults with the loaded settings.\n\t\twindow.SugarCube.settings = settings = Object.assign(defaultSettings, loadedSettings);\n\t}\n\n\tfunction settingsClear() {\n\t\twindow.SugarCube.settings = settings = settingsCreate();\n\t\tstorage.delete('settings');\n\t\treturn true;\n\t}\n\n\tfunction settingsReset(name) {\n\t\tif (arguments.length === 0) {\n\t\t\tsettingsClear();\n\t\t\tsettingsLoad();\n\t\t}\n\t\telse {\n\t\t\tif (name == null || !definitionsHas(name)) { // lazy equality for null\n\t\t\t\tthrow new Error(`nonexistent setting \"${name}\"`);\n\t\t\t}\n\n\t\t\tconst def = definitionsGet(name);\n\n\t\t\tif (def.type !== Types.Header) {\n\t\t\t\tsettings[name] = def.default;\n\t\t\t}\n\t\t}\n\n\t\treturn settingsSave();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDefinitions Functions.\n\t*******************************************************************************************************************/\n\tfunction definitionsForEach(callback, thisArg) {\n\t\t_definitions.forEach(callback, thisArg);\n\t}\n\n\tfunction definitionsAdd(type, name, def) {\n\t\tif (arguments.length < 3) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('type'); }\n\t\t\tif (arguments.length < 2) { errors.push('name'); }\n\t\t\tif (arguments.length < 3) { errors.push('definition'); }\n\t\t\tthrow new Error(`missing parameters, no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tif (typeof def !== 'object') {\n\t\t\tthrow new TypeError('definition parameter must be an object');\n\t\t}\n\n\t\tif (definitionsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing setting \"${name}\"`);\n\t\t}\n\n\t\t/*\n\t\t\tDefinition object properties and types:\n\t\t\t\ttype      →  (all)   → Setting.Types\n\t\t\t\tname      →  (all)   → string\n\t\t\t\tlabel     →  (all)   → string\n\t\t\t\tdesc      →  (all)   → string\n\t\t\t\tdefault\n\t\t\t\t\t(if defined)\n \t\t\t\t\t\t  →  Toggle  → boolean\n\t\t\t\t\t\t  →  List    → Array\n\t\t\t\t\t\t  →  Range   → number\n\t\t\t\t\t(if undefined)\n\t\t\t\t\t\t  →  Toggle  → false\n\t\t\t\t\t\t  →  List    → list[0]\n\t\t\t\t\t\t  →  Range   → max\n\t\t\t\tlist      →  List    → Array\n\t\t\t\tmin       →  Range   → number\n\t\t\t\tmax       →  Range   → number\n\t\t\t\tstep      →  Range   → number\n\t\t\t\tonInit    →  (all)   → function\n\t\t\t\tonChange  →  (all)   → function\n\t\t*/\n\t\tconst definition = {\n\t\t\ttype,\n\t\t\tname,\n\t\t\tlabel : typeof def.label === 'string' ? def.label.trim() : ''\n\t\t};\n\n\t\tif (typeof def.desc === 'string') {\n\t\t\tconst desc = def.desc.trim();\n\n\t\t\tif (desc !== '') {\n\t\t\t\tdefinition.desc = desc;\n\t\t\t}\n\t\t}\n\n\t\tswitch (type) {\n\t\tcase Types.Header:\n\t\t\tbreak;\n\n\t\tcase Types.Toggle:\n\t\t\tdefinition.default = !!def.default;\n\t\t\tbreak;\n\n\t\tcase Types.List:\n\t\t\tif (!def.hasOwnProperty('list')) {\n\t\t\t\tthrow new Error('no list specified');\n\t\t\t}\n\t\t\telse if (!Array.isArray(def.list)) {\n\t\t\t\tthrow new TypeError('list must be an array');\n\t\t\t}\n\t\t\telse if (def.list.length === 0) {\n\t\t\t\tthrow new Error('list must not be empty');\n\t\t\t}\n\n\t\t\tdefinition.list = Object.freeze(def.list);\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.list[0];\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst defaultIndex = def.list.indexOf(def.default);\n\n\t\t\t\tif (defaultIndex === -1) {\n\t\t\t\t\tthrow new Error('list does not contain default');\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.list[defaultIndex];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase Types.Range:\n\t\t\tif (!def.hasOwnProperty('min')) {\n\t\t\t\tthrow new Error('no min specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.min !== 'number'\n\t\t\t\t|| Number.isNaN(def.min)\n\t\t\t\t|| !Number.isFinite(def.min)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('min must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('max')) {\n\t\t\t\tthrow new Error('no max specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.max !== 'number'\n\t\t\t\t|| Number.isNaN(def.max)\n\t\t\t\t|| !Number.isFinite(def.max)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('max must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('step')) {\n\t\t\t\tthrow new Error('no step specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.step !== 'number'\n\t\t\t\t|| Number.isNaN(def.step)\n\t\t\t\t|| !Number.isFinite(def.step)\n\t\t\t\t|| def.step <= 0\n\t\t\t) {\n\t\t\t\tthrow new TypeError('step must be a finite number greater than zero');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Determine how many fractional digits we need to be concerned with based on the step value.\n\t\t\t\tconst fracDigits = (() => {\n\t\t\t\t\tconst str = String(def.step);\n\t\t\t\t\tconst pos = str.lastIndexOf('.');\n\t\t\t\t\treturn pos === -1 ? 0 : str.length - pos - 1;\n\t\t\t\t})();\n\n\t\t\t\t// Set up a function to validate a given value against the step value.\n\t\t\t\tfunction stepValidate(value) {\n\t\t\t\t\tif (fracDigits > 0) {\n\t\t\t\t\t\tconst ma = Number(`${def.min}e${fracDigits}`);\n\t\t\t\t\t\tconst sa = Number(`${def.step}e${fracDigits}`);\n\t\t\t\t\t\tconst va = Number(`${value}e${fracDigits}`) - ma;\n\t\t\t\t\t\treturn Number(`${va - va % sa + ma}e-${fracDigits}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst va = value - def.min;\n\t\t\t\t\treturn va - va % def.step + def.min;\n\t\t\t\t}\n\n\t\t\t\t// Sanity check the max value against the step value.\n\t\t\t\tif (stepValidate(def.max) !== def.max) {\n\t\t\t\t\tthrow new RangeError(`max (${def.max}) is not a multiple of the step (${def.step}) plus the min (${def.min})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefinition.max = def.max;\n\t\t\tdefinition.min = def.min;\n\t\t\tdefinition.step = def.step;\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.max;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (\n\t\t\t\t\t   typeof def.default !== 'number'\n\t\t\t\t\t|| Number.isNaN(def.default)\n\t\t\t\t\t|| !Number.isFinite(def.default)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError('default must be a finite number');\n\t\t\t\t}\n\t\t\t\telse if (def.default < def.min) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is less than min (${def.min})`);\n\t\t\t\t}\n\t\t\t\telse if (def.default > def.max) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is greater than max (${def.max})`);\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.default;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`unknown Setting type: ${type}`);\n\t\t}\n\n\t\tif (typeof def.onInit === 'function') {\n\t\t\tdefinition.onInit = Object.freeze(def.onInit);\n\t\t}\n\n\t\tif (typeof def.onChange === 'function') {\n\t\t\tdefinition.onChange = Object.freeze(def.onChange);\n\t\t}\n\n\t\t_definitions.push(Object.freeze(definition));\n\t}\n\n\tfunction definitionsAddHeader(name, desc) {\n\t\tdefinitionsAdd(Types.Header, name, { desc });\n\t}\n\n\tfunction definitionsAddToggle(...args) {\n\t\tdefinitionsAdd(Types.Toggle, ...args);\n\t}\n\n\tfunction definitionsAddList(...args) {\n\t\tdefinitionsAdd(Types.List, ...args);\n\t}\n\n\tfunction definitionsAddRange(...args) {\n\t\tdefinitionsAdd(Types.Range, ...args);\n\t}\n\n\tfunction definitionsIsEmpty() {\n\t\treturn _definitions.length === 0;\n\t}\n\n\tfunction definitionsHas(name) {\n\t\treturn _definitions.some(definition => definition.name === name);\n\t}\n\n\tfunction definitionsGet(name) {\n\t\treturn _definitions.find(definition => definition.name === name);\n\t}\n\n\tfunction definitionsDelete(name) {\n\t\tif (definitionsHas(name)) {\n\t\t\tdelete settings[name];\n\t\t}\n\n\t\tfor (let i = 0; i < _definitions.length; ++i) {\n\t\t\tif (_definitions[i].name === name) {\n\t\t\t\t_definitions.splice(i, 1);\n\t\t\t\tdefinitionsDelete(name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tEnumerations.\n\t\t*/\n\t\tTypes : { value : Types },\n\n\t\t/*\n\t\t\tSettings Functions.\n\t\t*/\n\t\tinit   : { value : settingsInit },\n\t\tcreate : { value : settingsCreate },\n\t\tsave   : { value : settingsSave },\n\t\tload   : { value : settingsLoad },\n\t\tclear  : { value : settingsClear },\n\t\treset  : { value : settingsReset },\n\n\t\t/*\n\t\t\tDefinitions Functions.\n\t\t*/\n\t\tforEach   : { value : definitionsForEach },\n\t\tadd       : { value : definitionsAdd },\n\t\taddHeader : { value : definitionsAddHeader },\n\t\taddToggle : { value : definitionsAddToggle },\n\t\taddList   : { value : definitionsAddList },\n\t\taddRange  : { value : definitionsAddRange },\n\t\tisEmpty   : { value : definitionsIsEmpty },\n\t\thas       : { value : definitionsHas },\n\t\tget       : { value : definitionsGet },\n\t\tdelete    : { value : definitionsDelete }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstory.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Alert, Config, Passage, Scripting, StyleWrapper, Util, Wikifier */\n\nvar Story = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Map of normal passages.\n\tconst _passages = {};\n\n\t// List of script passages.\n\tconst _scripts = [];\n\n\t// List of style passages.\n\tconst _styles = [];\n\n\t// List of widget passages.\n\tconst _widgets = [];\n\n\t// Story title.\n\tlet _title = '';\n\n\t// Story IFID.\n\tlet _ifId = '';\n\n\t// DOM-compatible ID.\n\tlet _domId = '';\n\n\n\t/*******************************************************************************************************************\n\t\tStory Functions.\n\t*******************************************************************************************************************/\n\tfunction storyLoad() {\n\t\tif (DEBUG) { console.log('[Story/storyLoad()]'); }\n\n\t\tconst validationCodeTags = [\n\t\t\t'widget'\n\t\t];\n\t\tconst validationNoCodeTagPassages = [\n\t\t\t'PassageDone',\n\t\t\t'PassageFooter',\n\t\t\t'PassageHeader',\n\t\t\t'PassageReady',\n\t\t\t'StoryAuthor',\n\t\t\t'StoryBanner',\n\t\t\t'StoryCaption',\n\t\t\t'StoryInit',\n\t\t\t'StoryMenu',\n\t\t\t'StoryShare',\n\t\t\t'StorySubtitle'\n\t\t];\n\n\t\tfunction validateStartingPassage(passage) {\n\t\t\tif (passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`starting passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\tfunction validateSpecialPassages(passage) {\n\t\t\tif (validationNoCodeTagPassages.includes(passage.title) && passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`special passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\t// For Twine 1.\n\t\tif (TWINE1) {\n\t\t\t/*\n\t\t\t\tAdditional Twine 1 validation setup.\n\t\t\t*/\n\t\t\tvalidationCodeTags.unshift('script', 'stylesheet');\n\t\t\tvalidationNoCodeTagPassages.push('StoryTitle');\n\n\t\t\tfunction validateTwine1CodePassages(passage) {\n\t\t\t\tconst codeTags  = [...validationCodeTags];\n\t\t\t\tconst foundTags = [];\n\n\t\t\t\tpassage.tags.forEach(tag => {\n\t\t\t\t\tif (codeTags.includes(tag)) {\n\t\t\t\t\t\tfoundTags.push(...codeTags.delete(tag));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (foundTags.length > 1) {\n\t\t\t\t\tthrow new Error(`code passage \"${passage.title}\" contains multiple code tags; invalid: \"${foundTags.sort().join('\", \"')}\"`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = (() => {\n\t\t\t\t/*\n\t\t\t\t\tHandle the Twine 1.4+ Test Play From Here feature.\n\n\t\t\t\t\tWARNING: Do not remove the `String()` wrapper from or change the quote\n\t\t\t\t\tstyle of the `\"START_AT\"` replacement target.  The former is there to\n\t\t\t\t\tkeep UglifyJS from pruning the code into oblivion—i.e. minifying the\n\t\t\t\t\tcode into something broken.  The latter is there because the Twine 1\n\t\t\t\t\tpattern that matches it depends upon the double quotes.\n\n\t\t\t\t*/\n\t\t\t\tconst testPlay = String(\"START_AT\"); // eslint-disable-line quotes\n\n\t\t\t\tif (testPlay !== '') {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tTest play; starting passage: \"${testPlay}\"`); }\n\n\t\t\t\t\tConfig.debug = true;\n\t\t\t\t\treturn testPlay;\n\t\t\t\t}\n\n\t\t\t\t// In the absence of a `testPlay` value, return 'Start'.\n\t\t\t\treturn 'Start';\n\t\t\t})();\n\n\t\t\t/*\n\t\t\t\tProcess the passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\tjQuery('#store-area')\n\t\t\t\t.children(':not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst passage = new Passage($this.attr('tiddler'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (passage.title === Config.passages.start) {\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('stylesheet')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_styles.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('script')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_scripts.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tSet the story title or throw an exception.\n\t\t\t*/\n\t\t\tif (_passages.hasOwnProperty('StoryTitle')) {\n\t\t\t\tconst buf = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(buf, _passages.StoryTitle.processText().trim());\n\t\t\t\t_storySetTitle(buf.textContent);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('cannot find the \"StoryTitle\" special passage');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\n\t\t// For Twine 2.\n\t\telse {\n\t\t\tconst $storydata = jQuery('tw-storydata');\n\t\t\tconst startNode  = $storydata.attr('startnode') || '';\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = null; // no default in Twine 2\n\n\t\t\t/*\n\t\t\t\tProcess story options.\n\n\t\t\t\tNOTE: Currently, the only option of interest is 'debug', so we\n\t\t\t\tsimply use a regular expression to check for it.\n\t\t\t*/\n\t\t\tConfig.debug = /\\bdebug\\b/.test($storydata.attr('options'));\n\n\t\t\t/*\n\t\t\t\tProcess stylesheet passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('style') // alternatively: '[type=\"text/twine-css\"]' or '#twine-user-stylesheet'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_styles.push(new Passage(`tw-user-style-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess script passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('script') // alternatively: '[type=\"text/twine-javascript\"]' or '#twine-user-script'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_scripts.push(new Passage(`tw-user-script-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess normal passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('tw-passagedata:not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst pid     = $this.attr('pid') || '';\n\t\t\t\t\tconst passage = new Passage($this.attr('name'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (pid === startNode && startNode !== '') {\n\t\t\t\t\t\tConfig.passages.start = passage.title;\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tGet the story IFID.\n\t\t\t*/\n\t\t\t_ifId = $storydata.attr('ifid');\n\n\t\t\t/*\n\t\t\t\tSet the story title.\n\n\t\t\t\tFIXME: Maybe `$storydata.attr('name')` should be used instead of `'{{STORY_NAME}}'`?\n\t\t\t*/\n\t\t\t// _storySetTitle($storydata.attr('name'));\n\t\t\t_storySetTitle('{{STORY_NAME}}');\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\t}\n\n\tfunction storyInit() {\n\t\tif (DEBUG) { console.log('[Story/storyInit()]'); }\n\n\t\t/*\n\t\t\tAdd the story styles.\n\t\t*/\n\t\t(() => {\n\t\t\tconst storyStyle = document.createElement('style');\n\n\t\t\tnew StyleWrapper(storyStyle)\n\t\t\t\t.add(_styles.map(style => style.text.trim()).join('\\n'));\n\n\t\t\tjQuery(storyStyle)\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-story',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t});\n\t\t})();\n\n\t\t/*\n\t\t\tEvaluate the story scripts.\n\t\t*/\n\t\tfor (let i = 0; i < _scripts.length; ++i) {\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(_scripts[i].text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_scripts[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tProcess the story widgets.\n\t\t*/\n\t\tfor (let i = 0; i < _widgets.length; ++i) {\n\t\t\ttry {\n\t\t\t\tWikifier.wikifyEval(_widgets[i].processText());\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_widgets[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction _storySetTitle(rawTitle) {\n\t\tif (rawTitle == null) { // lazy equality for null\n\t\t\tthrow new Error('story title must not be null or undefined');\n\t\t}\n\n\t\tconst title = Util.unescape(String(rawTitle)).trim();\n\n\t\tif (title === '') { // lazy equality for null\n\t\t\tthrow new Error('story title must not be empty or consist solely of whitespace');\n\t\t}\n\n\t\tdocument.title = _title = title;\n\n\t\t// TODO: In v3 the `_domId` should be created from a combination of the\n\t\t// `_title` slug and the IFID, if available, to avoid collisions between\n\t\t// stories whose titles generate identical slugs.\n\t\t_domId = Util.slugify(_title);\n\n\t\t// [v2] Protect the `_domId` against being an empty string.\n\t\t//\n\t\t// If `_domId` is empty, attempt a failover.\n\t\tif (_domId === '') {\n\t\t\t// If `_ifId` is not empty, then use it.\n\t\t\tif (_ifId !== '') {\n\t\t\t\t_domId = _ifId;\n\t\t\t}\n\n\t\t\t// Elsewise generate a string from the `_title`'s code points (in hexadecimal).\n\t\t\telse {\n\t\t\t\tfor (let i = 0, len = _title.length; i < len; ++i) {\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(_title, i);\n\t\t\t\t\t_domId += char.codePointAt(0).toString(16);\n\t\t\t\t\ti += end - start;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction storyTitle() {\n\t\treturn _title;\n\t}\n\n\tfunction storyDomId() {\n\t\treturn _domId;\n\t}\n\n\tfunction storyIfId() {\n\t\treturn _ifId;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Functions.\n\t*******************************************************************************************************************/\n\tfunction passagesAdd(passage) {\n\t\tif (!(passage instanceof Passage)) {\n\t\t\tthrow new TypeError('Story.add passage parameter must be an instance of Passage');\n\t\t}\n\n\t\tconst title = passage.title;\n\n\t\tif (!_passages.hasOwnProperty(title)) {\n\t\t\t_passages[title] = passage;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction passagesHas(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t\treturn _passages.hasOwnProperty(String(title));\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.has title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGet(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t/* eslint-disable indent */\n\t\t\t{\n\t\t\t\tconst id = String(title);\n\t\t\t\treturn _passages.hasOwnProperty(id) ? _passages[id] : new Passage(id || '(unknown)');\n\t\t\t}\n\t\t/* eslint-enable indent */\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.get title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGetAllRegular() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Object.assign({}, _passages));\n\t}\n\n\tfunction passagesGetAllScript() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_scripts));\n\t}\n\n\tfunction passagesGetAllStylesheet() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_styles));\n\t}\n\n\tfunction passagesGetAllWidget() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_widgets));\n\t}\n\n\tfunction passagesLookup(key, value  /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\t// Objects (sans `null`).\n\t\t\tif (typeof passage[key] === 'object' && passage[key] !== null) {\n\t\t\t\t// The only object type currently supported is `Array`, since the\n\t\t\t\t// non-method `Passage` object properties currently yield only either\n\t\t\t\t// primitives or arrays.\n\t\t\t\tif (passage[key] instanceof Array && passage[key].some(m => Util.sameValueZero(m, value))) {\n\t\t\t\t\tresults.push(passage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// All other types (incl. `null`).\n\t\t\telse if (Util.sameValueZero(passage[key], value)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\tfunction passagesLookupWith(predicate /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tif (typeof predicate !== 'function') {\n\t\t\tthrow new TypeError('Story.lookupWith predicate parameter must be a function');\n\t\t}\n\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\tif (predicate(passage)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Story Functions.\n\t\tload  : { value : storyLoad },\n\t\tinit  : { value : storyInit },\n\t\ttitle : { get : storyTitle },\n\t\tdomId : { get : storyDomId },\n\t\tifId  : { get : storyIfId },\n\n\t\t// Passage Functions.\n\t\tadd              : { value : passagesAdd },\n\t\thas              : { value : passagesHas },\n\t\tget              : { value : passagesGet },\n\t\tgetAllRegular    : { value : passagesGetAllRegular },\n\t\tgetAllScript     : { value : passagesGetAllScript },\n\t\tgetAllStylesheet : { value : passagesGetAllStylesheet },\n\t\tgetAllWidget     : { value : passagesGetAllWidget },\n\t\tlookup           : { value : passagesLookup },\n\t\tlookupWith       : { value : passagesLookupWith }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tui.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, Has, L10n, Save, Setting, State, Story, Util, Wikifier, Config, errorPrologRegExp,\n\t       settings\n*/\n\nvar UI = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Core.\n\t*******************************************************************************************************************/\n\tfunction uiAssembleLinkList(passage, listEl) {\n\t\tlet list = listEl;\n\n\t\t// Cache the values of `Config.debug` and `Config.cleanupWikifierOutput`,\n\t\t// then disable them during this method's run.\n\t\tconst debugState = Config.debug;\n\t\tconst cleanState = Config.cleanupWikifierOutput;\n\t\tConfig.debug = false;\n\t\tConfig.cleanupWikifierOutput = false;\n\n\t\ttry {\n\t\t\tif (list == null) { // lazy equality for null\n\t\t\t\tlist = document.createElement('ul');\n\t\t\t}\n\n\t\t\t// Wikify the content of the given source passage into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, Story.get(passage).processText().trim());\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\n\t\t\twhile (frag.hasChildNodes()) {\n\t\t\t\tconst node = frag.firstChild;\n\n\t\t\t\t// Create list items for <a>-element nodes.\n\t\t\t\tif (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toUpperCase() === 'A') {\n\t\t\t\t\tconst li = document.createElement('li');\n\t\t\t\t\tlist.appendChild(li);\n\t\t\t\t\tli.appendChild(node);\n\t\t\t\t}\n\n\t\t\t\t// Discard non-<a>-element nodes.\n\t\t\t\telse {\n\t\t\t\t\tfrag.removeChild(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t// Restore the `Config` settings to their original values.\n\t\t\tConfig.cleanupWikifierOutput = cleanState;\n\t\t\tConfig.debug = debugState;\n\t\t}\n\n\t\treturn list;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Built-ins.\n\t*******************************************************************************************************************/\n\tfunction uiOpenAlert(message, /* options, closeFn */ ...args) {\n\t\tjQuery(Dialog.setup(L10n.get('alertTitle'), 'alert'))\n\t\t\t.append(\n\t\t\t\t  `<p>${message}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"alert-ok\" class=\"ui-close\">${L10n.get(['alertOk', 'ok'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t);\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenJumpto(/* options, closeFn */ ...args) {\n\t\tuiBuildJumpto();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenRestart(/* options, closeFn */ ...args) {\n\t\tuiBuildRestart();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSaves(/* options, closeFn */ ...args) {\n\t\tuiBuildSaves();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSettings(/* options, closeFn */ ...args) {\n\t\tuiBuildSettings();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenShare(/* options, closeFn */ ...args) {\n\t\tuiBuildShare();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiBuildAutoload() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildAutoload()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('autoloadTitle'), 'autoload'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('autoloadPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"autoload-ok\" class=\"ui-close\">${L10n.get(['autoloadOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"autoload-cancel\" class=\"ui-close\">${L10n.get(['autoloadCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t// Add an additional delegated click handler for the `.ui-close` elements to handle autoloading.\n\t\tjQuery(document).one('click.autoload', '.ui-close', ev => {\n\t\t\tconst isAutoloadOk = ev.target.id === 'autoload-ok';\n\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\tif (!isAutoloadOk || !Save.autosave.load()) {\n\t\t\t\t\tEngine.play(Config.passages.start);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildJumpto() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildJumpto()]'); }\n\n\t\tconst list = document.createElement('ul');\n\n\t\tjQuery(Dialog.setup(L10n.get('jumptoTitle'), 'jumpto list'))\n\t\t\t.append(list);\n\n\t\tconst expired = State.expired.length;\n\n\t\tfor (let i = State.size - 1; i >= 0; --i) {\n\t\t\tif (i === State.activeIndex) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst passage = Story.get(State.history[i].title);\n\n\t\t\tif (passage && passage.tags.includes('bookmark')) {\n\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t.append(\n\t\t\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t\t\t.ariaClick({ one : true }, (function (idx) {\n\t\t\t\t\t\t\t\treturn () => jQuery(document).one(':dialogclosed', () => Engine.goTo(idx));\n\t\t\t\t\t\t\t})(i))\n\t\t\t\t\t\t\t.addClass('ui-close')\n\t\t\t\t\t\t\t.text(`${L10n.get('jumptoTurn')} ${expired + i + 1}: ${passage.description()}`)\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo(list);\n\t\t\t}\n\t\t}\n\n\t\tif (!list.hasChildNodes()) {\n\t\t\tjQuery(list).append(`<li><a><em>${L10n.get('jumptoUnavailable')}</em></a></li>`);\n\t\t}\n\t}\n\n\tfunction uiBuildRestart() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildRestart()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('restartTitle'), 'restart'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('restartPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"restart-ok\">${L10n.get(['restartOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"restart-cancel\" class=\"ui-close\">${L10n.get(['restartCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.find('#restart-ok')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#restart-ok' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `Engine.restart()` happens after the dialog\n\t\t\t\thas fully closed.  If we did not, then a race condition could occur, causing display\n\t\t\t\tshenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => Engine.restart());\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildSaves() {\n\t\tconst savesAllowed = typeof Config.saves.isAllowed !== 'function' || Config.saves.isAllowed();\n\n\t\tfunction createActionItem(bId, bClass, bText, bAction) {\n\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t.attr('id', `saves-${bId}`)\n\t\t\t\t.html(bText);\n\n\t\t\tif (bClass) {\n\t\t\t\t$btn.addClass(bClass);\n\t\t\t}\n\n\t\t\tif (bAction) {\n\t\t\t\t$btn.ariaClick(bAction);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('li'))\n\t\t\t\t.append($btn);\n\t\t}\n\n\t\tfunction createSaveList() {\n\t\t\tfunction createButton(bId, bClass, bText, bSlot, bAction) {\n\t\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t\t.attr('id', `saves-${bId}-${bSlot}`)\n\t\t\t\t\t.addClass(bId)\n\t\t\t\t\t.html(bText);\n\n\t\t\t\tif (bClass) {\n\t\t\t\t\t$btn.addClass(bClass);\n\t\t\t\t}\n\n\t\t\t\tif (bAction) {\n\t\t\t\t\tif (bSlot === 'auto') {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelAuto')}`\n\t\t\t\t\t\t}, () => bAction());\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelSlot')} ${bSlot + 1}`\n\t\t\t\t\t\t}, () => bAction(bSlot));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t\t}\n\n\t\t\t\treturn $btn;\n\t\t\t}\n\n\t\t\tconst index  = Save.index();\n\t\t\tconst $tbody = jQuery(document.createElement('tbody'));\n\n\t\t\tif (Save.autosave.ok()) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\tjQuery(document.createElement('b'))\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttitle        : L10n.get('savesLabelAuto'),\n\t\t\t\t\t\t'aria-label' : L10n.get('savesLabelAuto')\n\t\t\t\t\t})\n\t\t\t\t\t.text('A') // '\\u25C6' Black Diamond\n\t\t\t\t\t.appendTo($tdSlot);\n\n\t\t\t\tif (index.autosave) {\n\t\t\t\t\t// Add the load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), 'auto', () => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.autosave.load());\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.autosave.title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.autosave.date\n\t\t\t\t\t\t\t\t? `${new Date(index.autosave.date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto', () => {\n\t\t\t\t\t\t\tSave.autosave.delete();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the disabled load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', null, L10n.get('savesLabelLoad'), 'auto')\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto')\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = index.slots.length; i < iend; ++i) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\t$tdSlot.append(document.createTextNode(i + 1));\n\n\t\t\t\tif (index.slots[i]) {\n\t\t\t\t\t// Add the save & load buttons.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, Save.slots.save),\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), i, slot => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.slots.load(slot));\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.slots[i].title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.slots[i].date\n\t\t\t\t\t\t\t\t? `${new Date(index.slots[i].date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i, slot => {\n\t\t\t\t\t\t\tSave.slots.delete(slot);\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the save button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, savesAllowed ? Save.slots.save : null)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i)\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('table'))\n\t\t\t\t.attr('id', 'saves-list')\n\t\t\t\t.append($tbody);\n\t\t}\n\n\t\tif (DEBUG) { console.log('[UI/uiBuildSaves()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('savesTitle'), 'saves'));\n\t\tconst savesOk     = Save.ok();\n\n\t\t// Add saves list.\n\t\tif (savesOk) {\n\t\t\t$dialogBody.append(createSaveList());\n\t\t}\n\n\t\t// Add button bar items (export, import, and clear).\n\t\tif (savesOk || Has.fileAPI) {\n\t\t\tconst $btnBar = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass('buttons')\n\t\t\t\t.appendTo($dialogBody);\n\n\t\t\tif (Has.fileAPI) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'export',\n\t\t\t\t\t'ui-close',\n\t\t\t\t\tL10n.get('savesLabelExport'),\n\t\t\t\t\tsavesAllowed ? () => Save.export() : null\n\t\t\t\t));\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'import',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelImport'),\n\t\t\t\t\t() => $dialogBody.find('#saves-import-file').trigger('click')\n\t\t\t\t));\n\n\t\t\t\t// Add the hidden `input[type=file]` element which will be triggered by the `#saves-import` button.\n\t\t\t\tjQuery(document.createElement('input'))\n\t\t\t\t\t.css({\n\t\t\t\t\t\tdisplay    : 'block',\n\t\t\t\t\t\tvisibility : 'hidden',\n\t\t\t\t\t\tposition   : 'fixed',\n\t\t\t\t\t\tleft       : '-9999px',\n\t\t\t\t\t\ttop        : '-9999px',\n\t\t\t\t\t\twidth      : '1px',\n\t\t\t\t\t\theight     : '1px'\n\t\t\t\t\t})\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype          : 'file',\n\t\t\t\t\t\tid            : 'saves-import-file',\n\t\t\t\t\t\ttabindex      : -1,\n\t\t\t\t\t\t'aria-hidden' : true\n\t\t\t\t\t})\n\t\t\t\t\t.on('change', ev => {\n\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.import(ev));\n\t\t\t\t\t\tDialog.close();\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t}\n\n\t\t\tif (savesOk) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'clear',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelClear'),\n\t\t\t\t\tSave.autosave.has() || !Save.slots.isEmpty()\n\t\t\t\t\t\t? () => {\n\t\t\t\t\t\t\tSave.clear();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null\n\t\t\t\t));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tuiOpenAlert(L10n.get('savesIncapable'));\n\t\treturn false;\n\t}\n\n\tfunction uiBuildSettings() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildSettings()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('settingsTitle'), 'settings'));\n\n\t\tSetting.forEach(control => {\n\t\t\tif (control.type === Setting.Types.Header) {\n\t\t\t\tconst name     = control.name;\n\t\t\t\tconst id       = Util.slugify(name);\n\t\t\t\tconst $header  = jQuery(document.createElement('div'));\n\t\t\t\tconst $heading = jQuery(document.createElement('h2'));\n\n\t\t\t\t$header\n\t\t\t\t\t.attr('id', `header-body-${id}`)\n\t\t\t\t\t.append($heading)\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t\t$heading\n\t\t\t\t\t.attr('id', `header-heading-${id}`)\n\t\t\t\t\t.wiki(name);\n\n\t\t\t\t// Set up the description, if any.\n\t\t\t\tif (control.desc) {\n\t\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t\t.attr('id', `header-desc-${id}`)\n\t\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t\t.appendTo($header);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst name        = control.name;\n\t\t\tconst id          = Util.slugify(name);\n\t\t\tconst $setting    = jQuery(document.createElement('div'));\n\t\t\tconst $label      = jQuery(document.createElement('label'));\n\t\t\tconst $controlBox = jQuery(document.createElement('div'));\n\t\t\tlet $control;\n\n\t\t\t// Set up the label+control wrapper.\n\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t.append($label)\n\t\t\t\t.append($controlBox)\n\t\t\t\t.appendTo($setting);\n\n\t\t\t// Set up the description, if any.\n\t\t\tif (control.desc) {\n\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t.attr('id', `setting-desc-${id}`)\n\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t.appendTo($setting);\n\t\t\t}\n\n\t\t\t// Set up the label.\n\t\t\t$label\n\t\t\t\t.attr({\n\t\t\t\t\tid  : `setting-label-${id}`,\n\t\t\t\t\tfor : `setting-control-${id}` // must be in sync with $control's ID (see below)\n\t\t\t\t})\n\t\t\t\t.wiki(control.label);\n\n\t\t\t// Set up the control.\n\t\t\tif (settings[name] == null) { // lazy equality for null\n\t\t\t\tsettings[name] = control.default;\n\t\t\t}\n\n\t\t\tswitch (control.type) {\n\t\t\tcase Setting.Types.Toggle:\n\t\t\t\t$control = jQuery(document.createElement('button'));\n\n\t\t\t\tif (settings[name]) {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t}\n\n\t\t\t\t$control.ariaClick(function () {\n\t\t\t\t\tif (settings[name]) {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.removeClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t\t\tsettings[name] = false;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t\t\tsettings[name] = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tSetting.save();\n\n\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\tdefault : control.default\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.List:\n\t\t\t\t$control = jQuery(document.createElement('select'));\n\n\t\t\t\tfor (let i = 0, iend = control.list.length; i < iend; ++i) {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(control.list[i])\n\t\t\t\t\t\t.appendTo($control);\n\t\t\t\t}\n\n\t\t\t\t$control\n\t\t\t\t\t.val(control.list.indexOf(settings[name]))\n\t\t\t\t\t.attr('tabindex', 0)\n\t\t\t\t\t.on('change', function () {\n\t\t\t\t\t\tsettings[name] = control.list[Number(this.value)];\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tlist    : control.list\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.Range:\n\t\t\t\t$control = jQuery(document.createElement('input'));\n\n\t\t\t\t// NOTE: Setting the value with `<jQuery>.val()` can cause odd behavior\n\t\t\t\t// in Edge if it's called before the type is set, so we use the `value`\n\t\t\t\t// content attribute here to dodge the entire issue.\n\t\t\t\t$control\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype     : 'range',\n\t\t\t\t\t\tmin      : control.min,\n\t\t\t\t\t\tmax      : control.max,\n\t\t\t\t\t\tstep     : control.step,\n\t\t\t\t\t\tvalue    : settings[name],\n\t\t\t\t\t\ttabindex : 0\n\t\t\t\t\t})\n\t\t\t\t\t.on('change input', function () {\n\t\t\t\t\t\tsettings[name] = Number(this.value);\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tmin     : control.min,\n\t\t\t\t\t\t\t\tmax     : control.max,\n\t\t\t\t\t\t\t\tstep    : control.step\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.on('keypress', ev => {\n\t\t\t\t\t\tif (ev.which === 13) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$control.trigger('change');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$control\n\t\t\t\t.attr('id', `setting-control-${id}`)\n\t\t\t\t.appendTo($controlBox);\n\n\t\t\t$setting\n\t\t\t\t.attr('id', `setting-body-${id}`)\n\t\t\t\t.appendTo($dialogBody);\n\t\t});\n\n\t\t// Add the button bar.\n\t\t$dialogBody\n\t\t\t.append(\n\t\t\t\t  '<ul class=\"buttons\">'\n\t\t\t\t+     `<li><button id=\"settings-ok\" class=\"ui-close\">${L10n.get(['settingsOk', 'ok'])}</button></li>`\n\t\t\t\t+     `<li><button id=\"settings-reset\">${L10n.get('settingsReset')}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t)\n\t\t\t.find('#settings-reset')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#settings-reset' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `window.location.reload()` happens after the\n\t\t\t\tdialog has fully closed.  If we did not, then a race condition could occur, causing\n\t\t\t\tdisplay shenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\t\tSetting.reset();\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t});\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildShare() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildShare()]'); }\n\n\t\ttry {\n\t\t\tjQuery(Dialog.setup(L10n.get('shareTitle'), 'share list'))\n\t\t\t\t.append(uiAssembleLinkList('StoryShare'));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tconsole.error(ex);\n\t\t\tAlert.error('StoryShare', ex.message);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tUI Functions, Core.\n\t\t*/\n\t\tassembleLinkList : { value : uiAssembleLinkList },\n\n\t\t/*\n\t\t\tUI Functions, Built-ins.\n\t\t*/\n\t\talert         : { value : uiOpenAlert },\n\t\tjumpto        : { value : uiOpenJumpto },\n\t\trestart       : { value : uiOpenRestart },\n\t\tsaves         : { value : uiOpenSaves },\n\t\tsettings      : { value : uiOpenSettings },\n\t\tshare         : { value : uiOpenShare },\n\t\tbuildAutoload : { value : uiBuildAutoload },\n\t\tbuildJumpto   : { value : uiBuildJumpto },\n\t\tbuildRestart  : { value : uiBuildRestart },\n\t\tbuildSaves    : { value : uiBuildSaves },\n\t\tbuildSettings : { value : uiBuildSettings },\n\t\tbuildShare    : { value : uiBuildShare },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\t// `UIBar` methods.\n\t\t/* global UIBar */\n\t\tstow                     : { value : () => UIBar.stow() },\n\t\tunstow                   : { value : () => UIBar.unstow() },\n\t\tsetStoryElements         : { value : () => UIBar.update() },\n\t\t// `Dialog` methods.\n\t\tisOpen                   : { value : (...args) => Dialog.isOpen(...args) },\n\t\tbody                     : { value : () => Dialog.body() },\n\t\tsetup                    : { value : (...args) => Dialog.setup(...args) },\n\t\taddClickHandler          : { value : (...args) => Dialog.addClickHandler(...args) },\n\t\topen                     : { value : (...args) => Dialog.open(...args) },\n\t\tclose                    : { value : (...args) => Dialog.close(...args) },\n\t\tresize                   : { value : () => Dialog.resize() },\n\t\t// Deprecated method names.\n\t\tbuildDialogAutoload      : { value : uiBuildAutoload },\n\t\tbuildDialogJumpto        : { value : uiBuildJumpto },\n\t\tbuildDialogRestart       : { value : uiBuildRestart },\n\t\tbuildDialogSaves         : { value : uiBuildSaves },\n\t\tbuildDialogSettings      : { value : uiBuildSettings },\n\t\tbuildDialogShare         : { value : uiBuildShare },\n\t\tbuildLinkListFromPassage : { value : uiAssembleLinkList }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tuibar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, L10n, Setting, State, Story, UI, Config, setDisplayTitle, setPageElement\n*/\n\nvar UIBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// UI bar element cache.\n\tlet _$uiBar = null;\n\n\n\t/*******************************************************************************\n\t\tUI Bar Functions.\n\t*******************************************************************************/\n\n\tfunction uiBarDestroy() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarDestroy()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Hide the UI bar.\n\t\t_$uiBar.hide();\n\n\t\t// Remove its namespaced events.\n\t\tjQuery(document).off('.ui-bar');\n\n\t\t// Remove its styles.\n\t\tjQuery(document.head).find('#style-ui-bar').remove();\n\n\t\t// Remove it from the DOM.\n\t\t_$uiBar.remove();\n\n\t\t// Drop the reference to the element.\n\t\t_$uiBar = null;\n\t}\n\n\tfunction uiBarHide() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.hide();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarInit() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarInit()]'); }\n\n\t\tif (document.getElementById('ui-bar')) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Generate the UI bar elements.\n\t\tconst $elems = (() => {\n\t\t\tconst toggleLabel   = L10n.get('uiBarToggle');\n\t\t\tconst backwardLabel = L10n.get('uiBarBackward');\n\t\t\tconst jumptoLabel   = L10n.get('uiBarJumpto');\n\t\t\tconst forwardLabel  = L10n.get('uiBarForward');\n\n\t\t\treturn jQuery(document.createDocumentFragment())\n\t\t\t\t.append(\n\t\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t\t  '<div id=\"ui-bar\">'\n\t\t\t\t\t+     '<div id=\"ui-bar-tray\">'\n\t\t\t\t\t+         `<button id=\"ui-bar-toggle\" tabindex=\"0\" title=\"${toggleLabel}\" aria-label=\"${toggleLabel}\"></button>`\n\t\t\t\t\t+         '<div id=\"ui-bar-history\">'\n\t\t\t\t\t+             `<button id=\"history-backward\" tabindex=\"0\" title=\"${backwardLabel}\" aria-label=\"${backwardLabel}\">\\uE821</button>`\n\t\t\t\t\t+             `<button id=\"history-jumpto\" tabindex=\"0\" title=\"${jumptoLabel}\" aria-label=\"${jumptoLabel}\">\\uE839</button>`\n\t\t\t\t\t+             `<button id=\"history-forward\" tabindex=\"0\" title=\"${forwardLabel}\" aria-label=\"${forwardLabel}\">\\uE822</button>`\n\t\t\t\t\t+         '</div>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+     '<div id=\"ui-bar-body\">'\n\t\t\t\t\t+         '<header id=\"title\" role=\"banner\">'\n\t\t\t\t\t+             '<div id=\"story-banner\"></div>'\n\t\t\t\t\t+             '<h1 id=\"story-title\"></h1>'\n\t\t\t\t\t+             '<div id=\"story-subtitle\"></div>'\n\t\t\t\t\t+             '<div id=\"story-title-separator\"></div>'\n\t\t\t\t\t+             '<p id=\"story-author\"></p>'\n\t\t\t\t\t+         '</header>'\n\t\t\t\t\t+         '<div id=\"story-caption\"></div>'\n\t\t\t\t\t+         '<nav id=\"menu\" role=\"navigation\">'\n\t\t\t\t\t+             '<ul id=\"menu-story\"></ul>'\n\t\t\t\t\t+             '<ul id=\"menu-core\">'\n\t\t\t\t\t+                 `<li id=\"menu-item-saves\"><a tabindex=\"0\">${L10n.get('savesTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-settings\"><a tabindex=\"0\">${L10n.get('settingsTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-restart\"><a tabindex=\"0\">${L10n.get('restartTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-share\"><a tabindex=\"0\">${L10n.get('shareTitle')}</a></li>`\n\t\t\t\t\t+             '</ul>'\n\t\t\t\t\t+         '</nav>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+ '</div>'\n\t\t\t\t\t/* eslint-enable max-len */\n\t\t\t\t);\n\t\t})();\n\n\t\t/*\n\t\t\tCache the UI bar element, since its going to be used often.\n\n\t\t\tNOTE: We rewrap the element itself, rather than simply using the result\n\t\t\tof `find()`, so that we cache an uncluttered jQuery-wrapper (i.e. `context`\n\t\t\trefers to the element and there is no `prevObject`).\n\t\t*/\n\t\t_$uiBar = jQuery($elems.find('#ui-bar').get(0));\n\n\t\t// Insert the UI bar elements into the page before the main script.\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\n\t\t// Set up the UI bar's global event handlers.\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history-backward/-forward buttons.\n\t\t\t.on(':historyupdate.ui-bar', (($backward, $forward) => () => {\n\t\t\t\t$backward.ariaDisabled(State.length < 2);\n\t\t\t\t$forward.ariaDisabled(State.length === State.size);\n\t\t\t})(jQuery('#history-backward'), jQuery('#history-forward')));\n\t}\n\n\tfunction uiBarIsHidden() {\n\t\treturn _$uiBar && _$uiBar.css('display') === 'none';\n\t}\n\n\tfunction uiBarIsStowed() {\n\t\treturn _$uiBar && _$uiBar.hasClass('stowed');\n\t}\n\n\tfunction uiBarShow() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.show();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarStart() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarStart()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the #ui-bar's initial state.\n\t\tif (\n\t\t\ttypeof Config.ui.stowBarInitially === 'boolean'\n\t\t\t\t? Config.ui.stowBarInitially\n\t\t\t\t: jQuery(window).width() <= Config.ui.stowBarInitially\n\t\t) {\n\t\t\tuiBarStow(true);\n\t\t}\n\n\t\t// Set up the #ui-bar-toggle and #ui-bar-history widgets.\n\t\tjQuery('#ui-bar-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('uiBarToggle')\n\t\t\t}, () => _$uiBar.toggleClass('stowed'));\n\n\t\tif (Config.history.controls) {\n\t\t\tjQuery('#history-backward')\n\t\t\t\t.ariaDisabled(State.length < 2)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarBackward')\n\t\t\t\t}, () => Engine.backward());\n\n\t\t\tif (Story.lookup('tags', 'bookmark').length > 0) {\n\t\t\t\tjQuery('#history-jumpto')\n\t\t\t\t\t.ariaClick({\n\t\t\t\t\t\tlabel : L10n.get('uiBarJumpto')\n\t\t\t\t\t}, () => UI.jumpto());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery('#history-jumpto').remove();\n\t\t\t}\n\n\t\t\tjQuery('#history-forward')\n\t\t\t\t.ariaDisabled(State.length === State.size)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarForward')\n\t\t\t\t}, () => Engine.forward());\n\t\t}\n\t\telse {\n\t\t\tjQuery('#ui-bar-history').remove();\n\t\t}\n\n\t\t// Set up the story display title.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\telse {\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tsetPageElement('story-title', 'StoryTitle', Story.title);\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tjQuery('#story-title').text(Story.title);\n\t\t\t}\n\t\t}\n\n\t\t// Set up the dynamic page elements.\n\t\tif (!Story.has('StoryCaption')) {\n\t\t\tjQuery('#story-caption').remove();\n\t\t}\n\n\t\tif (!Story.has('StoryMenu')) {\n\t\t\tjQuery('#menu-story').remove();\n\t\t}\n\n\t\tif (!Config.ui.updateStoryElements) {\n\t\t\t// We only need to set the story elements here if `Config.ui.updateStoryElements`\n\t\t\t// is falsy, since otherwise they will be set by `Engine.play()`.\n\t\t\tuiBarUpdate();\n\t\t}\n\n\t\t// Set up the Saves menu item.\n\t\tjQuery('#menu-item-saves a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildSaves();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('savesTitle'));\n\n\t\t// Set up the Settings menu item.\n\t\tif (!Setting.isEmpty()) {\n\t\t\tjQuery('#menu-item-settings a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildSettings();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('settingsTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-settings').remove();\n\t\t}\n\n\t\t// Set up the Restart menu item.\n\t\tjQuery('#menu-item-restart a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildRestart();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('restartTitle'));\n\n\t\t// Set up the Share menu item.\n\t\tif (Story.has('StoryShare')) {\n\t\t\tjQuery('#menu-item-share a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildShare();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('shareTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-share').remove();\n\t\t}\n\t}\n\n\tfunction uiBarStow(noAnimation) {\n\t\tif (_$uiBar && !_$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.addClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUnstow(noAnimation) {\n\t\tif (_$uiBar && _$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.removeClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUpdate() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarUpdate()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the (non-navigation) dynamic page elements.\n\t\tsetPageElement('story-banner', 'StoryBanner');\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\tsetPageElement('story-subtitle', 'StorySubtitle');\n\t\tsetPageElement('story-author', 'StoryAuthor');\n\t\tsetPageElement('story-caption', 'StoryCaption');\n\n\t\t// Set up the #menu-story items.\n\t\tconst menuStory = document.getElementById('menu-story');\n\n\t\tif (menuStory !== null) {\n\t\t\tjQuery(menuStory).empty();\n\n\t\t\tif (Story.has('StoryMenu')) {\n\t\t\t\ttry {\n\t\t\t\t\tUI.assembleLinkList('StoryMenu', menuStory);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tconsole.error(ex);\n\t\t\t\t\tAlert.error('StoryMenu', ex.message);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tdestroy  : { value : uiBarDestroy },\n\t\thide     : { value : uiBarHide },\n\t\tinit     : { value : uiBarInit },\n\t\tisHidden : { value : uiBarIsHidden },\n\t\tisStowed : { value : uiBarIsStowed },\n\t\tshow     : { value : uiBarShow },\n\t\tstart    : { value : uiBarStart },\n\t\tstow     : { value : uiBarStow },\n\t\tunstow   : { value : uiBarUnstow },\n\t\tupdate   : { value : uiBarUpdate },\n\n\t\t// Legacy Functions.\n\t\tsetStoryElements : { value : uiBarUpdate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tdebugbar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal DebugView, Engine, L10n, Patterns, State, Util, session\n*/\n\nvar DebugBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _variableRe   = new RegExp(`^${Patterns.variable}$`);\n\tconst _numericKeyRe = /^\\d+$/;\n\tconst _watchList    = [];\n\tlet _$debugBar   = null;\n\tlet _$watchBody  = null;\n\tlet _$watchList  = null;\n\tlet _$turnSelect = null;\n\tlet _stowed      = true;\n\n\n\t/*******************************************************************************************************************\n\t\tDebug Bar Functions.\n\t*******************************************************************************************************************/\n\tfunction debugBarInit() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarInit()]'); }\n\n\t\t/*\n\t\t\tGenerate the debug bar elements and append them to the `<body>`.\n\t\t*/\n\t\tconst barToggleLabel   = L10n.get('debugBarToggle');\n\t\tconst watchAddLabel    = L10n.get('debugBarAddWatch');\n\t\tconst watchAllLabel    = L10n.get('debugBarWatchAll');\n\t\tconst watchNoneLabel   = L10n.get('debugBarWatchNone');\n\t\tconst watchToggleLabel = L10n.get('debugBarWatchToggle');\n\t\tconst viewsToggleLabel = L10n.get('debugBarViewsToggle');\n\n\t\tjQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"debug-bar\">'\n\t\t\t\t+     '<div id=\"debug-bar-watch\">'\n\t\t\t\t+         `<div>${L10n.get('debugBarNoWatches')}</div>>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-toggle\" tabindex=\"0\" title=\"${watchToggleLabel}\" aria-label=\"${watchToggleLabel}\">${L10n.get('debugBarLabelWatch')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-watch-label\" for=\"debug-bar-watch-input\">${L10n.get('debugBarLabelAdd')}</label>`\n\t\t\t\t+         '<input id=\"debug-bar-watch-input\" name=\"debug-bar-watch-input\" type=\"text\" list=\"debug-bar-watch-list\" tabindex=\"0\">'\n\t\t\t\t+         '<datalist id=\"debug-bar-watch-list\" aria-hidden=\"true\" hidden=\"hidden\"></datalist>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-add\" tabindex=\"0\" title=\"${watchAddLabel}\" aria-label=\"${watchAddLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-all\" tabindex=\"0\" title=\"${watchAllLabel}\" aria-label=\"${watchAllLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-none\" tabindex=\"0\" title=\"${watchNoneLabel}\" aria-label=\"${watchNoneLabel}\"></button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-views-toggle\" tabindex=\"0\" title=\"${viewsToggleLabel}\" aria-label=\"${viewsToggleLabel}\">${L10n.get('debugBarLabelViews')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-turn-label\" for=\"debug-bar-turn-select\">${L10n.get('debugBarLabelTurn')}</label>`\n\t\t\t\t+         '<select id=\"debug-bar-turn-select\" tabindex=\"0\"></select>'\n\t\t\t\t+     '</div>'\n\t\t\t\t+     `<button id=\"debug-bar-toggle\" tabindex=\"0\" title=\"${barToggleLabel}\" aria-label=\"${barToggleLabel}\"></button>`\n\t\t\t\t+ '</div>'\n\t\t\t\t+ '<div id=\"debug-bar-hint\"></div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.appendTo('body');\n\n\t\t/*\n\t\t\tCache various oft used elements.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$debugBar   = jQuery('#debug-bar');\n\t\t_$watchBody  = jQuery(_$debugBar.find('#debug-bar-watch').get(0));\n\t\t_$watchList  = jQuery(_$debugBar.find('#debug-bar-watch-list').get(0));\n\t\t_$turnSelect = jQuery(_$debugBar.find('#debug-bar-turn-select').get(0));\n\n\t\tconst $barToggle   = jQuery(_$debugBar.find('#debug-bar-toggle').get(0));\n\t\tconst $watchToggle = jQuery(_$debugBar.find('#debug-bar-watch-toggle').get(0));\n\t\tconst $watchInput  = jQuery(_$debugBar.find('#debug-bar-watch-input').get(0));\n\t\tconst $watchAdd    = jQuery(_$debugBar.find('#debug-bar-watch-add').get(0));\n\t\tconst $watchAll    = jQuery(_$debugBar.find('#debug-bar-watch-all').get(0));\n\t\tconst $watchNone   = jQuery(_$debugBar.find('#debug-bar-watch-none').get(0));\n\t\tconst $viewsToggle = jQuery(_$debugBar.find('#debug-bar-views-toggle').get(0));\n\n\t\t/*\n\t\t\tSet up the debug bar's local event handlers.\n\t\t*/\n\t\t$barToggle\n\t\t\t.ariaClick(debugBarToggle);\n\t\t$watchToggle\n\t\t\t.ariaClick(debugBarWatchToggle);\n\t\t$watchInput\n\t\t\t.on(':addwatch', function () {\n\t\t\t\tdebugBarWatchAdd(this.value.trim());\n\t\t\t\tthis.value = '';\n\t\t\t})\n\t\t\t.on('keypress', ev => {\n\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t$watchInput.trigger(':addwatch');\n\t\t\t\t}\n\t\t\t});\n\t\t$watchAdd\n\t\t\t.ariaClick(() => $watchInput.trigger(':addwatch'));\n\t\t$watchAll\n\t\t\t.ariaClick(debugBarWatchAddAll);\n\t\t$watchNone\n\t\t\t.ariaClick(debugBarWatchClear);\n\t\t_$turnSelect\n\t\t\t.on('change', function () {\n\t\t\t\tEngine.goTo(Number(this.value));\n\t\t\t});\n\t\t$viewsToggle\n\t\t\t.ariaClick(() => {\n\t\t\t\tDebugView.toggle();\n\t\t\t\t_updateSession();\n\t\t\t});\n\n\t\t/*\n\t\t\tSet up the debug bar's global event handlers.\n\t\t*/\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history select.\n\t\t\t.on(':historyupdate.debug-bar', _updateTurnSelect)\n\t\t\t// Set up a handler for the variables watch.\n\t\t\t.on(':passageend.debug-bar', () => {\n\t\t\t\t_updateWatchBody();\n\t\t\t\t_updateWatchList();\n\t\t\t})\n\t\t\t// Set up a handler for engine resets to clear the active debug session.\n\t\t\t.on(':enginerestart.debug-bar', _clearSession);\n\n\t\t/*\n\t\t\tInitially enable debug views if there's no active debug session.\n\t\t*/\n\t\tif (!_hasSession()) {\n\t\t\tDebugView.enable();\n\t\t}\n\t}\n\n\tfunction debugBarStart() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarStart()]'); }\n\n\t\t// Attempt to restore an existing session.\n\t\t_restoreSession();\n\n\t\t// Update the UI.\n\t\t_updateBar();\n\t\t_updateTurnSelect();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t}\n\n\tfunction debugBarIsStowed() {\n\t\treturn _stowed;\n\t}\n\n\tfunction debugBarStow() {\n\t\t_debugBarStowNoUpdate();\n\t\t_stowed = true;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarUnstow() {\n\t\t_debugBarUnstowNoUpdate();\n\t\t_stowed = false;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarToggle() {\n\t\tif (_stowed) {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarStow();\n\t\t}\n\t}\n\n\tfunction debugBarWatchAdd(varName) {\n\t\tif (!_variableRe.test(varName)) {\n\t\t\treturn;\n\t\t}\n\n\t\t_watchList.pushUnique(varName);\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchAddAll() {\n\t\tObject.keys(State.variables).map(name => _watchList.pushUnique(`$${name}`));\n\t\tObject.keys(State.temporary).map(name => _watchList.pushUnique(`_${name}`));\n\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchClear() {\n\t\tfor (let i = _watchList.length - 1; i >= 0; --i) {\n\t\t\t_watchList.pop();\n\t\t}\n\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDelete(varName) {\n\t\t_watchList.delete(varName);\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDisable() {\n\t\t_debugBarWatchDisableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchEnable() {\n\t\t_debugBarWatchEnableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchIsEnabled() {\n\t\treturn !_$watchBody.attr('hidden');\n\t}\n\n\tfunction debugBarWatchToggle() {\n\t\tif (_$watchBody.attr('hidden')) {\n\t\t\tdebugBarWatchEnable();\n\t\t}\n\t\telse {\n\t\t\tdebugBarWatchDisable();\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _debugBarStowNoUpdate() {\n\t\t_$debugBar.css('right', `-${_$debugBar.outerWidth()}px`);\n\t}\n\n\tfunction _debugBarUnstowNoUpdate() {\n\t\t_$debugBar.css('right', 0);\n\t}\n\n\tfunction _debugBarWatchDisableNoUpdate() {\n\t\t_$watchBody.attr({\n\t\t\t'aria-hidden' : true,\n\t\t\thidden        : 'hidden'\n\t\t});\n\t}\n\n\tfunction _debugBarWatchEnableNoUpdate() {\n\t\t_$watchBody.removeAttr('aria-hidden hidden');\n\t}\n\n\tfunction _clearSession() {\n\t\tsession.delete('debugState');\n\t}\n\n\tfunction _hasSession() {\n\t\treturn session.has('debugState');\n\t}\n\n\tfunction _restoreSession() {\n\t\tif (!_hasSession()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst debugState = session.get('debugState');\n\n\t\t_stowed = debugState.stowed;\n\n\t\t_watchList.push(...debugState.watchList);\n\n\t\tif (debugState.watchEnabled) {\n\t\t\t_debugBarWatchEnableNoUpdate();\n\t\t}\n\t\telse {\n\t\t\t_debugBarWatchDisableNoUpdate();\n\t\t}\n\n\t\tif (debugState.viewsEnabled) {\n\t\t\tDebugView.enable();\n\t\t}\n\t\telse {\n\t\t\tDebugView.disable();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction _updateSession() {\n\t\tsession.set('debugState', {\n\t\t\tstowed       : _stowed,\n\t\t\twatchList    : _watchList,\n\t\t\twatchEnabled : debugBarWatchIsEnabled(),\n\t\t\tviewsEnabled : DebugView.isEnabled()\n\t\t});\n\t}\n\n\tfunction _updateBar() {\n\t\tif (_stowed) {\n\t\t\tdebugBarStow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t}\n\n\tfunction _updateWatchBody() {\n\t\tif (_watchList.length === 0) {\n\t\t\t_$watchBody\n\t\t\t\t.empty()\n\t\t\t\t.append(`<div>${L10n.get('debugBarNoWatches')}</div>`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst delLabel = L10n.get('debugBarDeleteWatch');\n\t\tconst $table   = jQuery(document.createElement('table'));\n\t\tconst $tbody   = jQuery(document.createElement('tbody'));\n\n\t\tfor (let i = 0, len = _watchList.length; i < len; ++i) {\n\t\t\tconst varName = _watchList[i];\n\t\t\tconst varKey  = varName.slice(1);\n\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\tconst $row    = jQuery(document.createElement('tr'));\n\t\t\tconst $delBtn = jQuery(document.createElement('button'));\n\t\t\tconst $code   = jQuery(document.createElement('code'));\n\n\t\t\t$delBtn\n\t\t\t\t.addClass('watch-delete')\n\t\t\t\t.attr('data-name', varName)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tone   : true,\n\t\t\t\t\tlabel : delLabel\n\t\t\t\t}, () => debugBarWatchDelete(varName));\n\t\t\t$code\n\t\t\t\t.text(_toWatchString(store[varKey]));\n\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($delBtn)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.text(varName)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($code)\n\t\t\t\t.appendTo($row);\n\t\t\t$row\n\t\t\t\t.appendTo($tbody);\n\t\t}\n\n\t\t$table\n\t\t\t.append($tbody);\n\t\t_$watchBody\n\t\t\t.empty()\n\t\t\t.append($table);\n\t}\n\n\tfunction _updateWatchList() {\n\t\tconst svn = Object.keys(State.variables);\n\t\tconst tvn = Object.keys(State.temporary);\n\n\t\tif (svn.length === 0 && tvn.length === 0) {\n\t\t\t_$watchList.empty();\n\t\t\treturn;\n\t\t}\n\n\t\tconst names   = [...svn.map(name => `$${name}`), ...tvn.map(name => `_${name}`)].sort();\n\t\tconst options = document.createDocumentFragment();\n\n\t\tnames.delete(_watchList);\n\n\t\tfor (let i = 0, len = names.length; i < len; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(names[i])\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$watchList\n\t\t\t.empty()\n\t\t\t.append(options);\n\t}\n\n\tfunction _updateTurnSelect() {\n\t\tconst histLen = State.size;\n\t\tconst expLen  = State.expired.length;\n\t\tconst options = document.createDocumentFragment();\n\n\t\tfor (let i = 0; i < histLen; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(i)\n\t\t\t\t.text(`${expLen + i + 1}. ${Util.escape(State.history[i].title)}`)\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$turnSelect\n\t\t\t.empty()\n\t\t\t.ariaDisabled(histLen < 2)\n\t\t\t.append(options)\n\t\t\t.val(State.activeIndex);\n\t}\n\n\tfunction _toWatchString(value) {\n\t\t/*\n\t\t\tHandle the `null` primitive.\n\t\t*/\n\t\tif (value === null) {\n\t\t\treturn 'null';\n\t\t}\n\n\t\t/*\n\t\t\tHandle the rest of the primitives and functions.\n\t\t*/\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn 'NaN';\n\t\t\t}\n\t\t\telse if (!Number.isFinite(value)) {\n\t\t\t\treturn 'Infinity';\n\t\t\t}\n\t\t\t/* falls through */\n\t\tcase 'boolean':\n\t\tcase 'symbol':\n\t\tcase 'undefined':\n\t\t\treturn String(value);\n\n\t\tcase 'string':\n\t\t\treturn JSON.stringify(value);\n\n\t\tcase 'function':\n\t\t\treturn 'Function';\n\t\t}\n\n\t\tconst objType = Util.toStringTag(value);\n\n\t\t// /*\n\t\t// \tHandle instances of the primitive exemplar objects (`Boolean`, `Number`, `String`).\n\t\t// */\n\t\t// if (objType === 'Boolean') {\n\t\t// \treturn `Boolean\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'Number') {\n\t\t// \treturn `Number\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'String') {\n\t\t// \treturn `String\\u202F{\"${String(value)}\"}`;\n\t\t// }\n\n\t\t/*\n\t\t\tHandle `Date` objects.\n\t\t*/\n\t\tif (objType === 'Date') {\n\t\t\t// return `Date\\u202F${value.toISOString()}`;\n\t\t\treturn `Date\\u202F{${value.toLocaleString()}}`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `RegExp` objects.\n\t\t*/\n\t\tif (objType === 'RegExp') {\n\t\t\treturn `RegExp\\u202F${value.toString()}`;\n\t\t}\n\n\t\tconst result = [];\n\n\t\t/*\n\t\t\tHandle `Array` & `Set` objects.\n\t\t*/\n\t\tif (value instanceof Array || value instanceof Set) {\n\t\t\tconst list = value instanceof Array ? value : Array.from(value);\n\n\t\t\t// own numeric properties\n\t\t\t// NOTE: Do not use `<Array>.forEach()` here as it skips undefined members.\n\t\t\tfor (let i = 0, len = list.length; i < len; ++i) {\n\t\t\t\tresult.push(list.hasOwnProperty(i) ? _toWatchString(list[i]) : '<empty>');\n\t\t\t}\n\n\t\t\t// own enumerable non-numeric expando properties\n\t\t\tObject.keys(list)\n\t\t\t\t.filter(key => !_numericKeyRe.test(key))\n\t\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(list[key])}`));\n\n\t\t\treturn `${objType}(${list.length})\\u202F[${result.join(', ')}]`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `Map` objects.\n\t\t*/\n\t\tif (value instanceof Map) {\n\t\t\tvalue.forEach((val, key) => result.push(`${_toWatchString(key)} \\u2192 ${_toWatchString(val)}`));\n\n\t\t\treturn `${objType}(${value.size})\\u202F{${result.join(', ')}}`;\n\t\t}\n\n\t\t/*\n\t\t\tGeneral object handling.\n\t\t*/\n\t\t// own enumerable properties\n\t\tObject.keys(value)\n\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(value[key])}`));\n\n\t\treturn `${objType}\\u202F{${result.join(', ')}}`;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tDebug Bar Functions.\n\t\t*/\n\t\tinit     : { value : debugBarInit },\n\t\tisStowed : { value : debugBarIsStowed },\n\t\tstart    : { value : debugBarStart },\n\t\tstow     : { value : debugBarStow },\n\t\ttoggle   : { value : debugBarToggle },\n\t\tunstow   : { value : debugBarUnstow },\n\n\t\t/*\n\t\t\tWatch Functions.\n\t\t*/\n\t\twatch : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd       : { value : debugBarWatchAdd },\n\t\t\t\tall       : { value : debugBarWatchAddAll },\n\t\t\t\tclear     : { value : debugBarWatchClear },\n\t\t\t\tdelete    : { value : debugBarWatchDelete },\n\t\t\t\tdisable   : { value : debugBarWatchDisable },\n\t\t\t\tenable    : { value : debugBarWatchEnable },\n\t\t\t\tisEnabled : { value : debugBarWatchIsEnabled },\n\t\t\t\ttoggle    : { value : debugBarWatchToggle }\n\t\t\t}))\n\t\t}\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tloadscreen.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Engine */\n\nvar LoadScreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Locks collection.\n\tconst _locks = new Set();\n\n\t// Auto-incrementing lock ID.\n\tlet _autoId = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tLoadScreen Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize management of the loading screen.\n\t*/\n\tfunction loadScreenInit() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenInit()]'); }\n\n\t\t// Add a `readystatechange` listener for hiding/showing the loading screen.\n\t\tjQuery(document).on('readystatechange.SugarCube', () => {\n\t\t\tif (DEBUG) { console.log(`[LoadScreen/<readystatechange>] document.readyState: \"${document.readyState}\"; locks(${_locks.size}):`, _locks); }\n\n\t\t\tif (_locks.size > 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The value of `document.readyState` may be: 'loading' -> 'interactive' -> 'complete'.\n\t\t\t// Though, to reach this point, it must already be in, at least, the 'interactive' state.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\tif (jQuery(document.documentElement).attr('data-init') === 'loading') {\n\t\t\t\t\tif (Config.loadDelay > 0) {\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (_locks.size === 0) {\n\t\t\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, Math.max(Engine.minDomActionDelay, Config.loadDelay));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadScreenShow();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\tClear the loading screen.\n\t*/\n\tfunction loadScreenClear() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenClear()]'); }\n\n\t\t// Remove the event listener.\n\t\tjQuery(document).off('readystatechange.SugarCube');\n\n\t\t// Clear all locks.\n\t\t_locks.clear();\n\n\t\t// Hide the loading screen.\n\t\tloadScreenHide();\n\t}\n\n\t/*\n\t\tHide the loading screen.\n\t*/\n\tfunction loadScreenHide() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenHide()]'); }\n\n\t\tjQuery(document.documentElement).removeAttr('data-init');\n\t}\n\n\t/*\n\t\tShow the loading screen.\n\t*/\n\tfunction loadScreenShow() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenShow()]'); }\n\n\t\tjQuery(document.documentElement).attr('data-init', 'loading');\n\t}\n\n\t/*\n\t\tReturns a new lock ID after locking and showing the loading screen.\n\t*/\n\tfunction loadScreenLock() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenLock()]'); }\n\n\t\t++_autoId;\n\t\t_locks.add(_autoId);\n\n\t\tif (DEBUG) { console.log(`\\tacquired loading screen lock; id: ${_autoId}`); }\n\n\t\tloadScreenShow();\n\t\treturn _autoId;\n\t}\n\n\t/*\n\t\tRemove the lock associated with the given lock ID and, if no locks remain,\n\t\ttrigger a `readystatechange` event.\n\t*/\n\tfunction loadScreenUnlock(id) {\n\t\tif (DEBUG) { console.log(`[LoadScreen/loadScreenUnlock(id: ${id})]`); }\n\n\t\tif (id == null) { // lazy equality for null\n\t\t\tthrow new Error('LoadScreen.unlock called with a null or undefined ID');\n\t\t}\n\n\t\tif (_locks.has(id)) {\n\t\t\t_locks.delete(id);\n\n\t\t\tif (DEBUG) { console.log(`\\treleased loading screen lock; id: ${id}`); }\n\t\t}\n\n\t\tif (_locks.size === 0) {\n\t\t\tjQuery(document).trigger('readystatechange');\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : loadScreenInit },\n\t\tclear  : { value : loadScreenClear },\n\t\thide   : { value : loadScreenHide },\n\t\tshow   : { value : loadScreenShow },\n\t\tlock   : { value : loadScreenLock },\n\t\tunlock : { value : loadScreenUnlock }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsugarcube.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Browser, Config, Dialog, Engine, Fullscreen, Has, LoadScreen, SimpleStore, L10n, Macro, Passage,\n\t       Save, Scripting, Setting, SimpleAudio, State, Story, UI, UIBar, DebugBar, Util, Visibility, Wikifier\n*/\n/* eslint-disable no-var */\n\n/*\n\tVersion object.\n*/\nvar version = Object.freeze({\n\ttitle      : 'SugarCube',\n\tmajor      : 2,\n\tminor      : 33,\n\tpatch      : 2,\n\tprerelease : null,\n\tbuild      : 56,\n\tdate       : new Date(\"2020-08-29T12:37:07.022Z\"),\n\t/* legacy */\n\textensions : {},\n\t/* /legacy */\n\n\ttoString() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.major}.${this.minor}.${this.patch}${prerelease}+${this.build}`;\n\t},\n\n\tshort() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.title} (v${this.major}.${this.minor}.${this.patch}${prerelease})`;\n\t},\n\n\tlong() {\n\t\t'use strict';\n\n\t\treturn `${this.title} v${this.toString()} (${this.date.toUTCString()})`;\n\t}\n});\n\n/* eslint-disable no-unused-vars */\n/*\n\tInternal variables.\n*/\n// Temporary state object.\nvar TempState = {};\n\n// Legacy macros object.\nvar macros = {};\n\n// Post-display task callbacks object.\nvar postdisplay = {};\n\n// Post-render task callbacks object.\nvar postrender = {};\n\n// Pre-display task callbacks object.\nvar predisplay = {};\n\n// Pre-history task callbacks object.\nvar prehistory = {};\n\n// Pre-render task callbacks object.\nvar prerender = {};\n\n// Session storage manager object.\nvar session = null;\n\n// Settings object.\nvar settings = {};\n\n// Setup object.\nvar setup = {};\n\n// Persistant storage manager object.\nvar storage = null;\n\n/*\n\tLegacy aliases.\n*/\nvar browser       = Browser;\nvar config        = Config;\nvar has           = Has;\nvar History       = State;\nvar state         = State;\nvar tale          = Story;\nvar TempVariables = State.temporary;\n/* eslint-enable no-unused-vars */\n\n/*\n\tGlobal `SugarCube` object.  Allows scripts to detect if they're running in SugarCube by\n\ttesting for the object (e.g. `\"SugarCube\" in window`) and contains exported identifiers\n\tfor debugging purposes.\n*/\nwindow.SugarCube = {};\n\n/*\n\tMain function, entry point for the story.\n*/\njQuery(() => {\n\t'use strict';\n\n\tif (DEBUG) { console.log('[SugarCube/main()] Document loaded; beginning startup.'); }\n\n\t/*\n\t\tWARNING!\n\n\t\tThe ordering of the code within this function is critically important,\n\t\tso be careful when mucking around with it.\n\t*/\n\ttry {\n\t\t// Acquire an initial lock for and initialize the loading screen.\n\t\tconst lockId = LoadScreen.lock();\n\t\tLoadScreen.init();\n\n\t\t// Normalize the document.\n\t\tif (document.normalize) {\n\t\t\tdocument.normalize();\n\t\t}\n\n\t\t// Load the story data (must be done before most anything else).\n\t\tStory.load();\n\n\t\t// Instantiate the storage and session objects.\n\t\t// NOTE: `SimpleStore.create(storageId, persistent)`\n\t\tstorage = SimpleStore.create(Story.domId, true);\n\t\tsession = SimpleStore.create(Story.domId, false);\n\n\t\t// Initialize the user interface (must be done before story initialization, specifically before scripts).\n\t\tDialog.init();\n\t\tUIBar.init();\n\t\tEngine.init();\n\n\t\t// Initialize the story (largely load the user styles, scripts, and widgets).\n\t\tStory.init();\n\n\t\t// Initialize the localization (must be done after story initialization).\n\t\tL10n.init();\n\n\t\t// Alert when the browser is degrading required capabilities (must be done after localization initialization).\n\t\tif (!session.has('rcWarn') && storage.name === 'cookie') {\n\t\t\t/* eslint-disable no-alert */\n\t\t\tsession.set('rcWarn', 1);\n\t\t\twindow.alert(L10n.get('warningNoWebStorage'));\n\t\t\t/* eslint-enable no-alert */\n\t\t}\n\n\t\t// Initialize the saves (must be done after story initialization, but before engine start).\n\t\tSave.init();\n\n\t\t// Initialize the settings.\n\t\tSetting.init();\n\n\t\t// Initialize the macros.\n\t\tMacro.init();\n\n\t\t// Start the engine (should be done as late as possible, but before interface startup).\n\t\tEngine.start();\n\n\t\t// Initialize the debug bar interface (should be done as late as possible, but before interface startup).\n\t\tif (Config.debug) {\n\t\t\tDebugBar.init();\n\t\t}\n\n\t\t// Set a recurring timer to start the interfaces (necessary due to DOM readiness issues in some browsers).\n\t\tconst $window    = $(window);\n\t\tconst vprCheckId = setInterval(() => {\n\t\t\t// If `$window.width()` returns a zero value, bail out and wait.\n\t\t\tif (!$window.width()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear the recurring timer.\n\t\t\tclearInterval(vprCheckId);\n\n\t\t\t// Start the UI bar interface.\n\t\t\tUIBar.start();\n\n\t\t\t// Start the debug bar interface.\n\t\t\tif (Config.debug) {\n\t\t\t\tDebugBar.start();\n\t\t\t}\n\n\t\t\t// Trigger the `:storyready` global synthetic event.\n\t\t\tjQuery.event.trigger({ type : ':storyready' });\n\n\t\t\t// Release the loading screen lock after a short delay.\n\t\t\tsetTimeout(() => LoadScreen.unlock(lockId), Engine.minDomActionDelay * 2);\n\t\t}, Engine.minDomActionDelay);\n\n\t\t// Finally, export identifiers for debugging purposes.\n\t\tObject.defineProperty(window, 'SugarCube', {\n\t\t\t// WARNING: We need to assign new values at points, so seal it, do not freeze it.\n\t\t\tvalue : Object.seal(Object.assign(Object.create(null), {\n\t\t\t\tBrowser,\n\t\t\t\tConfig,\n\t\t\t\tDialog,\n\t\t\t\tEngine,\n\t\t\t\tFullscreen,\n\t\t\t\tHas,\n\t\t\t\tL10n,\n\t\t\t\tMacro,\n\t\t\t\tPassage,\n\t\t\t\tSave,\n\t\t\t\tScripting,\n\t\t\t\tSetting,\n\t\t\t\tSimpleAudio,\n\t\t\t\tState,\n\t\t\t\tStory,\n\t\t\t\tUI,\n\t\t\t\tUIBar,\n\t\t\t\tDebugBar,\n\t\t\t\tUtil,\n\t\t\t\tVisibility,\n\t\t\t\tWikifier,\n\t\t\t\tsession,\n\t\t\t\tsettings,\n\t\t\t\tsetup,\n\t\t\t\tstorage,\n\t\t\t\tversion\n\t\t\t}))\n\t\t});\n\n\t\tif (DEBUG) { console.log('[SugarCube/main()] Startup complete; story ready.'); }\n\t}\n\tcatch (ex) {\n\t\tconsole.error(ex);\n\t\tLoadScreen.clear();\n\t\treturn Alert.fatal(null, ex.message, ex);\n\t}\n});\n\n})(window, window.document, jQuery);\n}\n\t</script>\n</body>\n</html>\n"});
\ No newline at end of file
+window.storyFormat({"name":"SugarCube","version":"2.33.4","description":"A full featured, highly customizable story format.  See its <a href=\"http://www.motoslave.net/sugarcube/2/#documentation\" target=\"_blank\">documentation</a>.","author":"Thomas Michael Edwards","image":"icon.svg","url":"http://www.motoslave.net/sugarcube/","license":"BSD-2-Clause","proofing":false,"source":"<!DOCTYPE html>\n<html data-init=\"no-js\">\n<head>\n<meta charset=\"UTF-8\" />\n<title>{{STORY_NAME}}</title>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<!--\n\nSugarCube (v2.33.4): A free (gratis and libre) story format.\n\nCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-->\n<script id=\"script-libraries\" type=\"text/javascript\">\nif(document.head&&document.addEventListener&&document.querySelector&&Object.create&&Object.freeze&&JSON){document.documentElement.setAttribute(\"data-init\", \"loading\");\n/*! @source http://purl.eligrey.com/github/classList.js/blob/1.2.20171210/classList.js */\n\"document\"in self&&(\"classList\"in document.createElement(\"_\")&&(!document.createElementNS||\"classList\"in document.createElementNS(\"http://www.w3.org/2000/svg\",\"g\"))||!function(t){\"use strict\";if(\"Element\"in t){var e=\"classList\",n=\"prototype\",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\\s+|\\s+$/g,\"\")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(\"\"===e)throw new c(\"SYNTAX_ERR\",\"The token must not be empty.\");if(/\\s/.test(e))throw new c(\"INVALID_CHARACTER_ERR\",\"The token must not contain space characters.\");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute(\"class\")||\"\"),n=e?e.split(/\\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute(\"class\",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+\"\")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+\"\",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+\"\",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&\"remove\":e!==!1&&\"add\";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+\"\");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(\" \")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){\"use strict\";var t=document.createElement(\"_\");if(t.classList.add(\"c1\",\"c2\"),!t.classList.contains(\"c2\")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e(\"add\"),e(\"remove\")}if(t.classList.toggle(\"c3\",!1),t.classList.contains(\"c3\")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}\"replace\"in document.createElement(\"_\").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(\" \"),i=n.indexOf(t+\"\");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());\n/*!\n * https://github.com/es-shims/es5-shim\n * @license es5-shim Copyright 2009-2020 by contributors, MIT License\n * see https://github.com/es-shims/es5-shim/blob/v4.5.14/LICENSE\n */\n(function(t,r){\"use strict\";if(typeof define===\"function\"&&define.amd){define(r)}else if(typeof exports===\"object\"){module.exports=r()}else{t.returnExports=r()}})(this,function(){var t=Array;var r=t.prototype;var e=Object;var n=e.prototype;var i=Function;var a=i.prototype;var o=String;var f=o.prototype;var u=Number;var l=u.prototype;var s=r.slice;var c=r.splice;var v=r.push;var h=r.unshift;var p=r.concat;var y=r.join;var d=a.call;var g=a.apply;var w=Math.max;var b=Math.min;var T=n.toString;var m=typeof Symbol===\"function\"&&typeof Symbol.toStringTag===\"symbol\";var D;var S=Function.prototype.toString,x=/^\\s*class /,O=function isES6ClassFn(t){try{var r=S.call(t);var e=r.replace(/\\/\\/.*\\n/g,\"\");var n=e.replace(/\\/\\*[.\\s\\S]*\\*\\//g,\"\");var i=n.replace(/\\n/gm,\" \").replace(/ {2}/g,\" \");return x.test(i)}catch(a){return false}},E=function tryFunctionObject(t){try{if(O(t)){return false}S.call(t);return true}catch(r){return false}},j=\"[object Function]\",I=\"[object GeneratorFunction]\",D=function isCallable(t){if(!t){return false}if(typeof t!==\"function\"&&typeof t!==\"object\"){return false}if(m){return E(t)}if(O(t)){return false}var r=T.call(t);return r===j||r===I};var M;var U=RegExp.prototype.exec,$=function tryRegexExec(t){try{U.call(t);return true}catch(r){return false}},F=\"[object RegExp]\";M=function isRegex(t){if(typeof t!==\"object\"){return false}return m?$(t):T.call(t)===F};var N;var C=String.prototype.valueOf,k=function tryStringObject(t){try{C.call(t);return true}catch(r){return false}},A=\"[object String]\";N=function isString(t){if(typeof t===\"string\"){return true}if(typeof t!==\"object\"){return false}return m?k(t):T.call(t)===A};var R=e.defineProperty&&function(){try{var t={};e.defineProperty(t,\"x\",{enumerable:false,value:t});for(var r in t){return false}return t.x===t}catch(n){return false}}();var P=function(t){var r;if(R){r=function(t,r,n,i){if(!i&&r in t){return}e.defineProperty(t,r,{configurable:true,enumerable:false,writable:true,value:n})}}else{r=function(t,r,e,n){if(!n&&r in t){return}t[r]=e}}return function defineProperties(e,n,i){for(var a in n){if(t.call(n,a)){r(e,a,n[a],i)}}}}(n.hasOwnProperty);var J=function isPrimitive(t){var r=typeof t;return t===null||r!==\"object\"&&r!==\"function\"};var Y=u.isNaN||function isActualNaN(t){return t!==t};var z={ToInteger:function ToInteger(t){var r=+t;if(Y(r)){r=0}else if(r!==0&&r!==1/0&&r!==-(1/0)){r=(r>0||-1)*Math.floor(Math.abs(r))}return r},ToPrimitive:function ToPrimitive(t){var r,e,n;if(J(t)){return t}e=t.valueOf;if(D(e)){r=e.call(t);if(J(r)){return r}}n=t.toString;if(D(n)){r=n.call(t);if(J(r)){return r}}throw new TypeError},ToObject:function(t){if(t==null){throw new TypeError(\"can't convert \"+t+\" to object\")}return e(t)},ToUint32:function ToUint32(t){return t>>>0}};var Z=function Empty(){};P(a,{bind:function bind(t){var r=this;if(!D(r)){throw new TypeError(\"Function.prototype.bind called on incompatible \"+r)}var n=s.call(arguments,1);var a;var o=function(){if(this instanceof a){var i=g.call(r,this,p.call(n,s.call(arguments)));if(e(i)===i){return i}return this}else{return g.call(r,t,p.call(n,s.call(arguments)))}};var f=w(0,r.length-n.length);var u=[];for(var l=0;l<f;l++){v.call(u,\"$\"+l)}a=i(\"binder\",\"return function (\"+y.call(u,\",\")+\"){ return binder.apply(this, arguments); }\")(o);if(r.prototype){Z.prototype=r.prototype;a.prototype=new Z;Z.prototype=null}return a}});var G=d.bind(n.hasOwnProperty);var H=d.bind(n.toString);var W=d.bind(s);var B=g.bind(s);if(typeof document===\"object\"&&document&&document.documentElement){try{W(document.documentElement.childNodes)}catch(X){var L=W;var q=B;W=function arraySliceIE(t){var r=[];var e=t.length;while(e-- >0){r[e]=t[e]}return q(r,L(arguments,1))};B=function arraySliceApplyIE(t,r){return q(W(t),r)}}}var K=d.bind(f.slice);var Q=d.bind(f.split);var V=d.bind(f.indexOf);var _=d.bind(v);var tt=d.bind(n.propertyIsEnumerable);var rt=d.bind(r.sort);var et=t.isArray||function isArray(t){return H(t)===\"[object Array]\"};var nt=[].unshift(0)!==1;P(r,{unshift:function(){h.apply(this,arguments);return this.length}},nt);P(t,{isArray:et});var it=e(\"a\");var at=it[0]!==\"a\"||!(0 in it);var ot=function properlyBoxed(t){var r=true;var e=true;var n=false;if(t){try{t.call(\"foo\",function(t,e,n){if(typeof n!==\"object\"){r=false}});t.call([1],function(){\"use strict\";e=typeof this===\"string\"},\"x\")}catch(i){n=true}}return!!t&&!n&&r&&e};P(r,{forEach:function forEach(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=-1;var i=z.ToUint32(e.length);var a;if(arguments.length>1){a=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.forEach callback must be a function\")}while(++n<i){if(n in e){if(typeof a===\"undefined\"){t(e[n],n,r)}else{t.call(a,e[n],n,r)}}}}},!ot(r.forEach));P(r,{map:function map(r){var e=z.ToObject(this);var n=at&&N(this)?Q(this,\"\"):e;var i=z.ToUint32(n.length);var a=t(i);var o;if(arguments.length>1){o=arguments[1]}if(!D(r)){throw new TypeError(\"Array.prototype.map callback must be a function\")}for(var f=0;f<i;f++){if(f in n){if(typeof o===\"undefined\"){a[f]=r(n[f],f,e)}else{a[f]=r.call(o,n[f],f,e)}}}return a}},!ot(r.map));P(r,{filter:function filter(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i=[];var a;var o;if(arguments.length>1){o=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.filter callback must be a function\")}for(var f=0;f<n;f++){if(f in e){a=e[f];if(typeof o===\"undefined\"?t(a,f,r):t.call(o,a,f,r)){_(i,a)}}}return i}},!ot(r.filter));P(r,{every:function every(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.every callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&!(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return false}}return true}},!ot(r.every));P(r,{some:function some(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);var i;if(arguments.length>1){i=arguments[1]}if(!D(t)){throw new TypeError(\"Array.prototype.some callback must be a function\")}for(var a=0;a<n;a++){if(a in e&&(typeof i===\"undefined\"?t(e[a],a,r):t.call(i,e[a],a,r))){return true}}return false}},!ot(r.some));var ft=false;if(r.reduce){ft=typeof r.reduce.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduce:function reduce(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduce callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduce of empty array with no initial value\")}var i=0;var a;if(arguments.length>=2){a=arguments[1]}else{do{if(i in e){a=e[i++];break}if(++i>=n){throw new TypeError(\"reduce of empty array with no initial value\")}}while(true)}for(;i<n;i++){if(i in e){a=t(a,e[i],i,r)}}return a}},!ft);var ut=false;if(r.reduceRight){ut=typeof r.reduceRight.call(\"es5\",function(t,r,e,n){return n})===\"object\"}P(r,{reduceRight:function reduceRight(t){var r=z.ToObject(this);var e=at&&N(this)?Q(this,\"\"):r;var n=z.ToUint32(e.length);if(!D(t)){throw new TypeError(\"Array.prototype.reduceRight callback must be a function\")}if(n===0&&arguments.length===1){throw new TypeError(\"reduceRight of empty array with no initial value\")}var i;var a=n-1;if(arguments.length>=2){i=arguments[1]}else{do{if(a in e){i=e[a--];break}if(--a<0){throw new TypeError(\"reduceRight of empty array with no initial value\")}}while(true)}if(a<0){return i}do{if(a in e){i=t(i,e[a],a,r)}}while(a--);return i}},!ut);var lt=r.indexOf&&[0,1].indexOf(1,2)!==-1;P(r,{indexOf:function indexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=0;if(arguments.length>1){n=z.ToInteger(arguments[1])}n=n>=0?n:w(0,e+n);for(;n<e;n++){if(n in r&&r[n]===t){return n}}return-1}},lt);var st=r.lastIndexOf&&[0,1].lastIndexOf(0,-3)!==-1;P(r,{lastIndexOf:function lastIndexOf(t){var r=at&&N(this)?Q(this,\"\"):z.ToObject(this);var e=z.ToUint32(r.length);if(e===0){return-1}var n=e-1;if(arguments.length>1){n=b(n,z.ToInteger(arguments[1]))}n=n>=0?n:e-Math.abs(n);for(;n>=0;n--){if(n in r&&t===r[n]){return n}}return-1}},st);var ct=function(){var t=[1,2];var r=t.splice();return t.length===2&&et(r)&&r.length===0}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}else{return c.apply(this,arguments)}}},!ct);var vt=function(){var t={};r.splice.call(t,0,0,1);return t.length===1}();P(r,{splice:function splice(t,r){if(arguments.length===0){return[]}var e=arguments;this.length=w(z.ToInteger(this.length),0);if(arguments.length>0&&typeof r!==\"number\"){e=W(arguments);if(e.length<2){_(e,this.length-t)}else{e[1]=z.ToInteger(r)}}return c.apply(this,e)}},!vt);var ht=function(){var r=new t(1e5);r[8]=\"x\";r.splice(1,1);return r.indexOf(\"x\")===7}();var pt=function(){var t=256;var r=[];r[t]=\"a\";r.splice(t+1,0,\"b\");return r[t]===\"a\"}();P(r,{splice:function splice(t,r){var e=z.ToObject(this);var n=[];var i=z.ToUint32(e.length);var a=z.ToInteger(t);var f=a<0?w(i+a,0):b(a,i);var u=arguments.length===0?0:arguments.length===1?i-f:b(w(z.ToInteger(r),0),i-f);var l=0;var s;while(l<u){s=o(f+l);if(G(e,s)){n[l]=e[s]}l+=1}var c=W(arguments,2);var v=c.length;var h;if(v<u){l=f;var p=i-u;while(l<p){s=o(l+u);h=o(l+v);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l+=1}l=i;var y=i-u+v;while(l>y){delete e[l-1];l-=1}}else if(v>u){l=i-u;while(l>f){s=o(l+u-1);h=o(l+v-1);if(G(e,s)){e[h]=e[s]}else{delete e[h]}l-=1}}l=f;for(var d=0;d<c.length;++d){e[l]=c[d];l+=1}e.length=i-u+v;return n}},!ht||!pt);var yt=r.join;var dt;try{dt=Array.prototype.join.call(\"123\",\",\")!==\"1,2,3\"}catch(X){dt=true}if(dt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(N(this)?Q(this,\"\"):this,r)}},dt)}var gt=[1,2].join(undefined)!==\"1,2\";if(gt){P(r,{join:function join(t){var r=typeof t===\"undefined\"?\",\":t;return yt.call(this,r)}},gt)}var wt=function push(t){var r=z.ToObject(this);var e=z.ToUint32(r.length);var n=0;while(n<arguments.length){r[e+n]=arguments[n];n+=1}r.length=e+n;return e+n};var bt=function(){var t={};var r=Array.prototype.push.call(t,undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:function push(t){if(et(this)){return v.apply(this,arguments)}return wt.apply(this,arguments)}},bt);var Tt=function(){var t=[];var r=t.push(undefined);return r!==1||t.length!==1||typeof t[0]!==\"undefined\"||!G(t,0)}();P(r,{push:wt},Tt);P(r,{slice:function(t,r){var e=N(this)?Q(this,\"\"):this;return B(e,arguments)}},at);var mt=function(){try{[1,2].sort(null)}catch(t){try{[1,2].sort({})}catch(r){return false}}return true}();var Dt=function(){try{[1,2].sort(/a/);return false}catch(t){}return true}();var St=function(){try{[1,2].sort(undefined);return true}catch(t){}return false}();P(r,{sort:function sort(t){if(typeof t===\"undefined\"){return rt(this)}if(!D(t)){throw new TypeError(\"Array.prototype.sort callback must be a function\")}return rt(this,t)}},mt||!St||!Dt);var xt=!tt({toString:null},\"toString\");var Ot=tt(function(){},\"prototype\");var Et=!G(\"x\",\"0\");var jt=function(t){var r=t.constructor;return r&&r.prototype===t};var It={$applicationCache:true,$console:true,$external:true,$frame:true,$frameElement:true,$frames:true,$innerHeight:true,$innerWidth:true,$onmozfullscreenchange:true,$onmozfullscreenerror:true,$outerHeight:true,$outerWidth:true,$pageXOffset:true,$pageYOffset:true,$parent:true,$scrollLeft:true,$scrollTop:true,$scrollX:true,$scrollY:true,$self:true,$webkitIndexedDB:true,$webkitStorageInfo:true,$window:true,$width:true,$height:true,$top:true,$localStorage:true};var Mt=function(){if(typeof window===\"undefined\"){return false}for(var t in window){try{if(!It[\"$\"+t]&&G(window,t)&&window[t]!==null&&typeof window[t]===\"object\"){jt(window[t])}}catch(r){return true}}return false}();var Ut=function(t){if(typeof window===\"undefined\"||!Mt){return jt(t)}try{return jt(t)}catch(r){return false}};var $t=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"];var Ft=$t.length;var Nt=function isArguments(t){return H(t)===\"[object Arguments]\"};var Ct=function isArguments(t){return t!==null&&typeof t===\"object\"&&typeof t.length===\"number\"&&t.length>=0&&!et(t)&&D(t.callee)};var kt=Nt(arguments)?Nt:Ct;P(e,{keys:function keys(t){var r=D(t);var e=kt(t);var n=t!==null&&typeof t===\"object\";var i=n&&N(t);if(!n&&!r&&!e){throw new TypeError(\"Object.keys called on a non-object\")}var a=[];var f=Ot&&r;if(i&&Et||e){for(var u=0;u<t.length;++u){_(a,o(u))}}if(!e){for(var l in t){if(!(f&&l===\"prototype\")&&G(t,l)){_(a,o(l))}}}if(xt){var s=Ut(t);for(var c=0;c<Ft;c++){var v=$t[c];if(!(s&&v===\"constructor\")&&G(t,v)){_(a,v)}}}return a}});var At=e.keys&&function(){return e.keys(arguments).length===2}(1,2);var Rt=e.keys&&function(){var t=e.keys(arguments);return arguments.length!==1||t.length!==1||t[0]!==1}(1);var Pt=e.keys;P(e,{keys:function keys(t){if(kt(t)){return Pt(W(t))}else{return Pt(t)}}},!At||Rt);var Jt=new Date(-0xc782b5b342b24).getUTCMonth()!==0;var Yt=new Date(-0x55d318d56a724);var zt=new Date(14496624e5);var Zt=Yt.toUTCString()!==\"Mon, 01 Jan -45875 11:59:59 GMT\";var Gt;var Ht;var Wt=Yt.getTimezoneOffset();if(Wt<-720){Gt=Yt.toDateString()!==\"Tue Jan 02 -45875\";Ht=!/^Thu Dec 10 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}else{Gt=Yt.toDateString()!==\"Mon Jan 01 -45875\";Ht=!/^Wed Dec 09 2015 \\d\\d:\\d\\d:\\d\\d GMT[-+]\\d\\d\\d\\d(?: |$)/.test(String(zt))}var Bt=d.bind(Date.prototype.getFullYear);var Xt=d.bind(Date.prototype.getMonth);var Lt=d.bind(Date.prototype.getDate);var qt=d.bind(Date.prototype.getUTCFullYear);var Kt=d.bind(Date.prototype.getUTCMonth);var Qt=d.bind(Date.prototype.getUTCDate);var Vt=d.bind(Date.prototype.getUTCDay);var _t=d.bind(Date.prototype.getUTCHours);var tr=d.bind(Date.prototype.getUTCMinutes);var rr=d.bind(Date.prototype.getUTCSeconds);var er=d.bind(Date.prototype.getUTCMilliseconds);var nr=[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"];var ir=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"];var ar=function daysInMonth(t,r){return Lt(new Date(r,t,0))};P(Date.prototype,{getFullYear:function getFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);if(t<0&&Xt(this)>11){return t+1}return t},getMonth:function getMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);if(t<0&&r>11){return 0}return r},getDate:function getDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Bt(this);var r=Xt(this);var e=Lt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e},getUTCFullYear:function getUTCFullYear(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);if(t<0&&Kt(this)>11){return t+1}return t},getUTCMonth:function getUTCMonth(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);if(t<0&&r>11){return 0}return r},getUTCDate:function getUTCDate(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=qt(this);var r=Kt(this);var e=Qt(this);if(t<0&&r>11){if(r===12){return e}var n=ar(0,t+1);return n-e+1}return e}},Jt);P(Date.prototype,{toUTCString:function toUTCString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=Vt(this);var r=Qt(this);var e=Kt(this);var n=qt(this);var i=_t(this);var a=tr(this);var o=rr(this);return nr[t]+\", \"+(r<10?\"0\"+r:r)+\" \"+ir[e]+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"}},Jt||Zt);P(Date.prototype,{toDateString:function toDateString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n}},Jt||Gt);if(Jt||Ht){Date.prototype.toString=function toString(){if(!this||!(this instanceof Date)){throw new TypeError(\"this is not a Date object.\")}var t=this.getDay();var r=this.getDate();var e=this.getMonth();var n=this.getFullYear();var i=this.getHours();var a=this.getMinutes();var o=this.getSeconds();var f=this.getTimezoneOffset();var u=Math.floor(Math.abs(f)/60);var l=Math.floor(Math.abs(f)%60);return nr[t]+\" \"+ir[e]+\" \"+(r<10?\"0\"+r:r)+\" \"+n+\" \"+(i<10?\"0\"+i:i)+\":\"+(a<10?\"0\"+a:a)+\":\"+(o<10?\"0\"+o:o)+\" GMT\"+(f>0?\"-\":\"+\")+(u<10?\"0\"+u:u)+(l<10?\"0\"+l:l)};if(R){e.defineProperty(Date.prototype,\"toString\",{configurable:true,enumerable:false,writable:true})}}var or=-621987552e5;var fr=\"-000001\";var ur=Date.prototype.toISOString&&new Date(or).toISOString().indexOf(fr)===-1;var lr=Date.prototype.toISOString&&new Date(-1).toISOString()!==\"1969-12-31T23:59:59.999Z\";var sr=d.bind(Date.prototype.getTime);P(Date.prototype,{toISOString:function toISOString(){if(!isFinite(this)||!isFinite(sr(this))){throw new RangeError(\"Date.prototype.toISOString called on non-finite value.\")}var t=qt(this);var r=Kt(this);t+=Math.floor(r/12);r=(r%12+12)%12;var e=[r+1,Qt(this),_t(this),tr(this),rr(this)];t=(t<0?\"-\":t>9999?\"+\":\"\")+K(\"00000\"+Math.abs(t),0<=t&&t<=9999?-4:-6);for(var n=0;n<e.length;++n){e[n]=K(\"00\"+e[n],-2)}return t+\"-\"+W(e,0,2).join(\"-\")+\"T\"+W(e,2).join(\":\")+\".\"+K(\"000\"+er(this),-3)+\"Z\"}},ur||lr);var cr=function(){try{return Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(or).toJSON().indexOf(fr)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(t){return false}}();if(!cr){Date.prototype.toJSON=function toJSON(t){var r=e(this);var n=z.ToPrimitive(r);if(typeof n===\"number\"&&!isFinite(n)){return null}var i=r.toISOString;if(!D(i)){throw new TypeError(\"toISOString property is not callable\")}return i.call(r)}}var vr=Date.parse(\"+033658-09-27T01:46:40.000Z\")===1e15;var hr=!isNaN(Date.parse(\"2012-04-04T24:00:00.500Z\"))||!isNaN(Date.parse(\"2012-11-31T23:59:59.000Z\"))||!isNaN(Date.parse(\"2012-12-31T23:59:60.000Z\"));var pr=isNaN(Date.parse(\"2000-01-01T00:00:00.000Z\"));if(pr||hr||!vr){var yr=Math.pow(2,31)-1;var dr=Y(new Date(1970,0,1,0,0,0,yr+1).getTime());Date=function(t){var r=function Date(e,n,i,a,f,u,l){var s=arguments.length;var c;if(this instanceof t){var v=u;var h=l;if(dr&&s>=7&&l>yr){var p=Math.floor(l/yr)*yr;var y=Math.floor(p/1e3);v+=y;h-=y*1e3}c=s===1&&o(e)===e?new t(r.parse(e)):s>=7?new t(e,n,i,a,f,v,h):s>=6?new t(e,n,i,a,f,v):s>=5?new t(e,n,i,a,f):s>=4?new t(e,n,i,a):s>=3?new t(e,n,i):s>=2?new t(e,n):s>=1?new t(e instanceof t?+e:e):new t}else{c=t.apply(this,arguments)}if(!J(c)){P(c,{constructor:r},true)}return c};var e=new RegExp(\"^\"+\"(\\\\d{4}|[+-]\\\\d{6})\"+\"(?:-(\\\\d{2})\"+\"(?:-(\\\\d{2})\"+\"(?:\"+\"T(\\\\d{2})\"+\":(\\\\d{2})\"+\"(?:\"+\":(\\\\d{2})\"+\"(?:(\\\\.\\\\d{1,}))?\"+\")?\"+\"(\"+\"Z|\"+\"(?:\"+\"([-+])\"+\"(\\\\d{2})\"+\":(\\\\d{2})\"+\")\"+\")?)?)?)?\"+\"$\");var n=[0,31,59,90,120,151,181,212,243,273,304,334,365];var i=function dayFromMonth(t,r){var e=r>1?1:0;return n[r]+Math.floor((t-1969+e)/4)-Math.floor((t-1901+e)/100)+Math.floor((t-1601+e)/400)+365*(t-1970)};var a=function toUTC(r){var e=0;var n=r;if(dr&&n>yr){var i=Math.floor(n/yr)*yr;var a=Math.floor(i/1e3);e+=a;n-=a*1e3}return u(new t(1970,0,1,0,0,e,n))};for(var f in t){if(G(t,f)){r[f]=t[f]}}P(r,{now:t.now,UTC:t.UTC},true);r.prototype=t.prototype;P(r.prototype,{constructor:r},true);var l=function parse(r){var n=e.exec(r);if(n){var o=u(n[1]),f=u(n[2]||1)-1,l=u(n[3]||1)-1,s=u(n[4]||0),c=u(n[5]||0),v=u(n[6]||0),h=Math.floor(u(n[7]||0)*1e3),p=Boolean(n[4]&&!n[8]),y=n[9]===\"-\"?1:-1,d=u(n[10]||0),g=u(n[11]||0),w;var b=c>0||v>0||h>0;if(s<(b?24:25)&&c<60&&v<60&&h<1e3&&f>-1&&f<12&&d<24&&g<60&&l>-1&&l<i(o,f+1)-i(o,f)){w=((i(o,f)+l)*24+s+d*y)*60;w=((w+c+g*y)*60+v)*1e3+h;if(p){w=a(w)}if(-864e13<=w&&w<=864e13){return w}}return NaN}return t.parse.apply(this,arguments)};P(r,{parse:l});return r}(Date)}if(!Date.now){Date.now=function now(){return(new Date).getTime()}}var gr=l.toFixed&&(8e-5.toFixed(3)!==\"0.000\"||.9.toFixed(0)!==\"1\"||1.255.toFixed(2)!==\"1.25\"||(1000000000000000128).toFixed(0)!==\"1000000000000000128\");var wr={base:1e7,size:6,data:[0,0,0,0,0,0],multiply:function multiply(t,r){var e=-1;var n=r;while(++e<wr.size){n+=t*wr.data[e];wr.data[e]=n%wr.base;n=Math.floor(n/wr.base)}},divide:function divide(t){var r=wr.size;var e=0;while(--r>=0){e+=wr.data[r];wr.data[r]=Math.floor(e/t);e=e%t*wr.base}},numToString:function numToString(){var t=wr.size;var r=\"\";while(--t>=0){if(r!==\"\"||t===0||wr.data[t]!==0){var e=o(wr.data[t]);if(r===\"\"){r=e}else{r+=K(\"0000000\",0,7-e.length)+e}}}return r},pow:function pow(t,r,e){return r===0?e:r%2===1?pow(t,r-1,e*t):pow(t*t,r/2,e)},log:function log(t){var r=0;var e=t;while(e>=4096){r+=12;e/=4096}while(e>=2){r+=1;e/=2}return r}};var br=function toFixed(t){var r,e,n,i,a,f,l,s;r=u(t);r=Y(r)?0:Math.floor(r);if(r<0||r>20){throw new RangeError(\"Number.toFixed called with invalid number of decimals\")}e=u(this);if(Y(e)){return\"NaN\"}if(e<=-1e21||e>=1e21){return o(e)}n=\"\";if(e<0){n=\"-\";e=-e}i=\"0\";if(e>1e-21){a=wr.log(e*wr.pow(2,69,1))-69;f=a<0?e*wr.pow(2,-a,1):e/wr.pow(2,a,1);f*=4503599627370496;a=52-a;if(a>0){wr.multiply(0,f);l=r;while(l>=7){wr.multiply(1e7,0);l-=7}wr.multiply(wr.pow(10,l,1),0);l=a-1;while(l>=23){wr.divide(1<<23);l-=23}wr.divide(1<<l);wr.multiply(1,1);wr.divide(2);i=wr.numToString()}else{wr.multiply(0,f);wr.multiply(1<<-a,0);i=wr.numToString()+K(\"0.00000000000000000000\",2,2+r)}}if(r>0){s=i.length;if(s<=r){i=n+K(\"0.0000000000000000000\",0,r-s+2)+i}else{i=n+K(i,0,s-r)+\".\"+K(i,s-r)}}else{i=n+i}return i};P(l,{toFixed:br},gr);var Tr=function(){try{return 1..toPrecision(undefined)===\"1\"}catch(t){return true}}();var mr=l.toPrecision;P(l,{toPrecision:function toPrecision(t){return typeof t===\"undefined\"?mr.call(this):mr.call(this,t)}},Tr);if(\"ab\".split(/(?:ab)*/).length!==2||\".\".split(/(.?)(.?)/).length!==4||\"tesst\".split(/(s)*/)[1]===\"t\"||\"test\".split(/(?:)/,-1).length!==4||\"\".split(/.?/).length||\".\".split(/()()/).length>1){(function(){var t=typeof/()??/.exec(\"\")[1]===\"undefined\";var r=Math.pow(2,32)-1;f.split=function(e,n){var i=String(this);if(typeof e===\"undefined\"&&n===0){return[]}if(!M(e)){return Q(this,e,n)}var a=[];var o=(e.ignoreCase?\"i\":\"\")+(e.multiline?\"m\":\"\")+(e.unicode?\"u\":\"\")+(e.sticky?\"y\":\"\"),f=0,u,l,s,c;var h=new RegExp(e.source,o+\"g\");if(!t){u=new RegExp(\"^\"+h.source+\"$(?!\\\\s)\",o)}var p=typeof n===\"undefined\"?r:z.ToUint32(n);l=h.exec(i);while(l){s=l.index+l[0].length;if(s>f){_(a,K(i,f,l.index));if(!t&&l.length>1){l[0].replace(u,function(){for(var t=1;t<arguments.length-2;t++){if(typeof arguments[t]===\"undefined\"){l[t]=void 0}}})}if(l.length>1&&l.index<i.length){v.apply(a,W(l,1))}c=l[0].length;f=s;if(a.length>=p){break}}if(h.lastIndex===l.index){h.lastIndex++}l=h.exec(i)}if(f===i.length){if(c||!h.test(\"\")){_(a,\"\")}}else{_(a,K(i,f))}return a.length>p?W(a,0,p):a}})()}else if(\"0\".split(void 0,0).length){f.split=function split(t,r){if(typeof t===\"undefined\"&&r===0){return[]}return Q(this,t,r)}}var Dr=f.replace;var Sr=function(){var t=[];\"x\".replace(/x(.)?/g,function(r,e){_(t,e)});return t.length===1&&typeof t[0]===\"undefined\"}();if(!Sr){f.replace=function replace(t,r){var e=D(r);var n=M(t)&&/\\)[*?]/.test(t.source);if(!e||!n){return Dr.call(this,t,r)}else{var i=function(e){var n=arguments.length;var i=t.lastIndex;t.lastIndex=0;var a=t.exec(e)||[];t.lastIndex=i;_(a,arguments[n-2],arguments[n-1]);return r.apply(this,a)};return Dr.call(this,t,i)}}}var xr=f.substr;var Or=\"\".substr&&\"0b\".substr(-1)!==\"b\";P(f,{substr:function substr(t,r){var e=t;if(t<0){e=w(this.length+t,0)}return xr.call(this,e,r)}},Or);var Er=\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\"+\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\"+\"\\u2029\\ufeff\";var jr=\"\\u200b\";var Ir=\"[\"+Er+\"]\";var Mr=new RegExp(\"^\"+Ir+Ir+\"*\");var Ur=new RegExp(Ir+Ir+\"*$\");var $r=f.trim&&(Er.trim()||!jr.trim());P(f,{trim:function trim(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}return o(this).replace(Mr,\"\").replace(Ur,\"\")}},$r);var Fr=d.bind(String.prototype.trim);var Nr=f.lastIndexOf&&\"abc\\u3042\\u3044\".lastIndexOf(\"\\u3042\\u3044\",2)!==-1;P(f,{lastIndexOf:function lastIndexOf(t){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var r=o(this);var e=o(t);var n=arguments.length>1?u(arguments[1]):NaN;var i=Y(n)?Infinity:z.ToInteger(n);var a=b(w(i,0),r.length);var f=e.length;var l=a+f;while(l>0){l=w(0,l-f);var s=V(K(r,l,a+f),e);if(s!==-1){return l+s}}return-1}},Nr);var Cr=f.lastIndexOf;P(f,{lastIndexOf:function lastIndexOf(t){return Cr.apply(this,arguments)}},f.lastIndexOf.length!==1);if(parseInt(Er+\"08\")!==8||parseInt(Er+\"0x16\")!==22){parseInt=function(t){var r=/^[-+]?0[xX]/;return function parseInt(e,n){if(typeof e===\"symbol\"){\"\"+e}var i=Fr(String(e));var a=u(n)||(r.test(i)?16:10);return t(i,a)}}(parseInt)}if(1/parseFloat(\"-0\")!==-Infinity){parseFloat=function(t){return function parseFloat(r){var e=Fr(String(r));var n=t(e);return n===0&&K(e,0,1)===\"-\"?-0:n}}(parseFloat)}if(String(new RangeError(\"test\"))!==\"RangeError: test\"){var kr=function toString(){if(typeof this===\"undefined\"||this===null){throw new TypeError(\"can't convert \"+this+\" to object\")}var t=this.name;if(typeof t===\"undefined\"){t=\"Error\"}else if(typeof t!==\"string\"){t=o(t)}var r=this.message;if(typeof r===\"undefined\"){r=\"\"}else if(typeof r!==\"string\"){r=o(r)}if(!t){return r}if(!r){return t}return t+\": \"+r};Error.prototype.toString=kr}if(R){var Ar=function(t,r){if(tt(t,r)){var e=Object.getOwnPropertyDescriptor(t,r);if(e.configurable){e.enumerable=false;Object.defineProperty(t,r,e)}}};Ar(Error.prototype,\"message\");if(Error.prototype.message!==\"\"){Error.prototype.message=\"\"}Ar(Error.prototype,\"name\")}if(String(/a/gim)!==\"/a/gim\"){var Rr=function toString(){var t=\"/\"+this.source+\"/\";if(this.global){t+=\"g\"}if(this.ignoreCase){t+=\"i\"}if(this.multiline){t+=\"m\"}return t};RegExp.prototype.toString=Rr}});\n/*!\n * https://github.com/paulmillr/es6-shim\n * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)\n *   and contributors,  MIT License\n * es6-shim: v0.35.4\n * see https://github.com/paulmillr/es6-shim/blob/0.35.4/LICENSE\n * Details and documentation:\n * https://github.com/paulmillr/es6-shim/\n */\n(function(e,t){if(typeof define===\"function\"&&define.amd){define(t)}else if(typeof exports===\"object\"){module.exports=t()}else{e.returnExports=t()}})(this,function(){\"use strict\";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=Object.keys;var o=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var i=function(e){try{e();return false}catch(t){return true}};var a=function valueOrFalseIfThrows(e){try{return e()}catch(t){return false}};var u=o(i);var f=function(){return!i(function(){return Object.defineProperty({},\"x\",{get:function(){}})})};var s=!!Object.defineProperty&&f();var c=function foo(){}.name===\"foo\";var l=Function.call.bind(Array.prototype.forEach);var p=Function.call.bind(Array.prototype.reduce);var v=Function.call.bind(Array.prototype.filter);var y=Function.call.bind(Array.prototype.some);var h=function(e,t,r,n){if(!n&&t in e){return}if(s){Object.defineProperty(e,t,{configurable:true,enumerable:false,writable:true,value:r})}else{e[t]=r}};var b=function(e,t,r){l(n(t),function(n){var o=t[n];h(e,n,o,!!r)})};var g=Function.call.bind(Object.prototype.toString);var d=typeof/abc/===\"function\"?function IsCallableSlow(e){return typeof e===\"function\"&&g(e)===\"[object Function]\"}:function IsCallableFast(e){return typeof e===\"function\"};var m={getter:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}Object.defineProperty(e,t,{configurable:true,enumerable:false,get:r})},proxy:function(e,t,r){if(!s){throw new TypeError(\"getters require true ES5 support\")}var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,{configurable:n.configurable,enumerable:n.enumerable,get:function getKey(){return e[t]},set:function setKey(r){e[t]=r}})},redefine:function(e,t,r){if(s){var n=Object.getOwnPropertyDescriptor(e,t);n.value=r;Object.defineProperty(e,t,n)}else{e[t]=r}},defineByDescriptor:function(e,t,r){if(s){Object.defineProperty(e,t,r)}else if(\"value\"in r){e[t]=r.value}},preserveToString:function(e,t){if(t&&d(t.toString)){h(e,\"toString\",t.toString.bind(t),true)}}};var O=Object.create||function(e,t){var r=function Prototype(){};r.prototype=e;var o=new r;if(typeof t!==\"undefined\"){n(t).forEach(function(e){m.defineByDescriptor(o,e,t[e])})}return o};var w=function(e,t){if(!Object.setPrototypeOf){return false}return a(function(){var r=function Subclass(t){var r=new e(t);Object.setPrototypeOf(r,Subclass.prototype);return r};Object.setPrototypeOf(r,e);r.prototype=O(e.prototype,{constructor:{value:r}});return t(r)})};var j=function(){if(typeof self!==\"undefined\"){return self}if(typeof window!==\"undefined\"){return window}if(typeof global!==\"undefined\"){return global}throw new Error(\"unable to locate global object\")};var S=j();var T=S.isFinite;var I=Function.call.bind(String.prototype.indexOf);var E=Function.apply.bind(Array.prototype.indexOf);var P=Function.call.bind(Array.prototype.concat);var C=Function.call.bind(String.prototype.slice);var M=Function.call.bind(Array.prototype.push);var x=Function.apply.bind(Array.prototype.push);var N=Function.call.bind(Array.prototype.shift);var A=Math.max;var R=Math.min;var _=Math.floor;var k=Math.abs;var L=Math.exp;var F=Math.log;var D=Math.sqrt;var z=Function.call.bind(Object.prototype.hasOwnProperty);var q;var W=function(){};var G=S.Map;var H=G&&G.prototype[\"delete\"];var V=G&&G.prototype.get;var B=G&&G.prototype.has;var U=G&&G.prototype.set;var $=S.Symbol||{};var J=$.species||\"@@species\";var X=Number.isNaN||function isNaN(e){return e!==e};var K=Number.isFinite||function isFinite(e){return typeof e===\"number\"&&T(e)};var Z=d(Math.sign)?Math.sign:function sign(e){var t=Number(e);if(t===0){return t}if(X(t)){return t}return t<0?-1:1};var Y=function log1p(e){var t=Number(e);if(t<-1||X(t)){return NaN}if(t===0||t===Infinity){return t}if(t===-1){return-Infinity}return 1+t-1===0?t:t*(F(1+t)/(1+t-1))};var Q=function isArguments(e){return g(e)===\"[object Arguments]\"};var ee=function isArguments(e){return e!==null&&typeof e===\"object\"&&typeof e.length===\"number\"&&e.length>=0&&g(e)!==\"[object Array]\"&&g(e.callee)===\"[object Function]\"};var te=Q(arguments)?Q:ee;var re={primitive:function(e){return e===null||typeof e!==\"function\"&&typeof e!==\"object\"},string:function(e){return g(e)===\"[object String]\"},regex:function(e){return g(e)===\"[object RegExp]\"},symbol:function(e){return typeof S.Symbol===\"function\"&&typeof e===\"symbol\"}};var ne=function overrideNative(e,t,r){var n=e[t];h(e,t,r,true);m.preserveToString(e[t],n)};var oe=typeof $===\"function\"&&typeof $[\"for\"]===\"function\"&&re.symbol($());var ie=re.symbol($.iterator)?$.iterator:\"_es6-shim iterator_\";if(S.Set&&typeof(new S.Set)[\"@@iterator\"]===\"function\"){ie=\"@@iterator\"}if(!S.Reflect){h(S,\"Reflect\",{},true)}var ae=S.Reflect;var ue=String;var fe=typeof document===\"undefined\"||!document?null:document.all;var se=fe==null?function isNullOrUndefined(e){return e==null}:function isNullOrUndefinedAndNotDocumentAll(e){return e==null&&e!==fe};var ce={Call:function Call(t,r){var n=arguments.length>2?arguments[2]:[];if(!ce.IsCallable(t)){throw new TypeError(t+\" is not a function\")}return e(t,r,n)},RequireObjectCoercible:function(e,t){if(se(e)){throw new TypeError(t||\"Cannot call method on \"+e)}return e},TypeIsObject:function(e){if(e===void 0||e===null||e===true||e===false){return false}return typeof e===\"function\"||typeof e===\"object\"||e===fe},ToObject:function(e,t){return Object(ce.RequireObjectCoercible(e,t))},IsCallable:d,IsConstructor:function(e){return ce.IsCallable(e)},ToInt32:function(e){return ce.ToNumber(e)>>0},ToUint32:function(e){return ce.ToNumber(e)>>>0},ToNumber:function(e){if(g(e)===\"[object Symbol]\"){throw new TypeError(\"Cannot convert a Symbol value to a number\")}return+e},ToInteger:function(e){var t=ce.ToNumber(e);if(X(t)){return 0}if(t===0||!K(t)){return t}return(t>0?1:-1)*_(k(t))},ToLength:function(e){var t=ce.ToInteger(e);if(t<=0){return 0}if(t>Number.MAX_SAFE_INTEGER){return Number.MAX_SAFE_INTEGER}return t},SameValue:function(e,t){if(e===t){if(e===0){return 1/e===1/t}return true}return X(e)&&X(t)},SameValueZero:function(e,t){return e===t||X(e)&&X(t)},IsIterable:function(e){return ce.TypeIsObject(e)&&(typeof e[ie]!==\"undefined\"||te(e))},GetIterator:function(e){if(te(e)){return new q(e,\"value\")}var t=ce.GetMethod(e,ie);if(!ce.IsCallable(t)){throw new TypeError(\"value is not an iterable\")}var r=ce.Call(t,e);if(!ce.TypeIsObject(r)){throw new TypeError(\"bad iterator\")}return r},GetMethod:function(e,t){var r=ce.ToObject(e)[t];if(se(r)){return void 0}if(!ce.IsCallable(r)){throw new TypeError(\"Method not callable: \"+t)}return r},IteratorComplete:function(e){return!!e.done},IteratorClose:function(e,t){var r=ce.GetMethod(e,\"return\");if(r===void 0){return}var n,o;try{n=ce.Call(r,e)}catch(i){o=i}if(t){return}if(o){throw o}if(!ce.TypeIsObject(n)){throw new TypeError(\"Iterator's return method returned a non-object.\")}},IteratorNext:function(e){var t=arguments.length>1?e.next(arguments[1]):e.next();if(!ce.TypeIsObject(t)){throw new TypeError(\"bad iterator\")}return t},IteratorStep:function(e){var t=ce.IteratorNext(e);var r=ce.IteratorComplete(t);return r?false:t},Construct:function(e,t,r,n){var o=typeof r===\"undefined\"?e:r;if(!n&&ae.construct){return ae.construct(e,t,o)}var i=o.prototype;if(!ce.TypeIsObject(i)){i=Object.prototype}var a=O(i);var u=ce.Call(e,a,t);return ce.TypeIsObject(u)?u:a},SpeciesConstructor:function(e,t){var r=e.constructor;if(r===void 0){return t}if(!ce.TypeIsObject(r)){throw new TypeError(\"Bad constructor\")}var n=r[J];if(se(n)){return t}if(!ce.IsConstructor(n)){throw new TypeError(\"Bad @@species\")}return n},CreateHTML:function(e,t,r,n){var o=ce.ToString(e);var i=\"<\"+t;if(r!==\"\"){var a=ce.ToString(n);var u=a.replace(/\"/g,\"&quot;\");i+=\" \"+r+'=\"'+u+'\"'}var f=i+\">\";var s=f+o;return s+\"</\"+t+\">\"},IsRegExp:function IsRegExp(e){if(!ce.TypeIsObject(e)){return false}var t=e[$.match];if(typeof t!==\"undefined\"){return!!t}return re.regex(e)},ToString:function ToString(e){return ue(e)}};if(s&&oe){var le=function defineWellKnownSymbol(e){if(re.symbol($[e])){return $[e]}var t=$[\"for\"](\"Symbol.\"+e);Object.defineProperty($,e,{configurable:false,enumerable:false,writable:false,value:t});return t};if(!re.symbol($.search)){var pe=le(\"search\");var ve=String.prototype.search;h(RegExp.prototype,pe,function search(e){return ce.Call(ve,e,[this])});var ye=function search(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,pe);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(ve,t,[ce.ToString(e)])};ne(String.prototype,\"search\",ye)}if(!re.symbol($.replace)){var he=le(\"replace\");var be=String.prototype.replace;h(RegExp.prototype,he,function replace(e,t){return ce.Call(be,e,[this,t])});var ge=function replace(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,he);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(be,r,[ce.ToString(e),t])};ne(String.prototype,\"replace\",ge)}if(!re.symbol($.split)){var de=le(\"split\");var me=String.prototype.split;h(RegExp.prototype,de,function split(e,t){return ce.Call(me,e,[this,t])});var Oe=function split(e,t){var r=ce.RequireObjectCoercible(this);if(!se(e)){var n=ce.GetMethod(e,de);if(typeof n!==\"undefined\"){return ce.Call(n,e,[r,t])}}return ce.Call(me,r,[ce.ToString(e),t])};ne(String.prototype,\"split\",Oe)}var we=re.symbol($.match);var je=we&&function(){var e={};e[$.match]=function(){return 42};return\"a\".match(e)!==42}();if(!we||je){var Se=le(\"match\");var Te=String.prototype.match;h(RegExp.prototype,Se,function match(e){return ce.Call(Te,e,[this])});var Ie=function match(e){var t=ce.RequireObjectCoercible(this);if(!se(e)){var r=ce.GetMethod(e,Se);if(typeof r!==\"undefined\"){return ce.Call(r,e,[t])}}return ce.Call(Te,t,[ce.ToString(e)])};ne(String.prototype,\"match\",Ie)}}var Ee=function wrapConstructor(e,t,r){m.preserveToString(t,e);if(Object.setPrototypeOf){Object.setPrototypeOf(e,t)}if(s){l(Object.getOwnPropertyNames(e),function(n){if(n in W||r[n]){return}m.proxy(e,n,t)})}else{l(Object.keys(e),function(n){if(n in W||r[n]){return}t[n]=e[n]})}t.prototype=e.prototype;m.redefine(e.prototype,\"constructor\",t)};var Pe=function(){return this};var Ce=function(e){if(s&&!z(e,J)){m.getter(e,J,Pe)}};var Me=function(e,t){var r=t||function iterator(){return this};h(e,ie,r);if(!e[ie]&&re.symbol(ie)){e[ie]=r}};var xe=function createDataProperty(e,t,r){if(s){Object.defineProperty(e,t,{configurable:true,enumerable:true,writable:true,value:r})}else{e[t]=r}};var Ne=function createDataPropertyOrThrow(e,t,r){xe(e,t,r);if(!ce.SameValue(e[t],r)){throw new TypeError(\"property is nonconfigurable\")}};var Ae=function(e,t,r,n){if(!ce.TypeIsObject(e)){throw new TypeError(\"Constructor requires `new`: \"+t.name)}var o=t.prototype;if(!ce.TypeIsObject(o)){o=r}var i=O(o);for(var a in n){if(z(n,a)){var u=n[a];h(i,a,u,true)}}return i};if(String.fromCodePoint&&String.fromCodePoint.length!==1){var Re=String.fromCodePoint;ne(String,\"fromCodePoint\",function fromCodePoint(e){return ce.Call(Re,this,arguments)})}var _e={fromCodePoint:function fromCodePoint(e){var t=[];var r;for(var n=0,o=arguments.length;n<o;n++){r=Number(arguments[n]);if(!ce.SameValue(r,ce.ToInteger(r))||r<0||r>1114111){throw new RangeError(\"Invalid code point \"+r)}if(r<65536){M(t,String.fromCharCode(r))}else{r-=65536;M(t,String.fromCharCode((r>>10)+55296));M(t,String.fromCharCode(r%1024+56320))}}return t.join(\"\")},raw:function raw(e){var t=ce.ToObject(e,\"bad callSite\");var r=ce.ToObject(t.raw,\"bad raw value\");var n=r.length;var o=ce.ToLength(n);if(o<=0){return\"\"}var i=[];var a=0;var u,f,s,c;while(a<o){u=ce.ToString(a);s=ce.ToString(r[u]);M(i,s);if(a+1>=o){break}f=a+1<arguments.length?arguments[a+1]:\"\";c=ce.ToString(f);M(i,c);a+=1}return i.join(\"\")}};if(String.raw&&String.raw({raw:{0:\"x\",1:\"y\",length:2}})!==\"xy\"){ne(String,\"raw\",_e.raw)}b(String,_e);var ke=function repeat(e,t){if(t<1){return\"\"}if(t%2){return repeat(e,t-1)+e}var r=repeat(e,t/2);return r+r};var Le=Infinity;var Fe={repeat:function repeat(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);if(r<0||r>=Le){throw new RangeError(\"repeat count must be less than infinity and not overflow maximum string size\")}return ke(t,r)},startsWith:function startsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"startsWith\" with a regex')}var r=ce.ToString(e);var n;if(arguments.length>1){n=arguments[1]}var o=A(ce.ToInteger(n),0);return C(t,o,o+r.length)===r},endsWith:function endsWith(e){var t=ce.ToString(ce.RequireObjectCoercible(this));if(ce.IsRegExp(e)){throw new TypeError('Cannot call method \"endsWith\" with a regex')}var r=ce.ToString(e);var n=t.length;var o;if(arguments.length>1){o=arguments[1]}var i=typeof o===\"undefined\"?n:ce.ToInteger(o);var a=R(A(i,0),n);return C(t,a-r.length,a)===r},includes:function includes(e){if(ce.IsRegExp(e)){throw new TypeError('\"includes\" does not accept a RegExp')}var t=ce.ToString(e);var r;if(arguments.length>1){r=arguments[1]}return I(this,t,r)!==-1},codePointAt:function codePointAt(e){var t=ce.ToString(ce.RequireObjectCoercible(this));var r=ce.ToInteger(e);var n=t.length;if(r>=0&&r<n){var o=t.charCodeAt(r);var i=r+1===n;if(o<55296||o>56319||i){return o}var a=t.charCodeAt(r+1);if(a<56320||a>57343){return o}return(o-55296)*1024+(a-56320)+65536}}};if(String.prototype.includes&&\"a\".includes(\"a\",Infinity)!==false){ne(String.prototype,\"includes\",Fe.includes)}if(String.prototype.startsWith&&String.prototype.endsWith){var De=i(function(){return\"/a/\".startsWith(/a/)});var ze=a(function(){return\"abc\".startsWith(\"a\",Infinity)===false});if(!De||!ze){ne(String.prototype,\"startsWith\",Fe.startsWith);ne(String.prototype,\"endsWith\",Fe.endsWith)}}if(oe){var qe=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".startsWith(e)});if(!qe){ne(String.prototype,\"startsWith\",Fe.startsWith)}var We=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".endsWith(e)});if(!We){ne(String.prototype,\"endsWith\",Fe.endsWith)}var Ge=a(function(){var e=/a/;e[$.match]=false;return\"/a/\".includes(e)});if(!Ge){ne(String.prototype,\"includes\",Fe.includes)}}b(String.prototype,Fe);var He=[\"\\t\\n\\x0B\\f\\r \\xa0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\",\"\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\",\"\\u2029\\ufeff\"].join(\"\");var Ve=new RegExp(\"(^[\"+He+\"]+)|([\"+He+\"]+$)\",\"g\");var Be=function trim(){return ce.ToString(ce.RequireObjectCoercible(this)).replace(Ve,\"\")};var Ue=[\"\\x85\",\"\\u200b\",\"\\ufffe\"].join(\"\");var $e=new RegExp(\"[\"+Ue+\"]\",\"g\");var Je=/^[-+]0x[0-9a-f]+$/i;var Xe=Ue.trim().length!==Ue.length;h(String.prototype,\"trim\",Be,Xe);var Ke=function(e){return{value:e,done:arguments.length===0}};var Ze=function(e){ce.RequireObjectCoercible(e);this._s=ce.ToString(e);this._i=0};Ze.prototype.next=function(){var e=this._s;var t=this._i;if(typeof e===\"undefined\"||t>=e.length){this._s=void 0;return Ke()}var r=e.charCodeAt(t);var n,o;if(r<55296||r>56319||t+1===e.length){o=1}else{n=e.charCodeAt(t+1);o=n<56320||n>57343?1:2}this._i=t+o;return Ke(e.substr(t,o))};Me(Ze.prototype);Me(String.prototype,function(){return new Ze(this)});var Ye={from:function from(e){var r=this;var n;if(arguments.length>1){n=arguments[1]}var o,i;if(typeof n===\"undefined\"){o=false}else{if(!ce.IsCallable(n)){throw new TypeError(\"Array.from: when provided, the second argument must be a function\")}if(arguments.length>2){i=arguments[2]}o=true}var a=typeof(te(e)||ce.GetMethod(e,ie))!==\"undefined\";var u,f,s;if(a){f=ce.IsConstructor(r)?Object(new r):[];var c=ce.GetIterator(e);var l,p;s=0;while(true){l=ce.IteratorStep(c);if(l===false){break}p=l.value;try{if(o){p=typeof i===\"undefined\"?n(p,s):t(n,i,p,s)}f[s]=p}catch(v){ce.IteratorClose(c,true);throw v}s+=1}u=s}else{var y=ce.ToObject(e);u=ce.ToLength(y.length);f=ce.IsConstructor(r)?Object(new r(u)):new Array(u);var h;for(s=0;s<u;++s){h=y[s];if(o){h=typeof i===\"undefined\"?n(h,s):t(n,i,h,s)}Ne(f,s,h)}}f.length=u;return f},of:function of(){var e=arguments.length;var t=this;var n=r(t)||!ce.IsCallable(t)?new Array(e):ce.Construct(t,[e]);for(var o=0;o<e;++o){Ne(n,o,arguments[o])}n.length=e;return n}};b(Array,Ye);Ce(Array);q=function(e,t){this.i=0;this.array=e;this.kind=t};b(q.prototype,{next:function(){var e=this.i;var t=this.array;if(!(this instanceof q)){throw new TypeError(\"Not an ArrayIterator\")}if(typeof t!==\"undefined\"){var r=ce.ToLength(t.length);for(;e<r;e++){var n=this.kind;var o;if(n===\"key\"){o=e}else if(n===\"value\"){o=t[e]}else if(n===\"entry\"){o=[e,t[e]]}this.i=e+1;return Ke(o)}}this.array=void 0;return Ke()}});Me(q.prototype);var Qe=Array.of===Ye.of||function(){var e=function Foo(e){this.length=e};e.prototype=[];var t=Array.of.apply(e,[1,2]);return t instanceof e&&t.length===2}();if(!Qe){ne(Array,\"of\",Ye.of)}var et={copyWithin:function copyWithin(e,t){var r=ce.ToObject(this);var n=ce.ToLength(r.length);var o=ce.ToInteger(e);var i=ce.ToInteger(t);var a=o<0?A(n+o,0):R(o,n);var u=i<0?A(n+i,0):R(i,n);var f;if(arguments.length>2){f=arguments[2]}var s=typeof f===\"undefined\"?n:ce.ToInteger(f);var c=s<0?A(n+s,0):R(s,n);var l=R(c-u,n-a);var p=1;if(u<a&&a<u+l){p=-1;u+=l-1;a+=l-1}while(l>0){if(u in r){r[a]=r[u]}else{delete r[a]}u+=p;a+=p;l-=1}return r},fill:function fill(e){var t;if(arguments.length>1){t=arguments[1]}var r;if(arguments.length>2){r=arguments[2]}var n=ce.ToObject(this);var o=ce.ToLength(n.length);t=ce.ToInteger(typeof t===\"undefined\"?0:t);r=ce.ToInteger(typeof r===\"undefined\"?o:r);var i=t<0?A(o+t,0):R(t,o);var a=r<0?o+r:r;for(var u=i;u<o&&u<a;++u){n[u]=e}return n},find:function find(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#find: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0,a;i<n;i++){a=r[i];if(o){if(t(e,o,a,i,r)){return a}}else if(e(a,i,r)){return a}}},findIndex:function findIndex(e){var r=ce.ToObject(this);var n=ce.ToLength(r.length);if(!ce.IsCallable(e)){throw new TypeError(\"Array#findIndex: predicate must be a function\")}var o=arguments.length>1?arguments[1]:null;for(var i=0;i<n;i++){if(o){if(t(e,o,r[i],i,r)){return i}}else if(e(r[i],i,r)){return i}}return-1},keys:function keys(){return new q(this,\"key\")},values:function values(){return new q(this,\"value\")},entries:function entries(){return new q(this,\"entry\")}};if(Array.prototype.keys&&!ce.IsCallable([1].keys().next)){delete Array.prototype.keys}if(Array.prototype.entries&&!ce.IsCallable([1].entries().next)){delete Array.prototype.entries}if(Array.prototype.keys&&Array.prototype.entries&&!Array.prototype.values&&Array.prototype[ie]){b(Array.prototype,{values:Array.prototype[ie]});if(re.symbol($.unscopables)){Array.prototype[$.unscopables].values=true}}if(c&&Array.prototype.values&&Array.prototype.values.name!==\"values\"){var tt=Array.prototype.values;ne(Array.prototype,\"values\",function values(){return ce.Call(tt,this,arguments)});h(Array.prototype,ie,Array.prototype.values,true)}b(Array.prototype,et);if(1/[true].indexOf(true,-0)<0){h(Array.prototype,\"indexOf\",function indexOf(e){var t=E(this,arguments);if(t===0&&1/t<0){return 0}return t},true)}Me(Array.prototype,function(){return this.values()});if(Object.getPrototypeOf){Me(Object.getPrototypeOf([].values()))}var rt=function(){return a(function(){return Array.from({length:-1}).length===0})}();var nt=function(){var e=Array.from([0].entries());return e.length===1&&r(e[0])&&e[0][0]===0&&e[0][1]===0}();if(!rt||!nt){ne(Array,\"from\",Ye.from)}var ot=function(){return a(function(){return Array.from([0],void 0)})}();if(!ot){var it=Array.from;ne(Array,\"from\",function from(e){if(arguments.length>1&&typeof arguments[1]!==\"undefined\"){return ce.Call(it,this,arguments)}else{return t(it,this,e)}})}var at=-(Math.pow(2,32)-1);var ut=function(e,r){var n={length:at};n[r?(n.length>>>0)-1:0]=true;return a(function(){t(e,n,function(){throw new RangeError(\"should not reach here\")},[]);return true})};if(!ut(Array.prototype.forEach)){var ft=Array.prototype.forEach;ne(Array.prototype,\"forEach\",function forEach(e){return ce.Call(ft,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.map)){var st=Array.prototype.map;ne(Array.prototype,\"map\",function map(e){return ce.Call(st,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.filter)){var ct=Array.prototype.filter;ne(Array.prototype,\"filter\",function filter(e){return ce.Call(ct,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.some)){var lt=Array.prototype.some;ne(Array.prototype,\"some\",function some(e){return ce.Call(lt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.every)){var pt=Array.prototype.every;ne(Array.prototype,\"every\",function every(e){return ce.Call(pt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduce)){var vt=Array.prototype.reduce;ne(Array.prototype,\"reduce\",function reduce(e){return ce.Call(vt,this.length>=0?this:[],arguments)},true)}if(!ut(Array.prototype.reduceRight,true)){var yt=Array.prototype.reduceRight;ne(Array.prototype,\"reduceRight\",function reduceRight(e){return ce.Call(yt,this.length>=0?this:[],arguments)},true)}var ht=Number(\"0o10\")!==8;var bt=Number(\"0b10\")!==2;var gt=y(Ue,function(e){return Number(e+0+e)===0});if(ht||bt||gt){var dt=Number;var mt=/^0b[01]+$/i;var Ot=/^0o[0-7]+$/i;var wt=mt.test.bind(mt);var jt=Ot.test.bind(Ot);var St=function(e){var t;if(typeof e.valueOf===\"function\"){t=e.valueOf();if(re.primitive(t)){return t}}if(typeof e.toString===\"function\"){t=e.toString();if(re.primitive(t)){return t}}throw new TypeError(\"No default value\")};var Tt=$e.test.bind($e);var It=Je.test.bind(Je);var Et=function(){var e=function Number(t){var r;if(arguments.length>0){r=re.primitive(t)?t:St(t,\"number\")}else{r=0}if(typeof r===\"string\"){r=ce.Call(Be,r);if(wt(r)){r=parseInt(C(r,2),2)}else if(jt(r)){r=parseInt(C(r,2),8)}else if(Tt(r)||It(r)){r=NaN}}var n=this;var o=a(function(){dt.prototype.valueOf.call(n);return true});if(n instanceof e&&!o){return new dt(r)}return dt(r)};return e}();Ee(dt,Et,{});b(Et,{NaN:dt.NaN,MAX_VALUE:dt.MAX_VALUE,MIN_VALUE:dt.MIN_VALUE,NEGATIVE_INFINITY:dt.NEGATIVE_INFINITY,POSITIVE_INFINITY:dt.POSITIVE_INFINITY});Number=Et;m.redefine(S,\"Number\",Et)}var Pt=Math.pow(2,53)-1;b(Number,{MAX_SAFE_INTEGER:Pt,MIN_SAFE_INTEGER:-Pt,EPSILON:2.220446049250313e-16,parseInt:S.parseInt,parseFloat:S.parseFloat,isFinite:K,isInteger:function isInteger(e){return K(e)&&ce.ToInteger(e)===e},isSafeInteger:function isSafeInteger(e){return Number.isInteger(e)&&k(e)<=Number.MAX_SAFE_INTEGER},isNaN:X});h(Number,\"parseInt\",S.parseInt,Number.parseInt!==S.parseInt);if([,1].find(function(){return true})===1){ne(Array.prototype,\"find\",et.find)}if([,1].findIndex(function(){return true})!==0){ne(Array.prototype,\"findIndex\",et.findIndex)}var Ct=Function.bind.call(Function.bind,Object.prototype.propertyIsEnumerable);var Mt=function ensureEnumerable(e,t){if(s&&Ct(e,t)){Object.defineProperty(e,t,{enumerable:false})}};var xt=function sliceArgs(){var e=Number(this);var t=arguments.length;var r=t-e;var n=new Array(r<0?0:r);for(var o=e;o<t;++o){n[o-e]=arguments[o]}return n};var Nt=function assignTo(e){return function assignToSource(t,r){t[r]=e[r];return t}};var At=function(e,t){var r=n(Object(t));var o;if(ce.IsCallable(Object.getOwnPropertySymbols)){o=v(Object.getOwnPropertySymbols(Object(t)),Ct(t))}return p(P(r,o||[]),Nt(t),e)};var Rt={assign:function(e,t){var r=ce.ToObject(e,\"Cannot convert undefined or null to object\");return p(ce.Call(xt,1,arguments),At,r)},is:function is(e,t){return ce.SameValue(e,t)}};var _t=Object.assign&&Object.preventExtensions&&function(){var e=Object.preventExtensions({1:2});try{Object.assign(e,\"xy\")}catch(t){return e[1]===\"y\"}}();if(_t){ne(Object,\"assign\",Rt.assign)}b(Object,Rt);if(s){var kt={setPrototypeOf:function(e,r){var n;var o=function(e,t){if(!ce.TypeIsObject(e)){throw new TypeError(\"cannot set prototype on a non-object\")}if(!(t===null||ce.TypeIsObject(t))){throw new TypeError(\"can only set prototype to an object or null\"+t)}};var i=function(e,r){o(e,r);t(n,e,r);return e};try{n=e.getOwnPropertyDescriptor(e.prototype,r).set;t(n,{},null)}catch(a){if(e.prototype!=={}[r]){return}n=function(e){this[r]=e};i.polyfill=i(i({},null),e.prototype)instanceof e}return i}(Object,\"__proto__\")};b(Object,kt)}if(Object.setPrototypeOf&&Object.getPrototypeOf&&Object.getPrototypeOf(Object.setPrototypeOf({},null))!==null&&Object.getPrototypeOf(Object.create(null))===null){(function(){var e=Object.create(null);var t=Object.getPrototypeOf;var r=Object.setPrototypeOf;Object.getPrototypeOf=function(r){var n=t(r);return n===e?null:n};Object.setPrototypeOf=function(t,n){var o=n===null?e:n;return r(t,o)};Object.setPrototypeOf.polyfill=false})()}var Lt=!i(function(){return Object.keys(\"foo\")});if(!Lt){var Ft=Object.keys;ne(Object,\"keys\",function keys(e){return Ft(ce.ToObject(e))});n=Object.keys}var Dt=i(function(){return Object.keys(/a/g)});if(Dt){var zt=Object.keys;ne(Object,\"keys\",function keys(e){if(re.regex(e)){var t=[];for(var r in e){if(z(e,r)){M(t,r)}}return t}return zt(e)});n=Object.keys}if(Object.getOwnPropertyNames){var qt=!i(function(){return Object.getOwnPropertyNames(\"foo\")});if(!qt){var Wt=typeof window===\"object\"?Object.getOwnPropertyNames(window):[];var Gt=Object.getOwnPropertyNames;ne(Object,\"getOwnPropertyNames\",function getOwnPropertyNames(e){var t=ce.ToObject(e);if(g(t)===\"[object Window]\"){try{return Gt(t)}catch(r){return P([],Wt)}}return Gt(t)})}}if(Object.getOwnPropertyDescriptor){var Ht=!i(function(){return Object.getOwnPropertyDescriptor(\"foo\",\"bar\")});if(!Ht){var Vt=Object.getOwnPropertyDescriptor;ne(Object,\"getOwnPropertyDescriptor\",function getOwnPropertyDescriptor(e,t){return Vt(ce.ToObject(e),t)})}}if(Object.seal){var Bt=!i(function(){return Object.seal(\"foo\")});if(!Bt){var Ut=Object.seal;ne(Object,\"seal\",function seal(e){if(!ce.TypeIsObject(e)){return e}return Ut(e)})}}if(Object.isSealed){var $t=!i(function(){return Object.isSealed(\"foo\")});if(!$t){var Jt=Object.isSealed;ne(Object,\"isSealed\",function isSealed(e){if(!ce.TypeIsObject(e)){return true}return Jt(e)})}}if(Object.freeze){var Xt=!i(function(){return Object.freeze(\"foo\")});if(!Xt){var Kt=Object.freeze;ne(Object,\"freeze\",function freeze(e){if(!ce.TypeIsObject(e)){return e}return Kt(e)})}}if(Object.isFrozen){var Zt=!i(function(){return Object.isFrozen(\"foo\")});if(!Zt){var Yt=Object.isFrozen;ne(Object,\"isFrozen\",function isFrozen(e){if(!ce.TypeIsObject(e)){return true}return Yt(e)})}}if(Object.preventExtensions){var Qt=!i(function(){return Object.preventExtensions(\"foo\")});if(!Qt){var er=Object.preventExtensions;ne(Object,\"preventExtensions\",function preventExtensions(e){if(!ce.TypeIsObject(e)){return e}return er(e)})}}if(Object.isExtensible){var tr=!i(function(){return Object.isExtensible(\"foo\")});if(!tr){var rr=Object.isExtensible;ne(Object,\"isExtensible\",function isExtensible(e){if(!ce.TypeIsObject(e)){return false}return rr(e)})}}if(Object.getPrototypeOf){var nr=!i(function(){return Object.getPrototypeOf(\"foo\")});if(!nr){var or=Object.getPrototypeOf;ne(Object,\"getPrototypeOf\",function getPrototypeOf(e){return or(ce.ToObject(e))})}}var ir=s&&function(){var e=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\");return e&&ce.IsCallable(e.get)}();if(s&&!ir){var ar=function flags(){if(!ce.TypeIsObject(this)){throw new TypeError(\"Method called on incompatible type: must be an object.\")}var e=\"\";if(this.global){e+=\"g\"}if(this.ignoreCase){e+=\"i\"}if(this.multiline){e+=\"m\"}if(this.unicode){e+=\"u\"}if(this.sticky){e+=\"y\"}return e};m.getter(RegExp.prototype,\"flags\",ar)}var ur=s&&a(function(){return String(new RegExp(/a/g,\"i\"))===\"/a/i\"});var fr=oe&&s&&function(){var e=/./;e[$.match]=false;return RegExp(e)===e}();var sr=a(function(){return RegExp.prototype.toString.call({source:\"abc\"})===\"/abc/\"});var cr=sr&&a(function(){return RegExp.prototype.toString.call({source:\"a\",flags:\"b\"})===\"/a/b\"});if(!sr||!cr){var lr=RegExp.prototype.toString;h(RegExp.prototype,\"toString\",function toString(){var e=ce.RequireObjectCoercible(this);if(re.regex(e)){return t(lr,e)}var r=ue(e.source);var n=ue(e.flags);return\"/\"+r+\"/\"+n},true);m.preserveToString(RegExp.prototype.toString,lr)}if(s&&(!ur||fr)){var pr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"flags\").get;var vr=Object.getOwnPropertyDescriptor(RegExp.prototype,\"source\")||{};var yr=function(){return this.source};var hr=ce.IsCallable(vr.get)?vr.get:yr;var br=RegExp;var gr=function(){return function RegExp(e,t){var r=ce.IsRegExp(e);var n=this instanceof RegExp;if(!n&&r&&typeof t===\"undefined\"&&e.constructor===RegExp){return e}var o=e;var i=t;if(re.regex(e)){o=ce.Call(hr,e);i=typeof t===\"undefined\"?ce.Call(pr,e):t;return new RegExp(o,i)}else if(r){o=e.source;i=typeof t===\"undefined\"?e.flags:t}return new br(e,t)}}();Ee(br,gr,{$input:true});RegExp=gr;m.redefine(S,\"RegExp\",gr)}if(s){var dr={input:\"$_\",lastMatch:\"$&\",lastParen:\"$+\",leftContext:\"$`\",rightContext:\"$'\"};l(n(dr),function(e){if(e in RegExp&&!(dr[e]in RegExp)){m.getter(RegExp,dr[e],function get(){return RegExp[e]})}})}Ce(RegExp);var mr=1/Number.EPSILON;var Or=function roundTiesToEven(e){return e+mr-mr};var wr=Math.pow(2,-23);var jr=Math.pow(2,127)*(2-wr);var Sr=Math.pow(2,-126);var Tr=Math.E;var Ir=Math.LOG2E;var Er=Math.LOG10E;var Pr=Number.prototype.clz;delete Number.prototype.clz;var Cr={acosh:function acosh(e){var t=Number(e);if(X(t)||e<1){return NaN}if(t===1){return 0}if(t===Infinity){return t}var r=1/(t*t);if(t<2){return Y(t-1+D(1-r)*t)}var n=t/2;return Y(n+D(1-r)*n-1)+1/Ir},asinh:function asinh(e){var t=Number(e);if(t===0||!T(t)){return t}var r=k(t);var n=r*r;var o=Z(t);if(r<1){return o*Y(r+n/(D(n+1)+1))}return o*(Y(r/2+D(1+1/n)*r/2-1)+1/Ir)},atanh:function atanh(e){var t=Number(e);if(t===0){return t}if(t===-1){return-Infinity}if(t===1){return Infinity}if(X(t)||t<-1||t>1){return NaN}var r=k(t);return Z(t)*Y(2*r/(1-r))/2},cbrt:function cbrt(e){var t=Number(e);if(t===0){return t}var r=t<0;var n;if(r){t=-t}if(t===Infinity){n=Infinity}else{n=L(F(t)/3);n=(t/(n*n)+2*n)/3}return r?-n:n},clz32:function clz32(e){var t=Number(e);var r=ce.ToUint32(t);if(r===0){return 32}return Pr?ce.Call(Pr,r):31-_(F(r+.5)*Ir)},cosh:function cosh(e){var t=Number(e);if(t===0){return 1}if(X(t)){return NaN}if(!T(t)){return Infinity}var r=L(k(t)-1);return(r+1/(r*Tr*Tr))*(Tr/2)},expm1:function expm1(e){var t=Number(e);if(t===-Infinity){return-1}if(!T(t)||t===0){return t}if(k(t)>.5){return L(t)-1}var r=t;var n=0;var o=1;while(n+r!==n){n+=r;o+=1;r*=t/o}return n},hypot:function hypot(e,t){var r=0;var n=0;for(var o=0;o<arguments.length;++o){var i=k(Number(arguments[o]));if(n<i){r*=n/i*(n/i);r+=1;n=i}else{r+=i>0?i/n*(i/n):i}}return n===Infinity?Infinity:n*D(r)},log2:function log2(e){return F(e)*Ir},log10:function log10(e){return F(e)*Er},log1p:Y,sign:Z,sinh:function sinh(e){var t=Number(e);if(!T(t)||t===0){return t}var r=k(t);if(r<1){var n=Math.expm1(r);return Z(t)*n*(1+1/(n+1))/2}var o=L(r-1);return Z(t)*(o-1/(o*Tr*Tr))*(Tr/2)},tanh:function tanh(e){var t=Number(e);if(X(t)||t===0){return t}if(t>=20){return 1}if(t<=-20){return-1}return(Math.expm1(t)-Math.expm1(-t))/(L(t)+L(-t))},trunc:function trunc(e){var t=Number(e);return t<0?-_(-t):_(t)},imul:function imul(e,t){var r=ce.ToUint32(e);var n=ce.ToUint32(t);var o=r>>>16&65535;var i=r&65535;var a=n>>>16&65535;var u=n&65535;return i*u+(o*u+i*a<<16>>>0)|0},fround:function fround(e){var t=Number(e);if(t===0||t===Infinity||t===-Infinity||X(t)){return t}var r=Z(t);var n=k(t);if(n<Sr){return r*Or(n/Sr/wr)*Sr*wr}var o=(1+wr/Number.EPSILON)*n;var i=o-(o-n);if(i>jr||X(i)){return r*Infinity}return r*i}};var Mr=function withinULPDistance(e,t,r){return k(1-e/t)/Number.EPSILON<(r||8)};b(Math,Cr);h(Math,\"sinh\",Cr.sinh,Math.sinh(710)===Infinity);h(Math,\"cosh\",Cr.cosh,Math.cosh(710)===Infinity);h(Math,\"log1p\",Cr.log1p,Math.log1p(-1e-17)!==-1e-17);h(Math,\"asinh\",Cr.asinh,Math.asinh(-1e7)!==-Math.asinh(1e7));h(Math,\"asinh\",Cr.asinh,Math.asinh(1e300)===Infinity);h(Math,\"atanh\",Cr.atanh,Math.atanh(1e-300)===0);h(Math,\"tanh\",Cr.tanh,Math.tanh(-2e-17)!==-2e-17);\nh(Math,\"acosh\",Cr.acosh,Math.acosh(Number.MAX_VALUE)===Infinity);h(Math,\"acosh\",Cr.acosh,!Mr(Math.acosh(1+Number.EPSILON),Math.sqrt(2*Number.EPSILON)));h(Math,\"cbrt\",Cr.cbrt,!Mr(Math.cbrt(1e-300),1e-100));h(Math,\"sinh\",Cr.sinh,Math.sinh(-2e-17)!==-2e-17);var xr=Math.expm1(10);h(Math,\"expm1\",Cr.expm1,xr>22025.465794806718||xr<22025.465794806718);var Nr=Math.round;var Ar=Math.round(.5-Number.EPSILON/4)===0&&Math.round(-.5+Number.EPSILON/3.99)===1;var Rr=mr+1;var _r=2*mr-1;var kr=[Rr,_r].every(function(e){return Math.round(e)===e});h(Math,\"round\",function round(e){var t=_(e);var r=t===-1?-0:t+1;return e-t<.5?t:r},!Ar||!kr);m.preserveToString(Math.round,Nr);var Lr=Math.imul;if(Math.imul(4294967295,5)!==-5){Math.imul=Cr.imul;m.preserveToString(Math.imul,Lr)}if(Math.imul.length!==2){ne(Math,\"imul\",function imul(e,t){return ce.Call(Lr,Math,arguments)})}var Fr=function(){var e=S.setTimeout;if(typeof e!==\"function\"&&typeof e!==\"object\"){return}ce.IsPromise=function(e){if(!ce.TypeIsObject(e)){return false}if(typeof e._promise===\"undefined\"){return false}return true};var r=function(e){if(!ce.IsConstructor(e)){throw new TypeError(\"Bad promise constructor\")}var t=this;var r=function(e,r){if(t.resolve!==void 0||t.reject!==void 0){throw new TypeError(\"Bad Promise implementation!\")}t.resolve=e;t.reject=r};t.resolve=void 0;t.reject=void 0;t.promise=new e(r);if(!(ce.IsCallable(t.resolve)&&ce.IsCallable(t.reject))){throw new TypeError(\"Bad promise constructor\")}};var n;if(typeof window!==\"undefined\"&&ce.IsCallable(window.postMessage)){n=function(){var e=[];var t=\"zero-timeout-message\";var r=function(r){M(e,r);window.postMessage(t,\"*\")};var n=function(r){if(r.source===window&&r.data===t){r.stopPropagation();if(e.length===0){return}var n=N(e);n()}};window.addEventListener(\"message\",n,true);return r}}var o=function(){var e=S.Promise;var t=e&&e.resolve&&e.resolve();return t&&function(e){return t.then(e)}};var i=ce.IsCallable(S.setImmediate)?S.setImmediate:typeof process===\"object\"&&process.nextTick?process.nextTick:o()||(ce.IsCallable(n)?n():function(t){e(t,0)});var a=function(e){return e};var u=function(e){throw e};var f=0;var s=1;var c=2;var l=0;var p=1;var v=2;var y={};var h=function(e,t,r){i(function(){g(e,t,r)})};var g=function(e,t,r){var n,o;if(t===y){return e(r)}try{n=e(r);o=t.resolve}catch(i){n=i;o=t.reject}o(n)};var d=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.fulfillReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+l],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=s;r.reactionLength=0};var m=function(e,t){var r=e._promise;var n=r.reactionLength;if(n>0){h(r.rejectReactionHandler0,r.reactionCapability0,t);r.fulfillReactionHandler0=void 0;r.rejectReactions0=void 0;r.reactionCapability0=void 0;if(n>1){for(var o=1,i=0;o<n;o++,i+=3){h(r[i+p],r[i+v],t);e[i+l]=void 0;e[i+p]=void 0;e[i+v]=void 0}}}r.result=t;r.state=c;r.reactionLength=0};var O=function(e){var t=false;var r=function(r){var n;if(t){return}t=true;if(r===e){return m(e,new TypeError(\"Self resolution\"))}if(!ce.TypeIsObject(r)){return d(e,r)}try{n=r.then}catch(o){return m(e,o)}if(!ce.IsCallable(n)){return d(e,r)}i(function(){j(e,r,n)})};var n=function(r){if(t){return}t=true;return m(e,r)};return{resolve:r,reject:n}};var w=function(e,r,n,o){if(e===I){t(e,r,n,o,y)}else{t(e,r,n,o)}};var j=function(e,t,r){var n=O(e);var o=n.resolve;var i=n.reject;try{w(r,t,o,i)}catch(a){i(a)}};var T,I;var E=function(){var e=function Promise(t){if(!(this instanceof e)){throw new TypeError('Constructor Promise requires \"new\"')}if(this&&this._promise){throw new TypeError(\"Bad construction\")}if(!ce.IsCallable(t)){throw new TypeError(\"not a valid resolver\")}var r=Ae(this,e,T,{_promise:{result:void 0,state:f,reactionLength:0,fulfillReactionHandler0:void 0,rejectReactionHandler0:void 0,reactionCapability0:void 0}});var n=O(r);var o=n.reject;try{t(n.resolve,o)}catch(i){o(i)}return r};return e}();T=E.prototype;var P=function(e,t,r,n){var o=false;return function(i){if(o){return}o=true;t[e]=i;if(--n.count===0){var a=r.resolve;a(t)}}};var C=function(e,t,r){var n=e.iterator;var o=[];var i={count:1};var a,u;var f=0;while(true){try{a=ce.IteratorStep(n);if(a===false){e.done=true;break}u=a.value}catch(s){e.done=true;throw s}o[f]=void 0;var c=t.resolve(u);var l=P(f,o,r,i);i.count+=1;w(c.then,c,l,r.reject);f+=1}if(--i.count===0){var p=r.resolve;p(o)}return r.promise};var x=function(e,t,r){var n=e.iterator;var o,i,a;while(true){try{o=ce.IteratorStep(n);if(o===false){e.done=true;break}i=o.value}catch(u){e.done=true;throw u}a=t.resolve(i);w(a.then,a,r.resolve,r.reject)}return r.promise};b(E,{all:function all(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return C(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},race:function race(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Promise is not object\")}var n=new r(t);var o,i;try{o=ce.GetIterator(e);i={iterator:o,done:false};return x(i,t,n)}catch(a){var u=a;if(i&&!i.done){try{ce.IteratorClose(o,true)}catch(f){u=f}}var s=n.reject;s(u);return n.promise}},reject:function reject(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}var n=new r(t);var o=n.reject;o(e);return n.promise},resolve:function resolve(e){var t=this;if(!ce.TypeIsObject(t)){throw new TypeError(\"Bad promise constructor\")}if(ce.IsPromise(e)){var n=e.constructor;if(n===t){return e}}var o=new r(t);var i=o.resolve;i(e);return o.promise}});b(T,{\"catch\":function(e){return this.then(null,e)},then:function then(e,t){var n=this;if(!ce.IsPromise(n)){throw new TypeError(\"not a promise\")}var o=ce.SpeciesConstructor(n,E);var i;var b=arguments.length>2&&arguments[2]===y;if(b&&o===E){i=y}else{i=new r(o)}var g=ce.IsCallable(e)?e:a;var d=ce.IsCallable(t)?t:u;var m=n._promise;var O;if(m.state===f){if(m.reactionLength===0){m.fulfillReactionHandler0=g;m.rejectReactionHandler0=d;m.reactionCapability0=i}else{var w=3*(m.reactionLength-1);m[w+l]=g;m[w+p]=d;m[w+v]=i}m.reactionLength+=1}else if(m.state===s){O=m.result;h(g,i,O)}else if(m.state===c){O=m.result;h(d,i,O)}else{throw new TypeError(\"unexpected Promise state\")}return i.promise}});y=new r(E);I=T.then;return E}();if(S.Promise){delete S.Promise.accept;delete S.Promise.defer;delete S.Promise.prototype.chain}if(typeof Fr===\"function\"){b(S,{Promise:Fr});var Dr=w(S.Promise,function(e){return e.resolve(42).then(function(){})instanceof e});var zr=!i(function(){return S.Promise.reject(42).then(null,5).then(null,W)});var qr=i(function(){return S.Promise.call(3,W)});var Wr=function(e){var t=e.resolve(5);t.constructor={};var r=e.resolve(t);try{r.then(null,W).then(null,W)}catch(n){return true}return t===r}(S.Promise);var Gr=s&&function(){var e=0;var t=Object.defineProperty({},\"then\",{get:function(){e+=1}});Promise.resolve(t);return e===1}();var Hr=function BadResolverPromise(e){var t=new Promise(e);e(3,function(){});this.then=t.then;this.constructor=BadResolverPromise};Hr.prototype=Promise.prototype;Hr.all=Promise.all;var Vr=a(function(){return!!Hr.all([1,2])});if(!Dr||!zr||!qr||Wr||!Gr||Vr){Promise=Fr;ne(S,\"Promise\",Fr)}if(Promise.all.length!==1){var Br=Promise.all;ne(Promise,\"all\",function all(e){return ce.Call(Br,this,arguments)})}if(Promise.race.length!==1){var Ur=Promise.race;ne(Promise,\"race\",function race(e){return ce.Call(Ur,this,arguments)})}if(Promise.resolve.length!==1){var $r=Promise.resolve;ne(Promise,\"resolve\",function resolve(e){return ce.Call($r,this,arguments)})}if(Promise.reject.length!==1){var Jr=Promise.reject;ne(Promise,\"reject\",function reject(e){return ce.Call(Jr,this,arguments)})}Mt(Promise,\"all\");Mt(Promise,\"race\");Mt(Promise,\"resolve\");Mt(Promise,\"reject\");Ce(Promise)}var Xr=function(e){var t=n(p(e,function(e,t){e[t]=true;return e},{}));return e.join(\":\")===t.join(\":\")};var Kr=Xr([\"z\",\"a\",\"bb\"]);var Zr=Xr([\"z\",1,\"a\",\"3\",2]);if(s){var Yr=function fastkey(e,t){if(!t&&!Kr){return null}if(se(e)){return\"^\"+ce.ToString(e)}else if(typeof e===\"string\"){return\"$\"+e}else if(typeof e===\"number\"){if(!Zr){return\"n\"+e}return e}else if(typeof e===\"boolean\"){return\"b\"+e}return null};var Qr=function emptyObject(){return Object.create?Object.create(null):{}};var en=function addIterableToMap(e,n,o){if(r(o)||re.string(o)){l(o,function(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"Iterator value \"+e+\" is not an entry object\")}n.set(e[0],e[1])})}else if(o instanceof e){t(e.prototype.forEach,o,function(e,t){n.set(t,e)})}else{var i,a;if(!se(o)){a=n.set;if(!ce.IsCallable(a)){throw new TypeError(\"bad map\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{if(!ce.TypeIsObject(f)){throw new TypeError(\"Iterator value \"+f+\" is not an entry object\")}t(a,n,f[0],f[1])}catch(s){ce.IteratorClose(i,true);throw s}}}}};var tn=function addIterableToSet(e,n,o){if(r(o)||re.string(o)){l(o,function(e){n.add(e)})}else if(o instanceof e){t(e.prototype.forEach,o,function(e){n.add(e)})}else{var i,a;if(!se(o)){a=n.add;if(!ce.IsCallable(a)){throw new TypeError(\"bad set\")}i=ce.GetIterator(o)}if(typeof i!==\"undefined\"){while(true){var u=ce.IteratorStep(i);if(u===false){break}var f=u.value;try{t(a,n,f)}catch(s){ce.IteratorClose(i,true);throw s}}}}};var rn={Map:function(){var e={};var r=function MapEntry(e,t){this.key=e;this.value=t;this.next=null;this.prev=null};r.prototype.isRemoved=function isRemoved(){return this.key===e};var n=function isMap(e){return!!e._es6map};var o=function requireMapSlot(e,t){if(!ce.TypeIsObject(e)||!n(e)){throw new TypeError(\"Method Map.prototype.\"+t+\" called on incompatible receiver \"+ce.ToString(e))}};var i=function MapIterator(e,t){o(e,\"[[MapIterator]]\");this.head=e._head;this.i=this.head;this.kind=t};i.prototype={isMapIterator:true,next:function next(){if(!this.isMapIterator){throw new TypeError(\"Not a MapIterator\")}var e=this.i;var t=this.kind;var r=this.head;if(typeof this.i===\"undefined\"){return Ke()}while(e.isRemoved()&&e!==r){e=e.prev}var n;while(e.next!==r){e=e.next;if(!e.isRemoved()){if(t===\"key\"){n=e.key}else if(t===\"value\"){n=e.value}else{n=[e.key,e.value]}this.i=e;return Ke(n)}}this.i=void 0;return Ke()}};Me(i.prototype);var a;var u=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}if(this&&this._es6map){throw new TypeError(\"Bad construction\")}var e=Ae(this,Map,a,{_es6map:true,_head:null,_map:G?new G:null,_size:0,_storage:Qr()});var t=new r(null,null);t.next=t.prev=t;e._head=t;if(arguments.length>0){en(Map,e,arguments[0])}return e};a=u.prototype;m.getter(a,\"size\",function(){if(typeof this._size===\"undefined\"){throw new TypeError(\"size method called on incompatible Map\")}return this._size});b(a,{get:function get(e){o(this,\"get\");var t;var r=Yr(e,true);if(r!==null){t=this._storage[r];if(t){return t.value}else{return}}if(this._map){t=V.call(this._map,e);if(t){return t.value}else{return}}var n=this._head;var i=n;while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){return i.value}}},has:function has(e){o(this,\"has\");var t=Yr(e,true);if(t!==null){return typeof this._storage[t]!==\"undefined\"}if(this._map){return B.call(this._map,e)}var r=this._head;var n=r;while((n=n.next)!==r){if(ce.SameValueZero(n.key,e)){return true}}return false},set:function set(e,t){o(this,\"set\");var n=this._head;var i=n;var a;var u=Yr(e,true);if(u!==null){if(typeof this._storage[u]!==\"undefined\"){this._storage[u].value=t;return this}else{a=this._storage[u]=new r(e,t);i=n.prev}}else if(this._map){if(B.call(this._map,e)){V.call(this._map,e).value=t}else{a=new r(e,t);U.call(this._map,e,a);i=n.prev}}while((i=i.next)!==n){if(ce.SameValueZero(i.key,e)){i.value=t;return this}}a=a||new r(e,t);if(ce.SameValue(-0,e)){a.key=+0}a.next=this._head;a.prev=this._head.prev;a.prev.next=a;a.next.prev=a;this._size+=1;return this},\"delete\":function(t){o(this,\"delete\");var r=this._head;var n=r;var i=Yr(t,true);if(i!==null){if(typeof this._storage[i]===\"undefined\"){return false}n=this._storage[i].prev;delete this._storage[i]}else if(this._map){if(!B.call(this._map,t)){return false}n=V.call(this._map,t).prev;H.call(this._map,t)}while((n=n.next)!==r){if(ce.SameValueZero(n.key,t)){n.key=e;n.value=e;n.prev.next=n.next;n.next.prev=n.prev;this._size-=1;return true}}return false},clear:function clear(){o(this,\"clear\");this._map=G?new G:null;this._size=0;this._storage=Qr();var t=this._head;var r=t;var n=r.next;while((r=n)!==t){r.key=e;r.value=e;n=r.next;r.next=r.prev=t}t.next=t.prev=t},keys:function keys(){o(this,\"keys\");return new i(this,\"key\")},values:function values(){o(this,\"values\");return new i(this,\"value\")},entries:function entries(){o(this,\"entries\");return new i(this,\"key+value\")},forEach:function forEach(e){o(this,\"forEach\");var r=arguments.length>1?arguments[1]:null;var n=this.entries();for(var i=n.next();!i.done;i=n.next()){if(r){t(e,r,i.value[1],i.value[0],this)}else{e(i.value[1],i.value[0],this)}}}});Me(a,a.entries);return u}(),Set:function(){var e=function isSet(e){return e._es6set&&typeof e._storage!==\"undefined\"};var r=function requireSetSlot(t,r){if(!ce.TypeIsObject(t)||!e(t)){throw new TypeError(\"Set.prototype.\"+r+\" called on incompatible receiver \"+ce.ToString(t))}};var o;var i=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}if(this&&this._es6set){throw new TypeError(\"Bad construction\")}var e=Ae(this,Set,o,{_es6set:true,\"[[SetData]]\":null,_storage:Qr()});if(!e._es6set){throw new TypeError(\"bad set\")}if(arguments.length>0){tn(Set,e,arguments[0])}return e};o=i.prototype;var a=function(e){var t=e;if(t===\"^null\"){return null}else if(t===\"^undefined\"){return void 0}else{var r=t.charAt(0);if(r===\"$\"){return C(t,1)}else if(r===\"n\"){return+C(t,1)}else if(r===\"b\"){return t===\"btrue\"}}return+t};var u=function ensureMap(e){if(!e[\"[[SetData]]\"]){var t=new rn.Map;e[\"[[SetData]]\"]=t;l(n(e._storage),function(e){var r=a(e);t.set(r,r)});e[\"[[SetData]]\"]=t}e._storage=null};m.getter(i.prototype,\"size\",function(){r(this,\"size\");if(this._storage){return n(this._storage).length}u(this);return this[\"[[SetData]]\"].size});b(i.prototype,{has:function has(e){r(this,\"has\");var t;if(this._storage&&(t=Yr(e))!==null){return!!this._storage[t]}u(this);return this[\"[[SetData]]\"].has(e)},add:function add(e){r(this,\"add\");var t;if(this._storage&&(t=Yr(e))!==null){this._storage[t]=true;return this}u(this);this[\"[[SetData]]\"].set(e,e);return this},\"delete\":function(e){r(this,\"delete\");var t;if(this._storage&&(t=Yr(e))!==null){var n=z(this._storage,t);return delete this._storage[t]&&n}u(this);return this[\"[[SetData]]\"][\"delete\"](e)},clear:function clear(){r(this,\"clear\");if(this._storage){this._storage=Qr()}if(this[\"[[SetData]]\"]){this[\"[[SetData]]\"].clear()}},values:function values(){r(this,\"values\");u(this);return new f(this[\"[[SetData]]\"].values())},entries:function entries(){r(this,\"entries\");u(this);return new f(this[\"[[SetData]]\"].entries())},forEach:function forEach(e){r(this,\"forEach\");var n=arguments.length>1?arguments[1]:null;var o=this;u(o);this[\"[[SetData]]\"].forEach(function(r,i){if(n){t(e,n,i,i,o)}else{e(i,i,o)}})}});h(i.prototype,\"keys\",i.prototype.values,true);Me(i.prototype,i.prototype.values);var f=function SetIterator(e){this.it=e};f.prototype={isSetIterator:true,next:function next(){if(!this.isSetIterator){throw new TypeError(\"Not a SetIterator\")}return this.it.next()}};Me(f.prototype);return i}()};var nn=S.Set&&!Set.prototype[\"delete\"]&&Set.prototype.remove&&Set.prototype.items&&Set.prototype.map&&Array.isArray((new Set).keys);if(nn){S.Set=rn.Set}if(S.Map||S.Set){var on=a(function(){return new Map([[1,2]]).get(1)===2});if(!on){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,S.Map.prototype);return e};S.Map.prototype=O(G.prototype);h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var an=new Map;var un=function(){var e=new Map([[1,0],[2,0],[3,0],[4,0]]);e.set(-0,e);return e.get(0)===e&&e.get(-0)===e&&e.has(0)&&e.has(-0)}();var fn=an.set(1,2)===an;if(!un||!fn){ne(Map.prototype,\"set\",function set(e,r){t(U,this,e===0?0:e,r);return this})}if(!un){b(Map.prototype,{get:function get(e){return t(V,this,e===0?0:e)},has:function has(e){return t(B,this,e===0?0:e)}},true);m.preserveToString(Map.prototype.get,V);m.preserveToString(Map.prototype.has,B)}var sn=new Set;var cn=Set.prototype[\"delete\"]&&Set.prototype.add&&Set.prototype.has&&function(e){e[\"delete\"](0);e.add(-0);return!e.has(0)}(sn);var ln=sn.add(1)===sn;if(!cn||!ln){var pn=Set.prototype.add;Set.prototype.add=function add(e){t(pn,this,e===0?0:e);return this};m.preserveToString(Set.prototype.add,pn)}if(!cn){var vn=Set.prototype.has;Set.prototype.has=function has(e){return t(vn,this,e===0?0:e)};m.preserveToString(Set.prototype.has,vn);var yn=Set.prototype[\"delete\"];Set.prototype[\"delete\"]=function SetDelete(e){return t(yn,this,e===0?0:e)};m.preserveToString(Set.prototype[\"delete\"],yn)}var hn=w(S.Map,function(e){var t=new e([]);t.set(42,42);return t instanceof e});var bn=Object.setPrototypeOf&&!hn;var gn=function(){try{return!(S.Map()instanceof S.Map)}catch(e){return e instanceof TypeError}}();if(S.Map.length!==0||bn||!gn){S.Map=function Map(){if(!(this instanceof Map)){throw new TypeError('Constructor Map requires \"new\"')}var e=new G;if(arguments.length>0){en(Map,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Map.prototype);return e};S.Map.prototype=G.prototype;h(S.Map.prototype,\"constructor\",S.Map,true);m.preserveToString(S.Map,G)}var dn=w(S.Set,function(e){var t=new e([]);t.add(42,42);return t instanceof e});var mn=Object.setPrototypeOf&&!dn;var On=function(){try{return!(S.Set()instanceof S.Set)}catch(e){return e instanceof TypeError}}();if(S.Set.length!==0||mn||!On){var wn=S.Set;S.Set=function Set(){if(!(this instanceof Set)){throw new TypeError('Constructor Set requires \"new\"')}var e=new wn;if(arguments.length>0){tn(Set,e,arguments[0])}delete e.constructor;Object.setPrototypeOf(e,Set.prototype);return e};S.Set.prototype=wn.prototype;h(S.Set.prototype,\"constructor\",S.Set,true);m.preserveToString(S.Set,wn)}var jn=new S.Map;var Sn=!a(function(){return jn.keys().next().done});if(typeof S.Map.prototype.clear!==\"function\"||(new S.Set).size!==0||jn.size!==0||typeof S.Map.prototype.keys!==\"function\"||typeof S.Set.prototype.keys!==\"function\"||typeof S.Map.prototype.forEach!==\"function\"||typeof S.Set.prototype.forEach!==\"function\"||u(S.Map)||u(S.Set)||typeof jn.keys().next!==\"function\"||Sn||!hn){b(S,{Map:rn.Map,Set:rn.Set},true)}if(S.Set.prototype.keys!==S.Set.prototype.values){h(S.Set.prototype,\"keys\",S.Set.prototype.values,true)}Me(Object.getPrototypeOf((new S.Map).keys()));Me(Object.getPrototypeOf((new S.Set).keys()));if(c&&S.Set.prototype.has.name!==\"has\"){var Tn=S.Set.prototype.has;ne(S.Set.prototype,\"has\",function has(e){return t(Tn,this,e)})}}b(S,rn);Ce(S.Map);Ce(S.Set)}var In=function throwUnlessTargetIsObject(e){if(!ce.TypeIsObject(e)){throw new TypeError(\"target must be an object\")}};var En={apply:function apply(){return ce.Call(ce.Call,null,arguments)},construct:function construct(e,t){if(!ce.IsConstructor(e)){throw new TypeError(\"First argument must be a constructor.\")}var r=arguments.length>2?arguments[2]:e;if(!ce.IsConstructor(r)){throw new TypeError(\"new.target must be a constructor.\")}return ce.Construct(e,t,r,\"internal\")},deleteProperty:function deleteProperty(e,t){In(e);if(s){var r=Object.getOwnPropertyDescriptor(e,t);if(r&&!r.configurable){return false}}return delete e[t]},has:function has(e,t){In(e);return t in e}};if(Object.getOwnPropertyNames){Object.assign(En,{ownKeys:function ownKeys(e){In(e);var t=Object.getOwnPropertyNames(e);if(ce.IsCallable(Object.getOwnPropertySymbols)){x(t,Object.getOwnPropertySymbols(e))}return t}})}var Pn=function ConvertExceptionToBoolean(e){return!i(e)};if(Object.preventExtensions){Object.assign(En,{isExtensible:function isExtensible(e){In(e);return Object.isExtensible(e)},preventExtensions:function preventExtensions(e){In(e);return Pn(function(){return Object.preventExtensions(e)})}})}if(s){var Cn=function get(e,t,r){var n=Object.getOwnPropertyDescriptor(e,t);if(!n){var o=Object.getPrototypeOf(e);if(o===null){return void 0}return Cn(o,t,r)}if(\"value\"in n){return n.value}if(n.get){return ce.Call(n.get,r)}return void 0};var Mn=function set(e,r,n,o){var i=Object.getOwnPropertyDescriptor(e,r);if(!i){var a=Object.getPrototypeOf(e);if(a!==null){return Mn(a,r,n,o)}i={value:void 0,writable:true,enumerable:true,configurable:true}}if(\"value\"in i){if(!i.writable){return false}if(!ce.TypeIsObject(o)){return false}var u=Object.getOwnPropertyDescriptor(o,r);if(u){return ae.defineProperty(o,r,{value:n})}else{return ae.defineProperty(o,r,{value:n,writable:true,enumerable:true,configurable:true})}}if(i.set){t(i.set,o,n);return true}return false};Object.assign(En,{defineProperty:function defineProperty(e,t,r){In(e);return Pn(function(){return Object.defineProperty(e,t,r)})},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(e,t){In(e);return Object.getOwnPropertyDescriptor(e,t)},get:function get(e,t){In(e);var r=arguments.length>2?arguments[2]:e;return Cn(e,t,r)},set:function set(e,t,r){In(e);var n=arguments.length>3?arguments[3]:e;return Mn(e,t,r,n)}})}if(Object.getPrototypeOf){var xn=Object.getPrototypeOf;En.getPrototypeOf=function getPrototypeOf(e){In(e);return xn(e)}}if(Object.setPrototypeOf&&En.getPrototypeOf){var Nn=function(e,t){var r=t;while(r){if(e===r){return true}r=En.getPrototypeOf(r)}return false};Object.assign(En,{setPrototypeOf:function setPrototypeOf(e,t){In(e);if(t!==null&&!ce.TypeIsObject(t)){throw new TypeError(\"proto must be an object or null\")}if(t===ae.getPrototypeOf(e)){return true}if(ae.isExtensible&&!ae.isExtensible(e)){return false}if(Nn(e,t)){return false}Object.setPrototypeOf(e,t);return true}})}var An=function(e,t){if(!ce.IsCallable(S.Reflect[e])){h(S.Reflect,e,t)}else{var r=a(function(){S.Reflect[e](1);S.Reflect[e](NaN);S.Reflect[e](true);return true});if(r){ne(S.Reflect,e,t)}}};Object.keys(En).forEach(function(e){An(e,En[e])});var Rn=S.Reflect.getPrototypeOf;if(c&&Rn&&Rn.name!==\"getPrototypeOf\"){ne(S.Reflect,\"getPrototypeOf\",function getPrototypeOf(e){return t(Rn,S.Reflect,e)})}if(S.Reflect.setPrototypeOf){if(a(function(){S.Reflect.setPrototypeOf(1,{});return true})){ne(S.Reflect,\"setPrototypeOf\",En.setPrototypeOf)}}if(S.Reflect.defineProperty){if(!a(function(){var e=!S.Reflect.defineProperty(1,\"test\",{value:1});var t=typeof Object.preventExtensions!==\"function\"||!S.Reflect.defineProperty(Object.preventExtensions({}),\"test\",{});return e&&t})){ne(S.Reflect,\"defineProperty\",En.defineProperty)}}if(S.Reflect.construct){if(!a(function(){var e=function F(){};return S.Reflect.construct(function(){},[],e)instanceof e})){ne(S.Reflect,\"construct\",En.construct)}}if(String(new Date(NaN))!==\"Invalid Date\"){var _n=Date.prototype.toString;var kn=function toString(){var e=+this;if(e!==e){return\"Invalid Date\"}return ce.Call(_n,this)};ne(Date.prototype,\"toString\",kn)}var Ln={anchor:function anchor(e){return ce.CreateHTML(this,\"a\",\"name\",e)},big:function big(){return ce.CreateHTML(this,\"big\",\"\",\"\")},blink:function blink(){return ce.CreateHTML(this,\"blink\",\"\",\"\")},bold:function bold(){return ce.CreateHTML(this,\"b\",\"\",\"\")},fixed:function fixed(){return ce.CreateHTML(this,\"tt\",\"\",\"\")},fontcolor:function fontcolor(e){return ce.CreateHTML(this,\"font\",\"color\",e)},fontsize:function fontsize(e){return ce.CreateHTML(this,\"font\",\"size\",e)},italics:function italics(){return ce.CreateHTML(this,\"i\",\"\",\"\")},link:function link(e){return ce.CreateHTML(this,\"a\",\"href\",e)},small:function small(){return ce.CreateHTML(this,\"small\",\"\",\"\")},strike:function strike(){return ce.CreateHTML(this,\"strike\",\"\",\"\")},sub:function sub(){return ce.CreateHTML(this,\"sub\",\"\",\"\")},sup:function sub(){return ce.CreateHTML(this,\"sup\",\"\",\"\")}};l(Object.keys(Ln),function(e){var r=String.prototype[e];var n=false;if(ce.IsCallable(r)){var o=t(r,\"\",' \" ');var i=P([],o.match(/\"/g)).length;n=o!==o.toLowerCase()||i>2}else{n=true}if(n){ne(String.prototype,e,Ln[e])}});var Fn=function(){if(!oe){return false}var e=typeof JSON===\"object\"&&typeof JSON.stringify===\"function\"?JSON.stringify:null;if(!e){return false}if(typeof e($())!==\"undefined\"){return true}if(e([$()])!==\"[null]\"){return true}var t={a:$()};t[$()]=true;if(e(t)!==\"{}\"){return true}return false}();var Dn=a(function(){if(!oe){return true}return JSON.stringify(Object($()))===\"{}\"&&JSON.stringify([Object($())])===\"[{}]\"});if(Fn||!Dn){var zn=JSON.stringify;ne(JSON,\"stringify\",function stringify(e){if(typeof e===\"symbol\"){return}var n;if(arguments.length>1){n=arguments[1]}var o=[e];if(!r(n)){var i=ce.IsCallable(n)?n:null;var a=function(e,r){var n=i?t(i,this,e,r):r;if(typeof n!==\"symbol\"){if(re.symbol(n)){return Nt({})(n)}else{return n}}};o.push(a)}else{o.push(n)}if(arguments.length>2){o.push(arguments[2])}return zn.apply(this,o)})}return S});\n/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(C,e){\"use strict\";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return\"function\"==typeof e&&\"number\"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement(\"script\");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?n[o.call(e)]||\"object\":typeof e}var f=\"3.5.1\",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&\"length\"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&(\"array\"===n||0===t||\"number\"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for(\"boolean\"==typeof a&&(l=a,a=arguments[s]||{},s++),\"object\"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],\"__proto__\"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:\"jQuery\"+(f+Math.random()).replace(/\\D/g,\"\"),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||\"[object Object]\"!==o.call(e))&&(!(t=r(e))||\"function\"==typeof(n=v.call(t,\"constructor\")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,\"string\"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),\"function\"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){n[\"[object \"+t+\"]\"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S=\"sizzle\"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R=\"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",M=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",I=\"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",W=\"\\\\[\"+M+\"*(\"+I+\")(?:\"+M+\"*([*^$|!~]?=)\"+M+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+I+\"))|)\"+M+\"*\\\\]\",F=\":(\"+I+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+W+\")*)|.*)\\\\)|)\",B=new RegExp(M+\"+\",\"g\"),$=new RegExp(\"^\"+M+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+M+\"+$\",\"g\"),_=new RegExp(\"^\"+M+\"*,\"+M+\"*\"),z=new RegExp(\"^\"+M+\"*([>+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp(F),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+F),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+M+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+M+\"*(?:([+-]|)\"+M+\"*(\\\\d+)|))\"+M+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+R+\")$\",\"i\"),needsContext:new RegExp(\"^\"+M+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+M+\"*((?:-\\\\d)?\\\\d*)\"+M+\"*\\\\)|)(?=[^-]|$)\",\"i\")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),ne=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?\"\\0\"===e?\"\\ufffd\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&\"fieldset\"===e.nodeName.toLowerCase()},{dir:\"parentNode\",next:\"legend\"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],\"string\"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?\"#\"+s:\":scope\")+\" \"+xe(l[o]);c=l.join(\",\")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute(\"id\")}}}return g(t.replace($,\"$1\"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+\" \")>b.cacheLength&&delete e[r.shift()],e[t+\" \"]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split(\"|\"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return\"input\"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return(\"input\"===t||\"button\"===t)&&e.type===n}}function ge(t){return function(e){return\"form\"in e?e.parentNode&&!1===e.disabled?\"label\"in e?\"label\"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:\"label\"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||\"HTML\")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement(\"div\")),\"undefined\"!=typeof e.querySelectorAll&&!e.querySelectorAll(\":scope fieldset div\").length}),d.attributes=ce(function(e){return e.className=\"i\",!e.getAttribute(\"className\")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute(\"id\")===t}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=\"undefined\"!=typeof e.getAttributeNode&&e.getAttributeNode(\"id\");return t&&t.value===n}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(\"undefined\"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=\"<a id='\"+S+\"'></a><select id='\"+S+\"-\\r\\\\' msallowcapture=''><option selected=''></option></select>\",e.querySelectorAll(\"[msallowcapture^='']\").length&&v.push(\"[*^$]=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\"[selected]\").length||v.push(\"\\\\[\"+M+\"*(?:value|\"+R+\")\"),e.querySelectorAll(\"[id~=\"+S+\"-]\").length||v.push(\"~=\"),(t=C.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||v.push(\"\\\\[\"+M+\"*name\"+M+\"*=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+S+\"+*\").length||v.push(\".#.+[+~]\"),e.querySelectorAll(\"\\\\\\f\"),v.push(\"[\\\\r\\\\n\\\\f]\")}),ce(function(e){e.innerHTML=\"<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>\";var t=C.createElement(\"input\");t.setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),e.querySelectorAll(\"[name=d]\").length&&v.push(\"name\"+M+\"*[*^$|!~]?=\"),2!==e.querySelectorAll(\":enabled\").length&&v.push(\":enabled\",\":disabled\"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&v.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),v.push(\",.*:\")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,\"*\"),c.call(e,\"[s!='']:x\"),s.push(\"!=\",F)}),v=v.length&&new RegExp(v.join(\"|\")),s=s.length&&new RegExp(s.join(\"|\")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+\" \"]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+\"\").replace(re,ie)},se.error=function(e){throw new Error(\"Syntax error, unrecognized expression: \"+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||\"\").replace(te,ne),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&m(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?\"!=\"===r:!r||(t+=\"\",\"=\"===r?t===i:\"!=\"===r?t!==i:\"^=\"===r?i&&0===t.indexOf(i):\"*=\"===r?i&&-1<t.indexOf(i):\"$=\"===r?i&&t.slice(-i.length)===i:\"~=\"===r?-1<(\" \"+t.replace(B,\" \")+\" \").indexOf(i):\"|=\"===r&&(t===i||t.slice(0,i.length+1)===i+\"-\"))}},CHILD:function(h,e,t,g,v){var y=\"nth\"!==h.slice(0,3),m=\"last\"!==h.slice(-4),x=\"of-type\"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?\"nextSibling\":\"previousSibling\",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l=\"only\"===h&&!u&&\"nextSibling\"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error(\"unsupported pseudo: \"+e);return a[S]?a(o):1<a.length?(t=[e,e,\"\",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,\"$1\"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||\"\")||se.error(\"unsupported lang: \"+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute(\"xml:lang\")||e.getAttribute(\"lang\"))return(t=t.toLowerCase())===n||0===t.indexOf(n+\"-\")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||\"text\"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r=\"\";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&\"parentNode\"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||\"*\",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[\" \"],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:\" \"===e[s-2].type?\"*\":\"\"})).replace($,\"$1\"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+\" \"];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($,\" \")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+\" \"];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l=\"0\",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG(\"*\",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l=\"function\"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&\"ID\"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split(\"\").sort(D).join(\"\")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement(\"fieldset\"))}),ce(function(e){return e.innerHTML=\"<a href='#'></a>\",\"#\"===e.firstChild.getAttribute(\"href\")})||fe(\"type|href|height|width\",function(e,t,n){if(!n)return e.getAttribute(t,\"type\"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML=\"<input/>\",e.firstChild.setAttribute(\"value\",\"\"),\"\"===e.firstChild.getAttribute(\"value\")})||fe(\"value\",function(e,t,n){if(!n&&\"input\"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute(\"disabled\")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[\":\"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,\"string\"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a=\"string\"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,\"parentNode\")},parentsUntil:function(e,t,n){return h(e,\"parentNode\",n)},next:function(e){return O(e,\"nextSibling\")},prev:function(e){return O(e,\"previousSibling\")},nextAll:function(e){return h(e,\"nextSibling\")},prevAll:function(e){return h(e,\"previousSibling\")},nextUntil:function(e,t,n){return h(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return h(e,\"previousSibling\",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,\"template\")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return\"Until\"!==r.slice(-5)&&(t=e),t&&\"string\"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\\x20\\t\\r\\n\\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r=\"string\"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:\"\")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&\"string\"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t=\"\",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=\"\"),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[[\"notify\",\"progress\",S.Callbacks(\"memory\"),S.Callbacks(\"memory\"),2],[\"resolve\",\"done\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",S.Callbacks(\"once memory\"),S.Callbacks(\"once memory\"),1,\"rejected\"]],i=\"pending\",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},\"catch\":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+\"With\"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError(\"Thenable self-resolution\");t=e&&(\"object\"==typeof e||\"function\"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+\"With\"](this===s?void 0:this,arguments),this},s[t[0]+\"With\"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),\"pending\"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn(\"jQuery.Deferred exception: \"+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener(\"DOMContentLoaded\",B),C.removeEventListener(\"load\",B),S.ready()}S.fn.ready=function(e){return F.then(e)[\"catch\"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,\"complete\"===E.readyState||\"loading\"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener(\"DOMContentLoaded\",B),C.addEventListener(\"load\",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if(\"object\"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,\"ms-\").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if(\"string\"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&\"string\"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(K,\"-$&\").toLowerCase(),\"string\"==typeof(n=e.getAttribute(r))){try{n=\"true\"===(i=n)||\"false\"!==i&&(\"null\"===i?null:i===+i+\"\"?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,\"hasDataAttrs\"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf(\"data-\")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,\"hasDataAttrs\",!0)}return i}return\"object\"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks(\"once memory\").add(function(){Y.remove(e,[t+\"queue\",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return\"string\"!=typeof t&&(n=t,t=\"fx\",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),\"fx\"===t&&\"inprogress\"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};\"string\"!=typeof e&&(t=e,e=void 0),e=e||\"fx\";while(a--)(n=Y.get(o[a],e+\"queueHooks\"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,te=new RegExp(\"^(?:([+-])=|)(\"+ee+\")([a-z%]*)$\",\"i\"),ne=[\"Top\",\"Right\",\"Bottom\",\"Left\"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return\"none\"===(e=t||e).style.display||\"\"===e.style.display&&ie(e)&&\"none\"===S.css(e,\"display\")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,\"\")},u=s(),l=n&&n[3]||(S.cssNumber[t]?\"\":\"px\"),c=e.nodeType&&(S.cssNumber[t]||\"px\"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?(\"none\"===n&&(l[c]=Y.get(r,\"display\")||null,l[c]||(r.style.display=\"\")),\"\"===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,\"display\"),o.parentNode.removeChild(o),\"none\"===u&&(u=\"block\"),ue[s]=u)))):\"none\"!==n&&(l[c]=\"none\",Y.set(r,\"display\",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(fe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),fe.setAttribute(\"checked\",\"checked\"),fe.setAttribute(\"name\",\"t\"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML=\"<textarea>x</textarea>\",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=\"<option></option>\",y.option=!!ce.lastChild;var ge={thead:[1,\"<table>\",\"</table>\"],col:[2,\"<table><colgroup>\",\"</colgroup></table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:[0,\"\",\"\"]};function ve(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],\"globalEval\",!t||Y.get(t[n],\"globalEval\"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,\"<select multiple='multiple'>\",\"</select>\"]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if(\"object\"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement(\"div\")),s=(de.exec(o)||[\"\",\"\"])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=\"\"}else p.push(t.createTextNode(o));f.textContent=\"\",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),\"script\"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||\"\")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==(\"focus\"===t)}function ke(e,t,n,r,i,o){var a,s;if(\"object\"==typeof t){for(s in\"string\"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&(\"string\"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return\"undefined\"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||\"\").match(P)||[\"\"]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(\".\")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||\"\").match(P)||[\"\"]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&(\"**\"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,\"events\")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!(\"click\"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(\"click\"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+\" \"]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Ae(t,\"click\"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Y.get(t,\"click\")||A(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,\"char\":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+\".\"+r.namespace:r.origType,r.selector,r.handler),this;if(\"object\"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&\"function\"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\\s*(?:[^=]|=\\s*.checked.)/i,je=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;function qe(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&S(e).children(\"tbody\")[0]||e}function Le(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function He(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&\"string\"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,\"script\"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,\"script\"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||\"\")&&!Y.access(u,\"globalEval\")&&S.contains(l,u)&&(u.src&&\"module\"!==(u.type||\"\").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute(\"nonce\")},l):b(u.textContent.replace(je,\"\"),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,\"script\")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,\"input\"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:\"input\"!==l&&\"textarea\"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,\"script\")).length&&ye(a,!f&&ve(e,\"script\")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp(\"^(\"+ee+\")(?!px)[a-z%]+$\",\"i\"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join(\"|\"),\"i\");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(\"\"!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+\"\":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText=\"position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0\",l.style.cssText=\"position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%\",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n=\"1%\"!==e.top,s=12===t(e.marginLeft),l.style.right=\"60%\",o=36===t(e.right),r=36===t(e.width),l.style.position=\"absolute\",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement(\"div\"),l=E.createElement(\"div\");l.style&&(l.style.backgroundClip=\"content-box\",l.cloneNode(!0).style.backgroundClip=\"\",y.clearCloneStyle=\"content-box\"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement(\"table\"),t=E.createElement(\"tr\"),n=E.createElement(\"div\"),e.style.cssText=\"position:absolute;left:-11111px\",t.style.height=\"1px\",n.style.height=\"9px\",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=[\"Webkit\",\"Moz\",\"ms\"],ze=E.createElement(\"div\").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Qe={letterSpacing:\"0\",fontWeight:\"400\"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ke(e,t,n,r,i,o){var a=\"width\"===t?1:0,s=0,u=0;if(n===(r?\"border\":\"content\"))return 0;for(;a<4;a+=2)\"margin\"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?(\"content\"===n&&(u-=S.css(e,\"padding\"+ne[a],!0,i)),\"margin\"!==n&&(u-=S.css(e,\"border\"+ne[a]+\"Width\",!0,i))):(u+=S.css(e,\"padding\"+ne[a],!0,i),\"padding\"!==n?u+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i):s+=S.css(e,\"border\"+ne[a]+\"Width\",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,r),o=i,a=Be(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a=\"auto\"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===S.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===S.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?\"\":\"px\")),y.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),\"normal\"===i&&t in Qe&&(i=Qe[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each([\"height\",\"width\"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&\"absolute\"===i.position,a=(o||n)&&\"border-box\"===S.css(e,\"boxSizing\",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e[\"offset\"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,\"border\",!1,i)-.5)),s&&(r=te.exec(t))&&\"px\"!==(r[3]||\"px\")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,\"marginLeft\"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+\"px\"}),S.each({margin:\"\",padding:\"\",border:\"Width\"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r=\"string\"==typeof e?e.split(\" \"):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},\"margin\"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?\"\":\"px\")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=ne[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners[\"*\"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&\"expand\"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=[\"*\"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f=\"width\"in t||\"height\"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,\"fxshow\");for(r in n.queue||(null==(a=S._queueHooks(e,\"fx\")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,\"fx\").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||\"toggle\"===i,i===(g?\"hide\":\"show\")){if(\"show\"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,\"display\")),\"none\"===(c=S.css(e,\"display\"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,\"display\"),le([e]))),(\"inline\"===c||\"inline-block\"===c&&null!=l)&&\"none\"===S.css(e,\"float\")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l=\"none\"===c?\"\":c)),h.display=\"inline-block\")),n.overflow&&(h.overflow=\"hidden\",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?\"hidden\"in v&&(g=v.hidden):v=Y.access(e,\"fxshow\",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,\"fxshow\"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&\"object\"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,\"finish\"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return\"string\"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||\"fx\",[]),this.each(function(){var e=!0,t=null!=i&&i+\"queueHooks\",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||\"fx\"),this.each(function(){var e,t=Y.get(this),n=t[a+\"queue\"],r=t[a+\"queueHooks\"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each([\"toggle\",\"show\",\"hide\"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||\"boolean\"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt(\"show\"),slideUp:lt(\"hide\"),slideToggle:lt(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||\"fx\",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement(\"input\"),it=E.createElement(\"select\").appendChild(E.createElement(\"option\")),rt.type=\"checkbox\",y.checkOn=\"\"!==rt.value,y.optSelected=it.selected,(rt=E.createElement(\"input\")).value=\"t\",rt.type=\"radio\",y.radioValue=\"t\"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return\"undefined\"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&\"radio\"===t&&A(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(\" \")}function yt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function mt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,\"tabindex\");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr(\"class\",\"\");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&\" \"+vt(i)+\" \"){a=0;while(o=e[a++])while(-1<r.indexOf(\" \"+o+\" \"))r=r.replace(\" \"+o+\" \",\" \");i!==(s=vt(r))&&n.setAttribute(\"class\",s)}return this},toggleClass:function(i,t){var o=typeof i,a=\"string\"===o||Array.isArray(i);return\"boolean\"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&\"boolean\"!==o||((e=yt(this))&&Y.set(this,\"__className__\",e),this.setAttribute&&this.setAttribute(\"class\",e||!1===i?\"\":Y.get(this,\"__className__\")||\"\"))})},hasClass:function(e){var t,n,r=0;t=\" \"+e+\" \";while(n=this[r++])if(1===n.nodeType&&-1<(\" \"+vt(yt(n))+\" \").indexOf(t))return!0;return!1}});var xt=/\\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t=\"\":\"number\"==typeof t?t+=\"\":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?\"\":e+\"\"})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&\"set\"in r&&void 0!==r.set(this,t,\"value\")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&\"get\"in r&&void 0!==(e=r.get(t,\"value\"))?e:\"string\"==typeof(e=t.value)?e.replace(xt,\"\"):null==e?\"\":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,\"value\");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,\"optgroup\"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each([\"radio\",\"checkbox\"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}),y.focusin=\"onfocusin\"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,\"type\")?e.type:e,h=v.call(e,\"namespace\")?e.namespace.split(\".\"):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(\".\")&&(d=(h=d.split(\".\")).shift(),h.sort()),u=d.indexOf(\":\")<0&&\"on\"+d,(e=e[S.expando]?e:new S.Event(d,\"object\"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join(\".\"),e.rnamespace=e.namespace?new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,\"events\")||Object.create(null))[e.type]&&Y.get(o,\"handle\"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:\"focusin\",blur:\"focusout\"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\\?/;S.parseXML=function(e){var t;if(!e||\"string\"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,\"text/xml\")}catch(e){t=void 0}return t&&!t.getElementsByTagName(\"parsererror\").length||S.error(\"Invalid XML: \"+e),t};var St=/\\[\\]$/,kt=/\\r?\\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+\"[\"+(\"object\"==typeof t&&null!=t?e:\"\")+\"]\",t,r,i)});else if(r||\"object\"!==w(e))i(n,e);else for(t in e)Dt(n+\"[\"+t+\"]\",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join(\"&\")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,\"elements\");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(\":disabled\")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,\"\\r\\n\")}}):{name:t.name,value:n.replace(kt,\"\\r\\n\")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\\/\\//,Rt={},Mt={},It=\"*/\".concat(\"*\"),Wt=E.createElement(\"a\");function Ft(o){return function(e,t){\"string\"!=typeof e&&(t=e,e=\"*\");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])\"+\"===n[0]?(n=n.slice(1)||\"*\",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return\"string\"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s[\"*\"]&&l(\"*\")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":It,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){\"object\"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks(\"once memory\"),w=v.statusCode||{},a={},s={},u=\"canceled\",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+\" \"]=(n[t[1].toLowerCase()+\" \"]||[]).concat(t[2])}t=n[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+\"\").replace(Pt,Tt.protocol+\"//\"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||\"*\").toLowerCase().match(P)||[\"\"],null==v.crossDomain){r=E.createElement(\"a\");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+\"//\"+Wt.host!=r.protocol+\"//\"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&\"string\"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger(\"ajaxStart\"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,\"\"),v.hasContent?v.data&&v.processData&&0===(v.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(v.data=v.data.replace(jt,\"+\")):(o=v.url.slice(f.length),v.data&&(v.processData||\"string\"==typeof v.data)&&(f+=(Et.test(f)?\"&\":\"?\")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,\"$1\"),o=(Et.test(f)?\"&\":\"?\")+\"_=\"+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader(\"If-Modified-Since\",S.lastModified[f]),S.etag[f]&&T.setRequestHeader(\"If-None-Match\",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader(\"Content-Type\",v.contentType),T.setRequestHeader(\"Accept\",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+(\"*\"!==v.dataTypes[0]?\", \"+It+\"; q=0.01\":\"\"):v.accepts[\"*\"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u=\"abort\",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger(\"ajaxSend\",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort(\"timeout\")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,\"No Transport\");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||\"\",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while(\"*\"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray(\"script\",v.dataTypes)&&(v.converters[\"text script\"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e[\"throws\"])t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader(\"Last-Modified\"))&&(S.lastModified[f]=u),(u=T.getResponseHeader(\"etag\"))&&(S.etag[f]=u)),204===e||\"HEAD\"===v.type?l=\"nocontent\":304===e?l=\"notmodified\":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l=\"error\",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+\"\",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?\"ajaxSuccess\":\"ajaxError\",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger(\"ajaxComplete\",[T,v]),--S.active||S.event.trigger(\"ajaxStop\")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,\"json\")},getScript:function(e,t){return S.get(e,void 0,t,\"script\")}}),S.each([\"get\",\"post\"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&\"withCredentials\"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e[\"X-Requested-With\"]||(e[\"X-Requested-With\"]=\"XMLHttpRequest\"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,\"abort\"===e?r.abort():\"error\"===e?\"number\"!=typeof r.status?t(0,\"error\"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,\"text\"!==(r.responseType||\"text\")||\"string\"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o(\"error\"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o(\"abort\");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")}),S.ajaxTransport(\"script\",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S(\"<script>\").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on(\"load error\",i=function(e){r.remove(),i=null,e&&t(\"error\"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=Xt.pop()||S.expando+\"_\"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Vt.test(e.data)&&\"data\");if(a||\"jsonp\"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,\"$1\"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||S.error(r+\" was not called\"),o[0]},e.dataTypes[0]=\"json\",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"<form></form><form></form>\",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return\"string\"!=typeof e?[]:(\"boolean\"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument(\"\")).createElement(\"base\")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&\"object\"==typeof t&&(i=\"POST\"),0<a.length&&S.ajax({url:e,type:i||\"GET\",dataType:\"html\",data:t}).done(function(e){o=arguments,a.html(r?S(\"<div>\").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,\"position\"),c=S(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=S.css(e,\"top\"),u=S.css(e,\"left\"),(\"absolute\"===l||\"fixed\"===l)&&-1<(o+u).indexOf(\"auto\")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),\"using\"in t?t.using.call(e,f):(\"number\"==typeof f.top&&(f.top+=\"px\"),\"number\"==typeof f.left&&(f.left+=\"px\"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if(\"fixed\"===S.css(r,\"position\"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&\"static\"===S.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,\"borderTopWidth\",!0),i.left+=S.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-S.css(r,\"marginTop\",!0),left:t.left-i.left-S.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===S.css(e,\"position\"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each([\"top\",\"left\"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+\"px\":t})}),S.each({Height:\"height\",Width:\"width\"},function(a,s){S.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||\"boolean\"!=typeof e),i=r||(!0===e||!0===t?\"margin\":\"border\");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf(\"outer\")?e[\"inner\"+a]:e.document.documentElement[\"client\"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body[\"scroll\"+a],r[\"scroll\"+a],e.body[\"offset\"+a],r[\"offset\"+a],r[\"client\"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?\"\":(e+\"\").replace(Gt,\"\")},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},\"undefined\"==typeof e&&(C.jQuery=C.$=S),S});\n/*\n * jQuery throttle / debounce - v1.1 - 3/7/2010\n * http://benalman.com/projects/jquery-throttle-debounce-plugin/\n * \n * Copyright (c) 2010 \"Cowboy\" Ben Alman\n * Dual licensed under the MIT and GPL licenses.\n * http://benalman.com/about/license/\n */\n(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!==\"boolean\"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);\n/*!\n * imagesLoaded PACKAGED v4.1.4\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n!function(e,t){\"function\"==typeof define&&define.amd?define(\"ev-emitter/ev-emitter\",t):\"object\"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}(\"undefined\"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o<i.length;o++){var r=i[o],s=n&&n[r];s&&(this.off(e,r),delete n[r]),r.apply(this,t)}return this}},t.allOff=function(){delete this._events,delete this._onceEvents},e}),function(e,t){\"use strict\";\"function\"==typeof define&&define.amd?define([\"ev-emitter/ev-emitter\"],function(i){return t(e,i)}):\"object\"==typeof module&&module.exports?module.exports=t(e,require(\"ev-emitter\")):e.imagesLoaded=t(e,e.EvEmitter)}(\"undefined\"!=typeof window?window:this,function(e,t){function i(e,t){for(var i in t)e[i]=t[i];return e}function n(e){if(Array.isArray(e))return e;var t=\"object\"==typeof e&&\"number\"==typeof e.length;return t?d.call(e):[e]}function o(e,t,r){if(!(this instanceof o))return new o(e,t,r);var s=e;return\"string\"==typeof e&&(s=document.querySelectorAll(e)),s?(this.elements=n(s),this.options=i({},this.options),\"function\"==typeof t?r=t:i(this.options,t),r&&this.on(\"always\",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(this.check.bind(this))):void a.error(\"Bad element for imagesLoaded \"+(s||e))}function r(e){this.img=e}function s(e,t){this.url=e,this.element=t,this.img=new Image}var h=e.jQuery,a=e.console,d=Array.prototype.slice;o.prototype=Object.create(t.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(e){\"IMG\"==e.nodeName&&this.addImage(e),this.options.background===!0&&this.addElementBackgroundImages(e);var t=e.nodeType;if(t&&u[t]){for(var i=e.querySelectorAll(\"img\"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if(\"string\"==typeof this.options.background){var r=e.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var u={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(e){var t=getComputedStyle(e);if(t)for(var i=/url\\((['\"])?(.*?)\\1\\)/gi,n=i.exec(t.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,e),n=i.exec(t.backgroundImage)}},o.prototype.addImage=function(e){var t=new r(e);this.images.push(t)},o.prototype.addBackground=function(e,t){var i=new s(e,t);this.images.push(i)},o.prototype.check=function(){function e(e,i,n){setTimeout(function(){t.progress(e,i,n)})}var t=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(t){t.once(\"progress\",e),t.check()}):void this.complete()},o.prototype.progress=function(e,t,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emitEvent(\"progress\",[this,e,t]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,e),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log(\"progress: \"+i,e,t)},o.prototype.complete=function(){var e=this.hasAnyBroken?\"fail\":\"done\";if(this.isComplete=!0,this.emitEvent(e,[this]),this.emitEvent(\"always\",[this]),this.jqDeferred){var t=this.hasAnyBroken?\"reject\":\"resolve\";this.jqDeferred[t](this)}},r.prototype=Object.create(t.prototype),r.prototype.check=function(){var e=this.getIsImageComplete();return e?void this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"):(this.proxyImage=new Image,this.proxyImage.addEventListener(\"load\",this),this.proxyImage.addEventListener(\"error\",this),this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},r.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.img,t])},r.prototype.handleEvent=function(e){var t=\"on\"+e.type;this[t]&&this[t](e)},r.prototype.onload=function(){this.confirm(!0,\"onload\"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,\"onerror\"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener(\"load\",this),this.proxyImage.removeEventListener(\"error\",this),this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener(\"load\",this),this.img.addEventListener(\"error\",this),this.img.src=this.url;var e=this.getIsImageComplete();e&&(this.confirm(0!==this.img.naturalWidth,\"naturalWidth\"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener(\"load\",this),this.img.removeEventListener(\"error\",this)},s.prototype.confirm=function(e,t){this.isLoaded=e,this.emitEvent(\"progress\",[this,this.element,t])},o.makeJQueryPlugin=function(t){t=t||e.jQuery,t&&(h=t,h.fn.imagesLoaded=function(e,t){var i=new o(this,e,t);return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o});\n/*! lz-string-1.3.3-min.js | (c) 2013 Pieroxy | Licensed under a WTFPL license */\nvar LZString={_keyStr:\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",_f:String.fromCharCode,compressToBase64:function(e){if(e==null)return\"\";var t=\"\";var n,r,i,s,o,u,a;var f=0;e=LZString.compress(e);while(f<e.length*2){if(f%2==0){n=e.charCodeAt(f/2)>>8;r=e.charCodeAt(f/2)&255;if(f/2+1<e.length)i=e.charCodeAt(f/2+1)>>8;else i=NaN}else{n=e.charCodeAt((f-1)/2)&255;if((f+1)/2<e.length){r=e.charCodeAt((f+1)/2)>>8;i=e.charCodeAt((f+1)/2)&255}else r=i=NaN}f+=3;s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+LZString._keyStr.charAt(s)+LZString._keyStr.charAt(o)+LZString._keyStr.charAt(u)+LZString._keyStr.charAt(a)}return t},decompressFromBase64:function(e){if(e==null)return\"\";var t=\"\",n=0,r,i,s,o,u,a,f,l,c=0,h=LZString._f;e=e.replace(/[^A-Za-z0-9\\+\\/\\=]/g,\"\");while(c<e.length){u=LZString._keyStr.indexOf(e.charAt(c++));a=LZString._keyStr.indexOf(e.charAt(c++));f=LZString._keyStr.indexOf(e.charAt(c++));l=LZString._keyStr.indexOf(e.charAt(c++));i=u<<2|a>>4;s=(a&15)<<4|f>>2;o=(f&3)<<6|l;if(n%2==0){r=i<<8;if(f!=64){t+=h(r|s)}if(l!=64){r=o<<8}}else{t=t+h(r|i);if(f!=64){r=s<<8}if(l!=64){t+=h(r|o)}}n+=3}return LZString.decompress(t)},compressToUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i,s=0,o=LZString._f;e=LZString.compress(e);for(n=0;n<e.length;n++){r=e.charCodeAt(n);switch(s++){case 0:t+=o((r>>1)+32);i=(r&1)<<14;break;case 1:t+=o(i+(r>>2)+32);i=(r&3)<<13;break;case 2:t+=o(i+(r>>3)+32);i=(r&7)<<12;break;case 3:t+=o(i+(r>>4)+32);i=(r&15)<<11;break;case 4:t+=o(i+(r>>5)+32);i=(r&31)<<10;break;case 5:t+=o(i+(r>>6)+32);i=(r&63)<<9;break;case 6:t+=o(i+(r>>7)+32);i=(r&127)<<8;break;case 7:t+=o(i+(r>>8)+32);i=(r&255)<<7;break;case 8:t+=o(i+(r>>9)+32);i=(r&511)<<6;break;case 9:t+=o(i+(r>>10)+32);i=(r&1023)<<5;break;case 10:t+=o(i+(r>>11)+32);i=(r&2047)<<4;break;case 11:t+=o(i+(r>>12)+32);i=(r&4095)<<3;break;case 12:t+=o(i+(r>>13)+32);i=(r&8191)<<2;break;case 13:t+=o(i+(r>>14)+32);i=(r&16383)<<1;break;case 14:t+=o(i+(r>>15)+32,(r&32767)+32);s=0;break}}return t+o(i+32)},decompressFromUTF16:function(e){if(e==null)return\"\";var t=\"\",n,r,i=0,s=0,o=LZString._f;while(s<e.length){r=e.charCodeAt(s)-32;switch(i++){case 0:n=r<<1;break;case 1:t+=o(n|r>>14);n=(r&16383)<<2;break;case 2:t+=o(n|r>>13);n=(r&8191)<<3;break;case 3:t+=o(n|r>>12);n=(r&4095)<<4;break;case 4:t+=o(n|r>>11);n=(r&2047)<<5;break;case 5:t+=o(n|r>>10);n=(r&1023)<<6;break;case 6:t+=o(n|r>>9);n=(r&511)<<7;break;case 7:t+=o(n|r>>8);n=(r&255)<<8;break;case 8:t+=o(n|r>>7);n=(r&127)<<9;break;case 9:t+=o(n|r>>6);n=(r&63)<<10;break;case 10:t+=o(n|r>>5);n=(r&31)<<11;break;case 11:t+=o(n|r>>4);n=(r&15)<<12;break;case 12:t+=o(n|r>>3);n=(r&7)<<13;break;case 13:t+=o(n|r>>2);n=(r&3)<<14;break;case 14:t+=o(n|r>>1);n=(r&1)<<15;break;case 15:t+=o(n|r);i=0;break}s++}return LZString.decompress(t)},compress:function(e){if(e==null)return\"\";var t,n,r={},i={},s=\"\",o=\"\",u=\"\",a=2,f=3,l=2,c=\"\",h=0,p=0,d,v=LZString._f;for(d=0;d<e.length;d+=1){s=e.charAt(d);if(!Object.prototype.hasOwnProperty.call(r,s)){r[s]=f++;i[s]=true}o=u+s;if(Object.prototype.hasOwnProperty.call(r,o)){u=o}else{if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}r[o]=f++;u=String(s)}}if(u!==\"\"){if(Object.prototype.hasOwnProperty.call(i,u)){if(u.charCodeAt(0)<256){for(t=0;t<l;t++){h=h<<1;if(p==15){p=0;c+=v(h);h=0}else{p++}}n=u.charCodeAt(0);for(t=0;t<8;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}else{n=1;for(t=0;t<l;t++){h=h<<1|n;if(p==15){p=0;c+=v(h);h=0}else{p++}n=0}n=u.charCodeAt(0);for(t=0;t<16;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}delete i[u]}else{n=r[u];for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}}a--;if(a==0){a=Math.pow(2,l);l++}}n=2;for(t=0;t<l;t++){h=h<<1|n&1;if(p==15){p=0;c+=v(h);h=0}else{p++}n=n>>1}while(true){h=h<<1;if(p==15){c+=v(h);break}else p++}return c},decompress:function(e){if(e==null)return\"\";if(e==\"\")return null;var t=[],n,r=4,i=4,s=3,o=\"\",u=\"\",a,f,l,c,h,p,d,v=LZString._f,m={string:e,val:e.charCodeAt(0),position:32768,index:1};for(a=0;a<3;a+=1){t[a]=a}l=0;h=Math.pow(2,2);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(n=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}d=v(l);break;case 2:return\"\"}t[3]=d;f=u=d;while(true){if(m.index>m.string.length){return\"\"}l=0;h=Math.pow(2,s);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}switch(d=l){case 0:l=0;h=Math.pow(2,8);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 1:l=0;h=Math.pow(2,16);p=1;while(p!=h){c=m.val&m.position;m.position>>=1;if(m.position==0){m.position=32768;m.val=m.string.charCodeAt(m.index++)}l|=(c>0?1:0)*p;p<<=1}t[i++]=v(l);d=i-1;r--;break;case 2:return u}if(r==0){r=Math.pow(2,s);s++}if(t[d]){o=t[d]}else{if(d===i){o=f+f.charAt(0)}else{return null}}u+=o;t[i++]=f+o.charAt(0);r--;f=o;if(r==0){r=Math.pow(2,s);s++}}}};if(typeof module!==\"undefined\"&&module!=null){module.exports=LZString}\n/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */\nvar saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||function(e){\"use strict\";var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=e.URL||e.webkitURL||e,i=t.createElementNS(\"http://www.w3.org/1999/xhtml\",\"a\"),s=\"download\"in i,o=function(n){var r=t.createEvent(\"MouseEvents\");r.initMouseEvent(\"click\",true,false,e,0,0,0,0,0,false,false,false,false,0,null);n.dispatchEvent(r)},u=e.webkitRequestFileSystem,a=e.requestFileSystem||u||e.mozRequestFileSystem,f=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},l=\"application/octet-stream\",c=0,h=[],p=function(){var e=h.length;while(e--){var t=h[e];if(typeof t===\"string\"){r.revokeObjectURL(t)}else{t.remove()}}h.length=0},d=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var i=e[\"on\"+t[r]];if(typeof i===\"function\"){try{i.call(e,n||e)}catch(s){f(s)}}}},v=function(t,r){var f=this,p=t.type,v=false,m,g,y=function(){var e=n().createObjectURL(t);h.push(e);return e},b=function(){d(f,\"writestart progress write writeend\".split(\" \"))},w=function(){if(v||!m){m=y(t)}if(g){g.location.href=m}else{window.open(m,\"_blank\")}f.readyState=f.DONE;b()},E=function(e){return function(){if(f.readyState!==f.DONE){return e.apply(this,arguments)}}},S={create:true,exclusive:false},x;f.readyState=f.INIT;if(!r){r=\"download\"}if(s){m=y(t);i.href=m;i.download=r;o(i);f.readyState=f.DONE;b();return}if(e.chrome&&p&&p!==l){x=t.slice||t.webkitSlice;t=x.call(t,0,t.size,l);v=true}if(u&&r!==\"download\"){r+=\".download\"}if(p===l||u){g=e}if(!a){w();return}c+=t.size;a(e.TEMPORARY,c,E(function(e){e.root.getDirectory(\"saved\",S,E(function(e){var n=function(){e.getFile(r,S,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){g.location.href=e.toURL();h.push(e);f.readyState=f.DONE;d(f,\"writeend\",t)};n.onerror=function(){var e=n.error;if(e.code!==e.ABORT_ERR){w()}};\"writestart progress write abort\".split(\" \").forEach(function(e){n[\"on\"+e]=f[\"on\"+e]});n.write(t);f.abort=function(){n.abort();f.readyState=f.DONE};f.readyState=f.WRITING}),w)}),w)};e.getFile(r,{create:false},E(function(e){e.remove();n()}),E(function(e){if(e.code===e.NOT_FOUND_ERR){n()}else{w()}}))}),w)}),w)},m=v.prototype,g=function(e,t){return new v(e,t)};m.abort=function(){var e=this;e.readyState=e.DONE;d(e,\"abort\")};m.readyState=m.INIT=0;m.WRITING=1;m.DONE=2;m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null;e.addEventListener(\"unload\",p,false);return g}(self)\n/*! seedrandom.js v2.3.3 | (c) 2013 David Bau, all rights reserved. | Licensed under a BSD-style license */\n!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=r&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=r&f+1],c=c*d+h[r&(h[f]=h[g=r&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&\"object\"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:\"string\"==e?a:a+\"\\0\"}function l(a,b){for(var c,d=a+\"\",e=0;e<d.length;)b[r&e]=r&(c^=19*b[r&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return a.crypto.getRandomValues(c=new Uint8Array(d)),n(c)}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o=c.pow(d,e),p=c.pow(2,f),q=2*p,r=d-1,s=c[\"seed\"+i]=function(a,f,g){var h=[],r=l(k(f?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=o,c=0;p>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=q;)a/=2,b/=2,c>>>=1;return(a+c)/b},r,this==c)};l(c[i](),b),g&&g.exports?g.exports=s:h&&h.amd&&h(function(){return s})}(this,[],Math,256,6,52,\"object\"==typeof module&&module,\"function\"==typeof define&&define,\"random\");\n/*! console_hack.js | (c) 2015 Thomas Michael Edwards | Licensed under SugarCube's Simple BSD license */\n!function(){for(var methods=[\"assert\",\"clear\",\"count\",\"debug\",\"dir\",\"dirxml\",\"error\",\"exception\",\"group\",\"groupCollapsed\",\"groupEnd\",\"info\",\"log\",\"markTimeline\",\"profile\",\"profileEnd\",\"table\",\"time\",\"timeEnd\",\"timeline\",\"timelineEnd\",\"timeStamp\",\"trace\",\"warn\"],length=methods.length,noop=function(){},console=window.console=window.console||{};length--;){var method=methods[length];console[method]||(console[method]=noop)}}();\n}else{document.documentElement.setAttribute(\"data-init\", \"lacking\");}\n</script>\n<style id=\"style-normalize\" type=\"text/css\">/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS and IE text size adjust after device orientation change,\n *    without disabling user zoom.\n */\n\nhtml {\n  font-family: sans-serif; /* 1 */\n  -ms-text-size-adjust: 100%; /* 2 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n  margin: 0;\n}\n\n/* HTML5 display definitions\n   ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; /* 1 */\n  vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n/* Links\n   ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n  background-color: transparent;\n}\n\n/**\n * Improve readability of focused elements when they are also in an\n * active/hover state.\n */\n\na:active,\na:hover {\n  outline: 0;\n}\n\n/* Text-level semantics\n   ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n  font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n  font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n/* Embedded content\n   ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n  border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n/* Grouping content\n   ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n  margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n  overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n/* Forms\n   ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n *    Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; /* 1 */\n  font: inherit; /* 2 */\n  margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n  overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n *    and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n *    `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; /* 2 */\n  cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n  line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n */\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  box-sizing: content-box; /* 2 */\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n  border: 0; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n  overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n  font-weight: bold;\n}\n\n/* Tables\n   ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n</style>\n<style id=\"style-init-screen\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/init-screen.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@-webkit-keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@-o-keyframes init-loading-spin {\n\t0%   { -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n\n@keyframes init-loading-spin {\n\t0%   { -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); }\n\t100% { -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); }\n}\n#init-screen {\n\tdisplay: none;\n\tz-index: 500000;\n\tposition: fixed;\n\ttop: 0px;\n\tleft: 0px;\n\theight: 100%;\n\twidth: 100%;\n\tfont: 28px/1 Helmet, Freesans, sans-serif;\n\tfont-weight: bold;\n\tcolor: #eee;\n\tbackground-color: #111;\n\ttext-align: center;\n}\n#init-screen > div {\n\tdisplay: none;\n\tposition: relative;\n\tmargin: 0 auto;\n\tmax-width: 1136px;\n\ttop: 25%;\n}\nhtml[data-init=\"no-js\"] #init-screen, html[data-init=\"lacking\"] #init-screen, html[data-init=\"loading\"] #init-screen {\n\tdisplay: block;\n}\nhtml[data-init=\"no-js\"] #init-no-js, html[data-init=\"lacking\"] #init-lacking {\n\tdisplay: block;\n\tpadding: 0 1em;\n}\nhtml[data-init=\"no-js\"] #init-no-js {\n\tcolor: red;\n}\nhtml[data-init=\"loading\"] #init-loading {\n\tdisplay: block;\n\tborder: 24px solid transparent;\n\tborder-radius: 50%;\n\tborder-top-color: #7f7f7f;\n\tborder-bottom-color: #7f7f7f;\n\twidth: 100px;\n\theight: 100px;\n\t-webkit-animation: init-loading-spin 2s linear infinite;\n\t     -o-animation: init-loading-spin 2s linear infinite;\n\t        animation: init-loading-spin 2s linear infinite;\n}\nhtml[data-init=\"loading\"] #init-loading > div {\n\ttext-indent: 9999em;\n\toverflow: hidden;\n\twhite-space: nowrap;\n}\nhtml[data-init=\"loading\"] #ui-bar, html[data-init=\"loading\"] #passages {\n\tdisplay: none;\n}\n</style>\n<style id=\"style-font\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/font.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n@font-face {\n\t/*\n\t\ttme-fa-icons (2020-03-27)\n\n\t\t`tme-fa-icons` is a subset of the Font Awesome font v4.7.0 by Dave Gandy (http://fontawesome.com)\n\t\tand is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL).\n\t*/\n\tfont-family: \"tme-fa-icons\";\n\tsrc: url('data:application/octet-stream;base64,d09GRgABAAAAADLAAA8AAAAAWHgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+IEkIY21hcAAAAdgAAAG8AAAF3rob9jFjdnQgAAADlAAAABMAAAAgBtX/BGZwZ20AAAOoAAAFkAAAC3CKkZBZZ2FzcAAACTgAAAAIAAAACAAAABBnbHlmAAAJQAAAI6gAADv+gJOpzGhlYWQAACzoAAAAMwAAADYY1IZaaGhlYQAALRwAAAAgAAAAJAfCBClobXR4AAAtPAAAAJEAAAFMBfb/0WxvY2EAAC3QAAAAqAAAAKhjiHI5bWF4cAAALngAAAAgAAAAIAFjDA9uYW1lAAAumAAAAY0AAAL94+zEpHBvc3QAADAoAAACHAAAA11cG/YjcHJlcAAAMkQAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZNZgnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF4EMgf9z2KIYg5imAYUZgTJAQDSIQumAHic7dTVbltRAETR7dpNCikzQ8rMzMxt/M39mHnsU1/TfZz5jFpaV7pXJunMDLAZmOqaZjD5y4Tx+uPTyeL5lG2L5zN+L94zG8+ztr7ulXH1fra4bvK9M79xiWW2sNXPbWeFHexkF7vZw172sZ8DHOQQhznCUY5xnBOc5BSnOcNZVjnHeS5wkUtc5gpX/f3r3OAmt7jNHe5yj/s84CGPeMwTnvKM57zgJa94zRve8o73fOAjn/jMF77yje/84Ce/WGPun1zi/2tlXKbp3Xyc44bFyZanSWokJDXOOjXSk/LUSXn+pEwCKTNBaqQqZU5IjX+XMjukTBEp80TKZJEyY6RMGylzR8oEkjKLpEwlKfNJyqSSMrOkTC8pc0zKRJMy26RMOSnzTsrkk7IDpGwDKXtByoaQsiukbA0p+0PKJpGyU6RsFyl7RmosQcrukbKFpOwjKZtJyo6Ssq2k7C0pG0zKLpOy1aTsNymbTsrOk7L9pNwBUi4CKbeBlCtByr0g5XKQckNIuSak3BVSLgwpt4aUq0PK/SHlEpFyk0i5TqTcKVIuFim3i5QrRso9I+WykXLjXOYNzP8BuAPUwHicY2BAAxIQyBz0PwuEARJsA90AeJytVml300YUHXlJnIQsJQstamHExGmwRiZswYAJQbJjIF2crZWgixQ76b7xid/gX/Nk2nPoN35a7xsvJJC053Cak6N3583VzNtlElqS2AvrkZSbL8XU1iaN7DwJ6YZNy1F8KDt7IWWKyd8FURCtltq3HYdERCJQta6wRBD7HlmaZHzoUUbLtqRXTcotPekuW+NBvVXffho6yrE7oaRmM3RoPbIlVRhVokimPVLSpmWo+itJK7y/wsxXzVDCiE4iabwZxtBI3htntMpoNbbjKIpsstwoUiSa4UEUeZTVEufkigkMygfNkPLKpxHlw/yIrNijnFawS7bT/L4vead3OT+xX29RtuRAH8iO7ODsdCVfhFtbYdy0k+0oVBF213dCbNnsVP9mj/KaRgO3KzK90IxgqXyFECs/ocz+IVktnE/5kkejWrKRE0HrZU7sSz6B1uOIKXHNGFnQ3dEJEdT9kjMM9pg+Hvzx3imWCxMCeBzLekclnAgTKWFzNEnaMHJgJWWLKqn1rpg45XVaxFvCfu3a0ZfOaONQd2I8Ww8dWzlRyfFoUqeZTJ3aSc2jKQ2ilHQmeMyvAyg/oklebWM1iZVH0zhmxoREIgIt3EtTQSw7saQpBM2jGb25G6a5di1apMkD9dyj9/TmVri501PaDvSzRn9Wp2I62AvT6WnkL/Fp2uUiRen66Rl+TOJB1gIykS02w5SDB2/9DtLL15YchdcG2O7t8yuofdZE8KQB+xvQHk/VKQlMhZhViFZAYq1rWZbJ1awWqcjUd0OaVr6s0wSKchwXx76Mcf1fMzOWmBK+34nTsyMuPXPtSwjTHHybdT2a16nFcgFxZnlOp1mW7+s0x/IDneZZntfpCEtbp6MsP9RpgeVHOh1jeUELmnTfwZCLMOQCDpAwhKUDQ1hegiEsFQxhuQhDWBZhCMslGMLyYxjCchmGsLysZdXUU0nj2plYBmxCYGKOHrnMReVqKrlUQrtoVGpDnhJulVQUz6p/ZaBePPKGObAWSJfIml8xzpWPRuX41hUtbxo7V8Cx6m8fjvY58VLWi4U/Bf/V1lQlvWLNw5Or8BuGnmwnqjapeHRNl89VPbr+X1RUWAv0G0iFWCjKsmxwZyKEjzqdhmqglUPMbMw8tOt1y5qfw/03MUIWUP34NxQaC9yDTllJWe3grNXX27LcO4NyOBMsSTE38/pW+CIjs9J+kVnKno98HnAFjEpl2GoDrRW82ScxD5neJM8EcVtRNkja2M4EiQ0c84B5850EJmHqqg3kTuGGDfgFYW7BeSdconqjLIfuRezzKKT8W6fiRPaoaIzAs9kbYa/vQspvcQwkNPmlfgxUFaGpGDUV0DRSbqgGX8bZum1Cxg70Iyp2w7Ks4sPHFveVkm0ZhHykiNWjo5/WXqJOqtx+ZhSX752+BcEgNTF/e990cZDKu1rJMkdtA1O3GpVT15pD41WH6uZR9b3j7BM5a5puuiceel/TqtvBxVwssPZtDtJSJhfU9WGFDaLLxaVQ6mU0Se+4BxgWGNDvUIqN/6v62HyeK1WF0XEk307Ut9HnYAz8D9h/R/UD0Pdj6HINLs/3mhOfbvThbJmuohfrp+g3MGutuVm6BtzQdAPiIUetjrjKDXynBnF6pLkc6SHgY90V4gHAJoDF4BPdtYzmUwCj+Yw5PsDnzGHQZA6DLeYw2GbOGsAOcxjsMofBHnMYfMGcdYAvmcMgZA6DiDkMnjAnAHjKHAZfMYfB18xh8A1z7gN8yxwGMXMYJMxhsK/p1jDMLV7QXaC2QVWgA1NPWNzD4lBTZcj+jheG/b1BzP7BIKb+qOn2kPoTLwz1Z4OY+otBTP1V050h9TdeGOrvBjH1D4OY+ky/GMtlBr+MfJcKB5RdbD7n74n3D9vFQLkAAQAB//8AD3icrXsLcFzlleZ//v+++nb37dfte/VotVrdrW5JltuKpO6WJVluPyXbsmOEbGRjO4KVjWPZlsM4QIU4IYGlgCE2yxqKIQkJg2GreKQCTCZb1CZkiswjzOwOmSTATNVWTZLdDUwSMrVDsomD23vOf69assFAqgbk+773P4//nPOdc/5mwNjFl8WjosbaWblmxiOmIlTGYXzb11M7Z2ohAMbZCcZ5iG9prZl4whfwGju05xtttitUdwXYEUgkbQtWga5lC+XBaqJI285qpb8dVEc8Gnm5L5QM/f58yAlB399a7dD0mWAmdAqaMvBmKPKX9TdDwSjod9yhx03FAPcvI6Gk2lV33XoXUtKgL8C6Wa0WSzXbVjhg6JoqIPShCK0FOvOuE48K1V4B1VUQAVevugmP7Fz2CmTzW5/8+ZGP/+Kp7h/8oI4MuOZ7M5B9IvujH2Wf+PnCAjzn8ZK6AieMKRcvXnxWWSWCzGARlPcqtqs21WZzEBFgYIVDAYWzVJIrXBlH+hXGlWNM00GAJmYZ8sKBzTJFVZVppijqDFMVdTIWLa0o5JrdaHusPZGIG1IjFhQGK2mAZEe56kJnR1bTY7ZT7eivFGODBTdma3pHtlCNDVbwmgMHx/aO4R8ffeet5/ZCG6TfuV03IaSJU3oIzKsGO9+5PV+BwU5xqnOQx1aO8fW71yvD9fPn55/fA22PmsaFvfSgwZ8wzPiFvZ2DUMnzJ2hHVDMmHucPsSRrq7UQo4DMAV/AW7CANw/Zru3NIx20bBEpXwukArlxxOPReibaG63/SyQyiftzcBy3k1HuOHgjEgFHnkYfh4VodDJC4138Ff8hv5PlWbrWmm2O6grNEgGNCWGnbFtRm1Z04gzQPPWvkqMWF4eWU8DB267Df+gNe84bBvfRpfNI5Ny5yAmHDh5/PPLuByMlekDS9DsRR73nWHetwBShSO5PqCBQONO4E2wGlSvYZG6gMzeQ09SWFZC0tWION3oOVVXGTbGM+hrAzQj0O25yoN8R8YzzRsaZdzLwhpsGPEm783hAJ9+kq286eDX5pn/VydDjvk7i8EuchU21JJ3DNOqFzeABmywPcNXxhu/wB+3whrqwVw7Cn1j8vPfdDH6O7PR3fDV/Cb85wua/waSit309grZJHHOhHEQ9CA7zTGVCUb0ZvXzc1lrRe5Af+4An0aSL9op0IU9y6vRFtAYGKy7KJGFbQrdg2dXqGAwWilld0zUy9xLqd6A/zeFuy7gzYFmBOwOhZ6NNhZakm8YTI7S1pyM1mM032V26qevXGlzZ8+TKvROlB/BBkO9ACDalB7OZuBnuC5tRcAItpalENNOfhajVH1A2a1HjbHZ4N4r54oWLz4qPoe4jrMo2sonapm5QRQBQ3XycKVxwRRxjQuVCnWc6mrzOZ5FvBiraOmia5FubYRpok8n0ioRTKOQMNbWiszxYWAFZrQ1sB+dDJTFYghzyiBN3oB857ndIAha4Se/+YGUtjAkXXUO2xPE2eoe3TWPeMOXmzNi+j9w1HghvVbSAmu4c6nFac6MgbzXFU2baDr1241+98TfHtU/9t7df+MzU4msmfPYj06Wbw8Gqohda0/FkSyiyvtPGG/FsMKq1pLqmPvndkye/+y+08eYIHEJZpFmJra2Nop/VWuKcCwPvgRhnGlOFps6iIdAEmFVAKh53jblZyBU6nAFdbfX03tGwioZ9oGnEyDSWTQCyFzhoR+s/jdjgWLmc3L2K21zEmXCsM5aDm4g9Ny5v0PZVb+dY9Rcd/sl5eTiPj0nTaeiU+BhhW2qbuzJcUy2Nc2gCrjBkxWCqZiArGgOuwSzjAlDb5L91XfpvfYbpij6ZRIUWYoVcLqC2+UqNLddsMrekY3GZztVl2uQjpl4xjedIM+Uybc/r5viShuHgcgXuwQtpuoMHz8utCe/s8S+Y/7xMaYu2DX1yDrez3lp3cyyKdop+HLcLi878Uh9SKOSlj73cOIUFRVKVKHhaSS9JP452RCcYoJ3w85bz8Ckp9VN4xeZj9atpH4FnPCV4OtiDNIVYCrWwsbauK81VxUlaQnA+riFBisoUsiU0LbQpASCmmRAwg1MNJoH19uQ6WpriUV1jIQjphA5QjosiTfiy1tF+kPJKMTFYpAu6lkx40odnTv7VjYtC/QtTJyGH9HmMnKamGrcaqmaagRsMUwR9WeLmwsQREvIRevSvYa8uVFXo9Sc0w5A8/Rp5+jcxhXLuYAOIx/pbY+ggFp0FV8lNoLNAEQvyDkwBZR9T1ZC6xR0qFgsybix3DJcR7iIGwCsCHHQM0nYKVSBXQZPnqG7KDZiqrn8K524gpN9ghAx42k4GsvF3nohnA0kbnglkC9mrl7h4Db2TommKcVE1QOPRd97K5WJxsKO5nIjHbHtx/og3kK8iWys1he/EkKlqCbFOB6iKQp5Q5Yp6DB0AsjmPvHEFSGsoFraPaVpI2zLc2lmudErjJ/eeQwbKPoRBZulcwhwXYc6i1SdiEpiCF+KrFOIrAzKaHdhQ79tw4MAGuJv4rt8sQQu80jloGnnDfNVJBa+vn1WjSg1d8NHrg44FbTgdJ5+T77yy/gDI5wY7633yTbI7+CdyMDyEL2paDd0BvZjyfIYXH2/H+aqjDfXUisgnk3rFYMePoyZBegWanQpM5hKdlUSU1JnoQH2iG1d90IYADi0JPV2/oxMEeB7apm+aBngFw/ObMjzHzv7dgzyOh48fHZnmO9c8Wv+2RAGwHiP20UNnzx46mvYxyaMixDpZX22lQqQgRDqG4kYcMo92cpypAOo0Ti+iSYXJRGc5l8wvQhPfqpE0SYa7nDwkeQyh26OOlbecqVumoCwJ8+mDm+9/5T4eOyOt+4wk8WjavYTIG+7nD5Li2cWv8zGkMYoz51q2u3Y1JQSwfcumSn9JUxW0CZIWQ5isqKCo8zoGFPybx/lyHD2w4AaCBw7Ap1HIxAaHyZlr1tXG1owMt7h5Ox5QmxF4Iv6vDhaQoTFAYADuYIlnLa7b7RwtiOBDtVK1dbyCMXXxn5azOIIKfLFKaQP9K3HKIMYEQosvhkIWH23TQ9wIpCq9M4WxycnJsQIUYrEJ/bPGuOZohfHVzdmMaAmHm418c7DU3xdoyYPebFktPJtpHu7fefjw4R0VHiNTa06ZUTPe09a1sdTUVNrYtbo3nth11VW7tBa1d/U1a1t71rdG2u1IJNkWDYdbUs0pnnFT+OloWzISsdsjqVpvy9prqrNjed41POfPx2+J7TIXaWP5Wgf5bvToBEvpppQYOXPOJoda4gTQE6TsNK8sAvUSSHBFygfnfM9wL+8aK/Bddv0tZ8SufyKZ7ml7s20iCWdsPpPu4YVaXuur/2M6WX8riReTE21vtPUAnn4iyTxdf0vJ+/QM0pwsUL4D5BmUBQEeXpaU4W6RtExzN1In7eT9qHPpIoUdqAzSrdyHIPo1JNRps1uioCWJyIm2U/JG8oO4aZMXozF8b4RupRd5JJv7Nn8ZeUyxjlrakhJHcwO2gHMUDgGLR0MmS0GrIoMRWos0K81LUnPZYgmkw6sM8HtCoQTaT9y0Wu3f/MZuCYfijhMPhUVQNdL2hY8kMroS/8UvEqqeSfC/xzPV07k3vsqyrL+2ykYnhJjEz45OLIdbu4ncrbrWhImzltU7oqqaXAEdMWiHxPsSdvuF70K6/xf6FejjL13YBvFVP+UXrkwnyelZjBcltoLypQIoEmsoiDUw4C2QFS/I/J6yx5ybWOOqaMad0kJlKo+ElAdLqvRTMqOsgucuM64j3hgHU1H1mIkQ084Oje3eXT1lZwL1nwaD0BZMNfFTcHpv+sf7v6LEo4oZMlRbFNqH9tb60nEN0UkQ0mYawZNpR878eFuD1lWsl/Jb9J5iMbPlDKnkh3raBmS66U1M9Jm5rKUmMel1dKLOcxrVSgf5dJqU4o0g0pJyTlV37x4bytpCATOG8VUT4+m9cPoURiakE34ZMes/wbB0Roun+2p7h9oLSlwzQqZq2eIr+xe2/Rhp5QF8hHn5Hh/FfM9ibs32UrNG+l22ZbrnJ8N+fERAcD6YCp6nuPc2Aq/vWXiMfzKaye/BDfzTLPwe33MT8ns4P/y0uiLBdyZw3qRvNvGZegjF6H+escXvreOnkD7zm7IkADTbJEkU4sdw1jmwzpTj42smHLB82ijenjfx9YsXkccR+B5+I1azGtQk+4maTpnLlsBL75E7O1IPea+mzfPmUwQt00FJoG+nPxN/zruZzZprTljyx2GpXiHdYWBZtcINeB+2xVP16/GT9euDwf00TbqgK5gK7QvCmfp/wPn15WDa3BcM1l/Hy8F9wZQ31nf4Q2IjjrXyG+BnzXnMmqXiTuB5CLa01gINjvb8mesSTwHuSAEtzm8anp+uvwbdprkfhUs0wCNIxH6TP1l/vf6aPDThK0TXI5I+tjj+SX/8wIcaPxWX4y+CqsCiEIiAIzhsKrgfh+6qv+4L4RETPl6/zqMKukki9AA96OseZb3Zk7UKVKxZKt65tpR1J9p1sTGgP5Z4ah+KEzl73R/xEfr+I8H5fchlN/Jr0n0c3fSGkrz+vbhd5m2ZWltThDeKUaKh3WTZXWauS+WomBdMkjGvOCRuT9YfcIZxk0x24/5cT3q8redxe8TpTsLn03b9LIaFo/I0eQ7uxtjQm6rffI4elnTcJfbwX/qRFyG+Vw+SaJdyFIpugk0WXCcnaZFo1323viNAWHZPvk1+ugfjTf2sbcPR5LDT442bhwPj6Z5z9qi9wr8B85Ji51yXb3tISxlpSUtaZEYnFX8ZbHI6C8vk4ll2h5/YdRQbM0CUbSmXHsd5rH5zWz7fBnc/5iA1NLANwyQZ2+5JjiYfQ4mle+BxJA1prT9g+770dv4zTz9RnTPp+JlY4DJKoroOucmyT4fnsGIeFC3H9EtmB+qnGyP6413tPvs4BhIwipskDKakeIg+KKTgbl92JBkkHn1J3c8t+9ia2vBKhMFkGBJtEjrGDIzKE1SeAgTJiDAVRYpKmSGwMlksJor5gUWgjKlXYS0gurS4neaYflXJnaWBUjDKNAVhkWoF1WhHz7uJ2PapE8NHJkulySPD62/qVmLapMq10a997JqvnphQarc8dO3UQ2smYr38pfOWszK6fTs+eBKfHy4j8t2uWNrWnbDx5CNfe+TkxrHVE/EEW4ynxM9H2FhtpAeE2tnGFUH1J4xXChxD5hACLNXbLgOA3WU353rI37Y44SipbwdxwED/mEC0rOmuIzmlGkw7SBzNxZ6NN3519+zXRhV1Uosp3TdtGD68s4eXJo8uzHVtjyXc85gC9MYmRh+euuaRk+vhAG43Tm3RLGW7Clp52Oesq3N7dKVjnW9KxCdWjyFvi/nUs+Ja5CnPxtn+2t4NnVwLrELw76JqDMz1MacMGJoR0I5RVsA1lR/DLEdonLJLQWnOMcSamKlrszLnWWZ3mzcVOjsrnYWynTfVNmQ6aQFxrWtL9SQtgjqsojbxf5lp+qquSF1rVGyk02qZZEEKRkPdlv9ff3LVQ6MTFMas8xSft3fNVbd+vqg1KaF5w7RwBsirUye24UVXDS3oIcj/nz+56mF6qQlUAQ++gGoNytcxFG7P98DWMXMoHIL/6l/Z7p1riv8ko2oiyiru129WsSrmUjO1XdtWc0Pr7miOoWOV9YUQ07WQPmsCisWYDge5puAUAI3NIk6EQACmaQ+BGRaAwOSemV1TH90+vnlDrZBNFOi/nEUlLL96lYx5VZLqB5zDQLFQzGm6KuUX89L6YqxRuaPsCwXYTlJE1EVJt9ycWTo8bereoW7Wv38ewfOzmgI/N42Kn53LatjTxUCv85zbEyg+Y5hTcDddq99M2ysc8/51lABfjZ++8KvSxvUlnpCj7U+mIG3vR8yhXSbXEbaB3cDmatdds4lrhi9Z8hskNo2hkI+RQHWN6fMIUwKGFZiNhDmCNq6BoR1gejCoTzNdD86woB6cPDh33YFr91w99dHJLePr1tp525NylKZkzJtthLplN+ADzhOxjpiN1trRPwb/vhKfqIcMg8Mr3DDqd38o4cM36y9KWa+Tsn7v4/ocj134Vcg2TZsf/ABFKH4tYwrtObZYYxoF3QiQC+PjATwUho5+W8MkQlOOqUDxnlMNjZpsfB8zjJCxZe2afKeTjXeuboqT2XcOlsACB6XROFhWch7AeNy/VsZfx4fO5Aj8EjTVRPjLdtrmTS1NX7Azce6kmjZnnHf+1iuBiG0duzsmQTiZPzfjEoTGAqZ7xqt/nmmai8gXOWb0/sG9z8vyyPNOZjKDf9DlRgmQR93keVlHOU/9RTkfH8W8ieTQzWpsc21DGRM0Xw4soAUWDEDPtMB0oS9I5qeXC0PhMySPyTWjuYFctn9JEgWLp6FSXdz71TeSgzuQBmnXVHPXGqWhIhXkF+H2lQXxVqiSO5OthN9CQQSazmBOhdycwSgoZRJvQ28Yz8SVltDiwd3PUxMLN9De1dWehinH5783RvA9xhpyoHgnWAfGvE3sqtqOFT25rGIoMB4GBa0NAaepg2KYyiwVijQqFKGtopEeUFFYgQCboj0jZ8cCk7U1Q2W3MBBLjMRi0SCKxO0od6gD6Muon6Q35JGLDZS9DEpfLNZTA4aqrmrD0rwHCAO8Cs/Ur4a3J0Lql9WU4RfBJiYySfg+cviqaczLvipt59LuhbjXflPdSuRLUUd/9VV422jRv6SF/NbehYrcQ1p2AZ6jd80L5+kSRwE3WV+OVGTs9GvnU6yLDbJsrZ2aE+iijskqPtuHgTAktgz0r+xtaUaslZRRH6FKlRJrBwO9bK+lef8YXipg7j8GSJjM+GRPmSApRv6n//T4VrH3qqbRaNxoqoxSNMfwD6MV18yPulftrX+xZ7gXeka7vMCPgf2aQ8+N4bPuaKz7lg2LIGjjTT3x4T4jvubPYKL+cFtPTxscwm0DA+yVNaMjbFNt/cE9k+sUpoxgas8Gu1qjhG7GqUu+oAFel8B2gTDcgt+54If2XXv1VVsmVvRkM4m4TnnrYCGLtt5foc4F2rWO/NrIbxENvBHUy0WJBYqEg8hbyklAxoATvupfHMCZ3wAHaASEAFz/Y7pUPh+ZumWK7z65G1KGftgMJro0NbIzrOvbm1sCuhL9tBGKtrof1aLaZkdRjS4zYhzSDTDVw4bldnrPGtubWgKGiH0adR1JuR9VI/qErSgB72HMk0emp2+anr6F7kfTydZ+zdKSO0EdDRuTqaip3xAIjapaLa1aWqg/kmqNQEiXzza3ZFbqId3euezR4Iiqbkj5j7ZEIeTX7X4n9vLvLmKL2mAXILVUp0WPoyLCVJVGQ+/yNlGhjP8PNLq4y9vNy9rPrn+eW36OaPmdX0nTF7EIpu9XPlvW1XMgOiHbSHIL1ji1nsbpBdbAyXtlTRwjSHuCqyJvkjfAVEiMe317oeLEURlNKa/CI7tfVLxLp5qb4tFwUFVYJ3R6/STHqy1Xi1R9RLvvd6lSPIba14rZoo6zwa3w/7L18OGzRwC+N7B52+HD2zYPfA8OP3iIH9kyjkd4Fdwj9x85skUPzfXhQd9cSN96mB+97yjgoYUXvZ7kxYu/UW7mL7Eo+rwKK9byTKWsSWWz6N8VBaZxB5ShgDJZHerscm0J6L1at2fNGPH4CqQNZ+gAGTeaPZVNBB52IJ7nt/uI3N/B0707Do+8vmEH37rpdTLX8eEDd47Xr564Y3aIj+67azM8Q4dwYHjpHbJoOu1/8OkH++lk4s59Y2Lo+tsevG1ukA/N3uHb9f9TbkFebNRET61I/GEuOEtdCkxKcdfISpPptmSn01kdVNF4Y4NjHF1rWnh0cxWZKImsJdKcKPPIkZR5RIoYkcJ7dh665dDOHqV/4jgc2ILXkYz77zgwypGsH1zKst/L+ir6nJVsPdtR2zaC4aQTZC9CR4p0jigDQ6gmexJcUeeXdbS0xY7WslzDdpPF8uryAPX0L+tqoVNFD5QrFi7rbKGXwYnkJGCpwra4XMZe1th6JZcOCL0VgV445HepqL+l5jVdKMHP1VeH89a/WtYaK2/9Z/g4noyFYduzjf6WpSS0FIKDRovrC4aaQ9ZArQ9b1r/K58P0Yhi/4Msl4WPhFbUuzCTBb1OitsjmCVsAn+Qs3eokTJ1FeEQlI1nGXNlfnbS8tcc/sYz4wU8vsbd+lr/UII5u7mncOYBJ+WKc+5jsETe/L03AXCfmN4IlTVnCOYiVSxyWr5h45up7d/Lpu568c7ey4zRcu6yjzk9P3Xvu3im5qb9yaf98ab2AwZIsy0Zrq6nXxkG22zi121ScGiC71IoiZwcZqlAmzUCmvaU5GgkkzaQfoNCjlKimWnxvGhtR5b4r0roYFX59BZLFsvVB5NMRIVAGQ65bJoBsRuaFbLJM/0n3nZCLlZaWYKj+efWy88WezKv+yiG5dKnN23mX2uUJbi5bY+Rc4XgxBrHXGnlYb637gwLPEuXVBmD1ViiIyzgRfl5f9NddvfoB9NDxw0clWD9KV0FbdhMMjzfc+H7uJXEG8fo6Ns4O1q5fU+KCCq16PhXG3JuJcczFw6GwEaLSBXkQwNwF4DgLs1AgHDqA2FToAXEgCDpj+jTudDaDgUmnGsaG9bWx1UOVctJGzBlDf2HJWgZyh1HHkeV53RK5WC6Gc8fr45AzKSByxwlWHShTsxydSgfVLhALUZsXOiS8w0TlVOdJ006bC4ZayA43j7cN9WCqeCgYDTvGJzKnKG0Jn7ou6KSC18Frs8FUk2Jch1frv65/kWLdMMbf2fWfDKac4HFdaYpb8HY9ZDXZhnEylEgHP7t2L6YMcO46M22b111HA113zoFBDJQUpy/WLx6B36K+MxQdmlHBKerm0BIJKqUu68GgoLY0VSpexdChJiDC1WpC9qwK1cQYpS7U0BW0JAzeMrX6P+hRI2Dy4z/hqqmb4gS3jG8GLT77P1Ue5E4wfOFTFoioAS8NYVYZhv9umJapaPV6hct6wHdkHiowcqVYnvUivt5Qa+3vK/X2dBXy2Uy6pclBE4pR4XkwxWHTtq93vH+tv0l2U6pFvbNRePWrv51uBFbBWmgHbw/upXv+0LnRc1AxL/RjLrVgmvx/yP0Fq1KJxarV2A+PHct2HDvWwbvxJIYX60/THfzHrcdGH7shQm/iC2l6E/fXRumtaPU/ybeyx+p34UkVL0LJv9PAUHOomzL1w8q9uZaYoV3SIOoujHBKKii60eKMxhJHXUMgTQW0InVqqErq22AbYN6BHOMkFCMtlpWLDDc/0NM23tYLZ1uGMX5ZrWfPtkQj+chQ61lZiH+gZSiai0Sbz4JhDbeswXd2PSVr8E/twqtr8KXdu690Q2KpI6jHMMuhBjdRdbG9iQslEae6YVhDV7gBdOiHgK4iKtSERkv4yOpAP8ZMpimmNqtSRuUFG50FDD2AeaYRNrasGS0P2LTwuGDncpRJNtZ5Fpev85SVhMY6T3ewBJrtjAFIs8XZi95eSXOXLJkW75wKaXmE7afkst1TsjBDJxP3v3I//kG6Z9R+ce7WnfcfrvHRo6fPnT46CpteTMJZ7yXKMb2XTlHieMpspgUYLz+k3UsJV/LFTWNH7vvT08eHlfWHHtx+69yLSbZMRhHWhDn2SG0oYCC3qE/B0qAKuZqPg+b5XkErlwWmYkKlMkNY2eLmUAqF3OJinmKjrPIhuK1/W/IJ6z8kh5K1D2aK+/UTWqOzkz1fC4xVOgKKKqgjZqKV9nhTGGOgUGnZjiK4Mq8jJFKOMwJ0UxSLdlO/Ymvrtq8H3/UG+nBNcG0e7Vh79xt/yOf37KklDMPYaeyc3LZ1y8hwT671KoP8BApooL9agOqYUm2FATeRptTcdeQGxexmC3pWyw2u5ZSr4l9xsLCKW+DaaeqxVskXZql+XdTggY9NDrcHk331MoTzqZSj3fGlCe3GxJQT6IsGjeBkQOGQO53v+VKSb9E1EVMQ5vKs2/R7axiimWAmiVCh4/MZ1eYrecvvMWx9oa71KppmNkVhBs6G6m+veHkw8amOFi0QFY4pTI7RrikRxSd1jgBaCeytDEHmYSsUN/HTEEyqQVQ6ppY4937ER/nPmMXaWK6W8TvQy1cj+s28wcIlS70LHqQsSoS5rB18abP70ubwv0VMqrRhJALnb2SJXZ76vg7pAPc9++oF2+s8XzLyZUMt/3ajhy35encPGy4n+lIy4Xvvou1JERMJFmTa8zqHldSwlaBa9k6VC63heDzM/3cYttfndDMiKlbIwCN7eV2F8FS61toUNZhCzHltUoKChwg/yah6WVnr8jIXJer+ultoizj14CWn/GfvvEU5uIjTdtnx8nw86K1TlzWF5QsXBhYXGlw+3rLk/5IB/G8+gzZuYUagPa8ByoUaWfiRIuA35I4CrJOMVariLtOCaKD+Ryq3gvWTwSDci1AAfVtQi7zzsmWE4F5Vq/+RPKDe8714vX5SVSVGuXjxa2K/iCyN42qyOkbLkXwt6FIjmt5sRsHC11VB2yANZQl1HL+GWjFC9Em4Vx7Q2oKTeB2+oKkeL3xEkBU015zWlri4VEBrpICIlwZ7Eso2mgSVKs8GLeJN5YsLnmn7Lh7/45KS6ncSg5I/Por8NcaWi7UW27Te2JeyTGPqPg3VS9j2CzJyeznjn1qaGXCrpsq8ro655n7+1zi3WwkBRkGuRl5e3eMyi6L0TvDJcnYwTnM14fWG5Xyh5nXVLya56BmNJRUi25oYoiGRJBGXdaO+Rb1Y9R861mlZYzodsf21K8/wZ1mC8I75Hr9viTsF+fsW5BzxTSO1IFQNz6DJvnC/lPv9L6Ab4KcvfJU6gC94y9NfMP31s7i5XY5RIn6LJqKRxjAKDuOVppiQA65ycUhaEvVeQya8dVuF6uCYKClVFADVGd5FyGt9RTWAAgg50ahiNFm2YkcCarHv3QTW/6Jna0TY0VA0FEpnMkbciKJ0RGSrrMN/XzzKfy7p3saOsq21cUl7FVQ2Dboqxg8D38S4hnxooL2bGRU1qurqAtP1Q1M7xzd3Sd4MKlx9aN7cxtWq/1OQAi25ohrK0s0ilVHwbgmKVNGWD1T/IMmcmd2u68h5MhPsLpW6Mf7ZEUPfceC+0zfhdXy9uTm5cQffujnZrMQFzmZdv+n0HyDOnl33Z4RjOcFoIL338N50IIpBxBEd9+2+7fV+vGGHQ5Y18NCTDw1EwkITYRu/JwZe9fPMI+IfxFUsyjbSqvKhAcwt168pdedsTBbTzZxTnRzRxQyV/if8uggP8y2VcpqWw7iyTmgJOy3GuEu5oK35P5xC0L6GZFag308hopD9/6r3KyoE7rKG5Tqi3D9z8s6TM/3+7mEeeCxiPDanxdWDjxmRxwKIeebmVFVePajGtTl5VaWLcGDdLdMVpbTvxD0n9pWUyvQtew1RfioQFOU/1vU/Lotg4KmyMEz9nnuM2OINTVu8ETPuuUeXPuNZUcFYEmd9rFRbYSxfVUjLiN9dFerqyvd2yKWFBEEB548Fup3m1PWnBQHUERnjyKYur0O1v1pBfvl30u2Hnj4EwydOw/CBOyd23vd4+YefpuUbvHb84elmO9HXD1P3Tq1f48YM5VZ17msH5/d1fPtmWQndeOwTd1EnZNeXbtwsoBRbcbJ29T3T0GbGDO+3J95aen6KxViWDVCHL8i5wsPAxPLfE1FDb55RmQnmCYgrEoijOyX+6KeDoE66Tt5ONjkSgheKBA9LqL6qpqchU0E7QaSYtJ3+irSVimrrmpLJU3ej0gsK6vTRXbdlEXtnb9u17Z9B+Un9m9Hg5rmoE93YF4zCPwZ31H9b/6f6b3cEgzvAgAIYO4IwfMe64Q03nOX3fXzD8Lo7brzrLtiCz85tCkajwb6N0b9LJD738MOfSxTs2x7mj3yGsEgjzzBYB9Xw2hGCYAZOzYWG2uR6nFlvxlJeEZfO732ziNsX84be4ffMGz7/gRmQb1dyTX8Q/dtArU8Hb8Wybmgod53NBlQOilxbg9mPCIstK3sTAzE7O5BMJuTKkvJgwfulBskXM0CBMymXLQrpkSoDMVmJ6aA2QjGG4SJkRk38g5sd61dtYGoQ4St+jHY+U23r4aVWOEjtseoMnDgvfwCEm2+hp6r/X91AK4zYkU3HMVEaxnR3qBfqPzn+/wHPnsOreJxjYGRgYABi1vIsoXh+m68M3MwvgCIMt5YoxkDp2P9f/2exVDAHAbkcDEwgUQAz1Qu/AHicY2BkYGAO+p/FwMBS9v/r/68sFQxAERQQDACh8QbyeJxdT0sVwzAMy49AkAxAkbT3UQiAITGAISmAYQiA9th4ttUs7Q568UeSlVidiwSkB3PUPg+ESd6ZD/+8v7HyrtzwghY89ZB6BcyrYqc6RZhw48YhaA1Wc/v1PQtdeXJ/HppUmFM597nvBVK7D+Z+E8/uQZLBgDyaz3Llvxx02S3c/Hv813JrznryZP4F+6lSfQAAAAAAAAAAUAC2ATABaAGyAfoCJAKwAzYDmgQSBFwExgUyBbQF/AZOBvwHRAe2B/YISgigCPIJGglCCWQJignACgAKQAp2CroLAAtGC4oL8gxcDPINng5iDuYPag/6EF4RIBGGEeQSShKYEyQTbhOyFAoUYhS+FVoVphYoFooXHBeGGFoYnhjGGOwZChlOGXgZrBneGhwaWhqgGtIbLhvyHHQc2B1QHZ4d/wABAAAAUwBtAAYAAAAAAAIAIAAwAHMAAAB2C3AAAAAAeJx1kN9q2zAUh39q0441YxcbjN3tXJWWEcc1lEGvWkLbXZeSu8JUV/6T2VKQlY48w95ifYa9zt6jd/vFESUUYiP5O5/O8ZEE4AP+QWH9nHKsWeEdozXv4A0uIu/Sf488IN9G3sMQPyLv0/+MfICv+BV5iI/4wz+owVtGM/yNrPBZfYm8g/fqW+Rd+svIA/Jd5D18UovI+/S/Ix9gqp4iD3GoniduvvR1WQU5mhxLlmap3C/FUdVWN6IXoXK+k3MpnA2maVySuza0ZlToUZ07292YctFov6k2eWp8VzsrJ0m6qa+NNV4H87Dq1j2WWQiFFN61chX7yNy7mclDUoUwPxuPN/tjAoc5lvCoUaJCgOCI9pjfDGk/BPfMEGaus2pYaDQ0GgtWVP1Kx/ico2BkaQ0zGnKCnHNL09KNuK451721rLqhLfmfht5vzdrmp7Sr3nUfC07YL92afU1r+wrd7/Dh5WwdHrmLjDawanUK3+9acPXqPML7Wq3NaHL6pL+1QHuGMd8t5/8PvPGO3QAAAHicbZLnlpswEIV9DRiwvZtseu89Ib333vvmBWRZYMVC0pHEEufpg8BO/kTncOfTaLgMOtPr97o17P1/baKPACEiDBAjQYohRhhjDevYhu3YwA7sxC7sxh7sxT7sxwEcxCEcxhEcxTEcxwmcxCmcxhmcxTmcxwVcxCVkuIwruIpruI4buIlbuI07uIt7uI8HeIhHeIwneIpneI4XeIlXeI03eIt3eI8P+IhP+Iwv+Ipv+I5N/OiF1hEz9JKxUrtFrDl1lWF9NR9QIikToRaVjUouKxvOmNBjLxnlhgo2DbnM1djLKrNGnGPScSUzItzGv93yPP2bSQSX84z9cqFQdJ56yZRmMhW8mLlJJSaBI0XYPDaZKDUviZmvr6DrNjJMi0WcK1MTM02mqpbZlJtEsNx5SI238jSodJtoS7qv+BpPw67IY9xU+dg5TXjROTWwdGrIOzWhT+uA0jolxqjaZrSOnCF2Nmq16651EYpMm1fakAul9SJQeR5QVYQlk1VkZ8SwoVNFIVjWnKQrlBGdMToftdoZjrs77DajqXKrS02YEFxbbtdWkG0x44JJVUS5aBqKSlJwmhDrmOF2Hv9Wqsy4TNqoKhfmSrrQKuNSL5nvPG6p0s0AkEWkSWVZMy1Kx3ljk03qLuZ14lTmB8gNGmByGrGfjLrhlhJV2f7SaIneNF1ypQNbybBUSgZswQaWEUNngeay1/sD4l/60HicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZWJ02MTAyaIEYm7mYGDkgLD4GMIvNaRfTAaA0J5DN7rSLwQHCZmZw2ajC2BEYscGhI2Ijc4rLRjUQbxdHAwMji0NHckgESEkkEGzmYWLk0drB+L91A0vvRiYGFwAMdiP0AAA=') format('woff');\n}\n</style>\n<style id=\"style-core\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\nhtml {\n\t/*\n\t\tWe define the base font size and line height here as they affect the layout\n\t\tof the base page elements (i.e. `#ui-bar`, `#ui-dialog`, and `#story`).\n\t*/\n\tfont: 16px/1 Helmet, Freesans, sans-serif;\n}\n\n/* Story data styling. */\n#store-area, tw-storydata {\n\tdisplay: none !important;\n\tz-index: 0;\n}\n\n/* Special no transition styling. */\n.no-transition {\n\t-webkit-transition: none !important;\n\t-o-transition: none !important;\n\ttransition: none !important;\n}\n\n\n/*\n\tFullscreen appearance styles.\n*/\n*:-webkit-full-screen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:-ms-fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\n*:fullscreen { /* Cause Blink/WebKit to behave like Gecko. */\n\theight: 100%;\n\twidth: 100%;\n}\nbody::-ms-backdrop { /* Prevent IE 11 from hiding the `body` element's background. */\n\tbackground: none;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n*:focus {\n\toutline: thin dotted;\n}\n*:disabled {\n\tcursor: not-allowed !important;\n}\nbody {\n\tcolor: #eee;\n\tbackground-color: #111;\n\toverflow: auto;\n}\na {\n\tcursor: pointer;\n\tcolor: #68d;\n\ttext-decoration: none;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\na:hover {\n\tcolor: #8af;\n\ttext-decoration: underline;\n}\na.link-broken {\n\tcolor: #c22;\n}\na.link-broken:hover {\n\tcolor: #e44;\n}\na[disabled], span.link-disabled {\n\tcolor: #aaa;\n\tcursor: not-allowed !important;\n\t/*\n\t\tNOTE: Do not use `pointer-events` here as it disables\n\t\tthe display of a cursor in some browsers.\n\n\t\tpointer-events: none;\n\t*/\n\ttext-decoration: none;\n}\narea {\n\tcursor: pointer;\n}\nbutton {\n\tcursor: pointer;\n\tcolor: #eee;\n\tbackground-color: #35a;\n\tborder: 1px solid #57c;\n\tline-height: normal;\n\tpadding: 0.4em;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\nbutton:hover {\n\tbackground-color: #57c;\n\tborder-color: #79e;\n}\nbutton:disabled {\n\tbackground-color: #444;\n\tborder: 1px solid #666;\n}\ninput, select, textarea {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\nselect {\n\tpadding: 0.34em 0.4em;\n}\ninput[type=\"text\"] {\n\tmin-width: 18em;\n}\ntextarea {\n\tmin-width: 30em;\n\tresize: vertical;\n}\ninput[type=\"checkbox\"], input[type=\"file\"], input[type=\"radio\"], select {\n\tcursor: pointer;\n}\n/* BEGIN: input[type=\"range\"] */\ninput[type=\"range\"] {\n\t-webkit-appearance: none;\n\tmin-height: 1.2em;\n}\ninput[type=\"range\"]:focus {\n\toutline: none;\n}\ninput[type=\"range\"]::-webkit-slider-runnable-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n\t-webkit-appearance: none;\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\t/*\n\t\tNOTE: Ideally, `margin-top` should be `0` for Edge/Spartan (ca. v17), but\n\t\treal WebKit/Blink-based browsers need it.  Since there's more of them and\n\t\tEdge is co-opting the prefix anyway, we cater to them.  Edge will simply\n\t\thave to look ever so slightly off.\n\t*/\n\tmargin-top: -5px;\n\twidth: 33px;\n}\ninput[type=\"range\"]:focus::-webkit-slider-runnable-track {\n\tbackground: #222;\n}\ninput[type=\"range\"]::-moz-range-track {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: 100%;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 18px;\n\twidth: 33px;\n}\ninput[type=\"range\"]::-ms-track {\n\tbackground: transparent;\n\tborder-color: transparent;\n\tcolor: transparent;\n\tcursor: pointer;\n\theight: 10px;\n\twidth: calc(100% - 1px);\n}\ninput[type=\"range\"]::-ms-fill-lower {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-fill-upper {\n\tbackground: #222;\n\tborder: 1px solid #444;\n\tborder-radius: 0;\n}\ninput[type=\"range\"]::-ms-thumb {\n\tbackground: #35a;\n\tborder: 1px solid #57c;\n\tborder-radius: 0;\n\tcursor: pointer;\n\theight: 16px;\n\twidth: 33px;\n}\n/* END: input[type=\"range\"] */\ninput:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,\ninput:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\nhr {\n\tdisplay: block;\n\theight: 1px;\n\tborder: none;\n\tborder-top: 1px solid #eee;\n\tmargin: 1em 0;\n\tpadding: 0;\n}\naudio, canvas, progress, video {\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.error-view {\n\tbackground-color: #511;\n\tborder-left: 0.5em solid #c22;\n\tdisplay: inline-block;\n\tmargin: 0.1em;\n\tmax-width: 100%;\n\tpadding: 0 0.25em;\n\tposition: relative;\n}\n.error-view > .error-toggle {\n\tbackground-color: transparent;\n\tborder: none;\n\tline-height: inherit;\n\tleft: 0;\n\tpadding: 0;\n\tposition: absolute;\n\ttop: 0;\n\twidth: 1.75em;\n}\n.error-view > .error {\n\tdisplay: inline-block;\n\tmargin-left: 0.25em;\n}\n.error-view > .error-toggle + .error {\n\tmargin-left: 1.5em;\n}\n.error-view > .error-source[hidden] {\n\tdisplay: none;\n}\n.error-view > .error-source:not([hidden]) {\n\tbackground-color: rgba(0, 0, 0, 0.2);\n\tdisplay: block;\n\tmargin: 0 0 0.25em;\n\toverflow-x: auto;\n\tpadding: 0.25em;\n}\n\n.highlight, .marked {\n\tcolor: yellow;\n\tfont-weight: bold;\n\tfont-style: italic;\n}\n.nobr {\n\twhite-space: nowrap;\n}\n\n[data-icon]:before,\n[data-icon-before]:before,\n[data-icon-after]:after,\n.error-view > .error-toggle:before,\n.error-view > .error:before,\na.link-external:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n[data-icon]:before {\n\tcontent: attr(data-icon);\n}\n[data-icon-before]:before {\n\tcontent: attr(data-icon-before) \"\\00a0\\00a0\";\n}\n[data-icon-after]:after {\n\tcontent: \"\\00a0\\00a0\" attr(data-icon-after);\n}\n.error-view > .error-toggle:before {\n\tcontent: \"\\e81a\";\n}\n.error-view > .error-toggle.enabled:before {\n\tcontent: \"\\e818\";\n}\n.error-view > .error:before {\n\tcontent: \"\\e80d\\00a0\\00a0\";\n}\na.link-external:after {\n\tcontent: \"\\00a0\\e80e\";\n}\n</style>\n<style id=\"style-core-display\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-display.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n#story {\n\tz-index: 10;\n\tmargin: 2.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-right: 1.5em;\n\t}\n}\n#passages {\n\tmax-width: 54em;\n\tmargin: 0 auto;\n}\n</style>\n<style id=\"style-core-passage\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-passage.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n.passage {\n\tline-height: 1.75;\n\ttext-align: left;\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.passage-in {\n\topacity: 0;\n}\n.passage ul, .passage ol {\n\tmargin-left: 0.5em;\n\tpadding-left: 1.5em;\n}\n.passage table {\n\tmargin: 1em 0;\n\tborder-collapse: collapse;\n\tfont-size: 100%;\n}\n.passage tr, .passage th, .passage td, .passage caption {\n\tpadding: 3px;\n}\n</style>\n<style id=\"style-core-macro\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/core-macro.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault appearance styles.\n*/\n@-webkit-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@-o-keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n@keyframes cursor-blink {\n\t0% { opacity: 1; }\n\t50% { opacity: 0; }\n\t100% { opacity: 1; }\n}\n\n.macro-linkappend-insert,\n.macro-linkprepend-insert,\n.macro-linkreplace-insert,\n.macro-append-insert,\n.macro-prepend-insert,\n.macro-replace-insert,\n.macro-repeat-insert,\n.macro-timed-insert {\n\t-webkit-transition: opacity 400ms ease-in;\n\t-o-transition: opacity 400ms ease-in;\n\ttransition: opacity 400ms ease-in;\n}\n.macro-linkappend-in,\n.macro-linkprepend-in,\n.macro-linkreplace-in,\n.macro-append-in,\n.macro-prepend-in,\n.macro-replace-in,\n.macro-repeat-in,\n.macro-timed-in {\n\topacity: 0;\n}\n\n.macro-type-cursor:after {\n\t-webkit-animation: cursor-blink 1s infinite;\n\t     -o-animation: cursor-blink 1s infinite;\n\t        animation: cursor-blink 1s infinite;\n\tcontent: \"\\2590\"; /* \"\\2588\" */\n\topacity: 1;\n}\n</style>\n<style id=\"style-ui-dialog\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-dialog.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\nhtml[data-dialog] body {\n\toverflow: hidden;\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-overlay.open {\n\tvisibility: visible;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n#ui-overlay:not(.open) {\n\t-webkit-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\t-o-transition: visibility 200ms step-end, opacity 200ms ease-in;\n\ttransition: visibility 200ms step-end, opacity 200ms ease-in;\n}\n#ui-overlay {\n\tvisibility: hidden;\n\topacity: 0;\n\tz-index: 100000;\n\tposition: fixed;\n\t/*\n\ttop: -50vh;\n\tleft: -50vw;\n\theight: 200vh;\n\twidth: 200vw;\n\t*/\n\ttop: -50%;\n\tleft: -50%;\n\theight: 200%;\n\twidth: 200%;\n}\n#ui-dialog.open {\n\tdisplay: block;\n\t-webkit-transition: opacity 200ms ease-in;\n\t-o-transition: opacity 200ms ease-in;\n\ttransition: opacity 200ms ease-in;\n}\n/*\n\tWe do not animate `#ui-dialog:not(.open)` for various reasons.  Chief among\n\tthem, however, is so that the dialog isn't in the middle of its animation\n\twhen other page updates happen.\n\n\te.g. The restoration of `overflow` on `body` would cause the still animating\n\t     dialog to jump around a little if a scrollbar were to pop in.\n\n\t     Any dialog action which performs a task which has its own animations\n\t     (e.g. passage display) or causes the page to reload in addition to\n\t     closing the dialog could cause display shenanigans.\n*/\n#ui-dialog {\n\tdisplay: none;\n\topacity: 0;\n\tz-index: 100100;\n\tposition: fixed;\n\ttop: 50px;\n\tmargin: 0;\n\tpadding: 0;\n}\n#ui-dialog > * {\n\t-webkit-box-sizing: border-box;\n\t        box-sizing: border-box;\n}\n#ui-dialog-titlebar {\n\tposition: relative;\n}\n#ui-dialog-close {\n\tdisplay: block;\n\tposition: absolute;\n\tright: 0;\n\ttop: 0;\n\twhite-space: nowrap;\n}\n#ui-dialog-body {\n\toverflow: auto;\n\tmin-width: 280px;\n\theight: 92%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.1em); /* parent - title(2.1em) */\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-overlay {\n\tbackground-color: #000;\n}\n#ui-overlay.open {\n\topacity: 0.8;\n}\n#ui-dialog {\n\tmax-width: 66em;\n}\n#ui-dialog.open {\n\topacity: 1;\n}\n#ui-dialog-titlebar {\n\tbackground-color: #444;\n\tmin-height: 24px;\n}\n#ui-dialog-title {\n\tmargin: 0;\n\tpadding: 0.2em 3.5em 0.2em 0.5em;\n\tfont-size: 1.5em;\n\ttext-align: center;\n\ttext-transform: uppercase;\n}\n#ui-dialog-close {\n\tcursor: pointer;\n\tfont-size: 120%;\n\tmargin: 0;\n\tpadding: 0;\n\twidth: 3.6em;\n\theight: 92%;\n\tbackground-color: transparent;\n\tborder: 1px solid transparent;\n\t-webkit-transition-duration: 200ms;\n\t     -o-transition-duration: 200ms;\n\t        transition-duration: 200ms;\n}\n#ui-dialog-close:hover {\n\tbackground-color: #b44;\n\tborder-color: #d66;\n}\n#ui-dialog-body {\n\tbackground-color: #111;\n\tborder: 1px solid #444;\n\ttext-align: left;\n\tline-height: 1.5;\n\tpadding: 1em;\n}\n#ui-dialog-body > *:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body hr {\n\tbackground-color: #444;\n}\n\n/* Default dialog button bar styling. */\n#ui-dialog-body ul.buttons {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n#ui-dialog-body ul.buttons li {\n\tdisplay: inline-block;\n\tmargin: 0;\n\tpadding: 0.4em 0.4em 0 0;\n}\n#ui-dialog-body ul.buttons > li + li > button {\n\tmargin-left: 1em;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-close {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-close {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n</style>\n<style id=\"style-ui\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault structural styles.\n*/\n/* Settings dialog styling. */\n#ui-dialog-body.settings [id|=\"setting-body\"] > div:first-child {\n\tdisplay: table;\n\twidth: 100%;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] {\n\tdisplay: table-cell;\n\tpadding: 0.4em 2em 0.4em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-label\"] + div {\n\tdisplay: table-cell;\n\tmin-width: 8em;\n\ttext-align: right;\n\tvertical-align: middle;\n\twhite-space: nowrap;\n}\n\n\n/*\n\tBuilt-in dialog appearance styles.\n*/\n/* List-based dialog styling (primarily for the Jumpto & Share dialogs). */\n#ui-dialog-body.list {\n\tpadding: 0;\n}\n#ui-dialog-body.list ul {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid transparent;\n}\n#ui-dialog-body.list li {\n\tmargin: 0;\n}\n#ui-dialog-body.list li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.list li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-decoration: none;\n}\n#ui-dialog-body.list li a:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n/* Saves dialog styling. */\n#ui-dialog-body.saves {\n\tpadding: 0 0 1px; /* Webkit/Blink need 1px bottom padding or they'll trigger the scroll bar */\n}\n#ui-dialog-body.saves > *:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves table {\n\tborder-spacing: 0;\n\twidth: 100%;\n}\n#ui-dialog-body.saves tr:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#ui-dialog-body.saves td {\n\tpadding: 0.33em 0.33em;\n}\n#ui-dialog-body.saves td:first-child {\n\tmin-width: 1.5em;\n\ttext-align: center;\n}\n#ui-dialog-body.saves td:nth-child(3) {\n\tline-height: 1.2;\n}\n#ui-dialog-body.saves td:last-child {\n\ttext-align: right;\n}\n#ui-dialog-body.saves .empty {\n\tcolor: #999;\n\tspeak: none;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n#ui-dialog-body.saves .datestamp {\n\tfont-size: 75%;\n\tmargin-left: 1em;\n}\n#ui-dialog-body.saves ul.buttons li {\n\tpadding: 0.4em;\n}\n#ui-dialog-body.saves ul.buttons > li + li > button {\n\tmargin-left: 0.2em;\n}\n#ui-dialog-body.saves ul.buttons li:last-child {\n\t/*\n\t\tUsing `position:absolute;right:0;` here can produce poor results,\n\t\tso we use `float:right` instead.\n\t*/\n\tfloat: right;\n}\n\n/* Settings dialog styling. */\n#ui-dialog-body.settings div[id|=\"header-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:first-child {\n\tmargin-top: 0;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"]:not(:first-child) {\n\tborder-top: 1px solid #444;\n\tpadding-top: 1em;\n}\n#ui-dialog-body.settings div[id|=\"header-body\"] > * {\n\tmargin: 0;\n}\n#ui-dialog-body.settings h2[id|=\"header-heading\"] {\n\tfont-size: 1.375em;\n}\n#ui-dialog-body.settings p[id|=\"header-desc\"],\n#ui-dialog-body.settings p[id|=\"setting-desc\"] {\n\tfont-size: 87.5%;\n\tmargin: 0 0 0 0.5em;\n}\n#ui-dialog-body.settings div[id|=\"setting-body\"] + div[id|=\"setting-body\"] {\n\tmargin: 1em 0;\n}\n#ui-dialog-body.settings [id|=\"setting-control\"] {\n\twhite-space: nowrap;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"] {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tpadding: 0.4em;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n#ui-dialog-body.settings input[type=\"range\"][id|=\"setting-control\"] {\n\tmax-width: 35vw;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-dialog-body.list a,\n#ui-dialog-body.settings span[id|=\"setting-input\"] {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-dialog-body.saves button[id=\"saves-export\"]:before,\n#ui-dialog-body.saves button[id=\"saves-import\"]:before,\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before,\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after,\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-dialog-body.saves button[id=\"saves-export\"]:before {\n\tcontent: \"\\e829\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-import\"]:before {\n\tcontent: \"\\e82a\\00a0\";\n}\n#ui-dialog-body.saves button[id=\"saves-clear\"]:before {\n\tcontent: \"\\e827\\00a0\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"]:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#ui-dialog-body.settings button[id|=\"setting-control\"].enabled:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n</style>\n<style id=\"style-ui-bar\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-bar.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tPatches to the core styles.\n*/\n#story {\n\tmargin-left: 20em;\n\t-webkit-transition: margin-left 200ms ease-in;\n\t-o-transition: margin-left 200ms ease-in;\n\ttransition: margin-left 200ms ease-in;\n}\n#ui-bar.stowed ~ #story {\n\tmargin-left: 4.5em;\n}\n@media screen and (max-width: 1136px) {\n\t#story {\n\t\tmargin-left: 19em;\n\t}\n\t#ui-bar.stowed ~ #story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n/*\n\tAt very narrow viewport widths, set `#story{margin-left}` equal to the value\n\tof `#ui-bar.stowed~#story{margin-left}`, so that `#ui-bar` will side over top\n\tof `#story` when unstowed, rather than shoving it over.\n*/\n@media screen and (max-width: 768px) {\n\t#story {\n\t\tmargin-left: 3.5em;\n\t}\n}\n\n\n/*\n\tDefault structural styles.\n*/\n#ui-bar {\n\tposition: fixed;\n\tz-index: 50;\n\ttop: 0;\n\tleft: 0;\n\twidth: 17.5em;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\t-webkit-transition: left 200ms ease-in;\n\t-o-transition: left 200ms ease-in;\n\ttransition: left 200ms ease-in;\n}\n#ui-bar.stowed {\n\tleft: -15.5em;\n}\n#ui-bar-tray {\n\tposition: absolute;\n\ttop: 0.2em;\n\tleft: 0;\n\tright: 0;\n}\n#ui-bar-body {\n\theight: 90%; /* fallback for browsers without support for calc() */\n\theight: calc(100% - 2.5em);\n\tmargin: 2.5em 0;\n\tpadding: 0 1.5em;\n}\n#ui-bar.stowed #ui-bar-history,\n#ui-bar.stowed #ui-bar-body {\n\tvisibility: hidden;\n\t-webkit-transition: visibility 200ms step-end;\n\t-o-transition: visibility 200ms step-end;\n\ttransition: visibility 200ms step-end;\n}\n\n\n/*\n\tDefault appearance styles.\n*/\n#ui-bar {\n\tbackground-color: #222;\n\tborder-right: 1px solid #444;\n\ttext-align: center;\n}\n#ui-bar a {\n\ttext-decoration: none;\n}\n#ui-bar hr {\n\tborder-color: #444;\n}\n#ui-bar-toggle,\n#ui-bar-history [id|=\"history\"] {\n\tfont-size: 1.2em;\n\tline-height: inherit;\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n}\n#ui-bar-toggle {\n\tdisplay: block;\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tborder-right: none;\n\tpadding: 0.3em 0.45em 0.25em;\n}\n#ui-bar.stowed #ui-bar-toggle {\n\tpadding: 0.3em 0.35em 0.25em 0.55em;\n}\n#ui-bar-toggle:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history {\n\tmargin: 0 auto;\n}\n#ui-bar-history [id|=\"history\"] {\n\tpadding: 0.2em 0.45em 0.35em;\n}\n#ui-bar-history #history-jumpto {\n\tpadding: 0.2em 0.665em 0.35em;\n}\n#ui-bar-history [id|=\"history\"]:not(:first-child) {\n\tmargin-left: 1.2em;\n}\n#ui-bar-history [id|=\"history\"]:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n#ui-bar-history [id|=\"history\"]:disabled {\n\tcolor: #444;\n\tbackground-color: transparent;\n\tborder-color: #444;\n}\n#ui-bar-body {\n\tline-height: 1.5;\n\toverflow: auto;\n}\n#ui-bar-body > :not(:first-child) {\n\tmargin-top: 2em;\n}\n#story-title {\n\tmargin: 0;\n\tfont-size: 162.5%;\n}\n#story-author {\n\tmargin-top: 2em;\n\tfont-weight: bold;\n}\n#menu ul {\n\tmargin: 1em 0 0;\n\tpadding: 0;\n\tlist-style: none;\n\tborder: 1px solid #444;\n}\n#menu ul:empty {\n\tdisplay: none;\n}\n#menu li {\n\tmargin: 0;\n}\n#menu li:not(:first-child) {\n\tborder-top: 1px solid #444;\n}\n#menu li a {\n\tdisplay: block;\n\tpadding: 0.25em 0.75em;\n\tborder: 1px solid transparent;\n\tcolor: #eee;\n\ttext-transform: uppercase;\n}\n#menu li a:hover {\n\tbackground-color: #444;\n\tborder-color: #eee;\n}\n\n/* Stop text selection on certain UI elements. */\n#ui-bar-history [id|=\"history\"],\n#ui-bar-toggle,\n#menu a {\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n}\n\n\n/*\n\tDefault font icon styles.\n*/\n#ui-bar-toggle:before,\n#ui-bar-history [id|=\"history\"],\n#menu-core li[id|=\"menu-item\"] a:before {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#ui-bar-toggle:before {\n\tcontent: \"\\e81d\";\n}\n#ui-bar.stowed #ui-bar-toggle:before {\n\tcontent: \"\\e81e\";\n}\n#menu-item-saves a:before {\n\tcontent: \"\\e82b\\00a0\";\n}\n#menu-item-settings a:before {\n\tcontent: \"\\e82d\\00a0\";\n}\n#menu-item-restart a:before {\n\tcontent: \"\\e82c\\00a0\";\n}\n#menu-item-share a:before {\n\tcontent: \"\\e82f\\00a0\";\n}\n</style>\n<style id=\"style-ui-debug\" type=\"text/css\">/***********************************************************************************************************************\n\n\tcss/ui-debug.css\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tDefault debug bar styles.\n*/\n#debug-bar {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 0;\n\tmargin: 0;\n\tmax-height: 75%;\n\t/* max-width: 28em;\n\tmin-width: 22em; */\n\tpadding: 0.5em;\n\tposition: fixed;\n\tright: 0;\n\tz-index: 99900;\n}\n#debug-bar > div:not([id]) + div {\n\tmargin-top: 0.5em;\n}\n#debug-bar > div > label {\n\tmargin-right: 0.5em;\n}\n#debug-bar > div > input[type=\"text\"] {\n\tmin-width: 0;\n\twidth: 8em;\n}\n#debug-bar > div > select {\n\twidth: 15em;\n}\n\n#debug-bar-toggle {\n\tcolor: #eee;\n\tbackground-color: #222;\n\tborder: 1px solid #444;\n\theight: 101%; /* fallback for browsers without support for calc() */\n\theight: calc(100% + 1px);\n\tleft: -2em; /* fallback for browsers without support for calc() */\n\tleft: calc(-2em - 1px);\n\tposition: absolute;\n\ttop: -1px;\n\twidth: 2em;\n}\n#debug-bar-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n\n#debug-bar-hint {\n\tbottom: 0.175em;\n\tfont-size: 4.5em;\n\topacity: 0.33;\n\tpointer-events: none;\n\tposition: fixed;\n\tright: 0.6em;\n\t-webkit-user-select: none;\n\t   -moz-user-select: none;\n\t    -ms-user-select: none;\n\t        user-select: none;\n\twhite-space: nowrap;\n}\n\n#debug-bar-watch {\n\tbackground-color: #222;\n\tborder-left: 1px solid #444;\n\tborder-top: 1px solid #444;\n\tbottom: 102%; /* fallback for browsers without support for calc() */\n\tbottom: calc(100% + 1px);\n\tfont-size: 0.9em;\n\tleft: -1px;\n\tmax-height: 650%; /* fallback for browsers without support for vh units */\n\tmax-height: 65vh;\n\tposition: absolute;\n\toverflow-x: hidden;\n\toverflow-y: scroll;\n\tright: 0;\n\tz-index: 99800;\n}\n#debug-bar-watch[hidden] {\n\tdisplay: none;\n}\n#debug-bar-watch div {\n\tcolor: #999;\n\tfont-style: italic;\n\tmargin: 1em auto;\n\ttext-align: center;\n}\n#debug-bar-watch table {\n\twidth: 100%;\n}\n#debug-bar-watch tr:nth-child(2n) {\n\tbackground-color: rgba(127, 127, 127, 0.15);\n}\n#debug-bar-watch td {\n\tpadding: 0.2em 0;\n}\n#debug-bar-watch td:first-child + td {\n\tpadding: 0.2em 0.3em 0.2em 0.1em;\n}\n#debug-bar-watch .watch-delete {\n\tbackground-color: transparent;\n\tborder: none;\n\tcolor: #c00;\n}\n#debug-bar-watch-all,\n#debug-bar-watch-none {\n\tmargin-left: 0.5em;\n}\n#debug-bar-watch-toggle,\n#debug-bar-views-toggle {\n\tcolor: #eee;\n\tbackground-color: transparent;\n\tborder: 1px solid #444;\n\tmargin-right: 1em;\n\tpadding: 0.4em;\n}\n#debug-bar-watch-toggle:hover,\n#debug-bar-views-toggle:hover {\n\tbackground-color: #333;\n\tborder-color: #eee;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle,\nhtml[data-debug-view] #debug-bar-views-toggle {\n\tbackground-color: #282;\n\tborder-color: #4a4;\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:hover,\nhtml[data-debug-view] #debug-bar-views-toggle:hover {\n\tbackground-color: #4a4;\n\tborder-color: #6c6;\n}\n\n#debug-bar-toggle:before,\n#debug-bar-hint:after,\n#debug-bar-watch .watch-delete:before,\n#debug-bar-watch-add:before,\n#debug-bar-watch-all:before,\n#debug-bar-watch-none:before,\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tfont-family: \"tme-fa-icons\";\n\tfont-style: normal;\n\tfont-weight: normal;\n\tfont-variant: normal;\n\ttext-transform: none;\n\tline-height: 1;\n\tspeak: none;\n}\n#debug-bar-toggle:before {\n\tcontent: \"\\e838\";\n}\n#debug-bar-hint:after {\n\tcontent: \"\\e838\\202f\\e822\";\n}\n#debug-bar-watch .watch-delete:before {\n\tcontent: \"\\e804\";\n}\n#debug-bar-watch-add:before {\n\tcontent: \"\\e805\";\n}\n#debug-bar-watch-all:before {\n\tcontent: \"\\e83a\";\n}\n#debug-bar-watch-none:before {\n\tcontent: \"\\e827\";\n}\n#debug-bar-watch-toggle:after,\n#debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e830\";\n}\n#debug-bar-watch:not([hidden]) ~ div #debug-bar-watch-toggle:after,\nhtml[data-debug-view] #debug-bar-views-toggle:after {\n\tcontent: \"\\00a0\\00a0\\e831\";\n}\n\n\n/*\n\tDefault debug view styles.\n*/\nhtml[data-debug-view] .debug {\n\tpadding: 0.25em;\n\tbackground-color: #234; /* #541, #151 */\n}\nhtml[data-debug-view] .debug[title] {\n\tcursor: help;\n}\nhtml[data-debug-view] .debug.block {\n\tdisplay: inline-block;\n\tvertical-align: middle;\n}\nhtml[data-debug-view] .debug.invalid {\n\ttext-decoration: line-through;\n}\nhtml[data-debug-view] .debug.hidden,\nhtml[data-debug-view] .debug.hidden .debug {\n\tbackground-color: #555;\n}\nhtml:not([data-debug-view]) .debug.hidden {\n\tdisplay: none;\n}\n\nhtml[data-debug-view] .debug[data-name][data-type]:before,\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:after {\n\tbackground-color: rgba(0,0,0,0.25);\n\tfont-family: monospace, monospace;\n\twhite-space: pre;\n}\nhtml[data-debug-view] .debug[data-name][data-type]:before {\n\tcontent: attr(data-name);\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"]:before {\n\tcontent: \"<<\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"macro\"].nonvoid:after {\n\tcontent: \"<</\" attr(data-name) \">>\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"]:before {\n\tcontent: \"<\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"html\"].nonvoid:after {\n\tcontent: \"</\" attr(data-name) \">\";\n}\nhtml[data-debug-view] .debug[data-name][data-type]:not(:empty):before {\n\tmargin-right: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type].nonvoid:not(:empty):after {\n\tmargin-left: 0.25em;\n}\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"],\nhtml[data-debug-view] .debug[data-name][data-type|=\"special\"]:before {\n\tdisplay: block;\n}\n</style>\n</head>\n<body>\n\t<div id=\"init-screen\">\n\t\t<div id=\"init-no-js\"><noscript>JavaScript is required. Please enable it to continue.</noscript></div>\n\t\t<div id=\"init-lacking\">Your browser lacks required capabilities. Please upgrade it or switch to another to continue.</div>\n\t\t<div id=\"init-loading\"><div>Loading&hellip;</div></div>\n\t</div>\n\t{{STORY_DATA}}\n\t<script id=\"script-sugarcube\" type=\"text/javascript\">\n\t/*! SugarCube JS */\n\tif(document.documentElement.getAttribute(\"data-init\")===\"loading\"){window.TWINE1=false;\nwindow.DEBUG=false;\n(function (window, document, jQuery, undefined) {\n\"use strict\";\n\n/***********************************************************************************************************************\n\n\tlib/alert.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: This regular expression should be elsewhere.\n\n\tError prologs by engine/browser: (ca. 2018)\n\t\tChrome, Opera, & Vivaldi → `Uncaught \\w*Error: …`\n\t\tEdge & IE                → `…`\n\t\tFirefox                  → `Error: …`\n\t\tOpera (Presto)           → `Uncaught exception: \\w*(?:Error|Exception): …`\n\t\tSafari (ca. v5.1)        → `\\w*(?:Error|_ERR): …`\n*/\nvar errorPrologRegExp = /^(?:(?:uncaught\\s+(?:exception:\\s+)?)?\\w*(?:error|exception|_err):\\s+)+/i; // eslint-disable-line no-unused-vars, no-var\n\nvar Alert = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tError Functions.\n\t*******************************************************************************************************************/\n\tfunction _alertMesg(type, where, what, error) {\n\t\tconst isFatal = type === 'fatal';\n\t\tlet mesg = `Apologies! ${isFatal ? 'A fatal' : 'An'} error has occurred.`;\n\n\t\tif (isFatal) {\n\t\t\tmesg += ' Aborting.';\n\t\t}\n\t\telse {\n\t\t\tmesg += ' You may be able to continue, but some parts may not work properly.';\n\t\t}\n\n\t\tif (where != null || what != null) { // lazy equality for null\n\t\t\tmesg += '\\n\\nError';\n\n\t\t\tif (where != null) { // lazy equality for null\n\t\t\t\tmesg += ` [${where}]`;\n\t\t\t}\n\n\t\t\tif (what != null) { // lazy equality for null\n\t\t\t\tmesg += `: ${what.replace(errorPrologRegExp, '')}.`;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmesg += ': unknown error.';\n\t\t\t}\n\t\t}\n\n\t\tif (typeof error === 'object' && error !== null && error.stack) {\n\t\t\tmesg += `\\n\\nStack Trace:\\n${error.stack}`;\n\t\t}\n\n\t\twindow.alert(mesg); // eslint-disable-line no-alert\n\t}\n\n\tfunction alertError(where, what, error) {\n\t\t_alertMesg(null, where, what, error);\n\t}\n\n\tfunction alertFatal(where, what, error) {\n\t\t_alertMesg('fatal', where, what, error);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tError Event.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSet up a global error handler for uncaught exceptions.\n\t*/\n\t(origOnError => {\n\t\twindow.onerror = function (what, source, lineNum, colNum, error) {\n\t\t\t// Uncaught exceptions during play may be recoverable/ignorable.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\talertError(null, what, error);\n\t\t\t}\n\n\t\t\t// Uncaught exceptions during startup should be fatal.\n\t\t\telse {\n\t\t\t\talertFatal(null, what, error);\n\t\t\t\twindow.onerror = origOnError;\n\n\t\t\t\tif (typeof window.onerror === 'function') {\n\t\t\t\t\twindow.onerror.apply(this, arguments);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})(window.onerror);\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\terror : { value : alertError },\n\t\tfatal : { value : alertFatal }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/patterns.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tTODO: Move all markup patterns into here.\n*/\n\n/* eslint-disable max-len */\nvar Patterns = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPatterns.\n\t*******************************************************************************************************************/\n\t/*\n\t\tWhitespace patterns.\n\n\t\tSpace class (equivalent to `\\s`):\n\t\t\t[\\u0020\\f\\n\\r\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]\n\t\tSpace class, sans line terminators:\n\t\t\t[\\u0020\\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]\n\t\tLine Terminator class:\n\t\t\t[\\n\\r\\u2028\\u2029]\n\t*/\n\tconst space = (() => {\n\t\t/*\n\t\t\tSome browsers still supported by SugarCube have faulty space classes (`\\s`).\n\t\t\tWe check for that lossage here and, if necessary, build our own class from\n\t\t\tthe component pieces.\n\t\t*/\n\t\tconst wsMap = new Map([\n\t\t\t['\\u0020', '\\\\u0020'],\n\t\t\t['\\f', '\\\\f'],\n\t\t\t['\\n', '\\\\n'],\n\t\t\t['\\r', '\\\\r'],\n\t\t\t['\\t', '\\\\t'],\n\t\t\t['\\v', '\\\\v'],\n\t\t\t['\\u00a0', '\\\\u00a0'],\n\t\t\t['\\u1680', '\\\\u1680'],\n\t\t\t['\\u180e', '\\\\u180e'],\n\t\t\t['\\u2000', '\\\\u2000'],\n\t\t\t['\\u2001', '\\\\u2001'],\n\t\t\t['\\u2002', '\\\\u2002'],\n\t\t\t['\\u2003', '\\\\u2003'],\n\t\t\t['\\u2004', '\\\\u2004'],\n\t\t\t['\\u2005', '\\\\u2005'],\n\t\t\t['\\u2006', '\\\\u2006'],\n\t\t\t['\\u2007', '\\\\u2007'],\n\t\t\t['\\u2008', '\\\\u2008'],\n\t\t\t['\\u2009', '\\\\u2009'],\n\t\t\t['\\u200a', '\\\\u200a'],\n\t\t\t['\\u2028', '\\\\u2028'],\n\t\t\t['\\u2029', '\\\\u2029'],\n\t\t\t['\\u202f', '\\\\u202f'],\n\t\t\t['\\u205f', '\\\\u205f'],\n\t\t\t['\\u3000', '\\\\u3000'],\n\t\t\t['\\ufeff', '\\\\ufeff']\n\t\t]);\n\t\tconst wsRe = /^\\s$/;\n\t\tlet missing = '';\n\n\t\twsMap.forEach((pat, char) => {\n\t\t\tif (!wsRe.test(char)) {\n\t\t\t\tmissing += pat;\n\t\t\t}\n\t\t});\n\n\t\treturn missing ? `[\\\\s${missing}]` : '\\\\s';\n\t})();\n\tconst spaceNoTerminator = '[\\\\u0020\\\\f\\\\t\\\\v\\\\u00a0\\\\u1680\\\\u180e\\\\u2000-\\\\u200a\\\\u202f\\\\u205f\\\\u3000\\\\ufeff]';\n\tconst lineTerminator    = '[\\\\n\\\\r\\\\u2028\\\\u2029]';\n\tconst notSpace          = space === '\\\\s' ? '\\\\S' : space.replace(/^\\[/, '[^');\n\n\t/*\n\t\tCharacter patterns.\n\t*/\n\tconst anyChar = `(?:.|${lineTerminator})`;\n\n\t/*\n\t\tLetter patterns.\n\n\t\tFIXME:\n\t\t\t1. The existing set, which is a TiddlyWiki holdover, should probably\n\t\t\t   encompass a significantly greater range of BMP code points.\n\t\t\t2. Should we include the surrogate pair code units (\\uD800-\\uDBFF &\n\t\t\t   \\uDC00-\\uDFFF) to handle non-BMP code points?  Further, should we\n\t\t\t   simply be checking for the code units themselves or checking for\n\t\t\t   properly mated pairs?\n\t*/\n\tconst anyLetter       = '[0-9A-Z_a-z\\\\-\\\\u00c0-\\\\u00d6\\\\u00d8-\\\\u00f6\\\\u00f8-\\\\u00ff\\\\u0150\\\\u0170\\\\u0151\\\\u0171]';\n\tconst anyLetterStrict = anyLetter.replace('\\\\-', ''); // anyLetter sans hyphen\n\n\t/*\n\t\tIdentifier patterns.\n\n\t\tNOTE: Since JavaScript's RegExp syntax does not support Unicode character\n\t\tclasses, the correct regular expression to match a valid identifier name,\n\t\twithin the scope of our needs, would be on the order of approximately 5–6\n\t\tor 11–16 KiB, depending on how the pattern was built.  That being the case,\n\t\tfor the moment we restrict valid TwineScript identifiers to US-ASCII.\n\n\t\tFIXME: Fix this to, at least, approximate the correct range.\n\t*/\n\tconst identifierFirstChar = '[$A-Z_a-z]';\n\tconst identifierNextChar  = '[$0-9A-Z_a-z]';\n\tconst identifier          = `${identifierFirstChar}${identifierNextChar}*`;\n\n\t// Variable patterns.\n\tconst variableSigil = '[$_]';\n\tconst variable      = variableSigil + identifier;\n\n\t// Macro name pattern.\n\tconst macroName = '[A-Za-z][\\\\w-]*|[=-]';\n\n\t// Template name pattern.\n\tconst templateName = '[A-Za-z][\\\\w-]*';\n\n\t// HTML tag name pattern.\n\tconst htmlTagName = (() => {\n\t\t/*\n\t\t\tElement Name:\n\t\t\t\t[A-Za-z] [0-9A-Za-z]*\n\n\t\t\tCustom Element Name:\n\t\t\t\t[a-z] (CENChar)* '-' (CENChar)*\n\t\t\tCENChar:\n\t\t\t\t\"-\" | \".\" | [0-9] | \"_\" | [a-z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]\n\t\t*/\n\t\tconst cENChar = '(?:[\\\\x2D.0-9A-Z_a-z\\\\xB7\\\\xC0-\\\\xD6\\\\xD8-\\\\xF6\\\\xF8-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C\\\\u200D\\\\u203F\\\\u2040\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD]|[\\\\uD800-\\\\uDB7F][\\\\uDC00-\\\\uDFFF])';\n\n\t\treturn `[A-Za-z](?:${cENChar}*-${cENChar}*|[0-9A-Za-z]*)`;\n\t})();\n\n\t// CSS ID or class sigil pattern.\n\tconst cssIdOrClassSigil = '[#.]';\n\n\t// CSS image transclusion template pattern.\n\t//\n\t// NOTE: The alignment syntax isn't supported, but removing it might break uses\n\t// of the template in the wild, so we leave it alone for now.\n\tconst cssImage = '\\\\[[<>]?[Ii][Mm][Gg]\\\\[(?:\\\\s|\\\\S)*?\\\\]\\\\]+';\n\n\t// Inline CSS pattern.\n\tconst inlineCss = (() => {\n\t\t/* legacy */\n\t\tconst twStyle   = `(${anyLetter}+)\\\\(([^\\\\)\\\\|\\\\n]+)\\\\):`;\n\t\t/* /legacy */\n\t\tconst cssStyle  = `${spaceNoTerminator}*(${anyLetter}+)${spaceNoTerminator}*:([^;\\\\|\\\\n]+);`;\n\t\tconst idOrClass = `${spaceNoTerminator}*((?:${cssIdOrClassSigil}${anyLetter}+${spaceNoTerminator}*)+);`;\n\n\t\t// [1,2] = style(value):\n\t\t// [3,4] = style:value;\n\t\t// [5]   = #id.className;\n\t\treturn `${twStyle}|${cssStyle}|${idOrClass}`;\n\t})();\n\n\t// URL pattern.\n\tconst url = '(?:file|https?|mailto|ftp|javascript|irc|news|data):[^\\\\s\\'\"]+';\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze({\n\t\tspace,\n\t\tspaceNoTerminator,\n\t\tlineTerminator,\n\t\tnotSpace,\n\t\tanyChar,\n\t\tanyLetter,\n\t\tanyLetterStrict,\n\t\tidentifierFirstChar,\n\t\tidentifierNextChar,\n\t\tidentifier,\n\t\tvariableSigil,\n\t\tvariable,\n\t\tmacroName,\n\t\ttemplateName,\n\t\thtmlTagName,\n\t\tcssIdOrClassSigil,\n\t\tcssImage,\n\t\tinlineCss,\n\t\turl\n\t});\n})();\n/* eslint-enable max-len */\n\n/***********************************************************************************************************************\n\n\tlib/extensions.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\n/*\n\tJavaScript Polyfills.\n\n\tNOTE: The ES5 and ES6 polyfills come from the vendored `es5-shim.js` and `es6-shim.js` libraries.\n*/\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tTrims whitespace from either the start or end of the given string.\n\t*/\n\tconst _trimString = (() => {\n\t\t// Whitespace regular expressions.\n\t\tconst startWSRe = new RegExp(`^${Patterns.space}${Patterns.space}*`);\n\t\tconst endWSRe   = new RegExp(`${Patterns.space}${Patterns.space}*$`);\n\n\t\tfunction trimString(str, where) {\n\t\t\tconst val = String(str);\n\n\t\t\tif (!val) {\n\t\t\t\treturn val;\n\t\t\t}\n\n\t\t\tswitch (where) {\n\t\t\tcase 'start':\n\t\t\t\treturn startWSRe.test(val) ? val.replace(startWSRe, '') : val;\n\n\t\t\tcase 'end':\n\t\t\t\treturn endWSRe.test(val) ? val.replace(endWSRe, '') : val;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`_trimString called with incorrect where parameter value: \"${where}\"`);\n\t\t\t}\n\t\t}\n\n\t\treturn trimString;\n\t})();\n\n\t/*\n\t\tGenerates a pad string based upon the given string and length.\n\t*/\n\tfunction _createPadString(length, padding) {\n\t\tconst targetLength = Number.parseInt(length, 10) || 0;\n\n\t\tif (targetLength < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet padString = typeof padding === 'undefined' ? '' : String(padding);\n\n\t\tif (padString === '') {\n\t\t\tpadString = ' ';\n\t\t}\n\n\t\twhile (padString.length < targetLength) {\n\t\t\tconst curPadLength    = padString.length;\n\t\t\tconst remainingLength = targetLength - curPadLength;\n\n\t\t\tpadString += curPadLength > remainingLength\n\t\t\t\t? padString.slice(0, remainingLength)\n\t\t\t\t: padString;\n\t\t}\n\n\t\tif (padString.length > targetLength) {\n\t\t\tpadString = padString.slice(0, targetLength);\n\t\t}\n\n\t\treturn padString;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPolyfills.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[ES2019] Returns a new array consisting of the source array with all sub-array elements\n\t\tconcatenated into it recursively up to the given depth.\n\t*/\n\tif (!Array.prototype.flat) {\n\t\tObject.defineProperty(Array.prototype, 'flat', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\t\t\tvalue        : (() => {\n\t\t\t\tfunction flat(/* depth */) {\n\t\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\t\tthrow new TypeError('Array.prototype.flat called on null or undefined');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst depth = arguments.length === 0 ? 1 : Number(arguments[0]) || 0;\n\n\t\t\t\t\tif (depth < 1) {\n\t\t\t\t\t\treturn Array.prototype.slice.call(this);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Array.prototype.reduce.call(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\t(acc, cur) => {\n\t\t\t\t\t\t\tif (cur instanceof Array) {\n\t\t\t\t\t\t\t\t// acc.push.apply(acc, flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t\tacc.push(...flat.call(cur, depth - 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc.push(cur);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn flat;\n\t\t\t})()\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new array consisting of the result of calling the given mapping function\n\t\ton every element in the source array and then concatenating all sub-array elements into it\n\t\trecursively up to a depth of `1`.  Identical to calling `<Array>.map(fn).flat()`.\n\t*/\n\tif (!Array.prototype.flatMap) {\n\t\tObject.defineProperty(Array.prototype, 'flatMap', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* callback [, thisArg] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.flatMap called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.map.apply(this, arguments).flat();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2016] Returns whether the given element was found within the array.\n\t*/\n\tif (!Array.prototype.includes) {\n\t\tObject.defineProperty(Array.prototype, 'includes', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Array.prototype.includes called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst length = this.length >>> 0;\n\n\t\t\t\tif (length === 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst needle = arguments[0];\n\t\t\t\tlet i = Number(arguments[1]) || 0;\n\n\t\t\t\tif (i < 0) {\n\t\t\t\t\ti = Math.max(0, length + i);\n\t\t\t\t}\n\n\t\t\t\tfor (/* empty */; i < length; ++i) {\n\t\t\t\t\tconst value = this[i];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property/value\n\t\tpairs as `[key, value]` arrays.\n\t*/\n\tif (!Object.entries) {\n\t\tObject.defineProperty(Object, 'entries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.entries object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => [key, obj[key]]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a new generic object consisting of the given list's key/value pairs.\n\t*/\n\tif (!Object.fromEntries) {\n\t\tObject.defineProperty(Object, 'fromEntries', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(iter) {\n\t\t\t\treturn Array.from(iter).reduce(\n\t\t\t\t\t(acc, pair) => {\n\t\t\t\t\t\tif (Object(pair) !== pair) {\n\t\t\t\t\t\t\tthrow new TypeError('Object.fromEntries iterable parameter must yield objects');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (pair[0] in acc) {\n\t\t\t\t\t\t\tObject.defineProperty(acc, pair[0], {\n\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\tvalue        : pair[1]\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tacc[pair[0]] = pair[1]; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns all own property descriptors of the given object.\n\t*/\n\tif (!Object.getOwnPropertyDescriptors) {\n\t\tObject.defineProperty(Object, 'getOwnPropertyDescriptors', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (obj == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('Object.getOwnPropertyDescriptors object parameter is null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst O = Object(obj);\n\n\t\t\t\treturn Reflect.ownKeys(O).reduce(\n\t\t\t\t\t(acc, key) => {\n\t\t\t\t\t\tconst desc = Object.getOwnPropertyDescriptor(O, key);\n\n\t\t\t\t\t\tif (typeof desc !== 'undefined') {\n\t\t\t\t\t\t\tif (key in acc) {\n\t\t\t\t\t\t\t\tObject.defineProperty(acc, key, {\n\t\t\t\t\t\t\t\t\tconfigurable : true,\n\t\t\t\t\t\t\t\t\tenumerable   : true,\n\t\t\t\t\t\t\t\t\twritable     : true,\n\t\t\t\t\t\t\t\t\tvalue        : desc\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tacc[key] = desc; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a new array consisting of the given object's own enumerable property values.\n\t*/\n\tif (!Object.values) {\n\t\tObject.defineProperty(Object, 'values', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(obj) {\n\t\t\t\tif (typeof obj !== 'object' || obj === null) {\n\t\t\t\t\tthrow new TypeError('Object.values object parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\treturn Object.keys(obj).map(key => obj[key]);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the start of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padStart) {\n\t\tObject.defineProperty(String.prototype, 'padStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn _createPadString(targetLength - baseLength, padding) + baseString;\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2017] Returns a string based on concatenating the given padding, repeated as necessary,\n\t\tto the end of the string so that the given length is reached.\n\n\t\tNOTE: This pads based upon Unicode code units, rather than code points.\n\t*/\n\tif (!String.prototype.padEnd) {\n\t\tObject.defineProperty(String.prototype, 'padEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(length, padding) {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.padEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\tconst baseString   = String(this);\n\t\t\t\tconst baseLength   = baseString.length;\n\t\t\t\tconst targetLength = Number.parseInt(length, 10);\n\n\t\t\t\tif (targetLength <= baseLength) {\n\t\t\t\t\treturn baseString;\n\t\t\t\t}\n\n\t\t\t\treturn baseString + _createPadString(targetLength - baseLength, padding);\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the start of the string.\n\t*/\n\tif (!String.prototype.trimStart) {\n\t\tObject.defineProperty(String.prototype, 'trimStart', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimStart called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimLeft) {\n\t\tObject.defineProperty(String.prototype, 'trimLeft', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimLeft called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'start');\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\t[ES2019] Returns a string with all whitespace removed from the end of the string.\n\t*/\n\tif (!String.prototype.trimEnd) {\n\t\tObject.defineProperty(String.prototype, 'trimEnd', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimEnd called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n\n\tif (!String.prototype.trimRight) {\n\t\tObject.defineProperty(String.prototype, 'trimRight', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue() {\n\t\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError('String.prototype.trimRight called on null or undefined');\n\t\t\t\t}\n\n\t\t\t\treturn _trimString(this, 'end');\n\t\t\t}\n\t\t});\n\t}\n})();\n\n\n/*\n\tJavaScript Extensions.\n*/\n(() => {\n\t'use strict';\n\n\tconst _nativeMathRandom = Math.random;\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the given bounds.\n\t*/\n\tfunction _random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('_random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = arguments[0];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = arguments[0];\n\t\t\tmax = arguments[1];\n\t\t\tbreak;\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(_nativeMathRandom() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a randomly selected index within the given length and bounds.\n\t\tBounds may be negative.\n\t*/\n\tfunction _randomIndex(length, boundsArgs) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (boundsArgs.length) {\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = length - 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(boundsArgs[1]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(boundsArgs[1]);\n\t\t\tmax = Math.trunc(boundsArgs[2]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min)) {\n\t\t\tmin = 0;\n\t\t}\n\t\telse if (!Number.isFinite(min) || min >= length) {\n\t\t\tmin = length - 1;\n\t\t}\n\t\telse if (min < 0) {\n\t\t\tmin = length + min;\n\n\t\t\tif (min < 0) {\n\t\t\t\tmin = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (Number.isNaN(max)) {\n\t\t\tmax = 0;\n\t\t}\n\t\telse if (!Number.isFinite(max) || max >= length) {\n\t\t\tmax = length - 1;\n\t\t}\n\t\telse if (max < 0) {\n\t\t\tmax = length + max;\n\n\t\t\tif (max < 0) {\n\t\t\t\tmax = length - 1;\n\t\t\t}\n\t\t}\n\n\t\treturn _random(min, max);\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Will throw exceptions on invalid surrogate pairs.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction _getCodePointStartAndEnd(str, pos) {\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : pos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\tthrow new Error('high surrogate without trailing low surrogate');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tchar  : str.charAt(pos) + str.charAt(nextPos),\n\t\t\t\tstart : pos,\n\t\t\t\tend   : nextPos\n\t\t\t};\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\tthrow new Error('low surrogate without leading high surrogate');\n\t\t}\n\n\t\treturn {\n\t\t\tchar  : str.charAt(prevPos) + str.charAt(pos),\n\t\t\tstart : prevPos,\n\t\t\tend   : pos\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, General.\n\t*******************************************************************************************************************/\n\t/*\n\t\tRandomly selects an element from the given array, or array-like object, and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(array /* DEPRECATED: [, [min ,] max] */) {\n\t\t\tif (\n\t\t\t\t   typeof array !== 'object'\n\t\t\t\t|| array === null\n\t\t\t\t|| !Object.prototype.hasOwnProperty.call(array, 'length')\n\t\t\t) {\n\t\t\t\tthrow new TypeError('Array.random array parameter must be an array or array-lke object');\n\t\t\t}\n\n\t\t\tconst length = array.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, Array.prototype.slice.call(arguments, 1));\n\n\t\t\treturn array[index];\n\t\t}\n\t});\n\n\t/*\n\t\tConcatenates one or more unique elements to the end of the base array\n\t\tand returns the result as a new array.  Elements which are arrays will\n\t\tbe merged—i.e. their elements will be concatenated, rather than the\n\t\tarray itself.\n\t*/\n\tObject.defineProperty(Array.prototype, 'concatUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.concatUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst result = Array.from(this);\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst items   = Array.prototype.reduce.call(arguments, (prev, cur) => prev.concat(cur), []);\n\t\t\tconst addSize = items.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = items[i];\n\n\t\t\t\tif (indexOf.call(result, value) === -1) {\n\t\t\t\t\tpush.call(result, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst needle  = arguments[0];\n\t\t\tlet pos   = Number(arguments[1]) || 0;\n\t\t\tlet count = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\t++pos;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the given elements from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'delete', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.delete called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst needles       = Array.prototype.concat.apply([], arguments);\n\t\t\tconst needlesLength = needles.length;\n\t\t\tconst indices       = [];\n\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tconst value = this[i];\n\n\t\t\t\tfor (let j = 0; j < needlesLength; ++j) {\n\t\t\t\t\tconst needle = needles[j];\n\n\t\t\t\t\tif (value === needle || value !== value && needle !== needle) {\n\t\t\t\t\t\tindices.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0, iend = indices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[indices[i]];\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements at the given indices from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteAt', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* indices */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteAt called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice     = Array.prototype.splice;\n\t\t\tconst cpyIndices = [\n\t\t\t\t...new Set(\n\t\t\t\t\tArray.prototype.concat.apply([], arguments)\n\t\t\t\t\t\t// Map negative indices to their positive counterparts,\n\t\t\t\t\t\t// so the Set can properly filter out duplicates.\n\t\t\t\t\t\t.map(x => x < 0 ? Math.max(0, length + x) : x)\n\t\t\t\t).values()\n\t\t\t];\n\t\t\tconst delIndices = [...cpyIndices].sort((a, b) => b - a);\n\t\t\tconst result     = [];\n\n\t\t\t// Copy the elements (in originally specified order).\n\t\t\tfor (let i = 0, iend = cpyIndices.length; i < iend; ++i) {\n\t\t\t\tresult[i] = this[cpyIndices[i]];\n\t\t\t}\n\n\t\t\t// Delete the elements (in descending numeric order).\n\t\t\tfor (let i = 0, iend = delIndices.length; i < iend; ++i) {\n\t\t\t\tsplice.call(this, delIndices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRemoves and returns all of the elements that pass the test implemented\n\t\tby the given predicate function from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'deleteWith', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(predicate, thisArg) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.deleteWith called on null or undefined');\n\t\t\t}\n\t\t\tif (typeof predicate !== 'function') {\n\t\t\t\tthrow new Error('Array.prototype.deleteWith predicate parameter must be a function');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst splice  = Array.prototype.splice;\n\t\t\tconst indices = [];\n\t\t\tconst result  = [];\n\n\t\t\t// Copy the elements (in original order).\n\t\t\tfor (let i = 0; i < length; ++i) {\n\t\t\t\tif (predicate.call(thisArg, this[i], i, this)) {\n\t\t\t\t\tresult.push(this[i]);\n\t\t\t\t\tindices.push(i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete the elements (in reverse order).\n\t\t\tfor (let i = indices.length - 1; i >= 0; --i) {\n\t\t\t\tsplice.call(this, indices[i], 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[0];\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAll called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAll.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\t!Array.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'includesAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needles */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.includesAny called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length === 1) {\n\t\t\t\tif (Array.isArray(arguments[0])) {\n\t\t\t\t\treturn Array.prototype.includesAny.apply(this, arguments[0]);\n\t\t\t\t}\n\n\t\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = arguments.length; i < iend; ++i) {\n\t\t\t\tif (\n\t\t\t\t\tArray.prototype.some.call(this, function (val) {\n\t\t\t\t\t\treturn val === this.val || val !== val && this.val !== this.val;\n\t\t\t\t\t}, { val : arguments[i] })\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last element from the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn this[length - 1];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluck', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluck called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn Array.prototype.splice.call(this, index, 1)[0];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly removes the given number of unique elements from the base array\n\t\tand returns the removed elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pluckMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pluckMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.pluckMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst splice = Array.prototype.splice;\n\t\t\tconst result = [];\n\t\t\tlet max = length - 1;\n\n\t\t\tdo {\n\t\t\t\tresult.push(splice.call(this, _random(0, max--), 1)[0]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tAppends one or more unique elements to the end of the base array and\n\t\treturns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'pushUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.pushUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst push    = Array.prototype.push;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tpush.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects an element from the base array and returns it.\n\t\t[DEPRECATED] Optionally, from within the given bounds.\n\t*/\n\tObject.defineProperty(Array.prototype, 'random', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* DEPRECATED: [min ,] max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.random called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst index = arguments.length === 0\n\t\t\t\t? _random(0, length - 1)\n\t\t\t\t: _randomIndex(length, [...arguments]);\n\n\t\t\treturn this[index];\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly selects the given number of unique elements from the base array\n\t\tand returns the selected elements as a new array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'randomMany', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(wantSize) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.randomMany called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tlet want = Math.trunc(wantSize);\n\n\t\t\tif (!Number.isInteger(want)) {\n\t\t\t\tthrow new Error('Array.prototype.randomMany want parameter must be an integer');\n\t\t\t}\n\n\t\t\tif (want < 1) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (want > length) {\n\t\t\t\twant = length;\n\t\t\t}\n\n\t\t\tconst picked = new Map();\n\t\t\tconst result = [];\n\t\t\tconst max    = length - 1;\n\n\t\t\tdo {\n\t\t\t\tlet i;\n\t\t\t\tdo {\n\t\t\t\t\ti = _random(0, max);\n\t\t\t\t} while (picked.has(i));\n\t\t\t\tpicked.set(i, true);\n\t\t\t\tresult.push(this[i]);\n\t\t\t} while (result.length < want);\n\n\t\t\treturn result;\n\t\t}\n\t});\n\n\t/*\n\t\tRandomly shuffles the array and returns it.\n\t*/\n\tObject.defineProperty(Array.prototype, 'shuffle', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.shuffle called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tfor (let i = length - 1; i > 0; --i) {\n\t\t\t\tconst j = Math.floor(_nativeMathRandom() * (i + 1));\n\n\t\t\t\tif (i === j) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// [this[i], this[j]] = [this[j], this[i]];\n\t\t\t\tconst swap = this[i];\n\t\t\t\tthis[i] = this[j];\n\t\t\t\tthis[j] = swap;\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t});\n\n\t/*\n\t\tPrepends one or more unique elements to the beginning of the base array\n\t\tand returns its new length.\n\t*/\n\tObject.defineProperty(Array.prototype, 'unshiftUnique', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.unshiftUnique called on null or undefined');\n\t\t\t}\n\n\t\t\tconst addSize = arguments.length;\n\n\t\t\tif (addSize === 0) {\n\t\t\t\treturn this.length >>> 0;\n\t\t\t}\n\n\t\t\tconst indexOf = Array.prototype.indexOf;\n\t\t\tconst unshift = Array.prototype.unshift;\n\n\t\t\tfor (let i = 0; i < addSize; ++i) {\n\t\t\t\tconst value = arguments[i];\n\n\t\t\t\tif (indexOf.call(this, value) === -1) {\n\t\t\t\t\tunshift.call(this, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.length >>> 0;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a bound function that supplies the given arguments to the base\n\t\tfunction, followed by the arguments are supplied to the bound function,\n\t\twhenever it is called.\n\t*/\n\tObject.defineProperty(Function.prototype, 'partial', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* variadic */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Function.prototype.partial called on null or undefined');\n\t\t\t}\n\n\t\t\tconst slice = Array.prototype.slice;\n\t\t\tconst fn    = this;\n\t\t\tconst bound = slice.call(arguments, 0);\n\n\t\t\treturn function () {\n\t\t\t\tconst applied = [];\n\t\t\t\tlet argc = 0;\n\n\t\t\t\tfor (let i = 0; i < bound.length; ++i) {\n\t\t\t\t\tapplied.push(bound[i] === undefined ? arguments[argc++] : bound[i]);\n\t\t\t\t}\n\n\t\t\t\treturn fn.apply(this, applied.concat(slice.call(arguments, argc)));\n\t\t\t};\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the given numerical clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Math, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num, min, max) {\n\t\t\tconst value = Number(num);\n\t\t\treturn Number.isNaN(value) ? NaN : value.clamp(min, max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a decimal number eased from 0 to 1.\n\n\t\tNOTE: The magnitude of the returned value decreases if num < 0.5 or increases if num > 0.5.\n\t*/\n\tObject.defineProperty(Math, 'easeInOut', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(num) {\n\t\t\treturn 1 - (Math.cos(Number(num) * Math.PI) + 1) / 2;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number clamped to the specified bounds.\n\t*/\n\tObject.defineProperty(Number.prototype, 'clamp', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* min, max */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Number.prototype.clamp called on null or undefined');\n\t\t\t}\n\n\t\t\tif (arguments.length !== 2) {\n\t\t\t\tthrow new Error('Number.prototype.clamp called with an incorrect number of parameters');\n\t\t\t}\n\n\t\t\tlet min = Number(arguments[0]);\n\t\t\tlet max = Number(arguments[1]);\n\n\t\t\tif (min > max) {\n\t\t\t\t[min, max] = [max, min];\n\t\t\t}\n\n\t\t\treturn Math.min(Math.max(this, min), max);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the given string with all RegExp metacharacters escaped.\n\t*/\n\tif (!RegExp.escape) {\n\t\t(() => {\n\t\t\tconst _regExpMetaCharsRe    = /[\\\\^$*+?.()|[\\]{}]/g;\n\t\t\tconst _hasRegExpMetaCharsRe = new RegExp(_regExpMetaCharsRe.source); // to drop the global flag\n\n\t\t\tObject.defineProperty(RegExp, 'escape', {\n\t\t\t\tconfigurable : true,\n\t\t\t\twritable     : true,\n\n\t\t\t\tvalue(str) {\n\t\t\t\t\tconst val = String(str);\n\t\t\t\t\treturn val && _hasRegExpMetaCharsRe.test(val)\n\t\t\t\t\t\t? val.replace(_regExpMetaCharsRe, '\\\\$&')\n\t\t\t\t\t\t: val;\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t}\n\n\t/*\n\t\tReturns a formatted string, after replacing each format item in the given\n\t\tformat string with the text equivalent of the corresponding argument's value.\n\t*/\n\t(() => {\n\t\tconst _formatRegExp    = /{(\\d+)(?:,([+-]?\\d+))?}/g;\n\t\tconst _hasFormatRegExp = new RegExp(_formatRegExp.source); // to drop the global flag\n\n\t\tObject.defineProperty(String, 'format', {\n\t\t\tconfigurable : true,\n\t\t\twritable     : true,\n\n\t\t\tvalue(format) {\n\t\t\t\tfunction padString(str, align, pad) {\n\t\t\t\t\tif (!align) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst plen = Math.abs(align) - str.length;\n\n\t\t\t\t\tif (plen < 1) {\n\t\t\t\t\t\treturn str;\n\t\t\t\t\t}\n\n\t\t\t\t\t// const padding = Array(plen + 1).join(pad);\n\t\t\t\t\tconst padding = String(pad).repeat(plen);\n\t\t\t\t\treturn align < 0 ? str + padding : padding + str;\n\t\t\t\t}\n\n\t\t\t\tif (arguments.length < 2) {\n\t\t\t\t\treturn arguments.length === 0 ? '' : format;\n\t\t\t\t}\n\n\t\t\t\tconst args = arguments.length === 2 && Array.isArray(arguments[1])\n\t\t\t\t\t? [...arguments[1]]\n\t\t\t\t\t: Array.prototype.slice.call(arguments, 1);\n\n\t\t\t\tif (args.length === 0) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\tif (!_hasFormatRegExp.test(format)) {\n\t\t\t\t\treturn format;\n\t\t\t\t}\n\n\t\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t\t_formatRegExp.lastIndex = 0;\n\n\t\t\t\treturn format.replace(_formatRegExp, (match, index, align) => {\n\t\t\t\t\tlet retval = args[index];\n\n\t\t\t\t\tif (retval == null) { // lazy equality for null\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\n\t\t\t\t\twhile (typeof retval === 'function') {\n\t\t\t\t\t\tretval = retval();\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (typeof retval) {\n\t\t\t\t\tcase 'string': /* no-op */ break;\n\t\t\t\t\tcase 'object': retval = JSON.stringify(retval); break;\n\t\t\t\t\tdefault:       retval = String(retval); break;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn padString(retval, !align ? 0 : Number.parseInt(align, 10), ' ');\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t})();\n\n\t/*\n\t\tReturns whether the given string was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn String.prototype.indexOf.apply(this, arguments) !== -1;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the number of times the given substring was found within the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'count', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex ] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.count called on null or undefined');\n\t\t\t}\n\n\t\t\tconst needle = String(arguments[0] || '');\n\n\t\t\tif (needle === '') {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tconst indexOf = String.prototype.indexOf;\n\t\t\tconst step    = needle.length;\n\t\t\tlet pos     = Number(arguments[1]) || 0;\n\t\t\tlet count   = 0;\n\n\t\t\twhile ((pos = indexOf.call(this, needle, pos)) !== -1) {\n\t\t\t\t++count;\n\t\t\t\tpos += step;\n\t\t\t}\n\n\t\t\treturn count;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the first Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'first', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.first called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns the last Unicode code point from the string.\n\t*/\n\tObject.defineProperty(String.prototype, 'last', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.last called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the last code point—may be one or two code units—and its end position.\n\t\t\tconst { char } = _getCodePointStartAndEnd(str, str.length - 1);\n\n\t\t\treturn char;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with `delCount` characters replaced with\n\t\t`replacement`, starting at `startAt`.\n\t*/\n\tObject.defineProperty(String.prototype, 'splice', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(startAt, delCount, replacement) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splice called on null or undefined');\n\t\t\t}\n\n\t\t\tconst length = this.length >>> 0;\n\n\t\t\tif (length === 0) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tlet start = Number(startAt);\n\n\t\t\tif (!Number.isSafeInteger(start)) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\telse if (start < 0) {\n\t\t\t\tstart += length;\n\n\t\t\t\tif (start < 0) {\n\t\t\t\t\tstart = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (start > length) {\n\t\t\t\tstart = length;\n\t\t\t}\n\n\t\t\tlet count = Number(delCount);\n\n\t\t\tif (!Number.isSafeInteger(count) || count < 0) {\n\t\t\t\tcount = 0;\n\t\t\t}\n\n\t\t\tlet res = this.slice(0, start);\n\n\t\t\tif (typeof replacement !== 'undefined') {\n\t\t\t\tres += replacement;\n\t\t\t}\n\n\t\t\tif (start + count < length) {\n\t\t\t\tres += this.slice(start + count);\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t});\n\n\t/*\n\t\tReturns an array of strings, split from the string, or an empty array if the\n\t\tstring is empty.\n\t*/\n\tObject.defineProperty(String.prototype, 'splitOrEmpty', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* [ separator [, limit ]] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.splitOrEmpty called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tif (String(this) === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\treturn String.prototype.split.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased,\n\t\taccording to any locale-specific rules.\n\t*/\n\tObject.defineProperty(String.prototype, 'toLocaleUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toLocaleUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toLocaleUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\t/*\n\t\tReturns a copy of the base string with the first Unicode code point uppercased.\n\t*/\n\tObject.defineProperty(String.prototype, 'toUpperFirst', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.toUpperFirst called on null or undefined');\n\t\t\t}\n\n\t\t\t// Required as `this` could be a `String` object or come from a `call()` or `apply()`.\n\t\t\tconst str = String(this);\n\n\t\t\t// Get the first code point—may be one or two code units—and its end position.\n\t\t\tconst { char, end } = _getCodePointStartAndEnd(str, 0);\n\n\t\t\treturn end === -1 ? '' : char.toUpperCase() + str.slice(end + 1);\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, JSON.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDefine `toJSON()` methods on each prototype we wish to support.\n\t*/\n\tObject.defineProperty(Date.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:date)', this.toISOString()];\n\t\t}\n\t});\n\tObject.defineProperty(Function.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\t/*\n\t\t\t\tThe enclosing parenthesis here are necessary to force the function expression code\n\t\t\t\tstring, returned by `this.toString()`, to be evaluated as an expression during\n\t\t\t\trevival.  Without them, the function expression, which is likely nameless, will be\n\t\t\t\tevaluated as a function definition—which will throw a syntax error exception, since\n\t\t\t\tfunction definitions must have a name.\n\t\t\t*/\n\t\t\treturn ['(revive:eval)', `(${this.toString()})`];\n\t\t}\n\t});\n\tObject.defineProperty(Map.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:map)', [...this]];\n\t\t}\n\t});\n\tObject.defineProperty(RegExp.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:eval)', this.toString()];\n\t\t}\n\t});\n\tObject.defineProperty(Set.prototype, 'toJSON', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\treturn ['(revive:set)', [...this]];\n\t\t}\n\t});\n\n\t/*\n\t\tUtility method to allow users to easily wrap their code in the revive wrapper.\n\t*/\n\tObject.defineProperty(JSON, 'reviveWrapper', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(code, data) {\n\t\t\tif (typeof code !== 'string') {\n\t\t\t\tthrow new TypeError('JSON.reviveWrapper code parameter must be a string');\n\t\t\t}\n\n\t\t\treturn ['(revive:eval)', [code, data]];\n\t\t}\n\t});\n\n\t/*\n\t\tBackup the original `JSON.parse()` and replace it with a revive wrapper aware version.\n\t*/\n\tObject.defineProperty(JSON, '_real_parse', {\n\t\tvalue : JSON.parse\n\t});\n\tObject.defineProperty(JSON, 'parse', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(text, reviver) {\n\t\t\treturn JSON._real_parse(text, (key, val) => {\n\t\t\t\tlet value = val;\n\n\t\t\t\t/*\n\t\t\t\t\tAttempt to revive wrapped values.\n\t\t\t\t*/\n\t\t\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\t\t\tswitch (value[0]) {\n\t\t\t\t\tcase '(revive:set)':\n\t\t\t\t\t\tvalue = new Set(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:map)':\n\t\t\t\t\t\tvalue = new Map(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:date)':\n\t\t\t\t\t\tvalue = new Date(value[1]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '(revive:eval)':\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/* eslint-disable no-eval */\n\t\t\t\t\t\t\t// For post-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\tif (Array.isArray(value[1])) {\n\t\t\t\t\t\t\t\tconst $ReviveData$ = value[1][1]; // eslint-disable-line no-unused-vars\n\t\t\t\t\t\t\t\tvalue = eval(value[1][0]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// For regular expressions, functions, and pre-v2.9.0 `JSON.reviveWrapper()`.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvalue = eval(value[1]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* eslint-enable no-eval */\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* legacy */\n\t\t\t\telse if (typeof value === 'string' && value.slice(0, 10) === '@@revive@@') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = eval(value.slice(10)); // eslint-disable-line no-eval\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\t\t\t\t/* /legacy */\n\n\t\t\t\t/*\n\t\t\t\t\tCall the custom reviver, if specified.\n\t\t\t\t*/\n\t\t\t\tif (typeof reviver === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvalue = reviver(key, value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) { /* no-op; although, perhaps, it would be better to throw an error here */ }\n\t\t\t\t}\n\n\t\t\t\treturn value;\n\t\t\t});\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tExtensions, Deprecated.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns whether the given element was found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'contains', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.contains called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includes.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether all of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAll', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAll called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAll.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns whether any of the given elements were found within the array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'containsAny', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue(/* needle [, fromIndex] */) {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.containsAny called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.includesAny.apply(this, arguments);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns a new array consisting of the flattened source array.\n\t*/\n\tObject.defineProperty(Array.prototype, 'flatten', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('Array.prototype.flatten called on null or undefined');\n\t\t\t}\n\n\t\t\treturn Array.prototype.flat.call(this, Infinity);\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] Returns an array of link titles, parsed from the string.\n\n\t\tNOTE: Unused in SugarCube, only included for compatibility.\n\t*/\n\tObject.defineProperty(String.prototype, 'readBracketedList', {\n\t\tconfigurable : true,\n\t\twritable     : true,\n\n\t\tvalue() {\n\t\t\tif (this == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('String.prototype.readBracketedList called on null or undefined');\n\t\t\t}\n\n\t\t\t// RegExp groups: Double-square-bracket quoted | Unquoted.\n\t\t\tconst re    = new RegExp('(?:\\\\[\\\\[((?:\\\\s|\\\\S)*?)\\\\]\\\\])|([^\"\\'\\\\s]\\\\S*)', 'gm');\n\t\t\tconst names = [];\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.exec(this)) !== null) {\n\t\t\t\tif (match[1]) { // double-square-bracket quoted\n\t\t\t\t\tnames.push(match[1]);\n\t\t\t\t}\n\t\t\t\telse if (match[2]) { // unquoted\n\t\t\t\t\tnames.push(match[2]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn names;\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/browser.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Browser = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable max-len */\n\tconst userAgent = navigator.userAgent.toLowerCase();\n\n\tconst winPhone = userAgent.includes('windows phone');\n\tconst isMobile = Object.freeze({\n\t\tAndroid    : !winPhone && userAgent.includes('android'),\n\t\tBlackBerry : /blackberry|bb10/.test(userAgent),\n\t\tiOS        : !winPhone && /ip(?:hone|ad|od)/.test(userAgent),\n\t\tOpera      : !winPhone && (typeof window.operamini === 'object' || userAgent.includes('opera mini')),\n\t\tWindows    : winPhone || /iemobile|wpdesktop/.test(userAgent),\n\n\t\tany() {\n\t\t\treturn isMobile.Android || isMobile.BlackBerry || isMobile.iOS || isMobile.Opera || isMobile.Windows;\n\t\t}\n\t});\n\n\tconst isGecko = !isMobile.Windows && !/khtml|trident|edge/.test(userAgent) && userAgent.includes('gecko');\n\n\tconst isIE      = !userAgent.includes('opera') && /msie|trident/.test(userAgent);\n\tconst ieVersion = isIE\n\t\t? (() => {\n\t\t\tconst ver = /(?:msie\\s+|rv:)(\\d+\\.\\d)/.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\t// opera <= 12: \"opera/9.80 (windows nt 6.1; wow64) presto/2.12.388 version/12.16\"\n\t// opera >= 15: \"mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/28.0.1500.52 safari/537.36 opr/15.0.1147.130\"\n\tconst isOpera      = userAgent.includes('opera') || userAgent.includes(' opr/');\n\tconst operaVersion = isOpera\n\t\t? (() => {\n\t\t\tconst re  = new RegExp(`${/khtml|chrome/.test(userAgent) ? 'opr' : 'version'}\\\\/(\\\\d+\\\\.\\\\d+)`);\n\t\t\tconst ver = re.exec(userAgent);\n\t\t\treturn ver ? Number(ver[1]) : 0;\n\t\t})()\n\t\t: null;\n\n\tconst isVivaldi = userAgent.includes('vivaldi');\n\t/* eslint-enable max-len */\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\tuserAgent,\n\t\tisMobile,\n\t\tisGecko,\n\t\tisIE,\n\t\tieVersion,\n\t\tisOpera,\n\t\toperaVersion,\n\t\tisVivaldi\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/has.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Has = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tNOTE: The aggressive try/catch feature tests are necessitated by implementation\n\t\tbugs in various browsers.\n\t*/\n\n\t// Is the `HTMLAudioElement` API available?\n\tconst hasAudioElement = (() => {\n\t\ttry {\n\t\t\treturn typeof document.createElement('audio').canPlayType === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `File` API available?\n\tconst hasFile = (() => {\n\t\ttry {\n\t\t\treturn 'Blob' in window &&\n\t\t\t\t'File' in window &&\n\t\t\t\t'FileList' in window &&\n\t\t\t\t'FileReader' in window &&\n\t\t\t\t!Browser.isMobile.any() &&\n\t\t\t\t(!Browser.isOpera || Browser.operaVersion >= 15);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `geolocation` API available?\n\tconst hasGeolocation = (() => {\n\t\ttry {\n\t\t\treturn 'geolocation' in navigator &&\n\t\t\t\ttypeof navigator.geolocation.getCurrentPosition === 'function' &&\n\t\t\t\ttypeof navigator.geolocation.watchPosition === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `MutationObserver` API available?\n\tconst hasMutationObserver = (() => {\n\t\ttry {\n\t\t\treturn 'MutationObserver' in window &&\n\t\t\t\ttypeof window.MutationObserver === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the `performance` API available?\n\tconst hasPerformance = (() => {\n\t\ttry {\n\t\t\treturn 'performance' in window &&\n\t\t\t\ttypeof window.performance.now === 'function';\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the platform a touch device?\n\tconst hasTouch = (() => {\n\t\ttry {\n\t\t\treturn 'ontouchstart' in window ||\n\t\t\t\t!!window.DocumentTouch &&\n\t\t\t\tdocument instanceof window.DocumentTouch ||\n\t\t\t\t!!navigator.maxTouchPoints ||\n\t\t\t\t!!navigator.msMaxTouchPoints;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Is the transition end event available and by what name?\n\tconst hasTransitionEndEvent = (() => {\n\t\ttry {\n\t\t\tconst teMap = new Map([\n\t\t\t\t['transition',       'transitionend'],\n\t\t\t\t['MSTransition',     'msTransitionEnd'],\n\t\t\t\t['WebkitTransition', 'webkitTransitionEnd'],\n\t\t\t\t['MozTransition',    'transitionend']\n\t\t\t]);\n\t\t\tconst teKeys = [...teMap.keys()];\n\t\t\tconst el     = document.createElement('div');\n\n\t\t\tfor (let i = 0; i < teKeys.length; ++i) {\n\t\t\t\tif (el.style[teKeys[i]] !== undefined) {\n\t\t\t\t\treturn teMap.get(teKeys[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t})();\n\n\t// Module Exports.\n\treturn Object.freeze({\n\t\taudio              : hasAudioElement,\n\t\tfileAPI            : hasFile,\n\t\tgeolocation        : hasGeolocation,\n\t\tmutationObserver   : hasMutationObserver,\n\t\tperformance        : hasPerformance,\n\t\ttouch              : hasTouch,\n\t\ttransitionEndEvent : hasTransitionEndEvent\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/visibility.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar Visibility = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tThere are two versions of the Page Visibility API: First Edition and, the current,\n\t\tSecond Edition (i.e. \"Level 2\").  First Edition is mentioned here only because some\n\t\tolder browsers implement it, rather than the current specification.\n\n\t\tSEE:\n\t\t\tSecond Edition : https://www.w3.org/TR/page-visibility/\n\t\t\tFirst Edition  : https://www.w3.org/TR/2013/REC-page-visibility-20130514/\n\n\t\tNOTE: Generally, all supported browsers change the visibility state when either switching tabs\n\t\twithin the browser or minimizing the browser window.  Exceptions are noted below:\n\t\t\t* IE 9 doesn't support either version of the Page Visibility API.\n\t\t\t* Opera 12 (Presto) doesn't change the visibility state when the browser is minimized.\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'hidden',          // boolean; historical in 2nd edition\n\t\t\t\t\tstateProperty  : 'visibilityState', // string, values: 'hidden', 'visible'; 1st edition had more values\n\t\t\t\t\tchangeEvent    : 'visibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink & WebKit.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'webkitHidden',\n\t\t\t\t\tstateProperty  : 'webkitVisibilityState',\n\t\t\t\t\tchangeEvent    : 'webkitvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'mozHidden',\n\t\t\t\t\tstateProperty  : 'mozVisibilityState',\n\t\t\t\t\tchangeEvent    : 'mozvisibilitychange'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 10.\n\t\t\t\t{\n\t\t\t\t\thiddenProperty : 'msHidden',\n\t\t\t\t\tstateProperty  : 'msVisibilityState',\n\t\t\t\t\tchangeEvent    : 'msvisibilitychange'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.hiddenProperty in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getVisibility() {\n\t\treturn vendor && document[vendor.stateProperty] || 'visible';\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor);\n\t}\n\n\tfunction isHidden() {\n\t\t// return Boolean(vendor && document[vendor.stateProperty] === 'hidden');\n\t\treturn Boolean(vendor && document[vendor.hiddenProperty]); // NOTE: Historical, but probably better for 1st edition.\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Functions.\n\t\tvendor    : { get : getVendor },\n\t\tstate     : { get : getVisibility },\n\t\tisEnabled : { value : isEnabled },\n\t\tisHidden  : { value : isHidden },\n\n\t\t// Properties.\n\t\thiddenProperty : { value : vendor && vendor.hiddenProperty },\n\t\tstateProperty  : { value : vendor && vendor.stateProperty },\n\t\tchangeEvent    : { value : vendor && vendor.changeEvent }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/fullscreen.js\n\n\tCopyright © 2018–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Browser */\n\nvar Fullscreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tSEE:\n\t\t\thttps://fullscreen.spec.whatwg.org\n\t\t\thttps://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API\n\t*/\n\n\t// Vendor properties object.\n\tconst vendor = (() => {\n\t\ttry {\n\t\t\treturn Object.freeze([\n\t\t\t\t// Specification.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'fullscreenEnabled',\n\t\t\t\t\telement     : 'fullscreenElement',\n\t\t\t\t\trequestFn   : 'requestFullscreen',\n\t\t\t\t\texitFn      : 'exitFullscreen',\n\t\t\t\t\tchangeEvent : 'fullscreenchange', // prop: onfullscreenchange\n\t\t\t\t\terrorEvent  : 'fullscreenerror'   // prop: onfullscreenerror\n\t\t\t\t},\n\n\t\t\t\t// `webkit` prefixed: old Blink, WebKit, & Edge.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'webkitFullscreenEnabled',\n\t\t\t\t\telement     : 'webkitFullscreenElement',\n\t\t\t\t\trequestFn   : 'webkitRequestFullscreen',\n\t\t\t\t\texitFn      : 'webkitExitFullscreen',\n\t\t\t\t\tchangeEvent : 'webkitfullscreenchange',\n\t\t\t\t\terrorEvent  : 'webkitfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `moz` prefixed: old Gecko, maybe Seamonkey.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'mozFullScreenEnabled',\n\t\t\t\t\telement     : 'mozFullScreenElement',\n\t\t\t\t\trequestFn   : 'mozRequestFullScreen',\n\t\t\t\t\texitFn      : 'mozCancelFullScreen',\n\t\t\t\t\tchangeEvent : 'mozfullscreenchange',\n\t\t\t\t\terrorEvent  : 'mozfullscreenerror'\n\t\t\t\t},\n\n\t\t\t\t// `ms` prefixed: IE 11.\n\t\t\t\t{\n\t\t\t\t\tisEnabled   : 'msFullscreenEnabled',\n\t\t\t\t\telement     : 'msFullscreenElement',\n\t\t\t\t\trequestFn   : 'msRequestFullscreen',\n\t\t\t\t\texitFn      : 'msExitFullscreen',\n\t\t\t\t\tchangeEvent : 'MSFullscreenChange',\n\t\t\t\t\terrorEvent  : 'MSFullscreenError'\n\t\t\t\t}\n\t\t\t].find(vnd => vnd.isEnabled in document));\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn undefined;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************/\n\n\t// Return whether the request and exit fullscreen methods return a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _returnsPromise = (function () {\n\t\t// Cache of whether the request and exit methods return a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _returnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (vendor) {\n\t\t\t\ttry {\n\t\t\t\t\tconst value = document.exitFullscreen();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since we shouldn't be in fullscreen yet,\n\t\t\t\t\t// and we don't actually care about the error, since we just want the return\n\t\t\t\t\t// value, so we consign it to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _returnsPromise;\n\t})();\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _selectElement(requestedEl) {\n\t\tlet selectedEl = requestedEl || document.documentElement;\n\n\t\t// Document element scrolling workaround for older browsers.\n\t\tif (\n\t\t\t   selectedEl === document.documentElement\n\t\t\t&& (\n\t\t\t\t   vendor.requestFn === 'msRequestFullscreen'   // IE 11\n\t\t\t\t|| Browser.isOpera && Browser.operaVersion < 15 // Opera 12 (Presto)\n\t\t\t)\n\t\t) {\n\t\t\tselectedEl = document.body;\n\t\t}\n\n\t\treturn selectedEl;\n\t}\n\n\n\t/*******************************************************************************\n\t\tAPI Functions.\n\t*******************************************************************************/\n\n\tfunction getVendor() {\n\t\treturn vendor;\n\t}\n\n\tfunction getElement() {\n\t\treturn (vendor || null) && document[vendor.element];\n\t}\n\n\tfunction isEnabled() {\n\t\treturn Boolean(vendor && document[vendor.isEnabled]);\n\t}\n\n\tfunction isFullscreen() {\n\t\treturn Boolean(vendor && document[vendor.element]);\n\t}\n\n\tfunction requestFullscreen(options, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (typeof element[vendor.requestFn] !== 'function') {\n\t\t\treturn Promise.reject(new Error('fullscreen not supported'));\n\t\t}\n\t\tif (isFullscreen()) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn element[vendor.requestFn](options);\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_requestFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(element)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen request error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\telement[vendor.requestFn](options);\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction exitFullscreen() {\n\t\tif (!vendor || typeof document[vendor.exitFn] !== 'function') {\n\t\t\treturn Promise.reject(new TypeError('fullscreen not supported'));\n\t\t}\n\t\tif (!isFullscreen()) {\n\t\t\treturn Promise.reject(new TypeError('fullscreen mode not active'));\n\t\t}\n\n\t\tif (_returnsPromise()) {\n\t\t\treturn document[vendor.exitFn]();\n\t\t}\n\t\telse { // eslint-disable-line no-else-return\n\t\t\tconst namespace = '.Fullscreen_exitFullscreen';\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tjQuery(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`${vendor.errorEvent}${namespace} ${vendor.changeEvent}${namespace}`, ev => {\n\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\tif (ev.type === vendor.errorEvent) {\n\t\t\t\t\t\t\treject(new Error('unknown fullscreen exit error'));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tdocument[vendor.exitFn]();\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction toggleFullscreen(options, requestedEl) {\n\t\treturn isFullscreen() ? exitFullscreen() : requestFullscreen(options, requestedEl);\n\t}\n\n\tfunction onChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.changeEvent, handlerFn);\n\t}\n\n\tfunction offChange(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.changeEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.changeEvent);\n\t\t}\n\t}\n\n\tfunction onError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\t$(element).on(vendor.errorEvent, handlerFn);\n\t}\n\n\tfunction offError(handlerFn, requestedEl) {\n\t\tif (!vendor) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst element = _selectElement(requestedEl);\n\n\t\tif (handlerFn) {\n\t\t\t$(element).off(vendor.errorEvent, handlerFn);\n\t\t}\n\t\telse {\n\t\t\t$(element).off(vendor.errorEvent);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tvendor       : { get : getVendor },\n\t\telement      : { get : getElement },\n\t\tisEnabled    : { value : isEnabled },\n\t\tisFullscreen : { value : isFullscreen },\n\t\trequest      : { value : requestFullscreen },\n\t\texit         : { value : exitFullscreen },\n\t\ttoggle       : { value : toggleFullscreen },\n\t\tonChange     : { value : onChange },\n\t\toffChange    : { value : offChange },\n\t\tonError      : { value : onError },\n\t\toffError     : { value : offError }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/helpers.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, State, Story, Util, Wikifier */\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tclone,\n\tconvertBreaks,\n\tsafeActiveElement,\n\tsetDisplayTitle,\n\tsetPageElement,\n\tthrowError,\n\ttoStringOrDefault\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _getTextContent(source) {\n\t\tconst copy = source.cloneNode(true);\n\t\tconst frag = document.createDocumentFragment();\n\t\tlet node;\n\n\t\twhile ((node = copy.firstChild) !== null) {\n\t\t\t// Insert spaces before various elements.\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tswitch (node.nodeName.toUpperCase()) {\n\t\t\t\tcase 'BR':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'P':\n\t\t\t\t\tfrag.appendChild(document.createTextNode(' '));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfrag.appendChild(node);\n\t\t}\n\n\t\treturn frag.textContent;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a deep copy of the given object.\n\n\t\tNOTE:\n\t\t\t1. `clone()` does not clone functions, however, since function definitions\n\t\t\t   are immutable, the only issues are with expando properties and scope.\n\t\t\t   The former really should not be done.  The latter is problematic either\n\t\t\t   way—damned if you do, damned if you don't.\n\t\t\t2. `clone()` does not maintain referential relationships—e.g. multiple\n\t\t\t   references to the same object will, post-cloning, refer to different\n\t\t\t   equivalent objects; i.e. each reference will receive its own clone\n\t\t\t   of the original object.\n\t*/\n\tfunction clone(orig) {\n\t\t/*\n\t\t\tImmediately return the primitives and functions.\n\t\t*/\n\t\tif (typeof orig !== 'object' || orig === null) {\n\t\t\treturn orig;\n\t\t}\n\n\t\t/*\n\t\t\tUnbox instances of the primitive exemplar objects.\n\t\t*/\n\t\tif (orig instanceof String) {\n\t\t\treturn String(orig);\n\t\t}\n\t\tif (orig instanceof Number) {\n\t\t\treturn Number(orig);\n\t\t}\n\t\tif (orig instanceof Boolean) {\n\t\t\treturn Boolean(orig);\n\t\t}\n\n\t\t/*\n\t\t\tHonor native clone methods.\n\t\t*/\n\t\tif (typeof orig.clone === 'function') {\n\t\t\treturn orig.clone(true);\n\t\t}\n\t\tif (orig.nodeType && typeof orig.cloneNode === 'function') {\n\t\t\treturn orig.cloneNode(true);\n\t\t}\n\n\t\t/*\n\t\t\tCreate a copy of the original object.\n\n\t\t\tNOTE: Each non-generic object that we wish to support must be\n\t\t\texplicitly handled below.\n\t\t*/\n\t\tlet copy;\n\n\t\t// Handle instances of the core supported object types.\n\t\tif (orig instanceof Array) {\n\t\t\tcopy = new Array(orig.length);\n\t\t}\n\t\telse if (orig instanceof Date) {\n\t\t\tcopy = new Date(orig.getTime());\n\t\t}\n\t\telse if (orig instanceof Map) {\n\t\t\tcopy = new Map();\n\t\t\torig.forEach((val, key) => copy.set(key, clone(val)));\n\t\t}\n\t\telse if (orig instanceof RegExp) {\n\t\t\tcopy = new RegExp(orig);\n\t\t}\n\t\telse if (orig instanceof Set) {\n\t\t\tcopy = new Set();\n\t\t\torig.forEach(val => copy.add(clone(val)));\n\t\t}\n\n\t\t// Handle instances of unknown or generic objects.\n\t\telse {\n\t\t\t// We try to ensure that the returned copy has the same prototype as\n\t\t\t// the original, but this will probably produce less than satisfactory\n\t\t\t// results on non-generics.\n\t\t\tcopy = Object.create(Object.getPrototypeOf(orig));\n\t\t}\n\n\t\t/*\n\t\t\tDuplicate the original object's own enumerable properties, which will\n\t\t\tinclude expando properties on non-generic objects.\n\n\t\t\tNOTE: This preserves neither symbol properties nor ES5 property attributes.\n\t\t\tNeither does the delta coding or serialization code, however, so it's not\n\t\t\treally an issue at the moment.\n\t\t*/\n\t\tObject.keys(orig).forEach(name => copy[name] = clone(orig[name]));\n\n\t\treturn copy;\n\t}\n\n\t/*\n\t\tConverts <br> elements to <p> elements within the given node tree.\n\t*/\n\tfunction convertBreaks(source) {\n\t\tconst output = document.createDocumentFragment();\n\t\tlet para = document.createElement('p');\n\t\tlet node;\n\n\t\twhile ((node = source.firstChild) !== null) {\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase 'BR':\n\t\t\t\t\tif (\n\t\t\t\t\t\t   node.nextSibling !== null\n\t\t\t\t\t\t&& node.nextSibling.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t\t&& node.nextSibling.nodeName.toUpperCase() === 'BR'\n\t\t\t\t\t) {\n\t\t\t\t\t\tsource.removeChild(node.nextSibling);\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!para.hasChildNodes()) {\n\t\t\t\t\t\tsource.removeChild(node);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'ADDRESS':\n\t\t\t\tcase 'ARTICLE':\n\t\t\t\tcase 'ASIDE':\n\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\tcase 'CENTER':\n\t\t\t\tcase 'DIV':\n\t\t\t\tcase 'DL':\n\t\t\t\tcase 'FIGURE':\n\t\t\t\tcase 'FOOTER':\n\t\t\t\tcase 'FORM':\n\t\t\t\tcase 'H1':\n\t\t\t\tcase 'H2':\n\t\t\t\tcase 'H3':\n\t\t\t\tcase 'H4':\n\t\t\t\tcase 'H5':\n\t\t\t\tcase 'H6':\n\t\t\t\tcase 'HEADER':\n\t\t\t\tcase 'HR':\n\t\t\t\tcase 'MAIN':\n\t\t\t\tcase 'NAV':\n\t\t\t\tcase 'OL':\n\t\t\t\tcase 'P':\n\t\t\t\tcase 'PRE':\n\t\t\t\tcase 'SECTION':\n\t\t\t\tcase 'TABLE':\n\t\t\t\tcase 'UL':\n\t\t\t\t\tif (para.hasChildNodes()) {\n\t\t\t\t\t\toutput.appendChild(para);\n\t\t\t\t\t\tpara = document.createElement('p');\n\t\t\t\t\t}\n\n\t\t\t\t\toutput.appendChild(node);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpara.appendChild(node);\n\t\t}\n\n\t\tif (para.hasChildNodes()) {\n\t\t\toutput.appendChild(para);\n\t\t}\n\n\t\tsource.appendChild(output);\n\t}\n\n\t/*\n\t\tReturns `document.activeElement` or `null`.\n\t*/\n\tfunction safeActiveElement() {\n\t\t/*\n\t\t\tIE9 contains a bug where trying to access the active element of an iframe's\n\t\t\tparent document (i.e. `window.parent.document.activeElement`) will throw an\n\t\t\texception, so we must allow for an exception to be thrown.\n\n\t\t\tWe could simply return `undefined` here, but since the API's default behavior\n\t\t\tshould be to return `document.body` or `null` when there is no selection, we\n\t\t\tchoose to return `null` in all non-element cases (i.e. whether it returns\n\t\t\t`null` or throws an exception).  Just a bit of normalization.\n\t\t*/\n\t\ttry {\n\t\t\treturn document.activeElement || null;\n\t\t}\n\t\tcatch (ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/*\n\t\tSets the display title.\n\t*/\n\tfunction setDisplayTitle(title) {\n\t\tif (typeof title !== 'string') {\n\t\t\tthrow new TypeError(`story display title must be a string (received: ${Util.getType(title)})`);\n\t\t}\n\n\t\tconst render = document.createDocumentFragment();\n\t\tnew Wikifier(render, title);\n\n\t\tconst text = _getTextContent(render).trim();\n\n\t\t// if (text === '') {\n\t\t// \tthrow new Error('story display title must not render to an empty string or consist solely of whitespace');\n\t\t// }\n\n\t\tdocument.title = Config.passages.displayTitles && State.passage !== '' && State.passage !== Config.passages.start\n\t\t\t? `${State.passage} | ${text}`\n\t\t\t: text;\n\n\t\tconst storyTitle = document.getElementById('story-title');\n\n\t\tif (storyTitle !== null) {\n\t\t\tjQuery(storyTitle).empty().append(render);\n\t\t}\n\t}\n\n\t/*\n\t\tWikifies a passage into a DOM element corresponding to the passed ID and returns the element.\n\t*/\n\tfunction setPageElement(idOrElement, titles, defaultText) {\n\t\tconst el = typeof idOrElement === 'object'\n\t\t\t? idOrElement\n\t\t\t: document.getElementById(idOrElement);\n\n\t\tif (el == null) { // lazy equality for null\n\t\t\treturn null;\n\t\t}\n\n\t\tconst ids = Array.isArray(titles) ? titles : [titles];\n\n\t\tjQuery(el).empty();\n\n\t\tfor (let i = 0, iend = ids.length; i < iend; ++i) {\n\t\t\tif (Story.has(ids[i])) {\n\t\t\t\tnew Wikifier(el, Story.get(ids[i]).processText().trim());\n\t\t\t\treturn el;\n\t\t\t}\n\t\t}\n\n\t\tif (defaultText != null) { // lazy equality for null\n\t\t\tconst text = String(defaultText).trim();\n\n\t\t\tif (text !== '') {\n\t\t\t\tnew Wikifier(el, text);\n\t\t\t}\n\t\t}\n\n\t\treturn el;\n\t}\n\n\t/*\n\t\tAppends an error view to the passed DOM element.\n\t*/\n\tfunction throwError(place, message, source, stack) {\n\t\tconst $wrapper = jQuery(document.createElement('div'));\n\t\tconst $toggle  = jQuery(document.createElement('button'));\n\t\tconst $source  = jQuery(document.createElement('pre'));\n\t\tconst mesg     = `${L10n.get('errorTitle')}: ${message || 'unknown error'} ${Config.saves.version}`;\n\n\t\t$toggle\n\t\t\t.addClass('error-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('errorToggle')\n\t\t\t}, () => {\n\t\t\t\tif ($toggle.hasClass('enabled')) {\n\t\t\t\t\t$toggle.removeClass('enabled');\n\t\t\t\t\t$source.attr({\n\t\t\t\t\t\t'aria-hidden' : true,\n\t\t\t\t\t\thidden        : 'hidden'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$toggle.addClass('enabled');\n\t\t\t\t\t$source.removeAttr('aria-hidden hidden');\n\t\t\t\t}\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('span'))\n\t\t\t.addClass('error')\n\t\t\t.text(mesg)\n\t\t\t.appendTo($wrapper);\n\t\tjQuery(document.createElement('code'))\n\t\t\t.text(source)\n\t\t\t.appendTo($source);\n\t\t$source\n\t\t\t.addClass('error-source')\n\t\t\t.attr({\n\t\t\t\t'aria-hidden' : true,\n\t\t\t\thidden        : 'hidden'\n\t\t\t})\n\t\t\t.appendTo($wrapper);\n\t\tif (stack) {\n\t\t\tconst lines = stack.split('\\n');\n\t\t\tfor (const ll of lines) {\n\t\t\t\tconst div = document.createElement('div');\n\t\t\t\tdiv.append(ll.replace(/file:.*\\//, '<path>/'));\n\t\t\t\t$source.append(div);\n\t\t\t}\n\t\t}\n\t\t$wrapper\n\t\t\t.addClass('error-view')\n\t\t\t.appendTo(place);\n\n\t\tconsole.warn(`${mesg}\\n\\t${source.replace(/\\n/g, '\\n\\t')}`);\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the simple string representation of the passed value or, if there is none,\n\t\tthe passed default value.\n\t*/\n\tfunction toStringOrDefault(value, defValue) {\n\t\tconst tSOD = toStringOrDefault;\n\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\t// TODO: Perhaps NaN should be printed instead?\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\tif (value === null) {\n\t\t\t\treturn defValue;\n\t\t\t}\n\t\t\telse if (Array.isArray(value)) {\n\t\t\t\treturn value.map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Set) {\n\t\t\t\treturn [...value].map(val => tSOD(val, defValue)).join(', ');\n\t\t\t}\n\t\t\telse if (value instanceof Map) {\n\t\t\t\tconst result = [...value].map(([key, val]) => `${tSOD(key, defValue)} \\u2192 ${tSOD(val, defValue)}`);\n\t\t\t\treturn `{\\u202F${result.join(', ')}\\u202F}`;\n\t\t\t}\n\t\t\telse if (value instanceof Date) {\n\t\t\t\treturn value.toLocaleString();\n\t\t\t}\n\t\t\telse if (typeof value.toString === 'function') {\n\t\t\t\treturn value.toString();\n\t\t\t}\n\t\t\treturn Object.prototype.toString.call(value);\n\n\t\tcase 'function':\n\t\tcase 'undefined':\n\t\t\treturn defValue;\n\t\t}\n\n\t\treturn String(value);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tclone             : { value : clone },\n\t\tconvertBreaks     : { value : convertBreaks },\n\t\tsafeActiveElement : { value : safeActiveElement },\n\t\tsetDisplayTitle   : { value : setDisplayTitle },\n\t\tsetPageElement    : { value : setPageElement },\n\t\tthrowError        : { value : throwError },\n\t\ttoStringOrDefault : { value : toStringOrDefault }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/jquery-plugins.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Wikifier, errorPrologRegExp, safeActiveElement */\n\n/*\n\tWAI-ARIA methods plugin.\n\n\t`<jQuery>.ariaClick([options,] handler)`\n\t    Makes the target element(s) WAI-ARIA compatible clickables.\n\n\t`<jQuery>.ariaDisabled(state)`\n\t    Changes the disabled state of the target WAI-ARIA-compatible clickable element(s).\n\n\t`<jQuery>.ariaIsDisabled()`\n\t    Checks the disabled status of the target WAI-ARIA-compatible clickable element(s).\n*/\n(() => {\n\t'use strict';\n\n\t/*\n\t\tEvent handler & utility functions.\n\n\t\tNOTE: Do not replace the anonymous functions herein with arrow functions.\n\t*/\n\tfunction onKeypressFn(ev) {\n\t\t// 13 is Enter/Return, 32 is Space.\n\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\tev.preventDefault();\n\n\t\t\t// To allow delegation, attempt to trigger the event on `document.activeElement`,\n\t\t\t// if possible, elsewise on `this`.\n\t\t\tjQuery(safeActiveElement() || this).trigger('click');\n\t\t}\n\t}\n\n\tfunction onClickFnWrapper(fn) {\n\t\treturn function () {\n\t\t\tconst $this = jQuery(this);\n\n\t\t\tconst dataPassage = $this.attr('data-passage');\n\t\t\tconst initialDataPassage = window && window.SugarCube && window.SugarCube.State && window.SugarCube.State.passage;\n\t\t\tconst savedYOffset = window.pageYOffset;\n\n\t\t\t// Toggle \"aria-pressed\" status, if the attribute exists.\n\t\t\tif ($this.is('[aria-pressed]')) {\n\t\t\t\t$this.attr('aria-pressed', $this.attr('aria-pressed') === 'true' ? 'false' : 'true');\n\t\t\t}\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\n\t\t\tconst doJump = function(){ window.scrollTo(0, savedYOffset); }\n\t\t\tif ( dataPassage && (window.lastDataPassageLink === dataPassage || initialDataPassage === dataPassage))\n\t\t\t\tdoJump();\n\t\t\twindow.lastDataPassageLink = dataPassage;\n\t\t};\n\t}\n\n\tfunction oneClickFnWrapper(fn) {\n\t\treturn onClickFnWrapper(function () {\n\t\t\t// Remove both event handlers (keypress & click) and the other components.\n\t\t\tjQuery(this)\n\t\t\t\t.off('.aria-clickable')\n\t\t\t\t.removeAttr('tabindex aria-controls aria-pressed')\n\t\t\t\t.not('a,button')\n\t\t\t\t.removeAttr('role')\n\t\t\t\t.end()\n\t\t\t\t.filter('button')\n\t\t\t\t.prop('disabled', true);\n\n\t\t\t// Call the true handler.\n\t\t\tfn.apply(this, arguments);\n\t\t});\n\t}\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaClick()` method.\n\t\t*/\n\t\tariaClick(options, handler) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tlet opts = options;\n\t\t\tlet fn   = handler;\n\n\t\t\tif (fn == null) { // lazy equality for null\n\t\t\t\tfn   = opts;\n\t\t\t\topts = undefined;\n\t\t\t}\n\n\t\t\topts = jQuery.extend({\n\t\t\t\tnamespace : undefined,\n\t\t\t\tone       : false,\n\t\t\t\tselector  : undefined,\n\t\t\t\tdata      : undefined,\n\t\t\t\tcontrols  : undefined,\n\t\t\t\tpressed   : undefined,\n\t\t\t\tlabel     : undefined\n\t\t\t}, opts);\n\n\t\t\tif (typeof opts.namespace !== 'string') {\n\t\t\t\topts.namespace = '';\n\t\t\t}\n\t\t\telse if (opts.namespace[0] !== '.') {\n\t\t\t\topts.namespace = `.${opts.namespace}`;\n\t\t\t}\n\n\t\t\tif (typeof opts.pressed === 'boolean') {\n\t\t\t\topts.pressed = opts.pressed ? 'true' : 'false';\n\t\t\t}\n\n\t\t\t// Set `type` to `button` to suppress \"submit\" semantics, for <button> elements.\n\t\t\tthis.filter('button').prop('type', 'button');\n\n\t\t\t// Set `role` to `button`, for non-<a>/-<button> elements.\n\t\t\tthis.not('a,button').attr('role', 'button');\n\n\t\t\t// Set `tabindex` to `0` to make them focusable (unnecessary on <button> elements, but it doesn't hurt).\n\t\t\tthis.attr('tabindex', 0);\n\n\t\t\t// Set `aria-controls`.\n\t\t\tif (opts.controls != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-controls', opts.controls);\n\t\t\t}\n\n\t\t\t// Set `aria-pressed`.\n\t\t\tif (opts.pressed != null) { // lazy equality for null\n\t\t\t\tthis.attr('aria-pressed', opts.pressed);\n\t\t\t}\n\n\t\t\t// Set `aria-label` and `title`.\n\t\t\tif (opts.label != null) { // lazy equality for null\n\t\t\t\tthis.attr({\n\t\t\t\t\t'aria-label' : opts.label,\n\t\t\t\t\ttitle        : opts.label\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Set the keypress handlers, for non-<button> elements.\n\t\t\t// NOTE: For the single-use case, the click handler will also remove this handler.\n\t\t\tthis.not('button').on(\n\t\t\t\t`keypress.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\tonKeypressFn\n\t\t\t);\n\n\t\t\t// Set the click handlers.\n\t\t\t// NOTE: To ensure both handlers are properly removed, `one()` must not be used here.\n\t\t\tthis.on(\n\t\t\t\t`click.aria-clickable${opts.namespace}`,\n\t\t\t\topts.selector,\n\t\t\t\topts.data,\n\t\t\t\topts.one ? oneClickFnWrapper(fn) : onClickFnWrapper(fn)\n\t\t\t);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaDisabled()` method.\n\t\t*/\n\t\tariaDisabled(disable) {\n\t\t\t// Bail out if there are no target element(s) or parameters.\n\t\t\tif (this.length === 0 || arguments.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE: We use `<jQuery>.each()` callbacks to invoke the `<Element>.setAttribute()`\n\t\t\t\tmethods in the following because the `<jQuery>.attr()` method does not allow you\n\t\t\t\tto set a content attribute without a value, which is recommended for boolean\n\t\t\t\tcontent attributes by the HTML specification.\n\t\t\t*/\n\n\t\t\tconst $nonDisableable = this.not('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\t\t\tconst $disableable    = this.filter('button,fieldset,input,menuitem,optgroup,option,select,textarea');\n\n\t\t\tif (disable) {\n\t\t\t\t// Add boolean content attribute `disabled` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.setAttribute('disabled', '');\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `true` and set non-boolean content attribute\n\t\t\t\t// `aria-disabled` to `'true'`, for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = true;\n\t\t\t\t\tthis.setAttribute('aria-disabled', 'true');\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Remove content attributes `disabled` and `aria-disabled`, for non-disableable elements.\n\t\t\t\t$nonDisableable.each(function () {\n\t\t\t\t\tthis.removeAttribute('disabled');\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\n\t\t\t\t// Set IDL attribute `disabled` to `false` and remove content attribute `aria-disabled`,\n\t\t\t\t// for disableable elements.\n\t\t\t\t$disableable.each(function () {\n\t\t\t\t\tthis.disabled = false;\n\t\t\t\t\tthis.removeAttribute('aria-disabled');\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with an `ariaIsDisabled()` method.\n\t\t*/\n\t\tariaIsDisabled() {\n\t\t\t// Check content attribute `disabled`.\n\t\t\t//\n\t\t\t// NOTE: We simply check the `disabled` content attribute for all elements\n\t\t\t// since we have to check it for non-disableable elements and it may also\n\t\t\t// be used for disableable elements since their `disabled` IDL attribute\n\t\t\t// is required to reflect the status of their `disabled` content attribute,\n\t\t\t// and vice versa, by the HTML specification.\n\t\t\t// return this.toArray().some(el => el.hasAttribute('disabled'));\n\t\t\treturn this.is('[disabled]');\n\t\t}\n\t});\n})();\n\n/*\n\tWikifier methods plugin.\n\n\t`jQuery.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s), as directed by the given options.\n\n\t`jQuery.wiki(sources…)`\n\t    Wikifies the given content source(s).\n\n\t`<jQuery>.wikiWithOptions(options, sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s), as directed by the given options.\n\n\t`<jQuery>.wiki(sources…)`\n\t    Wikifies the given content source(s) and appends the result to the target\n\t    element(s).\n*/\n(() => {\n\t'use strict';\n\n\tjQuery.extend({\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out, if there are no content sources.\n\t\t\tif (sources.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's static methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\tthis.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n\n\tjQuery.fn.extend({\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wikiWithOptions()` method.\n\t\t*/\n\t\twikiWithOptions(options, ...sources) {\n\t\t\t// Bail out if there are no target element(s) or content sources.\n\t\t\tif (this.length === 0 || sources.length === 0) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t// Wikify the content sources into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tsources.forEach(content => new Wikifier(frag, content, options));\n\n\t\t\t// Append the fragment to the target element(s).\n\t\t\tthis.append(frag);\n\n\t\t\t// Return `this` for further chaining.\n\t\t\treturn this;\n\t\t},\n\n\t\t/*\n\t\t\tExtend jQuery's chainable methods with a `wiki()` method.\n\t\t*/\n\t\twiki(...sources) {\n\t\t\treturn this.wikiWithOptions(undefined, ...sources);\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tlib/util.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, Scripting */\n\nvar Util = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tType Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value yielded by `typeof` (for primitives), the `@@toStringTag`\n\t\tinternal property (for objects), and `'null'` for `null`.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot for objects.\n\t*/\n\tfunction utilGetType(obj) {\n\t\tif (obj === null) { return 'null'; }\n\n\t\tconst baseType = typeof obj;\n\t\treturn baseType === 'object'\n\t\t\t? Object.prototype.toString.call(obj).slice(8, -1)\n\t\t\t: baseType;\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a boolean or one of the strings \"true\"\n\t\tor \"false\".\n\t*/\n\tfunction utilIsBoolean(obj) {\n\t\treturn typeof obj === 'boolean' || typeof obj === 'string' && (obj === 'true' || obj === 'false');\n\t}\n\n\t/*\n\t\tReturns whether the passed value is iterable.\n\t*/\n\tfunction utilIsIterable(obj) {\n\t\treturn obj != null && typeof obj[Symbol.iterator] === 'function'; // lazy equality for null\n\t}\n\n\t/*\n\t\tReturns whether the passed value is a finite number or a numeric string which\n\t\tyields a finite number when parsed.\n\t*/\n\tfunction utilIsNumeric(obj) {\n\t\tlet num;\n\n\t\tswitch (typeof obj) {\n\t\tcase 'number':\n\t\t\tnum = obj;\n\t\t\tbreak;\n\n\t\tcase 'string':\n\t\t\tnum = Number(obj);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !Number.isNaN(num) && Number.isFinite(num);\n\t}\n\n\t/*\n\t\tReturns whether the passed values pass a SameValueZero comparison.\n\n\t\tSEE: http://ecma-international.org/ecma-262/8.0/#sec-samevaluezero\n\t*/\n\tfunction utilSameValueZero(a, b) {\n\t\t/*\n\t\t\tNOTE: This comparison could also be implemented thus:\n\n\t\t\t\t```\n\t\t\t\ta === b ||\n\t\t\t\ttypeof a === 'number' && typeof b === 'number' &&\n\t\t\t\tNumber.isNaN(a) && Number.isNaN(b)\n\t\t\t\t```\n\n\t\t\tThat's needlessly verbose, however, as `NaN` is the only value in\n\t\t\tthe language which is not reflexive.\n\t\t*/\n\t\treturn a === b || a !== a && b !== b;\n\t}\n\n\t/*\n\t\tReturns a pseudo-enumeration created from the given Array, Map, Set, or generic object.\n\t*/\n\tfunction utilToEnum(obj) {\n\t\tconst pEnum = Object.create(null);\n\n\t\tif (obj instanceof Array) {\n\t\t\tobj.forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Set) {\n\t\t\t// NOTE: Use `<Array>.forEach()` here rather than `<Set>.forEach()`\n\t\t\t// as the latter does not provide the indices we require.\n\t\t\tArray.from(obj).forEach((val, i) => pEnum[String(val)] = i);\n\t\t}\n\t\telse if (obj instanceof Map) {\n\t\t\tobj.forEach((val, key) => pEnum[String(key)] = val);\n\t\t}\n\t\telse if (\n\t\t\t   typeof obj === 'object'\n\t\t\t&& obj !== null\n\t\t\t&& Object.getPrototypeOf(obj) === Object.prototype\n\t\t) {\n\t\t\tObject.assign(pEnum, obj);\n\t\t}\n\t\telse {\n\t\t\tthrow new TypeError('Util.toEnum obj parameter must be an Array, Map, Set, or generic object');\n\t\t}\n\n\t\treturn Object.freeze(pEnum);\n\t}\n\n\t/*\n\t\tReturns the value of the `@@toStringTag` property of the given object.\n\n\t\tNOTE: In ≤ES5, returns the value of the `[[Class]]` internal slot.\n\t*/\n\tfunction utilToStringTag(obj) {\n\t\treturn Object.prototype.toString.call(obj).slice(8, -1);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tString Encoding Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a trimmed and encoded slug of the passed string that should be safe\n\t\tfor use as a DOM ID or class name.\n\n\t\tNOTE: The range of illegal characters consists of: C0 controls, space, exclamation,\n\t\tdouble quote, number, dollar, percent, ampersand, single quote, left paren, right\n\t\tparen, asterisk, plus, comma, hyphen, period, forward slash, colon, semi-colon,\n\t\tless-than, equals, greater-than, question, at, left bracket, backslash, right\n\t\tbracket, caret, backquote/grave, left brace, pipe/vertical-bar, right brace, tilde,\n\t\tdelete, C1 controls.\n\t*/\n\tconst _illegalSlugCharsRe = /[\\x00-\\x20!-/:-@[-^`{-\\x9f]+/g; // eslint-disable-line no-control-regex\n\t/* legacy */\n\tconst _isInvalidSlugRe = /^-*$/; // Matches the empty string or one comprised solely of hyphens.\n\t/* /legacy */\n\n\tfunction utilSlugify(str) {\n\t\tconst base = String(str).trim();\n\n\t\t/* legacy */\n\t\tconst _legacy = base\n\t\t\t.replace(/[^\\w\\s\\u2013\\u2014-]+/g, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-')\n\t\t\t.toLocaleLowerCase();\n\n\t\tif (!_isInvalidSlugRe.test(_legacy)) {\n\t\t\treturn _legacy;\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn base\n\t\t\t.replace(_illegalSlugCharsRe, '')\n\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-');\n\n\t\t// For v3.\n\t\t// return base.replace(_illegalSlugCharsRe, '-');\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Escapes the five primary HTML special characters, the backquote,\n\t\tand SugarCube markup metacharacters.\n\t*/\n\tconst _markupCharsRe    = /[!\"#$&'*\\-/<=>?@[\\\\\\]^_`{|}~]/g;\n\tconst _hasMarkupCharsRe = new RegExp(_markupCharsRe.source); // to drop the global flag\n\tconst _markupCharsMap   = utilToEnum({\n\t\t/* eslint-disable quote-props */\n\t\t'!'  : '&#33;',\n\t\t'\"'  : '&quot;',\n\t\t'#'  : '&#35;',\n\t\t'$'  : '&#36;',\n\t\t'&'  : '&amp;',\n\t\t\"'\"  : '&#39;',\n\t\t'*'  : '&#42;',\n\t\t'-'  : '&#45;',\n\t\t'/'  : '&#47;',\n\t\t'<'  : '&lt;',\n\t\t'='  : '&#61;',\n\t\t'>'  : '&gt;',\n\t\t'?'  : '&#63;',\n\t\t'@'  : '&#64;',\n\t\t'['  : '&#91;',\n\t\t'\\\\' : '&#92;',\n\t\t']'  : '&#93;',\n\t\t'^'  : '&#94;',\n\t\t'_'  : '&#95;',\n\t\t'`'  : '&#96;',\n\t\t'{'  : '&#123;',\n\t\t'|'  : '&#124;',\n\t\t'}'  : '&#125;',\n\t\t'~'  : '&#126;'\n\t\t/* eslint-enable quote-props */\n\t});\n\n\tfunction utilEscapeMarkup(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasMarkupCharsRe.test(val)\n\t\t\t? val.replace(_markupCharsRe, ch => _markupCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an entity encoded version of the passed string.\n\n\t\tNOTE: Only escapes the five primary special characters and the backquote.\n\t*/\n\tconst _htmlCharsRe    = /[&<>\"'`]/g;\n\tconst _hasHtmlCharsRe = new RegExp(_htmlCharsRe.source); // to drop the global flag\n\tconst _htmlCharsMap   = utilToEnum({\n\t\t'&' : '&amp;',\n\t\t'<' : '&lt;',\n\t\t'>' : '&gt;',\n\t\t'\"' : '&quot;',\n\t\t\"'\" : '&#39;',\n\t\t'`' : '&#96;'\n\t});\n\n\tfunction utilEscape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasHtmlCharsRe.test(val)\n\t\t\t? val.replace(_htmlCharsRe, ch => _htmlCharsMap[ch])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns a decoded version of the passed entity encoded string.\n\n\t\tNOTE: The extended replacement set here, in contrast to `utilEscape()`,\n\t\tis required due to observed stupidity from various sources.\n\t*/\n\tconst _escapedHtmlRe    = /&(?:amp|#38|#x26|lt|#60|#x3c|gt|#62|#x3e|quot|#34|#x22|apos|#39|#x27|#96|#x60);/gi;\n\tconst _hasEscapedHtmlRe = new RegExp(_escapedHtmlRe.source, 'i'); // to drop the global flag\n\tconst _escapedHtmlMap   = utilToEnum({\n\t\t'&amp;'  : '&', // ampersand (HTML character entity, XML predefined entity)\n\t\t'&#38;'  : '&', // ampersand (decimal numeric character reference)\n\t\t'&#x26;' : '&', // ampersand (hexadecimal numeric character reference)\n\t\t'&lt;'   : '<', // less-than (HTML character entity, XML predefined entity)\n\t\t'&#60;'  : '<', // less-than (decimal numeric character reference)\n\t\t'&#x3c;' : '<', // less-than (hexadecimal numeric character reference)\n\t\t'&gt;'   : '>', // greater-than (HTML character entity, XML predefined entity)\n\t\t'&#62;'  : '>', // greater-than (decimal numeric character reference)\n\t\t'&#x3e;' : '>', // greater-than (hexadecimal numeric character reference)\n\t\t'&quot;' : '\"', // double quote (HTML character entity, XML predefined entity)\n\t\t'&#34;'  : '\"', // double quote (decimal numeric character reference)\n\t\t'&#x22;' : '\"', // double quote (hexadecimal numeric character reference)\n\t\t'&apos;' : \"'\", // apostrophe (XML predefined entity)\n\t\t'&#39;'  : \"'\", // apostrophe (decimal numeric character reference)\n\t\t'&#x27;' : \"'\", // apostrophe (hexadecimal numeric character reference)\n\t\t'&#96;'  : '`', // backquote (decimal numeric character reference)\n\t\t'&#x60;' : '`'  // backquote (hexadecimal numeric character reference)\n\t});\n\n\tfunction utilUnescape(str) {\n\t\tif (str == null) { // lazy equality for null\n\t\t\treturn '';\n\t\t}\n\n\t\tconst val = String(str);\n\t\treturn val && _hasEscapedHtmlRe.test(val)\n\t\t\t? val.replace(_escapedHtmlRe, entity => _escapedHtmlMap[entity.toLowerCase()])\n\t\t\t: val;\n\t}\n\n\t/*\n\t\tReturns an object (`{ char, start, end }`) containing the Unicode character at\n\t\tposition `pos`, its starting position, and its ending position—surrogate pairs\n\t\tare properly handled.  If `pos` is out-of-bounds, returns an object containing\n\t\tthe empty string and start/end positions of `-1`.\n\n\t\tThis function is necessary because JavaScript strings are sequences of UTF-16\n\t\tcode units, so surrogate pairs are exposed and thus must be handled.  While the\n\t\tES6/2015 standard does improve the situation somewhat, it does not alleviate\n\t\tthe need for this function.\n\n\t\tNOTE: Returns the individual code units of invalid surrogate pairs as-is.\n\n\t\tIDEA: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n\t*/\n\tfunction utilCharAndPosAt(text, position) {\n\t\tconst str  = String(text);\n\t\tconst pos  = Math.trunc(position);\n\t\tconst code = str.charCodeAt(pos);\n\n\t\t// Given position was out-of-bounds.\n\t\tif (Number.isNaN(code)) {\n\t\t\treturn { char : '', start : -1, end : -1 };\n\t\t}\n\n\t\tconst retval = {\n\t\t\tchar  : str.charAt(pos),\n\t\t\tstart : pos,\n\t\t\tend   : pos\n\t\t};\n\n\t\t// Code unit is not a UTF-16 surrogate.\n\t\tif (code < 0xD800 || code > 0xDFFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a high surrogate (D800–DBFF).\n\t\tif (code >= 0xD800 && code <= 0xDBFF) {\n\t\t\tconst nextPos = pos + 1;\n\n\t\t\t// End of string.\n\t\t\tif (nextPos >= str.length) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tconst nextCode = str.charCodeAt(nextPos);\n\n\t\t\t// Next code unit is not a low surrogate (DC00–DFFF).\n\t\t\tif (nextCode < 0xDC00 || nextCode > 0xDFFF) {\n\t\t\t\treturn retval;\n\t\t\t}\n\n\t\t\tretval.char = retval.char + str.charAt(nextPos);\n\t\t\tretval.end = nextPos;\n\t\t\treturn retval;\n\t\t}\n\n\t\t// Code unit is a low surrogate (DC00–DFFF) in the first position.\n\t\tif (pos === 0) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tconst prevPos  = pos - 1;\n\t\tconst prevCode = str.charCodeAt(prevPos);\n\n\t\t// Previous code unit is not a high surrogate (D800–DBFF).\n\t\tif (prevCode < 0xD800 || prevCode > 0xDBFF) {\n\t\t\treturn retval;\n\t\t}\n\n\t\tretval.char = str.charAt(prevPos) + retval.char;\n\t\tretval.start = prevPos;\n\t\treturn retval;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTime Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of milliseconds elapsed since a reference epoch.\n\n\t\tNOTE: Use the Performance API, if available, elsewise use Date as a\n\t\tfailover.  The Performance API is preferred for its monotonic clock—\n\t\tmeaning, it's not subject to the vagaries of timezone changes and leap\n\t\tperiods, as is Date.\n\t*/\n\tconst _nowSource = Has.performance ? performance : Date;\n\n\tfunction utilNow() {\n\t\treturn _nowSource.now();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tConversion Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the number of miliseconds represented by the passed CSS time string.\n\t*/\n\tconst _cssTimeRe = /^([+-]?(?:\\d*\\.)?\\d+)([Mm]?[Ss])$/;\n\n\tfunction utilFromCssTime(cssTime) {\n\t\tconst match = _cssTimeRe.exec(String(cssTime));\n\n\t\tif (match === null) {\n\t\t\tthrow new SyntaxError(`invalid time value syntax: \"${cssTime}\"`);\n\t\t}\n\n\t\tlet msec = Number(match[1]);\n\n\t\tif (match[2].length === 1) {\n\t\t\tmsec *= 1000;\n\t\t}\n\n\t\tif (Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tthrow new RangeError(`invalid time value: \"${cssTime}\"`);\n\t\t}\n\n\t\treturn msec;\n\t}\n\n\t/*\n\t\tReturns the CSS time string represented by the passed number of milliseconds.\n\t*/\n\tfunction utilToCssTime(msec) {\n\t\tif (typeof msec !== 'number' || Number.isNaN(msec) || !Number.isFinite(msec)) {\n\t\t\tlet what;\n\n\t\t\tswitch (typeof msec) {\n\t\t\tcase 'string':\n\t\t\t\twhat = `\"${msec}\"`;\n\t\t\t\tbreak;\n\n\t\t\tcase 'number':\n\t\t\t\twhat = String(msec);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\twhat = utilToStringTag(msec);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow new Error(`invalid milliseconds: ${what}`);\n\t\t}\n\n\t\treturn `${msec}ms`;\n\t}\n\n\t/*\n\t\tReturns the DOM property name represented by the passed CSS property name.\n\t*/\n\tfunction utilFromCssProperty(cssName) {\n\t\tif (!cssName.includes('-')) {\n\t\t\tswitch (cssName) {\n\t\t\tcase 'bgcolor': return 'backgroundColor';\n\t\t\tcase 'float':   return 'cssFloat';\n\t\t\tdefault:        return cssName;\n\t\t\t}\n\t\t}\n\n\t\t// Strip the leading hyphen from the `-ms-` vendor prefix, so it stays lowercased.\n\t\tconst normalized = cssName.slice(0, 4) === '-ms-' ? cssName.slice(1) : cssName;\n\n\t\treturn normalized\n\t\t\t.split('-')\n\t\t\t.map((part, i) => i === 0 ? part : part.toUpperFirst())\n\t\t\t.join('');\n\t}\n\n\t/*\n\t\tReturns an object containing the component properties parsed from the passed URL.\n\t*/\n\tfunction utilParseUrl(url) {\n\t\tconst el       = document.createElement('a');\n\t\tconst queryObj = Object.create(null);\n\n\t\t// Let the `<a>` element parse the URL.\n\t\tel.href = url;\n\n\t\t// Populate the `queryObj` object with the query string attributes.\n\t\tif (el.search) {\n\t\t\tel.search\n\t\t\t\t.replace(/^\\?/, '')\n\t\t\t\t.splitOrEmpty(/(?:&(?:amp;)?|;)/)\n\t\t\t\t.forEach(query => {\n\t\t\t\t\tconst [key, value] = query.split('=');\n\t\t\t\t\tqueryObj[key] = value;\n\t\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tCaveats by browser:\n\t\t\t\tEdge and Internet Explorer (≥8) do not support authentication\n\t\t\t\tinformation within a URL at all and will throw a security exception\n\t\t\t\ton *any* property access if it's included.\n\n\t\t\t\tInternet Explorer does not include the leading forward slash on\n\t\t\t\t`pathname` when required.\n\n\t\t\t\tOpera (Presto) strips the authentication information from `href`\n\t\t\t\tand does not supply `username` or `password`.\n\n\t\t\t\tSafari (ca. v5.1.x) does not supply `username` or `password` and\n\t\t\t\tpeforms URI decoding on `pathname`.\n\t\t*/\n\n\t\t// Patch for IE not including the leading slash on `pathname` when required.\n\t\tconst pathname = el.host && el.pathname[0] !== '/' ? `/${el.pathname}` : el.pathname;\n\n\t\treturn {\n\t\t\t// The full URL that was originally parsed.\n\t\t\thref : el.href,\n\n\t\t\t// The request protocol, lowercased.\n\t\t\tprotocol : el.protocol,\n\n\t\t\t// // The full authentication information.\n\t\t\t// auth : el.username || el.password // eslint-disable-line no-nested-ternary\n\t\t\t// \t? `${el.username}:${el.password}`\n\t\t\t// \t: typeof el.username === 'string' ? '' : undefined,\n\t\t\t//\n\t\t\t// // The username portion of the auth info.\n\t\t\t// username : el.username,\n\t\t\t//\n\t\t\t// // The password portion of the auth info.\n\t\t\t// password : el.password,\n\n\t\t\t// The full host information, including port number, lowercased.\n\t\t\thost : el.host,\n\n\t\t\t// The hostname portion of the host info, lowercased.\n\t\t\thostname : el.hostname,\n\n\t\t\t// The port number portion of the host info.\n\t\t\tport : el.port,\n\n\t\t\t// The full path information, including query info.\n\t\t\tpath : `${pathname}${el.search}`,\n\n\t\t\t// The pathname portion of the path info.\n\t\t\tpathname,\n\n\t\t\t// The query string portion of the path info, including the leading question mark.\n\t\t\tquery  : el.search,\n\t\t\tsearch : el.search,\n\n\t\t\t// The attributes portion of the query string, parsed into an object.\n\t\t\tqueries  : queryObj,\n\t\t\tsearches : queryObj,\n\n\t\t\t// The fragment string, including the leading hash/pound sign.\n\t\t\thash : el.hash\n\t\t};\n\t}\n\n\t/*\n\t\tReturns a new exception based on the given exception.\n\n\t\tNOTE: Mostly useful for making a standard JavaScript exception type copy\n\t\tof a host exception type—e.g. `DOMException` → `Error`.\n\t*/\n\tfunction utilNewExceptionFrom(original, exceptionType, override) {\n\t\tif (typeof original !== 'object' || original === null) {\n\t\t\tthrow new Error('Util.newExceptionFrom original parameter must be an object');\n\t\t}\n\t\tif (typeof exceptionType !== 'function') {\n\t\t\tthrow new Error('Util.newExceptionFrom exceptionType parameter must be an error type constructor');\n\t\t}\n\n\t\tconst ex = new exceptionType(original.message); // eslint-disable-line new-cap\n\n\t\tif (typeof original.name !== 'undefined') {\n\t\t\tex.name = original.name;\n\t\t}\n\t\tif (typeof original.code !== 'undefined') {\n\t\t\tex.code = original.code;\n\t\t}\n\t\tif (typeof original.columnNumber !== 'undefined') {\n\t\t\tex.columnNumber = original.columnNumber;\n\t\t}\n\t\tif (typeof original.description !== 'undefined') {\n\t\t\tex.description = original.description;\n\t\t}\n\t\tif (typeof original.fileName !== 'undefined') {\n\t\t\tex.fileName = original.fileName;\n\t\t}\n\t\tif (typeof original.lineNumber !== 'undefined') {\n\t\t\tex.lineNumber = original.lineNumber;\n\t\t}\n\t\tif (typeof original.number !== 'undefined') {\n\t\t\tex.number = original.number;\n\t\t}\n\t\tif (typeof original.stack !== 'undefined') {\n\t\t\tex.stack = original.stack;\n\t\t}\n\n\t\tconst overrideType = typeof override;\n\n\t\tif (overrideType !== 'undefined') {\n\t\t\tif (overrideType === 'object' && override !== null) {\n\t\t\t\tObject.assign(ex, override);\n\t\t\t}\n\t\t\telse if (overrideType === 'string') {\n\t\t\t\tex.message = override;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('Util.newExceptionFrom override parameter must be an object or string');\n\t\t\t}\n\t\t}\n\n\t\treturn ex;\n\t}\n\n\t/*\n\t\tReturns a sanitized version of the passed `KeyboardEvent.key` value from\n\t\tprevious incarnations of the specification that should better reflect the\n\t\tcurrent incarnation.\n\t*/\n\tconst utilScrubEventKey = (() => {\n\t\tlet separatorKey;\n\t\tlet decimalKey;\n\n\t\t// Attempt to determine the player's 'Separator' and 'Decimal' key values\n\t\t// based on their current locale.\n\t\tif (typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function') {\n\t\t\tconst match = new Intl.NumberFormat().format(111111.5).match(/(\\D*)\\d+(\\D*)/);\n\n\t\t\tif (match) {\n\t\t\t\tseparatorKey = match[1];\n\t\t\t\tdecimalKey   = match[2];\n\t\t\t}\n\t\t}\n\n\t\t// Failover to US-centric values, if using `Intl.NumberFormat` failed.\n\t\tif (!separatorKey && !decimalKey) {\n\t\t\tseparatorKey = ',';\n\t\t\tdecimalKey   = '.';\n\t\t}\n\n\t\t// Maps older `KeyboardEvent.key` values to more current/correct ones.\n\t\tfunction utilScrubEventKey(key) {\n\t\t\tswitch (key) {\n\t\t\t// case 'OS':                 return 'Meta'; // Unreliable.\n\t\t\tcase 'Scroll':             return 'ScrollLock';\n\t\t\tcase 'Spacebar':           return '\\x20';\n\t\t\tcase 'Left':               return 'ArrowLeft';\n\t\t\tcase 'Right':              return 'ArrowRight';\n\t\t\tcase 'Up':                 return 'ArrowUp';\n\t\t\tcase 'Down':               return 'ArrowDown';\n\t\t\tcase 'Del':                return 'Delete';\n\t\t\tcase 'Crsel':              return 'CrSel';\n\t\t\tcase 'Exsel':              return 'ExSel';\n\t\t\tcase 'Esc':                return 'Escape';\n\t\t\tcase 'Apps':               return 'ContextMenu';\n\t\t\tcase 'Nonconvert':         return 'NonConvert';\n\t\t\tcase 'MediaNextTrack':     return 'MediaTrackNext';\n\t\t\tcase 'MediaPreviousTrack': return 'MediaTrackPrevious';\n\t\t\tcase 'VolumeUp':           return 'AudioVolumeUp';\n\t\t\tcase 'VolumeDown':         return 'AudioVolumeDown';\n\t\t\tcase 'VolumeMute':         return 'AudioVolumeMute';\n\t\t\tcase 'Zoom':               return 'ZoomToggle';\n\t\t\tcase 'SelectMedia':        /* see below */\n\t\t\tcase 'MediaSelect':        return 'LaunchMediaPlayer';\n\t\t\tcase 'Add':                return '+';\n\t\t\tcase 'Divide':             return '/';\n\t\t\tcase 'Multiply':           return '*';\n\t\t\tcase 'Subtract':           return '-';\n\t\t\tcase 'Decimal':            return decimalKey;\n\t\t\tcase 'Separator':          return separatorKey;\n\t\t\t}\n\n\t\t\treturn key;\n\t\t}\n\n\t\treturn utilScrubEventKey;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tType Functions.\n\t\t*/\n\t\tgetType       : { value : utilGetType },\n\t\tisBoolean     : { value : utilIsBoolean },\n\t\tisIterable    : { value : utilIsIterable },\n\t\tisNumeric     : { value : utilIsNumeric },\n\t\tsameValueZero : { value : utilSameValueZero },\n\t\ttoEnum        : { value : utilToEnum },\n\t\ttoStringTag   : { value : utilToStringTag },\n\n\t\t/*\n\t\t\tString Encoding Functions.\n\t\t*/\n\t\tslugify      : { value : utilSlugify },\n\t\tescapeMarkup : { value : utilEscapeMarkup },\n\t\tescape       : { value : utilEscape },\n\t\tunescape     : { value : utilUnescape },\n\t\tcharAndPosAt : { value : utilCharAndPosAt },\n\n\t\t/*\n\t\t\tTime Functions.\n\t\t*/\n\t\tnow : { value : utilNow },\n\n\t\t/*\n\t\t\tConversion Functions.\n\t\t*/\n\t\tfromCssTime      : { value : utilFromCssTime },\n\t\ttoCssTime        : { value : utilToCssTime },\n\t\tfromCssProperty  : { value : utilFromCssProperty },\n\t\tparseUrl         : { value : utilParseUrl },\n\t\tnewExceptionFrom : { value : utilNewExceptionFrom },\n\t\tscrubEventKey    : { value : utilScrubEventKey },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\trandom         : { value : Math.random },\n\t\tentityEncode   : { value : utilEscape },\n\t\tentityDecode   : { value : utilUnescape },\n\t\tevalExpression : { value : (...args) => Scripting.evalJavaScript(...args) }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) }  // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/simplestore.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar SimpleStore = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// In-order list of database adapters.\n\tconst _adapters = [];\n\n\t// The initialized adapter.\n\tlet _initialized = null;\n\n\n\t/*******************************************************************************************************************\n\t\tSimpleStore Functions.\n\t*******************************************************************************************************************/\n\tfunction storeCreate(storageId, persistent) {\n\t\tif (_initialized) {\n\t\t\treturn _initialized.create(storageId, persistent);\n\t\t}\n\n\t\t// Return the first adapter which successfully initializes, elsewise throw an exception.\n\t\tfor (let i = 0; i < _adapters.length; ++i) {\n\t\t\tif (_adapters[i].init(storageId, persistent)) {\n\t\t\t\t_initialized = _adapters[i];\n\t\t\t\treturn _initialized.create(storageId, persistent);\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error('no valid storage adapters found');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tAdapters List.\n\n\t\t\tTODO: This should probably have a getter, rather than being exported directly.\n\t\t*/\n\t\tadapters : { value : _adapters },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tcreate : { value : storeCreate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/FCHost.Storage.js\n\n\tCopyright © 2013–2019 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_FCHostStorageAdapter Class.\n        Note that FCHost is only intended for a single document, so we ignore both prefixing and storageID\n\t*******************************************************************************************************************/\n\tclass _FCHostStorageAdapter {\n\t\tconstructor(persistent) {\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.FCHostPersistent;\n\t\t\t\tname   = 'FCHostPersistent';\n\t\t\t}\n\t\t\telse {\n\t\t\t    engine = window.FCHostSession;\n\t\t\t\tname   = 'FCHostSession';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n                \n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this._engine.size();\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\treturn this._engine.keys();\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn this._engine.has(key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.get(key);\n\n\t\t\treturn value == null ? null : _FCHostStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.set(key, _FCHostStorageAdapter._serialize(value));\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.remove(key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tthis._engine.clear();\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(str);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// FCHost feature test.\n\t\tfunction hasFCHostStorage() {\n\t\t\ttry {\n\t\t\t    if (typeof window.FCHostPersistent !== 'undefined')\n\t\t\t        return true;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t_ok = hasFCHostStorage();\n\t\t\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _FCHostStorageAdapter(persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/webstorage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_WebStorageAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _WebStorageAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}.`;\n\t\t\tlet engine = null;\n\t\t\tlet name   = null;\n\n\t\t\tif (persistent) {\n\t\t\t\tengine = window.localStorage;\n\t\t\t\tname   = 'localStorage';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tengine = window.sessionStorage;\n\t\t\t\tname   = 'sessionStorage';\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_engine : {\n\t\t\t\t\tvalue : engine\n\t\t\t\t},\n\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : name\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\t/*\n\t\t\t\tNOTE: DO NOT do something like `return this._engine.length;` here,\n\t\t\t\tas that will return the length of the entire store, rather than\n\t\t\t\tjust our prefixed keys.\n\t\t\t*/\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tconst keys = [];\n\n\t\t\tfor (let i = 0; i < this._engine.length; ++i) {\n\t\t\t\tconst key = this._engine.key(i);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// // FIXME: This method should probably check for the key, rather than comparing its value.\n\t\t\t// return this._engine.getItem(this._prefix + key) != null; // lazy equality for null\n\n\t\t\treturn this._engine.hasOwnProperty(this._prefix + key);\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = this._engine.getItem(this._prefix + key);\n\n\t\t\treturn value == null ? null : _WebStorageAdapter._deserialize(value); // lazy equality for null\n\t\t}\n\n\t\tset(key, value, compression = true) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tthis._engine.setItem(this._prefix + key,\n\t\t\t\t\t_WebStorageAdapter._serialize(value, this.persistent && compression));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t/*\n\t\t\t\t\tIf the exception is a quota exceeded error, massage it into something\n\t\t\t\t\ta bit nicer for the player.\n\n\t\t\t\t\tNOTE: Ideally, we could simply do something like checking `ex.code`, but\n\t\t\t\t\tit's a non-standard property and not supported in all browsers.  Thus,\n\t\t\t\t\twe have to resort to pattern matching the name and message—the latter being\n\t\t\t\t\trequired by Opera (Presto).  I hate the parties responsible for this snafu\n\t\t\t\t\tso much.\n\t\t\t\t*/\n\t\t\t\tif (/quota.?(?:exceeded|reached)/i.test(ex.name + ex.message)) {\n\t\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `${this.name} quota exceeded`);\n\t\t\t\t}\n\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis._engine.removeItem(this._prefix + key);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// return this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _serialize(obj, compression) {\n\t\t\tif (compression) {\n\t\t\t\treturn LZString.compressToUTF16(JSON.stringify(obj));\n\t\t\t}\n\t\t\treturn JSON.stringify(obj);\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse((!str || str[0] == \"{\") ? str : LZString.decompressFromUTF16(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit() {\n\t\t// Web Storage feature test.\n\t\tfunction hasWebStorage(storeId) {\n\t\t\ttry {\n\t\t\t\tconst store = window[storeId];\n\t\t\t\tconst tid   = `_sc_${String(Date.now())}`;\n\t\t\t\tstore.setItem(tid, tid);\n\t\t\t\tconst result = store.getItem(tid) === tid;\n\t\t\t\tstore.removeItem(tid);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t\tJust to be safe, we feature test for both `localStorage` and `sessionStorage`,\n\t\t\tas you never know what browser implementation bugs you're going to run into.\n\t\t*/\n\t\t_ok = hasWebStorage('localStorage') && hasWebStorage('sessionStorage');\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _WebStorageAdapter(storageId, persistent);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/simplestore/adapters/cookie.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global SimpleStore, Util */\n\nSimpleStore.adapters.push((() => {\n\t'use strict';\n\n\t// Expiry constants.\n\tconst _MAX_EXPIRY = 'Tue, 19 Jan 2038 03:14:07 GMT'; // (new Date((Math.pow(2, 31) - 1) * 1000)).toUTCString()\n\tconst _MIN_EXPIRY = 'Thu, 01 Jan 1970 00:00:00 GMT'; // (new Date(0)).toUTCString()\n\n\t// Adapter readiness state.\n\tlet _ok = false;\n\n\n\t/*******************************************************************************************************************\n\t\t_CookieAdapter Class.\n\t*******************************************************************************************************************/\n\tclass _CookieAdapter {\n\t\tconstructor(storageId, persistent) {\n\t\t\tconst prefix = `${storageId}${persistent ? '!' : '*'}.`;\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t_prefix : {\n\t\t\t\t\tvalue : prefix\n\t\t\t\t},\n\n\t\t\t\t_prefixRe : {\n\t\t\t\t\tvalue : new RegExp(`^${RegExp.escape(prefix)}`)\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : 'cookie'\n\t\t\t\t},\n\n\t\t\t\tid : {\n\t\t\t\t\tvalue : storageId\n\t\t\t\t},\n\n\t\t\t\tpersistent : {\n\t\t\t\t\tvalue : !!persistent\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/* legacy */\n\t\tget length() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.length : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\t\t/* /legacy */\n\n\t\tsize() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.size() : Number]`); }\n\n\t\t\treturn this.keys().length;\n\t\t}\n\n\t\tkeys() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.keys() : String Array]`); }\n\n\t\t\tif (document.cookie === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\t\t\tconst keys    = [];\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (this._prefixRe.test(key)) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should omit such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\tif (value !== '') {\n\t\t\t\t\t\tkeys.push(key.replace(this._prefixRe, ''));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn keys;\n\t\t}\n\n\t\thas(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.has(key: \"${key}\") : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn _CookieAdapter._getCookie(this._prefix + key) !== null;\n\t\t}\n\n\t\tget(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.get(key: \"${key}\") : Any]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst value = _CookieAdapter._getCookie(this._prefix + key);\n\n\t\t\treturn value === null ? null : _CookieAdapter._deserialize(value);\n\t\t}\n\n\t\tset(key, value) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.set(key: \"${key}\", value: \\u2026) : Boolean]`); }\n\n\t\t\tif (typeof key !== 'string' || !key) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\t\t\t\t\t_CookieAdapter._serialize(value),\n\n\t\t\t\t\t// An undefined expiry denotes a session cookie.\n\t\t\t\t\tthis.persistent ? _MAX_EXPIRY : undefined\n\t\t\t\t);\n\n\t\t\t\tif (!this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during set');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tdelete(key) {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.delete(key: \"${key}\") : Boolean]`); }\n\n\t\t\t/*\n\t\t\t\tAttempting to delete a cookie implies setting it, so we test for its existence\n\t\t\t\tbeforehand, to avoid creating it in the event that it does not already exist.\n\t\t\t*/\n\t\t\tif (typeof key !== 'string' || !key || !this.has(key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\tthis._prefix + key,\n\n\t\t\t\t\t// Use `undefined` as the value.\n\t\t\t\t\tundefined,\n\n\t\t\t\t\t// Use the epoch as the expiry.\n\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t);\n\n\t\t\t\tif (this.has(key)) {\n\t\t\t\t\tthrow new Error('unknown validation error during delete');\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\t// Massage the cookie exception into something a bit nicer for the player.\n\t\t\t\tthrow Util.newExceptionFrom(ex, Error, `cookie error: ${ex.message}`);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tclear() {\n\t\t\tif (DEBUG) { console.log(`[<SimpleStore:${this.name}>.clear() : Boolean]`); }\n\n\t\t\tconst keys = this.keys();\n\n\t\t\tfor (let i = 0, iend = keys.length; i < iend; ++i) {\n\t\t\t\tif (DEBUG) { console.log('\\tdeleting key:', keys[i]); }\n\n\t\t\t\tthis.delete(keys[i]);\n\t\t\t}\n\n\t\t\t// this.keys().forEach(key => {\n\t\t\t// \tif (DEBUG) { console.log('\\tdeleting key:', key); }\n\t\t\t//\n\t\t\t// \tthis.delete(key);\n\t\t\t// });\n\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic _getCookie(prefixedKey) {\n\t\t\tif (!prefixedKey || document.cookie === '') {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst cookies = document.cookie.split(/;\\s*/);\n\n\t\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\t\tif (prefixedKey === key) {\n\t\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\t\tnot a serialized empty string, so we should yield `null` for such pairs.\n\t\t\t\t\t*/\n\t\t\t\t\treturn value || null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic _setCookie(prefixedKey, value, expiry) {\n\t\t\tif (!prefixedKey) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet payload = `${encodeURIComponent(prefixedKey)}=`;\n\n\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\tpayload += encodeURIComponent(value);\n\t\t\t}\n\n\t\t\tif (expiry != null) { // lazy equality for null\n\t\t\t\tpayload += `; expires=${expiry}`;\n\t\t\t}\n\n\t\t\tpayload += '; path=/';\n\t\t\tdocument.cookie = payload;\n\t\t}\n\n\t\tstatic _serialize(obj) {\n\t\t\treturn LZString.compressToBase64(JSON.stringify(obj));\n\t\t}\n\n\t\tstatic _deserialize(str) {\n\t\t\treturn JSON.parse(LZString.decompressFromBase64(str));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAdapter Utility Functions.\n\t*******************************************************************************************************************/\n\tfunction adapterInit(\n\t\t// Only used for stores updates.\n\t\tstorageId\n\t) {\n\t\t// Cookie feature test.\n\t\ttry {\n\t\t\tconst tid = `_sc_${String(Date.now())}`;\n\n\t\t\t// We only test a session cookie as that should suffice.\n\t\t\t_CookieAdapter._setCookie(tid, _CookieAdapter._serialize(tid), undefined);\n\t\t\t_ok = _CookieAdapter._deserialize(_CookieAdapter._getCookie(tid)) === tid;\n\t\t\t_CookieAdapter._setCookie(tid, undefined, _MIN_EXPIRY);\n\t\t}\n\t\tcatch (ex) {\n\t\t\t_ok = false;\n\t\t}\n\n\t\t/* legacy */\n\t\t// Attempt to update the cookie stores, if necessary.  This should happen only during initialization.\n\t\tif (_ok) {\n\t\t\t_updateCookieStores(storageId);\n\t\t}\n\t\t/* /legacy */\n\n\t\treturn _ok;\n\t}\n\n\tfunction adapterCreate(storageId, persistent) {\n\t\tif (!_ok) {\n\t\t\tthrow new Error('adapter not initialized');\n\t\t}\n\n\t\treturn new _CookieAdapter(storageId, persistent);\n\t}\n\n\t/* legacy */\n\t// Updates old non-segmented cookie stores into segmented stores.\n\tfunction _updateCookieStores(storageId) {\n\t\tif (document.cookie === '') {\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldPrefix     = `${storageId}.`;\n\t\tconst oldPrefixRe   = new RegExp(`^${RegExp.escape(oldPrefix)}`);\n\t\tconst persistPrefix = `${storageId}!.`;\n\t\tconst sessionPrefix = `${storageId}*.`;\n\t\tconst sessionTestRe = /\\.(?:state|rcWarn)$/;\n\t\tconst cookies       = document.cookie.split(/;\\s*/);\n\n\t\tfor (let i = 0; i < cookies.length; ++i) {\n\t\t\tconst kvPair = cookies[i].split('=');\n\t\t\tconst key    = decodeURIComponent(kvPair[0]);\n\n\t\t\tif (oldPrefixRe.test(key)) {\n\t\t\t\t/*\n\t\t\t\t\tAll stored values are serialized and an empty string serializes to a non-empty\n\t\t\t\t\tstring.  Therefore, receiving an empty string here signifies a deleted value,\n\t\t\t\t\tnot a serialized empty string, so we should skip processing such pairs.\n\t\t\t\t*/\n\t\t\t\tconst value = decodeURIComponent(kvPair[1]);\n\n\t\t\t\tif (value !== '') {\n\t\t\t\t\tconst persist = !sessionTestRe.test(key);\n\n\t\t\t\t\t// Delete the old k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t_MIN_EXPIRY\n\t\t\t\t\t);\n\n\t\t\t\t\t// Set the new k/v pair.\n\t\t\t\t\t_CookieAdapter._setCookie(\n\t\t\t\t\t\tkey.replace(oldPrefixRe, () => persist ? persistPrefix : sessionPrefix),\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tpersist ? _MAX_EXPIRY : undefined\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t/* /legacy */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : adapterInit },\n\t\tcreate : { value : adapterCreate }\n\t}));\n})());\n\n/***********************************************************************************************************************\n\n\tlib/debugview.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\tTODO: Make this use jQuery throughout.\n*/\nvar DebugView = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDebugView Class.\n\t*******************************************************************************************************************/\n\tclass DebugView {\n\t\tconstructor(parent, type, name, title) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : parent\n\t\t\t\t},\n\n\t\t\t\tview : {\n\t\t\t\t\tvalue : document.createElement('span')\n\t\t\t\t},\n\n\t\t\t\tbreak : {\n\t\t\t\t\tvalue : document.createElement('wbr')\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up the wrapper (`<span>`) element.\n\t\t\tjQuery(this.view)\n\t\t\t\t.attr({\n\t\t\t\t\ttitle,\n\t\t\t\t\t'aria-label' : title,\n\t\t\t\t\t'data-type'  : type != null ? type : '', // lazy equality for null\n\t\t\t\t\t'data-name'  : name != null ? name : ''  // lazy equality for null\n\t\t\t\t})\n\t\t\t\t.addClass('debug');\n\n\t\t\t// Set up the word break (`<wbr>`) element.\n\t\t\tjQuery(this.break).addClass('debug hidden');\n\n\t\t\t// Add the wrapper (`<span>`) and word break (`<wbr>`) elements to the `parent` element.\n\t\t\tthis.parent.appendChild(this.view);\n\t\t\tthis.parent.appendChild(this.break);\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this.view;\n\t\t}\n\n\t\tget type() {\n\t\t\treturn this.view.getAttribute('data-type');\n\t\t}\n\t\tset type(type) {\n\t\t\tthis.view.setAttribute('data-type', type != null ? type : ''); // lazy equality for null\n\t\t}\n\n\t\tget name() {\n\t\t\treturn this.view.getAttribute('data-name');\n\t\t}\n\t\tset name(name) {\n\t\t\tthis.view.setAttribute('data-name', name != null ? name : ''); // lazy equality for null\n\t\t}\n\n\t\tget title() {\n\t\t\treturn this.view.title;\n\t\t}\n\t\tset title(title) {\n\t\t\tthis.view.title = title;\n\t\t}\n\n\t\tappend(el) {\n\t\t\tjQuery(this.view).append(el);\n\t\t\treturn this;\n\t\t}\n\n\t\tmodes(options) {\n\t\t\tif (options == null) { // lazy equality for null\n\t\t\t\tconst current = {};\n\n\t\t\t\tthis.view.className.splitOrEmpty(/\\s+/).forEach(name => {\n\t\t\t\t\tif (name !== 'debug') {\n\t\t\t\t\t\tcurrent[name] = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\telse if (typeof options === 'object') {\n\t\t\t\tObject.keys(options).forEach(function (name) {\n\t\t\t\t\tthis[options[name] ? 'addClass' : 'removeClass'](name);\n\t\t\t\t}, jQuery(this.view));\n\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tthrow new Error('DebugView.prototype.modes options parameter must be an object or null/undefined');\n\t\t}\n\n\t\tremove() {\n\t\t\tconst $view = jQuery(this.view);\n\n\t\t\tif (this.view.hasChildNodes()) {\n\t\t\t\t$view.contents().appendTo(this.parent);\n\t\t\t}\n\n\t\t\t$view.remove();\n\t\t\tjQuery(this.break).remove();\n\t\t}\n\n\t\tstatic isEnabled() {\n\t\t\treturn jQuery(document.documentElement).attr('data-debug-view') === 'enabled';\n\t\t}\n\n\t\tstatic enable() {\n\t\t\tjQuery(document.documentElement).attr('data-debug-view', 'enabled');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic disable() {\n\t\t\tjQuery(document.documentElement).removeAttr('data-debug-view');\n\t\t\tjQuery.event.trigger(':debugviewupdate');\n\t\t}\n\n\t\tstatic toggle() {\n\t\t\tif (jQuery(document.documentElement).attr('data-debug-view') === 'enabled') {\n\t\t\t\tDebugView.disable();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDebugView.enable();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn DebugView;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/nodetyper.js\n\n\tCopyright © 2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar NodeTyper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tNodeTyper Class.\n\t*******************************************************************************************************************/\n\tclass NodeTyper {\n\t\tconstructor(config) {\n\t\t\tif (typeof config !== 'object' || config === null) {\n\t\t\t\tthrow new Error(`config parameter must be an object (received: ${Util.getType(config)})`);\n\t\t\t}\n\t\t\tif (!config.hasOwnProperty('targetNode') || !(config.targetNode instanceof Node)) {\n\t\t\t\tthrow new Error('config parameter object \"targetNode\" property must be a node');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tnode : {\n\t\t\t\t\tvalue : config.targetNode\n\t\t\t\t},\n\n\t\t\t\tchildNodes : {\n\t\t\t\t\tvalue : []\n\t\t\t\t},\n\n\t\t\t\tnodeValue : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t},\n\n\t\t\t\tappendTo : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.parentNode || null\n\t\t\t\t},\n\n\t\t\t\tclassNames : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : config.classNames || null\n\t\t\t\t},\n\n\t\t\t\tfinished : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst node = this.node;\n\n\t\t\tif (node.nodeValue) {\n\t\t\t\tthis.nodeValue = node.nodeValue;\n\t\t\t\tnode.nodeValue = '';\n\t\t\t}\n\n\t\t\tlet childNode;\n\n\t\t\twhile ((childNode = node.firstChild) !== null) {\n\t\t\t\tthis.childNodes.push(new NodeTyper({\n\t\t\t\t\ttargetNode : childNode,\n\t\t\t\t\tparentNode : node,\n\t\t\t\t\tclassNames : this.classNames\n\t\t\t\t}));\n\n\t\t\t\tnode.removeChild(childNode);\n\t\t\t}\n\t\t}\n\n\t\tfinish() {\n\t\t\twhile (this.type(true)) /* no-op */;\n\t\t\treturn false;\n\t\t}\n\n\t\ttype(flush) {\n\t\t\tif (this.finished) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (this.appendTo) {\n\t\t\t\tthis.appendTo.appendChild(this.node);\n\t\t\t\tthis.appendTo = null;\n\n\t\t\t\t// Immediately finish typing this node if….\n\t\t\t\tif (\n\t\t\t\t\t// …it's neither a element or text node.\n\t\t\t\t\tthis.node.nodeType !== Node.ELEMENT_NODE && this.node.nodeType !== Node.TEXT_NODE\n\n\t\t\t\t\t// …or the computed value of its parent node's `display` property is `'none'`.\n\t\t\t\t\t|| jQuery(this.node.parentNode).css('display') === 'none'\n\t\t\t\t) {\n\t\t\t\t\treturn this.finish();\n\t\t\t\t}\n\n\t\t\t\tif (this.node.parentNode && this.classNames) {\n\t\t\t\t\tjQuery(this.node.parentNode).addClass(this.classNames);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.nodeValue) {\n\t\t\t\tif (flush) {\n\t\t\t\t\t// Concatenate here in case we've already done some processing.\n\t\t\t\t\tthis.node.nodeValue += this.nodeValue;\n\t\t\t\t\tthis.nodeValue = '';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Use `Util.charAndPosAt()` here to properly handle Unicode code points\n\t\t\t\t\t// that are comprised of surrogate pairs.\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(this.nodeValue, 0);\n\t\t\t\t\tthis.node.nodeValue += char;\n\t\t\t\t\tthis.nodeValue = this.nodeValue.slice(1 + end - start);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (this.classNames) {\n\t\t\t\tjQuery(this.node.parentNode).removeClass(this.classNames);\n\t\t\t\tthis.classNames = null;\n\t\t\t}\n\n\t\t\tconst childNodes = this.childNodes;\n\n\t\t\twhile (childNodes.length > 0) {\n\t\t\t\tif (childNodes[0].type()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tchildNodes.shift();\n\t\t\t}\n\n\t\t\tthis.finished = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn NodeTyper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/prngwrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar PRNGWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tPRNGWrapper Class.\n\t*******************************************************************************************************************/\n\tclass PRNGWrapper {\n\t\tconstructor(seed, useEntropy) {\n\t\t\t/* eslint-disable new-cap */\n\t\t\tObject.defineProperties(this, new Math.seedrandom(seed, useEntropy, (prng, seed) => ({\n\t\t\t\t_prng : {\n\t\t\t\t\tvalue : prng\n\t\t\t\t},\n\n\t\t\t\tseed : {\n\t\t\t\t\t/*\n\t\t\t\t\t\tTODO: Make this non-writable.\n\t\t\t\t\t*/\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : seed\n\t\t\t\t},\n\n\t\t\t\tpull : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\trandom : {\n\t\t\t\t\tvalue() {\n\t\t\t\t\t\t++this.pull;\n\t\t\t\t\t\treturn this._prng();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})));\n\t\t\t/* eslint-enable new-cap */\n\t\t}\n\n\t\tstatic marshal(prng) {\n\t\t\tif (!prng || !prng.hasOwnProperty('seed') || !prng.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG is missing required data');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tseed : prng.seed,\n\t\t\t\tpull : prng.pull\n\t\t\t};\n\t\t}\n\n\t\tstatic unmarshal(prngObj) {\n\t\t\tif (!prngObj || !prngObj.hasOwnProperty('seed') || !prngObj.hasOwnProperty('pull')) {\n\t\t\t\tthrow new Error('PRNG object is missing required data');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCreate a new PRNG using the original seed and pull values from it until it\n\t\t\t\thas reached the original pull count.\n\t\t\t*/\n\t\t\tconst prng = new PRNGWrapper(prngObj.seed, false);\n\n\t\t\tfor (let i = prngObj.pull; i > 0; --i) {\n\t\t\t\tprng.random();\n\t\t\t}\n\n\t\t\treturn prng;\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn PRNGWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/stylewrapper.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Story, Wikifier */\n\nvar StyleWrapper = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _imageMarkupRe    = new RegExp(Patterns.cssImage, 'g');\n\tconst _hasImageMarkupRe = new RegExp(Patterns.cssImage);\n\n\n\t/*******************************************************************************************************************\n\t\tStyleWrapper Class.\n\t*******************************************************************************************************************/\n\tclass StyleWrapper {\n\t\tconstructor(style) {\n\t\t\tif (style == null) { // lazy equality for null\n\t\t\t\tthrow new TypeError('StyleWrapper style parameter must be an HTMLStyleElement object');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tstyle : {\n\t\t\t\t\tvalue : style\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tisEmpty() {\n\t\t\t// This should work in all supported browsers.\n\t\t\treturn this.style.cssRules.length === 0;\n\t\t}\n\n\t\tset(rawCss) {\n\t\t\tthis.clear();\n\t\t\tthis.add(rawCss);\n\t\t}\n\n\t\tadd(rawCss) {\n\t\t\tlet css = rawCss;\n\n\t\t\t// Check for wiki image transclusion.\n\t\t\tif (_hasImageMarkupRe.test(css)) {\n\t\t\t\t/*\n\t\t\t\t\tThe JavaScript specifications, since at least ES3, say that `<String>.replace()`\n\t\t\t\t\tshould reset a global-flagged regular expression's `lastIndex` property to `0`\n\t\t\t\t\tupon invocation.  Buggy browser versions exist, however, which do not reset\n\t\t\t\t\t`lastIndex`, so we should do so manually to support those browsers.\n\n\t\t\t\t\tNOTE: I do not think this is actually necessary, since `_imageMarkupRe` is\n\t\t\t\t\tscoped to this module—meaning users should not be able to access it.  That\n\t\t\t\t\tbeing the case, and since we search to exhaustion which should also cause\n\t\t\t\t\t`lastIndex` to be reset, there should never be an instance where we invoke\n\t\t\t\t\t`css.replace()` and `_imageMarkupRe.lastIndex` is not already `0`.  Still,\n\t\t\t\t\tconsidering the other bug, better safe than sorry.\n\t\t\t\t*/\n\t\t\t\t_imageMarkupRe.lastIndex = 0;\n\n\t\t\t\tcss = css.replace(_imageMarkupRe, wikiImage => {\n\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t});\n\n\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t*/\n\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For IE ≤ 10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText += css;\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥ 11).\n\t\t\telse {\n\t\t\t\tthis.style.appendChild(document.createTextNode(css));\n\t\t\t}\n\t\t}\n\n\t\tclear() {\n\t\t\t// For IE ≤10.\n\t\t\tif (this.style.styleSheet) {\n\t\t\t\tthis.style.styleSheet.cssText = '';\n\t\t\t}\n\n\t\t\t// For all other browsers (incl. IE ≥11).\n\t\t\telse {\n\t\t\t\tjQuery(this.style).empty();\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn StyleWrapper;\n})();\n\n/***********************************************************************************************************************\n\n\tlib/diff.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, clone */\n\nvar Diff = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tDiff Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tDiff operations object (pseudo-enumeration).\n\t*/\n\tconst Op = Util.toEnum({\n\t\tDelete      : 0,\n\t\tSpliceArray : 1,\n\t\tCopy        : 2,\n\t\tCopyDate    : 3\n\t});\n\n\t/*\n\t\tReturns a difference object generated from comparing the orig and dest objects.\n\t*/\n\tfunction diff(orig, dest) /* diff object */ {\n\t\tconst objToString = Object.prototype.toString;\n\t\tconst origIsArray = orig instanceof Array;\n\t\tconst keys        = []\n\t\t\t.concat(Object.keys(orig), Object.keys(dest))\n\t\t\t.sort()\n\t\t\t.filter((val, i, arr) => i === 0 || arr[i - 1] !== val);\n\t\tconst diffed      = {};\n\t\tlet aOpRef;\n\n\t\tconst keyIsAOpRef = key => key === aOpRef;\n\n\t\t/* eslint-disable max-depth */\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key   = keys[i];\n\t\t\tconst origP = orig[key];\n\t\t\tconst destP = dest[key];\n\n\t\t\tif (orig.hasOwnProperty(key)) {\n\t\t\t\t// Key exists in both.\n\t\t\t\tif (dest.hasOwnProperty(key)) {\n\t\t\t\t\t// Values are exactly the same, so do nothing.\n\t\t\t\t\tif (origP === destP) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Values are of the same basic type.\n\t\t\t\t\tif (typeof origP === typeof destP) { // eslint-disable-line valid-typeof\n\t\t\t\t\t\t// Values are functions.\n\t\t\t\t\t\tif (typeof origP === 'function') {\n\t\t\t\t\t\t\t/* diffed[key] = [Op.Copy, destP]; */\n\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are primitives.\n\t\t\t\t\t\telse if (typeof origP !== 'object' || origP === null) {\n\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, destP];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Values are objects.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst origPType = objToString.call(origP);\n\t\t\t\t\t\t\tconst destPType = objToString.call(destP);\n\n\t\t\t\t\t\t\t// Values are objects of the same reported type.\n\t\t\t\t\t\t\tif (origPType === destPType) {\n\t\t\t\t\t\t\t\t// Various special cases to handle supported non-generic objects.\n\t\t\t\t\t\t\t\tif (origP instanceof Date) {\n\t\t\t\t\t\t\t\t\tif (Number(origP) !== Number(destP)) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Map) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof RegExp) {\n\t\t\t\t\t\t\t\t\tif (origP.toString() !== destP.toString()) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (origP instanceof Set) {\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Unknown non-generic objects (custom or unsupported natives).\n\t\t\t\t\t\t\t\telse if (origPType !== '[object Object]') {\n\t\t\t\t\t\t\t\t\t// We cannot know how to process these objects,\n\t\t\t\t\t\t\t\t\t// so we simply accept them as-is.\n\t\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Generic objects.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tconst recurse = diff(origP, destP);\n\n\t\t\t\t\t\t\t\t\tif (recurse !== null) {\n\t\t\t\t\t\t\t\t\t\tdiffed[key] = recurse;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Values are objects of different reported types.\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdiffed[key] = [Op.Copy, clone(destP)];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Values are of different types.\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = [\n\t\t\t\t\t\t\tOp.Copy,\n\t\t\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Key only exists in orig.\n\t\t\t\telse {\n\t\t\t\t\tif (origIsArray && Util.isNumeric(key)) {\n\t\t\t\t\t\tconst nKey = Number(key);\n\n\t\t\t\t\t\tif (!aOpRef) {\n\t\t\t\t\t\t\taOpRef = '';\n\n\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\taOpRef += '~';\n\t\t\t\t\t\t\t} while (keys.some(keyIsAOpRef));\n\n\t\t\t\t\t\t\tdiffed[aOpRef] = [Op.SpliceArray, nKey, nKey];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey < diffed[aOpRef][1]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][1] = nKey;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nKey > diffed[aOpRef][2]) {\n\t\t\t\t\t\t\tdiffed[aOpRef][2] = nKey;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdiffed[key] = Op.Delete;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Key only exists in dest.\n\t\t\telse {\n\t\t\t\tdiffed[key] = [\n\t\t\t\t\tOp.Copy,\n\t\t\t\t\ttypeof destP !== 'object' || destP === null ? destP : clone(destP)\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\t/* eslint-enable max-depth */\n\n\t\treturn Object.keys(diffed).length > 0 ? diffed : null;\n\t}\n\n\t/*\n\t\tReturns the object resulting from updating the orig object with the diffed object.\n\t*/\n\tfunction patch(orig, diffed) /* patched object */ {\n\t\tconst keys    = Object.keys(diffed || {});\n\t\tconst patched = clone(orig);\n\n\t\tfor (let i = 0, klen = keys.length; i < klen; ++i) {\n\t\t\tconst key     = keys[i];\n\t\t\tconst diffedP = diffed[key];\n\n\t\t\tif (diffedP === Op.Delete) {\n\t\t\t\tdelete patched[key];\n\t\t\t}\n\t\t\telse if (diffedP instanceof Array) {\n\t\t\t\tswitch (diffedP[0]) {\n\t\t\t\tcase Op.SpliceArray:\n\t\t\t\t\tpatched.splice(diffedP[1], 1 + (diffedP[2] - diffedP[1]));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.Copy:\n\t\t\t\t\tpatched[key] = clone(diffedP[1]);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Op.CopyDate:\n\t\t\t\t\tpatched[key] = new Date(diffedP[1]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpatched[key] = patch(patched[key], diffedP);\n\t\t\t}\n\t\t}\n\n\t\treturn patched;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tOp    : { value : Op },\n\t\tdiff  : { value : diff },\n\t\tpatch : { value : patch }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/l10n.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global l10nStrings, strings */\n\nvar L10n = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Replacement pattern regular expressions.\n\tconst _patternRe    = /\\{\\w+\\}/g;\n\tconst _hasPatternRe = new RegExp(_patternRe.source); // to drop the global flag\n\n\n\t/*******************************************************************************************************************\n\t\tLocalization Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nInit() {\n\t\t/* legacy */\n\t\t_mapStringsToL10nStrings();\n\t\t/* /legacy */\n\t}\n\n\t/*******************************************************************************************************************\n\t\tLocalized String Functions.\n\t*******************************************************************************************************************/\n\tfunction l10nGet(ids, overrides) {\n\t\tif (!ids) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst id = (idList => {\n\t\t\tlet selectedId;\n\t\t\tidList.some(id => {\n\t\t\t\tif (l10nStrings.hasOwnProperty(id)) {\n\t\t\t\t\tselectedId = id;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\treturn selectedId;\n\t\t})(Array.isArray(ids) ? ids : [ids]);\n\n\t\tif (!id) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst maxIterations = 50;\n\t\tlet processed = l10nStrings[id];\n\t\tlet iteration = 0;\n\n\t\twhile (_hasPatternRe.test(processed)) {\n\t\t\tif (++iteration > maxIterations) {\n\t\t\t\tthrow new Error('L10n.get exceeded maximum replacement iterations, probable infinite loop');\n\t\t\t}\n\n\t\t\t// Possibly required by some old buggy browsers.\n\t\t\t_patternRe.lastIndex = 0;\n\n\t\t\tprocessed = processed.replace(_patternRe, pat => {\n\t\t\t\tconst subId = pat.slice(1, -1);\n\n\t\t\t\tif (overrides && overrides.hasOwnProperty(subId)) {\n\t\t\t\t\treturn overrides[subId];\n\t\t\t\t}\n\t\t\t\telse if (l10nStrings.hasOwnProperty(subId)) {\n\t\t\t\t\treturn l10nStrings[subId];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn processed;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tAttempt to map legacy `strings` object properties to the `l10nStrings` object.\n\t*/\n\tfunction _mapStringsToL10nStrings() {\n\t\tif (strings && Object.keys(strings).length > 0) {\n\t\t\tObject.keys(l10nStrings).forEach(id => {\n\t\t\t\ttry {\n\t\t\t\t\tlet value;\n\n\t\t\t\t\tswitch (id) {\n\t\t\t\t\t/*\n\t\t\t\t\t\tGeneral.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'identity': value = strings.identity; break;\n\t\t\t\t\tcase 'aborting': value = strings.aborting; break;\n\t\t\t\t\tcase 'cancel':   value = strings.cancel; break;\n\t\t\t\t\tcase 'close':    value = strings.close; break;\n\t\t\t\t\tcase 'ok':       value = strings.ok; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tErrors.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'errorTitle':              value = strings.errors.title; break;\n\t\t\t\t\tcase 'errorNonexistentPassage': value = strings.errors.nonexistentPassage; break;\n\t\t\t\t\tcase 'errorSaveMissingData':    value = strings.errors.saveMissingData; break;\n\t\t\t\t\tcase 'errorSaveIdMismatch':     value = strings.errors.saveIdMismatch; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tWarnings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'warningDegraded': value = strings.warnings.degraded; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tDebug View.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'debugViewTitle':  value = strings.debugView.title; break;\n\t\t\t\t\tcase 'debugViewToggle': value = strings.debugView.toggle; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tUI bar.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'uiBarToggle':   value = strings.uiBar.toggle; break;\n\t\t\t\t\tcase 'uiBarBackward': value = strings.uiBar.backward; break;\n\t\t\t\t\tcase 'uiBarForward':  value = strings.uiBar.forward; break;\n\t\t\t\t\tcase 'uiBarJumpto':   value = strings.uiBar.jumpto; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tJump To.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'jumptoTitle':       value = strings.jumpto.title; break;\n\t\t\t\t\tcase 'jumptoTurn':        value = strings.jumpto.turn; break;\n\t\t\t\t\tcase 'jumptoUnavailable': value = strings.jumpto.unavailable; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSaves.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'savesTitle':       value = strings.saves.title; break;\n\t\t\t\t\tcase 'savesDisallowed':  value = strings.saves.disallowed; break;\n\t\t\t\t\tcase 'savesIncapable':   value = strings.saves.incapable; break;\n\t\t\t\t\tcase 'savesLabelAuto':   value = strings.saves.labelAuto; break;\n\t\t\t\t\tcase 'savesLabelDelete': value = strings.saves.labelDelete; break;\n\t\t\t\t\tcase 'savesLabelExport': value = strings.saves.labelExport; break;\n\t\t\t\t\tcase 'savesLabelImport': value = strings.saves.labelImport; break;\n\t\t\t\t\tcase 'savesLabelLoad':   value = strings.saves.labelLoad; break;\n\t\t\t\t\tcase 'savesLabelClear':  value = strings.saves.labelClear; break;\n\t\t\t\t\tcase 'savesLabelSave':   value = strings.saves.labelSave; break;\n\t\t\t\t\tcase 'savesLabelSlot':   value = strings.saves.labelSlot; break;\n\t\t\t\t\tcase 'savesUnavailable': value = strings.saves.unavailable; break;\n\t\t\t\t\tcase 'savesUnknownDate': value = strings.saves.unknownDate; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tSettings.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'settingsTitle': value = strings.settings.title; break;\n\t\t\t\t\tcase 'settingsOff':   value = strings.settings.off; break;\n\t\t\t\t\tcase 'settingsOn':    value = strings.settings.on; break;\n\t\t\t\t\tcase 'settingsReset': value = strings.settings.reset; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tRestart.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'restartTitle':  value = strings.restart.title; break;\n\t\t\t\t\tcase 'restartPrompt': value = strings.restart.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tShare.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'shareTitle': value = strings.share.title; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAlert.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'alertTitle': /* none */ break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAutoload.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'autoloadTitle':  value = strings.autoload.title; break;\n\t\t\t\t\tcase 'autoloadCancel': value = strings.autoload.cancel; break;\n\t\t\t\t\tcase 'autoloadOk':     value = strings.autoload.ok; break;\n\t\t\t\t\tcase 'autoloadPrompt': value = strings.autoload.prompt; break;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tMacros.\n\t\t\t\t\t*/\n\t\t\t\t\tcase 'macroBackText':   value = strings.macros.back.text; break;\n\t\t\t\t\tcase 'macroReturnText': value = strings.macros.return.text; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tl10nStrings[id] = value.replace(/%\\w+%/g, pat => `{${pat.slice(1, -1)}}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tLocalization Functions.\n\t\t*/\n\t\tinit : { value : l10nInit },\n\n\t\t/*\n\t\t\tLocalized String Functions.\n\t\t*/\n\t\tget : { value : l10nGet }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tl10n/legacy.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\n/*\n\t[DEPRECATED] The `strings` object is deprecated and should no longer be used.\n\tAll new or updated translations should be based upon the `l10nStrings` object\n\t(see: `l10n/strings.js`).\n\n\tLegacy/existing uses of the `strings` object will be mapped to the `l10nStrings`\n\tobject after user script evaluation.\n*/\nvar strings = { // eslint-disable-line no-unused-vars, no-var\n\terrors    : {},\n\twarnings  : {},\n\tdebugView : {},\n\tuiBar     : {},\n\tjumpto    : {},\n\tsaves     : {},\n\tsettings  : {},\n\trestart   : {},\n\tshare     : {},\n\tautoload  : {},\n\tmacros    : {\n\t\tback   : {},\n\t\treturn : {}\n\t}\n};\n\n/***********************************************************************************************************************\n\n\tl10n/strings.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* eslint-disable max-len, prefer-template */\n\n/*\n\tATTENTION TRANSLATORS\n\n\tPlease use the `locale/l10n-template.js` file, from the root of the repository,\n\tas the template for your translation rather than this file.\n\n\tSEE: https://github.com/tmedwards/sugarcube-2/tree/develop/locale\n*/\nvar l10nStrings = { // eslint-disable-line no-unused-vars, no-var\n\t/*\n\t\tGeneral.\n\t*/\n\tidentity : 'game',\n\taborting : 'Aborting',\n\tcancel   : 'Cancel',\n\tclose    : 'Close',\n\tok       : 'OK',\n\n\t/*\n\t\tErrors.\n\t*/\n\terrorTitle              : 'Error',\n\terrorToggle             : 'Toggle the error view',\n\terrorNonexistentPassage : 'the passage \"{passage}\" does not exist', // NOTE: `passage` is supplied locally\n\terrorSaveMissingData    : 'save is missing required data. Either the loaded file is not a save or the save has become corrupted',\n\terrorSaveIdMismatch     : 'save is from the wrong {identity}',\n\n\t/*\n\t\tWarnings.\n\t*/\n\t_warningIntroLacking  : 'Your browser either lacks or has disabled',\n\t_warningOutroDegraded : ', so this {identity} is running in a degraded mode. You may be able to continue, however, some parts may not work properly.',\n\twarningNoWebStorage   : '{_warningIntroLacking} the Web Storage API{_warningOutroDegraded}',\n\twarningDegraded       : '{_warningIntroLacking} some of the capabilities required by this {identity}{_warningOutroDegraded}',\n\n\t/*\n\t\tDebug bar.\n\t*/\n\tdebugBarToggle      : 'Toggle the debug bar',\n\tdebugBarNoWatches   : '\\u2014 no watches set \\u2014',\n\tdebugBarAddWatch    : 'Add watch',\n\tdebugBarDeleteWatch : 'Delete watch',\n\tdebugBarWatchAll    : 'Watch all',\n\tdebugBarWatchNone   : 'Delete all',\n\tdebugBarLabelAdd    : 'Add',\n\tdebugBarLabelWatch  : 'Watch',\n\tdebugBarLabelTurn   : 'Turn', // (noun) chance to act (in a game), moment, period\n\tdebugBarLabelViews  : 'Views',\n\tdebugBarViewsToggle : 'Toggle the debug views',\n\tdebugBarWatchToggle : 'Toggle the watch panel',\n\n\t/*\n\t\tUI bar.\n\t*/\n\tuiBarToggle   : 'Toggle the UI bar',\n\tuiBarBackward : 'Go backward within the {identity} history',\n\tuiBarForward  : 'Go forward within the {identity} history',\n\tuiBarJumpto   : 'Jump to a specific point within the {identity} history',\n\n\t/*\n\t\tJump To.\n\t*/\n\tjumptoTitle       : 'Jump To',\n\tjumptoTurn        : 'Turn', // (noun) chance to act (in a game), moment, period\n\tjumptoUnavailable : 'No jump points currently available\\u2026',\n\n\t/*\n\t\tSaves.\n\t*/\n\tsavesTitle       : 'Saves',\n\tsavesDisallowed  : 'Saving has been disallowed on this passage.',\n\tsavesIncapable   : '{_warningIntroLacking} the capabilities required to support saves, so saves have been disabled for this session.',\n\tsavesLabelAuto   : 'Autosave',\n\tsavesLabelDelete : 'Delete',\n\tsavesLabelExport : 'Save to Disk\\u2026',\n\tsavesLabelImport : 'Load from Disk\\u2026',\n\tsavesLabelLoad   : 'Load',\n\tsavesLabelClear  : 'Delete All',\n\tsavesLabelSave   : 'Save',\n\tsavesLabelSlot   : 'Slot',\n\tsavesUnavailable : 'No save slots found\\u2026',\n\tsavesUnknownDate : 'unknown',\n\n\t/*\n\t\tSettings.\n\t*/\n\tsettingsTitle : 'Settings',\n\tsettingsOff   : 'Off',\n\tsettingsOn    : 'On',\n\tsettingsReset : 'Reset to Defaults',\n\n\t/*\n\t\tRestart.\n\t*/\n\trestartTitle  : 'Restart',\n\trestartPrompt : 'Are you sure that you want to restart? Unsaved progress will be lost.',\n\n\t/*\n\t\tShare.\n\t*/\n\tshareTitle : 'Share',\n\n\t/*\n\t\tAlert.\n\t*/\n\talertTitle : 'Alert',\n\n\t/*\n\t\tAutoload.\n\t*/\n\tautoloadTitle  : 'Autoload',\n\tautoloadCancel : 'Go to start',\n\tautoloadOk     : 'Load autosave',\n\tautoloadPrompt : 'An autosave exists. Load it now or go to the start?',\n\n\t/*\n\t\tMacros.\n\t*/\n\tmacroBackText   : 'Back',  // (verb) rewind, revert\n\tmacroReturnText : 'Return' // (verb) go/send back\n};\n\n/***********************************************************************************************************************\n\n\tconfig.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util */\n\nvar Config = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// General settings.\n\tlet _debug                 = false;\n\tlet _addVisitedLinkClass   = false;\n\tlet _cleanupWikifierOutput = false;\n\tlet _loadDelay             = 0;\n\n\t// Audio settings.\n\tlet _audioPauseOnFadeToZero = true;\n\tlet _audioPreloadMetadata   = true;\n\n\t// State history settings.\n\tlet _historyControls  = true;\n\tlet _historyMaxStates = 100;\n\n\t// Macros settings.\n\tlet _macrosIfAssignmentError   = true;\n\tlet _macrosMaxLoopIterations   = 1000;\n\tlet _macrosTypeSkipKey         = '\\x20'; // Space\n\tlet _macrosTypeVisitedPassages = true;\n\n\t// Navigation settings.\n\tlet _navigationOverride;\n\n\t// Passages settings.\n\tlet _passagesDescriptions;\n\tlet _passagesDisplayTitles = false;\n\tlet _passagesNobr          = false;\n\tlet _passagesStart; // set by `Story.load()`\n\tlet _passagesOnProcess;\n\tlet _passagesTransitionOut;\n\n\t// Saves settings.\n\tlet _savesAutoload;\n\tlet _savesAutosave;\n\tlet _savesId        = 'untitled-story';\n\tlet _savesIsAllowed;\n\tlet _savesOnLoad;\n\tlet _savesOnSave;\n\tlet _savesSlots     = 8;\n\tlet _savesVersion;\n\n\t// UI settings.\n\tlet _uiStowBarInitially    = 800;\n\tlet _uiUpdateStoryElements = true;\n\n\n\t/*******************************************************************************\n\t\tError Constants.\n\t*******************************************************************************/\n\n\tconst _errHistoryModeDeprecated     = 'Config.history.mode has been deprecated and is no longer used by SugarCube, please remove it from your code';\n\tconst _errHistoryTrackingDeprecated = 'Config.history.tracking has been deprecated, use Config.history.maxStates instead';\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze({\n\t\t/*\n\t\t\tGeneral settings.\n\t\t*/\n\t\tget debug() { return _debug; },\n\t\tset debug(value) { _debug = Boolean(value); },\n\n\t\tget addVisitedLinkClass() { return _addVisitedLinkClass; },\n\t\tset addVisitedLinkClass(value) { _addVisitedLinkClass = Boolean(value); },\n\n\t\tget cleanupWikifierOutput() { return _cleanupWikifierOutput; },\n\t\tset cleanupWikifierOutput(value) { _cleanupWikifierOutput = Boolean(value); },\n\n\t\tget loadDelay() { return _loadDelay; },\n\t\tset loadDelay(value) {\n\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\tthrow new RangeError('Config.loadDelay must be a non-negative integer');\n\t\t\t}\n\n\t\t\t_loadDelay = value;\n\t\t},\n\n\t\t/*\n\t\t\tAudio settings.\n\t\t*/\n\t\taudio : Object.freeze({\n\t\t\tget pauseOnFadeToZero() { return _audioPauseOnFadeToZero; },\n\t\t\tset pauseOnFadeToZero(value) { _audioPauseOnFadeToZero = Boolean(value); },\n\n\t\t\tget preloadMetadata() { return _audioPreloadMetadata; },\n\t\t\tset preloadMetadata(value) { _audioPreloadMetadata = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tState history settings.\n\t\t*/\n\t\thistory : Object.freeze({\n\t\t\t// TODO: (v3) This should be under UI settings → `Config.ui.historyControls`.\n\t\t\tget controls() { return _historyControls; },\n\t\t\tset controls(value) {\n\t\t\t\tconst controls = Boolean(value);\n\n\t\t\t\tif (_historyMaxStates === 1 && controls) {\n\t\t\t\t\tthrow new Error('Config.history.controls must be false when Config.history.maxStates is 1');\n\t\t\t\t}\n\n\t\t\t\t_historyControls = controls;\n\t\t\t},\n\n\t\t\tget maxStates() { return _historyMaxStates; },\n\t\t\tset maxStates(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.history.maxStates must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_historyMaxStates = value;\n\n\t\t\t\t// Force `Config.history.controls` to `false`, when limited to `1` moment.\n\t\t\t\tif (_historyControls && value === 1) {\n\t\t\t\t\t_historyControls = false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// legacy\n\t\t\t// Die if deprecated state history settings are accessed.\n\t\t\tget mode()  { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tset mode(_) { throw new Error(_errHistoryModeDeprecated); },\n\t\t\tget tracking()  { throw new Error(_errHistoryTrackingDeprecated); },\n\t\t\tset tracking(_) { throw new Error(_errHistoryTrackingDeprecated); }\n\t\t\t// /legacy\n\t\t}),\n\n\t\t/*\n\t\t\tMacros settings.\n\t\t*/\n\t\tmacros : Object.freeze({\n\t\t\tget ifAssignmentError() { return _macrosIfAssignmentError; },\n\t\t\tset ifAssignmentError(value) { _macrosIfAssignmentError = Boolean(value); },\n\n\t\t\tget maxLoopIterations() { return _macrosMaxLoopIterations; },\n\t\t\tset maxLoopIterations(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new RangeError('Config.macros.maxLoopIterations must be a non-negative integer');\n\t\t\t\t}\n\n\t\t\t\t_macrosMaxLoopIterations = value;\n\t\t\t},\n\n\t\t\tget typeSkipKey() { return _macrosTypeSkipKey; },\n\t\t\tset typeSkipKey(value) { _macrosTypeSkipKey = String(value); },\n\n\t\t\tget typeVisitedPassages() { return _macrosTypeVisitedPassages; },\n\t\t\tset typeVisitedPassages(value) { _macrosTypeVisitedPassages = Boolean(value); }\n\t\t}),\n\n\t\t/*\n\t\t\tNavigation settings.\n\t\t*/\n\t\tnavigation : Object.freeze({\n\t\t\tget override() { return _navigationOverride; },\n\t\t\tset override(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.navigation.override must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_navigationOverride = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tPassages settings.\n\t\t*/\n\t\tpassages : Object.freeze({\n\t\t\tget descriptions() { return _passagesDescriptions; },\n\t\t\tset descriptions(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'Object' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.descriptions must be a boolean, object, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesDescriptions = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.updateTitle`.\n\t\t\tget displayTitles() { return _passagesDisplayTitles; },\n\t\t\tset displayTitles(value) { _passagesDisplayTitles = Boolean(value); },\n\n\t\t\tget nobr() { return _passagesNobr; },\n\t\t\tset nobr(value) { _passagesNobr = Boolean(value); },\n\n\t\t\tget onProcess() { return _passagesOnProcess; },\n\t\t\tset onProcess(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.onProcess must be a function or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesOnProcess = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.(start|startingPassage)`.\n\t\t\tget start() { return _passagesStart; },\n\t\t\tset start(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.start must be a string or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesStart = value;\n\t\t\t},\n\n\t\t\t// TODO: (v3) This should be under Navigation settings → `Config.navigation.transitionOut`.\n\t\t\tget transitionOut() { return _passagesTransitionOut; },\n\t\t\tset transitionOut(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'string'\n\t\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.passages.transitionOut must be a string, non-negative integer, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_passagesTransitionOut = value;\n\t\t\t}\n\t\t}),\n\n\t\t/*\n\t\t\tSaves settings.\n\t\t*/\n\t\tsaves : Object.freeze({\n\t\t\tget autoload() { return _savesAutoload; },\n\t\t\tset autoload(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\tif (valueType !== 'boolean' && valueType !== 'string' && valueType !== 'function') {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autoload must be a boolean, string, function, or null/undefined (received: ${valueType})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutoload = value;\n\t\t\t},\n\n\t\t\tget autosave() { return _savesAutosave; },\n\t\t\tset autosave(value) {\n\t\t\t\tif (value != null) { // lazy equality for null\n\t\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\t\t// legacy\n\t\t\t\t\t// Convert a string value to an Array of string.\n\t\t\t\t\tif (valueType === 'string') {\n\t\t\t\t\t\t_savesAutosave = [value];\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// /legacy\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t\t&& (valueType !== 'Array' || !value.every(item => typeof item === 'string'))\n\t\t\t\t\t\t&& valueType !== 'function'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new TypeError(`Config.saves.autosave must be a boolean, Array of strings, function, or null/undefined (received: ${valueType}${valueType === 'Array' ? ' of mixed' : ''})`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_savesAutosave = value;\n\t\t\t},\n\n\t\t\tget id() { return _savesId; },\n\t\t\tset id(value) {\n\t\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\t\tthrow new TypeError(`Config.saves.id must be a non-empty string (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesId = value;\n\t\t\t},\n\n\t\t\tget isAllowed() { return _savesIsAllowed; },\n\t\t\tset isAllowed(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.isAllowed must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesIsAllowed = value;\n\t\t\t},\n\n\t\t\tget onLoad() { return _savesOnLoad; },\n\t\t\tset onLoad(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onLoad must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnLoad = value;\n\t\t\t},\n\n\t\t\tget onSave() { return _savesOnSave; },\n\t\t\tset onSave(value) {\n\t\t\t\tif (!(value == null || value instanceof Function)) { // lazy equality for null\n\t\t\t\t\tthrow new TypeError(`Config.saves.onSave must be a function or null/undefined (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesOnSave = value;\n\t\t\t},\n\n\t\t\tget slots() { return _savesSlots; },\n\t\t\tset slots(value) {\n\t\t\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\t\t\tthrow new TypeError(`Config.saves.slots must be a non-negative integer (received: ${Util.getType(value)})`);\n\t\t\t\t}\n\n\t\t\t\t_savesSlots = value;\n\t\t\t},\n\n\t\t\tget version() { return _savesVersion; },\n\t\t\tset version(value) { _savesVersion = value; }\n\t\t}),\n\n\t\t/*\n\t\t\tUI settings.\n\t\t*/\n\t\tui : Object.freeze({\n\t\t\tget stowBarInitially() { return _uiStowBarInitially; },\n\t\t\tset stowBarInitially(value) {\n\t\t\t\tconst valueType = Util.getType(value);\n\n\t\t\t\tif (\n\t\t\t\t\t   valueType !== 'boolean'\n\t\t\t\t\t&& (valueType !== 'number' || !Number.isSafeInteger(value) || value < 0)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError(`Config.ui.stowBarInitially must be a boolean or non-negative integer (received: ${valueType})`);\n\t\t\t\t}\n\n\t\t\t\t_uiStowBarInitially = value;\n\t\t\t},\n\n\t\t\tget updateStoryElements() { return _uiUpdateStoryElements; },\n\t\t\tset updateStoryElements(value) { _uiUpdateStoryElements = Boolean(value); }\n\t\t})\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tsimpleaudio.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Has, LoadScreen, Story, Util, Visibility, clone */\n\nvar SimpleAudio = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*\n\t\tEvents that count as user activation—i.e. \"user gestures\", \"activation behavior\".\n\n\t\tNOTE (ca. Dec, 2018): This not an exhaustive list and varies significantly by browser.\n\t\tProposals for a specification/standard are still very much in flux at this point.\n\n\t\tTODO (ca. Dec, 2018): Revisit this topic.\n\n\t\tSEE: (too many to list)\n\t\t\thttps://github.com/whatwg/html/issues/3849\n\t\t\thttps://github.com/whatwg/html/issues/1903\n\t\t\thttps://html.spec.whatwg.org/#activation\n\t\t\thttps://docs.google.com/spreadsheets/d/1DGXjhQ6D3yZXIePOMo0dsd2agz0t5W7rYH1NwJ-QGJo/edit#gid=0\n\t*/\n\tconst _gestureEventNames = Object.freeze(['click', 'contextmenu', 'dblclick', 'keyup', 'mouseup', 'pointerup', 'touchend']);\n\n\t// Special group IDs.\n\tconst _specialIds = Object.freeze([':not', ':all', ':looped', ':muted', ':paused', ':playing']);\n\n\t// Format specifier regular expression.\n\tconst _formatSpecRe = /^([\\w-]+)\\s*\\|\\s*(\\S.*)$/; // e.g. 'mp3|https://audiohost.tld/id'\n\n\t// ID verification regular expressions.\n\tconst _badIdRe = /[:\\s]/;\n\n\t// Tracks collection.\n\tconst _tracks = new Map();\n\n\t// Groups collection.\n\tconst _groups = new Map();\n\n\t// Playlists collection.\n\tconst _lists = new Map();\n\n\t// Subscriber collection.\n\tconst _subscribers = new Map();\n\n\t// Master playback rate.\n\tlet _masterRate = 1;\n\n\t// Master playback volume.\n\tlet _masterVolume = 1;\n\n\t// Master mute state.\n\tlet _masterMute = false;\n\n\t// Master mute on tab/window visibility state.\n\tlet _masterMuteOnHidden = false;\n\n\n\t/*******************************************************************************************************************\n\t\tFeature Detection Functions.\n\t*******************************************************************************************************************/\n\t// Return whether the `<HTMLAudioElement>.play()` method returns a `Promise`.\n\t//\n\t// NOTE: The initial result is cached for future calls.\n\tconst _playReturnsPromise = (function () {\n\t\t// Cache of whether `<HTMLAudioElement>.play()` returns a `Promise`.\n\t\tlet _hasPromise = null;\n\n\t\tfunction _playReturnsPromise() {\n\t\t\tif (_hasPromise !== null) {\n\t\t\t\treturn _hasPromise;\n\t\t\t}\n\n\t\t\t_hasPromise = false;\n\n\t\t\tif (Has.audio) {\n\t\t\t\ttry {\n\t\t\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t\t\t// NOTE (ca. Jan 01, 2020): Firefox will still log an \"Autoplay is only allowed\n\t\t\t\t\t// when […] media is muted.\" message to the console when attempting the test\n\t\t\t\t\t// below, even though the audio has been muted.  Stay classy, Firefox.\n\t\t\t\t\t//\n\t\t\t\t\t// QUESTION (ca. Jan 01, 2020): Keep this?  It's only here to appease Firefox,\n\t\t\t\t\t// but doesn't seem to work as Firefox seems to ignore mute in violation of the\n\t\t\t\t\t// `HTMLAudioElement` specification—willfully or simply a bug, I can't say.\n\t\t\t\t\taudio.muted = true;\n\n\t\t\t\t\tconst value = audio.play();\n\n\t\t\t\t\t// Silence \"Uncaught (in promise)\" console errors from Blink.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: Swallowing errors is generally bad, but in this case we know there's\n\t\t\t\t\t// going to be an error regardless, since there's no source, and we don't actually\n\t\t\t\t\t// care about the error, since we just want the return value, so we consign it\n\t\t\t\t\t// to the bit bucket.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: We don't ensure that the return value is not `undefined` here because\n\t\t\t\t\t// having the attempted call to `<Promise>.catch()` on an `undefined` value throw\n\t\t\t\t\t// is acceptable, since it will be caught and `false` eventually returned.\n\t\t\t\t\tvalue.catch(() => { /* no-op */ });\n\n\t\t\t\t\t_hasPromise = value instanceof Promise;\n\t\t\t\t}\n\t\t\t\tcatch (ex) { /* no-op */ }\n\t\t\t}\n\n\t\t\treturn _hasPromise;\n\t\t}\n\n\t\treturn _playReturnsPromise;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tAudioTrack Class.\n\t*******************************************************************************************************************/\n\tclass AudioTrack {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of sources or AudioTrack object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioTrack) {\n\t\t\t\tthis._copy(obj);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('sources parameter must be either an array, of URIs or source objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(sourceList) {\n\t\t\tconst dataUriRe   = /^data:\\s*audio\\/(?:x-)?([^;,]+)\\s*[;,]/i;\n\t\t\tconst extRe       = /\\.([^./\\\\]+)$/;\n\t\t\tconst formats     = AudioTrack.formats;\n\t\t\tconst usedSources = [];\n\t\t\t/*\n\t\t\t\tHTMLAudioElement: DOM factory method vs. constructor\n\n\t\t\t\tUse of the DOM factory method, `document.createElement('audio')`, should be\n\t\t\t\tpreferred over use of the constructor, `new Audio()`.  The reason being that\n\t\t\t\tobjects created by the latter are, erroneously, treated differently, often\n\t\t\t\tunfavorably, by certain browser engines—e.g. within some versions of the iOS\n\t\t\t\tbrowser core.\n\n\t\t\t\tNotably, the only difference between the two, per the specification, is that\n\t\t\t\tobjects created via the constructor should have their `preload` property\n\t\t\t\tautomatically set to 'auto'.  Thus, there's no technical reason to prefer\n\t\t\t\tusage of the constructor, even discounting buggy browser implementations.\n\t\t\t*/\n\t\t\tconst audio = document.createElement('audio');\n\n\t\t\t// Initially set the `preload` attribute to `'none'`.\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Process the array of sources, adding any valid sources to the `usedSources`\n\t\t\t// array and to the audio element as source elements.\n\t\t\tsourceList.forEach(src => {\n\t\t\t\tlet srcUri = null;\n\n\t\t\t\tswitch (typeof src) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t{\n\t\t\t\t\t\tlet match;\n\n\t\t\t\t\t\tif (src.slice(0, 5) === 'data:') {\n\t\t\t\t\t\t\tmatch = dataUriRe.exec(src);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source data URI missing media type');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tmatch = extRe.exec(Util.parseUrl(src).pathname);\n\n\t\t\t\t\t\t\tif (match === null) {\n\t\t\t\t\t\t\t\tthrow new Error('source URL missing file extension');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (formats[match[1]]) {\n\t\t\t\t\t\t\tsrcUri = src;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\t{\n\t\t\t\t\t\tif (src === null) {\n\t\t\t\t\t\t\tthrow new Error('source object cannot be null');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('src')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"src\" property');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!src.hasOwnProperty('format')) {\n\t\t\t\t\t\t\tthrow new Error('source object missing required \"format\" property');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (formats[src.format]) {\n\t\t\t\t\t\t\tsrcUri = src.src;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`invalid source value (type: ${typeof src})`);\n\t\t\t\t}\n\n\t\t\t\tif (srcUri !== null) {\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src = srcUri;\n\t\t\t\t\taudio.appendChild(source);\n\t\t\t\t\tusedSources.push(srcUri);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (audio.hasChildNodes()) {\n\t\t\t\t// Set the `preload` attribute to `'metadata'`, unless preloading has been disabled.\n\t\t\t\tif (Config.audio.preloadMetadata) {\n\t\t\t\t\taudio.preload = 'metadata';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._finalize(audio, usedSources, clone(sourceList));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(\n\t\t\t\tobj.audio.cloneNode(true), // deep clone of the audio element & its children\n\t\t\t\tclone(obj.sources),\n\t\t\t\tclone(obj.originals)\n\t\t\t);\n\t\t}\n\n\t\t_finalize(audio, sources, originals) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\taudio : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : audio\n\t\t\t\t},\n\n\t\t\t\tsources : {\n\t\t\t\t\tvalue : Object.freeze(sources)\n\t\t\t\t},\n\n\t\t\t\toriginals : {\n\t\t\t\t\tvalue : Object.freeze(originals)\n\t\t\t\t},\n\n\t\t\t\t_error : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_faderId : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Set up event handlers on the audio and source elements.\n\t\t\tjQuery(this.audio)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving a `loadstart` event on the audio element, set `_error` to\n\t\t\t\t\t`false`.\n\t\t\t\t*/\n\t\t\t\t.on('loadstart.AudioTrack', () => this._error = false)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the audio element, set `_error` to\n\t\t\t\t\t`true`.\n\n\t\t\t\t\tCaveats by browser:\n\t\t\t\t\t\tEdge violates the specification by triggering `error` events from source\n\t\t\t\t\t\telements on their parent media element, rather than the source element.\n\t\t\t\t\t\tTo enable error handling in all browsers, we set the error handler on the\n\t\t\t\t\t\taudio element and have the final source element forward its `error` event.\n\n\t\t\t\t\t\tIE does not trigger, at least some, `error` events from source elements at\n\t\t\t\t\t\tall, not on the source element or its parent media element.  AFAIK, nothing\n\t\t\t\t\t\tcan be done about this lossage.\n\t\t\t\t*/\n\t\t\t\t.on('error.AudioTrack', () => this._error = true)\n\t\t\t\t/*\n\t\t\t\t\tUpon receiving an `error` event on the final source element (if any), trigger\n\t\t\t\t\tan `error` event on the audio element—that being necessary because the source\n\t\t\t\t\t`error` event does not bubble.\n\t\t\t\t*/\n\t\t\t\t.find('source:last-of-type')\n\t\t\t\t.on('error.AudioTrack', () => this._trigger('error'));\n\n\t\t\t// Subscribe to command messages.\n\t\t\tsubscribe(this, mesg => {\n\t\t\t\tif (!this.audio) {\n\t\t\t\t\tunsubscribe(this);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tswitch (mesg) {\n\t\t\t\tcase 'loadwithscreen':\n\t\t\t\t\tif (this.hasSource()) {\n\t\t\t\t\t\tconst lockId = LoadScreen.lock();\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t// NOTE: Do not use an arrow function here.\n\t\t\t\t\t\t\t.one(\n\t\t\t\t\t\t\t\t'canplaythrough.AudioTrack_loadwithscreen error.AudioTrack_loadwithscreen',\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\tjQuery(this).off('.AudioTrack_loadwithscreen');\n\t\t\t\t\t\t\t\t\tLoadScreen.unlock(lockId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.load();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'load':   this.load();               break;\n\t\t\t\tcase 'mute':   this._updateAudioMute();   break;\n\t\t\t\tcase 'rate':   this._updateAudioRate();   break;\n\t\t\t\tcase 'stop':   this.stop();               break;\n\t\t\t\tcase 'volume': this._updateAudioVolume(); break;\n\t\t\t\tcase 'unload': this.unload();             break;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Synchronize with the current master audio settings.\n\t\t\tthis._updateAudioMute();\n\t\t\tthis._updateAudioRate();\n\t\t\tthis._updateAudioVolume();\n\t\t}\n\n\t\t_trigger(eventName) {\n\t\t\t// Do not use `trigger()` here as we do not want these events to bubble.\n\t\t\tjQuery(this.audio).triggerHandler(eventName);\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.  That said, since the audio element contains\n\t\t\t\tdata buffers for the selected audio source, which may be quite large, manually\n\t\t\t\tpurging them as soon as we know that they're no longer needed is not a bad idea.\n\t\t\t*/\n\t\t\tunsubscribe(this);\n\n\t\t\tif (!this.audio) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(this.audio).off();\n\t\t\tthis.unload();\n\t\t\tthis._error = true;\n\n\t\t\t// Delete the audio element property.\n\t\t\tdelete this.audio;\n\t\t}\n\n\t\tclone() {\n\t\t\treturn new AudioTrack(this);\n\t\t}\n\n\t\tload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.audio.pause();\n\n\t\t\tif (!this.audio.hasChildNodes()) {\n\t\t\t\tif (this.sources.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.sources.forEach(srcUri => {\n\t\t\t\t\tconst source = document.createElement('source');\n\t\t\t\t\tsource.src = srcUri;\n\t\t\t\t\tthis.audio.appendChild(source);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tif (!this.isLoading()) {\n\t\t\t\tthis.audio.load();\n\t\t\t}\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.fadeStop();\n\t\t\tthis.stop();\n\n\t\t\tconst audio = this.audio;\n\t\t\taudio.preload = 'none';\n\n\t\t\t// Remove all source elements.\n\t\t\twhile (audio.hasChildNodes()) {\n\t\t\t\taudio.removeChild(audio.firstChild);\n\t\t\t}\n\n\t\t\t// Force the audio element to drop any existing data buffers.\n\t\t\taudio.load();\n\t\t}\n\n\t\tplay() {\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tif (this.audio.preload !== 'auto') {\n\t\t\t\tthis.audio.preload = 'auto';\n\t\t\t}\n\n\t\t\tconst namespace = '.AudioTrack_play';\n\n\t\t\treturn _playReturnsPromise()\n\t\t\t\t? this.audio.play()\n\t\t\t\t: new Promise((resolve, reject) => {\n\t\t\t\t\tif (this.isPlaying()) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t\t\t.off(namespace)\n\t\t\t\t\t\t\t.one(`error${namespace} playing${namespace} timeupdate${namespace}`, ev => {\n\t\t\t\t\t\t\t\tjQuery(this).off(namespace);\n\n\t\t\t\t\t\t\t\tif (ev.type === 'error') {\n\t\t\t\t\t\t\t\t\treject(new Error('unknown audio play error'));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.audio.play();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioTrack_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioTrack_playWhenAllowed');\n\t\t\t\t\tthis.audio.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tthis.audio.pause();\n\t\t}\n\n\t\tstop() {\n\t\t\tthis.audio.pause();\n\t\t\tthis.time(0);\n\t\t\tthis._trigger(':stopped');\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (!this.hasSource()) {\n\t\t\t\treturn Promise.reject(new Error('none of the candidate sources were acceptable'));\n\t\t\t}\n\n\t\t\tif (this.isUnloaded()) {\n\t\t\t\treturn Promise.reject(new Error('no sources are loaded'));\n\t\t\t}\n\n\t\t\tif (this.isFailed()) {\n\t\t\t\treturn Promise.reject(new Error('failed to load any of the sources'));\n\t\t\t}\n\n\t\t\tthis.fadeStop();\n\n\t\t\tconst from = Math.clamp(fromVol == null ? this.volume() : fromVol, 0, 1); // lazy equality for null\n\t\t\tconst to   = Math.clamp(toVol, 0, 1);\n\n\t\t\tif (from === to) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.volume(from);\n\n\t\t\t/*\n\t\t\t\tWe listen for the `timeupdate` event here, rather than `playing`, because\n\t\t\t\tvarious browsers (notably, mobile browsers) are poor at firing media events\n\t\t\t\tin a timely fashion, so we use `timeupdate` to ensure that we don't start\n\t\t\t\tthe fade until the track is actually progressing.\n\t\t\t*/\n\t\t\tjQuery(this.audio)\n\t\t\t\t.off('timeupdate.AudioTrack_fade')\n\t\t\t\t.one('timeupdate.AudioTrack_fade', () => {\n\t\t\t\t\tlet min;\n\t\t\t\t\tlet max;\n\n\t\t\t\t\t// Fade in.\n\t\t\t\t\tif (from < to) {\n\t\t\t\t\t\tmin = from;\n\t\t\t\t\t\tmax = to;\n\t\t\t\t\t}\n\t\t\t\t\t// Fade out.\n\t\t\t\t\telse {\n\t\t\t\t\t\tmin = to;\n\t\t\t\t\t\tmax = from;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst time     = Math.max(duration, 1);\n\t\t\t\t\tconst interval = 25; // in milliseconds\n\t\t\t\t\tconst delta    = (to - from) / (time / (interval / 1000));\n\n\t\t\t\t\tthis._trigger(':fading');\n\t\t\t\t\tthis._faderId = setInterval(() => {\n\t\t\t\t\t\tif (!this.isPlaying()) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWhile it may seem like a good idea to also set the track volume\n\t\t\t\t\t\t\t\tto the `to` value here, we should not do so.  We cannot know why\n\t\t\t\t\t\t\t\tthe track is no longer playing, nor if the volume has been modified\n\t\t\t\t\t\t\t\tin the interim, so doing so now may clobber an end-user set volume.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.volume(Math.clamp(this.volume() + delta, min, max));\n\n\t\t\t\t\t\tif (Config.audio.pauseOnFadeToZero && this.volume() === 0) {\n\t\t\t\t\t\t\tthis.pause();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.volume() === to) {\n\t\t\t\t\t\t\tthis.fadeStop();\n\t\t\t\t\t\t\tthis._trigger(':faded');\n\t\t\t\t\t\t}\n\t\t\t\t\t}, interval);\n\t\t\t\t});\n\n\t\t\treturn this.play();\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this._faderId !== null) {\n\t\t\t\tclearInterval(this._faderId);\n\t\t\t\tthis._faderId = null;\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this.audio.loop;\n\t\t\t}\n\n\t\t\tthis.audio.loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\t\t\tthis._updateAudioMute();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioMute() {\n\t\t\tthis.audio.muted = this._mute || _masterMute;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tClamp the playback rate to sane values—some browsers also do this to varying degrees.\n\n\t\t\t\tNOTE (ca. Aug 2016): The specification allows negative values for reverse playback,\n\t\t\t\thowever, most browsers either completely ignore negative values or clamp them to\n\t\t\t\tsome positive value.  In some (notably, IE & Edge), setting a negative playback\n\t\t\t\trate breaks the associated controls, if displayed.\n\t\t\t*/\n\t\t\t/*\n\t\t\tthis._rate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\t\tthis._updateAudioRate();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioRate() {\n\t\t\t/*\n\t\t\tconst rate = this._rate * _masterRate;\n\t\t\tthis.audio.playbackRate = rate < 0\n\t\t\t\t? Math.clamp(rate, -0.2, -5) // clamp to 5× slower & faster, backward\n\t\t\t\t: Math.clamp(rate, 0.2, 5);  // clamp to 5× slower & faster, forward\n\t\t\t*/\n\t\t\tthis.audio.playbackRate = Math.clamp(this._rate * _masterRate, 0.2, 5); // clamp to 5× slower & faster\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\treturn this.audio.currentTime;\n\t\t\t}\n\n\t\t\tif (typeof time !== 'number') {\n\t\t\t\tthrow new TypeError('time parameter must be a number');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tNOTE (historic): If we try to modify the audio clip's `.currentTime` property\n\t\t\t\tbefore its metadata has been loaded, it will throw an `InvalidStateError`\n\t\t\t\t(since it doesn't know its duration, allowing `.currentTime` to be set would\n\t\t\t\tbe undefined behavior), so in case an exception is thrown we provide a fallback\n\t\t\t\tusing the `loadedmetadata` event.\n\n\t\t\t\tNOTE (ca. 2016): This workaround should no longer be necessary in most browsers.\n\t\t\t\tThat said, it will still be required for some time to service legacy browsers.\n\n\t\t\t\tNOTE (ca. Dec 09, 2018): Firefox will still log an `InvalidStateError` to the\n\t\t\t\tconsole when attempting to modify the clip's `.currentTime` property before its\n\t\t\t\tmetadata has been loaded, even though it handles the situation properly—by waiting\n\t\t\t\tfor the metadata, as all browsers do now.  To prevent this spurious logging, we\n\t\t\t\tmust now manually check for the existence of the metadata and always failover to\n\t\t\t\tan event regardless of if the browser needs it or not—because I don't want to\n\t\t\t\tintroduce a browser check here.  Stay classy, Firefox.\n\t\t\t*/\n\t\t\tif (this.hasMetadata()) {\n\t\t\t\tthis.audio.currentTime = time;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(this.audio)\n\t\t\t\t\t.off('loadedmetadata.AudioTrack_time')\n\t\t\t\t\t.one('loadedmetadata.AudioTrack_time', () => this.audio.currentTime = time);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\t\tthis._updateAudioVolume();\n\n\t\t\treturn this;\n\t\t}\n\t\t_updateAudioVolume() {\n\t\t\tthis.audio.volume = Math.clamp(this._volume * _masterVolume, 0, 1);\n\t\t}\n\n\t\tduration() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration;\n\t\t}\n\n\t\tremaining() {\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.audio.duration - this.audio.currentTime;\n\t\t}\n\n\t\tisFailed() {\n\t\t\treturn this._error;\n\t\t}\n\n\t\tisLoading() {\n\t\t\treturn this.audio.networkState === HTMLMediaElement.NETWORK_LOADING;\n\t\t}\n\n\t\tisUnloaded() {\n\t\t\treturn !this.audio.hasChildNodes();\n\t\t}\n\n\t\tisUnavailable() {\n\t\t\treturn !this.hasSource() || this.isUnloaded() || this.isFailed();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\t// NOTE: The `this.hasSomeData()` check is probably no longer necessary.\n\t\t\treturn !this.audio.paused && this.hasSomeData();\n\t\t}\n\n\t\tisPaused() {\n\t\t\t/*\n\t\t\t\tIf the selected audio resource is a stream, `currentTime` may return a non-zero\n\t\t\t\tvalue even at the earliest available position within the stream as the browser\n\t\t\t\tmay have dropped the earliest chunks of buffered data or the stream may have a\n\t\t\t\ttimeline which does not start at zero.\n\n\t\t\t\tIn an attempt to guard against these possiblities, as best as we can, we test\n\t\t\t\t`duration` against `Infinity` first, which should yield true for actual streams.\n\t\t\t*/\n\t\t\treturn this.audio.paused\n\t\t\t\t&& (this.audio.duration === Infinity || this.audio.currentTime > 0)\n\t\t\t\t&& !this.audio.ended;\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.audio.paused && this.audio.currentTime === 0;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.audio.ended;\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this._faderId !== null;\n\t\t}\n\n\t\tisSeeking() {\n\t\t\treturn this.audio.seeking;\n\t\t}\n\n\t\thasSource() {\n\t\t\treturn this.sources.length > 0;\n\t\t}\n\n\t\thasNoData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_NOTHING;\n\t\t}\n\n\t\thasMetadata() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_METADATA;\n\t\t}\n\n\t\thasSomeData() {\n\t\t\treturn this.audio.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA;\n\t\t}\n\n\t\thasData() {\n\t\t\treturn this.audio.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tjQuery.fn.on.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tjQuery.fn.one.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tjQuery.fn.off.apply(jQuery(this.audio), args);\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t// Attach the static data members.\n\tObject.defineProperties(AudioTrack, {\n\t\t/*\n\t\t\tCache of supported (common) audio formats.\n\n\t\t\tNOTE: Caveats by browser/engine:\n\t\t\t\tOpera ≤12 (Presto) will return a false-negative if the codecs value is quoted\n\t\t\t\twith single quotes, requiring the use of either double quotes or no quotes.\n\n\t\t\t\tSome versions of Blink-based browsers (e.g. Chrome, Opera ≥15) will return a\n\t\t\t\tfalse-negative for WAVE audio if the preferred MIME-type of 'audio/wave' is\n\t\t\t\tspecified, requiring the addition of 'audio/wav' for them.\n\t\t*/\n\t\tformats : {\n\t\t\tvalue : (() => {\n\t\t\t\tconst audio = document.createElement('audio');\n\t\t\t\tconst types = new Map();\n\n\t\t\t\tfunction canPlay(mimeType) {\n\t\t\t\t\tif (!types.has(mimeType)) {\n\t\t\t\t\t\t// Some early implementations return 'no' instead of the empty string.\n\t\t\t\t\t\ttypes.set(mimeType, audio.canPlayType(mimeType).replace(/^no$/i, '') !== '');\n\t\t\t\t\t}\n\n\t\t\t\t\treturn types.get(mimeType);\n\t\t\t\t}\n\n\t\t\t\treturn Object.assign(Object.create(null), {\n\t\t\t\t\t// AAC — MPEG-2 AAC audio; specific profiles vary, but commonly \"AAC-LC\".\n\t\t\t\t\taac : canPlay('audio/aac'),\n\n\t\t\t\t\t// CAF — Codecs vary.\n\t\t\t\t\tcaf : canPlay('audio/x-caf') || canPlay('audio/caf'),\n\n\t\t\t\t\t// FLAC.\n\t\t\t\t\tflac : canPlay('audio/x-flac') || canPlay('audio/flac'),\n\n\t\t\t\t\t// MP3 — MPEG-1/-2 Layer-III audio.\n\t\t\t\t\tmp3  : canPlay('audio/mpeg; codecs=\"mp3\"') || canPlay('audio/mpeg') || canPlay('audio/mp3') || canPlay('audio/mpa'),\n\t\t\t\t\tmpeg : canPlay('audio/mpeg'),\n\n\t\t\t\t\t// MP4 — Codecs vary, but commonly \"mp4a.40.2\" (a.k.a. \"AAC-LC\").\n\t\t\t\t\tm4a : canPlay('audio/x-m4a') || canPlay('audio/m4a') || canPlay('audio/aac'),\n\t\t\t\t\tmp4 : canPlay('audio/x-mp4') || canPlay('audio/mp4') || canPlay('audio/aac'),\n\n\t\t\t\t\t// OGG — Codecs vary, but commonly \"vorbis\" and, more recently, \"opus\".\n\t\t\t\t\togg : canPlay('audio/ogg'),\n\t\t\t\t\toga : canPlay('audio/ogg'),\n\n\t\t\t\t\t// OPUS — Opus audio in an Ogg container.\n\t\t\t\t\topus : canPlay('audio/ogg; codecs=\"opus\"') || canPlay('audio/opus'),\n\n\t\t\t\t\t// WAVE — Codecs vary, but commonly \"1\" (1 is the FourCC for PCM/LPCM).\n\t\t\t\t\twav  : canPlay('audio/wave; codecs=\"1\"') || canPlay('audio/wav; codecs=\"1\"') || canPlay('audio/wave') || canPlay('audio/wav'),\n\t\t\t\t\twave : canPlay('audio/wave; codecs=\"1\"') || canPlay('audio/wav; codecs=\"1\"') || canPlay('audio/wave') || canPlay('audio/wav'),\n\n\t\t\t\t\t// WEBM — Codecs vary, but commonly \"vorbis\" and, more recently, \"opus\".\n\t\t\t\t\tweba : canPlay('audio/webm'),\n\t\t\t\t\twebm : canPlay('audio/webm')\n\t\t\t\t});\n\t\t\t})()\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudioList Class.\n\t*******************************************************************************************************************/\n\tclass AudioList {\n\t\tconstructor(obj) {\n\t\t\t// Process the given array of track objects or AudioList object.\n\t\t\tif (obj instanceof Array) {\n\t\t\t\tthis._create(obj);\n\t\t\t}\n\t\t\telse if (obj instanceof AudioList) {\n\t\t\t\tthis._copy(obj);\n\t\t\t\t// this._create(obj.tracks);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('tracks parameter must be either an array, of track objects, or an AudioTrack instance');\n\t\t\t}\n\t\t}\n\n\t\t_create(trackList) {\n\t\t\t// Map the array of tracks to playlist track objects.\n\t\t\tthis._finalize(trackList.map(trackObj => {\n\t\t\t\tif (typeof trackObj !== 'object') { // lazy equality for null\n\t\t\t\t\tthrow new Error('tracks parameter array members must be objects');\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\tlet rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (trackObj instanceof AudioTrack) {\n\t\t\t\t\town    = true;\n\t\t\t\t\trate   = trackObj.rate();\n\t\t\t\t\ttrack  = trackObj.clone();\n\t\t\t\t\tvolume = trackObj.volume();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (!trackObj.hasOwnProperty('track')) {\n\t\t\t\t\t\tthrow new Error('track object missing required \"track\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (!(trackObj.track instanceof AudioTrack)) {\n\t\t\t\t\t\tthrow new Error('track object\\'s \"track\" property must be an AudioTrack object');\n\t\t\t\t\t}\n\t\t\t\t\t// else if (!trackObj.hasOwnProperty('volume')) {\n\t\t\t\t\t// \tthrow new Error('track object missing required \"volume\" property');\n\t\t\t\t\t// }\n\n\t\t\t\t\town    = trackObj.hasOwnProperty('own') && trackObj.own;\n\t\t\t\t\trate   = trackObj.hasOwnProperty('rate') ? trackObj.rate : trackObj.track.rate();\n\t\t\t\t\ttrack  = trackObj.track;\n\t\t\t\t\tvolume = trackObj.hasOwnProperty('volume') ? trackObj.volume : trackObj.track.volume();\n\t\t\t\t}\n\n\t\t\t\ttrack.stop();\n\t\t\t\ttrack.loop(false);\n\t\t\t\ttrack.mute(false);\n\t\t\t\ttrack.rate(rate);\n\t\t\t\ttrack.volume(volume);\n\t\t\t\ttrack.on('ended.AudioList', () => this._onEnd());\n\n\t\t\t\treturn { own, track, volume, rate };\n\t\t\t}));\n\t\t}\n\n\t\t_copy(obj) {\n\t\t\tthis._finalize(clone(obj.tracks));\n\t\t}\n\n\t\t_finalize(tracks) {\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttracks : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : Object.freeze(tracks)\n\t\t\t\t},\n\n\t\t\t\tqueue : {\n\t\t\t\t\tconfigurable : true,\n\t\t\t\t\tvalue        : []\n\t\t\t\t},\n\n\t\t\t\tcurrent : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_rate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_volume : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 1\n\t\t\t\t},\n\n\t\t\t\t_mute : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_loop : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t},\n\n\t\t\t\t_shuffle : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : false\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t_destroy() {\n\t\t\t/*\n\t\t\t\tStrictly speaking, self-destruction is not necessary as this object will,\n\t\t\t\teventually, be garbage collected.\n\t\t\t*/\n\t\t\t// Stop playback.\n\t\t\tthis.stop();\n\n\t\t\t// Destroy all owned tracks.\n\t\t\tthis.tracks\n\t\t\t\t.filter(trackObj => trackObj.own)\n\t\t\t\t.forEach(trackObj => trackObj.track._destroy());\n\n\t\t\t// Delete the reference-type properties.\n\t\t\tdelete this.tracks;\n\t\t\tdelete this.queue;\n\t\t}\n\n\t\tload() {\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.load());\n\t\t}\n\n\t\tunload() {\n\t\t\tthis.stop();\n\t\t\tthis.tracks.forEach(trackObj => trackObj.track.unload());\n\t\t}\n\n\t\tplay() {\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (this.queue.length === 0) {\n\t\t\t\t\tthis._fillQueue();\n\t\t\t\t}\n\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn Promise.reject(new Error('no tracks were available'));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.current.track.play();\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tthis.play().catch(() => {\n\t\t\t\tconst gestures = _gestureEventNames.map(name => `${name}.AudioList_playWhenAllowed`).join(' ');\n\t\t\t\tjQuery(document).one(gestures, () => {\n\t\t\t\t\tjQuery(document).off('.AudioList_playWhenAllowed');\n\t\t\t\t\tthis.play();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tpause() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.pause();\n\t\t\t}\n\t\t}\n\n\t\tstop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tthis._drainQueue();\n\t\t}\n\n\t\tskip() {\n\t\t\tif (this._next()) {\n\t\t\t\tthis.current.track.play();\n\t\t\t}\n\t\t\telse if (this._loop) {\n\t\t\t\tthis.play();\n\t\t\t}\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (typeof duration !== 'number') {\n\t\t\t\tthrow new TypeError('duration parameter must be a number');\n\t\t\t}\n\t\t\tif (typeof toVol !== 'number') {\n\t\t\t\tthrow new TypeError('toVolume parameter must be a number');\n\t\t\t}\n\t\t\tif (fromVol != null && typeof fromVol !== 'number') { // lazy equality for null\n\t\t\t\tthrow new TypeError('fromVolume parameter must be a number');\n\t\t\t}\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (this.current === null || this.current.track.isUnavailable() || this.current.track.isEnded()) {\n\t\t\t\tif (!this._next()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adjToVol = Math.clamp(toVol, 0, 1) * this.current.volume;\n\t\t\tlet adjFromVol;\n\n\t\t\tif (fromVol != null) { // lazy equality for null\n\t\t\t\tadjFromVol = Math.clamp(fromVol, 0, 1) * this.current.volume;\n\t\t\t}\n\n\t\t\tthis._volume = toVol; // NOTE: Kludgey, but necessary.\n\n\t\t\treturn this.current.track.fade(duration, adjToVol, adjFromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\treturn this.fade(duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\treturn this.fade(duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.fadeStop();\n\t\t\t}\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\treturn this._loop;\n\t\t\t}\n\n\t\t\tthis._loop = !!loop;\n\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\treturn this._mute;\n\t\t\t}\n\n\t\t\tthis._mute = !!mute;\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.mute(this._mute);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\treturn this._rate;\n\t\t\t}\n\n\t\t\tif (typeof rate !== 'number') {\n\t\t\t\tthrow new TypeError('rate parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._rate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tshuffle(shuffle) {\n\t\t\tif (shuffle == null) { // lazy equality for null\n\t\t\t\treturn this._shuffle;\n\t\t\t}\n\n\t\t\tthis._shuffle = !!shuffle;\n\n\t\t\tif (this.queue.length > 0) {\n\t\t\t\tthis._fillQueue();\n\n\t\t\t\t// Try not to immediately replay the last track when not shuffling.\n\t\t\t\tif (!this._shuffle && this.current !== null && this.queue.length > 1) {\n\t\t\t\t\tconst firstIdx = this.queue.findIndex(trackObj => trackObj === this.current);\n\n\t\t\t\t\tif (firstIdx !== -1) {\n\t\t\t\t\t\tthis.queue.push(...this.queue.splice(0, firstIdx + 1));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\treturn this._volume;\n\t\t\t}\n\n\t\t\tif (typeof volume !== 'number') {\n\t\t\t\tthrow new TypeError('volume parameter must be a number');\n\t\t\t}\n\n\t\t\tthis._volume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tduration() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('duration takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\treturn this.tracks\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\t\t}\n\n\t\tremaining() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('remaining takes no parameters');\n\t\t\t}\n\n\t\t\t// NOTE: May return a double (normally), Infinity (for streams), or NaN (without metadata).\n\t\t\tlet remainingTime = this.queue\n\t\t\t\t.map(trackObj => trackObj.track.duration())\n\t\t\t\t.reduce((prev, cur) => prev + cur, 0);\n\n\t\t\tif (this.current !== null) {\n\t\t\t\tremainingTime += this.current.track.remaining();\n\t\t\t}\n\n\t\t\treturn remainingTime;\n\t\t}\n\n\t\ttime() {\n\t\t\tif (arguments.length > 0) {\n\t\t\t\tthrow new Error('time takes no parameters');\n\t\t\t}\n\n\t\t\treturn this.duration() - this.remaining();\n\t\t}\n\n\t\tisPlaying() {\n\t\t\treturn this.current !== null && this.current.track.isPlaying();\n\t\t}\n\n\t\tisPaused() {\n\t\t\treturn this.current === null || this.current.track.isPaused();\n\t\t}\n\n\t\tisStopped() {\n\t\t\treturn this.queue.length === 0 && this.current === null;\n\t\t}\n\n\t\tisEnded() {\n\t\t\treturn this.queue.length === 0 && (this.current === null || this.current.track.isEnded());\n\t\t}\n\n\t\tisFading() {\n\t\t\treturn this.current !== null && this.current.track.isFading();\n\t\t}\n\n\t\t_next() {\n\t\t\tif (this.current !== null) {\n\t\t\t\tthis.current.track.stop();\n\t\t\t\tthis.current = null;\n\t\t\t}\n\n\t\t\tlet nextTrack;\n\n\t\t\twhile ((nextTrack = this.queue.shift())) {\n\t\t\t\tif (!nextTrack.track.isUnavailable()) {\n\t\t\t\t\tthis.current = nextTrack;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.current === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.current.track.mute(this._mute);\n\t\t\tthis.current.track.rate(this._rate * this.current.rate);\n\t\t\tthis.current.track.volume(this._volume * this.current.volume);\n\n\t\t\t// Attempt to protect against the `loop` state being reenabled\n\t\t\t// outside of the playlist.  Mostly for unowned tracks.\n\t\t\t//\n\t\t\t// TODO: Should we reapply the `ended` event handler too?\n\t\t\tthis.current.track.loop(false);\n\n\t\t\treturn true;\n\t\t}\n\n\t\t_onEnd() {\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\tif (!this._loop) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._fillQueue();\n\t\t\t}\n\n\t\t\tif (!this._next()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.current.track.play();\n\t\t}\n\n\t\t_drainQueue() {\n\t\t\tthis.queue.splice(0);\n\t\t}\n\n\t\t_fillQueue() {\n\t\t\tthis._drainQueue();\n\t\t\tthis.queue.push(...this.tracks.filter(trackObj => !trackObj.track.isUnavailable()));\n\n\t\t\tif (this.queue.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this._shuffle) {\n\t\t\t\tthis.queue.shuffle();\n\n\t\t\t\t// Try not to immediately replay the last track when shuffling.\n\t\t\t\tif (this.queue.length > 1 && this.queue[0] === this.current) {\n\t\t\t\t\tthis.queue.push(this.queue.shift());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tAudioRunner Class.\n\t*******************************************************************************************************************/\n\tclass AudioRunner {\n\t\tconstructor(list) {\n\t\t\tif (!(list instanceof Set || list instanceof AudioRunner)) {\n\t\t\t\tthrow new TypeError('list parameter must be a Set or a AudioRunner instance');\n\t\t\t}\n\n\t\t\t// Set up our own properties.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\ttrackIds : {\n\t\t\t\t\tvalue : new Set(list instanceof AudioRunner ? list.trackIds : list)\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.load);\n\t\t}\n\n\t\tunload() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.unload);\n\t\t}\n\n\t\tplay() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.play);\n\t\t}\n\n\t\tplayWhenAllowed() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.playWhenAllowed);\n\t\t}\n\n\t\tpause() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.pause);\n\t\t}\n\n\t\tstop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.stop);\n\t\t}\n\n\t\tfade(duration, toVol, fromVol) {\n\t\t\tif (duration == null || toVol == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fade requires parameters');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fade, duration, toVol, fromVol);\n\t\t}\n\n\t\tfadeIn(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeIn requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeIn, duration, 1, fromVol);\n\t\t}\n\n\t\tfadeOut(duration, fromVol) {\n\t\t\tif (duration == null) { // lazy equality for null\n\t\t\t\tthrow new Error('fadeOut requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeOut, duration, 0, fromVol);\n\t\t}\n\n\t\tfadeStop() {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.fadeStop);\n\t\t}\n\n\t\tloop(loop) {\n\t\t\tif (loop == null) { // lazy equality for null\n\t\t\t\tthrow new Error('loop requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.loop, loop);\n\t\t\treturn this;\n\t\t}\n\n\t\tmute(mute) {\n\t\t\tif (mute == null) { // lazy equality for null\n\t\t\t\tthrow new Error('mute requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.mute, mute);\n\t\t\treturn this;\n\t\t}\n\n\t\trate(rate) {\n\t\t\tif (rate == null) { // lazy equality for null\n\t\t\t\tthrow new Error('rate requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.rate, rate);\n\t\t\treturn this;\n\t\t}\n\n\t\ttime(time) {\n\t\t\tif (time == null) { // lazy equality for null\n\t\t\t\tthrow new Error('time requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.time, time);\n\t\t\treturn this;\n\t\t}\n\n\t\tvolume(volume) {\n\t\t\tif (volume == null) { // lazy equality for null\n\t\t\t\tthrow new Error('volume requires a parameter');\n\t\t\t}\n\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.volume, volume);\n\t\t\treturn this;\n\t\t}\n\n\t\ton(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.on, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tone(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.one, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\toff(...args) {\n\t\t\tAudioRunner._run(this.trackIds, AudioTrack.prototype.off, ...args);\n\t\t\treturn this;\n\t\t}\n\n\t\tstatic _run(ids, fn, ...args) {\n\t\t\tids.forEach(id => {\n\t\t\t\tconst track = _tracks.get(id);\n\n\t\t\t\tif (track) {\n\t\t\t\t\tfn.apply(track, args);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTrack Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.tracks.add(trackId, sources…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.tracks.add(\n\t\t\t\t'over_the_top',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.mp3',\n\t\t\t\t'https://audiohost.tld/id/over_the_top.ogg'\n\t\t\t);\n\t*/\n\tfunction trackAdd(/* trackId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('track ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('sources'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `track ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\tthrow new Error(`invalid ${what}: track IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst sources = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet track;\n\n\t\ttry {\n\t\t\ttrack = _newTrack(sources);\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during track initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If in Test Mode and no supported sources were specified, throw an error.\n\t\tif (Config.debug && !track.hasSource()) {\n\t\t\tthrow new Error(`${what}: no supported audio sources found`);\n\t\t}\n\n\t\t// If a track by the given ID already exists, destroy it.\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// Add the track to the cache.\n\t\t_tracks.set(id, track);\n\t}\n\n\tfunction trackDelete(id) {\n\t\tif (_tracks.has(id)) {\n\t\t\t_tracks.get(id)._destroy();\n\t\t}\n\n\t\t// TODO: Should this also remove references to the track from groups and playlists?\n\n\t\treturn _tracks.delete(id);\n\t}\n\n\tfunction trackClear() {\n\t\t_tracks.forEach(track => track._destroy());\n\t\t_tracks.clear();\n\t}\n\n\tfunction trackHas(id) {\n\t\treturn _tracks.has(id);\n\t}\n\n\tfunction trackGet(id) {\n\t\treturn _tracks.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tGroup Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.groups.add(groupId, trackIds…);\n\n\t\tE.g.\n\t\t\tSimpleAudio.groups.add(':ui', 'beep', 'boop', 'boing');\n\t*/\n\tfunction groupAdd(/* groupId , trackIds… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('group ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `group ID \"${id}\"`;\n\n\t\tif (id[0] !== ':' || _badIdRe.test(id.slice(1))) {\n\t\t\tthrow new Error(`invalid ${what}: group IDs must start with a colon and must not contain colons or whitespace`);\n\t\t}\n\n\t\tif (_specialIds.includes(id)) {\n\t\t\tthrow new Error(`cannot clobber special ${what}`);\n\t\t}\n\n\t\tconst trackIds = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet group;\n\n\t\ttry {\n\t\t\tgroup = new Set(trackIds.map(trackId => {\n\t\t\t\tif (!_tracks.has(trackId)) {\n\t\t\t\t\tthrow new Error(`track \"${trackId}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\treturn trackId;\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during group initialization: ${ex.message}`);\n\t\t}\n\n\t\t// Add the group to the cache.\n\t\t_groups.set(id, Object.freeze(Array.from(group)));\n\t}\n\n\tfunction groupDelete(id) {\n\t\treturn _groups.delete(id);\n\t}\n\n\tfunction groupClear() {\n\t\t_groups.clear();\n\t}\n\n\tfunction groupHas(id) {\n\t\treturn _groups.has(id);\n\t}\n\n\tfunction groupGet(id) {\n\t\treturn _groups.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPlaylist Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tSimpleAudio.lists.add(listId, sources…);\n\t\t\tWhere `sources` may be either a track ID or descriptor (object).\n\t\t\tTrack descriptors are either { id, [own], [rate], [volume] } or { sources, [rate], [volume] }.\n\n\t\tNOTE: Rate properties are currently unsupported due to poor browser support.\n\n\t\tE.g.\n\t\t\tSimpleAudio.lists.add(\n\t\t\t\t'bgm',\n\t\t\t\t'over_the_top',\n\t\t\t\t{\n\t\t\t\t\tid     : 'heavens_a_lie',\n\t\t\t\t\tvolume : 0.5,\n\t\t\t\t\town    : true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tsources : [\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.mp3',\n\t\t\t\t\t\t'https://audiohost.tld/id/swamped.ogg'\n\t\t\t\t\t],\n\t\t\t\t\tvolume  : 0.75\n\t\t\t\t}\n\t\t\t);\n\t*/\n\tfunction listAdd(/* listId , sources… */) {\n\t\tif (arguments.length < 2) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('list ID'); }\n\t\t\tif (arguments.length < 2) { errors.push('track IDs'); }\n\t\t\tthrow new Error(`no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tconst id   = String(arguments[0]).trim();\n\t\tconst what = `list ID \"${id}\"`;\n\n\t\tif (_badIdRe.test(id)) {\n\t\t\treturn this.error(`invalid ${what}: list IDs must not contain colons or whitespace`);\n\t\t}\n\n\t\tconst descriptors = Array.isArray(arguments[1])\n\t\t\t? Array.from(arguments[1])\n\t\t\t: Array.from(arguments).slice(1);\n\t\tlet list;\n\n\t\ttry {\n\t\t\tlist = new AudioList(descriptors.map(desc => {\n\t\t\t\tif (desc === null) {\n\t\t\t\t\tthrow new Error('track descriptor must be a string or object (type: null)');\n\t\t\t\t}\n\n\t\t\t\tswitch (typeof desc) {\n\t\t\t\tcase 'string':\n\t\t\t\t\t// Simply a track ID, so convert it into an object.\n\t\t\t\t\tdesc = { id : desc }; // eslint-disable-line no-param-reassign\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (!desc.hasOwnProperty('id') && !desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain one of either an \"id\" or a \"sources\" property');\n\t\t\t\t\t}\n\t\t\t\t\telse if (desc.hasOwnProperty('id') && desc.hasOwnProperty('sources')) {\n\t\t\t\t\t\tthrow new Error('track descriptor must contain either an \"id\" or a \"sources\" property, not both');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`track descriptor must be a string or object (type: ${typeof desc})`);\n\t\t\t\t}\n\n\t\t\t\tlet own;\n\t\t\t\t// let rate;\n\t\t\t\tlet track;\n\t\t\t\tlet volume;\n\n\t\t\t\tif (desc.hasOwnProperty('id')) {\n\t\t\t\t\tif (typeof desc.id !== 'string') {\n\t\t\t\t\t\tthrow new Error('\"id\" property must be a string');\n\t\t\t\t\t}\n\t\t\t\t\tif (!_tracks.has(desc.id)) {\n\t\t\t\t\t\tthrow new Error(`track \"${desc.id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\ttrack = _tracks.get(desc.id);\n\t\t\t\t}\n\t\t\t\telse if (desc.hasOwnProperty('sources')) {\n\t\t\t\t\tif (!Array.isArray(desc.sources) || desc.sources.length === 0) {\n\t\t\t\t\t\tthrow new Error('\"sources\" property must be a non-empty array');\n\t\t\t\t\t}\n\t\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\t\tthrow new Error('\"own\" property is not allowed with the \"sources\" property');\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttrack = _newTrack(desc.sources);\n\t\t\t\t\t\town = true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`error during track initialization: ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\t\tif (Config.debug && !track.hasSource()) {\n\t\t\t\t\t\tthrow new Error('no supported audio sources found');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (desc.hasOwnProperty('own')) {\n\t\t\t\t\tif (typeof desc.own !== 'boolean') {\n\t\t\t\t\t\tthrow new Error('\"own\" property must be a boolean');\n\t\t\t\t\t}\n\n\t\t\t\t\town = desc.own;\n\n\t\t\t\t\tif (own) {\n\t\t\t\t\t\ttrack = track.clone();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if (desc.hasOwnProperty('rate')) {\n\t\t\t\t// \tif (\n\t\t\t\t// \t\t   typeof desc.rate !== 'number'\n\t\t\t\t// \t\t|| Number.isNaN(desc.rate)\n\t\t\t\t// \t\t|| !Number.isFinite(desc.rate)\n\t\t\t\t// \t) {\n\t\t\t\t// \t\tthrow new Error('\"rate\" property must be a finite number');\n\t\t\t\t// \t}\n\t\t\t\t//\n\t\t\t\t// \trate = desc.rate;\n\t\t\t\t// }\n\n\t\t\t\tif (desc.hasOwnProperty('volume')) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t   typeof desc.volume !== 'number'\n\t\t\t\t\t\t|| Number.isNaN(desc.volume)\n\t\t\t\t\t\t|| !Number.isFinite(desc.volume)\n\t\t\t\t\t\t|| desc.volume < 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error('\"volume\" property must be a non-negative finite number');\n\t\t\t\t\t}\n\n\t\t\t\t\tvolume = desc.volume;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\town    : own != null ? own : false, // lazy equality for null,\n\t\t\t\t\t// rate   : rate != null ? rate : track.rate(), // lazy equality for null,\n\t\t\t\t\ttrack,\n\t\t\t\t\tvolume : volume != null ? volume : track.volume() // lazy equality for null\n\t\t\t\t};\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`${what}: error during playlist initialization: ${ex.message}`);\n\t\t}\n\n\t\t// If a playlist by the given ID already exists, destroy it.\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\t// Add the playlist to the cache.\n\t\t_lists.set(id, list);\n\t}\n\n\tfunction listDelete(id) {\n\t\tif (_lists.has(id)) {\n\t\t\t_lists.get(id)._destroy();\n\t\t}\n\n\t\treturn _lists.delete(id);\n\t}\n\n\tfunction listClear() {\n\t\t_lists.forEach(list => list._destroy());\n\t\t_lists.clear();\n\t}\n\n\tfunction listHas(id) {\n\t\treturn _lists.has(id);\n\t}\n\n\tfunction listGet(id) {\n\t\treturn _lists.get(id) || null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tRunner Functions.\n\t*******************************************************************************************************************/\n\tconst _runnerParseSelector = (() => {\n\t\tconst notWsRe = /\\S/g;\n\t\tconst parenRe = /[()]/g;\n\n\t\tfunction processNegation(str, startPos) {\n\t\t\tlet match;\n\n\t\t\tnotWsRe.lastIndex = startPos;\n\t\t\tmatch = notWsRe.exec(str);\n\n\t\t\tif (match === null || match[0] !== '(') {\n\t\t\t\tthrow new Error('invalid \":not()\" syntax: missing parentheticals');\n\t\t\t}\n\n\t\t\tparenRe.lastIndex = notWsRe.lastIndex;\n\t\t\tconst start  = notWsRe.lastIndex;\n\t\t\tconst result = { str : '', nextMatch : -1 };\n\t\t\tlet depth = 1;\n\n\t\t\twhile ((match = parenRe.exec(str)) !== null) {\n\t\t\t\tif (match[0] === '(') {\n\t\t\t\t\t++depth;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t--depth;\n\t\t\t\t}\n\n\t\t\t\tif (depth < 1) {\n\t\t\t\t\tresult.nextMatch = parenRe.lastIndex;\n\t\t\t\t\tresult.str = str.slice(start, result.nextMatch - 1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tfunction parseSelector(idArg) {\n\t\t\tconst ids  = [];\n\t\t\tconst idRe = /:?[^\\s:()]+/g;\n\t\t\tlet match;\n\n\t\t\twhile ((match = idRe.exec(idArg)) !== null) {\n\t\t\t\tconst id = match[0];\n\n\t\t\t\t// Group negation.\n\t\t\t\tif (id === ':not') {\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error('invalid negation: no group ID preceded \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst parent = ids[ids.length - 1];\n\n\t\t\t\t\tif (parent.id[0] !== ':') {\n\t\t\t\t\t\tthrow new Error(`invalid negation of track \"${parent.id}\": only groups may be negated with \":not()\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst negation = processNegation(idArg, idRe.lastIndex);\n\n\t\t\t\t\tif (negation.nextMatch === -1) {\n\t\t\t\t\t\tthrow new Error('unknown error parsing \":not()\"');\n\t\t\t\t\t}\n\n\t\t\t\t\tidRe.lastIndex = negation.nextMatch;\n\t\t\t\t\tparent.not = parseSelector(negation.str);\n\t\t\t\t}\n\n\t\t\t\t// Group or track ID.\n\t\t\t\telse {\n\t\t\t\t\tids.push({ id });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ids;\n\t\t}\n\n\t\treturn parseSelector;\n\t})();\n\n\t/*\n\t\tSimpleAudio.select(selector).…;\n\n\t\tE.g.\n\t\t\tSimpleAudio.select(':ui').…\n\t\t\tSimpleAudio.select(':ui:not(boop)').…\n\t\t\tSimpleAudio.select('boop beep').…\n\t\t\tSimpleAudio.select(':ui :sfx').…\n\t\t\tSimpleAudio.select(':ui:not(boop) :sfx overthetop').…\n\t*/\n\tfunction runnerGet(/* selector */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('no track selector specified');\n\t\t}\n\n\t\tconst selector = String(arguments[0]).trim();\n\t\tconst trackIds = new Set();\n\n\t\ttry {\n\t\t\tconst allIds = Array.from(_tracks.keys());\n\n\t\t\tfunction renderIds(idObj) {\n\t\t\t\tconst id = idObj.id;\n\t\t\t\tlet ids;\n\n\t\t\t\tswitch (id) {\n\t\t\t\tcase ':all':     ids = allIds; break;\n\t\t\t\tcase ':looped':  ids = allIds.filter(id => _tracks.get(id).loop()); break;\n\t\t\t\tcase ':muted':   ids = allIds.filter(id => _tracks.get(id).mute()); break;\n\t\t\t\tcase ':paused':  ids = allIds.filter(id => _tracks.get(id).isPaused()); break;\n\t\t\t\tcase ':playing': ids = allIds.filter(id => _tracks.get(id).isPlaying()); break;\n\t\t\t\tdefault:         ids = id[0] === ':' ? _groups.get(id) : [id]; break;\n\t\t\t\t}\n\n\t\t\t\tif (idObj.hasOwnProperty('not')) {\n\t\t\t\t\tconst negated = idObj.not.map(idObj => renderIds(idObj)).flat(Infinity);\n\t\t\t\t\tids = ids.filter(id => !negated.includes(id));\n\t\t\t\t}\n\n\t\t\t\treturn ids;\n\t\t\t}\n\n\t\t\t_runnerParseSelector(selector).forEach(idObj => renderIds(idObj).forEach(id => {\n\t\t\t\tif (!_tracks.has(id)) {\n\t\t\t\t\tthrow new Error(`track \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\ttrackIds.add(id);\n\t\t\t}));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tthrow new Error(`error during runner initialization: ${ex.message}`);\n\t\t}\n\n\t\treturn new AudioRunner(trackIds);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMaster Audio Functions.\n\t*******************************************************************************************************************/\n\tfunction masterLoad() {\n\t\tpublish('load');\n\t}\n\n\tfunction masterLoadWithScreen() {\n\t\tpublish('loadwithscreen');\n\t}\n\n\tfunction masterMute(mute) {\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMute;\n\t\t}\n\n\t\t_masterMute = !!mute;\n\t\tpublish('mute', _masterMute);\n\t}\n\n\tfunction masterMuteOnHidden(mute) {\n\t\t// NOTE: Some older browsers—notably: IE 9—do not support the Page Visibility API.\n\t\tif (!Visibility.isEnabled()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mute == null) { // lazy equality for null\n\t\t\treturn _masterMuteOnHidden;\n\t\t}\n\n\t\t_masterMuteOnHidden = !!mute;\n\n\t\tconst namespace = '.SimpleAudio_masterMuteOnHidden';\n\n\t\tif (_masterMuteOnHidden) {\n\t\t\tconst visibilityChange = `${Visibility.changeEvent}${namespace}`;\n\t\t\tjQuery(document)\n\t\t\t\t.off(namespace)\n\t\t\t\t.on(visibilityChange, () => masterMute(Visibility.isHidden()));\n\n\t\t\t// Only change the mute state initially if hidden.\n\t\t\tif (Visibility.isHidden()) {\n\t\t\t\tmasterMute(true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tjQuery(document).off(namespace);\n\t\t}\n\t}\n\n\tfunction masterRate(rate) {\n\t\tif (rate == null) { // lazy equality for null\n\t\t\treturn _masterRate;\n\t\t}\n\n\t\tif (typeof rate !== 'number' || Number.isNaN(rate) || !Number.isFinite(rate)) {\n\t\t\tthrow new Error('rate must be a finite number');\n\t\t}\n\n\t\t_masterRate = Math.clamp(rate, 0.2, 5); // clamp to 5× slower & faster\n\t\tpublish('rate', _masterRate);\n\t}\n\n\tfunction masterStop() {\n\t\tpublish('stop');\n\t}\n\n\tfunction masterUnload() {\n\t\tpublish('unload');\n\t}\n\n\tfunction masterVolume(volume) {\n\t\tif (volume == null) { // lazy equality for null\n\t\t\treturn _masterVolume;\n\t\t}\n\n\t\tif (typeof volume !== 'number' || Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\tthrow new Error('volume must be a finite number');\n\t\t}\n\n\t\t_masterVolume = Math.clamp(volume, 0, 1); // clamp to 0 (silent) & 1 (full loudness)\n\t\tpublish('volume', _masterVolume);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSubscription Functions.\n\t*******************************************************************************************************************/\n\tfunction subscribe(id, callback) {\n\t\tif (typeof callback !== 'function') {\n\t\t\tthrow new Error('callback parameter must be a function');\n\t\t}\n\n\t\t_subscribers.set(id, callback);\n\t}\n\n\tfunction unsubscribe(id) {\n\t\t_subscribers.delete(id);\n\t}\n\n\tfunction publish(mesg, data) {\n\t\t_subscribers.forEach(fn => fn(mesg, data));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _newTrack(sources) {\n\t\treturn new AudioTrack(sources.map(source => {\n\t\t\t// Handle audio passages.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.audio')) {\n\t\t\t\t\treturn passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle URIs—possibly prefixed with a format specifier.\n\t\t\tconst match = _formatSpecRe.exec(source);\n\t\t\treturn match === null ? source : {\n\t\t\t\tformat : match[1],\n\t\t\t\tsrc    : match[2]\n\t\t\t};\n\t\t}));\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Track Functions.\n\t\ttracks : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : trackAdd },\n\t\t\t\tdelete : { value : trackDelete },\n\t\t\t\tclear  : { value : trackClear },\n\t\t\t\thas    : { value : trackHas },\n\t\t\t\tget    : { value : trackGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Group Functions.\n\t\tgroups : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : groupAdd },\n\t\t\t\tdelete : { value : groupDelete },\n\t\t\t\tclear  : { value : groupClear },\n\t\t\t\thas    : { value : groupHas },\n\t\t\t\tget    : { value : groupGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Playlist Functions.\n\t\tlists : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd    : { value : listAdd },\n\t\t\t\tdelete : { value : listDelete },\n\t\t\t\tclear  : { value : listClear },\n\t\t\t\thas    : { value : listHas },\n\t\t\t\tget    : { value : listGet }\n\t\t\t}))\n\t\t},\n\n\t\t// Runner Functions.\n\t\tselect : { value : runnerGet },\n\n\t\t// Master Audio Functions.\n\t\tload           : { value : masterLoad },\n\t\tloadWithScreen : { value : masterLoadWithScreen },\n\t\tmute           : { value : masterMute },\n\t\tmuteOnHidden   : { value : masterMuteOnHidden },\n\t\trate           : { value : masterRate },\n\t\tstop           : { value : masterStop },\n\t\tunload         : { value : masterUnload },\n\t\tvolume         : { value : masterVolume }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstate.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Diff, Engine, PRNGWrapper, Scripting, clone, session, storage */\n\nvar State = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// History moment stack.\n\tlet _history = [];\n\n\t// Currently active/played moment.\n\tlet _active = momentCreate();\n\n\t// Currently active/played moment index.\n\tlet _activeIndex = -1;\n\n\t// Titles of all moments which have expired (i.e. fallen off the bottom of the stack).\n\tlet _expired = [];\n\n\t// (optional) Seedable PRNG object.\n\tlet _prng = null;\n\n\t// Temporary variables object.\n\tlet _tempVariables = {};\n\n\n\t/*******************************************************************************************************************\n\t\tState Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tResets the story state.\n\t*/\n\tfunction stateReset() {\n\t\tif (DEBUG) { console.log('[State/stateReset()]'); }\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tsession.delete('state');\n\n\t\t/*\n\t\t\tReset the properties.\n\t\t*/\n\t\t_history     = [];\n\t\t_active      = momentCreate();\n\t\t_activeIndex = -1;\n\t\t_expired     = [];\n\t\t_prng        = _prng === null ? null : new PRNGWrapper(_prng.seed, false);\n\t}\n\n\t/*\n\t\tRestores the story state from the active session.\n\t*/\n\tfunction stateRestore() {\n\t\tif (DEBUG) { console.log('[State/stateRestore()]'); }\n\n\t\t/*\n\t\t\tAttempt to restore an active session.\n\t\t*/\n\t\tif (session.has('state')) {\n\t\t\t/*\n\t\t\t\tRetrieve the session.\n\t\t\t*/\n\t\t\tconst stateObj = session.get('state');\n\n\t\t\tif (DEBUG) { console.log('\\tsession state:', stateObj); }\n\n\t\t\tif (stateObj == null) { // lazy equality for null\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tRestore the session.\n\t\t\t*/\n\t\t\tstateUnmarshal(stateObj);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a serializable object.\n\t*/\n\tfunction stateMarshal(noDelta) {\n\t\t/*\n\t\t\tGather the properties.\n\t\t*/\n\t\tconst stateObj = {\n\t\t\tindex : _activeIndex\n\t\t};\n\n\t\tif (noDelta) {\n\t\t\tstateObj.history = clone(_history);\n\t\t}\n\t\telse {\n\t\t\tstateObj.delta = historyDeltaEncode(_history);\n\t\t}\n\n\t\tif (_expired.length > 0) {\n\t\t\tstateObj.expired = [];\n\t\t}\n\n\t\tif (_prng !== null) {\n\t\t\tstateObj.seed = _prng.seed;\n\t\t}\n\n\t\treturn stateObj;\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled story state serialization object.\n\t*/\n\tfunction stateUnmarshal(stateObj, noDelta) {\n\t\tif (stateObj == null) { // lazy equality for null\n\t\t\tthrow new Error('state object is null or undefined');\n\t\t}\n\n\t\tif (\n\t\t\t   !stateObj.hasOwnProperty(noDelta ? 'history' : 'delta')\n\t\t\t|| stateObj[noDelta ? 'history' : 'delta'].length === 0\n\t\t) {\n\t\t\tthrow new Error('state object has no history or history is empty');\n\t\t}\n\n\t\tif (!stateObj.hasOwnProperty('index')) {\n\t\t\tthrow new Error('state object has no index');\n\t\t}\n\n\t\tif (_prng !== null && !stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has no seed, but PRNG is enabled');\n\t\t}\n\n\t\tif (_prng === null && stateObj.hasOwnProperty('seed')) {\n\t\t\tthrow new Error('state object has seed, but PRNG is disabled');\n\t\t}\n\n\t\t/*\n\t\t\tRestore the properties.\n\t\t*/\n\t\t_history     = noDelta ? clone(stateObj.history) : historyDeltaDecode(stateObj.delta);\n\t\t_activeIndex = stateObj.index;\n\t\t_expired     = stateObj.hasOwnProperty('expired') ? [...stateObj.expired] : [];\n\n\t\tif (stateObj.hasOwnProperty('seed')) {\n\t\t\t/*\n\t\t\t\tWe only need to restore the PRNG's seed here as `momentActivate()` will handle\n\t\t\t\tfully restoring the PRNG to its proper state.\n\t\t\t*/\n\t\t\t_prng.seed = stateObj.seed;\n\t\t}\n\n\t\t/*\n\t\t\tActivate the current moment (do this only after all properties have been restored).\n\t\t*/\n\t\tmomentActivate(_activeIndex);\n\t}\n\n\t/*\n\t\tReturns the current story state marshaled into a save-compatible serializable object.\n\t*/\n\tfunction stateMarshalForSave() {\n\t\treturn stateMarshal(true);\n\t}\n\n\t/*\n\t\tRestores the story state from a marshaled save-compatible story state serialization object.\n\t*/\n\tfunction stateUnmarshalForSave(stateObj) {\n\t\treturn stateUnmarshal(stateObj, true);\n\t}\n\n\t/*\n\t\tReturns the titles of expired moments.\n\t*/\n\tfunction stateExpired() {\n\t\treturn _expired;\n\t}\n\n\t/*\n\t\tReturns the total number of played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTurns() {\n\t\treturn _expired.length + historyLength();\n\t}\n\n\t/*\n\t\tReturns the passage titles of all played moments (expired + in-play history moments).\n\t*/\n\tfunction stateTitles() {\n\t\treturn _expired.concat(_history.slice(0, historyLength()).map(moment => moment.title));\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title has been played (expired + in-play history moments).\n\t*/\n\tfunction stateHasPlayed(title) {\n\t\tif (title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tif (_expired.includes(title)) {\n\t\t\treturn true;\n\t\t}\n\t\telse if (_history.slice(0, historyLength()).some(moment => moment.title === title)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMoment Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a new moment object created from the given passage title and variables object.\n\t*/\n\tfunction momentCreate(title, variables) {\n\t\treturn {\n\t\t\ttitle     : title == null ? '' : String(title), // lazy equality for null\n\t\t\tvariables : variables == null ? {} : variables   // lazy equality for null\n\t\t};\n\t}\n\n\t/*\n\t\tReturns the active (present) moment.\n\t*/\n\tfunction momentActive() {\n\t\treturn _active;\n\t}\n\n\t/*\n\t\tReturns the index within the history of the active (present) moment.\n\t*/\n\tfunction momentActiveIndex() {\n\t\treturn _activeIndex;\n\t}\n\n\t/*\n\t\tReturns the title from the active (present) moment.\n\t*/\n\tfunction momentActiveTitle() {\n\t\treturn _active.title;\n\t}\n\n\t/*\n\t\tReturns the variables from the active (present) moment.\n\t*/\n\tfunction momentActiveVariables() {\n\t\treturn _active.variables;\n\t}\n\n\t/*\n\t\tReturns the active (present) moment after setting it to either the given moment object\n\t\tor the moment object at the given history index.  Additionally, updates the active session\n\t\tand triggers a history update event.\n\t*/\n\tfunction momentActivate(moment) {\n\t\tif (moment == null) { // lazy equality for null\n\t\t\tthrow new Error('moment activation attempted with null or undefined');\n\t\t}\n\n\t\t/*\n\t\t\tSet the active moment.\n\t\t*/\n\t\tswitch (typeof moment) {\n\t\tcase 'object':\n\t\t\t_active = clone(moment);\n\t\t\tbreak;\n\n\t\tcase 'number':\n\t\t\tif (historyIsEmpty()) {\n\t\t\t\tthrow new Error('moment activation attempted with index on empty history');\n\t\t\t}\n\n\t\t\tif (moment < 0 || moment >= historySize()) {\n\t\t\t\tthrow new RangeError(`moment activation attempted with out-of-bounds index; need [0, ${historySize() - 1}], got ${moment}`);\n\t\t\t}\n\n\t\t\t_active = clone(_history[moment]);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new TypeError(`moment activation attempted with a \"${typeof moment}\"; must be an object or valid history stack index`);\n\t\t}\n\n\t\t/*\n\t\t\tRestore the seedable PRNG.\n\n\t\t\tNOTE: We cannot simply set `_prng.pull` to `_active.pull` as that would\n\t\t\tnot properly mutate the PRNG's internal state.\n\t\t*/\n\t\tif (_prng !== null) {\n\t\t\t_prng = PRNGWrapper.unmarshal({\n\t\t\t\tseed : _prng.seed,\n\t\t\t\tpull : _active.pull\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t\tUpdate the active session.\n\t\t*/\n\t\tsession.set('state', stateMarshal());\n\n\t\t/*\n\t\t\tTrigger a global `:historyupdate` event.\n\n\t\t\tNOTE: We do this here because setting a new active moment is a core component\n\t\t\tof, virtually, all history updates.\n\t\t*/\n\t\tjQuery.event.trigger(':historyupdate');\n\n\t\treturn _active;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tHistory Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the moment history.\n\t*/\n\tfunction historyGet() {\n\t\treturn _history;\n\t}\n\n\t/*\n\t\tReturns the number of active history moments (past only).\n\t*/\n\tfunction historyLength() {\n\t\treturn _activeIndex + 1;\n\t}\n\n\t/*\n\t\tReturns the total number of history moments (past + future).\n\t*/\n\tfunction historySize() {\n\t\treturn _history.length;\n\t}\n\n\t/*\n\t\tReturns whether the history is empty.\n\t*/\n\tfunction historyIsEmpty() {\n\t\treturn _history.length === 0;\n\t}\n\n\t/*\n\t\tReturns the current (pre-play version of the active) moment within the history.\n\t*/\n\tfunction historyCurrent() {\n\t\treturn _history.length > 0 ? _history[_activeIndex] : null;\n\t}\n\n\t/*\n\t\tReturns the topmost (most recent) moment within the history.\n\t*/\n\tfunction historyTop() {\n\t\treturn _history.length > 0 ? _history[_history.length - 1] : null;\n\t}\n\n\t/*\n\t\tReturns the bottommost (least recent) moment within the history.\n\t*/\n\tfunction historyBottom() {\n\t\treturn _history.length > 0 ? _history[0] : null;\n\t}\n\n\t/*\n\t\tReturns the moment at the given index within the history.\n\t*/\n\tfunction historyIndex(index) {\n\t\tif (historyIsEmpty() || index < 0 || index > _activeIndex) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[index];\n\t}\n\n\t/*\n\t\tReturns the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyPeek(offset) {\n\t\tif (historyIsEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst lengthOffset = 1 + (offset ? Math.abs(offset) : 0);\n\n\t\tif (lengthOffset > historyLength()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn _history[historyLength() - lengthOffset];\n\t}\n\n\t/*\n\t\tReturns whether a moment with the given title exists within the history.\n\t*/\n\tfunction historyHas(title) {\n\t\tif (historyIsEmpty() || title == null || title === '') { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = _activeIndex; i >= 0; --i) {\n\t\t\tif (_history[i].title === title) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/*\n\t\tCreates a new moment and pushes it onto the history, discarding future moments if necessary.\n\t*/\n\tfunction historyCreate(title) {\n\t\tif (DEBUG) { console.log(`[State/historyCreate(title: \"${title}\")]`); }\n\n\t\t/*\n\t\t\tTODO: It might be good to have some assertions about the passage title here.\n\t\t*/\n\n\t\t/*\n\t\t\tIf we're not at the top of the stack, discard the future moments.\n\t\t*/\n\t\tif (historyLength() < historySize()) {\n\t\t\tif (DEBUG) { console.log(`\\tnon-top push; discarding ${historySize() - historyLength()} future moments`); }\n\n\t\t\t_history.splice(historyLength(), historySize() - historyLength());\n\t\t}\n\n\t\t/*\n\t\t\tPush the new moment onto the history stack.\n\t\t*/\n\t\t_history.push(momentCreate(title, _active.variables));\n\n\t\tif (_prng) {\n\t\t\thistoryTop().pull = _prng.pull;\n\t\t}\n\n\t\t/*\n\t\t\tTruncate the history, if necessary, by discarding moments from the bottom.\n\t\t*/\n\t\tif (Config.history.maxStates > 0) {\n\t\t\twhile (historySize() > Config.history.maxStates) {\n\t\t\t\t_expired.push(_history.shift().title);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tActivate the new top moment.\n\t\t*/\n\t\t_activeIndex = historySize() - 1;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn historyLength();\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the history.\n\t*/\n\tfunction historyGoTo(index) {\n\t\tif (DEBUG) { console.log(`[State/historyGoTo(index: ${index})]`); }\n\n\t\tif (\n\t\t\t   index == null /* lazy equality for null */\n\t\t\t|| index < 0\n\t\t\t|| index >= historySize()\n\t\t\t|| index === _activeIndex\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t_activeIndex = index;\n\t\tmomentActivate(_activeIndex);\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the history.\n\t*/\n\tfunction historyGo(offset) {\n\t\tif (DEBUG) { console.log(`[State/historyGo(offset: ${offset})]`); }\n\n\t\tif (offset == null || offset === 0) { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\treturn historyGoTo(_activeIndex + offset);\n\t}\n\n\t/*\n\t\tReturns the delta encoded form of the given history array.\n\t*/\n\tfunction historyDeltaEncode(historyArr) {\n\t\tif (!Array.isArray(historyArr)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (historyArr.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst delta = [historyArr[0]];\n\n\t\tfor (let i = 1, iend = historyArr.length; i < iend; ++i) {\n\t\t\tdelta.push(Diff.diff(historyArr[i - 1], historyArr[i]));\n\t\t}\n\n\t\treturn delta;\n\t}\n\n\t/*\n\t\tReturns a history array from the given delta encoded history array.\n\t*/\n\tfunction historyDeltaDecode(delta) {\n\t\tif (!Array.isArray(delta)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (delta.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst historyArr = [clone(delta[0])];\n\n\t\tfor (let i = 1, iend = delta.length; i < iend; ++i) {\n\t\t\thistoryArr.push(Diff.patch(historyArr[i - 1], delta[i]));\n\t\t}\n\n\t\treturn historyArr;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPRNG Functions.\n\t*******************************************************************************************************************/\n\tfunction prngInit(seed, useEntropy) {\n\t\tif (DEBUG) { console.log(`[State/prngInit(seed: ${seed}, useEntropy: ${useEntropy})]`); }\n\n\t\tif (!historyIsEmpty()) {\n\t\t\tlet scriptSection;\n\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tscriptSection = 'a script-tagged passage';\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tscriptSection = 'the Story JavaScript';\n\t\t\t}\n\n\t\t\tthrow new Error(`State.initPRNG must be called during initialization, within either ${scriptSection} or the StoryInit special passage`);\n\t\t}\n\n\t\t_prng = new PRNGWrapper(seed, useEntropy);\n\t\t_active.pull = _prng.pull;\n\t}\n\n\tfunction prngIsEnabled() {\n\t\treturn _prng !== null;\n\t}\n\n\tfunction prngPull() {\n\t\treturn _prng ? _prng.pull : NaN;\n\t}\n\n\tfunction prngSeed() {\n\t\treturn _prng ? _prng.seed : null;\n\t}\n\n\tfunction prngRandom() {\n\t\tif (DEBUG) { console.log('[State/prngRandom()]'); }\n\n\t\treturn _prng ? _prng.random() : Math.random();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTemporary Variables Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tClear the temporary variables.\n\t*/\n\tfunction tempVariablesClear() {\n\t\tif (DEBUG) { console.log('[State/tempVariablesReset()]'); }\n\n\t\t_tempVariables = {};\n\n\t\t/* legacy */\n\t\tTempVariables = _tempVariables; // eslint-disable-line no-undef\n\t\t/* /legacy */\n\t}\n\n\t/*\n\t\tReturns the current temporary variables.\n\t*/\n\tfunction tempVariables() {\n\t\treturn _tempVariables;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tVariable Chain Parsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the value of the given story/temporary variable.\n\t*/\n\tfunction variableGet(varExpression) {\n\t\ttry {\n\t\t\treturn Scripting.evalTwineScript(varExpression);\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\t}\n\n\t/*\n\t\tSets the value of the given story/temporary variable.\n\t*/\n\tfunction variableSet(varExpression, value) {\n\t\ttry {\n\t\t\tScripting.evalTwineScript(`${varExpression} = evalTwineScript$Data$`, null, value);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (ex) { /* no-op */ }\n\n\t\treturn false;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tStory Metadata Functions.\n\t*******************************************************************************************************************/\n\tconst _METADATA_STORE = 'metadata';\n\n\tfunction metadataClear() {\n\t\tstorage.delete(_METADATA_STORE);\n\t}\n\n\tfunction metadataDelete(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.delete key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\n\t\tif (store && store.hasOwnProperty(key)) {\n\t\t\tif (Object.keys(store).length === 1) {\n\t\t\t\tstorage.delete(_METADATA_STORE);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelete store[key];\n\t\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction metadataGet(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.get key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key) ? store[key] : undefined;\n\t}\n\n\tfunction metadataHas(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.has key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store && store.hasOwnProperty(key);\n\t}\n\n\tfunction metadataSet(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`State.metadata.set key parameter must be a string (received: ${typeof key})`);\n\t\t}\n\n\t\tif (typeof value === 'undefined') {\n\t\t\tmetadataDelete(key);\n\t\t}\n\t\telse {\n\t\t\tconst store = storage.get(_METADATA_STORE) || {};\n\t\t\tstore[key] = value;\n\t\t\tstorage.set(_METADATA_STORE, store);\n\t\t}\n\t}\n\n\tfunction metadataSize() {\n\t\tconst store = storage.get(_METADATA_STORE);\n\t\treturn store ? Object.keys(store).length : 0;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tState Functions.\n\t\t*/\n\t\treset            : { value : stateReset },\n\t\trestore          : { value : stateRestore },\n\t\tmarshalForSave   : { value : stateMarshalForSave },\n\t\tunmarshalForSave : { value : stateUnmarshalForSave },\n\t\texpired          : { get : stateExpired },\n\t\tturns            : { get : stateTurns },\n\t\tpassages         : { get : stateTitles },\n\t\thasPlayed        : { value : stateHasPlayed },\n\n\t\t/*\n\t\t\tMoment Functions.\n\t\t*/\n\t\tactive      : { get : momentActive },\n\t\tactiveIndex : { get : momentActiveIndex },\n\t\tpassage     : { get : momentActiveTitle },     // shortcut for `State.active.title`\n\t\tvariables   : { get : momentActiveVariables }, // shortcut for `State.active.variables`\n\n\t\t/*\n\t\t\tHistory Functions.\n\t\t*/\n\t\thistory     : { get : historyGet },\n\t\tlength      : { get : historyLength },\n\t\tsize        : { get : historySize },\n\t\tisEmpty     : { value : historyIsEmpty },\n\t\tcurrent     : { get : historyCurrent },\n\t\ttop         : { get : historyTop },\n\t\tbottom      : { get : historyBottom },\n\t\tindex       : { value : historyIndex },\n\t\tpeek        : { value : historyPeek },\n\t\thas         : { value : historyHas },\n\t\tcreate      : { value : historyCreate },\n\t\tgoTo        : { value : historyGoTo },\n\t\tgo          : { value : historyGo },\n\t\tdeltaEncode : { value : historyDeltaEncode },\n\t\tdeltaDecode : { value : historyDeltaDecode },\n\n\t\t/*\n\t\t\tPRNG Functions.\n\t\t*/\n\t\tprng : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tinit      : { value : prngInit },\n\t\t\t\tisEnabled : { value : prngIsEnabled },\n\t\t\t\tpull      : { get : prngPull },\n\t\t\t\tseed      : { get : prngSeed }\n\t\t\t}))\n\t\t},\n\t\trandom : { value : prngRandom },\n\n\t\t/*\n\t\t\tTemporary Variables Functions.\n\t\t*/\n\t\tclearTemporary : { value : tempVariablesClear },\n\t\ttemporary      : { get : tempVariables },\n\n\t\t/*\n\t\t\tVariable Chain Parsing Functions.\n\t\t*/\n\t\tgetVar : { value : variableGet },\n\t\tsetVar : { value : variableSet },\n\n\t\t/*\n\t\t\tStory Metadata Functions.\n\t\t*/\n\t\tmetadata : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tclear  : { value : metadataClear },\n\t\t\t\tdelete : { value : metadataDelete },\n\t\t\t\tget    : { value : metadataGet },\n\t\t\t\thas    : { value : metadataHas },\n\t\t\t\tset    : { value : metadataSet },\n\t\t\t\tsize   : { get : metadataSize }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tinitPRNG : { value : prngInit },\n\t\trestart  : { value : () => Engine.restart() },\n\t\tbackward : { value : () => Engine.backward() },\n\t\tforward  : { value : () => Engine.forward() },\n\t\tdisplay  : { value : (...args) => Engine.display(...args) },\n\t\tshow     : { value : (...args) => Engine.show(...args) },\n\t\tplay     : { value : (...args) => Engine.play(...args) }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/scripting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Engine, Patterns, State, Story, Util */\n\nvar Scripting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/* eslint-disable no-unused-vars */\n\n\t/*******************************************************************************************************************\n\t\tDeprecated Legacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Returns the jQuery-wrapped target element(s) after making them accessible\n\t\tclickables (ARIA compatibility).\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction addAccessibleClickHandler(targets, selector, handler, one, namespace) {\n\t\tif (arguments.length < 2) {\n\t\t\tthrow new Error('addAccessibleClickHandler insufficient number of parameters');\n\t\t}\n\n\t\tlet fn;\n\t\tlet opts;\n\n\t\tif (typeof selector === 'function') {\n\t\t\tfn = selector;\n\t\t\topts = {\n\t\t\t\tnamespace : one,\n\t\t\t\tone       : !!handler\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tfn = handler;\n\t\t\topts = {\n\t\t\t\tnamespace,\n\t\t\t\tone : !!one,\n\t\t\t\tselector\n\t\t\t};\n\t\t}\n\n\t\tif (typeof fn !== 'function') {\n\t\t\tthrow new TypeError('addAccessibleClickHandler handler parameter must be a function');\n\t\t}\n\n\t\treturn jQuery(targets).ariaClick(opts, fn);\n\t}\n\n\t/*\n\t\t[DEPRECATED] Returns a new DOM element, optionally appending it to the passed DOM element, if any.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertElement(place, type, id, classNames, text, title) { // eslint-disable-line max-params\n\t\tconst $el = jQuery(document.createElement(type));\n\n\t\t// Add attributes/properties.\n\t\tif (id) {\n\t\t\t$el.attr('id', id);\n\t\t}\n\n\t\tif (classNames) {\n\t\t\t$el.addClass(classNames);\n\t\t}\n\n\t\tif (title) {\n\t\t\t$el.attr('title', title);\n\t\t}\n\n\t\t// Add text content.\n\t\tif (text) {\n\t\t\t$el.text(text);\n\t\t}\n\n\t\t// Append it to the given node.\n\t\tif (place) {\n\t\t\t$el.appendTo(place);\n\t\t}\n\n\t\treturn $el[0];\n\t}\n\n\t/*\n\t\t[DEPRECATED] Creates a new text node and appends it to the passed DOM element.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction insertText(place, text) {\n\t\tjQuery(place).append(document.createTextNode(text));\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes all children from the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeChildren(node) {\n\t\tjQuery(node).empty();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Removes the passed DOM node.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction removeElement(node) {\n\t\tjQuery(node).remove();\n\t}\n\n\t/*\n\t\t[DEPRECATED] Fades a DOM element in or out.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction fade(el, options) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tconst direction = options.fade === 'in' ? 1 : -1;\n\t\tlet current;\n\t\tlet proxy      = el.cloneNode(true);\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tcurrent += 0.05 * direction;\n\t\t\tsetOpacity(proxy, Math.easeInOut(current));\n\n\t\t\tif (direction === 1 && current >= 1 || direction === -1 && current <= 0) {\n\t\t\t\tel.style.visibility = options.fade === 'in' ? 'visible' : 'hidden';\n\t\t\t\tproxy.parentNode.replaceChild(el, proxy);\n\t\t\t\tproxy = null;\n\t\t\t\twindow.clearInterval(intervalId);\n\n\t\t\t\tif (options.onComplete) {\n\t\t\t\t\toptions.onComplete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction setOpacity(el, opacity) {\n\t\t\t// Old IE.\n\t\t\tel.style.zoom = 1;\n\t\t\tel.style.filter = `alpha(opacity=${Math.floor(opacity * 100)})`;\n\n\t\t\t// CSS.\n\t\t\tel.style.opacity = opacity;\n\t\t}\n\n\t\tel.parentNode.replaceChild(proxy, el);\n\n\t\tif (options.fade === 'in') {\n\t\t\tcurrent = 0;\n\t\t\tproxy.style.visibility = 'visible';\n\t\t}\n\t\telse {\n\t\t\tcurrent = 1;\n\t\t}\n\n\t\tsetOpacity(proxy, current);\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\t/*\n\t\t[DEPRECATED] Scrolls the browser window to ensure that a DOM element is in view.\n\n\t\tNOTE: Unused, included only for compatibility.\n\t*/\n\tfunction scrollWindowTo(el, incrementBy) {\n\t\t/* eslint-disable no-param-reassign */\n\t\tlet increment = incrementBy != null ? Number(incrementBy) : 0.1; // lazy equality for null\n\n\t\tif (Number.isNaN(increment) || !Number.isFinite(increment) || increment < 0) {\n\t\t\tincrement = 0.1;\n\t\t}\n\t\telse if (increment > 1) {\n\t\t\tincrement = 1;\n\t\t}\n\n\t\tconst start     = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\tconst end       = ensureVisible(el);\n\t\tconst distance  = Math.abs(start - end);\n\t\tconst direction = start > end ? -1 : 1;\n\t\tlet progress   = 0;\n\t\tlet intervalId; // eslint-disable-line prefer-const\n\n\t\tfunction tick() {\n\t\t\tprogress += increment;\n\t\t\twindow.scroll(0, start + direction * (distance * Math.easeInOut(progress)));\n\n\t\t\tif (progress >= 1) {\n\t\t\t\twindow.clearInterval(intervalId);\n\t\t\t}\n\t\t}\n\n\t\tfunction findPosY(el) { // eslint-disable-line no-shadow\n\t\t\tlet curtop = 0;\n\n\t\t\twhile (el.offsetParent) {\n\t\t\t\tcurtop += el.offsetTop;\n\t\t\t\tel = el.offsetParent;\n\t\t\t}\n\n\t\t\treturn curtop;\n\t\t}\n\n\t\tfunction ensureVisible(el) { // eslint-disable-line no-shadow\n\t\t\tconst posTop    = findPosY(el);\n\t\t\tconst posBottom = posTop + el.offsetHeight;\n\t\t\tconst winTop    = window.scrollY ? window.scrollY : document.body.scrollTop;\n\t\t\tconst winHeight = window.innerHeight ? window.innerHeight : document.body.clientHeight;\n\t\t\tconst winBottom = winTop + winHeight;\n\n\t\t\treturn posTop >= winTop && posBottom > winBottom && el.offsetHeight < winHeight\n\t\t\t\t? posTop - (winHeight - el.offsetHeight) + 20\n\t\t\t\t: posTop;\n\t\t}\n\n\t\tintervalId = window.setInterval(tick, 25);\n\t\t/* eslint-enable no-param-reassign */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUser Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns a random value from its given arguments.\n\t*/\n\tfunction either(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn Array.prototype.concat.apply([], arguments).random();\n\t}\n\n\t/*\n\t\tRemoves the given key, and its value, from the story metadata store.\n\t*/\n\tfunction forget(key) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`forget key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.delete(key);\n\t}\n\n\t/*\n\t\tReturns whether a passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the logical-AND\n\t\taggregate of the set.\n\t*/\n\tfunction hasVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('hasVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend; ++i) {\n\t\t\tif (!played.includes(needles[i])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/*\n\t\tReturns the number of turns that have passed since the last instance of the given passage\n\t\toccurred within the story history or `-1` if it does not exist.  If multiple passages are\n\t\tgiven, returns the lowest count (which can be `-1`).\n\t*/\n\tfunction lastVisited(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('lastVisited called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst played  = State.passages;\n\t\tconst uBound  = played.length - 1;\n\t\tlet turns = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && turns > -1; ++i) {\n\t\t\tconst lastIndex = played.lastIndexOf(needles[i]);\n\t\t\tturns = Math.min(turns, lastIndex === -1 ? -1 : uBound - lastIndex);\n\t\t}\n\n\t\treturn turns;\n\t}\n\n\t/*\n\t\tSets the given key/value pair within the story metadata store.\n\t*/\n\tfunction memorize(key, value) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`memorize key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\tState.metadata.set(key, value);\n\t}\n\n\t/*\n\t\tReturns the title of the current passage.\n\t*/\n\tfunction passage() {\n\t\treturn State.passage;\n\t}\n\n\t/*\n\t\tReturns the title of a previous passage, either the most recent one whose title does not\n\t\tmatch that of the active passage or the one at the optional offset, or an empty string,\n\t\tif there is no such passage.\n\t*/\n\tfunction previous(/* legacy: offset */) {\n\t\tconst passages = State.passages;\n\n\t\t/* legacy: behavior with an offset */\n\t\tif (arguments.length > 0) {\n\t\t\tconst offset = Number(arguments[0]);\n\n\t\t\tif (!Number.isSafeInteger(offset) || offset < 1) {\n\t\t\t\tthrow new RangeError('previous offset parameter must be a positive integer greater than zero');\n\t\t\t}\n\n\t\t\treturn passages.length > offset ? passages[passages.length - 1 - offset] : '';\n\t\t}\n\t\t/* /legacy */\n\n\t\tfor (let i = passages.length - 2; i >= 0; --i) {\n\t\t\tif (passages[i] !== State.passage) {\n\t\t\t\treturn passages[i];\n\t\t\t}\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t/*\n\t\tReturns a pseudo-random whole number (integer) within the range of the given bounds.\n\t*/\n\tfunction random(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('random called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0;\n\t\t\tmax = Math.trunc(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Math.trunc(arguments[0]);\n\t\t\tmax = Math.trunc(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!Number.isInteger(min)) {\n\t\t\tthrow new Error('random min parameter must be an integer');\n\t\t}\n\t\tif (!Number.isInteger(max)) {\n\t\t\tthrow new Error('random max parameter must be an integer');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn Math.floor(State.random() * (max - min + 1)) + min;\n\t}\n\n\t/*\n\t\tReturns a pseudo-random real number (floating-point) within the range of the given bounds.\n\n\t\tNOTE: Unlike with its sibling function `random()`, the `max` parameter\n\t\tis exclusive, not inclusive—i.e. the range goes to, but does not include,\n\t\tthe given value.\n\t*/\n\tfunction randomFloat(/* [min ,] max */) {\n\t\tlet min;\n\t\tlet max;\n\n\t\tswitch (arguments.length) {\n\t\tcase 0:\n\t\t\tthrow new Error('randomFloat called with insufficient parameters');\n\t\tcase 1:\n\t\t\tmin = 0.0;\n\t\t\tmax = Number(arguments[0]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmin = Number(arguments[0]);\n\t\t\tmax = Number(arguments[1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (Number.isNaN(min) || !Number.isFinite(min)) {\n\t\t\tthrow new Error('randomFloat min parameter must be a number');\n\t\t}\n\t\tif (Number.isNaN(max) || !Number.isFinite(max)) {\n\t\t\tthrow new Error('randomFloat max parameter must be a number');\n\t\t}\n\n\t\tif (min > max) {\n\t\t\t[min, max] = [max, min];\n\t\t}\n\n\t\treturn State.random() * (max - min) + min;\n\t}\n\n\t/*\n\t\tReturns the value of the given key from the story metadata store\n\t\tor the given default value if the key does not exist.\n\t*/\n\tfunction recall(key, defaultValue) {\n\t\tif (typeof key !== 'string') {\n\t\t\tthrow new TypeError(`recall key parameter must be a string (received: ${Util.getType(key)})`);\n\t\t}\n\n\t\treturn State.metadata.has(key) ? State.metadata.get(key) : defaultValue;\n\t}\n\n\t/*\n\t\tReturns a new array consisting of all of the tags of the given passages.\n\t*/\n\tfunction tags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\treturn Story.get(State.passage).tags.slice(0);\n\t\t}\n\n\t\tconst passages = Array.prototype.concat.apply([], arguments);\n\t\tlet tags = [];\n\n\t\tfor (let i = 0, iend = passages.length; i < iend; ++i) {\n\t\t\ttags = tags.concat(Story.get(passages[i]).tags);\n\t\t}\n\n\t\treturn tags;\n\t}\n\n\t/*\n\t\tReturns a reference to the current temporary _variables store.\n\t*/\n\tfunction temporary() {\n\t\treturn State.temporary;\n\t}\n\n\t/*\n\t\tReturns the number of milliseconds which have passed since the current passage was rendered.\n\t*/\n\tfunction time() {\n\t\treturn Engine.lastPlay === null ? 0 : Util.now() - Engine.lastPlay;\n\t}\n\n\t/*\n\t\tReturns the number of passages that the player has visited.\n\n\t\tNOTE: Passages which were visited but have been undone—e.g. via the backward\n\t\tbutton or the `<<back>>` macro—are no longer part of the in-play story\n\t\thistory and thus are not tallied.  Passages which were visited but have\n\t\texpired from the story history, on the other hand, are tallied.\n\t*/\n\tfunction turns() {\n\t\treturn State.turns;\n\t}\n\n\t/*\n\t\tReturns a reference to the current story $variables store.\n\t*/\n\tfunction variables() {\n\t\treturn State.variables;\n\t}\n\n\t/*\n\t\tReturns the number of times that the passage with the given title exists within the story\n\t\thistory.  If multiple passage titles are given, returns the lowest count.\n\t*/\n\tfunction visited(/* variadic */) {\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments.length === 0 ? [State.passage] : arguments);\n\t\tconst played  = State.passages;\n\t\tlet count = State.turns;\n\n\t\tfor (let i = 0, iend = needles.length; i < iend && count > 0; ++i) {\n\t\t\tcount = Math.min(count, played.count(needles[i]));\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/*\n\t\tReturns the number of passages within the story history which are tagged with all of the given tags.\n\t*/\n\tfunction visitedTags(/* variadic */) {\n\t\tif (arguments.length === 0) {\n\t\t\tthrow new Error('visitedTags called with insufficient parameters');\n\t\t}\n\n\t\tif (State.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst needles = Array.prototype.concat.apply([], arguments);\n\t\tconst nLength = needles.length;\n\t\tconst played  = State.passages;\n\t\tconst seen    = new Map();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0, iend = played.length; i < iend; ++i) {\n\t\t\tconst title = played[i];\n\n\t\t\tif (seen.has(title)) {\n\t\t\t\tif (seen.get(title)) {\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst tags = Story.get(title).tags;\n\n\t\t\t\tif (tags.length > 0) {\n\t\t\t\t\tlet found = 0;\n\n\t\t\t\t\tfor (let j = 0; j < nLength; ++j) {\n\t\t\t\t\t\tif (tags.includes(needles[j])) {\n\t\t\t\t\t\t\t++found;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (found === nLength) {\n\t\t\t\t\t\t++count;\n\t\t\t\t\t\tseen.set(title, true);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tseen.set(title, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/* eslint-enable no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tImport Functions.\n\t*******************************************************************************************************************/\n\tvar { // eslint-disable-line no-var\n\t\t/* eslint-disable no-unused-vars */\n\t\timportScripts,\n\t\timportStyles\n\t\t/* eslint-enable no-unused-vars */\n\t} = (() => {\n\t\t// Slugify the given URL.\n\t\tfunction slugifyUrl(url) {\n\t\t\treturn Util.parseUrl(url).path\n\t\t\t\t.replace(/^[^\\w]+|[^\\w]+$/g, '')\n\t\t\t\t.replace(/[^\\w]+/g, '-')\n\t\t\t\t.toLocaleLowerCase();\n\t\t}\n\n\t\t// Add a <script> element which will load the script from the given URL.\n\t\tfunction addScript(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('script'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importScripts failed to load the script \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `script-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\ttype : 'text/javascript',\n\t\t\t\t\t\tsrc  : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Add a <link> element which will load the stylesheet from the given URL.\n\t\tfunction addStyle(url) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t/*\n\t\t\t\t\tWARNING: The ordering of the code within this function is important,\n\t\t\t\t\tas some browsers don't play well with different arrangements, so\n\t\t\t\t\tbe careful when mucking around with it.\n\n\t\t\t\t\tThe best supported ordering seems be: events → DOM append → attributes.\n\t\t\t\t*/\n\t\t\t\tjQuery(document.createElement('link'))\n\t\t\t\t\t.one('load abort error', ev => {\n\t\t\t\t\t\tjQuery(ev.target).off();\n\n\t\t\t\t\t\tif (ev.type === 'load') {\n\t\t\t\t\t\t\tresolve(ev.target);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treject(new Error(`importStyles failed to load the stylesheet \"${url}\".`));\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo(document.head)\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid   : `style-imported-${slugifyUrl(url)}`,\n\t\t\t\t\t\trel  : 'stylesheet',\n\t\t\t\t\t\thref : url\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// Turn a list of callbacks into a sequential chain of `Promise` objects.\n\t\tfunction sequence(callbacks) {\n\t\t\treturn callbacks.reduce((seq, fn) => seq = seq.then(fn), Promise.resolve()); // eslint-disable-line no-param-reassign\n\t\t}\n\n\t\t/*\n\t\t\tImport scripts from a URL.\n\t\t*/\n\t\tfunction importScripts(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addScript(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addScript(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t/*\n\t\t\tImport stylesheets from a URL.\n\t\t*/\n\t\tfunction importStyles(...urls) {\n\t\t\treturn Promise.all(urls.map(oneOrSeries => {\n\t\t\t\t// Array of URLs to be imported in sequence.\n\t\t\t\tif (Array.isArray(oneOrSeries)) {\n\t\t\t\t\treturn sequence(oneOrSeries.map(url => () => addStyle(url)));\n\t\t\t\t}\n\n\t\t\t\t// Single URL to be imported.\n\t\t\t\treturn addStyle(oneOrSeries);\n\t\t\t}));\n\t\t}\n\n\t\t// Exports.\n\t\treturn {\n\t\t\timportScripts,\n\t\t\timportStyles\n\t\t};\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tParsing Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tReturns the given string after converting all TwineScript syntactical sugars to\n\t\ttheir native JavaScript counterparts.\n\t*/\n\tconst parse = (() => {\n\t\tconst tokenTable = Util.toEnum({\n\t\t\t/* eslint-disable quote-props */\n\t\t\t// Story $variable sigil-prefix.\n\t\t\t'$'     : 'State.variables.',\n\t\t\t// Temporary _variable sigil-prefix.\n\t\t\t'_'     : 'State.temporary.',\n\t\t\t// Assignment operators.\n\t\t\t'to'    : '=',\n\t\t\t// Equality operators.\n\t\t\t'eq'    : '==',\n\t\t\t'neq'   : '!=',\n\t\t\t'is'    : '===',\n\t\t\t'isnot' : '!==',\n\t\t\t// Relational operators.\n\t\t\t'gt'    : '>',\n\t\t\t'gte'   : '>=',\n\t\t\t'lt'    : '<',\n\t\t\t'lte'   : '<=',\n\t\t\t// Logical operators.\n\t\t\t'and'   : '&&',\n\t\t\t'or'    : '||',\n\t\t\t// Unary operators.\n\t\t\t'not'   : '!',\n\t\t\t'def'   : '\"undefined\" !== typeof',\n\t\t\t'ndef'  : '\"undefined\" === typeof'\n\t\t\t/* eslint-enable quote-props */\n\t\t});\n\t\tconst parseRe = new RegExp([\n\t\t\t'(\"\"|\\'\\')',                                          // 1=Empty quotes\n\t\t\t'(\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\")',                            // 2=Double quoted, non-empty\n\t\t\t\"('(?:\\\\\\\\.|[^'\\\\\\\\])+')\",                            // 3=Single quoted, non-empty\n\t\t\t'([=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}]+)',       // 4=Operator delimiters\n\t\t\t'([^\"\\'=+\\\\-*\\\\/%<>&\\\\|\\\\^~!?:,;\\\\(\\\\)\\\\[\\\\]{}\\\\s]+)' // 5=Barewords\n\t\t].join('|'), 'g');\n\t\tconst notSpaceRe      = /\\S/;\n\t\tconst varTest         = new RegExp(`^${Patterns.variable}`);\n\t\tconst withColonTestRe = /^\\s*:/;\n\t\tconst withNotTestRe   = /^\\s+not\\b/;\n\n\t\tfunction parse(rawCodeString) {\n\t\t\tif (parseRe.lastIndex !== 0) {\n\t\t\t\tthrow new RangeError('Scripting.parse last index is non-zero at start');\n\t\t\t}\n\n\t\t\tlet code  = rawCodeString;\n\t\t\tlet match;\n\n\t\t\twhile ((match = parseRe.exec(code)) !== null) {\n\t\t\t\t// no-op: Empty quotes | Double quoted | Single quoted | Operator delimiters\n\n\t\t\t\t// Barewords.\n\t\t\t\tif (match[5]) {\n\t\t\t\t\tlet token = match[5];\n\n\t\t\t\t\t// If the token is simply a dollar-sign or underscore, then it's either\n\t\t\t\t\t// just the raw character or, probably, a function alias, so skip it.\n\t\t\t\t\tif (token === '$' || token === '_') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is a story $variable or temporary _variable, reset it\n\t\t\t\t\t// to just its sigil—for later mapping.\n\t\t\t\t\telse if (varTest.test(token)) {\n\t\t\t\t\t\ttoken = token[0];\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is `is`, check to see if it's followed by `not`, if so,\n\t\t\t\t\t// convert them into the `isnot` operator.\n\t\t\t\t\t//\n\t\t\t\t\t// NOTE: This is a safety feature, since `$a is not $b` probably sounds\n\t\t\t\t\t// reasonable to most users.\n\t\t\t\t\telse if (token === 'is') {\n\t\t\t\t\t\tconst start = parseRe.lastIndex;\n\t\t\t\t\t\tconst ahead = code.slice(start);\n\n\t\t\t\t\t\tif (withNotTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcode = code.splice(start, ahead.search(notSpaceRe));\n\t\t\t\t\t\t\ttoken = 'isnot';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the token is followed by a colon, then it's likely to be an object\n\t\t\t\t\t// property, so skip it.\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst ahead = code.slice(parseRe.lastIndex);\n\n\t\t\t\t\t\tif (withColonTestRe.test(ahead)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the finalized token has a mapping, replace it within the code string\n\t\t\t\t\t// with its counterpart.\n\t\t\t\t\tif (tokenTable[token]) {\n\t\t\t\t\t\tcode = code.splice(\n\t\t\t\t\t\t\tmatch.index,      // starting index\n\t\t\t\t\t\t\ttoken.length,     // replace how many\n\t\t\t\t\t\t\ttokenTable[token] // replacement string\n\t\t\t\t\t\t);\n\t\t\t\t\t\tparseRe.lastIndex += tokenTable[token].length - token.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn code;\n\t\t}\n\n\t\treturn parse;\n\t})();\n\n\n\t/*******************************************************************************************************************\n\t\tEval Functions.\n\t*******************************************************************************************************************/\n\t/* eslint-disable no-eval, no-extra-parens, no-unused-vars */\n\t/*\n\t\tEvaluates the given JavaScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalJavaScript(code, output, data) {\n\t\treturn (function (code, output, evalJavaScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, String(code), output, data);\n\t}\n\n\t/*\n\t\tEvaluates the given TwineScript code and returns the result, throwing if there were errors.\n\t*/\n\tfunction evalTwineScript(code, output, data) {\n\t\t// NOTE: Do not move the dollar sign to the front of `evalTwineScript$Data$`,\n\t\t// as `parse()` will break references to it within the code string.\n\t\treturn (function (code, output, evalTwineScript$Data$) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, parse(String(code)), output, data);\n\t}\n\t/* eslint-enable no-eval, no-extra-parens, no-unused-vars */\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tparse           : { value : parse },\n\t\tevalJavaScript  : { value : evalJavaScript },\n\t\tevalTwineScript : { value : evalTwineScript }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/lexer.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n\nvar { // eslint-disable-line no-var\n\t/* eslint-disable no-unused-vars */\n\tEOF,\n\tLexer\n\t/* eslint-enable no-unused-vars */\n} = (() => {\n\t'use strict';\n\n\t// End of file (string, actually).\n\tconst EOF = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tLexer Class.\n\t*******************************************************************************************************************/\n\tclass Lexer {\n\t\tconstructor(source, initialState) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer constructor called with too few parameters (source:string , initialState:function)');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tthis.source  → the string to be scanned\n\t\t\t\tthis.initial → initial state\n\t\t\t\tthis.state   → current state\n\t\t\t\tthis.start   → start position of an item\n\t\t\t\tthis.pos     → current position in the source string\n\t\t\t\tthis.depth   → current brace/bracket/parenthesis nesting depth\n\t\t\t\tthis.items   → scanned item queue\n\t\t\t\tthis.data    → lexing data\n\t\t\t*/\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : source\n\t\t\t\t},\n\n\t\t\t\tinitial : {\n\t\t\t\t\tvalue : initialState\n\t\t\t\t},\n\n\t\t\t\tstate : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : initialState\n\t\t\t\t},\n\n\t\t\t\tstart : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tpos : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\tdepth : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\titems : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : []\n\t\t\t\t},\n\n\t\t\t\tdata : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : {}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treset() {\n\t\t\tthis.state  = this.initial;\n\t\t\tthis.start  = 0;\n\t\t\tthis.pos    = 0;\n\t\t\tthis.depth  = 0;\n\t\t\tthis.items  = [];\n\t\t\tthis.data   = {};\n\t\t}\n\n\t\trun() {\n\t\t\t// scan the source string until no states remain\n\t\t\twhile (this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the array of items\n\t\t\treturn this.items;\n\t\t}\n\n\t\tnextItem() {\n\t\t\t// scan the source string until we have an item or no states remain\n\t\t\twhile (this.items.length === 0 && this.state !== null) {\n\t\t\t\tthis.state = this.state(this);\n\t\t\t}\n\n\t\t\t// return the current item\n\t\t\treturn this.items.shift();\n\t\t}\n\n\t\tnext() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos++];\n\t\t}\n\n\t\tpeek() {\n\t\t\tif (this.pos >= this.source.length) {\n\t\t\t\treturn EOF;\n\t\t\t}\n\n\t\t\treturn this.source[this.pos];\n\t\t}\n\n\t\tbackup(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos -= num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t--this.pos;\n\t\t\t// }\n\t\t\tthis.pos -= num || 1;\n\t\t}\n\n\t\tforward(num) {\n\t\t\t// if (num) {\n\t\t\t// \tthis.pos += num;\n\t\t\t// }\n\t\t\t// else {\n\t\t\t// \t++this.pos;\n\t\t\t// }\n\t\t\tthis.pos += num || 1;\n\t\t}\n\n\t\tignore() {\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\taccept(valid) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (valid.includes(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRe(validRe) {\n\t\t\tconst ch = this.next();\n\n\t\t\tif (ch === EOF) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (validRe.test(ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t\treturn false;\n\t\t}\n\n\t\tacceptRun(valid) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!valid.includes(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\tacceptRunRe(validRe) {\n\t\t\tfor (;;) {\n\t\t\t\tconst ch = this.next();\n\n\t\t\t\tif (ch === EOF) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!validRe.test(ch)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.backup();\n\t\t}\n\n\t\temit(type) {\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\tthis.start = this.pos;\n\t\t}\n\n\t\terror(type, message) {\n\t\t\tif (arguments.length < 2) {\n\t\t\t\tthrow new Error('Lexer.prototype.error called with too few parameters (type:number , message:string)');\n\t\t\t}\n\n\t\t\tthis.items.push({\n\t\t\t\ttype,\n\t\t\t\tmessage,\n\t\t\t\ttext  : this.source.slice(this.start, this.pos),\n\t\t\t\tstart : this.start,\n\t\t\t\tpos   : this.pos\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic enumFromNames(names) {\n\t\t\tconst obj = names.reduce((obj, name, i) => {\n\t\t\t\tobj[name] = i; // eslint-disable-line no-param-reassign\n\t\t\t\treturn obj;\n\t\t\t}, {});\n\t\t\treturn Object.freeze(Object.assign(Object.create(null), obj));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn {\n\t\tEOF,\n\t\tLexer\n\t};\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/wikifier.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, EOF, Engine, Lexer, Patterns, Scripting, State, Story, TempState, Util, convertBreaks,\n\t       errorPrologRegExp\n*/\n\n/*\n\tTODO: The Wikifier, and associated code, could stand to receive a serious refactoring.\n*/\n/* eslint-disable max-len */\nvar Wikifier = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Wikifier call depth.\n\tlet _callDepth = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tWikifier Class.\n\t*******************************************************************************************************************/\n\tclass Wikifier {\n\t\tconstructor(destination, source, options) {\n\t\t\tif (Wikifier.Parser.Profile.isEmpty()) {\n\t\t\t\tWikifier.Parser.Profile.compile();\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// General Wikifier properties.\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : String(source)\n\t\t\t\t},\n\n\t\t\t\toptions : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Object.assign({\n\t\t\t\t\t\tprofile : 'all'\n\t\t\t\t\t}, options)\n\t\t\t\t},\n\n\t\t\t\tnextMatch : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : 0\n\t\t\t\t},\n\n\t\t\t\toutput : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t// Macro parser ('macro') related properties.\n\t\t\t\t_rawArgs : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : ''\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// No destination specified.  Create a fragment to act as the output buffer.\n\t\t\tif (destination == null) { // lazy equality for null\n\t\t\t\tthis.output = document.createDocumentFragment();\n\t\t\t}\n\n\t\t\t// jQuery-wrapped destination.  Grab the first element.\n\t\t\telse if (destination.jquery) { // cannot use `hasOwnProperty()` here as `jquery` is from jQuery's prototype\n\t\t\t\tthis.output = destination[0];\n\t\t\t}\n\n\t\t\t// Normal destination.\n\t\t\telse {\n\t\t\t\tthis.output = destination;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tWikify the source into the output buffer element, possibly converting line\n\t\t\t\tbreaks into paragraphs.\n\n\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\tto ensure that the call depth is properly restored in the event that an\n\t\t\t\tuncaught exception is thrown during the call to `subWikify()`.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\t++_callDepth;\n\n\t\t\t\tthis.subWikify(this.output);\n\n\t\t\t\t// Limit line break conversion to non-recursive calls.\n\t\t\t\tif (_callDepth === 1 && Config.cleanupWikifierOutput) {\n\t\t\t\t\tconvertBreaks(this.output);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t--_callDepth;\n\t\t\t}\n\t\t}\n\n\t\tsubWikify(output, terminator, options) {\n\t\t\t// Cache and temporarily replace the current output buffer.\n\t\t\tconst oldOutput = this.output;\n\t\t\tthis.output = output;\n\n\t\t\tlet newOptions;\n\t\t\tlet oldOptions;\n\n\t\t\t// Parser option overrides.\n\t\t\tif (Wikifier.Option.length > 0) {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, Wikifier.Option.options);\n\t\t\t}\n\t\t\t// Local parameter option overrides.\n\t\t\tif (options !== null && typeof options === 'object') {\n\t\t\t\tnewOptions = Object.assign(newOptions || {}, options);\n\t\t\t}\n\t\t\t// If new options exist, cache and temporarily replace the current options.\n\t\t\tif (newOptions) {\n\t\t\t\toldOptions = this.options;\n\t\t\t\tthis.options = Object.assign({}, this.options, newOptions);\n\t\t\t}\n\n\t\t\tconst parsersProfile   = Wikifier.Parser.Profile.get(this.options.profile);\n\t\t\tconst terminatorRegExp = terminator\n\t\t\t\t? new RegExp(`(?:${terminator})`, this.options.ignoreTerminatorCase ? 'gim' : 'gm')\n\t\t\t\t: null;\n\t\t\tlet terminatorMatch;\n\t\t\tlet parserMatch;\n\n\t\t\tdo {\n\t\t\t\t// Prepare the RegExp match positions.\n\t\t\t\tparsersProfile.parserRegExp.lastIndex = this.nextMatch;\n\n\t\t\t\tif (terminatorRegExp) {\n\t\t\t\t\tterminatorRegExp.lastIndex = this.nextMatch;\n\t\t\t\t}\n\n\t\t\t\t// Get the first matches.\n\t\t\t\tparserMatch     = parsersProfile.parserRegExp.exec(this.source);\n\t\t\t\tterminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;\n\n\t\t\t\t// Try for a terminator match, unless there's a closer parser match.\n\t\t\t\tif (terminatorMatch && (!parserMatch || terminatorMatch.index <= parserMatch.index)) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (terminatorMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, terminatorMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = terminatorMatch.index;\n\t\t\t\t\tthis.matchLength = terminatorMatch[0].length;\n\t\t\t\t\tthis.matchText   = terminatorMatch[0];\n\t\t\t\t\tthis.nextMatch   = terminatorRegExp.lastIndex;\n\n\t\t\t\t\t// Restore the original output buffer and options.\n\t\t\t\t\tthis.output = oldOutput;\n\n\t\t\t\t\tif (oldOptions) {\n\t\t\t\t\t\tthis.options = oldOptions;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Exit.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try for a parser match.\n\t\t\t\telse if (parserMatch) {\n\t\t\t\t\t// Output any text before the match.\n\t\t\t\t\tif (parserMatch.index > this.nextMatch) {\n\t\t\t\t\t\tthis.outputText(this.output, this.nextMatch, parserMatch.index);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the match parameters.\n\t\t\t\t\tthis.matchStart  = parserMatch.index;\n\t\t\t\t\tthis.matchLength = parserMatch[0].length;\n\t\t\t\t\tthis.matchText   = parserMatch[0];\n\t\t\t\t\tthis.nextMatch   = parsersProfile.parserRegExp.lastIndex;\n\n\t\t\t\t\t// Figure out which parser matched.\n\t\t\t\t\tlet matchingParser;\n\n\t\t\t\t\tfor (let i = 1, iend = parserMatch.length; i < iend; ++i) {\n\t\t\t\t\t\tif (parserMatch[i]) {\n\t\t\t\t\t\t\tmatchingParser = i - 1;\n\t\t\t\t\t\t\tbreak; // stop once we've found the matching parser\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call the parser.\n\t\t\t\t\tparsersProfile.parsers[matchingParser].handler(this);\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (terminatorMatch || parserMatch);\n\n\t\t\t// Output any text after the last match.\n\t\t\tif (TempState.break == null) { // lazy equality for null\n\t\t\t\tif (this.nextMatch < this.source.length) {\n\t\t\t\t\tthis.outputText(this.output, this.nextMatch, this.source.length);\n\t\t\t\t\tthis.nextMatch = this.source.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In case of <<break>>/<<continue>>, remove the last <br>.\n\t\t\telse if (\n\t\t\t\t   this.output.lastChild\n\t\t\t\t&& this.output.lastChild.nodeType === Node.ELEMENT_NODE\n\t\t\t\t&& this.output.lastChild.nodeName.toUpperCase() === 'BR'\n\t\t\t) {\n\t\t\t\tjQuery(this.output.lastChild).remove();\n\t\t\t}\n\n\t\t\t// Restore the original output buffer and options.\n\t\t\tthis.output = oldOutput;\n\n\t\t\tif (oldOptions) {\n\t\t\t\tthis.options = oldOptions;\n\t\t\t}\n\t\t}\n\n\t\toutputText(destination, startPos, endPos) {\n\t\t\tdestination.appendChild(document.createTextNode(this.source.substring(startPos, endPos)));\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the raw, unprocessed\n\t\t\ttext given to the currently executing macro.\n\t\t*/\n\t\trawArgs() {\n\t\t\treturn this._rawArgs;\n\t\t}\n\n\t\t/*\n\t\t\t[DEPRECATED] Meant to be called by legacy macros, this returns the text given to\n\t\t\tthe currently executing macro after doing TwineScript-to-JavaScript transformations.\n\t\t*/\n\t\tfullArgs() {\n\t\t\treturn Scripting.parse(this._rawArgs);\n\t\t}\n\n\t\t/*\n\t\t\tReturns the output generated by wikifying the given text, throwing if there were errors.\n\t\t*/\n\t\tstatic wikifyEval(text) {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\tnew Wikifier(output, text);\n\n\t\t\tconst errors = output.querySelector('.error');\n\n\t\t\tif (errors !== null) {\n\t\t\t\tthrow new Error(errors.textContent.replace(errorPrologRegExp, ''));\n\t\t\t}\n\n\t\t\treturn output;\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an internal link.\n\t\t*/\n\t\tstatic createInternalLink(destination, passage, text, callback) {\n\t\t\tconst $link = jQuery(document.createElement('a'));\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\n\t\t\t\t$link.ariaClick({ one : true }, () => {\n\t\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\n\t\t\t\t\tEngine.play(passage);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (text) {\n\t\t\t\t$link.append(document.createTextNode(text));\n\t\t\t}\n\n\t\t\tif (destination) {\n\t\t\t\t$link.appendTo(destination);\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tCreate and return an external link.\n\t\t*/\n\t\tstatic createExternalLink(destination, url, text) {\n\t\t\tconst $link = jQuery(document.createElement('a'))\n\t\t\t\t.attr('target', '_blank')\n\t\t\t\t.addClass('link-external')\n\t\t\t\t.text(text)\n\t\t\t\t.appendTo(destination);\n\n\t\t\tif (url != null) { // lazy equality for null\n\t\t\t\t$link.attr({\n\t\t\t\t\thref     : url,\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For legacy-compatibility we must return the DOM node.\n\t\t\treturn $link[0];\n\t\t}\n\n\t\t/*\n\t\t\tReturns whether the given link source is external (probably).\n\t\t*/\n\t\tstatic isExternalLink(link) {\n\t\t\tif (Story.has(link)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst urlRegExp = new RegExp(`^${Patterns.url}`, 'gim');\n\t\t\treturn urlRegExp.test(link) || /[/.?#]/.test(link);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tOption Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Option', {\n\t\tvalue : (() => {\n\t\t\t// Options array (stack).\n\t\t\tlet _optionsStack = [];\n\n\n\t\t\t/*\n\t\t\t\tGlobalOption Functions.\n\t\t\t*/\n\t\t\tfunction optionLength() {\n\t\t\t\treturn _optionsStack.length;\n\t\t\t}\n\n\t\t\tfunction optionGetter() {\n\t\t\t\treturn Object.assign({}, ..._optionsStack);\n\t\t\t}\n\n\t\t\tfunction optionClear() {\n\t\t\t\t_optionsStack = [];\n\t\t\t}\n\n\t\t\tfunction optionGet(idx) {\n\t\t\t\treturn _optionsStack[idx];\n\t\t\t}\n\n\t\t\tfunction optionPop() {\n\t\t\t\treturn _optionsStack.pop();\n\t\t\t}\n\n\t\t\tfunction optionPush(options) {\n\t\t\t\tif (typeof options !== 'object' || options === null) {\n\t\t\t\t\tthrow new TypeError(`Wikifier.Option.push options parameter must be an object (received: ${Util.getType(options)})`);\n\t\t\t\t}\n\n\t\t\t\treturn _optionsStack.push(options);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\tlength  : { get : optionLength },\n\t\t\t\toptions : { get : optionGetter },\n\t\t\t\tclear   : { value : optionClear },\n\t\t\t\tget     : { value : optionGet },\n\t\t\t\tpop     : { value : optionPop },\n\t\t\t\tpush    : { value : optionPush }\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tParser Static Object.\n\t*******************************************************************************************************************/\n\tObject.defineProperty(Wikifier, 'Parser', {\n\t\tvalue : (() => {\n\t\t\t// Parser definition array.  Ordering matters, so this must be an ordered list.\n\t\t\tconst _parsers = [];\n\n\t\t\t// Parser profiles object.\n\t\t\tlet _profiles;\n\n\n\t\t\t/*\n\t\t\t\tParser Functions.\n\t\t\t*/\n\t\t\tfunction parsersGetter() {\n\t\t\t\treturn _parsers;\n\t\t\t}\n\n\t\t\tfunction parsersAdd(parser) {\n\t\t\t\t// Parser object sanity checks.\n\t\t\t\tif (typeof parser !== 'object') {\n\t\t\t\t\tthrow new Error('Wikifier.Parser.add parser parameter must be an object');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('name')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"name\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.name !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"name\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('match')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"match\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.match !== 'string') {\n\t\t\t\t\tthrow new Error('parser object \"match\" property must be a string');\n\t\t\t\t}\n\n\t\t\t\tif (!parser.hasOwnProperty('handler')) {\n\t\t\t\t\tthrow new Error('parser object missing required \"handler\" property');\n\t\t\t\t}\n\t\t\t\telse if (typeof parser.handler !== 'function') {\n\t\t\t\t\tthrow new Error('parser object \"handler\" property must be a function');\n\t\t\t\t}\n\n\t\t\t\tif (parser.hasOwnProperty('profiles') && !Array.isArray(parser.profiles)) {\n\t\t\t\t\tthrow new Error('parser object \"profiles\" property must be an array');\n\t\t\t\t}\n\n\t\t\t\t// Check for an existing parser with the same name.\n\t\t\t\tif (parsersHas(parser.name)) {\n\t\t\t\t\tthrow new Error(`cannot clobber existing parser \"${parser.name}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Add the parser to the end of the array.\n\t\t\t\t_parsers.push(parser);\n\t\t\t}\n\n\t\t\tfunction parsersDelete(name) {\n\t\t\t\tconst parser = _parsers.find(parser => parser.name === name);\n\n\t\t\t\tif (parser) {\n\t\t\t\t\t_parsers.delete(parser);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction parsersIsEmpty() {\n\t\t\t\treturn _parsers.length === 0;\n\t\t\t}\n\n\t\t\tfunction parsersHas(name) {\n\t\t\t\treturn !!_parsers.find(parser => parser.name === name);\n\t\t\t}\n\n\t\t\tfunction parsersGet(name) {\n\t\t\t\treturn _parsers.find(parser => parser.name === name) || null;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tParser Profile Functions.\n\t\t\t*/\n\t\t\tfunction profilesGetter() {\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesCompile() {\n\t\t\t\tif (DEBUG) { console.log('[Wikifier.Parser/profilesCompile()]'); }\n\n\t\t\t\tconst all  = _parsers;\n\t\t\t\tconst core = all.filter(parser => !Array.isArray(parser.profiles) || parser.profiles.includes('core'));\n\n\t\t\t\t_profiles = Object.freeze({\n\t\t\t\t\tall : {\n\t\t\t\t\t\tparsers      : all,\n\t\t\t\t\t\tparserRegExp : new RegExp(all.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t},\n\t\t\t\t\tcore : {\n\t\t\t\t\t\tparsers      : core,\n\t\t\t\t\t\tparserRegExp : new RegExp(core.map(parser => `(${parser.match})`).join('|'), 'gm')\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn _profiles;\n\t\t\t}\n\n\t\t\tfunction profilesIsEmpty() {\n\t\t\t\treturn typeof _profiles !== 'object' || Object.keys(_profiles).length === 0;\n\t\t\t}\n\n\t\t\tfunction profilesGet(profile) {\n\t\t\t\tif (typeof _profiles !== 'object' || !_profiles.hasOwnProperty(profile)) {\n\t\t\t\t\tthrow new Error(`nonexistent parser profile \"${profile}\"`);\n\t\t\t\t}\n\n\t\t\t\treturn _profiles[profile];\n\t\t\t}\n\n\t\t\tfunction profilesHas(profile) {\n\t\t\t\treturn typeof _profiles === 'object' && _profiles.hasOwnProperty(profile);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t\tExports.\n\t\t\t*/\n\t\t\treturn Object.freeze(Object.defineProperties({}, {\n\t\t\t\t/*\n\t\t\t\t\tParser Containers.\n\t\t\t\t*/\n\t\t\t\tparsers : { get : parsersGetter },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Functions.\n\t\t\t\t*/\n\t\t\t\tadd     : { value : parsersAdd },\n\t\t\t\tdelete  : { value : parsersDelete },\n\t\t\t\tisEmpty : { value : parsersIsEmpty },\n\t\t\t\thas     : { value : parsersHas },\n\t\t\t\tget     : { value : parsersGet },\n\n\t\t\t\t/*\n\t\t\t\t\tParser Profile.\n\t\t\t\t*/\n\t\t\t\tProfile : {\n\t\t\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Containers.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tprofiles : { get : profilesGetter },\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tProfiles Functions.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tcompile : { value : profilesCompile },\n\t\t\t\t\t\tisEmpty : { value : profilesIsEmpty },\n\t\t\t\t\t\thas     : { value : profilesHas },\n\t\t\t\t\t\tget     : { value : profilesGet }\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t}));\n\t\t})()\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAdditional Static Properties.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier, {\n\t\thelpers : { value : {} },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tgetValue       : { value : State.getVar },              // SEE: `state.js`.\n\t\tsetValue       : { value : State.setVar },              // SEE: `state.js`.\n\t\tparse          : { value : Scripting.parse },           // SEE: `markup/scripting.js`.\n\t\tevalExpression : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\tevalStatements : { value : Scripting.evalTwineScript }, // SEE: `markup/scripting.js`.\n\t\ttextPrimitives : { value : Patterns }                   // SEE: `lib/patterns.js`.\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tHelper Static Methods.\n\t*******************************************************************************************************************/\n\tObject.defineProperties(Wikifier.helpers, {\n\t\tinlineCss : {\n\t\t\tvalue : (() => {\n\t\t\t\tconst lookaheadRe = new RegExp(Patterns.inlineCss, 'gm');\n\t\t\t\tconst idOrClassRe = new RegExp(`(${Patterns.cssIdOrClassSigil})(${Patterns.anyLetter}+)`, 'g');\n\n\t\t\t\tfunction helperInlineCss(w) {\n\t\t\t\t\tconst css = { classes : [], id : '', styles : {} };\n\t\t\t\t\tlet matched;\n\n\t\t\t\t\tdo {\n\t\t\t\t\t\tlookaheadRe.lastIndex = w.nextMatch;\n\n\t\t\t\t\t\tconst match = lookaheadRe.exec(w.source);\n\n\t\t\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\tif (match[1]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[1])] = match[2].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[3]) {\n\t\t\t\t\t\t\t\tcss.styles[Util.fromCssProperty(match[3])] = match[4].trim();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (match[5]) {\n\t\t\t\t\t\t\t\tlet subMatch;\n\n\t\t\t\t\t\t\t\tidOrClassRe.lastIndex = 0; // NOTE: Guard against buggy implementations.\n\n\t\t\t\t\t\t\t\twhile ((subMatch = idOrClassRe.exec(match[5])) !== null) {\n\t\t\t\t\t\t\t\t\tif (subMatch[1] === '.') {\n\t\t\t\t\t\t\t\t\t\tcss.classes.push(subMatch[2]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tcss.id = subMatch[2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tw.nextMatch = lookaheadRe.lastIndex; // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (matched);\n\n\t\t\t\t\treturn css;\n\t\t\t\t}\n\n\t\t\t\treturn helperInlineCss;\n\t\t\t})()\n\t\t},\n\n\t\tevalText : {\n\t\t\tvalue(text) {\n\t\t\t\tlet result;\n\n\t\t\t\ttry {\n\t\t\t\t\tresult = Scripting.evalTwineScript(text);\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tAttempt to prevent the leakage of auto-globals by enforcing that\n\t\t\t\t\t\tthe resultant value be either a string or a number.\n\n\t\t\t\t\t\tNOTE: This is not a foolproof solution to the problem of auto-global\n\t\t\t\t\t\tleakage.  Various auto-globals, which return strings or numbers, can\n\t\t\t\t\t\tstill leak through—e.g. `window.status` → string.\n\t\t\t\t\t*/\n\t\t\t\t\tswitch (typeof result) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tif (result.trim() === '') {\n\t\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tresult = String(result);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresult = text;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tresult = text;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t},\n\n\t\tevalPassageId : {\n\t\t\tvalue(passage) {\n\t\t\t\tif (passage == null || Story.has(passage)) { // lazy equality for null; `0` is a valid name, so we cannot simply evaluate `passage`\n\t\t\t\t\treturn passage;\n\t\t\t\t}\n\n\t\t\t\treturn Wikifier.helpers.evalText(passage);\n\t\t\t}\n\t\t},\n\n\t\thasBlockContext : {\n\t\t\tvalue(nodes) {\n\t\t\t\tconst hasGCS = typeof window.getComputedStyle === 'function';\n\n\t\t\t\tfor (let i = nodes.length - 1; i >= 0; --i) {\n\t\t\t\t\tconst node = nodes[i];\n\n\t\t\t\t\tswitch (node.nodeType) {\n\t\t\t\t\tcase Node.ELEMENT_NODE:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst tagName = node.nodeName.toUpperCase();\n\n\t\t\t\t\t\t\tif (tagName === 'BR') {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst styles = hasGCS ? window.getComputedStyle(node, null) : node.currentStyle;\n\n\t\t\t\t\t\t\tif (styles && styles.display) {\n\t\t\t\t\t\t\t\tif (styles.display === 'none') {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn styles.display === 'block';\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tWebKit/Blink-based browsers do not attach any computed style\n\t\t\t\t\t\t\t\tinformation to elements until they're inserted into the DOM\n\t\t\t\t\t\t\t\t(and probably visible), not even the default browser styles\n\t\t\t\t\t\t\t\tand any user styles.  So, we make an assumption based on the\n\t\t\t\t\t\t\t\telement.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\t\tcase 'ADDRESS':\n\t\t\t\t\t\t\tcase 'ARTICLE':\n\t\t\t\t\t\t\tcase 'ASIDE':\n\t\t\t\t\t\t\tcase 'BLOCKQUOTE':\n\t\t\t\t\t\t\tcase 'CENTER':\n\t\t\t\t\t\t\tcase 'DIV':\n\t\t\t\t\t\t\tcase 'DL':\n\t\t\t\t\t\t\tcase 'FIGURE':\n\t\t\t\t\t\t\tcase 'FOOTER':\n\t\t\t\t\t\t\tcase 'FORM':\n\t\t\t\t\t\t\tcase 'H1':\n\t\t\t\t\t\t\tcase 'H2':\n\t\t\t\t\t\t\tcase 'H3':\n\t\t\t\t\t\t\tcase 'H4':\n\t\t\t\t\t\t\tcase 'H5':\n\t\t\t\t\t\t\tcase 'H6':\n\t\t\t\t\t\t\tcase 'HEADER':\n\t\t\t\t\t\t\tcase 'HR':\n\t\t\t\t\t\t\tcase 'MAIN':\n\t\t\t\t\t\t\tcase 'NAV':\n\t\t\t\t\t\t\tcase 'OL':\n\t\t\t\t\t\t\tcase 'P':\n\t\t\t\t\t\t\tcase 'PRE':\n\t\t\t\t\t\t\tcase 'SECTION':\n\t\t\t\t\t\t\tcase 'TABLE':\n\t\t\t\t\t\t\tcase 'UL':\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tcase Node.COMMENT_NODE:\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\n\t\tcreateShadowSetterCallback : {\n\t\t\tvalue : (() => {\n\t\t\t\tlet macroParser = null;\n\n\t\t\t\tfunction cacheMacroParser() {\n\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\tmacroParser = Wikifier.Parser.get('macro');\n\n\t\t\t\t\t\tif (!macroParser) {\n\t\t\t\t\t\t\tthrow new Error('cannot find \"macro\" parser');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn macroParser;\n\t\t\t\t}\n\n\t\t\t\tfunction getMacroContextShadowView() {\n\t\t\t\t\tconst macro = macroParser || cacheMacroParser();\n\t\t\t\t\tconst view  = new Set();\n\n\t\t\t\t\tfor (let context = macro.context; context !== null; context = context.parent) {\n\t\t\t\t\t\tif (context._shadows) {\n\t\t\t\t\t\t\tcontext._shadows.forEach(name => view.add(name));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [...view];\n\t\t\t\t}\n\n\t\t\t\tfunction helperCreateShadowSetterCallback(code) {\n\t\t\t\t\tconst shadowStore = {};\n\n\t\t\t\t\tgetMacroContextShadowView().forEach(varName => {\n\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t\t});\n\n\t\t\t\t\treturn function () {\n\t\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\t\tevaluation.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Evaluate the JavaScript.\n\t\t\t\t\t\t\treturn Scripting.evalJavaScript(code);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn helperCreateShadowSetterCallback;\n\t\t\t})()\n\t\t},\n\n\t\tparseSquareBracketedMarkup : {\n\t\t\tvalue : (() => {\n\t\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t\t'Error',     // error\n\t\t\t\t\t'DelimLTR',  // '|' or '->'\n\t\t\t\t\t'DelimRTL',  // '<-'\n\t\t\t\t\t'InnerMeta', // ']['\n\t\t\t\t\t'ImageMeta', // '[img[', '[<img[', or '[>img['\n\t\t\t\t\t'LinkMeta',  // '[['\n\t\t\t\t\t'Link',      // link destination\n\t\t\t\t\t'RightMeta', // ']]'\n\t\t\t\t\t'Setter',    // setter expression\n\t\t\t\t\t'Source',    // image source\n\t\t\t\t\t'Text'       // link text or image alt text\n\t\t\t\t]);\n\t\t\t\tconst Delim = Lexer.enumFromNames([ // delimiter state object (pseudo-enumeration)\n\t\t\t\t\t'None', // no delimiter encountered\n\t\t\t\t\t'LTR',  // '|' or '->'\n\t\t\t\t\t'RTL'   // '<-'\n\t\t\t\t]);\n\n\t\t\t\t// Lexing functions.\n\t\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\t\tloop: for (;;) {\n\t\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/* falls through */\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t\t}\n\n\t\t\t\t\treturn lexer.pos;\n\t\t\t\t}\n\n\t\t\t\tfunction lexLeftMeta(lexer) {\n\t\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t}\n\n\t\t\t\t\t// Is link markup.\n\t\t\t\t\tif (lexer.accept('[')) {\n\t\t\t\t\t\tlexer.data.isLink = true;\n\t\t\t\t\t\tlexer.emit(Item.LinkMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\t// May be image markup.\n\t\t\t\t\telse {\n\t\t\t\t\t\tlexer.accept('<>'); // aligner syntax\n\n\t\t\t\t\t\tif (!lexer.accept('Ii') || !lexer.accept('Mm') || !lexer.accept('Gg') || !lexer.accept('[')) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, 'malformed square-bracketed markup');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlexer.data.isLink = false;\n\t\t\t\t\t\tlexer.emit(Item.ImageMeta);\n\t\t\t\t\t}\n\n\t\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\t\t\t\t\treturn lexCoreComponents;\n\t\t\t\t}\n\n\t\t\t\tfunction lexCoreComponents(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\t\t\t\t\tlet delim = Delim.None;\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '|': // possible pipe ('|') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None) {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward();\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '-': // possible right arrow ('->') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '>') {\n\t\t\t\t\t\t\t\tdelim = Delim.LTR;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimLTR);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '<': // possible left arrow ('<-') delimiter\n\t\t\t\t\t\t\tif (delim === Delim.None && lexer.peek() === '-') {\n\t\t\t\t\t\t\t\tdelim = Delim.RTL;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.DelimRTL);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexer.data.isLink ? lexSetter : lexImageLink;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\n\t\t\t\t\t\t\t\t\tif (delim === Delim.RTL) {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(Item.Text);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tlexer.emit(lexer.data.isLink ? Item.Link : Item.Source);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexImageLink(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tThis is not entirely reliable within sections that allow raw strings, since\n\t\t\t\t\t\t\t\tit's possible, however unlikely, for a raw string to contain unpaired double\n\t\t\t\t\t\t\t\tquotes.  The likelihood is low enough, however, that I'm deeming the risk as\n\t\t\t\t\t\t\t\tacceptable—for now, at least.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup link component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tswitch (lexer.peek()) {\n\t\t\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.InnerMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn lexSetter;\n\n\t\t\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.Link);\n\t\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction lexSetter(lexer) {\n\t\t\t\t\tconst what = lexer.data.isLink ? 'link' : 'image';\n\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\t\tcase EOF:\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\t\tcase '\"':\n\t\t\t\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated double quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated single quoted string in ${what} markup setter component`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase '[':\n\t\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase ']':\n\t\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\t\tif (lexer.peek() !== ']') {\n\t\t\t\t\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t\t\tlexer.emit(Item.Setter);\n\t\t\t\t\t\t\t\tlexer.forward(2);\n\t\t\t\t\t\t\t\tlexer.emit(Item.RightMeta);\n\t\t\t\t\t\t\t\t// lexer.ignore();\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Parse function.\n\t\t\t\tfunction parseSquareBracketedMarkup(w) {\n\t\t\t\t\t// Initialize the lexer.\n\t\t\t\t\tconst lexer  = new Lexer(w.source, lexLeftMeta);\n\n\t\t\t\t\t// Set the initial positions within the source string.\n\t\t\t\t\tlexer.start = lexer.pos = w.matchStart;\n\n\t\t\t\t\t// Lex the raw argument string.\n\t\t\t\t\tconst markup = {};\n\t\t\t\t\tconst items  = lexer.run();\n\t\t\t\t\tconst last   = items.last();\n\n\t\t\t\t\tif (last && last.type === Item.Error) {\n\t\t\t\t\t\tmarkup.error = last.message;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\titems.forEach(item => {\n\t\t\t\t\t\t\tconst text = item.text.trim();\n\n\t\t\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\t\t\tcase Item.ImageMeta:\n\t\t\t\t\t\t\t\tmarkup.isImage = true;\n\n\t\t\t\t\t\t\t\tif (text[1] === '<') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'left';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (text[1] === '>') {\n\t\t\t\t\t\t\t\t\tmarkup.align = 'right';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.LinkMeta:\n\t\t\t\t\t\t\t\tmarkup.isLink = true;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Link:\n\t\t\t\t\t\t\t\tif (text[0] === '~') {\n\t\t\t\t\t\t\t\t\tmarkup.forceInternal = true;\n\t\t\t\t\t\t\t\t\tmarkup.link = text.slice(1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tmarkup.link = text;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Setter:\n\t\t\t\t\t\t\t\tmarkup.setter = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Source:\n\t\t\t\t\t\t\t\tmarkup.source = text;\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase Item.Text:\n\t\t\t\t\t\t\t\tmarkup.text = text;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tmarkup.pos = lexer.pos;\n\t\t\t\t\treturn markup;\n\t\t\t\t}\n\n\t\t\t\treturn parseSquareBracketedMarkup;\n\t\t\t\t/* eslint-enable no-param-reassign */\n\t\t\t})()\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Wikifier;\n})();\n/* eslint-enable max-len */\n\n/***********************************************************************************************************************\n\n\tmarkup/parserlib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, EOF, Engine, Lexer, Macro, MacroContext, Patterns, Scripting, State, Story, Template,\n\t       Wikifier, toStringOrDefault, throwError\n*/\n/* eslint \"no-param-reassign\": [ 2, { \"props\" : false } ] */\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _verbatimTagHandler(w) {\n\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\tconst match = this.lookahead.exec(w.source);\n\n\t\tif (match && match.index === w.matchStart) {\n\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(match[1])\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tParsers.\n\t*******************************************************************************************************************/\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByBlock',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^<<<\\\\n',\n\t\tterminator : '^<<<\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t.appendTo(w.output)\n\t\t\t\t\t.get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'quoteByLine',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^>+',\n\t\tlookahead  : /^>+/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curLevel = 0;\n\t\t\tlet newLevel = w.matchLength;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement('blockquote'))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcurLevel = newLevel;\n\t\t\t\tw.subWikify(destStack[destStack.length - 1], this.terminator);\n\t\t\t\tjQuery(document.createElement('br')).appendTo(destStack[destStack.length - 1]);\n\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tnewLevel = match[0].length;\n\t\t\t\t\tw.nextMatch += match[0].length;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'macro',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<<',\n\t\tlookahead : new RegExp(`<<(/?${Patterns.macroName})(?:\\\\s*)((?:(?:\\`(?:\\\\\\\\.|[^\\`\\\\\\\\])*\\`)|(?:\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")|(?:'(?:\\\\\\\\.|[^'\\\\\\\\])*')|(?:\\\\[(?:[<>]?[Ii][Mm][Gg])?\\\\[[^\\\\r\\\\n]*?\\\\]\\\\]+)|[^>]|(?:>(?!>)))*)>>`, 'gm'),\n\t\tworking   : { source : '', name : '', arguments : '', index : 0 }, // the working parse object\n\t\tcontext   : null, // last execution context object (top-level macros, hierarchically, have a null context)\n\n\t\thandler(w) {\n\t\t\tconst matchStart = this.lookahead.lastIndex = w.matchStart;\n\n\t\t\tif (this.parseTag(w)) {\n\t\t\t\t/*\n\t\t\t\t\tIf `parseBody()` is called below, it will modify the current working\n\t\t\t\t\tvalues, so we must cache them now.\n\t\t\t\t*/\n\t\t\t\tconst nextMatch = w.nextMatch;\n\t\t\t\tconst name      = this.working.name;\n\t\t\t\tconst rawArgs   = this.working.arguments;\n\t\t\t\tlet macro;\n\n\t\t\t\ttry {\n\t\t\t\t\tmacro = Macro.get(name);\n\n\t\t\t\t\tif (macro) {\n\t\t\t\t\t\tlet payload = null;\n\n\t\t\t\t\t\tif (typeof macro.tags !== 'undefined') {\n\t\t\t\t\t\t\tpayload = this.parseBody(w, macro);\n\n\t\t\t\t\t\t\tif (!payload) {\n\t\t\t\t\t\t\t\tw.nextMatch = nextMatch; // we must reset `w.nextMatch` here, as `parseBody()` modifies it\n\t\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t\t`cannot find a closing tag for macro <<${name}>>`,\n\t\t\t\t\t\t\t\t\t`${w.source.slice(matchStart, w.nextMatch)}\\u2026`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (typeof macro.handler === 'function') {\n\t\t\t\t\t\t\tconst args = !payload\n\t\t\t\t\t\t\t\t? this.createArgs(rawArgs, this.skipArgs(macro, macro.name))\n\t\t\t\t\t\t\t\t: payload[0].args;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tNew-style macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (typeof macro._MACRO_API !== 'undefined') {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tAdd the macro's execution context to the context chain.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tthis.context = new MacroContext({\n\t\t\t\t\t\t\t\t\tmacro,\n\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\t\t\tsource : w.source.slice(matchStart, w.nextMatch),\n\t\t\t\t\t\t\t\t\tparent : this.context,\n\t\t\t\t\t\t\t\t\tparser : w\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the execution context is properly restored in the event\n\t\t\t\t\t\t\t\t\tthat an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler.call(this.context);\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\tQUESTION: Swap to the following, which passes macro arguments in\n\t\t\t\t\t\t\t\t\t\tas parameters to the handler function, in addition to them being\n\t\t\t\t\t\t\t\t\t\tavailable on its `this`?  If so, it might still be something to\n\t\t\t\t\t\t\t\t\t\thold off on until v3, when the legacy macro API is removed.\n\n\t\t\t\t\t\t\t\t\t\tmacro.handler.apply(this.context, this.context.args);\n\t\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tthis.context = this.context.parent;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t[DEPRECATED] Old-style/legacy macros.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tSet up the raw arguments string.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\tconst prevRawArgs = w._rawArgs;\n\t\t\t\t\t\t\t\tw._rawArgs = rawArgs;\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tCall the handler.\n\n\t\t\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists solely\n\t\t\t\t\t\t\t\t\tto ensure that the previous raw arguments string is properly restored in\n\t\t\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the handler call.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tmacro.handler(w.output, name, args, w, payload);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t\tw._rawArgs = prevRawArgs;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`macro <<${name}>> handler function ${typeof macro.handler === 'undefined' ? 'does not exist' : 'is not a function'}`,\n\t\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (Macro.tags.has(name)) {\n\t\t\t\t\t\tconst tags = Macro.tags.get(name);\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`child tag <<${name}>> was found outside of a call to its parent macro${tags.length === 1 ? '' : 's'} <<${tags.join('>>, <<')}>>`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`macro <<${name}>> does not exist`,\n\t\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute ${macro && macro.isWidget ? 'widget' : 'macro'} <<${name}>>: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.working.source    = '';\n\t\t\t\t\tthis.working.name      = '';\n\t\t\t\t\tthis.working.arguments = '';\n\t\t\t\t\tthis.working.index     = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t}\n\t\t},\n\n\t\tparseTag(w) {\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart && match[1]) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tthis.working.source    = w.source.slice(match.index, this.lookahead.lastIndex);\n\t\t\t\tthis.working.name      = match[1];\n\t\t\t\tthis.working.arguments = match[2];\n\t\t\t\tthis.working.index     = match.index;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseBody(w, macro) {\n\t\t\tconst openTag  = this.working.name;\n\t\t\tconst closeTag = `/${openTag}`;\n\t\t\tconst closeAlt = `end${openTag}`;\n\t\t\tconst bodyTags = Array.isArray(macro.tags) ? macro.tags : false;\n\t\t\tconst payload  = [];\n\t\t\tlet end          = -1;\n\t\t\tlet opened       = 1;\n\t\t\tlet curSource    = this.working.source;\n\t\t\tlet curTag       = this.working.name;\n\t\t\tlet curArgument  = this.working.arguments;\n\t\t\tlet contentStart = w.nextMatch;\n\n\t\t\twhile ((w.matchStart = w.source.indexOf(this.match, w.nextMatch)) !== -1) {\n\t\t\t\tif (!this.parseTag(w)) {\n\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = w.matchStart + this.match.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst tagSource = this.working.source;\n\t\t\t\tconst tagName   = this.working.name;\n\t\t\t\tconst tagArgs   = this.working.arguments;\n\t\t\t\tconst tagBegin  = this.working.index;\n\t\t\t\tconst tagEnd    = w.nextMatch;\n\t\t\t\tconst hasArgs   = tagArgs.trim() !== '';\n\n\t\t\t\tswitch (tagName) {\n\t\t\t\tcase openTag:\n\t\t\t\t\t++opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase closeAlt:\n\t\t\t\tcase closeTag:\n\t\t\t\t\tif (hasArgs) {\n\t\t\t\t\t\t// Skip over malformed closing tags and throw.\n\t\t\t\t\t\tw.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tthrow new Error(`malformed closing tag: \"${tagSource}\"`);\n\t\t\t\t\t}\n\t\t\t\t\t--opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (hasArgs && (tagName.startsWith('/') || tagName.startsWith('end'))) {\n\t\t\t\t\t\t// Skip over malformed alien closing tags.\n\t\t\t\t\t\tthis.lookahead.lastIndex = w.nextMatch = tagBegin + 2 + tagName.length;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (opened === 1 && bodyTags) {\n\t\t\t\t\t\tfor (let i = 0, iend = bodyTags.length; i < iend; ++i) {\n\t\t\t\t\t\t\tif (tagName === bodyTags[i]) {\n\t\t\t\t\t\t\t\tpayload.push({\n\t\t\t\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tcurSource    = tagSource;\n\t\t\t\t\t\t\t\tcurTag       = tagName;\n\t\t\t\t\t\t\t\tcurArgument  = tagArgs;\n\t\t\t\t\t\t\t\tcontentStart = tagEnd;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (opened === 0) {\n\t\t\t\t\tpayload.push({\n\t\t\t\t\t\tsource    : curSource,\n\t\t\t\t\t\tname      : curTag,\n\t\t\t\t\t\targuments : curArgument,\n\t\t\t\t\t\targs      : this.createArgs(curArgument, this.skipArgs(macro, curTag)),\n\t\t\t\t\t\tcontents  : w.source.slice(contentStart, tagBegin)\n\t\t\t\t\t});\n\t\t\t\t\tend = tagEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (end !== -1) {\n\t\t\t\tw.nextMatch = end;\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tcreateArgs(rawArgsString, skipArgs) {\n\t\t\tconst args = skipArgs ? [] : this.parseArgs(rawArgsString);\n\n\t\t\t// Extend the args array with the raw and full argument strings.\n\t\t\tObject.defineProperties(args, {\n\t\t\t\traw : {\n\t\t\t\t\tvalue : rawArgsString\n\t\t\t\t},\n\t\t\t\tfull : {\n\t\t\t\t\tvalue : Scripting.parse(rawArgsString)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn args;\n\t\t},\n\n\t\tskipArgs(macro, tagName) {\n\t\t\tif (typeof macro.skipArgs !== 'undefined') {\n\t\t\t\tconst sa = macro.skipArgs;\n\n\t\t\t\treturn typeof sa === 'boolean' && sa || Array.isArray(sa) && sa.includes(tagName);\n\t\t\t}\n\t\t\t/* legacy */\n\t\t\telse if (typeof macro.skipArg0 !== 'undefined') {\n\t\t\t\treturn macro.skipArg0 && macro.name === tagName;\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\treturn false;\n\t\t},\n\n\t\tparseArgs : (() => {\n\t\t\tconst Item = Lexer.enumFromNames([ // lex item types object (pseudo-enumeration)\n\t\t\t\t'Error',        // error\n\t\t\t\t'Bareword',     // bare identifier\n\t\t\t\t'Expression',   // expression (backquoted)\n\t\t\t\t'String',       // quoted string (single or double)\n\t\t\t\t'SquareBracket' // [[…]] or [img[…]]\n\t\t\t]);\n\t\t\tconst spaceRe    = new RegExp(Patterns.space);\n\t\t\tconst notSpaceRe = new RegExp(Patterns.notSpace);\n\t\t\tconst varTest    = new RegExp(`^${Patterns.variable}`);\n\n\t\t\t// Lexing functions.\n\t\t\tfunction slurpQuote(lexer, endQuote) {\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn EOF;\n\n\t\t\t\t\tcase endQuote:\n\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\treturn lexer.pos;\n\t\t\t}\n\n\t\t\tfunction lexSpace(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(notSpaceRe);\n\n\t\t\t\tif (offset === EOF) {\n\t\t\t\t\t// no non-whitespace characters, so bail\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\telse if (offset !== 0) {\n\t\t\t\t\tlexer.pos += offset;\n\t\t\t\t\tlexer.ignore();\n\t\t\t\t}\n\n\t\t\t\t// determine what the next state is\n\t\t\t\tswitch (lexer.next()) {\n\t\t\t\tcase '`':\n\t\t\t\t\treturn lexExpression;\n\t\t\t\tcase '\"':\n\t\t\t\t\treturn lexDoubleQuote;\n\t\t\t\tcase \"'\":\n\t\t\t\t\treturn lexSingleQuote;\n\t\t\t\tcase '[':\n\t\t\t\t\treturn lexSquareBracket;\n\t\t\t\tdefault:\n\t\t\t\t\treturn lexBareword;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction lexExpression(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '`') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated backquote expression');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.Expression);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexDoubleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, '\"') === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated double quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSingleQuote(lexer) {\n\t\t\t\tif (slurpQuote(lexer, \"'\") === EOF) {\n\t\t\t\t\treturn lexer.error(Item.Error, 'unterminated single quoted string');\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.String);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexSquareBracket(lexer) {\n\t\t\t\tconst imgMeta = '<>IiMmGg';\n\t\t\t\tlet what;\n\n\t\t\t\tif (lexer.accept(imgMeta)) {\n\t\t\t\t\twhat = 'image';\n\t\t\t\t\tlexer.acceptRun(imgMeta);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\twhat = 'link';\n\t\t\t\t}\n\n\t\t\t\tif (!lexer.accept('[')) {\n\t\t\t\t\treturn lexer.error(Item.Error, `malformed ${what} markup`);\n\t\t\t\t}\n\n\t\t\t\tlexer.depth = 2; // account for both initial left square brackets\n\n\t\t\t\tloop: for (;;) {\n\t\t\t\t\t/* eslint-disable indent */\n\t\t\t\t\tswitch (lexer.next()) {\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst ch = lexer.next();\n\n\t\t\t\t\t\t\tif (ch !== EOF && ch !== '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/* falls through */\n\t\t\t\t\tcase EOF:\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\treturn lexer.error(Item.Error, `unterminated ${what} markup`);\n\n\t\t\t\t\tcase '[':\n\t\t\t\t\t\t++lexer.depth;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ']':\n\t\t\t\t\t\t--lexer.depth;\n\n\t\t\t\t\t\tif (lexer.depth < 0) {\n\t\t\t\t\t\t\treturn lexer.error(Item.Error, \"unexpected right square bracket ']'\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lexer.depth === 1) {\n\t\t\t\t\t\t\tif (lexer.next() === ']') {\n\t\t\t\t\t\t\t\t--lexer.depth;\n\t\t\t\t\t\t\t\tbreak loop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlexer.backup();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable indent */\n\t\t\t\t}\n\n\t\t\t\tlexer.emit(Item.SquareBracket);\n\t\t\t\treturn lexSpace;\n\t\t\t}\n\n\t\t\tfunction lexBareword(lexer) {\n\t\t\t\tconst offset = lexer.source.slice(lexer.pos).search(spaceRe);\n\t\t\t\tlexer.pos = offset === EOF ? lexer.source.length : lexer.pos + offset;\n\t\t\t\tlexer.emit(Item.Bareword);\n\t\t\t\treturn offset === EOF ? null : lexSpace;\n\t\t\t}\n\n\t\t\t// Parse function.\n\t\t\tfunction parseMacroArgs(rawArgsString) {\n\t\t\t\t// Initialize the lexer.\n\t\t\t\tconst lexer = new Lexer(rawArgsString, lexSpace);\n\t\t\t\tconst args  = [];\n\n\t\t\t\t// Lex the raw argument string.\n\t\t\t\tlexer.run().forEach(item => {\n\t\t\t\t\tlet arg = item.text;\n\n\t\t\t\t\tswitch (item.type) {\n\t\t\t\t\tcase Item.Error:\n\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${item.message}`);\n\n\t\t\t\t\tcase Item.Bareword:\n\t\t\t\t\t\t// A variable, so substitute its value.\n\t\t\t\t\t\tif (varTest.test(arg)) {\n\t\t\t\t\t\t\targ = State.getVar(arg);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Property access on the settings or setup objects, so try to evaluate it.\n\t\t\t\t\t\telse if (/^(?:settings|setup)[.[]/.test(arg)) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(arg);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Null literal, so convert it into null.\n\t\t\t\t\t\telse if (arg === 'null') {\n\t\t\t\t\t\t\targ = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Undefined literal, so convert it into undefined.\n\t\t\t\t\t\telse if (arg === 'undefined') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean true literal, so convert it into true.\n\t\t\t\t\t\telse if (arg === 'true') {\n\t\t\t\t\t\t\targ = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Boolean false literal, so convert it into false.\n\t\t\t\t\t\telse if (arg === 'false') {\n\t\t\t\t\t\t\targ = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// NaN literal, so convert it into NaN.\n\t\t\t\t\t\telse if (arg === 'NaN') {\n\t\t\t\t\t\t\targ = NaN;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Attempt to convert it into a number, in case it's a numeric literal.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst argAsNum = Number(arg);\n\n\t\t\t\t\t\t\tif (!Number.isNaN(argAsNum)) {\n\t\t\t\t\t\t\t\targ = argAsNum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.Expression:\n\t\t\t\t\t\targ = arg.slice(1, -1).trim(); // remove the backquotes and trim the expression\n\n\t\t\t\t\t\t// Empty backquotes.\n\t\t\t\t\t\tif (arg === '') {\n\t\t\t\t\t\t\targ = undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Evaluate the expression.\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\tThe enclosing parenthesis here are necessary to force a code string\n\t\t\t\t\t\t\t\t\tconsisting solely of an object literal to be evaluated as such, rather\n\t\t\t\t\t\t\t\t\tthan as a code block.\n\t\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\t\targ = Scripting.evalTwineScript(`(${arg})`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument expression \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.String:\n\t\t\t\t\t\t// Evaluate the string to handle escaped characters.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targ = Scripting.evalJavaScript(arg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument string \"${arg}\": ${ex.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Item.SquareBracket:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\t\tsource     : arg,\n\t\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": ${markup.error}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (markup.pos < arg.length) {\n\t\t\t\t\t\t\t\tthrow new Error(`unable to parse macro argument \"${arg}\": unexpected character(s) \"${arg.slice(markup.pos)}\" (pos: ${markup.pos})`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Convert to a link or image object.\n\t\t\t\t\t\t\tif (markup.isLink) {\n\t\t\t\t\t\t\t\t// .isLink, [.text], [.forceInternal], .link, [.setter]\n\t\t\t\t\t\t\t\targ = { isLink : true };\n\t\t\t\t\t\t\t\targ.count    = markup.hasOwnProperty('text') ? 2 : 1;\n\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\targ.text     = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : arg.link;\n\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\targ.setFn    = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (markup.isImage) {\n\t\t\t\t\t\t\t\t// .isImage, [.align], [.title], .source, [.forceInternal], [.link], [.setter]\n\t\t\t\t\t\t\t\targ = (source => {\n\t\t\t\t\t\t\t\t\tconst imgObj = {\n\t\t\t\t\t\t\t\t\t\tsource,\n\t\t\t\t\t\t\t\t\t\tisImage : true\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t// Check for Twine 1.4 Base64 image passage transclusion.\n\t\t\t\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\t\t\t\timgObj.source  = passage.text;\n\t\t\t\t\t\t\t\t\t\t\timgObj.passage = passage.title;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn imgObj;\n\t\t\t\t\t\t\t\t})(Wikifier.helpers.evalPassageId(markup.source));\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\t\t\t\t\t\targ.align = markup.align;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\t\t\t\t\t\targ.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\t\t\t\t\t\targ.link     = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\t\t\t\t\t\t\targ.external = !markup.forceInternal && Wikifier.isExternalLink(arg.link);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\targ.setFn = markup.hasOwnProperty('setter')\n\t\t\t\t\t\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t\t\t\t\t\t: null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\targs.push(arg);\n\t\t\t\t});\n\n\t\t\t\treturn args;\n\t\t\t}\n\n\t\t\treturn parseMacroArgs;\n\t\t})()\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'link',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[\\\\[[^[]',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// text=(text), forceInternal=(~), link=link, setter=(setter)\n\t\t\tconst link  = Wikifier.helpers.evalPassageId(markup.link);\n\t\t\tconst text  = markup.hasOwnProperty('text') ? Wikifier.helpers.evalText(markup.text) : link;\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\n\t\t\t// Debug view setup.\n\t\t\tconst output = (Config.debug\n\t\t\t\t? new DebugView(w.output, 'link-markup', '[[link]]', w.source.slice(w.matchStart, w.nextMatch))\n\t\t\t\t: w\n\t\t\t).output;\n\n\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\tWikifier.createInternalLink(output, link, text, setFn);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tWikifier.createExternalLink(output, link, text);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'urlLink',\n\t\tprofiles : ['core'],\n\t\tmatch    : Patterns.url,\n\n\t\thandler(w) {\n\t\t\tw.outputText(Wikifier.createExternalLink(w.output, w.matchText), w.matchStart, w.nextMatch);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'image',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\[[<>]?[Ii][Mm][Gg]\\\\[',\n\n\t\thandler(w) {\n\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup(w);\n\n\t\t\tif (markup.hasOwnProperty('error')) {\n\t\t\t\tw.outputText(w.output, w.matchStart, w.nextMatch);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = markup.pos;\n\n\t\t\t// Debug view setup.\n\t\t\tlet debugView;\n\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tw.output,\n\t\t\t\t\t'image-markup',\n\t\t\t\t\tmarkup.hasOwnProperty('link') ? '[img[][link]]' : '[img[]]',\n\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// align=(left|right), title=(title), source=source, forceInternal=(~), link=(link), setter=(setter)\n\t\t\tconst setFn = markup.hasOwnProperty('setter')\n\t\t\t\t? Wikifier.helpers.createShadowSetterCallback(Scripting.parse(markup.setter))\n\t\t\t\t: null;\n\t\t\tlet el     = (Config.debug ? debugView : w).output;\n\t\t\tlet source;\n\n\t\t\tif (markup.hasOwnProperty('link')) {\n\t\t\t\tconst link = Wikifier.helpers.evalPassageId(markup.link);\n\n\t\t\t\tif (markup.forceInternal || !Wikifier.isExternalLink(link)) {\n\t\t\t\t\tel = Wikifier.createInternalLink(el, link, null, setFn);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tel = Wikifier.createExternalLink(el, link);\n\t\t\t\t}\n\n\t\t\t\tel.classList.add('link-image');\n\t\t\t}\n\n\t\t\tel = jQuery(document.createElement('img'))\n\t\t\t\t.appendTo(el)\n\t\t\t\t.get(0);\n\t\t\tsource = Wikifier.helpers.evalPassageId(markup.source);\n\n\t\t\t// Check for image passage transclusion.\n\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\tel.setAttribute('data-passage', passage.title);\n\t\t\t\t\tsource = passage.text.trim();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tel.src = source;\n\n\t\t\tif (markup.hasOwnProperty('text')) {\n\t\t\t\tel.title = Wikifier.helpers.evalText(markup.text);\n\t\t\t}\n\n\t\t\tif (markup.hasOwnProperty('align')) {\n\t\t\t\tel.align = markup.align;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'monospacedByBlock',\n\t\tprofiles  : ['block'],\n\t\tmatch     : '^\\\\{\\\\{\\\\{\\\\n',\n\t\tlookahead : /^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tconst pre = jQuery(document.createElement('pre'));\n\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t.text(match[1])\n\t\t\t\t\t.appendTo(pre);\n\t\t\t\tpre.appendTo(w.output);\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'formatByChar',\n\t\tprofiles : ['core'],\n\t\tmatch    : \"''|//|__|\\\\^\\\\^|~~|==|\\\\{\\\\{\\\\{\",\n\n\t\thandler(w) {\n\t\t\tswitch (w.matchText) {\n\t\t\tcase \"''\":\n\t\t\t\tw.subWikify(jQuery(document.createElement('strong')).appendTo(w.output).get(0), \"''\");\n\t\t\t\tbreak;\n\n\t\t\tcase '//':\n\t\t\t\tw.subWikify(jQuery(document.createElement('em')).appendTo(w.output).get(0), '//');\n\t\t\t\tbreak;\n\n\t\t\tcase '__':\n\t\t\t\tw.subWikify(jQuery(document.createElement('u')).appendTo(w.output).get(0), '__');\n\t\t\t\tbreak;\n\n\t\t\tcase '^^':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sup')).appendTo(w.output).get(0), '\\\\^\\\\^');\n\t\t\t\tbreak;\n\n\t\t\tcase '~~':\n\t\t\t\tw.subWikify(jQuery(document.createElement('sub')).appendTo(w.output).get(0), '~~');\n\t\t\t\tbreak;\n\n\t\t\tcase '==':\n\t\t\t\tw.subWikify(jQuery(document.createElement('s')).appendTo(w.output).get(0), '==');\n\t\t\t\tbreak;\n\n\t\t\tcase '{{{':\n\t\t\t\t{\n\t\t\t\t\tconst lookahead = /\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}/gm;\n\n\t\t\t\t\tlookahead.lastIndex = w.matchStart;\n\n\t\t\t\t\tconst match = lookahead.exec(w.source);\n\n\t\t\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\t\t\tjQuery(document.createElement('code'))\n\t\t\t\t\t\t\t.text(match[1])\n\t\t\t\t\t\t\t.appendTo(w.output);\n\t\t\t\t\t\tw.nextMatch = lookahead.lastIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'customStyle',\n\t\tprofiles   : ['core'],\n\t\tmatch      : '@@',\n\t\tterminator : '@@',\n\t\tblockRe    : /\\s*\\n/gm,\n\n\t\thandler(w) {\n\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\n\t\t\tthis.blockRe.lastIndex = w.nextMatch; // must follow the call to `inlineCss()`\n\n\t\t\tconst blockMatch = this.blockRe.exec(w.source);\n\t\t\tconst blockLevel = blockMatch && blockMatch.index === w.nextMatch;\n\t\t\tconst $el        = jQuery(document.createElement(blockLevel ? 'div' : 'span'))\n\t\t\t\t.appendTo(w.output);\n\n\t\t\tif (css.classes.length === 0 && css.id === '' && Object.keys(css.styles).length === 0) {\n\t\t\t\t$el.addClass('marked');\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcss.classes.forEach(className => $el.addClass(className));\n\n\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t$el.attr('id', css.id);\n\t\t\t\t}\n\n\t\t\t\t$el.css(css.styles);\n\t\t\t}\n\n\t\t\tif (blockLevel) {\n\t\t\t\t// Skip the leading and, if it exists, trailing newlines.\n\t\t\t\tw.nextMatch += blockMatch[0].length;\n\t\t\t\tw.subWikify($el[0], `\\\\n?${this.terminator}`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tw.subWikify($el[0], this.terminator);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimText',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '\"{3}|<[Nn][Oo][Ww][Ii][Kk][Ii]>',\n\t\tlookahead : /(?:\"{3}((?:.|\\n)*?)\"{3})|(?:<[Nn][Oo][Ww][Ii][Kk][Ii]>((?:.|\\n)*?)<\\/[Nn][Oo][Ww][Ii][Kk][Ii]>)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('verbatim')\n\t\t\t\t\t.text(match[1] || match[2])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'horizontalRule',\n\t\tprofiles : ['core'],\n\t\tmatch    : '^----+$\\\\n?|<[Hh][Rr]\\\\s*/?>\\\\n?',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createElement('hr')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'emdash',\n\t\tprofiles : ['core'],\n\t\tmatch    : '--',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('\\u2014')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'doubleDollarSign',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\${2}', // eslint-disable-line no-template-curly-in-string\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createTextNode('$')).appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tSupported syntax:\n\t\t\t\t$variable\n\t\t\t\t$variable.property\n\t\t\t\t$variable[numericIndex]\n\t\t\t\t$variable[\"property\"]\n\t\t\t\t$variable['property']\n\t\t\t\t$variable[$indexOrPropertyVariable]\n\t\t*/\n\t\tname     : 'nakedVariable',\n\t\tprofiles : ['core'],\n\t\tmatch    : `${Patterns.variable}(?:(?:\\\\.${Patterns.identifier})|(?:\\\\[\\\\d+\\\\])|(?:\\\\[\"(?:\\\\\\\\.|[^\"\\\\\\\\])+\"\\\\])|(?:\\\\['(?:\\\\\\\\.|[^'\\\\\\\\])+'\\\\])|(?:\\\\[${Patterns.variable}\\\\]))*`,\n\n\t\thandler(w) {\n\t\t\tconst result = toStringOrDefault(State.getVar(w.matchText), null);\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'variable', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'template',\n\t\tprofiles : ['core'],\n\t\tmatch    : `\\\\?${Patterns.templateName}`,\n\n\t\thandler(w) {\n\t\t\tconst name = w.matchText.slice(1);\n\t\t\tlet template = Template.get(name);\n\t\t\tlet result   = null;\n\n\t\t\t// If we have an array of templates, randomly choose one.\n\t\t\tif (template instanceof Array) {\n\t\t\t\ttemplate = template.random();\n\t\t\t}\n\n\t\t\tswitch (typeof template) {\n\t\t\tcase 'function':\n\t\t\t\ttry {\n\t\t\t\t\tresult = toStringOrDefault(template.call({ name }), null);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot execute function template ?${name}: ${ex.message}`,\n\t\t\t\t\t\tw.source.slice(w.matchStart, w.nextMatch)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tresult = template;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (result === null) {\n\t\t\t\tjQuery(document.createTextNode(w.matchText)).appendTo(w.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnew Wikifier(\n\t\t\t\t\t(Config.debug\n\t\t\t\t\t\t? new DebugView(w.output, 'template', w.matchText, w.matchText) // Debug view setup.\n\t\t\t\t\t\t: w\n\t\t\t\t\t).output,\n\t\t\t\t\tresult\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'heading',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^!{1,6}',\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.subWikify(\n\t\t\t\tjQuery(document.createElement(`h${w.matchLength}`)).appendTo(w.output).get(0),\n\t\t\t\tthis.terminator\n\t\t\t);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'table',\n\t\tprofiles       : ['block'],\n\t\tmatch          : '^\\\\|(?:[^\\\\n]*)\\\\|(?:[fhck]?)$',\n\t\tlookahead      : /^\\|([^\\n]*)\\|([fhck]?)$/gm,\n\t\trowTerminator  : '\\\\|(?:[cfhk]?)$\\\\n?',\n\t\tcellPattern    : '(?:\\\\|([^\\\\n\\\\|]*)\\\\|)|(\\\\|[cfhk]?$\\\\n?)',\n\t\tcellTerminator : '(?:\\\\u0020*)\\\\|',\n\t\trowTypes       : { c : 'caption', f : 'tfoot', h : 'thead', '' : 'tbody' }, // eslint-disable-line id-length\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst table       = jQuery(document.createElement('table')).appendTo(w.output).get(0);\n\t\t\tconst prevColumns = [];\n\t\t\tlet curRowType    = null;\n\t\t\tlet $rowContainer = null;\n\t\t\tlet rowCount      = 0;\n\t\t\tlet matched;\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst nextRowType = match[2];\n\n\t\t\t\t\tif (nextRowType === 'k') {\n\t\t\t\t\t\ttable.className = match[1];\n\t\t\t\t\t\tw.nextMatch += match[0].length + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (nextRowType !== curRowType) {\n\t\t\t\t\t\t\tcurRowType = nextRowType;\n\t\t\t\t\t\t\t$rowContainer = jQuery(document.createElement(this.rowTypes[nextRowType]))\n\t\t\t\t\t\t\t\t.appendTo(table);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (curRowType === 'c') {\n\t\t\t\t\t\t\t$rowContainer.css('caption-side', rowCount === 0 ? 'top' : 'bottom');\n\t\t\t\t\t\t\tw.nextMatch += 1;\n\t\t\t\t\t\t\tw.subWikify($rowContainer[0], this.rowTerminator);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.rowHandler(\n\t\t\t\t\t\t\t\tw,\n\t\t\t\t\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t\t\t\t\t.appendTo($rowContainer)\n\t\t\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\t\t\tprevColumns\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t++rowCount;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t},\n\n\t\trowHandler(w, rowEl, prevColumns) {\n\t\t\tconst cellRe = new RegExp(this.cellPattern, 'gm');\n\t\t\tlet col         = 0;\n\t\t\tlet curColCount = 1;\n\t\t\tlet matched;\n\n\t\t\tdo {\n\t\t\t\tcellRe.lastIndex = w.nextMatch;\n\n\t\t\t\tconst cellMatch = cellRe.exec(w.source);\n\n\t\t\t\tmatched = cellMatch && cellMatch.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tif (cellMatch[1] === '~') {\n\t\t\t\t\t\tconst last = prevColumns[col];\n\n\t\t\t\t\t\tif (last) {\n\t\t\t\t\t\t\t++last.rowCount;\n\t\t\t\t\t\t\tlast.$element\n\t\t\t\t\t\t\t\t.attr('rowspan', last.rowCount)\n\t\t\t\t\t\t\t\t.css('vertical-align', 'middle');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[1] === '>') {\n\t\t\t\t\t\t++curColCount;\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length - 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cellMatch[2]) {\n\t\t\t\t\t\tw.nextMatch = cellMatch.index + cellMatch[0].length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t++w.nextMatch;\n\n\t\t\t\t\t\tconst css = Wikifier.helpers.inlineCss(w);\n\t\t\t\t\t\tlet spaceLeft  = false;\n\t\t\t\t\t\tlet spaceRight = false;\n\t\t\t\t\t\tlet $cell;\n\n\t\t\t\t\t\twhile (w.source.substr(w.nextMatch, 1) === ' ') {\n\t\t\t\t\t\t\tspaceLeft = true;\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (w.source.substr(w.nextMatch, 1) === '!') {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('th')).appendTo(rowEl);\n\t\t\t\t\t\t\t++w.nextMatch;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$cell = jQuery(document.createElement('td')).appendTo(rowEl);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevColumns[col] = {\n\t\t\t\t\t\t\trowCount : 1,\n\t\t\t\t\t\t\t$element : $cell\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (curColCount > 1) {\n\t\t\t\t\t\t\t$cell.attr('colspan', curColCount);\n\t\t\t\t\t\t\tcurColCount = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tw.subWikify($cell[0], this.cellTerminator);\n\n\t\t\t\t\t\tif (w.matchText.substr(w.matchText.length - 2, 1) === ' ') {\n\t\t\t\t\t\t\tspaceRight = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcss.classes.forEach(className => $cell.addClass(className));\n\n\t\t\t\t\t\tif (css.id !== '') {\n\t\t\t\t\t\t\t$cell.attr('id', css.id);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (spaceLeft && spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'center';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceLeft) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'right';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (spaceRight) {\n\t\t\t\t\t\t\tcss.styles['text-align'] = 'left';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$cell.css(css.styles);\n\n\t\t\t\t\t\tw.nextMatch = w.nextMatch - 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t++col;\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname       : 'list',\n\t\tprofiles   : ['block'],\n\t\tmatch      : '^(?:(?:\\\\*+)|(?:#+))',\n\t\tlookahead  : /^(?:(\\*+)|(#+))/gm,\n\t\tterminator : '\\\\n',\n\n\t\thandler(w) {\n\t\t\tif (!Wikifier.helpers.hasBlockContext(w.output.childNodes)) {\n\t\t\t\tjQuery(w.output).append(document.createTextNode(w.matchText));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tw.nextMatch = w.matchStart;\n\n\t\t\tconst destStack = [w.output];\n\t\t\tlet curType  = null;\n\t\t\tlet curLevel = 0;\n\t\t\tlet matched;\n\t\t\tlet i;\n\n\t\t\tdo {\n\t\t\t\tthis.lookahead.lastIndex = w.nextMatch;\n\n\t\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\t\tmatched = match && match.index === w.nextMatch;\n\n\t\t\t\tif (matched) {\n\t\t\t\t\tconst newType  = match[2] ? 'ol' : 'ul';\n\t\t\t\t\tconst newLevel = match[0].length;\n\n\t\t\t\t\tw.nextMatch += match[0].length;\n\n\t\t\t\t\tif (newLevel > curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i < newLevel; ++i) {\n\t\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel < curLevel) {\n\t\t\t\t\t\tfor (i = curLevel; i > newLevel; --i) {\n\t\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (newLevel === curLevel && newType !== curType) {\n\t\t\t\t\t\tdestStack.pop();\n\t\t\t\t\t\tdestStack.push(\n\t\t\t\t\t\t\tjQuery(document.createElement(newType))\n\t\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t\t.get(0)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tcurLevel = newLevel;\n\t\t\t\t\tcurType = newType;\n\t\t\t\t\tw.subWikify(\n\t\t\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t\t\t.appendTo(destStack[destStack.length - 1])\n\t\t\t\t\t\t\t.get(0),\n\t\t\t\t\t\tthis.terminator\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} while (matched);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'commentByBlock',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '(?:/(?:%|\\\\*))|(?:<!--)',\n\t\tlookahead : /(?:\\/(%|\\*)(?:(?:.|\\n)*?)\\1\\/)|(?:<!--(?:(?:.|\\n)*?)-->)/gm,\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineContinuation',\n\t\tprofiles : ['core'],\n\n\t\t// WARNING: The ordering here is important: end-of-line, start-of-line, end-of-string, start-of-string.\n\t\tmatch : `\\\\\\\\${Patterns.spaceNoTerminator}*\\\\n|\\\\n${Patterns.spaceNoTerminator}*\\\\\\\\|\\\\n?\\\\\\\\${Patterns.spaceNoTerminator}*$|^${Patterns.spaceNoTerminator}*\\\\\\\\\\\\n?`,\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'lineBreak',\n\t\tprofiles : ['core'],\n\t\tmatch    : '\\\\n|<[Bb][Rr]\\\\s*/?>',\n\n\t\thandler(w) {\n\t\t\tif (!w.options.nobr) {\n\t\t\t\tjQuery(document.createElement('br')).appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'htmlCharacterReference',\n\t\tprofiles : ['core'],\n\t\tmatch    : '(?:(?:&#?[0-9A-Za-z]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9A-Fa-f]|1D[C-Fc-f][0-9A-Fa-f]|20[D-Fd-f][0-9A-Fa-f]|FE2[0-9A-Fa-f])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[0-9A-Za-z]{2,8};)',\n\n\t\thandler(w) {\n\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t.append(w.matchText)\n\t\t\t\t.appendTo(w.output);\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname     : 'xmlProlog',\n\t\tprofiles : ['core'],\n\t\tmatch    : '<\\\\?[Xx][Mm][Ll][^>]*\\\\?>',\n\n\t\thandler(w) {\n\t\t\tw.nextMatch = w.matchStart + w.matchLength;\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimHtml',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Hh][Tt][Mm][Ll]>',\n\t\tlookahead : /<[Hh][Tt][Mm][Ll]>((?:.|\\n)*?)<\\/[Hh][Tt][Mm][Ll]>/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'verbatimScriptTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Cc][Rr][Ii][Pp][Tt][^>]*>',\n\t\tlookahead : /(<[Ss][Cc][Rr][Ii][Pp][Tt]*>(?:.|\\n)*?<\\/[Ss][Cc][Rr][Ii][Pp][Tt]>)/gm,\n\t\thandler   : _verbatimTagHandler\n\t});\n\n\tWikifier.Parser.add({\n\t\tname           : 'styleTag',\n\t\tprofiles       : ['core'],\n\t\tmatch          : '<[Ss][Tt][Yy][Ll][Ee][^>]*>',\n\t\tlookahead      : /(<[Ss][Tt][Yy][Ll][Ee]*>)((?:.|\\n)*?)(<\\/[Ss][Tt][Yy][Ll][Ee]>)/gm,\n\t\timageMarkup    : new RegExp(Patterns.cssImage, 'g'),\n\t\thasImageMarkup : new RegExp(Patterns.cssImage),\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tlet css = match[2];\n\n\t\t\t\t// Check for wiki image transclusion.\n\t\t\t\tif (this.hasImageMarkup.test(css)) {\n\t\t\t\t\tthis.imageMarkup.lastIndex = 0;\n\n\t\t\t\t\tcss = css.replace(this.imageMarkup, wikiImage => {\n\t\t\t\t\t\tconst markup = Wikifier.helpers.parseSquareBracketedMarkup({\n\t\t\t\t\t\t\tsource     : wikiImage,\n\t\t\t\t\t\t\tmatchStart : 0\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (markup.hasOwnProperty('error') || markup.pos < wikiImage.length) {\n\t\t\t\t\t\t\treturn wikiImage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet source = markup.source;\n\n\t\t\t\t\t\t// Handle image passage transclusion.\n\t\t\t\t\t\tif (source.slice(0, 5) !== 'data:' && Story.has(source)) {\n\t\t\t\t\t\t\tconst passage = Story.get(source);\n\n\t\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t\tsource = passage.text;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tThe source may be URI- or Base64-encoded, so we cannot use `encodeURIComponent()`\n\t\t\t\t\t\t\there.  Instead, we simply encode any double quotes, since the URI will be\n\t\t\t\t\t\t\tdelimited by them.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\treturn `url(\"${source.replace(/\"/g, '%22')}\")`;\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createDocumentFragment())\n\t\t\t\t\t.append(match[1] + css + match[3])\n\t\t\t\t\t.appendTo(w.output);\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\tname      : 'svgTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : '<[Ss][Vv][Gg][^>]*>',\n\t\tlookahead : /(<[Ss][Vv][Gg][^>]*>(?:.|\\n)*?<\\/[Ss][Vv][Gg]>)/gm,\n\t\tnamespace : 'http://www.w3.org/2000/svg',\n\n\t\thandler(w) {\n\t\t\tthis.lookahead.lastIndex = w.matchStart;\n\n\t\t\tconst match = this.lookahead.exec(w.source);\n\n\t\t\tif (match && match.index === w.matchStart) {\n\t\t\t\tw.nextMatch = this.lookahead.lastIndex;\n\n\t\t\t\tconst $frag = jQuery(document.createDocumentFragment()).append(match[1]);\n\n\t\t\t\t// Postprocess the relevant SVG element nodes.\n\t\t\t\t$frag.find('a[data-passage],image[data-passage]').each((_, el) => {\n\t\t\t\t\tconst tagName = el.tagName.toLowerCase();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`svg|<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$frag.appendTo(w.output);\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// '<image>' element, so attempt media passage transclusion.\n\t\t\t\tif (tagName === 'image') {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tif (passage.tags.includes('Twine.image')) {\n\t\t\t\t\t\t\t// NOTE: SVG `.href` IDL attribute is read-only,\n\t\t\t\t\t\t\t// so set its `href` content attribute instead.\n\t\t\t\t\t\t\tel.setAttribute('href', passage.text.trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>'.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\tWikifier.Parser.add({\n\t\t/*\n\t\t\tNOTE: This parser MUST come after any parser which handles HTML tag-\n\t\t\tlike constructs—e.g. 'verbatimText', 'horizontalRule', 'lineBreak',\n\t\t\t'xmlProlog', 'verbatimHtml', 'verbatimSvgTag', 'verbatimScriptTag',\n\t\t\tand 'styleTag'.\n\t\t*/\n\t\tname      : 'htmlTag',\n\t\tprofiles  : ['core'],\n\t\tmatch     : `<${Patterns.htmlTagName}(?:\\\\s+[^\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\s\"'>\\\\/=]+(?:\\\\s*=\\\\s*(?:\"[^\"]*?\"|'[^']*?'|[^\\\\s\"'=<>\\`]+))?)*\\\\s*\\\\/?>`,\n\t\ttagRe     : new RegExp(`^<(${Patterns.htmlTagName})`),\n\t\tmediaTags : ['audio', 'img', 'source', 'track', 'video'], // NOTE: The `<picture>` element should not be in this list.\n\t\tnobrTags  : ['audio', 'colgroup', 'datalist', 'dl', 'figure', 'meter', 'ol', 'optgroup', 'picture', 'progress', 'ruby', 'select', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'ul', 'video'],\n\t\tvoidTags  : ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],\n\n\t\thandler(w) {\n\t\t\tconst tagMatch = this.tagRe.exec(w.matchText);\n\t\t\tconst tag      = tagMatch && tagMatch[1];\n\t\t\tconst tagName  = tag && tag.toLowerCase();\n\n\t\t\tif (tagName) {\n\t\t\t\tconst isVoid = this.voidTags.includes(tagName) || w.matchText.endsWith('/>');\n\t\t\t\tconst isNobr = this.nobrTags.includes(tagName);\n\t\t\t\tlet terminator;\n\t\t\t\tlet terminatorMatch;\n\n\t\t\t\tif (!isVoid) {\n\t\t\t\t\tterminator = `<\\\\/${tagName}\\\\s*>`;\n\n\t\t\t\t\tconst terminatorRe = new RegExp(terminator, 'gim'); // ignore case during match\n\n\t\t\t\t\tterminatorRe.lastIndex = w.matchStart;\n\t\t\t\t\tterminatorMatch = terminatorRe.exec(w.source);\n\t\t\t\t}\n\n\t\t\t\tif (isVoid || terminatorMatch) {\n\t\t\t\t\tlet output    = w.output;\n\t\t\t\t\tlet el        = document.createElement(w.output.tagName);\n\t\t\t\t\tlet debugView;\n\n\t\t\t\t\tel.innerHTML = w.matchText;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of a `while` statement here is curious, however,\n\t\t\t\t\t\tI'm hesitant to change it for fear of breaking some edge case.\n\t\t\t\t\t*/\n\t\t\t\t\twhile (el.firstChild) {\n\t\t\t\t\t\tel = el.firstChild;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.processAttributeDirectives(el);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn throwError(\n\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t`<${tagName}>: ${ex.message}`,\n\t\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (el.hasAttribute('data-passage')) {\n\t\t\t\t\t\tthis.processDataAttributes(el, tagName);\n\n\t\t\t\t\t\t// Debug view setup.\n\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t\t\t`html-${tagName}`,\n\t\t\t\t\t\t\t\ttagName,\n\t\t\t\t\t\t\t\tw.matchText\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tdebugView.modes({\n\t\t\t\t\t\t\t\tblock   : tagName === 'img',\n\t\t\t\t\t\t\t\tnonvoid : terminatorMatch\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toutput = debugView.output;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (terminatorMatch) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: There's no catch clause here because this try/finally exists\n\t\t\t\t\t\t\tsolely to ensure that the options stack is properly restored in\n\t\t\t\t\t\t\tthe event that an uncaught exception is thrown during the call to\n\t\t\t\t\t\t\t`subWikify()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tWikifier.Option.push({ nobr : isNobr });\n\t\t\t\t\t\t\tw.subWikify(el, terminator, { ignoreTerminatorCase : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tWikifier.Option.pop();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tDebug view modification.  If the current element has any debug\n\t\t\t\t\t\t\tview descendants who have \"block\" mode set, then set its debug\n\t\t\t\t\t\t\tview to the same.  It just makes things look a bit nicer.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif (debugView && jQuery(el).find('.debug.block').length > 0) {\n\t\t\t\t\t\t\tdebugView.modes({ block : true });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tNOTE: The use of `cloneNode(true)` here for `<track>` elements\n\t\t\t\t\t\tis necessary to workaround a poorly understood rehoming issue.\n\t\t\t\t\t*/\n\t\t\t\t\toutput.appendChild(tagName === 'track' ? el.cloneNode(true) : el);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn throwError(\n\t\t\t\t\t\tw.output,\n\t\t\t\t\t\t`cannot find a closing tag for HTML <${tag}>`,\n\t\t\t\t\t\t`${w.matchText}\\u2026`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tprocessAttributeDirectives(el) {\n\t\t\t// NOTE: The `.attributes` property yields a live collection, so we\n\t\t\t// must make a non-live copy of it as we will be adding and removing\n\t\t\t// members of said collection if any directives are found.\n\t\t\t[...el.attributes].forEach(({ name, value }) => {\n\t\t\t\tconst evalShorthand = name[0] === '@';\n\n\t\t\t\tif (evalShorthand || name.startsWith('sc-eval:')) {\n\t\t\t\t\tconst newName = name.slice(evalShorthand ? 1 : 8); // Remove eval directive prefix.\n\n\t\t\t\t\tif (newName === 'data-setter') {\n\t\t\t\t\t\tthrow new Error(`evaluation directive is not allowed on the data-setter attribute: \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\t// Evaluate the value as TwineScript.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = Scripting.evalTwineScript(value);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`bad evaluation from attribute directive \"${name}\": ${ex.message}`);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Assign the result to the new attribute and remove the old one.\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: Most browsers (ca. Nov 2017) have broken `setAttribute()`\n\t\t\t\t\t\t\tmethod implementations that throw on attribute names that start\n\t\t\t\t\t\t\twith, or contain, various symbols that are completely valid per\n\t\t\t\t\t\t\tthe specification.  Thus this code could fail if the user chooses\n\t\t\t\t\t\t\tattribute names that, after removing the directive prefix, are\n\t\t\t\t\t\t\tunpalatable to `setAttribute()`.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tel.setAttribute(newName, result);\n\t\t\t\t\t\tel.removeAttribute(name);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\tthrow new Error(`cannot transform attribute directive \"${name}\" into attribute \"${newName}\"`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tprocessDataAttributes(el, tagName) {\n\t\t\tlet passage = el.getAttribute('data-passage');\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst evaluated = Wikifier.helpers.evalPassageId(passage);\n\n\t\t\tif (evaluated !== passage) {\n\t\t\t\tpassage = evaluated;\n\t\t\t\tel.setAttribute('data-passage', evaluated);\n\t\t\t}\n\n\t\t\tif (passage !== '') {\n\t\t\t\t// Media element, so attempt media passage transclusion.\n\t\t\t\tif (this.mediaTags.includes(tagName)) {\n\t\t\t\t\tif (passage.slice(0, 5) !== 'data:' && Story.has(passage)) {\n\t\t\t\t\t\tpassage = Story.get(passage);\n\n\t\t\t\t\t\tlet parentName;\n\t\t\t\t\t\tlet twineTag;\n\n\t\t\t\t\t\tswitch (tagName) {\n\t\t\t\t\t\tcase 'audio':\n\t\t\t\t\t\tcase 'video':\n\t\t\t\t\t\t\ttwineTag = `Twine.${tagName}`;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'img':\n\t\t\t\t\t\t\ttwineTag = 'Twine.image';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'track':\n\t\t\t\t\t\t\ttwineTag = 'Twine.vtt';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'source':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst $parent = $(el).closest('audio,picture,video');\n\n\t\t\t\t\t\t\t\tif ($parent.length) {\n\t\t\t\t\t\t\t\t\tparentName = $parent.get(0).tagName.toLowerCase();\n\t\t\t\t\t\t\t\t\ttwineTag = `Twine.${parentName === 'picture' ? 'image' : parentName}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (passage.tags.includes(twineTag)) {\n\t\t\t\t\t\t\tel[parentName === 'picture' ? 'srcset' : 'src'] = passage.text.trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Elsewise, assume a link element of some type—e.g., '<a>', '<area>', '<button>', etc.\n\t\t\t\telse {\n\t\t\t\t\tlet setter = el.getAttribute('data-setter');\n\t\t\t\t\tlet setFn;\n\n\t\t\t\t\tif (setter != null) { // lazy equality for null\n\t\t\t\t\t\tsetter = String(setter).trim();\n\n\t\t\t\t\t\tif (setter !== '') {\n\t\t\t\t\t\t\tsetFn = Wikifier.helpers.createShadowSetterCallback(Scripting.parse(setter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tel.classList.add('link-internal');\n\n\t\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t\tel.classList.add('link-visited');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.classList.add('link-broken');\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery(el).ariaClick({ one : true }, function () {\n\t\t\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\t\t\tsetFn.call(this);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tmarkup/template.js\n\n\tCopyright © 2019–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns */\n\nvar Template = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Template definitions.\n\tconst _templates = new Map();\n\n\t// Valid template name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.templateName})$`);\n\n\t// Valid template type predicate.\n\tconst _validType = template => {\n\t\tconst templateType = typeof template;\n\t\treturn templateType === 'function' || templateType === 'string';\n\t};\n\n\n\t/*******************************************************************************\n\t\tTemplate Functions.\n\t*******************************************************************************/\n\n\tfunction templateAdd(name, template) {\n\t\tif (\n\t\t\t   !_validType(template)\n\t\t\t&& !(template instanceof Array && template.length > 0 && template.every(_validType))\n\t\t) {\n\t\t\tthrow new TypeError(`invalid template type (${name}); templates must be: functions, strings, or an array of either`);\n\t\t}\n\n\t\t(name instanceof Array ? name : [name]).forEach(name => {\n\t\t\tif (!_validNameRe.test(name)) {\n\t\t\t\tthrow new Error(`invalid template name \"${name}\"`);\n\t\t\t}\n\t\t\tif (_templates.has(name)) {\n\t\t\t\tthrow new Error(`cannot clobber existing template ?${name}`);\n\t\t\t}\n\n\t\t\t_templates.set(name, template);\n\t\t});\n\t}\n\n\tfunction templateDelete(name) {\n\t\t(name instanceof Array ? name : [name]).forEach(name => _templates.delete(name));\n\t}\n\n\tfunction templateGet(name) {\n\t\treturn _templates.has(name) ? _templates.get(name) : null;\n\t}\n\n\tfunction templateHas(name) {\n\t\treturn _templates.has(name);\n\t}\n\n\tfunction templateSize() {\n\t\treturn _templates.size;\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tadd    : { value : templateAdd },\n\t\tdelete : { value : templateDelete },\n\t\tget    : { value : templateGet },\n\t\thas    : { value : templateHas },\n\t\tsize   : { get : templateSize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macro.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Patterns, Scripting, macros */\n\nvar Macro = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Macro definitions.\n\tconst _macros = {};\n\n\t// Map of all macro tags and their parents (key: 'tag name' => value: ['list of parent names']).\n\tconst _tags = {};\n\n\t// Valid macro name regular expression.\n\tconst _validNameRe = new RegExp(`^(?:${Patterns.macroName})$`);\n\n\n\t/*******************************************************************************************************************\n\t\tMacros Functions.\n\t*******************************************************************************************************************/\n\tfunction macrosAdd(name, def) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosAdd(name, def));\n\t\t\treturn;\n\t\t}\n\n\t\tif (!_validNameRe.test(name)) {\n\t\t\tthrow new Error(`invalid macro name \"${name}\"`);\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing macro <<${name}>>`);\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber child tag <<${name}>> of parent macro${_tags[name].length === 1 ? '' : 's'} <<${_tags[name].join('>>, <<')}>>`);\n\t\t}\n\n\t\ttry {\n\t\t\tif (typeof def === 'object') {\n\t\t\t\t// Add the macro definition.\n\t\t\t\t//\n\t\t\t\t// NOTE: Since `macrosGet()` may return legacy macros, we add the `_MACRO_API`\n\t\t\t\t// flag to (modern) API macros, so that the macro formatter will know how to\n\t\t\t\t// call the macro.  This should be removed in v3.\n\t\t\t\t_macros[name] = Object.assign(Object.create(null), def, { _MACRO_API : true });\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Add the macro alias.\n\t\t\t\tif (macrosHas(def)) {\n\t\t\t\t\t_macros[name] = Object.create(_macros[def], {\n\t\t\t\t\t\t_ALIAS_OF : {\n\t\t\t\t\t\t\tenumerable : true,\n\t\t\t\t\t\t\tvalue      : def\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`cannot create alias of nonexistent macro <<${def}>>`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(_macros, name, { writable : false });\n\t\t}\n\t\tcatch (ex) {\n\t\t\tif (ex.name === 'TypeError') {\n\t\t\t\tthrow new Error(`cannot clobber protected macro <<${name}>>`);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`unknown error when attempting to add macro <<${name}>>: [${ex.name}] ${ex.message}`);\n\t\t\t}\n\t\t}\n\n\t\t// Tags post-processing.\n\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\tif (_macros[name].tags == null) { // lazy equality for null\n\t\t\t\ttagsRegister(name);\n\t\t\t}\n\t\t\telse if (Array.isArray(_macros[name].tags)) {\n\t\t\t\ttagsRegister(name, _macros[name].tags);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`bad value for \"tags\" property of macro <<${name}>>`);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction macrosDelete(name) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosDelete(name));\n\t\t\treturn;\n\t\t}\n\n\t\tif (macrosHas(name)) {\n\t\t\t// Tags pre-processing.\n\t\t\tif (typeof _macros[name].tags !== 'undefined') {\n\t\t\t\ttagsUnregister(name);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Remove the macro definition.\n\t\t\t\tObject.defineProperty(_macros, name, { writable : true });\n\t\t\t\tdelete _macros[name];\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tthrow new Error(`unknown error removing macro <<${name}>>: ${ex.message}`);\n\t\t\t}\n\t\t}\n\t\telse if (tagsHas(name)) {\n\t\t\tthrow new Error(`cannot remove child tag <<${name}>> of parent macro <<${_tags[name]}>>`);\n\t\t}\n\t}\n\n\tfunction macrosIsEmpty() {\n\t\treturn Object.keys(_macros).length === 0;\n\t}\n\n\tfunction macrosHas(name) {\n\t\treturn _macros.hasOwnProperty(name);\n\t}\n\n\tfunction macrosGet(name) {\n\t\tlet macro = null;\n\n\t\tif (macrosHas(name) && typeof _macros[name].handler === 'function') {\n\t\t\tmacro = _macros[name];\n\t\t}\n\t\t/* legacy macro support */\n\t\telse if (macros.hasOwnProperty(name) && typeof macros[name].handler === 'function') {\n\t\t\tmacro = macros[name];\n\t\t}\n\t\t/* /legacy macro support */\n\n\t\treturn macro;\n\t}\n\n\tfunction macrosInit(handler = 'init') { // eslint-disable-line no-unused-vars\n\t\tObject.keys(_macros).forEach(name => {\n\t\t\tif (typeof _macros[name][handler] === 'function') {\n\t\t\t\t_macros[name][handler](name);\n\t\t\t}\n\t\t});\n\n\t\t/* legacy macro support */\n\t\tObject.keys(macros).forEach(name => {\n\t\t\tif (typeof macros[name][handler] === 'function') {\n\t\t\t\tmacros[name][handler](name);\n\t\t\t}\n\t\t});\n\t\t/* /legacy macro support */\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tTags Functions.\n\t*******************************************************************************************************************/\n\tfunction tagsRegister(parent, bodyTags) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tconst endTags = [`/${parent}`, `end${parent}`]; // automatically create the closing tags\n\t\tconst allTags = [].concat(endTags, Array.isArray(bodyTags) ? bodyTags : []);\n\n\t\tfor (let i = 0; i < allTags.length; ++i) {\n\t\t\tconst tag = allTags[i];\n\n\t\t\tif (macrosHas(tag)) {\n\t\t\t\tthrow new Error('cannot register tag for an existing macro');\n\t\t\t}\n\n\t\t\tif (tagsHas(tag)) {\n\t\t\t\tif (!_tags[tag].includes(parent)) {\n\t\t\t\t\t_tags[tag].push(parent);\n\t\t\t\t\t_tags[tag].sort();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t_tags[tag] = [parent];\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction tagsUnregister(parent) {\n\t\tif (!parent) {\n\t\t\tthrow new Error('no parent specified');\n\t\t}\n\n\t\tObject.keys(_tags).forEach(tag => {\n\t\t\tconst i = _tags[tag].indexOf(parent);\n\n\t\t\tif (i !== -1) {\n\t\t\t\tif (_tags[tag].length === 1) {\n\t\t\t\t\tdelete _tags[tag];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t_tags[tag].splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction tagsHas(name) {\n\t\treturn _tags.hasOwnProperty(name);\n\t}\n\n\tfunction tagsGet(name) {\n\t\treturn tagsHas(name) ? _tags[name] : null;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tMacro Functions.\n\t\t*/\n\t\tadd     : { value : macrosAdd },\n\t\tdelete  : { value : macrosDelete },\n\t\tisEmpty : { value : macrosIsEmpty },\n\t\thas     : { value : macrosHas },\n\t\tget     : { value : macrosGet },\n\t\tinit    : { value : macrosInit },\n\n\t\t/*\n\t\t\tTags Functions.\n\t\t*/\n\t\ttags : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tregister   : { value : tagsRegister },\n\t\t\t\tunregister : { value : tagsUnregister },\n\t\t\t\thas        : { value : tagsHas },\n\t\t\t\tget        : { value : tagsGet }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\tevalStatements : { value : (...args) => Scripting.evalJavaScript(...args) } // SEE: `markup/scripting.js`.\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrocontext.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, DebugView, Patterns, State, Wikifier, throwError */\n\nvar MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tMacroContext Class.\n\t*******************************************************************************************************************/\n\tclass MacroContext {\n\t\tconstructor(contextData) {\n\t\t\tconst context = Object.assign({\n\t\t\t\tparent      : null,\n\t\t\t\tmacro       : null,\n\t\t\t\tname        : '',\n\t\t\t\tdisplayName : '',\n\t\t\t\targs        : null,\n\t\t\t\tpayload     : null,\n\t\t\t\tparser      : null,\n\t\t\t\tsource      : ''\n\t\t\t}, contextData);\n\n\t\t\tif (context.macro === null || context.name === '' || context.parser === null) {\n\t\t\t\tthrow new TypeError('context object missing required properties');\n\t\t\t}\n\n\t\t\tObject.defineProperties(this, {\n\t\t\t\tself : {\n\t\t\t\t\tvalue : context.macro\n\t\t\t\t},\n\n\t\t\t\tname : {\n\t\t\t\t\tvalue : typeof context.macro._ALIAS_OF === 'undefined' ? context.name : context.macro._ALIAS_OF\n\t\t\t\t},\n\n\t\t\t\tdisplayName : {\n\t\t\t\t\tvalue : context.name\n\t\t\t\t},\n\n\t\t\t\targs : {\n\t\t\t\t\tvalue : context.args\n\t\t\t\t},\n\n\t\t\t\tpayload : {\n\t\t\t\t\tvalue : context.payload\n\t\t\t\t},\n\n\t\t\t\tsource : {\n\t\t\t\t\tvalue : context.source\n\t\t\t\t},\n\n\t\t\t\tparent : {\n\t\t\t\t\tvalue : context.parent\n\t\t\t\t},\n\n\t\t\t\tparser : {\n\t\t\t\t\tvalue : context.parser\n\t\t\t\t},\n\n\t\t\t\t_output : {\n\t\t\t\t\tvalue : context.parser.output\n\t\t\t\t},\n\n\t\t\t\t_shadows : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugView : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t},\n\n\t\t\t\t_debugViewEnabled : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : Config.debug\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tget output() {\n\t\t\treturn this._debugViewEnabled ? this.debugView.output : this._output;\n\t\t}\n\n\t\tget shadows() {\n\t\t\treturn [...this._shadows];\n\t\t}\n\n\t\tget shadowView() {\n\t\t\tconst view = new Set();\n\t\t\tthis.contextSelectAll(ctx => ctx._shadows)\n\t\t\t\t.forEach(ctx => ctx._shadows.forEach(name => view.add(name)));\n\t\t\treturn [...view];\n\t\t}\n\n\t\tget debugView() {\n\t\t\tif (this._debugViewEnabled) {\n\t\t\t\treturn this._debugView !== null ? this._debugView : this.createDebugView();\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextHas(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tcontextSelect(filter) {\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tcontextSelectAll(filter) {\n\t\t\tconst result = [];\n\t\t\tlet context = this;\n\n\t\t\twhile ((context = context.parent) !== null) {\n\t\t\t\tif (filter(context)) {\n\t\t\t\t\tresult.push(context);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\taddShadow(...names) {\n\t\t\tif (!this._shadows) {\n\t\t\t\tthis._shadows = new Set();\n\t\t\t}\n\n\t\t\tconst varRe = new RegExp(`^${Patterns.variable}$`);\n\n\t\t\tnames\n\t\t\t\t.flat(Infinity)\n\t\t\t\t.forEach(name => {\n\t\t\t\t\tif (typeof name !== 'string') {\n\t\t\t\t\t\tthrow new TypeError(`variable name must be a string; type: ${typeof name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!varRe.test(name)) {\n\t\t\t\t\t\tthrow new Error(`invalid variable name \"${name}\"`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._shadows.add(name);\n\t\t\t\t});\n\t\t}\n\n\t\tcreateShadowWrapper(callback, doneCallback, startCallback) {\n\t\t\tconst shadowContext = this;\n\t\t\tlet shadowStore;\n\n\t\t\tif (typeof callback === 'function') {\n\t\t\t\tshadowStore = {};\n\t\t\t\tthis.shadowView.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\t\t\tshadowStore[varName] = store[varKey];\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn function (...args) {\n\t\t\t\tif (typeof startCallback === 'function') {\n\t\t\t\t\tstartCallback.apply(this, args);\n\t\t\t\t}\n\n\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\tconst shadowNames = Object.keys(shadowStore);\n\t\t\t\t\tconst valueCache  = shadowNames.length > 0 ? {} : null;\n\t\t\t\t\tconst macroParser = Wikifier.Parser.get('macro');\n\t\t\t\t\tlet contextCache;\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t\tcallback.\n\t\t\t\t\t*/\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tCache the existing values of the variables to be shadowed and assign the\n\t\t\t\t\t\t\tshadow values.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tstore[varKey] = shadowStore[varName];\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Cache the existing macro execution context and assign the shadow context.\n\t\t\t\t\t\tcontextCache = macroParser.context;\n\t\t\t\t\t\tmacroParser.context = shadowContext;\n\n\t\t\t\t\t\t// Call the callback function.\n\t\t\t\t\t\tcallback.apply(this, args);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\t// Revert the macro execution context shadowing.\n\t\t\t\t\t\tif (contextCache !== undefined) {\n\t\t\t\t\t\t\tmacroParser.context = contextCache;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Revert the variable shadowing.\n\t\t\t\t\t\tshadowNames.forEach(varName => {\n\t\t\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\tUpdate the shadow store with the variable's current value, in case it\n\t\t\t\t\t\t\t\twas modified during the callback.\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tshadowStore[varName] = store[varKey];\n\n\t\t\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (typeof doneCallback === 'function') {\n\t\t\t\t\tdoneCallback.apply(this, args);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tcreateDebugView(name, title) {\n\t\t\tthis._debugView = new DebugView(\n\t\t\t\tthis._output,\n\t\t\t\t'macro',\n\t\t\t\tname ? name : this.displayName,\n\t\t\t\ttitle ? title : this.source\n\t\t\t);\n\n\t\t\tif (this.payload !== null && this.payload.length > 0) {\n\t\t\t\tthis._debugView.modes({ nonvoid : true });\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = true;\n\t\t\treturn this._debugView;\n\t\t}\n\n\t\tremoveDebugView() {\n\t\t\tif (this._debugView !== null) {\n\t\t\t\tthis._debugView.remove();\n\t\t\t\tthis._debugView = null;\n\t\t\t}\n\n\t\t\tthis._debugViewEnabled = false;\n\t\t}\n\n\t\terror(message, source, stack) {\n\t\t\treturn throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source, stack);\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn MacroContext;\n})();\n\n/***********************************************************************************************************************\n\n\tmacros/macrolib.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Config, DebugView, Engine, Has, L10n, Macro, NodeTyper, Patterns, Scripting, SimpleAudio, State,\n\t       Story, TempState, Util, Wikifier, postdisplay, prehistory, storage, toStringOrDefault\n*/\n\n(() => {\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tVariables Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<capture>>\n\t*/\n\tMacro.add('capture', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\t\ttsVarRe  : new RegExp(`(${Patterns.variable})`,'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.raw.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst valueCache = {};\n\n\t\t\t/*\n\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t`Wikifier` call.\n\t\t\t*/\n\t\t\ttry {\n\t\t\t\tconst tsVarRe = this.self.tsVarRe;\n\t\t\t\tlet match;\n\n\t\t\t\t/*\n\t\t\t\t\tCache the existing values of the variables and add a shadow.\n\t\t\t\t*/\n\t\t\t\twhile ((match = tsVarRe.exec(this.args.raw)) !== null) {\n\t\t\t\t\tconst varName = match[1];\n\t\t\t\t\tconst varKey  = varName.slice(1);\n\t\t\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (store.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tvalueCache[varKey] = store[varKey];\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.addShadow(varName);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier(this.output, this.payload[0].contents);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t// Revert the variable shadowing.\n\t\t\t\tthis.shadows.forEach(varName => {\n\t\t\t\t\tconst varKey = varName.slice(1);\n\t\t\t\t\tconst store  = varName[0] === '$' ? State.variables : State.temporary;\n\n\t\t\t\t\tif (valueCache.hasOwnProperty(varKey)) {\n\t\t\t\t\t\tstore[varKey] = valueCache[varKey];\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete store[varKey];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<set>>\n\t*/\n\tMacro.add('set', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<unset>>\n\t*/\n\tMacro.add('unset', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(\n\t\t\t`State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})`,\n\t\t\t'g'\n\t\t),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story/temporary variable list specified');\n\t\t\t}\n\n\t\t\tconst jsVarRe = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst store = State[match[1]];\n\t\t\t\tconst name  = match[2];\n\n\t\t\t\tif (store.hasOwnProperty(name)) {\n\t\t\t\t\tdelete store[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remember>>\n\t*/\n\tMacro.add('remember', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember') || {};\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\t\t\t\tremember[name] = State.variables[name];\n\t\t\t}\n\n\t\t\tif (!storage.set('remember', remember)) {\n\t\t\t\treturn this.error(`unknown error, cannot remember: ${this.args.raw}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t},\n\n\t\tinit() {\n\t\t\tconst remember = storage.get('remember');\n\n\t\t\tif (remember) {\n\t\t\t\tObject.keys(remember).forEach(name => State.variables[name] = remember[name]);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<forget>>\n\t*/\n\tMacro.add('forget', {\n\t\tskipArgs : true,\n\t\tjsVarRe  : new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g'),\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no story variable list specified');\n\t\t\t}\n\n\t\t\tconst remember = storage.get('remember');\n\t\t\tconst jsVarRe  = this.self.jsVarRe;\n\t\t\tlet match;\n\t\t\tlet needStore = false;\n\n\t\t\twhile ((match = jsVarRe.exec(this.args.full)) !== null) {\n\t\t\t\tconst name = match[1];\n\n\t\t\t\tif (State.variables.hasOwnProperty(name)) {\n\t\t\t\t\tdelete State.variables[name];\n\t\t\t\t}\n\n\t\t\t\tif (remember && remember.hasOwnProperty(name)) {\n\t\t\t\t\tneedStore = true;\n\t\t\t\t\tdelete remember[name];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needStore) {\n\t\t\t\tif (Object.keys(remember).length === 0) {\n\t\t\t\t\tif (!storage.delete('remember')) {\n\t\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!storage.set('remember', remember)) {\n\t\t\t\t\treturn this.error('unknown error, cannot update remember store');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tScripting Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<run>>\n\t*/\n\tMacro.add('run', 'set'); // add <<run>> as an alias of <<set>>\n\n\t/*\n\t\t<<script>>\n\t*/\n\tMacro.add('script', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst output = document.createDocumentFragment();\n\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(this.payload[0].contents, output);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.createDebugView();\n\t\t\t}\n\n\t\t\tif (output.hasChildNodes()) {\n\t\t\t\tthis.output.appendChild(output);\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDisplay Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<include>>\n\t*/\n\tMacro.add('include', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tpassage = Story.get(passage);\n\t\t\tlet $el;\n\n\t\t\tif (this.args[1]) {\n\t\t\t\t$el = jQuery(document.createElement(this.args[1]))\n\t\t\t\t\t.addClass(`${passage.domId} macro-${this.name}`)\n\t\t\t\t\t.attr('data-passage', passage.title)\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(this.output);\n\t\t\t}\n\n\t\t\t$el.wiki(passage.processText());\n\t\t}\n\t});\n\n\t/*\n\t\t<<nobr>>\n\t*/\n\tMacro.add('nobr', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\t/*\n\t\t\t\tWikify the contents, after removing all leading & trailing newlines and compacting\n\t\t\t\tall internal sequences of newlines into single spaces.\n\t\t\t*/\n\t\t\tnew Wikifier(this.output, this.payload[0].contents.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' '));\n\t\t}\n\t});\n\n\t/*\n\t\t<<print>>, <<=>>, & <<->>\n\t*/\n\tMacro.add(['print', '=', '-'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = toStringOrDefault(Scripting.evalJavaScript(this.args.full), null);\n\n\t\t\t\tif (result !== null) {\n\t\t\t\t\tnew Wikifier(this.output, this.name === '-' ? Util.escape(result) : result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<silently>>\n\t*/\n\tMacro.add('silently', {\n\t\tskipArgs : true,\n\t\ttags     : null,\n\n\t\thandler() {\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.payload[0].contents.trim());\n\n\t\t\tif (Config.debug) {\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tthis.debugView.modes({ block : true, hidden : true });\n\t\t\t\tthis.output.appendChild(frag);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Discard the output, unless there were errors.\n\t\t\t\tconst errList = [...frag.querySelectorAll('.error')].map(errEl => errEl.textContent);\n\n\t\t\t\tif (errList.length > 0) {\n\t\t\t\t\treturn this.error(`error${errList.length === 1 ? '' : 's'} within contents (${errList.join('; ')})`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<type speed [start delay] [class classes] [element tag] [id ID] [keep|none] [skipkey key]>>\n\t*/\n\tMacro.add('type', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttypeId  : 0,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no speed specified');\n\t\t\t}\n\n\t\t\tconst speed = Util.fromCssTime(this.args[0]); // in milliseconds\n\n\t\t\tif (speed < 0) {\n\t\t\t\treturn this.error(`speed time value must be non-negative (received: ${this.args[0]})`);\n\t\t\t}\n\n\t\t\tlet cursor;\n\t\t\tlet elClass = '';\n\t\t\tlet elId    = '';\n\t\t\tlet elTag   = 'div';\n\t\t\tlet skipKey = Config.macros.typeSkipKey;\n\t\t\tlet start   = 400; // in milliseconds\n\n\t\t\t// Process optional arguments.\n\t\t\tconst options = this.args.slice(1);\n\n\t\t\twhile (options.length > 0) {\n\t\t\t\tconst option = options.shift();\n\n\t\t\t\tswitch (option) {\n\t\t\t\tcase 'class': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('class option missing required class name(s)');\n\t\t\t\t\t}\n\n\t\t\t\t\telClass = options.shift();\n\n\t\t\t\t\tif (elClass === '') {\n\t\t\t\t\t\tthrow new Error('class option class name(s) must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'element': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('element option missing required element tag name');\n\t\t\t\t\t}\n\n\t\t\t\t\telTag = options.shift();\n\n\t\t\t\t\tif (elTag === '') {\n\t\t\t\t\t\tthrow new Error('element option tag name must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'id': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('id option missing required ID');\n\t\t\t\t\t}\n\n\t\t\t\t\telId = options.shift();\n\n\t\t\t\t\tif (elId === '') {\n\t\t\t\t\t\tthrow new Error('id option ID must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'keep':\n\t\t\t\t\tcursor = 'keep';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'none':\n\t\t\t\t\tcursor = 'none';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'skipkey': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('skipkey option missing required key value');\n\t\t\t\t\t}\n\n\t\t\t\t\tskipKey = options.shift();\n\n\t\t\t\t\tif (skipKey === '') {\n\t\t\t\t\t\tthrow new Error('skipkey option key value must be non-empty (received: \"\")');\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase 'start': {\n\t\t\t\t\tif (options.length === 0) {\n\t\t\t\t\t\treturn this.error('start option missing required time value');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = options.shift();\n\t\t\t\t\tstart = Util.fromCssTime(value);\n\n\t\t\t\t\tif (start < 0) {\n\t\t\t\t\t\tthrow new Error(`start option time value must be non-negative (received: ${value})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.error(`unknown option: ${option}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contents = this.payload[0].contents;\n\n\t\t\t// Do nothing if there's no content to type out.\n\t\t\tif (contents.trim() === '') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\t// Set up our base class name and event namespace.\n\t\t\tconst className = `macro-${this.name}`;\n\t\t\tconst namespace = `.${className}`;\n\n\t\t\t// Create a target to be later replaced by the typing wrapper.\n\t\t\tconst $target = jQuery(document.createElement(elTag))\n\t\t\t\t.addClass(`${className} ${className}-target`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Initialize the queue and clean up handlers.\n\t\t\tif (!TempState.macroTypeQueue) {\n\t\t\t\t// Set up the typing handler queue for all invocations.\n\t\t\t\tTempState.macroTypeQueue = [];\n\n\t\t\t\t// Immediately clear any existing handlers from our namespace and set up a\n\t\t\t\t// `:passageinit` event handler to clean up after navigation.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(namespace)\n\t\t\t\t\t.one(`:passageinit${namespace}`, () => $(document).off(namespace));\n\t\t\t}\n\n\t\t\t// If the queue is empty at this point, set the start typing flag.\n\t\t\tconst startTyping = TempState.macroTypeQueue.length === 0;\n\n\t\t\t// Generate our unique ID.\n\t\t\tconst selfId = ++this.self.typeId;\n\n\t\t\t// Push our typing handler onto the queue.\n\t\t\tTempState.macroTypeQueue.push({\n\t\t\t\tid : selfId,\n\n\t\t\t\thandler() {\n\t\t\t\t\tconst $wrapper = jQuery(document.createElement(elTag))\n\t\t\t\t\t\t.addClass(className);\n\n\t\t\t\t\t// Add the user ID, if any.\n\t\t\t\t\tif (elId) {\n\t\t\t\t\t\t$wrapper.attr('id', elId);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add the user class(es), if any.\n\t\t\t\t\tif (elClass) {\n\t\t\t\t\t\t$wrapper.addClass(elClass);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wikify the contents into `$wrapper`.\n\t\t\t\t\tnew Wikifier($wrapper, contents);\n\n\t\t\t\t\t// Cache info about the current turn.\n\t\t\t\t\tconst passage = State.passage;\n\t\t\t\t\tconst turn    = State.turns;\n\n\t\t\t\t\t// Skip typing if….\n\t\t\t\t\tif (\n\t\t\t\t\t\t// …we've visited the passage before.\n\t\t\t\t\t\t!Config.macros.typeVisitedPassages\n\t\t\t\t\t\t&& State.passages.slice(0, -1).some(title => title === passage)\n\n\t\t\t\t\t\t// …there were any content errors.\n\t\t\t\t\t\t|| $wrapper.find('.error').length > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t\t// Run the next typing handler in the queue, if any.\n\t\t\t\t\t\tif (TempState.macroTypeQueue.length > 0) {\n\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Exit.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create a new `NodeTyper` instance for the wrapper's contents and\n\t\t\t\t\t// replace the target with the typing wrapper.\n\t\t\t\t\tconst typer = new NodeTyper({\n\t\t\t\t\t\ttargetNode : $wrapper.get(0),\n\t\t\t\t\t\tclassNames : cursor === 'none' ? null : `${className}-cursor`\n\t\t\t\t\t});\n\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t// Set up event IDs.\n\t\t\t\t\tconst typingCompleteId = ':typingcomplete';\n\t\t\t\t\tconst typingStartId    = ':typingstart';\n\t\t\t\t\tconst typingStopId     = ':typingstop';\n\t\t\t\t\tconst keydownAndNS     = `keydown${namespace}`;\n\t\t\t\t\tconst typingStopAndNS  = `${typingStopId}${namespace}`;\n\n\t\t\t\t\t// Set up handlers for spacebar aborting and continuations.\n\t\t\t\t\t$(document)\n\t\t\t\t\t\t.off(keydownAndNS)\n\t\t\t\t\t\t.on(keydownAndNS, ev => {\n\t\t\t\t\t\t\t// Finish typing if the player aborts via the skip key.\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tUtil.scrubEventKey(ev.key) === skipKey\n\t\t\t\t\t\t\t\t&& (ev.target === document.body || ev.target === document.documentElement)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t\t$(document).off(keydownAndNS);\n\t\t\t\t\t\t\t\ttyper.finish();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.one(typingStopAndNS, () => {\n\t\t\t\t\t\t\tif (TempState.macroTypeQueue) {\n\t\t\t\t\t\t\t\t// If the queue is empty, fire the typing complete event.\n\t\t\t\t\t\t\t\tif (TempState.macroTypeQueue.length === 0) {\n\t\t\t\t\t\t\t\t\tjQuery.event.trigger(typingCompleteId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Elsewise, run the next typing handler in the queue.\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t// Set up the typing interval and start/stop event firing.\n\t\t\t\t\tconst typeNode = function typeNode() {\n\t\t\t\t\t\t// Fire the typing start event.\n\t\t\t\t\t\t$wrapper.trigger(typingStartId);\n\n\t\t\t\t\t\tconst typeNodeId = setInterval(() => {\n\t\t\t\t\t\t\t// Stop typing if….\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t// …we've navigated away.\n\t\t\t\t\t\t\t\tState.passage !== passage\n\t\t\t\t\t\t\t\t|| State.turns !== turn\n\n\t\t\t\t\t\t\t\t// …we're done typing.\n\t\t\t\t\t\t\t\t|| !typer.type()\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Terminate the timer.\n\t\t\t\t\t\t\t\tclearInterval(typeNodeId);\n\n\t\t\t\t\t\t\t\t// Remove this handler from the queue, if the queue still exists and the\n\t\t\t\t\t\t\t\t// handler IDs match.\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.length > 0\n\t\t\t\t\t\t\t\t\t&& TempState.macroTypeQueue.first().id === selfId\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Fire the typing stop event.\n\t\t\t\t\t\t\t\t$wrapper.trigger(typingStopId);\n\n\t\t\t\t\t\t\t\t// Add the done class to the wrapper.\n\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-done`);\n\n\t\t\t\t\t\t\t\t// Add the cursor class to the wrapper, if we're keeping it.\n\t\t\t\t\t\t\t\tif (cursor === 'keep') {\n\t\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-cursor`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, speed);\n\t\t\t\t\t};\n\n\t\t\t\t\t// Kick off typing the node.\n\t\t\t\t\tif (start) {\n\t\t\t\t\t\tsetTimeout(typeNode, start);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\ttypeNode();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we're to start typing, then either set up a `:passageend` event handler\n\t\t\t// to do so or start it immediately, depending on the engine state.\n\t\t\tif (startTyping) {\n\t\t\t\tif (Engine.isPlaying()) {\n\t\t\t\t\t$(document).one(`:passageend${namespace}`, () => TempState.macroTypeQueue.first().handler());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tTempState.macroTypeQueue.first().handler();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<display>>\n\t*/\n\tMacro.add('display', 'include'); // add <<display>> as an alias of <<include>>\n\n\n\t/*******************************************************************************************************************\n\t\tControl Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<if>>, <<elseif>>, & <<else>>\n\t*/\n\tMacro.add('if', {\n\t\tskipArgs   : true,\n\t\ttags       : ['elseif', 'else'],\n\t\telseifWsRe : /^\\s*if\\b/i,\n\t\tifAssignRe : /[^!=&^|<>*/%+-]=[^=>]/,\n\n\t\thandler() {\n\t\t\tlet i;\n\n\t\t\ttry {\n\t\t\t\tconst len = this.payload.length;\n\n\t\t\t\t// Sanity checks.\n\t\t\t\tconst elseifWsRe = this.self.elseifWsRe;\n\t\t\t\tconst ifAssignRe = this.self.ifAssignRe;\n\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t/* eslint-disable prefer-template */\n\t\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\t\tcase 'else':\n\t\t\t\t\t\tif (this.payload[i].args.raw.length > 0) {\n\t\t\t\t\t\t\tif (elseifWsRe.test(this.payload[i].args.raw)) {\n\t\t\t\t\t\t\t\treturn this.error(`whitespace is not allowed between the \"else\" and \"if\" in <<elseif>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn this.error(`<<else>> does not accept a conditional expression (perhaps you meant to use <<elseif>>), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\t\treturn this.error('<<else>> must be the final clause');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (this.payload[i].args.full.length === 0) {\n\t\t\t\t\t\t\treturn this.error(`no conditional expression specified for <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (\n\t\t\t\t\t\t\t   Config.macros.ifAssignmentError\n\t\t\t\t\t\t\t&& ifAssignRe.test(this.payload[i].args.full)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn this.error(`assignment operator found within <<${this.payload[i].name}>> clause${i > 0 ? ' (#' + i + ')' : ''} (perhaps you meant to use an equality operator: ==, ===, eq, is), invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t/* eslint-enable prefer-template */\n\t\t\t\t}\n\n\t\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\t\tlet success = false;\n\n\t\t\t\t// Evaluate the clauses.\n\t\t\t\tfor (/* declared previously */ i = 0; i < len; ++i) {\n\t\t\t\t\t// Custom debug view setup for the current clause.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Conditional test.\n\t\t\t\t\tif (this.payload[i].name === 'else' || !!evalJavaScript(this.payload[i].args.full)) {\n\t\t\t\t\t\tsuccess = true;\n\t\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t\t// Custom debug view setup for a failed conditional.\n\t\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup for the remaining clauses.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tFake a debug view for `<</if>>`.  We do this to aid the checking of nesting\n\t\t\t\t\t\tand as a quick indicator of if any of the clauses matched.\n\t\t\t\t\t*/\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : !success,\n\t\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression in <<${i === 0 ? 'if' : 'elseif'}>> clause${i > 0 ? ' (#' + i + ')' : ''}: ${typeof ex === 'object' ? `${ex.name}: ${ex.message}` : ex}`, null, ex.stack); // eslint-disable-line prefer-template\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<switch>>, <<case>>, & <<default>>\n\t*/\n\tMacro.add('switch', {\n\t\tskipArgs : ['switch'],\n\t\ttags     : ['case', 'default'],\n\n\t\thandler() {\n\t\t\tif (this.args.full.length === 0) {\n\t\t\t\treturn this.error('no expression specified');\n\t\t\t}\n\n\t\t\tconst len = this.payload.length;\n\n\t\t\t// if (len === 1 || !this.payload.some(p => p.name === 'case')) {\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no cases specified');\n\t\t\t}\n\n\t\t\tlet i;\n\n\t\t\t// Sanity checks.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\tswitch (this.payload[i].name) {\n\t\t\t\tcase 'default':\n\t\t\t\t\tif (this.payload[i].args.length > 0) {\n\t\t\t\t\t\treturn this.error(`<<default>> does not accept values, invalid: ${this.payload[i].args.raw}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (i + 1 !== len) {\n\t\t\t\t\t\treturn this.error('<<default>> must be the final case');\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no value(s) specified for <<${this.payload[i].name}>> (#${i})`);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet result;\n\n\t\t\ttry {\n\t\t\t\tresult = Scripting.evalJavaScript(this.args.full);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\n\t\t\tconst debugView = this.debugView; // cache it now, to be modified later\n\t\t\tlet success = false;\n\n\t\t\t// Initial debug view setup for `<<switch>>`.\n\t\t\tif (Config.debug) {\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Evaluate the clauses.\n\t\t\tfor (/* declared previously */ i = 1; i < len; ++i) {\n\t\t\t\t// Custom debug view setup for the current case.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({ nonvoid : false });\n\t\t\t\t}\n\n\t\t\t\t// Case test(s).\n\t\t\t\tif (this.payload[i].name === 'default' || this.payload[i].args.some(val => val === result)) {\n\t\t\t\t\tsuccess = true;\n\t\t\t\t\tnew Wikifier(this.output, this.payload[i].contents);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (Config.debug) {\n\t\t\t\t\t// Custom debug view setup for a failed case.\n\t\t\t\t\tthis.debugView.modes({\n\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup for the remaining cases.\n\t\t\tif (Config.debug) {\n\t\t\t\tfor (++i; i < len; ++i) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true,\n\t\t\t\t\t\t\tinvalid : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t\tFinalize the debug view for `<<switch>>` and fake a debug view for `<</switch>>`.\n\t\t\t\t\tWe do both as a quick indicator of if any of the cases matched and the latter\n\t\t\t\t\tto aid the checking of nesting.\n\t\t\t\t*/\n\t\t\t\tdebugView\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t\tthis\n\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t.modes({\n\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\thidden  : true, // !success,\n\t\t\t\t\t\tinvalid : !success\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<for>>, <<break>>, & <<continue>>\n\t*/\n\tMacro.add('for', {\n\t\t/* eslint-disable max-len */\n\t\tskipArgs    : true,\n\t\ttags        : null,\n\t\thasRangeRe  : new RegExp(`^\\\\S${Patterns.anyChar}*?\\\\s+range\\\\s+\\\\S${Patterns.anyChar}*?$`),\n\t\trangeRe     : new RegExp(`^(?:State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s*,\\\\s*)?State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s+range\\\\s+(\\\\S${Patterns.anyChar}*?)$`),\n\t\tthreePartRe : /^([^;]*?)\\s*;\\s*([^;]*?)\\s*;\\s*([^;]*?)$/,\n\t\tforInRe     : /^\\S+\\s+in\\s+\\S+/i,\n\t\tforOfRe     : /^\\S+\\s+of\\s+\\S+/i,\n\t\t/* eslint-enable max-len */\n\n\t\thandler() {\n\t\t\tconst argsStr = this.args.full.trim();\n\t\t\tconst payload = this.payload[0].contents.replace(/\\n$/, '');\n\n\t\t\t// Empty form.\n\t\t\tif (argsStr.length === 0) {\n\t\t\t\tthis.self.handleFor.call(this, payload, null, true, null);\n\t\t\t}\n\n\t\t\t// Range form.\n\t\t\telse if (this.self.hasRangeRe.test(argsStr)) {\n\t\t\t\tconst parts = argsStr.match(this.self.rangeRe);\n\n\t\t\t\tif (parts === null) {\n\t\t\t\t\treturn this.error('invalid range form syntax, format: [index ,] value range collection');\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleForRange.call(\n\t\t\t\t\tthis,\n\t\t\t\t\tpayload,\n\t\t\t\t\t{ type : parts[1], name : parts[2] },\n\t\t\t\t\t{ type : parts[3], name : parts[4] },\n\t\t\t\t\tparts[5]\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Conditional forms.\n\t\t\telse {\n\t\t\t\tlet init;\n\t\t\t\tlet condition;\n\t\t\t\tlet post;\n\n\t\t\t\t// Conditional-only form.\n\t\t\t\tif (argsStr.indexOf(';') === -1) {\n\t\t\t\t\t// Sanity checks.\n\t\t\t\t\tif (this.self.forInRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…in is not supported; see: for…range');\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.self.forOfRe.test(argsStr)) {\n\t\t\t\t\t\treturn this.error('invalid syntax, for…of is not supported; see: for…range');\n\t\t\t\t\t}\n\n\t\t\t\t\tcondition = argsStr;\n\t\t\t\t}\n\n\t\t\t\t// 3-part conditional form.\n\t\t\t\telse {\n\t\t\t\t\tconst parts = argsStr.match(this.self.threePartRe);\n\n\t\t\t\t\tif (parts === null) {\n\t\t\t\t\t\treturn this.error('invalid 3-part conditional form syntax, format: [init] ; [condition] ; [post]');\n\t\t\t\t\t}\n\n\t\t\t\t\tinit      = parts[1];\n\t\t\t\t\tcondition = parts[2].trim();\n\t\t\t\t\tpost      = parts[3];\n\n\t\t\t\t\tif (condition.length === 0) {\n\t\t\t\t\t\tcondition = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.self.handleFor.call(this, payload, init, condition, post);\n\t\t\t}\n\t\t},\n\n\t\thandleFor(payload, init, condition, post) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet first  = true;\n\t\t\tlet safety = Config.macros.maxLoopIterations;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tif (init) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tevalJavaScript(init);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad init expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile (evalJavaScript(condition)) {\n\t\t\t\t\tif (--safety < 0) {\n\t\t\t\t\t\treturn this.error(`exceeded configured maximum loop iterations (${Config.macros.maxLoopIterations})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (post) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevalJavaScript(post);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\treturn this.error(`bad post expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`bad conditional expression: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\thandleForRange(payload, indexVar, valueVar, rangeExp) {\n\t\t\tlet first     = true;\n\t\t\tlet rangeList;\n\n\t\t\ttry {\n\t\t\t\trangeList = this.self.toRangeList(rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tTempState.break = null;\n\n\t\t\t\tfor (let i = 0; i < rangeList.length; ++i) {\n\t\t\t\t\tif (indexVar.name) {\n\t\t\t\t\t\tState[indexVar.type][indexVar.name] = rangeList[i][0];\n\t\t\t\t\t}\n\n\t\t\t\t\tState[valueVar.type][valueVar.name] = rangeList[i][1];\n\n\t\t\t\t\tnew Wikifier(this.output, first ? payload.replace(/^\\n/, '') : payload);\n\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TempState.break != null) { // lazy equality for null\n\t\t\t\t\t\tif (TempState.break === 1) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (TempState.break === 2) {\n\t\t\t\t\t\t\tTempState.break = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tTempState.break = null;\n\t\t\t}\n\t\t},\n\n\t\ttoRangeList(rangeExp) {\n\t\t\tconst evalJavaScript = Scripting.evalJavaScript;\n\t\t\tlet value;\n\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t*/\n\t\t\t\tvalue = evalJavaScript(rangeExp[0] === '{' ? `(${rangeExp})` : rangeExp);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tif (typeof ex !== 'object') {\n\t\t\t\t\tthrow new Error(`bad range expression: ${ex}`);\n\t\t\t\t}\n\n\t\t\t\tex.message = `bad range expression: ${ex.message}`;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tlet list;\n\n\t\t\tswitch (typeof value) {\n\t\t\tcase 'string':\n\t\t\t\tlist = [];\n\t\t\t\tfor (let i = 0; i < value.length; /* empty */) {\n\t\t\t\t\tconst obj = Util.charAndPosAt(value, i);\n\t\t\t\t\tlist.push([i, obj.char]);\n\t\t\t\t\ti = 1 + obj.end;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'object':\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tlist = value.map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Set) {\n\t\t\t\t\tlist = [...value].map((val, i) => [i, val]);\n\t\t\t\t}\n\t\t\t\telse if (value instanceof Map) {\n\t\t\t\t\tlist = [...value.entries()];\n\t\t\t\t}\n\t\t\t\telse if (Util.toStringTag(value) === 'Object') {\n\t\t\t\t\tlist = Object.keys(value).map(key => [key, value[key]]);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new Error(`unsupported range expression type: ${Util.toStringTag(value)}`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`unsupported range expression type: ${typeof value}`);\n\t\t\t}\n\n\t\t\treturn list;\n\t\t}\n\t});\n\tMacro.add(['break', 'continue'], {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (this.contextHas(ctx => ctx.name === 'for')) {\n\t\t\t\tTempState.break = this.name === 'continue' ? 1 : 2;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<for>>');\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tInteractive Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<button>> & <<link>>\n\t*/\n\tMacro.add(['button', 'link'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error(`no ${this.name === 'button' ? 'button' : 'link'} text specified`);\n\t\t\t}\n\n\t\t\tconst $link = jQuery(document.createElement(this.name === 'button' ? 'button' : 'a'));\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\tconst $image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t.attr('src', this.args[0].source)\n\t\t\t\t\t\t.appendTo($link);\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t}\n\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t$link.append(document.createTextNode(this.args[0].text));\n\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the link text.\n\t\t\t\t$link.wikiWithOptions({ profile : 'core' }, this.args[0]);\n\t\t\t\tpassage = this.args.length > 1 ? this.args[1] : undefined;\n\t\t\t}\n\n\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t$link.attr('data-passage', passage);\n\n\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t$link.addClass('link-internal');\n\n\t\t\t\t\tif (Config.addVisitedLinkClass && State.hasPlayed(passage)) {\n\t\t\t\t\t\t$link.addClass('link-visited');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$link.addClass('link-broken');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$link.addClass('link-internal');\n\t\t\t}\n\n\t\t\t$link\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : passage != null // lazy equality for null\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\tthis.payload[0].contents !== ''\n\t\t\t\t\t\t? () => Wikifier.wikifyEval(this.payload[0].contents.trim())\n\t\t\t\t\t\t: null,\n\t\t\t\t\tpassage != null // lazy equality for null\n\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t: null\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<checkbox>>\n\t*/\n\tMacro.add('checkbox', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 3) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('unchecked value'); }\n\t\t\t\tif (this.args.length < 3) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst uncheckValue = this.args[1];\n\t\t\tconst checkValue   = this.args[2];\n\t\t\tconst el           = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'checkbox',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.checked ? checkValue : uncheckValue);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and input element to the appropriate value and state, as requested.\n\t\t\t*/\n\t\t\tswitch (this.args[3]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tState.setVar(varName, uncheckValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<cycle>>, <<listbox>>, <<option>>, & <<optionsfrom>>\n\t*/\n\tMacro.add(['cycle', 'listbox'], {\n\t\tisAsync  : true,\n\t\tskipArgs : ['optionsfrom'],\n\t\ttags     : ['option', 'optionsfrom'],\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no variable name specified');\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst len   = this.payload.length;\n\n\t\t\tif (len === 1) {\n\t\t\t\treturn this.error('no options specified');\n\t\t\t}\n\n\t\t\tconst autoselect = this.args.length > 1 && this.args[1] === 'autoselect';\n\t\t\tconst options    = [];\n\t\t\tconst tagCount   = { option : 0, optionsfrom : 0 };\n\t\t\tlet selectedIdx = -1;\n\n\t\t\t// Get the options and selected index, if any.\n\t\t\tfor (let i = 1; i < len; ++i) {\n\t\t\t\tconst payload = this.payload[i];\n\n\t\t\t\t// <<option label value [selected]>>\n\t\t\t\tif (payload.name === 'option') {\n\t\t\t\t\t++tagCount.option;\n\n\t\t\t\t\tif (payload.args.length === 0) {\n\t\t\t\t\t\treturn this.error(`no arguments specified for <<${payload.name}>> (#${tagCount.option})`);\n\t\t\t\t\t}\n\n\t\t\t\t\toptions.push({\n\t\t\t\t\t\tlabel : String(payload.args[0]),\n\t\t\t\t\t\tvalue : payload.args.length === 1 ? payload.args[0] : payload.args[1]\n\t\t\t\t\t});\n\n\t\t\t\t\tif (payload.args.length > 2 && payload.args[2] === 'selected') {\n\t\t\t\t\t\tif (autoselect) {\n\t\t\t\t\t\t\treturn this.error('cannot specify both the autoselect and selected keywords');\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (selectedIdx !== -1) {\n\t\t\t\t\t\t\treturn this.error(`multiple selected keywords specified for <<${payload.name}>> (#${selectedIdx + 1} & #${tagCount.option})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tselectedIdx = options.length - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// <<optionsfrom expression>>\n\t\t\t\telse {\n\t\t\t\t\t++tagCount.optionsfrom;\n\n\t\t\t\t\tif (payload.args.full.length === 0) {\n\t\t\t\t\t\treturn this.error(`no expression specified for <<${payload.name}>> (#${tagCount.optionsfrom})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet result;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tNOTE: If the first character is the left curly brace, then we\n\t\t\t\t\t\t\tassume that it's part of an object literal and wrap it within\n\t\t\t\t\t\t\tparenthesis to ensure that it is not mistaken for a block\n\t\t\t\t\t\t\tduring evaluation—which would cause an error.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tconst exp = payload.args.full;\n\t\t\t\t\t\tresult = Scripting.evalJavaScript(exp[0] === '{' ? `(${exp})` : exp);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\treturn this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (typeof result !== 'object' || result === null) {\n\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (type: ${result === null ? 'null' : typeof result})`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result instanceof Array || result instanceof Set) {\n\t\t\t\t\t\tresult.forEach(val => options.push({ label : String(val), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse if (result instanceof Map) {\n\t\t\t\t\t\tresult.forEach((val, key) => options.push({ label : String(key), value : val }));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tconst oType = Util.toStringTag(result);\n\n\t\t\t\t\t\tif (oType !== 'Object') {\n\t\t\t\t\t\t\treturn this.error(`expression must yield a supported collection or generic object (object type: ${oType})`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tObject.keys(result).forEach(key => options.push({ label : key, value : result[key] }));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No options were selected by the user, so we must select one.\n\t\t\tif (selectedIdx === -1) {\n\t\t\t\t// Attempt to automatically select an option by matching the variable's current value.\n\t\t\t\tif (autoselect) {\n\t\t\t\t\t// NOTE: This will usually fail for objects due to a variety of reasons.\n\t\t\t\t\tconst sameValueZero = Util.sameValueZero;\n\t\t\t\t\tconst curValue      = State.getVar(varName);\n\t\t\t\t\tconst curValueIdx   = options.findIndex(opt => sameValueZero(opt.value, curValue));\n\t\t\t\t\tselectedIdx = curValueIdx === -1 ? 0 : curValueIdx;\n\t\t\t\t}\n\n\t\t\t\t// Simply select the first option.\n\t\t\t\telse {\n\t\t\t\t\tselectedIdx = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set up and append the appropriate element to the output buffer.\n\t\t\tif (this.name === 'cycle') {\n\t\t\t\tlet cycleIdx = selectedIdx;\n\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t.wikiWithOptions({ profile : 'core' }, options[selectedIdx].label)\n\t\t\t\t\t.attr('id', `${this.name}-${varId}`)\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () {\n\t\t\t\t\t\tcycleIdx = (cycleIdx + 1) % options.length;\n\t\t\t\t\t\t$(this).empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label);\n\t\t\t\t\t\tState.setVar(varName, options[cycleIdx].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\t\t\telse { // this.name === 'listbox'\n\t\t\t\tconst $select = jQuery(document.createElement('select'));\n\n\t\t\t\toptions.forEach((opt, i) => {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(opt.label)\n\t\t\t\t\t\t.appendTo($select);\n\t\t\t\t});\n\n\t\t\t\t$select\n\t\t\t\t\t.attr({\n\t\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t\t})\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.val(selectedIdx)\n\t\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\t\tState.setVar(varName, options[Number(this.value)].value);\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t}\n\n\t\t\t// Set the variable to the appropriate value, as requested.\n\t\t\tState.setVar(varName, options[selectedIdx].value);\n\t\t}\n\t});\n\n\t/*\n\t\t<<linkappend>>, <<linkprepend>>, & <<linkreplace>>\n\t*/\n\tMacro.add(['linkappend', 'linkprepend', 'linkreplace'], {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no link text specified');\n\t\t\t}\n\n\t\t\tconst $link      = jQuery(document.createElement('a'));\n\t\t\tconst $insert    = jQuery(document.createElement('span'));\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\n\t\t\t$link\n\t\t\t\t.wikiWithOptions({ profile : 'core' }, this.args[0])\n\t\t\t\t.addClass(`link-internal macro-${this.name}`)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tnamespace : '.macros',\n\t\t\t\t\tone       : true\n\t\t\t\t}, this.createShadowWrapper(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tif (this.name === 'linkreplace') {\n\t\t\t\t\t\t\t$link.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t$link\n\t\t\t\t\t\t\t\t.wrap(`<span class=\"macro-${this.name}\"></span>`)\n\t\t\t\t\t\t\t\t.replaceWith(() => $link.html());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\t\t\t\t\t\t\t$insert.append(frag);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (transition) {\n\t\t\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t$insert.addClass(`macro-${this.name}-insert`);\n\n\t\t\tif (transition) {\n\t\t\t\t$insert.addClass(`macro-${this.name}-in`);\n\t\t\t}\n\n\t\t\tif (this.name === 'linkprepend') {\n\t\t\t\t$insert.insertBefore($link);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$insert.insertAfter($link);\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<numberbox>> & <<textbox>>\n\t*/\n\tMacro.add(['numberbox', 'textbox'], {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst asNumber     = this.name === 'numberbox';\n\t\t\tconst defaultValue = asNumber ? Number(this.args[1]) : this.args[1];\n\n\t\t\tif (asNumber && Number.isNaN(defaultValue)) {\n\t\t\t\treturn this.error(`default value \"${this.args[1]}\" is neither a number nor can it be parsed into a number`);\n\t\t\t}\n\n\t\t\tconst varId = Util.slugify(varName);\n\t\t\tconst el    = document.createElement('input');\n\t\t\tlet autofocus = false;\n\t\t\tlet passage;\n\n\t\t\tif (this.args.length > 3) {\n\t\t\t\tpassage   = this.args[2];\n\t\t\t\tautofocus = this.args[3] === 'autofocus';\n\t\t\t}\n\t\t\telse if (this.args.length > 2) {\n\t\t\t\tif (this.args[2] === 'autofocus') {\n\t\t\t\t\tautofocus = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpassage = this.args[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (typeof passage === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = passage.link;\n\t\t\t}\n\n\t\t\t// Set up and append the input element to the output buffer.\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : asNumber ? 'number' : 'text',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\t\t\t\t}))\n\t\t\t\t.on('keypress.macros', this.createShadowWrapper(function (ev) {\n\t\t\t\t\t// If Return/Enter is pressed, set the variable and, optionally, forward to another passage.\n\t\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\tState.setVar(varName, asNumber ? Number(this.value) : this.value);\n\n\t\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Set the step value for `<input type=\"number\">`.\n\t\t\tif (asNumber) {\n\t\t\t\tel.step = 'any';\n\t\t\t}\n\n\t\t\t// Set the variable and input element to the default value.\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\tel.value = defaultValue;\n\n\t\t\t// Autofocus the input element, if requested.\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<radiobutton>>\n\t*/\n\tMacro.add('radiobutton', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('checked value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\tconst varId      = Util.slugify(varName);\n\t\t\tconst checkValue = this.args[1];\n\t\t\tconst el         = document.createElement('input');\n\n\t\t\t/*\n\t\t\t\tSet up and initialize the group counter.\n\t\t\t*/\n\t\t\tif (!TempState.hasOwnProperty(this.name)) {\n\t\t\t\tTempState[this.name] = {};\n\t\t\t}\n\n\t\t\tif (!TempState[this.name].hasOwnProperty(varId)) {\n\t\t\t\tTempState[this.name][varId] = 0;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet up and append the input element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}-${TempState[this.name][varId]++}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\ttype     : 'radio',\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tif (this.checked) {\n\t\t\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable to the checked value and the input element to checked, if requested.\n\t\t\t*/\n\t\t\tswitch (this.args[2]) {\n\t\t\tcase 'autocheck':\n\t\t\t\tif (State.getVar(varName) === checkValue) {\n\t\t\t\t\tel.checked = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'checked':\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<textarea>>\n\t*/\n\tMacro.add('textarea', {\n\t\tisAsync : true,\n\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('variable name'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('default value'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\t// Ensure that the variable name argument is a string.\n\t\t\tif (typeof this.args[0] !== 'string') {\n\t\t\t\treturn this.error('variable name argument is not a string');\n\t\t\t}\n\n\t\t\tconst varName = this.args[0].trim();\n\n\t\t\t// Try to ensure that we receive the variable's name (incl. sigil), not its value.\n\t\t\tif (varName[0] !== '$' && varName[0] !== '_') {\n\t\t\t\treturn this.error(`variable name \"${this.args[0]}\" is missing its sigil ($ or _)`);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst varId        = Util.slugify(varName);\n\t\t\tconst defaultValue = this.args[1];\n\t\t\tconst autofocus    = this.args[2] === 'autofocus';\n\t\t\tconst el           = document.createElement('textarea');\n\n\t\t\t/*\n\t\t\t\tSet up and append the textarea element to the output buffer.\n\t\t\t*/\n\t\t\tjQuery(el)\n\t\t\t\t.attr({\n\t\t\t\t\tid       : `${this.name}-${varId}`,\n\t\t\t\t\tname     : `${this.name}-${varId}`,\n\t\t\t\t\trows     : 4,\n\t\t\t\t\t// cols     : 68, // instead of setting \"cols\" we set the `min-width` in CSS\n\t\t\t\t\ttabindex : 0 // for accessiblity\n\t\t\t\t})\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.on('change.macros', this.createShadowWrapper(function () {\n\t\t\t\t\tState.setVar(varName, this.value);\n\t\t\t\t}))\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t/*\n\t\t\t\tSet the variable and textarea element to the default value.\n\t\t\t*/\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\t// Ideally, we should be setting `.defaultValue` here, but IE doesn't support it,\n\t\t\t// so we have to use `.textContent`, which is equivalent.\n\t\t\tel.textContent = defaultValue;\n\n\t\t\t/*\n\t\t\t\tAutofocus the textarea element, if requested.\n\t\t\t*/\n\t\t\tif (autofocus) {\n\t\t\t\t// Set the element's \"autofocus\" attribute.\n\t\t\t\tel.setAttribute('autofocus', 'autofocus');\n\n\t\t\t\t// Set up a single-use post-display task to autofocus the element.\n\t\t\t\tpostdisplay[`#autofocus:${el.id}`] = task => {\n\t\t\t\t\tdelete postdisplay[task]; // single-use task\n\t\t\t\t\tsetTimeout(() => el.focus(), Engine.minDomActionDelay);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t[DEPRECATED] <<click>>\n\t*/\n\tMacro.add('click', 'link'); // add <<click>> as an alias of <<link>>\n\n\n\t/*******************************************************************************************************************\n\t\tLinks Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<actions>>\n\t*/\n\tMacro.add('actions', {\n\t\thandler() {\n\t\t\tconst $list = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass(this.name)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\tfor (let i = 0; i < this.args.length; ++i) {\n\t\t\t\tlet passage;\n\t\t\t\tlet text;\n\t\t\t\tlet $image;\n\t\t\t\tlet setFn;\n\n\t\t\t\tif (typeof this.args[i] === 'object') {\n\t\t\t\t\tif (this.args[i].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[i].source);\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[i].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[i].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[i].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[i].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[i].text;\n\t\t\t\t\t\tpassage = this.args[i].link;\n\t\t\t\t\t\tsetFn   = this.args[i].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[i];\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t   State.variables.hasOwnProperty('#actions')\n\t\t\t\t\t&& State.variables['#actions'].hasOwnProperty(passage)\n\t\t\t\t\t&& State.variables['#actions'][passage]\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tjQuery(Wikifier.createInternalLink(\n\t\t\t\t\tjQuery(document.createElement('li')).appendTo($list),\n\t\t\t\t\tpassage,\n\t\t\t\t\tnull,\n\t\t\t\t\t((passage, fn) => () => {\n\t\t\t\t\t\tif (!State.variables.hasOwnProperty('#actions')) {\n\t\t\t\t\t\t\tState.variables['#actions'] = {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tState.variables['#actions'][passage] = true;\n\n\t\t\t\t\t\tif (typeof fn === 'function') {\n\t\t\t\t\t\t\tfn();\n\t\t\t\t\t\t}\n\t\t\t\t\t})(passage, setFn)\n\t\t\t\t))\n\t\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t\t.append($image || document.createTextNode(text));\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<back>> & <<return>>\n\t*/\n\tMacro.add(['back', 'return'], {\n\t\thandler() {\n\t\t\t/* legacy */\n\t\t\tif (this.args.length > 1) {\n\t\t\t\treturn this.error('too many arguments specified, check the documentation for details');\n\t\t\t}\n\t\t\t/* /legacy */\n\n\t\t\tlet momentIndex = -1;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('link')) {\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\tif (this.args[0].count === 1) {\n\t\t\t\t\t\t\t// Simple link syntax: `[[...]]`.\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Pretty link syntax: `[[...|...]]`.\n\t\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (this.args.length === 1) {\n\t\t\t\t\t// Argument was simply the link text.\n\t\t\t\t\ttext = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\t/*\n\t\t\t\t\tFind the index and title of the most recent moment whose title does not match\n\t\t\t\t\tthat of the active (present) moment's.\n\t\t\t\t*/\n\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\tif (State.history[i].title !== State.passage) {\n\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\tpassage = State.history[i].title;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we failed to find a passage and we're `<<return>>`, fallback to `State.expired`.\n\t\t\t\tif (passage == null && this.name === 'return') { // lazy equality for null\n\t\t\t\t\tfor (let i = State.expired.length - 1; i >= 0; --i) {\n\t\t\t\t\t\tif (State.expired[i] !== State.passage) {\n\t\t\t\t\t\t\tpassage = State.expired[i];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tif (this.name === 'back') {\n\t\t\t\t\t/*\n\t\t\t\t\t\tFind the index of the most recent moment whose title matches that of the\n\t\t\t\t\t\tspecified passage.\n\t\t\t\t\t*/\n\t\t\t\t\tfor (let i = State.length - 2; i >= 0; --i) {\n\t\t\t\t\t\tif (State.history[i].title === passage) {\n\t\t\t\t\t\t\tmomentIndex = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (momentIndex === -1) {\n\t\t\t\t\t\treturn this.error(`cannot find passage \"${passage}\" in the current story history`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (passage == null) { // lazy equality for null\n\t\t\t\treturn this.error('cannot find passage');\n\t\t\t}\n\n\t\t\t// if (this.name === \"back\" && momentIndex === -1) {\n\t\t\t// \t// no-op; we're already at the first passage in the current story history\n\t\t\t// \treturn;\n\t\t\t// }\n\n\t\t\tlet $el;\n\n\t\t\tif (this.name !== 'back' || momentIndex !== -1) {\n\t\t\t\t$el = jQuery(document.createElement('a'))\n\t\t\t\t\t.addClass('link-internal')\n\t\t\t\t\t.ariaClick(\n\t\t\t\t\t\t{ one : true },\n\t\t\t\t\t\tthis.name === 'return'\n\t\t\t\t\t\t\t? () => Engine.play(passage)\n\t\t\t\t\t\t\t: () => Engine.goTo(momentIndex)\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$el = jQuery(document.createElement('span'))\n\t\t\t\t\t.addClass('link-disabled');\n\t\t\t}\n\n\t\t\t$el\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text || L10n.get(`macro${this.name.toUpperFirst()}Text`)))\n\t\t\t\t.appendTo(this.output);\n\t\t}\n\t});\n\n\t/*\n\t\t<<choice>>\n\t*/\n\tMacro.add('choice', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tconst choiceId = State.passage;\n\t\t\tlet passage;\n\t\t\tlet text;\n\t\t\tlet $image;\n\t\t\tlet setFn;\n\n\t\t\tif (this.args.length === 1) {\n\t\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t\tif (this.args[0].isImage) {\n\t\t\t\t\t\t// Argument was in wiki image syntax.\n\t\t\t\t\t\t$image = jQuery(document.createElement('img'))\n\t\t\t\t\t\t\t.attr('src', this.args[0].source);\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('passage')) {\n\t\t\t\t\t\t\t$image.attr('data-passage', this.args[0].passage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('title')) {\n\t\t\t\t\t\t\t$image.attr('title', this.args[0].title);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (this.args[0].hasOwnProperty('align')) {\n\t\t\t\t\t\t\t$image.attr('align', this.args[0].align);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\ttext    = this.args[0].text;\n\t\t\t\t\t\tpassage = this.args[0].link;\n\t\t\t\t\t\tsetFn   = this.args[0].setFn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\ttext = passage = this.args[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// NOTE: The arguments here are backwards.\n\t\t\t\tpassage = this.args[0];\n\t\t\t\ttext    = this.args[1];\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t   State.variables.hasOwnProperty('#choice')\n\t\t\t\t&& State.variables['#choice'].hasOwnProperty(choiceId)\n\t\t\t\t&& State.variables['#choice'][choiceId]\n\t\t\t) {\n\t\t\t\tjQuery(document.createElement('span'))\n\t\t\t\t\t.addClass(`link-disabled macro-${this.name}`)\n\t\t\t\t\t.attr('tabindex', -1)\n\t\t\t\t\t.append($image || document.createTextNode(text))\n\t\t\t\t\t.appendTo(this.output);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tjQuery(Wikifier.createInternalLink(this.output, passage, null, () => {\n\t\t\t\tif (!State.variables.hasOwnProperty('#choice')) {\n\t\t\t\t\tState.variables['#choice'] = {};\n\t\t\t\t}\n\n\t\t\t\tState.variables['#choice'][choiceId] = true;\n\n\t\t\t\tif (typeof setFn === 'function') {\n\t\t\t\t\tsetFn();\n\t\t\t\t}\n\t\t\t}))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.append($image || document.createTextNode(text));\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tDOM Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<addclass>> & <<toggleclass>>\n\t*/\n\tMacro.add(['addclass', 'toggleclass'], {\n\t\thandler() {\n\t\t\tif (this.args.length < 2) {\n\t\t\t\tconst errors = [];\n\t\t\t\tif (this.args.length < 1) { errors.push('selector'); }\n\t\t\t\tif (this.args.length < 2) { errors.push('class names'); }\n\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tswitch (this.name) {\n\t\t\tcase 'addclass':\n\t\t\t\t$targets.addClass(this.args[1].trim());\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggleclass':\n\t\t\t\t$targets.toggleClass(this.args[1].trim());\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<removeclass>>\n\t*/\n\tMacro.add('removeclass', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.args.length > 1) {\n\t\t\t\t$targets.removeClass(this.args[1].trim());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$targets.removeClass();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<copy>>\n\t*/\n\tMacro.add('copy', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tjQuery(this.output).append($targets.html());\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<append>>, <<prepend>>, & <<replace>>\n\t*/\n\tMacro.add(['append', 'prepend', 'replace'], {\n\t\ttags  : null,\n\t\tt8nRe : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\tif (this.payload[0].contents !== '') {\n\t\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\t\tlet $insert;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$insert = jQuery(document.createElement('span'));\n\t\t\t\t\t$insert.addClass(`macro-${this.name}-insert macro-${this.name}-in`);\n\t\t\t\t\tsetTimeout(() => $insert.removeClass(`macro-${this.name}-in`), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$insert = jQuery(document.createDocumentFragment());\n\t\t\t\t}\n\n\t\t\t\t$insert.wiki(this.payload[0].contents);\n\n\t\t\t\tswitch (this.name) {\n\t\t\t\tcase 'replace':\n\t\t\t\t\t$targets.empty();\n\t\t\t\t\t/* falls through */\n\n\t\t\t\tcase 'append':\n\t\t\t\t\t$targets.append($insert);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'prepend':\n\t\t\t\t\t$targets.prepend($insert);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (this.name === 'replace') {\n\t\t\t\t$targets.empty();\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<remove>>\n\t*/\n\tMacro.add('remove', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no selector specified');\n\t\t\t}\n\n\t\t\tconst $targets = jQuery(this.args[0]);\n\n\t\t\tif ($targets.length === 0) {\n\t\t\t\treturn this.error(`no elements matched the selector \"${this.args[0]}\"`);\n\t\t\t}\n\n\t\t\t$targets.remove();\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\n\t/*******************************************************************************************************************\n\t\tAudio Macros.\n\t*******************************************************************************************************************/\n\tif (Has.audio) {\n\t\tconst errorOnePlaybackAction = (cur, prev) => `only one playback action allowed per invocation, \"${cur}\" cannot be combined with \"${prev}\"`;\n\n\t\t/*\n\t\t\t<<audio>>\n\t\t*/\n\t\tMacro.add('audio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track and/or group IDs'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tlet selected;\n\n\t\t\t\t// Process the track and/or group IDs.\n\t\t\t\ttry {\n\t\t\t\t\tselected = SimpleAudio.select(this.args[0]);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(1);\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet passage;\n\t\t\t\tlet time;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'time':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('time missing required seconds value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\ttime = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(time) || !Number.isFinite(time)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse time: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'goto':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('goto missing required passage title');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\n\t\t\t\t\t\tif (typeof raw === 'object') {\n\t\t\t\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\t\t\t\tpassage = raw.link;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// Argument was simply the passage name.\n\t\t\t\t\t\t\tpassage = raw;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!Story.has(passage)) {\n\t\t\t\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tselected.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (time != null) { // lazy equality for null\n\t\t\t\t\t\tselected.time(time);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tselected.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tselected.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (passage != null) { // lazy equality for null\n\t\t\t\t\t\tconst nsEnded = `ended.macros.macro-${this.name}_goto`;\n\t\t\t\t\t\tselected\n\t\t\t\t\t\t\t.off(nsEnded)\n\t\t\t\t\t\t\t.one(nsEnded, () => {\n\t\t\t\t\t\t\t\tselected.off(nsEnded);\n\t\t\t\t\t\t\t\tEngine.play(passage);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tselected.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tselected.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tselected.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tselected.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tselected.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tselected.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<cacheaudio track_id source_list>>\n\t\t*/\n\t\tMacro.add('cacheaudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\tconst errors = [];\n\t\t\t\t\tif (this.args.length < 1) { errors.push('track ID'); }\n\t\t\t\t\tif (this.args.length < 2) { errors.push('sources'); }\n\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t}\n\n\t\t\t\tconst id       = String(this.args[0]).trim();\n\t\t\t\tconst oldFmtRe = /^format:\\s*([\\w-]+)\\s*;\\s*/i;\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.tracks.add(id, this.args.slice(1).map(source => {\n\t\t\t\t\t\t/* legacy */\n\t\t\t\t\t\t// Transform an old format specifier into the new style.\n\t\t\t\t\t\tif (oldFmtRe.test(source)) {\n\t\t\t\t\t\t\t// If in Test Mode, return an error.\n\t\t\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\t\t\treturn this.error(`track ID \"${id}\": format specifier migration required, \"format:formatId;\" \\u2192 \"formatId|\"`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsource = source.replace(oldFmtRe, '$1|'); // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn source;\n\t\t\t\t\t\t/* /legacy */\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// If in Test Mode and no supported sources were specified, return an error.\n\t\t\t\tif (Config.debug && !SimpleAudio.tracks.get(id).hasSource()) {\n\t\t\t\t\treturn this.error(`track ID \"${id}\": no supported audio sources found`);\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createaudiogroup group_id>>\n\t\t\t\t<<track track_id>>\n\t\t\t\t…\n\t\t\t<</createaudiogroup>>\n\t\t*/\n\t\tMacro.add('createaudiogroup', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst groupId  = String(this.args[0]).trim();\n\t\t\t\tconst trackIds = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length < 1) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackIds.push(String(this.payload[i].args[0]).trim());\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.groups.add(groupId, trackIds);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createaudiogroup>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<createplaylist list_id>>\n\t\t\t\t<<track track_id action_list>>\n\t\t\t\t…\n\t\t\t<</createplaylist>>\n\t\t*/\n\t\tMacro.add('createplaylist', {\n\t\t\ttags : ['track'],\n\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tif (this.payload.length === 1) {\n\t\t\t\t\treturn this.error('no tracks defined via <<track>>');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'createplaylist') {\n\t\t\t\t\treturn this.error('a playlist has already been defined with <<setplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Initial debug view setup for `<<createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst listId    = String(this.args[0]).trim();\n\t\t\t\tconst trackObjs = [];\n\n\t\t\t\tfor (let i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\tif (this.payload[i].args.length === 0) {\n\t\t\t\t\t\treturn this.error('no track ID specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tconst trackObj = { id : String(this.payload[i].args[0]).trim() };\n\t\t\t\t\tconst args     = this.payload[i].args.slice(1);\n\n\t\t\t\t\t// Process arguments.\n\t\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\t\tlet raw;\n\t\t\t\t\t\tlet parsed;\n\n\t\t\t\t\t\tswitch (arg) {\n\t\t\t\t\t\tcase 'copy': // [DEPRECATED]\n\t\t\t\t\t\tcase 'own':\n\t\t\t\t\t\t\ttrackObj.own = true;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'rate':\n\t\t\t\t\t\t\t// if (args.length === 0) {\n\t\t\t\t\t\t\t// \treturn this.error('rate missing required speed value');\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// raw = args.shift();\n\t\t\t\t\t\t\t// parsed = Number.parseFloat(raw);\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// if (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t// \treturn this.error(`cannot parse rate: ${raw}`);\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// trackObj.rate = parsed;\n\t\t\t\t\t\t\tif (args.length > 0) {\n\t\t\t\t\t\t\t\targs.shift();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\t\tparsed = Number.parseFloat(raw);\n\n\t\t\t\t\t\t\tif (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttrackObj.volume = parsed;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ttrackObjs.push(trackObj);\n\n\t\t\t\t\t// Custom debug view setup for the current `<<track>>`.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t.createDebugView(this.payload[i].name, this.payload[i].source)\n\t\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add(listId, trackObjs);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'createplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom fake debug view setup for `<</createplaylist>>`.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis\n\t\t\t\t\t\t.createDebugView(`/${this.name}`, `<</${this.name}>>`)\n\t\t\t\t\t\t.modes({\n\t\t\t\t\t\t\tnonvoid : false,\n\t\t\t\t\t\t\thidden  : true\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<masteraudio action_list>>\n\t\t*/\n\t\tMacro.add('masteraudio', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t}\n\n\t\t\t\tconst args = this.args.slice(0);\n\t\t\t\tlet action;\n\t\t\t\tlet mute;\n\t\t\t\tlet muteOnHide;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'muteonhide':\n\t\t\t\t\tcase 'nomuteonhide':\n\t\t\t\t\t\tmuteOnHide = arg === 'muteonhide';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (muteOnHide != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.muteOnHidden(muteOnHide);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tSimpleAudio.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tSimpleAudio.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tSimpleAudio.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tSimpleAudio.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<playlist list_id action_list>>  ← <<createplaylist>> syntax\n\t\t\t<<playlist action_list>>          ← <<setplaylist>> syntax\n\t\t*/\n\t\tMacro.add('playlist', {\n\t\t\tfrom : null,\n\n\t\t\thandler() {\n\t\t\t\tconst from = this.self.from;\n\n\t\t\t\tif (from === null) {\n\t\t\t\t\treturn this.error('no playlists have been created');\n\t\t\t\t}\n\n\t\t\t\tlet list;\n\t\t\t\tlet args;\n\n\t\t\t\tif (from === 'createplaylist') {\n\t\t\t\t\tif (this.args.length < 2) {\n\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\tif (this.args.length < 1) { errors.push('list ID'); }\n\t\t\t\t\t\tif (this.args.length < 2) { errors.push('actions'); }\n\t\t\t\t\t\treturn this.error(`no ${errors.join(' or ')} specified`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get(id);\n\t\t\t\t\targs = this.args.slice(1);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\t\treturn this.error('no actions specified');\n\t\t\t\t\t}\n\n\t\t\t\t\tlist = SimpleAudio.lists.get('setplaylist');\n\t\t\t\t\targs = this.args.slice(0);\n\t\t\t\t}\n\n\t\t\t\tlet action;\n\t\t\t\tlet fadeOver = 5;\n\t\t\t\tlet fadeTo;\n\t\t\t\tlet loop;\n\t\t\t\tlet mute;\n\t\t\t\tlet shuffle;\n\t\t\t\tlet volume;\n\n\t\t\t\t// Process arguments.\n\t\t\t\twhile (args.length > 0) {\n\t\t\t\t\tconst arg = args.shift();\n\t\t\t\t\tlet raw;\n\n\t\t\t\t\tswitch (arg) {\n\t\t\t\t\tcase 'load':\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\tcase 'play':\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = arg;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadein':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 1;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeout':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\tfadeTo = 0;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('fadeto missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'fadeoverto':\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn this.error(errorOnePlaybackAction(arg, action));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (args.length < 2) {\n\t\t\t\t\t\t\tconst errors = [];\n\t\t\t\t\t\t\tif (args.length < 1) { errors.push('seconds'); }\n\t\t\t\t\t\t\tif (args.length < 2) { errors.push('level'); }\n\t\t\t\t\t\t\treturn this.error(`fadeoverto missing required ${errors.join(' and ')} value${errors.length > 1 ? 's' : ''}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\taction = 'fade';\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeOver = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeOver) || !Number.isFinite(fadeOver)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tfadeTo = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(fadeTo) || !Number.isFinite(fadeTo)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse fadeoverto: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'volume':\n\t\t\t\t\t\tif (args.length === 0) {\n\t\t\t\t\t\t\treturn this.error('volume missing required level value');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\traw = args.shift();\n\t\t\t\t\t\tvolume = Number.parseFloat(raw);\n\n\t\t\t\t\t\tif (Number.isNaN(volume) || !Number.isFinite(volume)) {\n\t\t\t\t\t\t\treturn this.error(`cannot parse volume: ${raw}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'mute':\n\t\t\t\t\tcase 'unmute':\n\t\t\t\t\t\tmute = arg === 'mute';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'loop':\n\t\t\t\t\tcase 'unloop':\n\t\t\t\t\t\tloop = arg === 'loop';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'shuffle':\n\t\t\t\t\tcase 'unshuffle':\n\t\t\t\t\t\tshuffle = arg === 'shuffle';\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn this.error(`unknown action: ${arg}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (volume != null) { // lazy equality for null\n\t\t\t\t\t\tlist.volume(volume);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mute != null) { // lazy equality for null\n\t\t\t\t\t\tlist.mute(mute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (loop != null) { // lazy equality for null\n\t\t\t\t\t\tlist.loop(loop);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (shuffle != null) { // lazy equality for null\n\t\t\t\t\t\tlist.shuffle(shuffle);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\tcase 'fade':\n\t\t\t\t\t\tlist.fade(fadeOver, fadeTo);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'load':\n\t\t\t\t\t\tlist.load();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'pause':\n\t\t\t\t\t\tlist.pause();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'play':\n\t\t\t\t\t\tlist.playWhenAllowed();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'skip':\n\t\t\t\t\t\tlist.skip();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\tlist.stop();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 'unload':\n\t\t\t\t\t\tlist.unload();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Custom debug view setup.\n\t\t\t\t\tif (Config.debug) {\n\t\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`error executing action: ${ex.message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeaudiogroup group_id>>\n\t\t*/\n\t\tMacro.add('removeaudiogroup', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no group ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.groups.has(id)) {\n\t\t\t\t\treturn this.error(`group \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.groups.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<removeplaylist list_id>>\n\t\t*/\n\t\tMacro.add('removeplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no list ID specified');\n\t\t\t\t}\n\n\t\t\t\tconst id = String(this.args[0]).trim();\n\n\t\t\t\tif (!SimpleAudio.lists.has(id)) {\n\t\t\t\t\treturn this.error(`playlist \"${id}\" does not exist`);\n\t\t\t\t}\n\n\t\t\t\tSimpleAudio.lists.delete(id);\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t<<waitforaudio>>\n\t\t*/\n\t\tMacro.add('waitforaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.loadWithScreen();\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<setplaylist track_id_list>>\n\t\t*/\n\t\tMacro.add('setplaylist', {\n\t\t\thandler() {\n\t\t\t\tif (this.args.length === 0) {\n\t\t\t\t\treturn this.error('no track ID(s) specified');\n\t\t\t\t}\n\n\t\t\t\tconst playlist = Macro.get('playlist');\n\n\t\t\t\tif (playlist.from !== null && playlist.from !== 'setplaylist') {\n\t\t\t\t\treturn this.error('playlists have already been defined with <<createplaylist>>');\n\t\t\t\t}\n\n\t\t\t\t// Create the new playlist.\n\t\t\t\ttry {\n\t\t\t\t\tSimpleAudio.lists.add('setplaylist', this.args.slice(0));\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(ex.message);\n\t\t\t\t}\n\n\t\t\t\t// Lock `<<playlist>>` into our syntax.\n\t\t\t\tif (playlist.from === null) {\n\t\t\t\t\tplaylist.from = 'setplaylist';\n\t\t\t\t}\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t/*\n\t\t\t[DEPRECATED] <<stopallaudio>>\n\t\t*/\n\t\tMacro.add('stopallaudio', {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\tSimpleAudio.select(':all').stop();\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\telse {\n\t\t/* The HTML5 <audio> API appears to be missing or disabled, set up no-op macros. */\n\t\tMacro.add([\n\t\t\t'audio',\n\t\t\t'cacheaudio',\n\t\t\t'createaudiogroup',\n\t\t\t'createplaylist',\n\t\t\t'masteraudio',\n\t\t\t'playlist',\n\t\t\t'removeaudiogroup',\n\t\t\t'removeplaylist',\n\t\t\t'waitforaudio',\n\n\t\t\t// Deprecated.\n\t\t\t'setplaylist',\n\t\t\t'stopallaudio'\n\t\t], {\n\t\t\tskipArgs : true,\n\n\t\t\thandler() {\n\t\t\t\t/* no-op */\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tMiscellaneous Macros.\n\t*******************************************************************************************************************/\n\t/*\n\t\t<<goto>>\n\t*/\n\tMacro.add('goto', {\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no passage specified');\n\t\t\t}\n\n\t\t\tlet passage;\n\n\t\t\tif (typeof this.args[0] === 'object') {\n\t\t\t\t// Argument was in wiki link syntax.\n\t\t\t\tpassage = this.args[0].link;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Argument was simply the passage name.\n\t\t\t\tpassage = this.args[0];\n\t\t\t}\n\n\t\t\tif (!Story.has(passage)) {\n\t\t\t\treturn this.error(`passage \"${passage}\" does not exist`);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tCall `Engine.play()` asynchronously.\n\n\t\t\t\tNOTE: This does not terminate the current Wikifier call chain,\n\t\t\t\tthough, ideally, it should.  Doing so would not be trivial, however,\n\t\t\t\tand there's also the question of whether that behavior would be\n\t\t\t\tunwanted by users, who are used to the current behavior from\n\t\t\t\tsimilar macros and constructs.\n\t\t\t*/\n\t\t\tsetTimeout(() => Engine.play(passage), Engine.minDomActionDelay);\n\t\t}\n\t});\n\n\t/*\n\t\t<<repeat>> & <<stop>>\n\t*/\n\tMacro.add('repeat', {\n\t\tisAsync : true,\n\t\ttags    : null,\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified');\n\t\t\t}\n\n\t\t\tlet delay;\n\n\t\t\ttry {\n\t\t\t\tdelay = Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0]));\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(ex.message);\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerInterval(this.createShadowWrapper(() => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, this.payload[0].contents);\n\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-repeat-insert macro-repeat-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-repeat-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), delay);\n\t\t},\n\n\t\tregisterInterval(callback, delay) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId = null;\n\n\t\t\t// Set up the interval.\n\t\t\ttimerId = setInterval(() => {\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\tclearInterval(timerId);\n\t\t\t\t\ttimers.delete(timerId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet timerIdCache;\n\t\t\t\t/*\n\t\t\t\t\tThere's no catch clause because this try/finally is here simply to ensure that\n\t\t\t\t\tproper cleanup is done in the event that an exception is thrown during the\n\t\t\t\t\t`Wikifier` call.\n\t\t\t\t*/\n\t\t\t\ttry {\n\t\t\t\t\tTempState.break = null;\n\n\t\t\t\t\t// Set up the `repeatTimerId` value, caching the existing value, if necessary.\n\t\t\t\t\tif (TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\t\t\ttimerIdCache = TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.repeatTimerId = timerId;\n\n\t\t\t\t\t// Execute the callback.\n\t\t\t\t\tcallback.call(this);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\t// Teardown the `repeatTimerId` property, restoring the cached value, if necessary.\n\t\t\t\t\tif (typeof timerIdCache !== 'undefined') {\n\t\t\t\t\t\tTempState.repeatTimerId = timerIdCache;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdelete TempState.repeatTimerId;\n\t\t\t\t\t}\n\n\t\t\t\t\tTempState.break = null;\n\t\t\t\t}\n\t\t\t}, delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#repeat-timers-cleanup')) {\n\t\t\t\tprehistory['#repeat-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearInterval(timerId));\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\tMacro.add('stop', {\n\t\tskipArgs : true,\n\n\t\thandler() {\n\t\t\tif (!TempState.hasOwnProperty('repeatTimerId')) {\n\t\t\t\treturn this.error('must only be used in conjunction with its parent macro <<repeat>>');\n\t\t\t}\n\n\t\t\tconst timers  = Macro.get('repeat').timers;\n\t\t\tconst timerId = TempState.repeatTimerId;\n\t\t\tclearInterval(timerId);\n\t\t\ttimers.delete(timerId);\n\t\t\tTempState.break = 2;\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<timed>> & <<next>>\n\t*/\n\tMacro.add('timed', {\n\t\tisAsync : true,\n\t\ttags    : ['next'],\n\t\ttimers  : new Set(),\n\t\tt8nRe   : /^(?:transition|t8n)$/,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no time value specified in <<timed>>');\n\t\t\t}\n\n\t\t\tconst items = [];\n\n\t\t\ttry {\n\t\t\t\titems.push({\n\t\t\t\t\tname    : this.name,\n\t\t\t\t\tsource  : this.source,\n\t\t\t\t\tdelay   : Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.args[0])),\n\t\t\t\t\tcontent : this.payload[0].contents\n\t\t\t\t});\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`${ex.message} in <<timed>>`);\n\t\t\t}\n\n\t\t\tif (this.payload.length > 1) {\n\t\t\t\tlet i;\n\n\t\t\t\ttry {\n\t\t\t\t\tlet len;\n\n\t\t\t\t\tfor (i = 1, len = this.payload.length; i < len; ++i) {\n\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\tname   : this.payload[i].name,\n\t\t\t\t\t\t\tsource : this.payload[i].source,\n\t\t\t\t\t\t\tdelay  : this.payload[i].args.length === 0\n\t\t\t\t\t\t\t\t? items[items.length - 1].delay\n\t\t\t\t\t\t\t\t: Math.max(Engine.minDomActionDelay, Util.fromCssTime(this.payload[i].args[0])),\n\t\t\t\t\t\t\tcontent : this.payload[i].contents\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\treturn this.error(`${ex.message} in <<next>> (#${i})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Custom debug view setup.\n\t\t\tif (Config.debug) {\n\t\t\t\tthis.debugView.modes({ block : true });\n\t\t\t}\n\n\t\t\tconst transition = this.args.length > 1 && this.self.t8nRe.test(this.args[1]);\n\t\t\tconst $wrapper   = jQuery(document.createElement('span'))\n\t\t\t\t.addClass(`macro-${this.name}`)\n\t\t\t\t.appendTo(this.output);\n\n\t\t\t// Register the timer.\n\t\t\tthis.self.registerTimeout(this.createShadowWrapper(item => {\n\t\t\t\tconst frag = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(frag, item.content);\n\n\t\t\t\t// Output.\n\t\t\t\tlet $output = $wrapper;\n\n\t\t\t\t// Custom debug view setup for `<<next>>`.\n\t\t\t\tif (Config.debug && item.name === 'next') {\n\t\t\t\t\t$output = jQuery(new DebugView( // eslint-disable-line no-param-reassign\n\t\t\t\t\t\t$output[0],\n\t\t\t\t\t\t'macro',\n\t\t\t\t\t\titem.name,\n\t\t\t\t\t\titem.source\n\t\t\t\t\t).output);\n\t\t\t\t}\n\n\t\t\t\tif (transition) {\n\t\t\t\t\t$output = jQuery(document.createElement('span'))\n\t\t\t\t\t\t.addClass('macro-timed-insert macro-timed-in')\n\t\t\t\t\t\t.appendTo($output);\n\t\t\t\t}\n\n\t\t\t\t$output.append(frag);\n\n\t\t\t\tif (transition) {\n\t\t\t\t\tsetTimeout(() => $output.removeClass('macro-timed-in'), Engine.minDomActionDelay);\n\t\t\t\t}\n\t\t\t}), items);\n\t\t},\n\n\t\tregisterTimeout(callback, items) {\n\t\t\tif (typeof callback !== 'function') {\n\t\t\t\tthrow new TypeError('callback parameter must be a function');\n\t\t\t}\n\n\t\t\t// Cache info about the current turn.\n\t\t\tconst passage = State.passage;\n\t\t\tconst turn    = State.turns;\n\n\t\t\t// Timer info.\n\t\t\tconst timers = this.timers;\n\t\t\tlet timerId  = null;\n\t\t\tlet nextItem = items.shift();\n\n\t\t\tconst worker = function () {\n\t\t\t\t// Bookkeeping.\n\t\t\t\ttimers.delete(timerId);\n\n\t\t\t\t// Terminate if we've navigated away.\n\t\t\t\tif (State.passage !== passage || State.turns !== turn) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the current item and set up the next worker, if any.\n\t\t\t\tconst curItem = nextItem;\n\n\t\t\t\tif ((nextItem = items.shift()) != null) { // lazy equality for null\n\t\t\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\t\t\ttimers.add(timerId);\n\t\t\t\t}\n\n\t\t\t\t// Execute the callback.\n\t\t\t\tcallback.call(this, curItem);\n\t\t\t};\n\n\t\t\t// Setup the timeout.\n\t\t\ttimerId = setTimeout(worker, nextItem.delay);\n\t\t\ttimers.add(timerId);\n\n\t\t\t// Set up a single-use `prehistory` task to remove pending timers.\n\t\t\tif (!prehistory.hasOwnProperty('#timed-timers-cleanup')) {\n\t\t\t\tprehistory['#timed-timers-cleanup'] = task => {\n\t\t\t\t\tdelete prehistory[task]; // single-use task\n\t\t\t\t\ttimers.forEach(timerId => clearTimeout(timerId)); // eslint-disable-line no-shadow\n\t\t\t\t\ttimers.clear();\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n\n\t/*\n\t\t<<widget>>\n\t*/\n\tMacro.add('widget', {\n\t\ttags : null,\n\n\t\thandler() {\n\t\t\tif (this.args.length === 0) {\n\t\t\t\treturn this.error('no widget name specified');\n\t\t\t}\n\n\t\t\tconst widgetName = this.args[0];\n\n\t\t\tif (Macro.has(widgetName)) {\n\t\t\t\tif (!Macro.get(widgetName).isWidget) {\n\t\t\t\t\treturn this.error(`cannot clobber existing macro \"${widgetName}\"`);\n\t\t\t\t}\n\n\t\t\t\t// Delete the existing widget.\n\t\t\t\tMacro.delete(widgetName);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMacro.add(widgetName, {\n\t\t\t\t\tisWidget : true,\n\t\t\t\t\thandler  : (function (contents) {\n\t\t\t\t\t\treturn function () {\n\t\t\t\t\t\t\tlet argsCache;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Cache the existing value of the `$args` variable, if necessary.\n\t\t\t\t\t\t\t\tif (State.variables.hasOwnProperty('args')) {\n\t\t\t\t\t\t\t\t\targsCache = State.variables.args;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set up the widget `$args` variable and add a shadow.\n\t\t\t\t\t\t\t\tState.variables.args = [...this.args];\n\t\t\t\t\t\t\t\tState.variables.args.raw = this.args.raw;\n\t\t\t\t\t\t\t\tState.variables.args.full = this.args.full;\n\t\t\t\t\t\t\t\tthis.addShadow('$args');\n\n\t\t\t\t\t\t\t\t// Set up the error trapping variables.\n\t\t\t\t\t\t\t\tconst resFrag = document.createDocumentFragment();\n\t\t\t\t\t\t\t\tconst errList = [];\n\n\t\t\t\t\t\t\t\t// Wikify the widget contents.\n\t\t\t\t\t\t\t\tnew Wikifier(resFrag, contents);\n\n\t\t\t\t\t\t\t\t// Carry over the output, unless there were errors.\n\t\t\t\t\t\t\t\tArray.from(resFrag.querySelectorAll('.error')).forEach(errEl => {\n\t\t\t\t\t\t\t\t\terrList.push(errEl.textContent);\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tif (errList.length === 0) {\n\t\t\t\t\t\t\t\t\tthis.output.appendChild(resFrag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\treturn this.error(`error${errList.length > 1 ? 's' : ''} within widget contents (${errList.join('; ')})`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ex) {\n\t\t\t\t\t\t\t\treturn this.error(`cannot execute widget: ${ex.message}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t\t// Revert the `$args` variable shadowing.\n\t\t\t\t\t\t\t\tif (typeof argsCache !== 'undefined') {\n\t\t\t\t\t\t\t\t\tState.variables.args = argsCache;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tdelete State.variables.args;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t})(this.payload[0].contents)\n\t\t\t\t});\n\n\t\t\t\t// Custom debug view setup.\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tthis.debugView.modes({ hidden : true });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\treturn this.error(`cannot create widget macro \"${widgetName}\": ${ex.message}`);\n\t\t\t}\n\t\t}\n\t});\n})();\n\n/***********************************************************************************************************************\n\n\tdialog.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Has, L10n, safeActiveElement */\n\nvar Dialog = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Dialog element caches.\n\tlet _$overlay       = null;\n\tlet _$dialog        = null;\n\tlet _$dialogTitle   = null;\n\tlet _$dialogBody    = null;\n\n\t// The last active/focused non-dialog element.\n\tlet _lastActive     = null;\n\n\t// The width of the browser's scrollbars.\n\tlet _scrollbarWidth = 0;\n\n\t// Dialog mutation resize handler.\n\tlet _dialogObserver = null;\n\n\n\t/*******************************************************************************\n\t\tDialog Functions.\n\t*******************************************************************************/\n\n\t/*\n\t\t[DEPRECATED] Adds a click hander to the target element(s) which opens the dialog modal.\n\t*/\n\tfunction dialogAddClickHandler(targets, options, startFn, doneFn, closeFn) {\n\t\treturn jQuery(targets).ariaClick(ev => {\n\t\t\tev.preventDefault();\n\n\t\t\t// Call the start function.\n\t\t\tif (typeof startFn === 'function') {\n\t\t\t\tstartFn(ev);\n\t\t\t}\n\n\t\t\t// Open the dialog.\n\t\t\tdialogOpen(options, closeFn);\n\n\t\t\t// Call the done function.\n\t\t\tif (typeof doneFn === 'function') {\n\t\t\t\tdoneFn(ev);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction dialogBodyAppend(...args) {\n\t\t_$dialogBody.append(...args);\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogBody() {\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogClose(ev) {\n\t\t// Trigger a `:dialogclosing` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogclosing');\n\n\t\t// Largely reverse the actions taken in `dialogOpen()`.\n\t\tjQuery(document)\n\t\t\t.off('.dialog-close');\n\t\tif (_dialogObserver) {\n\t\t\t_dialogObserver.disconnect();\n\t\t\t_dialogObserver = null;\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.off('.dialog-resize');\n\t\t}\n\t\tjQuery(window)\n\t\t\t.off('.dialog-resize');\n\t\t_$dialog\n\t\t\t.removeClass('open')\n\t\t\t.css({ left : '', right : '', top : '', bottom : '' });\n\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex=-2]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.attr('tabindex', 0);\n\t\tjQuery('body>[tabindex=-3]')\n\t\t\t.removeAttr('aria-hidden')\n\t\t\t.removeAttr('tabindex');\n\n\t\t_$overlay\n\t\t\t.removeClass('open');\n\t\tjQuery(document.documentElement)\n\t\t\t.removeAttr('data-dialog');\n\n\t\t// Clear the dialog's content.\n\t\t_$dialogTitle\n\t\t\t.empty();\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\t// Attempt to restore focus to whichever element had it prior to opening the dialog.\n\t\tif (_lastActive !== null) {\n\t\t\tjQuery(_lastActive).focus();\n\t\t\t_lastActive = null;\n\t\t}\n\n\t\t// Call the given \"on close\" callback function, if any.\n\t\tif (ev && ev.data && typeof ev.data.closeFn === 'function') {\n\t\t\tev.data.closeFn(ev);\n\t\t}\n\n\t\t// Trigger a `:dialogclosed` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogclose');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogclosed');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogInit() {\n\t\tif (DEBUG) { console.log('[Dialog/dialogInit()]'); }\n\n\t\tif (document.getElementById('ui-dialog')) {\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t\tCalculate and cache the width of scrollbars.\n\t\t*/\n\t\t_scrollbarWidth = (() => {\n\t\t\tlet scrollbarWidth;\n\n\t\t\ttry {\n\t\t\t\tconst inner = document.createElement('p');\n\t\t\t\tconst outer = document.createElement('div');\n\n\t\t\t\tinner.style.width      = '100%';\n\t\t\t\tinner.style.height     = '200px';\n\t\t\t\touter.style.position   = 'absolute';\n\t\t\t\touter.style.left       = '0px';\n\t\t\t\touter.style.top        = '0px';\n\t\t\t\touter.style.width      = '100px';\n\t\t\t\touter.style.height     = '100px';\n\t\t\t\touter.style.visibility = 'hidden';\n\t\t\t\touter.style.overflow   = 'hidden';\n\n\t\t\t\touter.appendChild(inner);\n\t\t\t\tdocument.body.appendChild(outer);\n\n\t\t\t\tconst w1 = inner.offsetWidth;\n\t\t\t\t/*\n\t\t\t\t\tThe `overflow: scroll` style property value does not work consistently\n\t\t\t\t\twith scrollbars which are styled with `::-webkit-scrollbar`, so we use\n\t\t\t\t\t`overflow: auto` with dimensions guaranteed to force a scrollbar.\n\t\t\t\t*/\n\t\t\t\touter.style.overflow = 'auto';\n\t\t\t\tlet w2 = inner.offsetWidth;\n\n\t\t\t\tif (w1 === w2) {\n\t\t\t\t\tw2 = outer.clientWidth;\n\t\t\t\t}\n\n\t\t\t\tdocument.body.removeChild(outer);\n\n\t\t\t\tscrollbarWidth = w1 - w2;\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\n\t\t\treturn scrollbarWidth || 17; // 17px is a reasonable failover\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate the dialog elements.\n\t\t*/\n\t\tconst $elems = jQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"ui-overlay\" class=\"ui-close\"></div>'\n\t\t\t\t+ '<div id=\"ui-dialog\" tabindex=\"0\" role=\"dialog\" aria-labelledby=\"ui-dialog-title\">'\n\t\t\t\t+     '<div id=\"ui-dialog-titlebar\">'\n\t\t\t\t+         '<h1 id=\"ui-dialog-title\"></h1>'\n\t\t\t\t+         `<button id=\"ui-dialog-close\" class=\"ui-close\" tabindex=\"0\" aria-label=\"${L10n.get('close')}\">\\uE804</button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div id=\"ui-dialog-body\"></div>'\n\t\t\t\t+ '</div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t/*\n\t\t\tCache the dialog elements, since they're going to be used often.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$overlay     = jQuery($elems.find('#ui-overlay').get(0));\n\t\t_$dialog      = jQuery($elems.find('#ui-dialog').get(0));\n\t\t_$dialogTitle = jQuery($elems.find('#ui-dialog-title').get(0));\n\t\t_$dialogBody  = jQuery($elems.find('#ui-dialog-body').get(0));\n\n\t\t/*\n\t\t\tInsert the dialog elements into the page before the main script.\n\t\t*/\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t}\n\n\tfunction dialogIsOpen(classNames) {\n\t\treturn _$dialog.hasClass('open')\n\t\t\t&& (classNames ? classNames.splitOrEmpty(/\\s+/).every(cn => _$dialogBody.hasClass(cn)) : true);\n\t}\n\n\tfunction dialogOpen(options, closeFn) {\n\t\t// Trigger a `:dialogopening` event on the dialog body.\n\t\t_$dialogBody.trigger(':dialogopening');\n\n\t\t// Grab the options we care about.\n\t\tconst { top } = jQuery.extend({ top : 50 }, options);\n\n\t\t// Record the last active/focused non-dialog element.\n\t\tif (!dialogIsOpen()) {\n\t\t\t_lastActive = safeActiveElement();\n\t\t}\n\n\t\t// Add the `data-dialog` attribute to <html> (mostly used to style <body>).\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-dialog', 'open');\n\n\t\t// Display the overlay.\n\t\t_$overlay\n\t\t\t.addClass('open');\n\n\t\t/*\n\t\t\tAdd the imagesLoaded handler to the dialog body, if necessary.\n\n\t\t\tNOTE: We use `querySelector()` here as jQuery has no simple way to\n\t\t\tcheck if, and only if, at least one element of the specified type\n\t\t\texists.  The best that jQuery offers is analogous to `querySelectorAll()`,\n\t\t\twhich enumerates all elements of the specified type.\n\t\t*/\n\t\tif (_$dialogBody[0].querySelector('img') !== null) {\n\t\t\t_$dialogBody\n\t\t\t\t.imagesLoaded()\n\t\t\t\t.always(() => _resizeHandler({ data : { top } }));\n\t\t}\n\n\t\t// Add `aria-hidden=true` to all direct non-dialog-children of <body> to\n\t\t// hide the underlying page from screen readers while the dialog is open.\n\t\tjQuery('body>:not(script,#store-area,tw-storydata,#ui-bar,#ui-overlay,#ui-dialog)')\n\t\t\t.attr('tabindex', -3)\n\t\t\t.attr('aria-hidden', true);\n\t\tjQuery('#ui-bar,#story')\n\t\t\t.find('[tabindex]:not([tabindex^=-])')\n\t\t\t.attr('tabindex', -2)\n\t\t\t.attr('aria-hidden', true);\n\n\t\t// Display the dialog.\n\t\t_$dialog\n\t\t\t.css(_calcPosition(top))\n\t\t\t.addClass('open')\n\t\t\t.focus();\n\n\t\t// Add the UI resize handler.\n\t\tjQuery(window)\n\t\t\t.on('resize.dialog-resize', null, { top }, jQuery.throttle(40, _resizeHandler));\n\n\t\t// Add the dialog mutation resize handler.\n\t\tif (Has.mutationObserver) {\n\t\t\t_dialogObserver = new MutationObserver(mutations => {\n\t\t\t\tfor (let i = 0; i < mutations.length; ++i) {\n\t\t\t\t\tif (mutations[i].type === 'childList') {\n\t\t\t\t\t\t_resizeHandler({ data : { top } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t_dialogObserver.observe(_$dialogBody[0], {\n\t\t\t\tchildList : true,\n\t\t\t\tsubtree   : true\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t_$dialogBody\n\t\t\t\t.on(\n\t\t\t\t\t'DOMNodeInserted.dialog-resize DOMNodeRemoved.dialog-resize',\n\t\t\t\t\tnull,\n\t\t\t\t\t{ top },\n\t\t\t\t\tjQuery.throttle(40, _resizeHandler)\n\t\t\t\t);\n\t\t}\n\n\t\t// Set up the delegated UI close handler.\n\t\tjQuery(document)\n\t\t\t.one('click.dialog-close', '.ui-close', { closeFn }, ev => {\n\t\t\t\t// NOTE: Do not allow this event handler to return the `Dialog` static object,\n\t\t\t\t// as doing so causes Edge (ca. 18) to throw a \"Number expected\" exception due\n\t\t\t\t// to `Dialog` not having a prototype.\n\t\t\t\tdialogClose(ev);\n\t\t\t\t/* implicit `return undefined;` */\n\t\t\t})\n\t\t\t.one('keypress.dialog-close', '.ui-close', function (ev) {\n\t\t\t\t// 13 is Enter/Return, 32 is Space.\n\t\t\t\tif (ev.which === 13 || ev.which === 32) {\n\t\t\t\t\tjQuery(this).trigger('click');\n\t\t\t\t}\n\t\t\t});\n\n\t\t// Trigger a `:dialogopened` event on the dialog body.\n\t\t/* legacy */\n\t\t_$dialogBody.trigger(':dialogopen');\n\t\t/* /legacy */\n\t\t_$dialogBody.trigger(':dialogopened');\n\n\t\treturn Dialog;\n\t}\n\n\tfunction dialogResize(data) {\n\t\treturn _resizeHandler(typeof data === 'object' ? { data } : undefined);\n\t}\n\n\tfunction dialogSetup(title, classNames) {\n\t\t_$dialogBody\n\t\t\t.empty()\n\t\t\t.removeClass();\n\n\t\tif (classNames != null) { // lazy equality for null\n\t\t\t_$dialogBody.addClass(classNames);\n\t\t}\n\n\t\t_$dialogTitle\n\t\t\t.empty()\n\t\t\t.append((title != null ? String(title) : '') || '\\u00A0'); // lazy equality for null\n\n\t\t// TODO: In v3 this should return `Dialog` for chaining.\n\t\treturn _$dialogBody.get(0);\n\t}\n\n\tfunction dialogBodyWiki(...args) {\n\t\t_$dialogBody.wiki(...args);\n\t\treturn Dialog;\n\t}\n\n\n\t/*******************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************/\n\n\tfunction _calcPosition(topPos) {\n\t\tconst top       = topPos != null ? topPos : 50; // lazy equality for null\n\t\tconst $parent   = jQuery(window);\n\t\tconst dialogPos = { left : '', right : '', top : '', bottom : '' };\n\n\t\t// Unset the dialog's positional properties before checking its dimensions.\n\t\t_$dialog.css(dialogPos);\n\n\t\tlet horzSpace = $parent.width() - _$dialog.outerWidth(true) - 1;   // -1 to address a Firefox issue\n\t\tlet vertSpace = $parent.height() - _$dialog.outerHeight(true) - 1; // -1 to address a Firefox issue\n\n\t\tif (horzSpace <= 32 + _scrollbarWidth) {\n\t\t\tvertSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (vertSpace <= 32 + _scrollbarWidth) {\n\t\t\thorzSpace -= _scrollbarWidth;\n\t\t}\n\n\t\tif (horzSpace <= 32) {\n\t\t\tdialogPos.left = dialogPos.right = 16;\n\t\t}\n\t\telse {\n\t\t\tdialogPos.left = dialogPos.right = horzSpace / 2 >> 0;\n\t\t}\n\n\t\tif (vertSpace <= 32) {\n\t\t\tdialogPos.top = dialogPos.bottom = 16;\n\t\t}\n\t\telse {\n\t\t\tif (vertSpace / 2 > top) {\n\t\t\t\tdialogPos.top = top;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdialogPos.top = dialogPos.bottom = vertSpace / 2 >> 0;\n\t\t\t}\n\t\t}\n\n\t\tObject.keys(dialogPos).forEach(key => {\n\t\t\tif (dialogPos[key] !== '') {\n\t\t\t\tdialogPos[key] += 'px';\n\t\t\t}\n\t\t});\n\n\t\treturn dialogPos;\n\t}\n\n\tfunction _resizeHandler(ev) {\n\t\tconst top = ev && ev.data && typeof ev.data.top !== 'undefined' ? ev.data.top : 50;\n\n\t\tif (_$dialog.css('display') === 'block') {\n\t\t\t// Stow the dialog.\n\t\t\t_$dialog.css({ display : 'none' });\n\n\t\t\t// Restore the dialog with its new positional properties.\n\t\t\t_$dialog.css(jQuery.extend({ display : '' }, _calcPosition(top)));\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tappend : { value : dialogBodyAppend },\n\t\tbody   : { value : dialogBody },\n\t\tclose  : { value : dialogClose },\n\t\tinit   : { value : dialogInit },\n\t\tisOpen : { value : dialogIsOpen },\n\t\topen   : { value : dialogOpen },\n\t\tresize : { value : dialogResize },\n\t\tsetup  : { value : dialogSetup },\n\t\twiki   : { value : dialogBodyWiki },\n\n\t\t// Legacy Functions.\n\t\taddClickHandler : { value : dialogAddClickHandler }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tengine.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Config, DebugView, Dialog, Has, LoadScreen, Save, State, Story, StyleWrapper, UI, UIBar, Util,\n\t       Wikifier, postdisplay, postrender, predisplay, prehistory, prerender, setDisplayTitle\n*/\n\nvar Engine = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Engine state types object (pseudo-enumeration).\n\tconst States = Util.toEnum({\n\t\tIdle      : 'idle',\n\t\tPlaying   : 'playing',\n\t\tRendering : 'rendering'\n\t});\n\n\t// Minimum delay for DOM actions (in milliseconds).\n\tconst minDomActionDelay = 40;\n\n\t// Current state of the engine (default: `Engine.States.Idle`).\n\tlet _state = States.Idle;\n\n\t// Last time `enginePlay()` was called (in milliseconds).\n\tlet _lastPlay = null;\n\n\t// Cache of the debug view for the StoryInit special passage.\n\tlet _storyInitDebugView = null;\n\n\t// Cache of the outline patching <style> element (`StyleWrapper`-wrapped).\n\tlet _outlinePatch = null;\n\n\t// List of objects describing `StoryInterface` elements to update via passages during navigation.\n\tlet _updating = null;\n\n\n\t/*******************************************************************************************************************\n\t\tEngine Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize the core story elements and perform some bookkeeping.\n\t*/\n\tfunction engineInit() {\n\t\tif (DEBUG) { console.log('[Engine/engineInit()]'); }\n\n\t\t/*\n\t\t\tRemove #init-no-js & #init-lacking from #init-screen.\n\t\t*/\n\t\tjQuery('#init-no-js,#init-lacking').remove();\n\n\t\t/*\n\t\t\tGenerate the core story elements and insert them into the page before the store area.\n\t\t*/\n\t\t(() => {\n\t\t\tconst $elems = jQuery(document.createDocumentFragment());\n\t\t\tconst markup = Story.has('StoryInterface') && Story.get('StoryInterface').text.trim();\n\n\t\t\tif (markup) {\n\t\t\t\t// Remove the UI bar, its styles, and events.\n\t\t\t\tUIBar.destroy();\n\n\t\t\t\t// Remove the core display area styles.\n\t\t\t\tjQuery(document.head).find('#style-core-display').remove();\n\n\t\t\t\t$elems.append(markup);\n\n\t\t\t\tif ($elems.find('#passages').length === 0) {\n\t\t\t\t\tthrow new Error('no element with ID \"passages\" found within \"StoryInterface\" special passage');\n\t\t\t\t}\n\n\t\t\t\tconst updating = [];\n\n\t\t\t\t$elems.find('[data-passage]').each((i, el) => {\n\t\t\t\t\tif (el.id === 'passages') {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} id=\"passages\"> must not contain a \"data-passage\" content attribute`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst passage = el.getAttribute('data-passage').trim();\n\n\t\t\t\t\tif (el.firstElementChild !== null) {\n\t\t\t\t\t\tthrow new Error(`\"StoryInterface\" element <${el.nodeName.toLowerCase()} data-passage=\"${passage}\"> contains child elements`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Story.has(passage)) {\n\t\t\t\t\t\tupdating.push({\n\t\t\t\t\t\t\tpassage,\n\t\t\t\t\t\t\telement : el\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (updating.length > 0) {\n\t\t\t\t\t_updating = updating;\n\t\t\t\t}\n\n\t\t\t\tConfig.ui.updateStoryElements = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$elems.append('<div id=\"story\" role=\"main\"><div id=\"passages\"></div></div>');\n\t\t\t}\n\n\t\t\t// Insert the core UI elements into the page before the main script.\n\t\t\t$elems.insertBefore('body>script#script-sugarcube');\n\t\t})();\n\n\t\t/*\n\t\t\tGenerate and cache the ARIA outlines <style> element (`StyleWrapper`-wrapped)\n\t\t\tand set up the handler to manipulate the outlines.\n\n\t\t\tIDEA: http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/\n\t\t*/\n\t\t_outlinePatch = new StyleWrapper((\n\t\t\t() => jQuery(document.createElement('style'))\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-aria-outlines',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t})\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.get(0) // return the <style> element itself\n\t\t)());\n\t\t_hideOutlines(); // initially hide outlines\n\t\tlet _lastOutlineEvent;\n\t\tjQuery(document).on(\n\t\t\t'mousedown.aria-outlines keydown.aria-outlines',\n\t\t\tev => {\n\t\t\t\tif (ev.type !== _lastOutlineEvent) {\n\t\t\t\t\t_lastOutlineEvent = ev.type;\n\n\t\t\t\t\tif (ev.type === 'keydown') {\n\t\t\t\t\t\t_showOutlines();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t_hideOutlines();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\n\t/*\n\t\tStarts the story.\n\t*/\n\tfunction engineStart() {\n\t\tif (DEBUG) { console.log('[Engine/engineStart()]'); }\n\n\t\t/*\n\t\t\tExecute the StoryInit special passage.\n\t\t*/\n\t\tif (Story.has('StoryInit')) {\n\t\t\ttry {\n\t\t\t\tconst debugBuffer = Wikifier.wikifyEval(Story.get('StoryInit').text);\n\n\t\t\t\tif (Config.debug) {\n\t\t\t\t\tconst debugView = new DebugView(\n\t\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t\t'special',\n\t\t\t\t\t\t'StoryInit',\n\t\t\t\t\t\t'StoryInit'\n\t\t\t\t\t);\n\t\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\t\tdebugView.append(debugBuffer);\n\t\t\t\t\t_storyInitDebugView = debugView.output;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('StoryInit', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Sanity checks.\n\t\tif (Config.passages.start == null) { // lazy equality for null\n\t\t\tthrow new Error('starting passage not selected');\n\t\t}\n\t\tif (!Story.has(Config.passages.start)) {\n\t\t\tthrow new Error(`starting passage (\"${Config.passages.start}\") not found`);\n\t\t}\n\n\t\t// Focus the document element initially.\n\t\tjQuery(document.documentElement).focus();\n\n\t\t/*\n\t\t\tAttempt to restore an active session.  Failing that, attempt to autoload the autosave,\n\t\t\tif requested.  Failing that, display the starting passage.\n\t\t*/\n\t\tif (State.restore()) {\n\t\t\tengineShow();\n\t\t}\n\t\telse {\n\t\t\tlet loadStart = true;\n\n\t\t\tswitch (typeof Config.saves.autoload) {\n\t\t\tcase 'boolean':\n\t\t\t\tif (Config.saves.autoload && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'string':\n\t\t\t\tif (Config.saves.autoload === 'prompt' && Save.autosave.ok() && Save.autosave.has()) {\n\t\t\t\t\tloadStart = false;\n\t\t\t\t\tUI.buildAutoload();\n\t\t\t\t\tDialog.open();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'function':\n\t\t\t\tif (Save.autosave.ok() && Save.autosave.has() && !!Config.saves.autoload()) {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\t\tloadStart = !Save.autosave.load();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (loadStart) {\n\t\t\t\tif (DEBUG) { console.log(`\\tstarting passage: \"${Config.passages.start}\"`); }\n\n\t\t\t\tenginePlay(Config.passages.start);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t\tRestarts the story.\n\t*/\n\tfunction engineRestart() {\n\t\tif (DEBUG) { console.log('[Engine/engineRestart()]'); }\n\n\t\t/*\n\t\t\tShow the loading screen to hide any unsightly rendering shenanigans during the\n\t\t\tpage reload.\n\t\t*/\n\t\tLoadScreen.show();\n\n\t\t/*\n\t\t\tScroll the window to the top.\n\n\t\t\tThis is required by most browsers for the starting passage or it will remain at\n\t\t\twhatever its current scroll position is after the page reload.  We do it generally,\n\t\t\trather than only for the currently set starting passage, since the starting passage\n\t\t\tmay be dynamically manipulated.\n\t\t*/\n\t\twindow.scroll(0, 0);\n\n\t\t/*\n\t\t\tDelete the active session.\n\t\t*/\n\t\tState.reset();\n\n\t\t/*\n\t\t\tTrigger an ':enginerestart' event.\n\t\t*/\n\t\tjQuery.event.trigger(':enginerestart');\n\n\t\t/*\n\t\t\tReload the page.\n\t\t*/\n\t\twindow.location.reload();\n\t}\n\n\t/*\n\t\tReturns the current state of the engine.\n\t*/\n\tfunction engineState() {\n\t\treturn _state;\n\t}\n\n\t/*\n\t\tReturns whether the engine is idle.\n\t*/\n\tfunction engineIsIdle() {\n\t\treturn _state === States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is playing.\n\t*/\n\tfunction engineIsPlaying() {\n\t\treturn _state !== States.Idle;\n\t}\n\n\t/*\n\t\tReturns whether the engine is rendering.\n\t*/\n\tfunction engineIsRendering() {\n\t\treturn _state === States.Rendering;\n\t}\n\n\t/*\n\t\tReturns a timestamp representing the last time `Engine.play()` was called.\n\t*/\n\tfunction engineLastPlay() {\n\t\treturn _lastPlay;\n\t}\n\n\t/*\n\t\tActivate the moment at the given index within the state history and show it.\n\t*/\n\tfunction engineGoTo(idx) {\n\t\tconst succeded = State.goTo(idx);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tActivate the moment at the given offset from the active moment within the state history\n\t\tand show it.\n\t*/\n\tfunction engineGo(offset) {\n\t\tconst succeded = State.go(offset);\n\n\t\tif (succeded) {\n\t\t\tengineShow();\n\t\t}\n\n\t\treturn succeded;\n\t}\n\n\t/*\n\t\tGo to the moment which directly precedes the active moment and show it.\n\t*/\n\tfunction engineBackward() {\n\t\treturn engineGo(-1);\n\t}\n\n\t/*\n\t\tGo to the moment which directly follows the active moment and show it.\n\t*/\n\tfunction engineForward() {\n\t\treturn engineGo(1);\n\t}\n\n\t/*\n\t\tRenders and displays the active (present) moment's associated passage without adding\n\t\ta new moment to the history.\n\t*/\n\tfunction engineShow() {\n\t\treturn enginePlay(State.passage, true);\n\t}\n\n\t/*\n\t\tRenders and displays the passage referenced by the given title, optionally without\n\t\tadding a new moment to the history.\n\t*/\n\tfunction enginePlay(title, noHistory) {\n\t\tif (DEBUG) { console.log(`[Engine/enginePlay(title: \"${title}\", noHistory: ${noHistory})]`); }\n\n\t\tlet passageTitle = title;\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Reset the temporary state and variables objects.\n\t\tTempState = {}; // eslint-disable-line no-undef\n\t\tState.clearTemporary();\n\n\t\t// Debug view setup.\n\t\tlet passageReadyOutput;\n\t\tlet passageDoneOutput;\n\n\t\t// Execute the navigation override callback.\n\t\tif (typeof Config.navigation.override === 'function') {\n\t\t\ttry {\n\t\t\t\tconst overrideTitle = Config.navigation.override(passageTitle);\n\n\t\t\t\tif (overrideTitle) {\n\t\t\t\t\tpassageTitle = overrideTitle;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op */ }\n\t\t}\n\n\t\t// Retrieve the passage by the given title.\n\t\t//\n\t\t// NOTE: The values of the `title` parameter and `passageTitle` variable\n\t\t// may be empty, strings, or numbers (though using a number as reference\n\t\t// to a numeric title should be discouraged), so after loading the passage,\n\t\t// always refer to `passage.title` and never to the others.\n\t\tconst passage = Story.get(passageTitle);\n\n\t\t// Execute the pre-history events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype : ':passageinit',\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prehistory).forEach(task => {\n\t\t\tif (typeof prehistory[task] === 'function') {\n\t\t\t\tprehistory[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Create a new entry in the history.\n\t\tif (!noHistory) {\n\t\t\tState.create(passage.title);\n\t\t}\n\n\t\t// Clear the document body's classes.\n\t\tif (document.body.className) {\n\t\t\tdocument.body.className = '';\n\t\t}\n\n\t\t// Update the last play time.\n\t\t//\n\t\t// NOTE: This is mostly for event, task, and special passage code,\n\t\t// though the likelihood of it being needed this early is low.  This\n\t\t// will be updated again later at the end.\n\t\t_lastPlay = Util.now();\n\n\t\t// Execute pre-display tasks and the `PassageReady` special passage.\n\t\tObject.keys(predisplay).forEach(task => {\n\t\t\tif (typeof predisplay[task] === 'function') {\n\t\t\t\tpredisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\tif (Story.has('PassageReady')) {\n\t\t\ttry {\n\t\t\t\tpassageReadyOutput = Wikifier.wikifyEval(Story.get('PassageReady').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageReady', ex.message);\n\t\t\t}\n\t\t}\n\n\t\t// Update the engine state.\n\t\t_state = States.Rendering;\n\n\t\t// Get the passage's tags as a string, or `null` if there aren't any.\n\t\tconst dataTags = passage.tags.length > 0 ? passage.tags.join(' ') : null;\n\n\t\t// Create and set up the incoming passage element.\n\t\tconst passageEl = document.createElement('div');\n\t\tjQuery(passageEl)\n\t\t\t.attr({\n\t\t\t\tid             : passage.domId,\n\t\t\t\t'data-passage' : passage.title,\n\t\t\t\t'data-tags'    : dataTags\n\t\t\t})\n\t\t\t.addClass(`passage ${passage.className}`);\n\n\t\t// Add the passage's classes and tags to the document body.\n\t\tjQuery(document.body)\n\t\t\t.attr('data-tags', dataTags)\n\t\t\t.addClass(passage.className);\n\n\t\t// Add the passage's tags to the document element.\n\t\tjQuery(document.documentElement)\n\t\t\t.attr('data-tags', dataTags);\n\n\t\t// Execute pre-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagestart',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(prerender).forEach(task => {\n\t\t\tif (typeof prerender[task] === 'function') {\n\t\t\t\tprerender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Render the `PassageHeader` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageHeader')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageHeader').processText());\n\t\t}\n\n\t\t// Render the passage into its element.\n\t\tpassageEl.appendChild(passage.render());\n\n\t\t// Render the `PassageFooter` passage, if it exists, into the passage element.\n\t\tif (Story.has('PassageFooter')) {\n\t\t\tnew Wikifier(passageEl, Story.get('PassageFooter').processText());\n\t\t}\n\n\t\t// Execute post-render events and tasks.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagerender',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postrender).forEach(task => {\n\t\t\tif (typeof postrender[task] === 'function') {\n\t\t\t\tpostrender[task].call(passage, passageEl, task);\n\t\t\t}\n\t\t});\n\n\t\t// Cache the passage container.\n\t\tconst containerEl = document.getElementById('passages');\n\n\t\t// Empty the passage container.\n\t\tif (containerEl.hasChildNodes()) {\n\t\t\tif (\n\t\t\t\t   typeof Config.passages.transitionOut === 'number'\n\t\t\t\t|| typeof Config.passages.transitionOut === 'string'\n\t\t\t\t&& Config.passages.transitionOut !== ''\n\t\t\t\t&& Has.transitionEndEvent\n\t\t\t) {\n\t\t\t\t[...containerEl.childNodes].forEach(outgoing => {\n\t\t\t\t\tconst $outgoing = jQuery(outgoing);\n\n\t\t\t\t\tif (outgoing.nodeType === Node.ELEMENT_NODE && $outgoing.hasClass('passage')) {\n\t\t\t\t\t\tif ($outgoing.hasClass('passage-out')) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$outgoing\n\t\t\t\t\t\t\t.attr('id', `out-${$outgoing.attr('id')}`)\n\t\t\t\t\t\t\t.addClass('passage-out');\n\n\t\t\t\t\t\tif (typeof Config.passages.transitionOut === 'string') {\n\t\t\t\t\t\t\t$outgoing.on(Has.transitionEndEvent, ev => {\n\t\t\t\t\t\t\t\tif (ev.originalEvent.propertyName === Config.passages.transitionOut) {\n\t\t\t\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t\t() => $outgoing.remove(),\n\t\t\t\t\t\t\t\tMath.max(minDomActionDelay, Config.passages.transitionOut)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$outgoing.remove();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery(containerEl).empty();\n\t\t\t}\n\t\t}\n\n\t\t// Append the passage element to the passage container and set up its transition.\n\t\tjQuery(passageEl)\n\t\t\t.addClass('passage-in')\n\t\t\t.appendTo(containerEl);\n\t\tsetTimeout(() => jQuery(passageEl).removeClass('passage-in'), minDomActionDelay);\n\n\t\t// Update the story display title, if necessary.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\t// NOTE: We don't have an `else` here because that case will be handled later (below).\n\t\t\tif (_updating !== null || !Config.ui.updateStoryElements) {\n\t\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t\t}\n\t\t}\n\t\telse if (Config.passages.displayTitles && passage.title !== Config.passages.start) {\n\t\t\tdocument.title = `${passage.title} | ${Story.title}`;\n\t\t}\n\n\t\t// Scroll the window to the top.\n\t\twindow.scroll(0, 0);\n\n\t\t// Update the engine state.\n\t\t_state = States.Playing;\n\n\t\t// Execute post-display events, tasks, and the `PassageDone` special passage.\n\t\tif (Story.has('PassageDone')) {\n\t\t\ttry {\n\t\t\t\tpassageDoneOutput = Wikifier.wikifyEval(Story.get('PassageDone').text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error('PassageDone', ex.message);\n\t\t\t}\n\t\t}\n\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passagedisplay',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\t\tObject.keys(postdisplay).forEach(task => {\n\t\t\tif (typeof postdisplay[task] === 'function') {\n\t\t\t\tpostdisplay[task].call(passage, task);\n\t\t\t}\n\t\t});\n\n\t\t// Update the other interface elements, if necessary.\n\t\tif (_updating !== null) {\n\t\t\t_updating.forEach(pair => {\n\t\t\t\tjQuery(pair.element).empty();\n\t\t\t\tnew Wikifier(pair.element, Story.get(pair.passage).processText().trim());\n\t\t\t});\n\t\t}\n\t\telse if (Config.ui.updateStoryElements) {\n\t\t\tUIBar.update();\n\t\t}\n\n\t\t// Add the completed debug views for `StoryInit`, `PassageReady`, and `PassageDone`\n\t\t// to the incoming passage element.\n\t\tif (Config.debug) {\n\t\t\tlet debugView;\n\n\t\t\t// Prepend the `PassageReady` debug view.\n\t\t\tif (passageReadyOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageReady',\n\t\t\t\t\t'PassageReady'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageReadyOutput);\n\t\t\t\tjQuery(passageEl).prepend(debugView.output);\n\t\t\t}\n\n\t\t\t// Append the `PassageDone` debug view.\n\t\t\tif (passageDoneOutput != null) { // lazy equality for null\n\t\t\t\tdebugView = new DebugView(\n\t\t\t\t\tdocument.createDocumentFragment(),\n\t\t\t\t\t'special',\n\t\t\t\t\t'PassageDone',\n\t\t\t\t\t'PassageDone'\n\t\t\t\t);\n\t\t\t\tdebugView.modes({ hidden : true });\n\t\t\t\tdebugView.append(passageDoneOutput);\n\t\t\t\tjQuery(passageEl).append(debugView.output);\n\t\t\t}\n\n\t\t\t// Prepend the cached `StoryInit` debug view, if we're showing the first moment/turn.\n\t\t\tif (State.turns === 1 && _storyInitDebugView != null) { // lazy equality for null\n\t\t\t\tjQuery(passageEl).prepend(_storyInitDebugView);\n\t\t\t}\n\t\t}\n\n\t\t// Last second post-processing for accessibility and other things.\n\t\tjQuery('#story')\n\t\t\t// Add `link-external` to all `href` bearing `<a>` elements which don't have it.\n\t\t\t.find('a[href]:not(.link-external)')\n\t\t\t.addClass('link-external')\n\t\t\t.end()\n\t\t\t// Add `tabindex=0` to all interactive elements which don't have it.\n\t\t\t.find('a,link,button,input,select,textarea')\n\t\t\t.not('[tabindex]')\n\t\t\t.attr('tabindex', 0);\n\n\t\t// Handle autosaves.\n\t\tswitch (typeof Config.saves.autosave) {\n\t\tcase 'boolean':\n\t\t\tif (Config.saves.autosave) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'object':\n\t\t\tif (passage.tags.some(tag => Config.saves.autosave.includes(tag))) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'function':\n\t\t\tif (Config.saves.autosave()) {\n\t\t\t\tSave.autosave.save();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// Execute post-play events.\n\t\tjQuery.event.trigger({\n\t\t\ttype    : ':passageend',\n\t\t\tcontent : passageEl,\n\t\t\tpassage\n\t\t});\n\n\t\t// Reset the engine state.\n\t\t_state = States.Idle;\n\n\t\t// Update the last play time.\n\t\t_lastPlay = Util.now();\n\n\t\treturn passageEl;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tLegacy Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\t[DEPRECATED] Play the given passage, optionally without altering the history.\n\t*/\n\tfunction engineDisplay(title, link, option) {\n\t\tif (DEBUG) { console.log('[Engine/engineDisplay()]'); }\n\n\t\tlet noHistory = false;\n\n\t\t// Process the option parameter.\n\t\tswitch (option) {\n\t\tcase undefined:\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'replace':\n\t\tcase 'back':\n\t\t\tnoHistory = true;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`Engine.display option parameter called with obsolete value \"${option}\"; please notify the developer`);\n\t\t}\n\n\t\tenginePlay(title, noHistory);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _hideOutlines() {\n\t\t_outlinePatch.set('*:focus{outline:none;}');\n\t}\n\n\tfunction _showOutlines() {\n\t\t_outlinePatch.clear();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tConstants.\n\t\t*/\n\t\tStates            : { value : States },\n\t\tminDomActionDelay : { value : minDomActionDelay },\n\n\t\t/*\n\t\t\tCore Functions.\n\t\t*/\n\t\tinit        : { value : engineInit },\n\t\tstart       : { value : engineStart },\n\t\trestart     : { value : engineRestart },\n\t\tstate       : { get : engineState },\n\t\tisIdle      : { value : engineIsIdle },\n\t\tisPlaying   : { value : engineIsPlaying },\n\t\tisRendering : { value : engineIsRendering },\n\t\tlastPlay    : { get : engineLastPlay },\n\t\tgoTo        : { value : engineGoTo },\n\t\tgo          : { value : engineGo },\n\t\tbackward    : { value : engineBackward },\n\t\tforward     : { value : engineForward },\n\t\tshow        : { value : engineShow },\n\t\tplay        : { value : enginePlay },\n\n\t\t/*\n\t\t\tLegacy Functions.\n\t\t*/\n\t\tdisplay : { value : engineDisplay }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tpassage.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, L10n, Util, Wikifier */\n\nvar Passage = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tlet _tagsToSkip;\n\tlet _twine1Unescape;\n\n\t/*\n\t\tTags which should not be transformed into classes:\n\t\t\tdebug      → special tag\n\t\t\tnobr       → special tag\n\t\t\tpassage    → the default class\n\t\t\tscript     → special tag (only in Twine 1)\n\t\t\tstylesheet → special tag (only in Twine 1)\n\t\t\ttwine.*    → special tag\n\t\t\twidget     → special tag\n\t*/\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|script|stylesheet|widget|twine\\..*)$/i;\n\t}\n\t// For Twine 2\n\telse {\n\t\t_tagsToSkip = /^(?:debug|nobr|passage|widget|twine\\..*)$/i;\n\t}\n\n\t// For Twine 1\n\tif (TWINE1) {\n\t\t/*\n\t\t\tReturns a decoded version of the passed Twine 1 passage store encoded string.\n\t\t*/\n\t\tconst _twine1EscapesRe    = /(?:\\\\n|\\\\t|\\\\s|\\\\|\\r)/g;\n\t\tconst _hasTwine1EscapesRe = new RegExp(_twine1EscapesRe.source); // to drop the global flag\n\t\tconst _twine1EscapesMap   = Object.freeze({\n\t\t\t'\\\\n' : '\\n',\n\t\t\t'\\\\t' : '\\t',\n\t\t\t'\\\\s' : '\\\\',\n\t\t\t'\\\\'  : '\\\\',\n\t\t\t'\\r'  : ''\n\t\t});\n\n\t\t_twine1Unescape = function (str) {\n\t\t\tif (str == null) { // lazy equality for null\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst val = String(str);\n\t\t\treturn val && _hasTwine1EscapesRe.test(val)\n\t\t\t\t? val.replace(_twine1EscapesRe, esc => _twine1EscapesMap[esc])\n\t\t\t\t: val;\n\t\t};\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Class.\n\t*******************************************************************************************************************/\n\tclass Passage {\n\t\tconstructor(title, el) {\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage title/ID.\n\t\t\t\ttitle : {\n\t\t\t\t\tvalue : Util.unescape(title)\n\t\t\t\t},\n\n\t\t\t\t// Passage data element (within the story data element; i.e. T1: '[tiddler]', T2: 'tw-passagedata').\n\t\t\t\telement : {\n\t\t\t\t\tvalue : el || null\n\t\t\t\t},\n\n\t\t\t\t// Passage tags array (sorted and unique).\n\t\t\t\ttags : {\n\t\t\t\t\tvalue : Object.freeze(el && el.hasAttribute('tags')\n\t\t\t\t\t\t? el.getAttribute('tags')\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.splitOrEmpty(/\\s+/)\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.filter((tag, i, aref) => i === 0 || aref[i - 1] !== tag)\n\t\t\t\t\t\t: [])\n\t\t\t\t},\n\n\t\t\t\t// Passage excerpt.  Used by the `description()` method.\n\t\t\t\t_excerpt : {\n\t\t\t\t\twritable : true,\n\t\t\t\t\tvalue    : null\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Properties dependant upon the above set.\n\t\t\tObject.defineProperties(this, {\n\t\t\t\t// Passage DOM-compatible ID.\n\t\t\t\tdomId : {\n\t\t\t\t\tvalue : `passage-${Util.slugify(this.title)}`\n\t\t\t\t},\n\n\t\t\t\t// Passage classes array (sorted and unique).\n\t\t\t\tclasses : {\n\t\t\t\t\tvalue : Object.freeze(this.tags.length === 0 ? [] : (() =>\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tReturn the sorted list of unique classes.\n\n\t\t\t\t\t\t\tNOTE: The `this.tags` array is already sorted and unique,\n\t\t\t\t\t\t\tso we only need to filter and map here.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tthis.tags\n\t\t\t\t\t\t\t.filter(tag => !_tagsToSkip.test(tag))\n\t\t\t\t\t\t\t.map(tag => Util.slugify(tag))\n\t\t\t\t\t)())\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Getters.\n\t\tget className() {\n\t\t\treturn this.classes.join(' ');\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get source`.\n\t\tget text() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\tconst passage = Util.escapeMarkup(this.title);\n\t\t\t\tconst mesg    = `${L10n.get('errorTitle')}: ${L10n.get('errorNonexistentPassage', { passage })}`;\n\t\t\t\treturn `<div class=\"error-view\"><span class=\"error\">${mesg}</span></div>`;\n\t\t\t}\n\n\t\t\t// For Twine 1\n\t\t\tif (TWINE1) {\n\t\t\t\treturn _twine1Unescape(this.element.textContent);\n\t\t\t}\n\t\t\t// For Twine 2\n\t\t\telse { // eslint-disable-line no-else-return\n\t\t\t\treturn this.element.textContent.replace(/\\r/g, '');\n\t\t\t}\n\t\t}\n\n\t\tdescription() {\n\t\t\tconst descriptions = Config.passages.descriptions;\n\n\t\t\tif (descriptions != null) { // lazy equality for null\n\t\t\t\tswitch (typeof descriptions) {\n\t\t\t\tcase 'boolean':\n\t\t\t\t\tif (descriptions) {\n\t\t\t\t\t\treturn this.title;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'object':\n\t\t\t\t\tif (descriptions instanceof Map && descriptions.has(this.title)) {\n\t\t\t\t\t\treturn descriptions.get(this.title);\n\t\t\t\t\t}\n\t\t\t\t\telse if (descriptions.hasOwnProperty(this.title)) {\n\t\t\t\t\t\treturn descriptions[this.title];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'function':\n\t\t\t\t\t{\n\t\t\t\t\t\tconst result = descriptions.call(this);\n\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new TypeError('Config.passages.descriptions must be a boolean, object, or function');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Initialize the excerpt cache from the raw passage text, if necessary.\n\t\t\tif (this._excerpt === null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromText(this.text);\n\t\t\t}\n\n\t\t\treturn this._excerpt;\n\t\t}\n\n\t\t// TODO: (v3) This should be → `get text`.\n\t\tprocessText() {\n\t\t\tif (this.element == null) { // lazy equality for null\n\t\t\t\treturn this.text;\n\t\t\t}\n\n\t\t\t// Handle image passage transclusion.\n\t\t\tif (this.tags.includes('Twine.image')) {\n\t\t\t\treturn `[img[${this.text}]]`;\n\t\t\t}\n\n\t\t\tlet processed = this.text;\n\n\t\t\t// Handle `Config.passages.onProcess`.\n\t\t\tif (Config.passages.onProcess) {\n\t\t\t\tprocessed = Config.passages.onProcess.call(null, {\n\t\t\t\t\ttitle : this.title,\n\t\t\t\t\ttags  : this.tags,\n\t\t\t\t\ttext  : processed\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Handle `Config.passages.nobr` and the `nobr` tag.\n\t\t\tif (Config.passages.nobr || this.tags.includes('nobr')) {\n\t\t\t\t// Remove all leading & trailing newlines and compact all internal sequences\n\t\t\t\t// of newlines into single spaces.\n\t\t\t\tprocessed = processed.replace(/^\\n+|\\n+$/g, '').replace(/\\n+/g, ' ');\n\t\t\t}\n\n\t\t\treturn processed;\n\t\t}\n\n\t\trender(options) {\n\t\t\t// Wikify the passage into a document fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, this.processText(), options);\n\n\t\t\t// Update the excerpt cache to reflect the rendered text, if we need it for the passage description\n\t\t\tif (Config.passages.descriptions == null) {\n\t\t\t\tthis._excerpt = Passage.getExcerptFromNode(frag);\n\t\t\t}\n\n\t\t\treturn frag;\n\t\t}\n\n\t\tstatic getExcerptFromNode(node, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromNode(node=…, count=${count})]`, node); }\n\n\t\t\tif (!node.hasChildNodes()) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t// WARNING: es5-shim's `<String>.trim()` can cause \"too much recursion\" errors\n\t\t\t// here on very large strings (e.g., ≥40 KiB), at least in Firefox, for unknown\n\t\t\t// reasons.\n\t\t\t//\n\t\t\t// To fix the issue, we're removed `\\u180E` from es5-shim's whitespace pattern\n\t\t\t// to prevent it from erroneously shimming `<String>.trim()` in the first place.\n\t\t\tlet excerpt = node.textContent.trim();\n\n\t\t\tif (excerpt !== '') {\n\t\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\t\texcerpt = excerpt\n\t\t\t\t\t// Compact whitespace.\n\t\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t\t.match(excerptRe);\n\t\t\t}\n\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\n\t\tstatic getExcerptFromText(text, count) {\n\t\t\tif (DEBUG) { console.log(`[Passage.getExcerptFromText(text=…, count=${count})]`, text); }\n\n\t\t\tif (text === '') {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst excerptRe = new RegExp(`(\\\\S+(?:\\\\s+\\\\S+){0,${count > 0 ? count - 1 : 7}})`);\n\t\t\tconst excerpt   = text\n\t\t\t\t// Strip macro tags (replace with a space).\n\t\t\t\t.replace(/<<.*?>>/g, ' ')\n\t\t\t\t// Strip html tags (replace with a space).\n\t\t\t\t.replace(/<.*?>/g, ' ')\n\t\t\t\t// The above might have left problematic whitespace, so trim.\n\t\t\t\t.trim()\n\t\t\t\t// Strip table markup.\n\t\t\t\t.replace(/^\\s*\\|.*\\|.*?$/gm, '')\n\t\t\t\t// Strip image markup.\n\t\t\t\t.replace(/\\[[<>]?img\\[[^\\]]*\\]\\]/g, '')\n\t\t\t\t// Clean link markup (remove all but the link text).\n\t\t\t\t.replace(/\\[\\[([^|\\]]*?)(?:(?:\\||->|<-)[^\\]]*)?\\]\\]/g, '$1')\n\t\t\t\t// Clean heading markup.\n\t\t\t\t.replace(/^\\s*!+(.*?)$/gm, '$1')\n\t\t\t\t// Clean bold/italic/underline/highlight styles.\n\t\t\t\t.replace(/'{2}|\\/{2}|_{2}|@{2}/g, '')\n\t\t\t\t// A final trim.\n\t\t\t\t.trim()\n\t\t\t\t// Compact whitespace.\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t// Attempt to match the excerpt regexp.\n\t\t\t\t.match(excerptRe);\n\t\t\treturn excerpt ? `${excerpt[1]}\\u2026` : '\\u2026'; // horizontal ellipsis\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Passage;\n})();\n\n/***********************************************************************************************************************\n\n\tsave.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Dialog, Engine, L10n, State, Story, UI, Util, storage */\n\nvar Save = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Save operation type pseudo-enumeration.\n\tconst Type = Util.toEnum({\n\t\tAutosave  : 'autosave',\n\t\tDisk      : 'disk',\n\t\tSerialize : 'serialize',\n\t\tSlot      : 'slot'\n\t});\n\n\t// The upper bound of the saves slots.\n\tlet _slotsUBound = -1;\n\n\n\t/*******************************************************************************************************************\n\t\tSaves Functions.\n\t*******************************************************************************************************************/\n\tfunction savesInit() {\n\t\tif (DEBUG) { console.log('[Save/savesInit()]'); }\n\n\t\t// Disable save slots and the autosave when Web Storage is unavailable.\n\t\tif (storage.name === 'cookie') {\n\t\t\tsavesObjClear();\n\t\t\tConfig.saves.autoload = undefined;\n\t\t\tConfig.saves.autosave = undefined;\n\t\t\tConfig.saves.slots = 0;\n\t\t\treturn false;\n\t\t}\n\n\n\t\t// convert the single big saves object into the new smaller objects\n\t\tconst saves = storage.get('saves');\n\t\tif (saves !== null) {\n\t\t\tstorage.delete('saves'); // Make sure we have enough space to store the saves in the new format\n\t\t\tconst container = Dialog.setup('Backup');\n\t\t\tconst message = document.createElement('span');\n\t\t\tmessage.className = 'red';\n\t\t\tmessage.append('Due to changes to the saves system your existing saves were converted. Backup all saves' +\n\t\t\t\t' that are important to you as they may have been lost during conversion. Once you close this dialog' +\n\t\t\t\t' it will be IMPOSSIBLE to get your old saves back.');\n\t\t\tcontainer.append(message);\n\t\t\tconst index = indexGet();\n\t\t\t// we store the index every time we store a save, so in case we exceed local storage only the\n\t\t\t// last save is lost\n\t\t\tif (saves.autosave !== null) {\n\t\t\t\tcontainer.append(savesLink('autosave', saves.autosave));\n\t\t\t\tindex.autosave = { title : saves.autosave.title, date : saves.autosave.date };\n\t\t\t\ttry {\n\t\t\t\t\tstorage.set('autosave', saves.autosave);\n\t\t\t\t\tindexSave(index);\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tstorage.delete('autosave');\n\t\t\t\t\tindex.autosave = null;\n\t\t\t\t\tindexSave(index);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (let i = 0; i < saves.slots.length; i++) {\n\t\t\t\tif (saves.slots[i] !== null) {\n\t\t\t\t\tcontainer.append(savesLink(`slot${i}`, saves.slots[i]));\n\t\t\t\t\tindex.slots[i] = { title : saves.slots[i].title, date : saves.slots[i].date };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstorage.set(`slot${i}`, saves.slots[i]);\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tstorage.delete(`slot${i}`);\n\t\t\t\t\t\tindex.slots[i] = null;\n\t\t\t\t\t\tindexSave(index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tDialog.open();\n\t\t}\n\n\t\tfunction savesLink(name, save) {\n\t\t\tconst div = document.createElement('div');\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.append(`Backup ${name}`);\n\t\t\ta.onclick = () => {\n\t\t\t\texportToDisk(`backup-${name}`, {}, save);\n\t\t\t};\n\t\t\tdiv.append(a);\n\t\t\treturn div;\n\t\t}\n\n\t\t/* All SC2 compatibility converters are disabled, too much work */\n\n\t\t/* legacy */\n\t\t// Convert an ancient saves array into a new saves object.\n\t\t/* if (Array.isArray(saves)) {\n\t\t\tsaves = {\n\t\t\t\tautosave : null,\n\t\t\t\tslots    : saves\n\t\t\t};\n\t\t\tupdated = true;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Handle the author changing the number of save slots.\n\t\t/*\n\t\tif (Config.saves.slots !== saves.slots.length) {\n\t\t\tif (Config.saves.slots < saves.slots.length) {\n\t\t\t\t// Attempt to decrease the number of slots; this will only compact\n\t\t\t\t// the slots array, by removing empty slots, no saves will be deleted.\n\t\t\t\tsaves.slots.reverse();\n\n\t\t\t\tsaves.slots = saves.slots.filter(function (val) {\n\t\t\t\t\tif (val === null && this.count > 0) {\n\t\t\t\t\t\t--this.count;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}, { count : saves.slots.length - Config.saves.slots });\n\n\t\t\t\tsaves.slots.reverse();\n\t\t\t}\n\t\t\telse if (Config.saves.slots > saves.slots.length) {\n\t\t\t\t// Attempt to increase the number of slots.\n\t\t\t\t_appendSlots(saves.slots, Config.saves.slots - saves.slots.length);\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t */\n\n\t\t/* legacy */\n\t\t// Update saves with old/obsolete properties.\n\t\t/* if (_savesObjUpdate(saves.autosave)) {\n\t\t\tupdated = true;\n\t\t}\n\n\t\tfor (let i = 0; i < saves.slots.length; ++i) {\n\t\t\tif (_savesObjUpdate(saves.slots[i])) {\n\t\t\t\tupdated = true;\n\t\t\t}\n\t\t}\n\n\t\t// Remove save stores which are empty.\n\t\tif (_savesObjIsEmpty(saves)) {\n\t\t\tstorage.delete('saves');\n\t\t\tupdated = false;\n\t\t}\n\t\t/* /legacy */\n\n\t\t// If the saves object was updated, then update the store.\n\t\t/* if (updated) {\n\t\t\t_savesObjSave(saves);\n\t\t}\n\t\t*/\n\n\t\t_slotsUBound = indexGet().slots.length - 1;\n\n\t\treturn true;\n\t}\n\n\tfunction indexCreate() {\n\t\treturn {\n\t\t\tautosave : null,\n\t\t\tslots    : _appendSlots([], Config.saves.slots)\n\t\t};\n\t}\n\n\tfunction indexGet() {\n\t\tconst index = storage.get('index');\n\t\treturn index === null ? indexCreate() : index;\n\t}\n\n\tfunction indexSave(index) {\n\t\treturn storage.set('index', index);\n\t}\n\n\tfunction savesObjClear() {\n\t\tstorage.delete('autosave');\n\t\tconst index = indexGet();\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tstorage.delete(`slot${i}`);\n\t\t}\n\t\tstorage.delete('index');\n\t\treturn true;\n\t}\n\n\tfunction savesOk() {\n\t\treturn autosaveOk() || slotsOk();\n\t}\n\n\t/*******************************************************************************************************************\n\t\tAutosave Functions.\n\t*******************************************************************************************************************/\n\tfunction autosaveOk() {\n\t\treturn storage.name !== 'cookie' && typeof Config.saves.autosave !== 'undefined';\n\t}\n\n\tfunction autosaveHas() {\n\t\treturn indexGet().autosave !== null;\n\t}\n\n\tfunction autosaveGet() {\n\t\treturn storage.get('autosave');\n\t}\n\n\tfunction autosaveLoad() {\n\t\tconst autosave = autosaveGet();\n\n\t\tif (autosave === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn _unmarshal(autosave);\n\t}\n\n\tfunction autosaveSave(title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle : title || Story.get(State.passage).description(),\n\t\t\tdate  : Date.now()\n\t\t};\n\t\tconst index = indexGet();\n\t\tindex.autosave = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set('autosave', _marshal(supplemental, { type : Type.Autosave }), false);\n\t\t} catch (ex) {\n\t\t\tindex.autosave = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction autosaveDelete() {\n\t\tconst index = indexGet();\n\t\tindex.autosave = null;\n\t\tindexSave(index);\n\t\treturn storage.delete('autosave');\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSlots Functions.\n\t*******************************************************************************************************************/\n\tfunction slotsOk() {\n\t\treturn storage.name !== 'cookie' && _slotsUBound !== -1;\n\t}\n\n\tfunction slotsLength() {\n\t\treturn _slotsUBound + 1;\n\t}\n\n\tfunction slotsCount() {\n\t\tif (!slotsOk()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tconst index = indexGet();\n\t\tlet count = 0;\n\n\t\tfor (let i = 0; i < index.slots.length; i++) {\n\t\t\tif (index.slots[i] !== null) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\treturn count;\n\t}\n\n\tfunction slotsIsEmpty() {\n\t\treturn slotsCount() === 0;\n\t}\n\n\tfunction slotsHas(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length || index.slots[slot] === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction slotsGet(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn storage.get(`slot${slot}`);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tfunction slotsLoad(slot) {\n\t\tif (slotsHas(slot)) {\n\t\t\treturn _unmarshal(storage.get(`slot${slot}`));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction slotsSave(slot, title, metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst supplemental = {\n\t\t\ttitle: title || Story.get(State.passage).description(),\n\t\t\tdate: Date.now()\n\t\t};\n\n\t\tindex.slots[slot] = supplemental;\n\t\ttry {\n\t\t\tindexSave(index);\n\t\t} catch (ex) {\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (metadata != null) { // lazy equality for null\n\t\t\tsupplemental.metadata = metadata;\n\t\t}\n\n\t\ttry {\n\t\t\treturn storage.set(`slot${slot}`, _marshal(supplemental, { type : Type.Slot }));\n\t\t} catch (ex) {\n\t\t\tindex.slots[slot] = null;\n\t\t\tindexSave(index);\n\t\t\t_storageAlert();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction slotsDelete(slot) {\n\t\tif (slot < 0 || slot > _slotsUBound) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst index = indexGet();\n\n\t\tif (slot >= index.slots.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tindex.slots[slot] = null;\n\t\tindexSave(index);\n\n\t\treturn storage.delete(`slot${slot}`);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDisk Import/Export Functions.\n\t*******************************************************************************************************************/\n\tfunction exportToDisk(filename, metadata, marshaledSave) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tfunction datestamp() {\n\t\t\tconst now = new Date();\n\t\t\tlet MM = now.getMonth() + 1;\n\t\t\tlet DD = now.getDate();\n\t\t\tlet hh = now.getHours();\n\t\t\tlet mm = now.getMinutes();\n\t\t\tlet ss = now.getSeconds();\n\n\t\t\tif (MM < 10) { MM = `0${MM}`; }\n\t\t\tif (DD < 10) { DD = `0${DD}`; }\n\t\t\tif (hh < 10) { hh = `0${hh}`; }\n\t\t\tif (mm < 10) { mm = `0${mm}`; }\n\t\t\tif (ss < 10) { ss = `0${ss}`; }\n\n\t\t\treturn `${now.getFullYear()}${MM}${DD}-${hh}${mm}${ss}`;\n\t\t}\n\n\t\tfunction legalizeName(str) {\n\t\t\t/*\n\t\t\t\tNOTE: The range of illegal characters consists of: C0 controls, double quote,\n\t\t\t\tnumber, dollar, percent, ampersand, single quote, asterisk, plus, comma,\n\t\t\t\tforward slash, colon, semi-colon, less-than, equals, greater-than, question,\n\t\t\t\tbackslash, caret, backquote/grave, pipe/vertical-bar, delete, C1 controls.\n\t\t\t*/\n\t\t\treturn String(str).trim()\n\t\t\t\t.replace(/[\\x00-\\x1f\"#$%&'*+,/:;<=>?\\\\^`|\\x7f-\\x9f]+/g, '') // eslint-disable-line no-control-regex\n\t\t\t\t.replace(/[_\\s\\u2013\\u2014-]+/g, '-'); // legacy\n\t\t}\n\n\t\tconst baseName     = filename == null ? Story.domId : legalizeName(filename); // lazy equality for null\n\t\tconst saveName     = `${baseName}-${datestamp()}.save`;\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\tconst saveObj      = LZString.compressToBase64(JSON.stringify(\n\t\t\tmarshaledSave == null ? _marshal(supplemental, { type : Type.Disk }) : marshaledSave\n\t\t));\n\t\tsaveAs(new Blob([saveObj], { type : 'text/plain;charset=UTF-8' }), saveName);\n\t}\n\n\tfunction importFromDisk(event) {\n\t\tconst file   = event.target.files[0];\n\t\tconst reader = new FileReader();\n\n\t\t// Add the handler that will capture the file information once the load is finished.\n\t\tjQuery(reader).on('load', ev => {\n\t\t\tconst target = ev.currentTarget;\n\n\t\t\tif (!target.result) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet saveObj;\n\n\t\t\ttry {\n\t\t\t\tsaveObj = JSON.parse(\n\t\t\t\t\t/* legacy */ /\\.json$/i.test(file.name) || /^\\{/.test(target.result)\n\t\t\t\t\t\t? target.result\n\t\t\t\t\t\t: /* /legacy */ LZString.decompressFromBase64(target.result)\n\t\t\t\t);\n\t\t\t}\n\t\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\t\t_unmarshal(saveObj);\n\t\t});\n\n\t\t// Initiate the file load.\n\t\treader.readAsText(file);\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tSerialization Functions.\n\t*******************************************************************************************************************/\n\tfunction serialize(metadata) {\n\t\tif (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {\n\t\t\tif (Dialog.isOpen()) {\n\t\t\t\t$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUI.alert(L10n.get('savesDisallowed'));\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst supplemental = metadata == null ? {} : { metadata }; // lazy equality for null\n\t\treturn LZString.compressToBase64(JSON.stringify(_marshal(supplemental, { type : Type.Serialize })));\n\t}\n\n\tfunction deserialize(base64Str) {\n\t\t/*\n\t\t\tNOTE: We purposefully do not attempt to catch parameter shenanigans\n\t\t\there, instead relying on `_unmarshal()` to do the heavy lifting.\n\t\t*/\n\n\t\tlet saveObj;\n\n\t\ttry {\n\t\t\tsaveObj = JSON.parse(LZString.decompressFromBase64(base64Str));\n\t\t}\n\t\tcatch (ex) { /* no-op; `_unmarshal()` will handle the error */ }\n\n\t\tif (!_unmarshal(saveObj)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn saveObj.metadata;\n\t}\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _storageAlert() {\n\t\tif (Dialog.isOpen()) {\n\t\t\t$(document).one(':dialogclosed', () => UI.alert('Local storage full, delete saves or increase local storage'));\n\t\t} else {\n\t\t\tUI.alert('Local storage full, delete saves or increase local storage');\n\t\t}\n\t}\n\n\tfunction _appendSlots(array, num) {\n\t\tfor (let i = 0; i < num; ++i) {\n\t\t\tarray.push(null);\n\t\t}\n\n\t\treturn array;\n\t}\n\n\tfunction _savesObjUpdate(saveObj) {\n\t\tif (saveObj == null || typeof saveObj !== \"object\") { // lazy equality for null\n\t\t\treturn false;\n\t\t}\n\n\t\tlet updated = false;\n\n\t\t/* eslint-disable no-param-reassign */\n\t\tif (\n\t\t\t   !saveObj.hasOwnProperty('state')\n\t\t\t|| !saveObj.state.hasOwnProperty('delta')\n\t\t\t|| !saveObj.state.hasOwnProperty('index')\n\t\t) {\n\t\t\tif (saveObj.hasOwnProperty('data')) {\n\t\t\t\tdelete saveObj.mode;\n\t\t\t\tsaveObj.state = {\n\t\t\t\t\tdelta : State.deltaEncode(saveObj.data)\n\t\t\t\t};\n\t\t\t\tdelete saveObj.data;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('delta')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\t\t\tdelete saveObj.state.history;\n\t\t\t}\n\t\t\telse if (!saveObj.state.hasOwnProperty('index')) {\n\t\t\t\tdelete saveObj.state.mode;\n\t\t\t}\n\n\t\t\tsaveObj.state.index = saveObj.state.delta.length - 1;\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (saveObj.state.hasOwnProperty('rseed')) {\n\t\t\tsaveObj.state.seed = saveObj.state.rseed;\n\t\t\tdelete saveObj.state.rseed;\n\n\t\t\tsaveObj.state.delta.forEach((_, i, delta) => {\n\t\t\t\tif (delta[i].hasOwnProperty('rcount')) {\n\t\t\t\t\tdelta[i].pull = delta[i].rcount;\n\t\t\t\t\tdelete delta[i].rcount;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tupdated = true;\n\t\t}\n\n\t\tif (\n\t\t\t   saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number'\n\t\t\t||  saveObj.state.hasOwnProperty('unique')\n\t\t\t||  saveObj.state.hasOwnProperty('last')\n\t\t) {\n\t\t\tif (saveObj.state.hasOwnProperty('expired') && typeof saveObj.state.expired === 'number') {\n\t\t\t\tdelete saveObj.state.expired;\n\t\t\t}\n\n\t\t\tif (saveObj.state.hasOwnProperty('unique') || saveObj.state.hasOwnProperty('last')) {\n\t\t\t\tsaveObj.state.expired = [];\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('unique')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.unique);\n\t\t\t\t\tdelete saveObj.state.unique;\n\t\t\t\t}\n\n\t\t\t\tif (saveObj.state.hasOwnProperty('last')) {\n\t\t\t\t\tsaveObj.state.expired.push(saveObj.state.last);\n\t\t\t\t\tdelete saveObj.state.last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tupdated = true;\n\t\t}\n\t\t/* eslint-enable no-param-reassign */\n\n\t\treturn updated;\n\t}\n\n\tfunction _marshal(supplemental, details) {\n\t\tif (DEBUG) { console.log(`[Save/_marshal(…, { type : '${details.type}' })]`); }\n\n\t\tif (supplemental != null && typeof supplemental !== 'object') { // lazy equality for null\n\t\t\tthrow new Error('supplemental parameter must be an object');\n\t\t}\n\n\t\tconst saveObj = Object.assign({}, supplemental, {\n\t\t\tid    : Config.saves.id,\n\t\t\tstate : State.marshalForSave()\n\t\t});\n\n\t\tif (Config.saves.version) {\n\t\t\tsaveObj.version = Config.saves.version;\n\t\t}\n\n\t\tif (typeof Config.saves.onSave === 'function') {\n\t\t\tConfig.saves.onSave(saveObj, details);\n\t\t}\n\n\t\t// Delta encode the state history and delete the non-encoded property.\n\t\tsaveObj.state.delta = State.deltaEncode(saveObj.state.history);\n\t\tdelete saveObj.state.history;\n\n\t\treturn saveObj;\n\t}\n\n\tfunction _unmarshal(saveObj) {\n\t\tif (DEBUG) { console.log('[Save/_unmarshal()]'); }\n\n\t\ttry {\n\t\t\t/* eslint-disable no-param-reassign */\n\t\t\t/* legacy */\n\t\t\t// Update saves with old/obsolete properties.\n\t\t\t_savesObjUpdate(saveObj);\n\t\t\t/* /legacy */\n\n\t\t\tif (!saveObj || !saveObj.hasOwnProperty('id') || !saveObj.hasOwnProperty('state')) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveMissingData'));\n\t\t\t}\n\n\t\t\t// Delta decode the state history and delete the encoded property.\n\t\t\tsaveObj.state.history = State.deltaDecode(saveObj.state.delta);\n\t\t\tdelete saveObj.state.delta;\n\n\t\t\tif (typeof Config.saves.onLoad === 'function') {\n\t\t\t\tConfig.saves.onLoad(saveObj);\n\t\t\t}\n\n\t\t\tif (saveObj.id !== Config.saves.id) {\n\t\t\t\tthrow new Error(L10n.get('errorSaveIdMismatch'));\n\t\t\t}\n\n\t\t\t// Restore the state.\n\t\t\tState.unmarshalForSave(saveObj.state); // may also throw exceptions\n\n\t\t\t// Show the active moment.\n\t\t\tEngine.show();\n\t\t\t/* eslint-enable no-param-reassign */\n\t\t}\n\t\tcatch (ex) {\n\t\t\tUI.alert(`${ex.message.toUpperFirst()}.</p><p>${L10n.get('aborting')}.`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tSave Functions.\n\t\t*/\n\t\tinit  : { value : savesInit },\n\t\tclear : { value : savesObjClear },\n\t\tok    : { value : savesOk },\n\t\tindex : { value : indexGet },\n\n\t\t/*\n\t\t\tAutosave Functions.\n\t\t*/\n\t\tautosave : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok     : { value : autosaveOk },\n\t\t\t\thas    : { value : autosaveHas },\n\t\t\t\tget    : { value : autosaveGet },\n\t\t\t\tload   : { value : autosaveLoad },\n\t\t\t\tsave   : { value : autosaveSave },\n\t\t\t\tdelete : { value : autosaveDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tSlots Functions.\n\t\t*/\n\t\tslots : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tok      : { value : slotsOk },\n\t\t\t\tlength  : { get : slotsLength },\n\t\t\t\tisEmpty : { value : slotsIsEmpty },\n\t\t\t\tcount   : { value : slotsCount },\n\t\t\t\thas     : { value : slotsHas },\n\t\t\t\tget     : { value : slotsGet },\n\t\t\t\tload    : { value : slotsLoad },\n\t\t\t\tsave    : { value : slotsSave },\n\t\t\t\tdelete  : { value : slotsDelete }\n\t\t\t}))\n\t\t},\n\n\t\t/*\n\t\t\tDisk Import/Export Functions.\n\t\t*/\n\t\texport : { value : exportToDisk },\n\t\timport : { value : importFromDisk },\n\n\t\t/*\n\t\t\tSerialization Functions.\n\t\t*/\n\t\tserialize   : { value : serialize },\n\t\tdeserialize : { value : deserialize }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsetting.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Util, settings:true, storage */\n\nvar Setting = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Setting control types object (pseudo-enumeration).\n\tconst Types = Util.toEnum({\n\t\tHeader : 0,\n\t\tToggle : 1,\n\t\tList   : 2,\n\t\tRange  : 3\n\t});\n\n\t// Setting definition array.\n\tconst _definitions = [];\n\n\n\t/*******************************************************************************************************************\n\t\tSettings Functions.\n\t*******************************************************************************************************************/\n\tfunction settingsInit() {\n\t\tif (DEBUG) { console.log('[Setting/settingsInit()]'); }\n\n\t\t/* legacy */\n\t\t// Attempt to migrate an existing `options` store to `settings`.\n\t\tif (storage.has('options')) {\n\t\t\tconst old = storage.get('options');\n\n\t\t\tif (old !== null) {\n\t\t\t\twindow.SugarCube.settings = settings = Object.assign(settingsCreate(), old);\n\t\t\t}\n\n\t\t\tsettingsSave();\n\t\t\tstorage.delete('options');\n\t\t}\n\t\t/* /legacy */\n\n\t\t// Load existing settings.\n\t\tsettingsLoad();\n\n\t\t// Execute `onInit` callbacks.\n\t\t_definitions.forEach(def => {\n\t\t\tif (def.hasOwnProperty('onInit')) {\n\t\t\t\tconst thisArg = {\n\t\t\t\t\tname    : def.name,\n\t\t\t\t\tvalue   : settings[def.name],\n\t\t\t\t\tdefault : def.default\n\t\t\t\t};\n\n\t\t\t\tif (def.hasOwnProperty('list')) {\n\t\t\t\t\tthisArg.list = def.list;\n\t\t\t\t}\n\n\t\t\t\tdef.onInit.call(thisArg);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction settingsCreate() {\n\t\treturn Object.create(null);\n\t}\n\n\tfunction settingsSave() {\n\t\tconst savedSettings = settingsCreate();\n\n\t\tif (Object.keys(settings).length > 0) {\n\t\t\t_definitions\n\t\t\t\t.filter(def => def.type !== Types.Header && settings[def.name] !== def.default)\n\t\t\t\t.forEach(def => savedSettings[def.name] = settings[def.name]);\n\t\t}\n\n\t\tif (Object.keys(savedSettings).length === 0) {\n\t\t\tstorage.delete('settings');\n\t\t\treturn true;\n\t\t}\n\n\t\treturn storage.set('settings', savedSettings);\n\t}\n\n\tfunction settingsLoad() {\n\t\tconst defaultSettings = settingsCreate();\n\t\tconst loadedSettings  = storage.get('settings') || settingsCreate();\n\n\t\t// Load the defaults.\n\t\t_definitions\n\t\t\t.filter(def => def.type !== Types.Header)\n\t\t\t.forEach(def => defaultSettings[def.name] = def.default);\n\n\t\t// Assign to the `settings` object while overwriting the defaults with the loaded settings.\n\t\twindow.SugarCube.settings = settings = Object.assign(defaultSettings, loadedSettings);\n\t}\n\n\tfunction settingsClear() {\n\t\twindow.SugarCube.settings = settings = settingsCreate();\n\t\tstorage.delete('settings');\n\t\treturn true;\n\t}\n\n\tfunction settingsReset(name) {\n\t\tif (arguments.length === 0) {\n\t\t\tsettingsClear();\n\t\t\tsettingsLoad();\n\t\t}\n\t\telse {\n\t\t\tif (name == null || !definitionsHas(name)) { // lazy equality for null\n\t\t\t\tthrow new Error(`nonexistent setting \"${name}\"`);\n\t\t\t}\n\n\t\t\tconst def = definitionsGet(name);\n\n\t\t\tif (def.type !== Types.Header) {\n\t\t\t\tsettings[name] = def.default;\n\t\t\t}\n\t\t}\n\n\t\treturn settingsSave();\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tDefinitions Functions.\n\t*******************************************************************************************************************/\n\tfunction definitionsForEach(callback, thisArg) {\n\t\t_definitions.forEach(callback, thisArg);\n\t}\n\n\tfunction definitionsAdd(type, name, def) {\n\t\tif (arguments.length < 3) {\n\t\t\tconst errors = [];\n\t\t\tif (arguments.length < 1) { errors.push('type'); }\n\t\t\tif (arguments.length < 2) { errors.push('name'); }\n\t\t\tif (arguments.length < 3) { errors.push('definition'); }\n\t\t\tthrow new Error(`missing parameters, no ${errors.join(' or ')} specified`);\n\t\t}\n\n\t\tif (typeof def !== 'object') {\n\t\t\tthrow new TypeError('definition parameter must be an object');\n\t\t}\n\n\t\tif (definitionsHas(name)) {\n\t\t\tthrow new Error(`cannot clobber existing setting \"${name}\"`);\n\t\t}\n\n\t\t/*\n\t\t\tDefinition object properties and types:\n\t\t\t\ttype      →  (all)   → Setting.Types\n\t\t\t\tname      →  (all)   → string\n\t\t\t\tlabel     →  (all)   → string\n\t\t\t\tdesc      →  (all)   → string\n\t\t\t\tdefault\n\t\t\t\t\t(if defined)\n \t\t\t\t\t\t  →  Toggle  → boolean\n\t\t\t\t\t\t  →  List    → Array\n\t\t\t\t\t\t  →  Range   → number\n\t\t\t\t\t(if undefined)\n\t\t\t\t\t\t  →  Toggle  → false\n\t\t\t\t\t\t  →  List    → list[0]\n\t\t\t\t\t\t  →  Range   → max\n\t\t\t\tlist      →  List    → Array\n\t\t\t\tmin       →  Range   → number\n\t\t\t\tmax       →  Range   → number\n\t\t\t\tstep      →  Range   → number\n\t\t\t\tonInit    →  (all)   → function\n\t\t\t\tonChange  →  (all)   → function\n\t\t*/\n\t\tconst definition = {\n\t\t\ttype,\n\t\t\tname,\n\t\t\tlabel : typeof def.label === 'string' ? def.label.trim() : ''\n\t\t};\n\n\t\tif (typeof def.desc === 'string') {\n\t\t\tconst desc = def.desc.trim();\n\n\t\t\tif (desc !== '') {\n\t\t\t\tdefinition.desc = desc;\n\t\t\t}\n\t\t}\n\n\t\tswitch (type) {\n\t\tcase Types.Header:\n\t\t\tbreak;\n\n\t\tcase Types.Toggle:\n\t\t\tdefinition.default = !!def.default;\n\t\t\tbreak;\n\n\t\tcase Types.List:\n\t\t\tif (!def.hasOwnProperty('list')) {\n\t\t\t\tthrow new Error('no list specified');\n\t\t\t}\n\t\t\telse if (!Array.isArray(def.list)) {\n\t\t\t\tthrow new TypeError('list must be an array');\n\t\t\t}\n\t\t\telse if (def.list.length === 0) {\n\t\t\t\tthrow new Error('list must not be empty');\n\t\t\t}\n\n\t\t\tdefinition.list = Object.freeze(def.list);\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.list[0];\n\t\t\t}\n\t\t\telse {\n\t\t\t\tconst defaultIndex = def.list.indexOf(def.default);\n\n\t\t\t\tif (defaultIndex === -1) {\n\t\t\t\t\tthrow new Error('list does not contain default');\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.list[defaultIndex];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase Types.Range:\n\t\t\tif (!def.hasOwnProperty('min')) {\n\t\t\t\tthrow new Error('no min specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.min !== 'number'\n\t\t\t\t|| Number.isNaN(def.min)\n\t\t\t\t|| !Number.isFinite(def.min)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('min must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('max')) {\n\t\t\t\tthrow new Error('no max specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.max !== 'number'\n\t\t\t\t|| Number.isNaN(def.max)\n\t\t\t\t|| !Number.isFinite(def.max)\n\t\t\t) {\n\t\t\t\tthrow new TypeError('max must be a finite number');\n\t\t\t}\n\n\t\t\tif (!def.hasOwnProperty('step')) {\n\t\t\t\tthrow new Error('no step specified');\n\t\t\t}\n\t\t\telse if (\n\t\t\t\t   typeof def.step !== 'number'\n\t\t\t\t|| Number.isNaN(def.step)\n\t\t\t\t|| !Number.isFinite(def.step)\n\t\t\t\t|| def.step <= 0\n\t\t\t) {\n\t\t\t\tthrow new TypeError('step must be a finite number greater than zero');\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Determine how many fractional digits we need to be concerned with based on the step value.\n\t\t\t\tconst fracDigits = (() => {\n\t\t\t\t\tconst str = String(def.step);\n\t\t\t\t\tconst pos = str.lastIndexOf('.');\n\t\t\t\t\treturn pos === -1 ? 0 : str.length - pos - 1;\n\t\t\t\t})();\n\n\t\t\t\t// Set up a function to validate a given value against the step value.\n\t\t\t\tfunction stepValidate(value) {\n\t\t\t\t\tif (fracDigits > 0) {\n\t\t\t\t\t\tconst ma = Number(`${def.min}e${fracDigits}`);\n\t\t\t\t\t\tconst sa = Number(`${def.step}e${fracDigits}`);\n\t\t\t\t\t\tconst va = Number(`${value}e${fracDigits}`) - ma;\n\t\t\t\t\t\treturn Number(`${va - va % sa + ma}e-${fracDigits}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst va = value - def.min;\n\t\t\t\t\treturn va - va % def.step + def.min;\n\t\t\t\t}\n\n\t\t\t\t// Sanity check the max value against the step value.\n\t\t\t\tif (stepValidate(def.max) !== def.max) {\n\t\t\t\t\tthrow new RangeError(`max (${def.max}) is not a multiple of the step (${def.step}) plus the min (${def.min})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefinition.max = def.max;\n\t\t\tdefinition.min = def.min;\n\t\t\tdefinition.step = def.step;\n\n\t\t\tif (def.default == null) { // lazy equality for null\n\t\t\t\tdefinition.default = def.max;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (\n\t\t\t\t\t   typeof def.default !== 'number'\n\t\t\t\t\t|| Number.isNaN(def.default)\n\t\t\t\t\t|| !Number.isFinite(def.default)\n\t\t\t\t) {\n\t\t\t\t\tthrow new TypeError('default must be a finite number');\n\t\t\t\t}\n\t\t\t\telse if (def.default < def.min) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is less than min (${def.min})`);\n\t\t\t\t}\n\t\t\t\telse if (def.default > def.max) {\n\t\t\t\t\tthrow new RangeError(`default (${def.default}) is greater than max (${def.max})`);\n\t\t\t\t}\n\n\t\t\t\tdefinition.default = def.default;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tthrow new Error(`unknown Setting type: ${type}`);\n\t\t}\n\n\t\tif (typeof def.onInit === 'function') {\n\t\t\tdefinition.onInit = Object.freeze(def.onInit);\n\t\t}\n\n\t\tif (typeof def.onChange === 'function') {\n\t\t\tdefinition.onChange = Object.freeze(def.onChange);\n\t\t}\n\n\t\t_definitions.push(Object.freeze(definition));\n\t}\n\n\tfunction definitionsAddHeader(name, desc) {\n\t\tdefinitionsAdd(Types.Header, name, { desc });\n\t}\n\n\tfunction definitionsAddToggle(...args) {\n\t\tdefinitionsAdd(Types.Toggle, ...args);\n\t}\n\n\tfunction definitionsAddList(...args) {\n\t\tdefinitionsAdd(Types.List, ...args);\n\t}\n\n\tfunction definitionsAddRange(...args) {\n\t\tdefinitionsAdd(Types.Range, ...args);\n\t}\n\n\tfunction definitionsIsEmpty() {\n\t\treturn _definitions.length === 0;\n\t}\n\n\tfunction definitionsHas(name) {\n\t\treturn _definitions.some(definition => definition.name === name);\n\t}\n\n\tfunction definitionsGet(name) {\n\t\treturn _definitions.find(definition => definition.name === name);\n\t}\n\n\tfunction definitionsDelete(name) {\n\t\tif (definitionsHas(name)) {\n\t\t\tdelete settings[name];\n\t\t}\n\n\t\tfor (let i = 0; i < _definitions.length; ++i) {\n\t\t\tif (_definitions[i].name === name) {\n\t\t\t\t_definitions.splice(i, 1);\n\t\t\t\tdefinitionsDelete(name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tEnumerations.\n\t\t*/\n\t\tTypes : { value : Types },\n\n\t\t/*\n\t\t\tSettings Functions.\n\t\t*/\n\t\tinit   : { value : settingsInit },\n\t\tcreate : { value : settingsCreate },\n\t\tsave   : { value : settingsSave },\n\t\tload   : { value : settingsLoad },\n\t\tclear  : { value : settingsClear },\n\t\treset  : { value : settingsReset },\n\n\t\t/*\n\t\t\tDefinitions Functions.\n\t\t*/\n\t\tforEach   : { value : definitionsForEach },\n\t\tadd       : { value : definitionsAdd },\n\t\taddHeader : { value : definitionsAddHeader },\n\t\taddToggle : { value : definitionsAddToggle },\n\t\taddList   : { value : definitionsAddList },\n\t\taddRange  : { value : definitionsAddRange },\n\t\tisEmpty   : { value : definitionsIsEmpty },\n\t\thas       : { value : definitionsHas },\n\t\tget       : { value : definitionsGet },\n\t\tdelete    : { value : definitionsDelete }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tstory.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Alert, Config, Passage, Scripting, StyleWrapper, Util, Wikifier */\n\nvar Story = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Map of normal passages.\n\tconst _passages = {};\n\n\t// List of script passages.\n\tconst _scripts = [];\n\n\t// List of style passages.\n\tconst _styles = [];\n\n\t// List of widget passages.\n\tconst _widgets = [];\n\n\t// Story title.\n\tlet _title = '';\n\n\t// Story IFID.\n\tlet _ifId = '';\n\n\t// DOM-compatible ID.\n\tlet _domId = '';\n\n\n\t/*******************************************************************************************************************\n\t\tStory Functions.\n\t*******************************************************************************************************************/\n\tfunction storyLoad() {\n\t\tif (DEBUG) { console.log('[Story/storyLoad()]'); }\n\n\t\tconst validationCodeTags = [\n\t\t\t'widget'\n\t\t];\n\t\tconst validationNoCodeTagPassages = [\n\t\t\t'PassageDone',\n\t\t\t'PassageFooter',\n\t\t\t'PassageHeader',\n\t\t\t'PassageReady',\n\t\t\t'StoryAuthor',\n\t\t\t'StoryBanner',\n\t\t\t'StoryCaption',\n\t\t\t'StoryInit',\n\t\t\t'StoryMenu',\n\t\t\t'StoryShare',\n\t\t\t'StorySubtitle'\n\t\t];\n\n\t\tfunction validateStartingPassage(passage) {\n\t\t\tif (passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`starting passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\tfunction validateSpecialPassages(passage) {\n\t\t\tif (validationNoCodeTagPassages.includes(passage.title) && passage.tags.includesAny(validationCodeTags)) {\n\t\t\t\tthrow new Error(`special passage \"${passage.title}\" contains illegal tags; invalid: \"${passage.tags.filter(tag => validationCodeTags.includes(tag)).sort().join('\", \"')}\"`);\n\t\t\t}\n\t\t}\n\n\t\t// For Twine 1.\n\t\tif (TWINE1) {\n\t\t\t/*\n\t\t\t\tAdditional Twine 1 validation setup.\n\t\t\t*/\n\t\t\tvalidationCodeTags.unshift('script', 'stylesheet');\n\t\t\tvalidationNoCodeTagPassages.push('StoryTitle');\n\n\t\t\tfunction validateTwine1CodePassages(passage) {\n\t\t\t\tconst codeTags  = [...validationCodeTags];\n\t\t\t\tconst foundTags = [];\n\n\t\t\t\tpassage.tags.forEach(tag => {\n\t\t\t\t\tif (codeTags.includes(tag)) {\n\t\t\t\t\t\tfoundTags.push(...codeTags.delete(tag));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (foundTags.length > 1) {\n\t\t\t\t\tthrow new Error(`code passage \"${passage.title}\" contains multiple code tags; invalid: \"${foundTags.sort().join('\", \"')}\"`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = (() => {\n\t\t\t\t/*\n\t\t\t\t\tHandle the Twine 1.4+ Test Play From Here feature.\n\n\t\t\t\t\tWARNING: Do not remove the `String()` wrapper from or change the quote\n\t\t\t\t\tstyle of the `\"START_AT\"` replacement target.  The former is there to\n\t\t\t\t\tkeep UglifyJS from pruning the code into oblivion—i.e. minifying the\n\t\t\t\t\tcode into something broken.  The latter is there because the Twine 1\n\t\t\t\t\tpattern that matches it depends upon the double quotes.\n\n\t\t\t\t*/\n\t\t\t\tconst testPlay = String(\"START_AT\"); // eslint-disable-line quotes\n\n\t\t\t\tif (testPlay !== '') {\n\t\t\t\t\tif (DEBUG) { console.log(`\\tTest play; starting passage: \"${testPlay}\"`); }\n\n\t\t\t\t\tConfig.debug = true;\n\t\t\t\t\treturn testPlay;\n\t\t\t\t}\n\n\t\t\t\t// In the absence of a `testPlay` value, return 'Start'.\n\t\t\t\treturn 'Start';\n\t\t\t})();\n\n\t\t\t/*\n\t\t\t\tProcess the passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\tjQuery('#store-area')\n\t\t\t\t.children(':not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst passage = new Passage($this.attr('tiddler'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (passage.title === Config.passages.start) {\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('stylesheet')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_styles.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('script')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_scripts.push(passage);\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\tvalidateTwine1CodePassages(passage);\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tSet the story title or throw an exception.\n\t\t\t*/\n\t\t\tif (_passages.hasOwnProperty('StoryTitle')) {\n\t\t\t\tconst buf = document.createDocumentFragment();\n\t\t\t\tnew Wikifier(buf, _passages.StoryTitle.processText().trim());\n\t\t\t\t_storySetTitle(buf.textContent);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error('cannot find the \"StoryTitle\" special passage');\n\t\t\t}\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\n\t\t// For Twine 2.\n\t\telse {\n\t\t\tconst $storydata = jQuery('tw-storydata');\n\t\t\tconst startNode  = $storydata.attr('startnode') || '';\n\n\t\t\t/*\n\t\t\t\tSet the default starting passage.\n\t\t\t*/\n\t\t\tConfig.passages.start = null; // no default in Twine 2\n\n\t\t\t/*\n\t\t\t\tProcess story options.\n\n\t\t\t\tNOTE: Currently, the only option of interest is 'debug', so we\n\t\t\t\tsimply use a regular expression to check for it.\n\t\t\t*/\n\t\t\tConfig.debug = /\\bdebug\\b/.test($storydata.attr('options'));\n\n\t\t\t/*\n\t\t\t\tProcess stylesheet passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('style') // alternatively: '[type=\"text/twine-css\"]' or '#twine-user-stylesheet'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_styles.push(new Passage(`tw-user-style-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess script passages.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('script') // alternatively: '[type=\"text/twine-javascript\"]' or '#twine-user-script'\n\t\t\t\t.each(function (i) {\n\t\t\t\t\t_scripts.push(new Passage(`tw-user-script-${i}`, this));\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tProcess normal passages, excluding any tagged 'Twine.private' or 'annotation'.\n\t\t\t*/\n\t\t\t$storydata\n\t\t\t\t.children('tw-passagedata:not([tags~=\"Twine.private\"],[tags~=\"annotation\"])')\n\t\t\t\t.each(function () {\n\t\t\t\t\tconst $this   = jQuery(this);\n\t\t\t\t\tconst pid     = $this.attr('pid') || '';\n\t\t\t\t\tconst passage = new Passage($this.attr('name'), this);\n\n\t\t\t\t\t// Special cases.\n\t\t\t\t\tif (pid === startNode && startNode !== '') {\n\t\t\t\t\t\tConfig.passages.start = passage.title;\n\t\t\t\t\t\tvalidateStartingPassage(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t\telse if (passage.tags.includes('widget')) {\n\t\t\t\t\t\t_widgets.push(passage);\n\t\t\t\t\t}\n\n\t\t\t\t\t// All other passages.\n\t\t\t\t\telse {\n\t\t\t\t\t\tvalidateSpecialPassages(passage);\n\t\t\t\t\t\t_passages[passage.title] = passage;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t/*\n\t\t\t\tGet the story IFID.\n\t\t\t*/\n\t\t\t_ifId = $storydata.attr('ifid');\n\n\t\t\t/*\n\t\t\t\tSet the story title.\n\n\t\t\t\tFIXME: Maybe `$storydata.attr('name')` should be used instead of `'{{STORY_NAME}}'`?\n\t\t\t*/\n\t\t\t// _storySetTitle($storydata.attr('name'));\n\t\t\t_storySetTitle('{{STORY_NAME}}');\n\n\t\t\t/*\n\t\t\t\tSet the default saves ID (must be done after the call to `_storySetTitle()`).\n\t\t\t*/\n\t\t\tConfig.saves.id = Story.domId;\n\t\t}\n\t}\n\n\tfunction storyInit() {\n\t\tif (DEBUG) { console.log('[Story/storyInit()]'); }\n\n\t\t/*\n\t\t\tAdd the story styles.\n\t\t*/\n\t\t(() => {\n\t\t\tconst storyStyle = document.createElement('style');\n\n\t\t\tnew StyleWrapper(storyStyle)\n\t\t\t\t.add(_styles.map(style => style.text.trim()).join('\\n'));\n\n\t\t\tjQuery(storyStyle)\n\t\t\t\t.appendTo(document.head)\n\t\t\t\t.attr({\n\t\t\t\t\tid   : 'style-story',\n\t\t\t\t\ttype : 'text/css'\n\t\t\t\t});\n\t\t})();\n\n\t\t/*\n\t\t\tEvaluate the story scripts.\n\t\t*/\n\t\tfor (let i = 0; i < _scripts.length; ++i) {\n\t\t\ttry {\n\t\t\t\tScripting.evalJavaScript(_scripts[i].text);\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_scripts[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t\tProcess the story widgets.\n\t\t*/\n\t\tfor (let i = 0; i < _widgets.length; ++i) {\n\t\t\ttry {\n\t\t\t\tWikifier.wikifyEval(_widgets[i].processText());\n\t\t\t}\n\t\t\tcatch (ex) {\n\t\t\t\tconsole.error(ex);\n\t\t\t\tAlert.error(_widgets[i].title, typeof ex === 'object' ? ex.message : ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction _storySetTitle(rawTitle) {\n\t\tif (rawTitle == null) { // lazy equality for null\n\t\t\tthrow new Error('story title must not be null or undefined');\n\t\t}\n\n\t\tconst title = Util.unescape(String(rawTitle)).trim();\n\n\t\tif (title === '') { // lazy equality for null\n\t\t\tthrow new Error('story title must not be empty or consist solely of whitespace');\n\t\t}\n\n\t\tdocument.title = _title = title;\n\n\t\t// TODO: In v3 the `_domId` should be created from a combination of the\n\t\t// `_title` slug and the IFID, if available, to avoid collisions between\n\t\t// stories whose titles generate identical slugs.\n\t\t_domId = Util.slugify(_title);\n\n\t\t// [v2] Protect the `_domId` against being an empty string.\n\t\t//\n\t\t// If `_domId` is empty, attempt a failover.\n\t\tif (_domId === '') {\n\t\t\t// If `_ifId` is not empty, then use it.\n\t\t\tif (_ifId !== '') {\n\t\t\t\t_domId = _ifId;\n\t\t\t}\n\n\t\t\t// Elsewise generate a string from the `_title`'s code points (in hexadecimal).\n\t\t\telse {\n\t\t\t\tfor (let i = 0, len = _title.length; i < len; ++i) {\n\t\t\t\t\tconst { char, start, end } = Util.charAndPosAt(_title, i);\n\t\t\t\t\t_domId += char.codePointAt(0).toString(16);\n\t\t\t\t\ti += end - start;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction storyTitle() {\n\t\treturn _title;\n\t}\n\n\tfunction storyDomId() {\n\t\treturn _domId;\n\t}\n\n\tfunction storyIfId() {\n\t\treturn _ifId;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tPassage Functions.\n\t*******************************************************************************************************************/\n\tfunction passagesAdd(passage) {\n\t\tif (!(passage instanceof Passage)) {\n\t\t\tthrow new TypeError('Story.add passage parameter must be an instance of Passage');\n\t\t}\n\n\t\tconst title = passage.title;\n\n\t\tif (!_passages.hasOwnProperty(title)) {\n\t\t\t_passages[title] = passage;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction passagesHas(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t\treturn _passages.hasOwnProperty(String(title));\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.has title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGet(title) {\n\t\tlet type = typeof title;\n\n\t\tswitch (type) {\n\t\t// Valid types.\n\t\tcase 'number':\n\t\tcase 'string':\n\t\t/* eslint-disable indent */\n\t\t\t{\n\t\t\t\tconst id = String(title);\n\t\t\t\treturn _passages.hasOwnProperty(id) ? _passages[id] : new Passage(id || '(unknown)');\n\t\t\t}\n\t\t/* eslint-enable indent */\n\n\t\t// Invalid types.  We do the extra processing just to make a nicer error.\n\t\tcase 'undefined':\n\t\t\t/* no-op */\n\t\t\tbreak;\n\n\t\tcase 'object':\n\t\t\ttype = title === null ? 'null' : 'an object';\n\t\t\tbreak;\n\n\t\tdefault: // 'bigint', 'boolean', 'function', 'symbol'\n\t\t\ttype = `a ${type}`;\n\t\t\tbreak;\n\t\t}\n\n\t\tthrow new TypeError(`Story.get title parameter cannot be ${type}`);\n\t}\n\n\tfunction passagesGetAllRegular() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Object.assign({}, _passages));\n\t}\n\n\tfunction passagesGetAllScript() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_scripts));\n\t}\n\n\tfunction passagesGetAllStylesheet() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_styles));\n\t}\n\n\tfunction passagesGetAllWidget() {\n\t\t// NOTE: Return an immutable copy, rather than the internal mutable original.\n\t\treturn Object.freeze(Array.from(_widgets));\n\t}\n\n\tfunction passagesLookup(key, value  /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\t// Objects (sans `null`).\n\t\t\tif (typeof passage[key] === 'object' && passage[key] !== null) {\n\t\t\t\t// The only object type currently supported is `Array`, since the\n\t\t\t\t// non-method `Passage` object properties currently yield only either\n\t\t\t\t// primitives or arrays.\n\t\t\t\tif (passage[key] instanceof Array && passage[key].some(m => Util.sameValueZero(m, value))) {\n\t\t\t\t\tresults.push(passage);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// All other types (incl. `null`).\n\t\t\telse if (Util.sameValueZero(passage[key], value)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\tfunction passagesLookupWith(predicate /* legacy */, sortKey = 'title'/* /legacy */) {\n\t\tif (typeof predicate !== 'function') {\n\t\t\tthrow new TypeError('Story.lookupWith predicate parameter must be a function');\n\t\t}\n\n\t\tconst results = [];\n\n\t\tObject.keys(_passages).forEach(name => {\n\t\t\tconst passage = _passages[name];\n\n\t\t\tif (predicate(passage)) {\n\t\t\t\tresults.push(passage);\n\t\t\t}\n\t\t});\n\n\t\t// For v3.\n\t\t// /* eslint-disable no-nested-ternary */\n\t\t// // QUESTION: Do we really need to sort the list?\n\t\t// results.sort((a, b) => a.title === b.title ? 0 : a.title < b.title ? -1 : +1);\n\t\t// /* eslint-enable no-nested-ternary */\n\n\t\t/* legacy */\n\t\t/* eslint-disable eqeqeq, no-nested-ternary, max-len */\n\t\tresults.sort((a, b) => a[sortKey] == b[sortKey] ? 0 : a[sortKey] < b[sortKey] ? -1 : +1); // lazy equality for null\n\t\t/* eslint-enable eqeqeq, no-nested-ternary, max-len */\n\t\t/* /legacy */\n\n\t\treturn results;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t// Story Functions.\n\t\tload  : { value : storyLoad },\n\t\tinit  : { value : storyInit },\n\t\ttitle : { get : storyTitle },\n\t\tdomId : { get : storyDomId },\n\t\tifId  : { get : storyIfId },\n\n\t\t// Passage Functions.\n\t\tadd              : { value : passagesAdd },\n\t\thas              : { value : passagesHas },\n\t\tget              : { value : passagesGet },\n\t\tgetAllRegular    : { value : passagesGetAllRegular },\n\t\tgetAllScript     : { value : passagesGetAllScript },\n\t\tgetAllStylesheet : { value : passagesGetAllStylesheet },\n\t\tgetAllWidget     : { value : passagesGetAllWidget },\n\t\tlookup           : { value : passagesLookup },\n\t\tlookupWith       : { value : passagesLookupWith }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tui.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, Has, L10n, Save, Setting, State, Story, Util, Wikifier, Config, errorPrologRegExp,\n\t       settings\n*/\n\nvar UI = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Core.\n\t*******************************************************************************************************************/\n\tfunction uiAssembleLinkList(passage, listEl) {\n\t\tlet list = listEl;\n\n\t\t// Cache the values of `Config.debug` and `Config.cleanupWikifierOutput`,\n\t\t// then disable them during this method's run.\n\t\tconst debugState = Config.debug;\n\t\tconst cleanState = Config.cleanupWikifierOutput;\n\t\tConfig.debug = false;\n\t\tConfig.cleanupWikifierOutput = false;\n\n\t\ttry {\n\t\t\tif (list == null) { // lazy equality for null\n\t\t\t\tlist = document.createElement('ul');\n\t\t\t}\n\n\t\t\t// Wikify the content of the given source passage into a fragment.\n\t\t\tconst frag = document.createDocumentFragment();\n\t\t\tnew Wikifier(frag, Story.get(passage).processText().trim());\n\n\t\t\t// Gather the text of any error elements within the fragment…\n\t\t\tconst errors = [...frag.querySelectorAll('.error')]\n\t\t\t\t.map(errEl => errEl.textContent.replace(errorPrologRegExp, ''));\n\n\t\t\t// …and throw an exception, if there were any errors.\n\t\t\tif (errors.length > 0) {\n\t\t\t\tthrow new Error(errors.join('; '));\n\t\t\t}\n\n\t\t\twhile (frag.hasChildNodes()) {\n\t\t\t\tconst node = frag.firstChild;\n\n\t\t\t\t// Create list items for <a>-element nodes.\n\t\t\t\tif (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toUpperCase() === 'A') {\n\t\t\t\t\tconst li = document.createElement('li');\n\t\t\t\t\tlist.appendChild(li);\n\t\t\t\t\tli.appendChild(node);\n\t\t\t\t}\n\n\t\t\t\t// Discard non-<a>-element nodes.\n\t\t\t\telse {\n\t\t\t\t\tfrag.removeChild(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t// Restore the `Config` settings to their original values.\n\t\t\tConfig.cleanupWikifierOutput = cleanState;\n\t\t\tConfig.debug = debugState;\n\t\t}\n\n\t\treturn list;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUI Functions, Built-ins.\n\t*******************************************************************************************************************/\n\tfunction uiOpenAlert(message, /* options, closeFn */ ...args) {\n\t\tjQuery(Dialog.setup(L10n.get('alertTitle'), 'alert'))\n\t\t\t.append(\n\t\t\t\t  `<p>${message}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"alert-ok\" class=\"ui-close\">${L10n.get(['alertOk', 'ok'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t);\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenJumpto(/* options, closeFn */ ...args) {\n\t\tuiBuildJumpto();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenRestart(/* options, closeFn */ ...args) {\n\t\tuiBuildRestart();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSaves(/* options, closeFn */ ...args) {\n\t\tuiBuildSaves();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenSettings(/* options, closeFn */ ...args) {\n\t\tuiBuildSettings();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiOpenShare(/* options, closeFn */ ...args) {\n\t\tuiBuildShare();\n\t\tDialog.open(...args);\n\t}\n\n\tfunction uiBuildAutoload() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildAutoload()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('autoloadTitle'), 'autoload'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('autoloadPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"autoload-ok\" class=\"ui-close\">${L10n.get(['autoloadOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"autoload-cancel\" class=\"ui-close\">${L10n.get(['autoloadCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t);\n\n\t\t// Add an additional delegated click handler for the `.ui-close` elements to handle autoloading.\n\t\tjQuery(document).one('click.autoload', '.ui-close', ev => {\n\t\t\tconst isAutoloadOk = ev.target.id === 'autoload-ok';\n\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\tif (DEBUG) { console.log(`\\tattempting autoload: \"${Save.autosave.get().title}\"`); }\n\n\t\t\t\tif (!isAutoloadOk || !Save.autosave.load()) {\n\t\t\t\t\tEngine.play(Config.passages.start);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildJumpto() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildJumpto()]'); }\n\n\t\tconst list = document.createElement('ul');\n\n\t\tjQuery(Dialog.setup(L10n.get('jumptoTitle'), 'jumpto list'))\n\t\t\t.append(list);\n\n\t\tconst expired = State.expired.length;\n\n\t\tfor (let i = State.size - 1; i >= 0; --i) {\n\t\t\tif (i === State.activeIndex) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst passage = Story.get(State.history[i].title);\n\n\t\t\tif (passage && passage.tags.includes('bookmark')) {\n\t\t\t\tjQuery(document.createElement('li'))\n\t\t\t\t\t.append(\n\t\t\t\t\t\tjQuery(document.createElement('a'))\n\t\t\t\t\t\t\t.ariaClick({ one : true }, (function (idx) {\n\t\t\t\t\t\t\t\treturn () => jQuery(document).one(':dialogclosed', () => Engine.goTo(idx));\n\t\t\t\t\t\t\t})(i))\n\t\t\t\t\t\t\t.addClass('ui-close')\n\t\t\t\t\t\t\t.text(`${L10n.get('jumptoTurn')} ${expired + i + 1}: ${passage.description()}`)\n\t\t\t\t\t)\n\t\t\t\t\t.appendTo(list);\n\t\t\t}\n\t\t}\n\n\t\tif (!list.hasChildNodes()) {\n\t\t\tjQuery(list).append(`<li><a><em>${L10n.get('jumptoUnavailable')}</em></a></li>`);\n\t\t}\n\t}\n\n\tfunction uiBuildRestart() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildRestart()]'); }\n\n\t\tjQuery(Dialog.setup(L10n.get('restartTitle'), 'restart'))\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  `<p>${L10n.get('restartPrompt')}</p><ul class=\"buttons\">`\n\t\t\t\t+ `<li><button id=\"restart-ok\">${L10n.get(['restartOk', 'ok'])}</button></li>`\n\t\t\t\t+ `<li><button id=\"restart-cancel\" class=\"ui-close\">${L10n.get(['restartCancel', 'cancel'])}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.find('#restart-ok')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#restart-ok' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `Engine.restart()` happens after the dialog\n\t\t\t\thas fully closed.  If we did not, then a race condition could occur, causing display\n\t\t\t\tshenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => Engine.restart());\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildSaves() {\n\t\tconst savesAllowed = typeof Config.saves.isAllowed !== 'function' || Config.saves.isAllowed();\n\n\t\tfunction createActionItem(bId, bClass, bText, bAction) {\n\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t.attr('id', `saves-${bId}`)\n\t\t\t\t.html(bText);\n\n\t\t\tif (bClass) {\n\t\t\t\t$btn.addClass(bClass);\n\t\t\t}\n\n\t\t\tif (bAction) {\n\t\t\t\t$btn.ariaClick(bAction);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('li'))\n\t\t\t\t.append($btn);\n\t\t}\n\n\t\tfunction createSaveList() {\n\t\t\tfunction createButton(bId, bClass, bText, bSlot, bAction) {\n\t\t\t\tconst $btn = jQuery(document.createElement('button'))\n\t\t\t\t\t.attr('id', `saves-${bId}-${bSlot}`)\n\t\t\t\t\t.addClass(bId)\n\t\t\t\t\t.html(bText);\n\n\t\t\t\tif (bClass) {\n\t\t\t\t\t$btn.addClass(bClass);\n\t\t\t\t}\n\n\t\t\t\tif (bAction) {\n\t\t\t\t\tif (bSlot === 'auto') {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelAuto')}`\n\t\t\t\t\t\t}, () => bAction());\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$btn.ariaClick({\n\t\t\t\t\t\t\tlabel : `${bText} ${L10n.get('savesLabelSlot')} ${bSlot + 1}`\n\t\t\t\t\t\t}, () => bAction(bSlot));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$btn.ariaDisabled(true);\n\t\t\t\t}\n\n\t\t\t\treturn $btn;\n\t\t\t}\n\n\t\t\tconst index  = Save.index();\n\t\t\tconst $tbody = jQuery(document.createElement('tbody'));\n\n\t\t\tif (Save.autosave.ok()) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\tjQuery(document.createElement('b'))\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttitle        : L10n.get('savesLabelAuto'),\n\t\t\t\t\t\t'aria-label' : L10n.get('savesLabelAuto')\n\t\t\t\t\t})\n\t\t\t\t\t.text('A') // '\\u25C6' Black Diamond\n\t\t\t\t\t.appendTo($tdSlot);\n\n\t\t\t\tif (index.autosave) {\n\t\t\t\t\t// Add the load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), 'auto', () => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.autosave.load());\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.autosave.title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.autosave.date\n\t\t\t\t\t\t\t\t? `${new Date(index.autosave.date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto', () => {\n\t\t\t\t\t\t\tSave.autosave.delete();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the disabled load button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('load', null, L10n.get('savesLabelLoad'), 'auto')\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), 'auto')\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\tfor (let i = 0, iend = index.slots.length; i < iend; ++i) {\n\t\t\t\tconst $tdSlot = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdLoad = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDesc = jQuery(document.createElement('td'));\n\t\t\t\tconst $tdDele = jQuery(document.createElement('td'));\n\n\t\t\t\t// Add the slot ID.\n\t\t\t\t$tdSlot.append(document.createTextNode(i + 1));\n\n\t\t\t\tif (index.slots[i]) {\n\t\t\t\t\t// Add the save & load buttons.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, Save.slots.save),\n\t\t\t\t\t\tcreateButton('load', 'ui-close', L10n.get('savesLabelLoad'), i, slot => {\n\t\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.slots.load(slot));\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description (title and datestamp).\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.text(index.slots[i].title)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\t\t\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t\t\t.addClass('datestamp')\n\t\t\t\t\t\t.html(\n\t\t\t\t\t\t\tindex.slots[i].date\n\t\t\t\t\t\t\t\t? `${new Date(index.slots[i].date).toLocaleString()}`\n\t\t\t\t\t\t\t\t: `<em>${L10n.get('savesUnknownDate')}</em>`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.appendTo($tdDesc);\n\n\t\t\t\t\t// Add the delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i, slot => {\n\t\t\t\t\t\t\tSave.slots.delete(slot);\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Add the save button.\n\t\t\t\t\t$tdLoad.append(\n\t\t\t\t\t\tcreateButton('save', 'ui-close', L10n.get('savesLabelSave'), i, savesAllowed ? Save.slots.save : null)\n\t\t\t\t\t);\n\n\t\t\t\t\t// Add the description.\n\t\t\t\t\t$tdDesc.addClass('empty').text('\\u2022\\u00a0\\u00a0\\u2022\\u00a0\\u00a0\\u2022');\n\n\t\t\t\t\t// Add the disabled delete button.\n\t\t\t\t\t$tdDele.append(\n\t\t\t\t\t\tcreateButton('delete', null, L10n.get('savesLabelDelete'), i)\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tjQuery(document.createElement('tr'))\n\t\t\t\t\t.append($tdSlot)\n\t\t\t\t\t.append($tdLoad)\n\t\t\t\t\t.append($tdDesc)\n\t\t\t\t\t.append($tdDele)\n\t\t\t\t\t.appendTo($tbody);\n\t\t\t}\n\n\t\t\treturn jQuery(document.createElement('table'))\n\t\t\t\t.attr('id', 'saves-list')\n\t\t\t\t.append($tbody);\n\t\t}\n\n\t\tif (DEBUG) { console.log('[UI/uiBuildSaves()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('savesTitle'), 'saves'));\n\t\tconst savesOk     = Save.ok();\n\n\t\t// Add saves list.\n\t\tif (savesOk) {\n\t\t\t$dialogBody.append(createSaveList());\n\t\t}\n\n\t\t// Add button bar items (export, import, and clear).\n\t\tif (savesOk || Has.fileAPI) {\n\t\t\tconst $btnBar = jQuery(document.createElement('ul'))\n\t\t\t\t.addClass('buttons')\n\t\t\t\t.appendTo($dialogBody);\n\n\t\t\tif (Has.fileAPI) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'export',\n\t\t\t\t\t'ui-close',\n\t\t\t\t\tL10n.get('savesLabelExport'),\n\t\t\t\t\tsavesAllowed ? () => Save.export() : null\n\t\t\t\t));\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'import',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelImport'),\n\t\t\t\t\t() => $dialogBody.find('#saves-import-file').trigger('click')\n\t\t\t\t));\n\n\t\t\t\t// Add the hidden `input[type=file]` element which will be triggered by the `#saves-import` button.\n\t\t\t\tjQuery(document.createElement('input'))\n\t\t\t\t\t.css({\n\t\t\t\t\t\tdisplay    : 'block',\n\t\t\t\t\t\tvisibility : 'hidden',\n\t\t\t\t\t\tposition   : 'fixed',\n\t\t\t\t\t\tleft       : '-9999px',\n\t\t\t\t\t\ttop        : '-9999px',\n\t\t\t\t\t\twidth      : '1px',\n\t\t\t\t\t\theight     : '1px'\n\t\t\t\t\t})\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype          : 'file',\n\t\t\t\t\t\tid            : 'saves-import-file',\n\t\t\t\t\t\ttabindex      : -1,\n\t\t\t\t\t\t'aria-hidden' : true\n\t\t\t\t\t})\n\t\t\t\t\t.on('change', ev => {\n\t\t\t\t\t\tjQuery(document).one(':dialogclosed', () => Save.import(ev));\n\t\t\t\t\t\tDialog.close();\n\t\t\t\t\t})\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t}\n\n\t\t\tif (savesOk) {\n\t\t\t\t$btnBar.append(createActionItem(\n\t\t\t\t\t'clear',\n\t\t\t\t\tnull,\n\t\t\t\t\tL10n.get('savesLabelClear'),\n\t\t\t\t\tSave.autosave.has() || !Save.slots.isEmpty()\n\t\t\t\t\t\t? () => {\n\t\t\t\t\t\t\tSave.clear();\n\t\t\t\t\t\t\tuiBuildSaves();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null\n\t\t\t\t));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tuiOpenAlert(L10n.get('savesIncapable'));\n\t\treturn false;\n\t}\n\n\tfunction uiBuildSettings() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildSettings()]'); }\n\n\t\tconst $dialogBody = jQuery(Dialog.setup(L10n.get('settingsTitle'), 'settings'));\n\n\t\tSetting.forEach(control => {\n\t\t\tif (control.type === Setting.Types.Header) {\n\t\t\t\tconst name     = control.name;\n\t\t\t\tconst id       = Util.slugify(name);\n\t\t\t\tconst $header  = jQuery(document.createElement('div'));\n\t\t\t\tconst $heading = jQuery(document.createElement('h2'));\n\n\t\t\t\t$header\n\t\t\t\t\t.attr('id', `header-body-${id}`)\n\t\t\t\t\t.append($heading)\n\t\t\t\t\t.appendTo($dialogBody);\n\t\t\t\t$heading\n\t\t\t\t\t.attr('id', `header-heading-${id}`)\n\t\t\t\t\t.wiki(name);\n\n\t\t\t\t// Set up the description, if any.\n\t\t\t\tif (control.desc) {\n\t\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t\t.attr('id', `header-desc-${id}`)\n\t\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t\t.appendTo($header);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst name        = control.name;\n\t\t\tconst id          = Util.slugify(name);\n\t\t\tconst $setting    = jQuery(document.createElement('div'));\n\t\t\tconst $label      = jQuery(document.createElement('label'));\n\t\t\tconst $controlBox = jQuery(document.createElement('div'));\n\t\t\tlet $control;\n\n\t\t\t// Set up the label+control wrapper.\n\t\t\tjQuery(document.createElement('div'))\n\t\t\t\t.append($label)\n\t\t\t\t.append($controlBox)\n\t\t\t\t.appendTo($setting);\n\n\t\t\t// Set up the description, if any.\n\t\t\tif (control.desc) {\n\t\t\t\tjQuery(document.createElement('p'))\n\t\t\t\t\t.attr('id', `setting-desc-${id}`)\n\t\t\t\t\t.wiki(control.desc)\n\t\t\t\t\t.appendTo($setting);\n\t\t\t}\n\n\t\t\t// Set up the label.\n\t\t\t$label\n\t\t\t\t.attr({\n\t\t\t\t\tid  : `setting-label-${id}`,\n\t\t\t\t\tfor : `setting-control-${id}` // must be in sync with $control's ID (see below)\n\t\t\t\t})\n\t\t\t\t.wiki(control.label);\n\n\t\t\t// Set up the control.\n\t\t\tif (settings[name] == null) { // lazy equality for null\n\t\t\t\tsettings[name] = control.default;\n\t\t\t}\n\n\t\t\tswitch (control.type) {\n\t\t\tcase Setting.Types.Toggle:\n\t\t\t\t$control = jQuery(document.createElement('button'));\n\n\t\t\t\tif (settings[name]) {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t$control\n\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t}\n\n\t\t\t\t$control.ariaClick(function () {\n\t\t\t\t\tif (settings[name]) {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.removeClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOff'));\n\t\t\t\t\t\tsettings[name] = false;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tjQuery(this)\n\t\t\t\t\t\t\t.addClass('enabled')\n\t\t\t\t\t\t\t.text(L10n.get('settingsOn'));\n\t\t\t\t\t\tsettings[name] = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tSetting.save();\n\n\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\tdefault : control.default\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.List:\n\t\t\t\t$control = jQuery(document.createElement('select'));\n\n\t\t\t\tfor (let i = 0, iend = control.list.length; i < iend; ++i) {\n\t\t\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t\t\t.val(i)\n\t\t\t\t\t\t.text(control.list[i])\n\t\t\t\t\t\t.appendTo($control);\n\t\t\t\t}\n\n\t\t\t\t$control\n\t\t\t\t\t.val(control.list.indexOf(settings[name]))\n\t\t\t\t\t.attr('tabindex', 0)\n\t\t\t\t\t.on('change', function () {\n\t\t\t\t\t\tsettings[name] = control.list[Number(this.value)];\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tlist    : control.list\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\n\t\t\tcase Setting.Types.Range:\n\t\t\t\t$control = jQuery(document.createElement('input'));\n\n\t\t\t\t// NOTE: Setting the value with `<jQuery>.val()` can cause odd behavior\n\t\t\t\t// in Edge if it's called before the type is set, so we use the `value`\n\t\t\t\t// content attribute here to dodge the entire issue.\n\t\t\t\t$control\n\t\t\t\t\t.attr({\n\t\t\t\t\t\ttype     : 'range',\n\t\t\t\t\t\tmin      : control.min,\n\t\t\t\t\t\tmax      : control.max,\n\t\t\t\t\t\tstep     : control.step,\n\t\t\t\t\t\tvalue    : settings[name],\n\t\t\t\t\t\ttabindex : 0\n\t\t\t\t\t})\n\t\t\t\t\t.on('change input', function () {\n\t\t\t\t\t\tsettings[name] = Number(this.value);\n\t\t\t\t\t\tSetting.save();\n\n\t\t\t\t\t\tif (control.hasOwnProperty('onChange')) {\n\t\t\t\t\t\t\tcontrol.onChange.call({\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tvalue   : settings[name],\n\t\t\t\t\t\t\t\tdefault : control.default,\n\t\t\t\t\t\t\t\tmin     : control.min,\n\t\t\t\t\t\t\t\tmax     : control.max,\n\t\t\t\t\t\t\t\tstep    : control.step\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.on('keypress', ev => {\n\t\t\t\t\t\tif (ev.which === 13) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$control.trigger('change');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$control\n\t\t\t\t.attr('id', `setting-control-${id}`)\n\t\t\t\t.appendTo($controlBox);\n\n\t\t\t$setting\n\t\t\t\t.attr('id', `setting-body-${id}`)\n\t\t\t\t.appendTo($dialogBody);\n\t\t});\n\n\t\t// Add the button bar.\n\t\t$dialogBody\n\t\t\t.append(\n\t\t\t\t  '<ul class=\"buttons\">'\n\t\t\t\t+     `<li><button id=\"settings-ok\" class=\"ui-close\">${L10n.get(['settingsOk', 'ok'])}</button></li>`\n\t\t\t\t+     `<li><button id=\"settings-reset\">${L10n.get('settingsReset')}</button></li>`\n\t\t\t\t+ '</ul>'\n\t\t\t)\n\t\t\t.find('#settings-reset')\n\t\t\t/*\n\t\t\t\tInstead of adding '.ui-close' to '#settings-reset' (to receive the use of the default\n\t\t\t\tdelegated dialog close handler), we set up a special case close handler here.  We\n\t\t\t\tdo this to ensure that the invocation of `window.location.reload()` happens after the\n\t\t\t\tdialog has fully closed.  If we did not, then a race condition could occur, causing\n\t\t\t\tdisplay shenanigans.\n\t\t\t*/\n\t\t\t.ariaClick({ one : true }, () => {\n\t\t\t\tjQuery(document).one(':dialogclosed', () => {\n\t\t\t\t\tSetting.reset();\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t});\n\t\t\t\tDialog.close();\n\t\t\t});\n\n\t\treturn true;\n\t}\n\n\tfunction uiBuildShare() {\n\t\tif (DEBUG) { console.log('[UI/uiBuildShare()]'); }\n\n\t\ttry {\n\t\t\tjQuery(Dialog.setup(L10n.get('shareTitle'), 'share list'))\n\t\t\t\t.append(uiAssembleLinkList('StoryShare'));\n\t\t}\n\t\tcatch (ex) {\n\t\t\tconsole.error(ex);\n\t\t\tAlert.error('StoryShare', ex.message);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tUI Functions, Core.\n\t\t*/\n\t\tassembleLinkList : { value : uiAssembleLinkList },\n\n\t\t/*\n\t\t\tUI Functions, Built-ins.\n\t\t*/\n\t\talert         : { value : uiOpenAlert },\n\t\tjumpto        : { value : uiOpenJumpto },\n\t\trestart       : { value : uiOpenRestart },\n\t\tsaves         : { value : uiOpenSaves },\n\t\tsettings      : { value : uiOpenSettings },\n\t\tshare         : { value : uiOpenShare },\n\t\tbuildAutoload : { value : uiBuildAutoload },\n\t\tbuildJumpto   : { value : uiBuildJumpto },\n\t\tbuildRestart  : { value : uiBuildRestart },\n\t\tbuildSaves    : { value : uiBuildSaves },\n\t\tbuildSettings : { value : uiBuildSettings },\n\t\tbuildShare    : { value : uiBuildShare },\n\n\t\t/*\n\t\t\tLegacy Aliases.\n\t\t*/\n\t\t// `UIBar` methods.\n\t\t/* global UIBar */\n\t\tstow                     : { value : () => UIBar.stow() },\n\t\tunstow                   : { value : () => UIBar.unstow() },\n\t\tsetStoryElements         : { value : () => UIBar.update() },\n\t\t// `Dialog` methods.\n\t\tisOpen                   : { value : (...args) => Dialog.isOpen(...args) },\n\t\tbody                     : { value : () => Dialog.body() },\n\t\tsetup                    : { value : (...args) => Dialog.setup(...args) },\n\t\taddClickHandler          : { value : (...args) => Dialog.addClickHandler(...args) },\n\t\topen                     : { value : (...args) => Dialog.open(...args) },\n\t\tclose                    : { value : (...args) => Dialog.close(...args) },\n\t\tresize                   : { value : () => Dialog.resize() },\n\t\t// Deprecated method names.\n\t\tbuildDialogAutoload      : { value : uiBuildAutoload },\n\t\tbuildDialogJumpto        : { value : uiBuildJumpto },\n\t\tbuildDialogRestart       : { value : uiBuildRestart },\n\t\tbuildDialogSaves         : { value : uiBuildSaves },\n\t\tbuildDialogSettings      : { value : uiBuildSettings },\n\t\tbuildDialogShare         : { value : uiBuildShare },\n\t\tbuildLinkListFromPassage : { value : uiAssembleLinkList }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tuibar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Dialog, Engine, L10n, Setting, State, Story, UI, Config, setDisplayTitle, setPageElement\n*/\n\nvar UIBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// UI bar element cache.\n\tlet _$uiBar = null;\n\n\n\t/*******************************************************************************\n\t\tUI Bar Functions.\n\t*******************************************************************************/\n\n\tfunction uiBarDestroy() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarDestroy()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Hide the UI bar.\n\t\t_$uiBar.hide();\n\n\t\t// Remove its namespaced events.\n\t\tjQuery(document).off('.ui-bar');\n\n\t\t// Remove its styles.\n\t\tjQuery(document.head).find('#style-ui-bar').remove();\n\n\t\t// Remove it from the DOM.\n\t\t_$uiBar.remove();\n\n\t\t// Drop the reference to the element.\n\t\t_$uiBar = null;\n\t}\n\n\tfunction uiBarHide() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.hide();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarInit() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarInit()]'); }\n\n\t\tif (document.getElementById('ui-bar')) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Generate the UI bar elements.\n\t\tconst $elems = (() => {\n\t\t\tconst toggleLabel   = L10n.get('uiBarToggle');\n\t\t\tconst backwardLabel = L10n.get('uiBarBackward');\n\t\t\tconst jumptoLabel   = L10n.get('uiBarJumpto');\n\t\t\tconst forwardLabel  = L10n.get('uiBarForward');\n\n\t\t\treturn jQuery(document.createDocumentFragment())\n\t\t\t\t.append(\n\t\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t\t  '<div id=\"ui-bar\">'\n\t\t\t\t\t+     '<div id=\"ui-bar-tray\">'\n\t\t\t\t\t+         `<button id=\"ui-bar-toggle\" tabindex=\"0\" title=\"${toggleLabel}\" aria-label=\"${toggleLabel}\"></button>`\n\t\t\t\t\t+         '<div id=\"ui-bar-history\">'\n\t\t\t\t\t+             `<button id=\"history-backward\" tabindex=\"0\" title=\"${backwardLabel}\" aria-label=\"${backwardLabel}\">\\uE821</button>`\n\t\t\t\t\t+             `<button id=\"history-jumpto\" tabindex=\"0\" title=\"${jumptoLabel}\" aria-label=\"${jumptoLabel}\">\\uE839</button>`\n\t\t\t\t\t+             `<button id=\"history-forward\" tabindex=\"0\" title=\"${forwardLabel}\" aria-label=\"${forwardLabel}\">\\uE822</button>`\n\t\t\t\t\t+         '</div>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+     '<div id=\"ui-bar-body\">'\n\t\t\t\t\t+         '<header id=\"title\" role=\"banner\">'\n\t\t\t\t\t+             '<div id=\"story-banner\"></div>'\n\t\t\t\t\t+             '<h1 id=\"story-title\"></h1>'\n\t\t\t\t\t+             '<div id=\"story-subtitle\"></div>'\n\t\t\t\t\t+             '<div id=\"story-title-separator\"></div>'\n\t\t\t\t\t+             '<p id=\"story-author\"></p>'\n\t\t\t\t\t+         '</header>'\n\t\t\t\t\t+         '<div id=\"story-caption\"></div>'\n\t\t\t\t\t+         '<nav id=\"menu\" role=\"navigation\">'\n\t\t\t\t\t+             '<ul id=\"menu-story\"></ul>'\n\t\t\t\t\t+             '<ul id=\"menu-core\">'\n\t\t\t\t\t+                 `<li id=\"menu-item-saves\"><a tabindex=\"0\">${L10n.get('savesTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-settings\"><a tabindex=\"0\">${L10n.get('settingsTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-restart\"><a tabindex=\"0\">${L10n.get('restartTitle')}</a></li>`\n\t\t\t\t\t+                 `<li id=\"menu-item-share\"><a tabindex=\"0\">${L10n.get('shareTitle')}</a></li>`\n\t\t\t\t\t+             '</ul>'\n\t\t\t\t\t+         '</nav>'\n\t\t\t\t\t+     '</div>'\n\t\t\t\t\t+ '</div>'\n\t\t\t\t\t/* eslint-enable max-len */\n\t\t\t\t);\n\t\t})();\n\n\t\t/*\n\t\t\tCache the UI bar element, since its going to be used often.\n\n\t\t\tNOTE: We rewrap the element itself, rather than simply using the result\n\t\t\tof `find()`, so that we cache an uncluttered jQuery-wrapper (i.e. `context`\n\t\t\trefers to the element and there is no `prevObject`).\n\t\t*/\n\t\t_$uiBar = jQuery($elems.find('#ui-bar').get(0));\n\n\t\t// Insert the UI bar elements into the page before the main script.\n\t\t$elems.insertBefore('body>script#script-sugarcube');\n\n\t\t// Set up the UI bar's global event handlers.\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history-backward/-forward buttons.\n\t\t\t.on(':historyupdate.ui-bar', (($backward, $forward) => () => {\n\t\t\t\t$backward.ariaDisabled(State.length < 2);\n\t\t\t\t$forward.ariaDisabled(State.length === State.size);\n\t\t\t})(jQuery('#history-backward'), jQuery('#history-forward')));\n\t}\n\n\tfunction uiBarIsHidden() {\n\t\treturn _$uiBar && _$uiBar.css('display') === 'none';\n\t}\n\n\tfunction uiBarIsStowed() {\n\t\treturn _$uiBar && _$uiBar.hasClass('stowed');\n\t}\n\n\tfunction uiBarShow() {\n\t\tif (_$uiBar) {\n\t\t\t_$uiBar.show();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarStart() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarStart()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the #ui-bar's initial state.\n\t\tif (\n\t\t\ttypeof Config.ui.stowBarInitially === 'boolean'\n\t\t\t\t? Config.ui.stowBarInitially\n\t\t\t\t: jQuery(window).width() <= Config.ui.stowBarInitially\n\t\t) {\n\t\t\tuiBarStow(true);\n\t\t}\n\n\t\t// Set up the #ui-bar-toggle and #ui-bar-history widgets.\n\t\tjQuery('#ui-bar-toggle')\n\t\t\t.ariaClick({\n\t\t\t\tlabel : L10n.get('uiBarToggle')\n\t\t\t}, () => _$uiBar.toggleClass('stowed'));\n\n\t\tif (Config.history.controls) {\n\t\t\tjQuery('#history-backward')\n\t\t\t\t.ariaDisabled(State.length < 2)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarBackward')\n\t\t\t\t}, () => Engine.backward());\n\n\t\t\tif (Story.lookup('tags', 'bookmark').length > 0) {\n\t\t\t\tjQuery('#history-jumpto')\n\t\t\t\t\t.ariaClick({\n\t\t\t\t\t\tlabel : L10n.get('uiBarJumpto')\n\t\t\t\t\t}, () => UI.jumpto());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tjQuery('#history-jumpto').remove();\n\t\t\t}\n\n\t\t\tjQuery('#history-forward')\n\t\t\t\t.ariaDisabled(State.length === State.size)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tlabel : L10n.get('uiBarForward')\n\t\t\t\t}, () => Engine.forward());\n\t\t}\n\t\telse {\n\t\t\tjQuery('#ui-bar-history').remove();\n\t\t}\n\n\t\t// Set up the story display title.\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\telse {\n\t\t\tif (TWINE1) { // for Twine 1\n\t\t\t\tsetPageElement('story-title', 'StoryTitle', Story.title);\n\t\t\t}\n\t\t\telse { // for Twine 2\n\t\t\t\tjQuery('#story-title').text(Story.title);\n\t\t\t}\n\t\t}\n\n\t\t// Set up the dynamic page elements.\n\t\tif (!Story.has('StoryCaption')) {\n\t\t\tjQuery('#story-caption').remove();\n\t\t}\n\n\t\tif (!Story.has('StoryMenu')) {\n\t\t\tjQuery('#menu-story').remove();\n\t\t}\n\n\t\tif (!Config.ui.updateStoryElements) {\n\t\t\t// We only need to set the story elements here if `Config.ui.updateStoryElements`\n\t\t\t// is falsy, since otherwise they will be set by `Engine.play()`.\n\t\t\tuiBarUpdate();\n\t\t}\n\n\t\t// Set up the Saves menu item.\n\t\tjQuery('#menu-item-saves a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildSaves();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('savesTitle'));\n\n\t\t// Set up the Settings menu item.\n\t\tif (!Setting.isEmpty()) {\n\t\t\tjQuery('#menu-item-settings a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildSettings();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('settingsTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-settings').remove();\n\t\t}\n\n\t\t// Set up the Restart menu item.\n\t\tjQuery('#menu-item-restart a')\n\t\t\t.ariaClick(ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tUI.buildRestart();\n\t\t\t\tDialog.open();\n\t\t\t})\n\t\t\t.text(L10n.get('restartTitle'));\n\n\t\t// Set up the Share menu item.\n\t\tif (Story.has('StoryShare')) {\n\t\t\tjQuery('#menu-item-share a')\n\t\t\t\t.ariaClick(ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tUI.buildShare();\n\t\t\t\t\tDialog.open();\n\t\t\t\t})\n\t\t\t\t.text(L10n.get('shareTitle'));\n\t\t}\n\t\telse {\n\t\t\tjQuery('#menu-item-share').remove();\n\t\t}\n\t}\n\n\tfunction uiBarStow(noAnimation) {\n\t\tif (_$uiBar && !_$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.addClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUnstow(noAnimation) {\n\t\tif (_$uiBar && _$uiBar.hasClass('stowed')) {\n\t\t\tlet $story;\n\n\t\t\tif (noAnimation) {\n\t\t\t\t$story = jQuery('#story');\n\t\t\t\t$story.addClass('no-transition');\n\t\t\t\t_$uiBar.addClass('no-transition');\n\t\t\t}\n\n\t\t\t_$uiBar.removeClass('stowed');\n\n\t\t\tif (noAnimation) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$story.removeClass('no-transition');\n\t\t\t\t\t_$uiBar.removeClass('no-transition');\n\t\t\t\t}, Engine.minDomActionDelay);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tfunction uiBarUpdate() {\n\t\tif (DEBUG) { console.log('[UIBar/uiBarUpdate()]'); }\n\n\t\tif (!_$uiBar) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up the (non-navigation) dynamic page elements.\n\t\tsetPageElement('story-banner', 'StoryBanner');\n\t\tif (Story.has('StoryDisplayTitle')) {\n\t\t\tsetDisplayTitle(Story.get('StoryDisplayTitle').processText());\n\t\t}\n\t\tsetPageElement('story-subtitle', 'StorySubtitle');\n\t\tsetPageElement('story-author', 'StoryAuthor');\n\t\tsetPageElement('story-caption', 'StoryCaption');\n\n\t\t// Set up the #menu-story items.\n\t\tconst menuStory = document.getElementById('menu-story');\n\n\t\tif (menuStory !== null) {\n\t\t\tjQuery(menuStory).empty();\n\n\t\t\tif (Story.has('StoryMenu')) {\n\t\t\t\ttry {\n\t\t\t\t\tUI.assembleLinkList('StoryMenu', menuStory);\n\t\t\t\t}\n\t\t\t\tcatch (ex) {\n\t\t\t\t\tconsole.error(ex);\n\t\t\t\t\tAlert.error('StoryMenu', ex.message);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*******************************************************************************\n\t\tObject Exports.\n\t*******************************************************************************/\n\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tdestroy  : { value : uiBarDestroy },\n\t\thide     : { value : uiBarHide },\n\t\tinit     : { value : uiBarInit },\n\t\tisHidden : { value : uiBarIsHidden },\n\t\tisStowed : { value : uiBarIsStowed },\n\t\tshow     : { value : uiBarShow },\n\t\tstart    : { value : uiBarStart },\n\t\tstow     : { value : uiBarStow },\n\t\tunstow   : { value : uiBarUnstow },\n\t\tupdate   : { value : uiBarUpdate },\n\n\t\t// Legacy Functions.\n\t\tsetStoryElements : { value : uiBarUpdate }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tdebugbar.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal DebugView, Engine, L10n, Patterns, State, Util, session\n*/\n\nvar DebugBar = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\tconst _variableRe   = new RegExp(`^${Patterns.variable}$`);\n\tconst _numericKeyRe = /^\\d+$/;\n\tconst _watchList    = [];\n\tlet _$debugBar   = null;\n\tlet _$watchBody  = null;\n\tlet _$watchList  = null;\n\tlet _$turnSelect = null;\n\tlet _stowed      = true;\n\n\n\t/*******************************************************************************************************************\n\t\tDebug Bar Functions.\n\t*******************************************************************************************************************/\n\tfunction debugBarInit() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarInit()]'); }\n\n\t\t/*\n\t\t\tGenerate the debug bar elements and append them to the `<body>`.\n\t\t*/\n\t\tconst barToggleLabel   = L10n.get('debugBarToggle');\n\t\tconst watchAddLabel    = L10n.get('debugBarAddWatch');\n\t\tconst watchAllLabel    = L10n.get('debugBarWatchAll');\n\t\tconst watchNoneLabel   = L10n.get('debugBarWatchNone');\n\t\tconst watchToggleLabel = L10n.get('debugBarWatchToggle');\n\t\tconst viewsToggleLabel = L10n.get('debugBarViewsToggle');\n\n\t\tjQuery(document.createDocumentFragment())\n\t\t\t.append(\n\t\t\t\t/* eslint-disable max-len */\n\t\t\t\t  '<div id=\"debug-bar\">'\n\t\t\t\t+     '<div id=\"debug-bar-watch\">'\n\t\t\t\t+         `<div>${L10n.get('debugBarNoWatches')}</div>>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-toggle\" tabindex=\"0\" title=\"${watchToggleLabel}\" aria-label=\"${watchToggleLabel}\">${L10n.get('debugBarLabelWatch')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-watch-label\" for=\"debug-bar-watch-input\">${L10n.get('debugBarLabelAdd')}</label>`\n\t\t\t\t+         '<input id=\"debug-bar-watch-input\" name=\"debug-bar-watch-input\" type=\"text\" list=\"debug-bar-watch-list\" tabindex=\"0\">'\n\t\t\t\t+         '<datalist id=\"debug-bar-watch-list\" aria-hidden=\"true\" hidden=\"hidden\"></datalist>'\n\t\t\t\t+         `<button id=\"debug-bar-watch-add\" tabindex=\"0\" title=\"${watchAddLabel}\" aria-label=\"${watchAddLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-all\" tabindex=\"0\" title=\"${watchAllLabel}\" aria-label=\"${watchAllLabel}\"></button>`\n\t\t\t\t+         `<button id=\"debug-bar-watch-none\" tabindex=\"0\" title=\"${watchNoneLabel}\" aria-label=\"${watchNoneLabel}\"></button>`\n\t\t\t\t+     '</div>'\n\t\t\t\t+     '<div>'\n\t\t\t\t+         `<button id=\"debug-bar-views-toggle\" tabindex=\"0\" title=\"${viewsToggleLabel}\" aria-label=\"${viewsToggleLabel}\">${L10n.get('debugBarLabelViews')}</button>`\n\t\t\t\t+         `<label id=\"debug-bar-turn-label\" for=\"debug-bar-turn-select\">${L10n.get('debugBarLabelTurn')}</label>`\n\t\t\t\t+         '<select id=\"debug-bar-turn-select\" tabindex=\"0\"></select>'\n\t\t\t\t+     '</div>'\n\t\t\t\t+     `<button id=\"debug-bar-toggle\" tabindex=\"0\" title=\"${barToggleLabel}\" aria-label=\"${barToggleLabel}\"></button>`\n\t\t\t\t+ '</div>'\n\t\t\t\t+ '<div id=\"debug-bar-hint\"></div>'\n\t\t\t\t/* eslint-enable max-len */\n\t\t\t)\n\t\t\t.appendTo('body');\n\n\t\t/*\n\t\t\tCache various oft used elements.\n\n\t\t\tNOTE: We rewrap the elements themselves, rather than simply using\n\t\t\tthe results of `find()`, so that we cache uncluttered jQuery-wrappers\n\t\t\t(i.e. `context` refers to the elements and there is no `prevObject`).\n\t\t*/\n\t\t_$debugBar   = jQuery('#debug-bar');\n\t\t_$watchBody  = jQuery(_$debugBar.find('#debug-bar-watch').get(0));\n\t\t_$watchList  = jQuery(_$debugBar.find('#debug-bar-watch-list').get(0));\n\t\t_$turnSelect = jQuery(_$debugBar.find('#debug-bar-turn-select').get(0));\n\n\t\tconst $barToggle   = jQuery(_$debugBar.find('#debug-bar-toggle').get(0));\n\t\tconst $watchToggle = jQuery(_$debugBar.find('#debug-bar-watch-toggle').get(0));\n\t\tconst $watchInput  = jQuery(_$debugBar.find('#debug-bar-watch-input').get(0));\n\t\tconst $watchAdd    = jQuery(_$debugBar.find('#debug-bar-watch-add').get(0));\n\t\tconst $watchAll    = jQuery(_$debugBar.find('#debug-bar-watch-all').get(0));\n\t\tconst $watchNone   = jQuery(_$debugBar.find('#debug-bar-watch-none').get(0));\n\t\tconst $viewsToggle = jQuery(_$debugBar.find('#debug-bar-views-toggle').get(0));\n\n\t\t/*\n\t\t\tSet up the debug bar's local event handlers.\n\t\t*/\n\t\t$barToggle\n\t\t\t.ariaClick(debugBarToggle);\n\t\t$watchToggle\n\t\t\t.ariaClick(debugBarWatchToggle);\n\t\t$watchInput\n\t\t\t.on(':addwatch', function () {\n\t\t\t\tdebugBarWatchAdd(this.value.trim());\n\t\t\t\tthis.value = '';\n\t\t\t})\n\t\t\t.on('keypress', ev => {\n\t\t\t\tif (ev.which === 13) { // 13 is Return/Enter\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t$watchInput.trigger(':addwatch');\n\t\t\t\t}\n\t\t\t});\n\t\t$watchAdd\n\t\t\t.ariaClick(() => $watchInput.trigger(':addwatch'));\n\t\t$watchAll\n\t\t\t.ariaClick(debugBarWatchAddAll);\n\t\t$watchNone\n\t\t\t.ariaClick(debugBarWatchClear);\n\t\t_$turnSelect\n\t\t\t.on('change', function () {\n\t\t\t\tEngine.goTo(Number(this.value));\n\t\t\t});\n\t\t$viewsToggle\n\t\t\t.ariaClick(() => {\n\t\t\t\tDebugView.toggle();\n\t\t\t\t_updateSession();\n\t\t\t});\n\n\t\t/*\n\t\t\tSet up the debug bar's global event handlers.\n\t\t*/\n\t\tjQuery(document)\n\t\t\t// Set up a handler for the history select.\n\t\t\t.on(':historyupdate.debug-bar', _updateTurnSelect)\n\t\t\t// Set up a handler for the variables watch.\n\t\t\t.on(':passageend.debug-bar', () => {\n\t\t\t\t_updateWatchBody();\n\t\t\t\t_updateWatchList();\n\t\t\t})\n\t\t\t// Set up a handler for engine resets to clear the active debug session.\n\t\t\t.on(':enginerestart.debug-bar', _clearSession);\n\n\t\t/*\n\t\t\tInitially enable debug views if there's no active debug session.\n\t\t*/\n\t\tif (!_hasSession()) {\n\t\t\tDebugView.enable();\n\t\t}\n\t}\n\n\tfunction debugBarStart() {\n\t\tif (DEBUG) { console.log('[DebugBar/debugBarStart()]'); }\n\n\t\t// Attempt to restore an existing session.\n\t\t_restoreSession();\n\n\t\t// Update the UI.\n\t\t_updateBar();\n\t\t_updateTurnSelect();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t}\n\n\tfunction debugBarIsStowed() {\n\t\treturn _stowed;\n\t}\n\n\tfunction debugBarStow() {\n\t\t_debugBarStowNoUpdate();\n\t\t_stowed = true;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarUnstow() {\n\t\t_debugBarUnstowNoUpdate();\n\t\t_stowed = false;\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarToggle() {\n\t\tif (_stowed) {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarStow();\n\t\t}\n\t}\n\n\tfunction debugBarWatchAdd(varName) {\n\t\tif (!_variableRe.test(varName)) {\n\t\t\treturn;\n\t\t}\n\n\t\t_watchList.pushUnique(varName);\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchAddAll() {\n\t\tObject.keys(State.variables).map(name => _watchList.pushUnique(`$${name}`));\n\t\tObject.keys(State.temporary).map(name => _watchList.pushUnique(`_${name}`));\n\n\t\t_watchList.sort();\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchClear() {\n\t\tfor (let i = _watchList.length - 1; i >= 0; --i) {\n\t\t\t_watchList.pop();\n\t\t}\n\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDelete(varName) {\n\t\t_watchList.delete(varName);\n\t\t_updateWatchBody();\n\t\t_updateWatchList();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchDisable() {\n\t\t_debugBarWatchDisableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchEnable() {\n\t\t_debugBarWatchEnableNoUpdate();\n\t\t_updateSession();\n\t}\n\n\tfunction debugBarWatchIsEnabled() {\n\t\treturn !_$watchBody.attr('hidden');\n\t}\n\n\tfunction debugBarWatchToggle() {\n\t\tif (_$watchBody.attr('hidden')) {\n\t\t\tdebugBarWatchEnable();\n\t\t}\n\t\telse {\n\t\t\tdebugBarWatchDisable();\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tUtility Functions.\n\t*******************************************************************************************************************/\n\tfunction _debugBarStowNoUpdate() {\n\t\t_$debugBar.css('right', `-${_$debugBar.outerWidth()}px`);\n\t}\n\n\tfunction _debugBarUnstowNoUpdate() {\n\t\t_$debugBar.css('right', 0);\n\t}\n\n\tfunction _debugBarWatchDisableNoUpdate() {\n\t\t_$watchBody.attr({\n\t\t\t'aria-hidden' : true,\n\t\t\thidden        : 'hidden'\n\t\t});\n\t}\n\n\tfunction _debugBarWatchEnableNoUpdate() {\n\t\t_$watchBody.removeAttr('aria-hidden hidden');\n\t}\n\n\tfunction _clearSession() {\n\t\tsession.delete('debugState');\n\t}\n\n\tfunction _hasSession() {\n\t\treturn session.has('debugState');\n\t}\n\n\tfunction _restoreSession() {\n\t\tif (!_hasSession()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst debugState = session.get('debugState');\n\n\t\t_stowed = debugState.stowed;\n\n\t\t_watchList.push(...debugState.watchList);\n\n\t\tif (debugState.watchEnabled) {\n\t\t\t_debugBarWatchEnableNoUpdate();\n\t\t}\n\t\telse {\n\t\t\t_debugBarWatchDisableNoUpdate();\n\t\t}\n\n\t\tif (debugState.viewsEnabled) {\n\t\t\tDebugView.enable();\n\t\t}\n\t\telse {\n\t\t\tDebugView.disable();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tfunction _updateSession() {\n\t\tsession.set('debugState', {\n\t\t\tstowed       : _stowed,\n\t\t\twatchList    : _watchList,\n\t\t\twatchEnabled : debugBarWatchIsEnabled(),\n\t\t\tviewsEnabled : DebugView.isEnabled()\n\t\t});\n\t}\n\n\tfunction _updateBar() {\n\t\tif (_stowed) {\n\t\t\tdebugBarStow();\n\t\t}\n\t\telse {\n\t\t\tdebugBarUnstow();\n\t\t}\n\t}\n\n\tfunction _updateWatchBody() {\n\t\tif (_watchList.length === 0) {\n\t\t\t_$watchBody\n\t\t\t\t.empty()\n\t\t\t\t.append(`<div>${L10n.get('debugBarNoWatches')}</div>`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst delLabel = L10n.get('debugBarDeleteWatch');\n\t\tconst $table   = jQuery(document.createElement('table'));\n\t\tconst $tbody   = jQuery(document.createElement('tbody'));\n\n\t\tfor (let i = 0, len = _watchList.length; i < len; ++i) {\n\t\t\tconst varName = _watchList[i];\n\t\t\tconst varKey  = varName.slice(1);\n\t\t\tconst store   = varName[0] === '$' ? State.variables : State.temporary;\n\t\t\tconst $row    = jQuery(document.createElement('tr'));\n\t\t\tconst $delBtn = jQuery(document.createElement('button'));\n\t\t\tconst $code   = jQuery(document.createElement('code'));\n\n\t\t\t$delBtn\n\t\t\t\t.addClass('watch-delete')\n\t\t\t\t.attr('data-name', varName)\n\t\t\t\t.ariaClick({\n\t\t\t\t\tone   : true,\n\t\t\t\t\tlabel : delLabel\n\t\t\t\t}, () => debugBarWatchDelete(varName));\n\t\t\t$code\n\t\t\t\t.text(_toWatchString(store[varKey]));\n\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($delBtn)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.text(varName)\n\t\t\t\t.appendTo($row);\n\t\t\tjQuery(document.createElement('td'))\n\t\t\t\t.append($code)\n\t\t\t\t.appendTo($row);\n\t\t\t$row\n\t\t\t\t.appendTo($tbody);\n\t\t}\n\n\t\t$table\n\t\t\t.append($tbody);\n\t\t_$watchBody\n\t\t\t.empty()\n\t\t\t.append($table);\n\t}\n\n\tfunction _updateWatchList() {\n\t\tconst svn = Object.keys(State.variables);\n\t\tconst tvn = Object.keys(State.temporary);\n\n\t\tif (svn.length === 0 && tvn.length === 0) {\n\t\t\t_$watchList.empty();\n\t\t\treturn;\n\t\t}\n\n\t\tconst names   = [...svn.map(name => `$${name}`), ...tvn.map(name => `_${name}`)].sort();\n\t\tconst options = document.createDocumentFragment();\n\n\t\tnames.delete(_watchList);\n\n\t\tfor (let i = 0, len = names.length; i < len; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(names[i])\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$watchList\n\t\t\t.empty()\n\t\t\t.append(options);\n\t}\n\n\tfunction _updateTurnSelect() {\n\t\tconst histLen = State.size;\n\t\tconst expLen  = State.expired.length;\n\t\tconst options = document.createDocumentFragment();\n\n\t\tfor (let i = 0; i < histLen; ++i) {\n\t\t\tjQuery(document.createElement('option'))\n\t\t\t\t.val(i)\n\t\t\t\t.text(`${expLen + i + 1}. ${Util.escape(State.history[i].title)}`)\n\t\t\t\t.appendTo(options);\n\t\t}\n\n\t\t_$turnSelect\n\t\t\t.empty()\n\t\t\t.ariaDisabled(histLen < 2)\n\t\t\t.append(options)\n\t\t\t.val(State.activeIndex);\n\t}\n\n\tfunction _toWatchString(value) {\n\t\t/*\n\t\t\tHandle the `null` primitive.\n\t\t*/\n\t\tif (value === null) {\n\t\t\treturn 'null';\n\t\t}\n\n\t\t/*\n\t\t\tHandle the rest of the primitives and functions.\n\t\t*/\n\t\tswitch (typeof value) {\n\t\tcase 'number':\n\t\t\tif (Number.isNaN(value)) {\n\t\t\t\treturn 'NaN';\n\t\t\t}\n\t\t\telse if (!Number.isFinite(value)) {\n\t\t\t\treturn 'Infinity';\n\t\t\t}\n\t\t\t/* falls through */\n\t\tcase 'boolean':\n\t\tcase 'symbol':\n\t\tcase 'undefined':\n\t\t\treturn String(value);\n\n\t\tcase 'string':\n\t\t\treturn JSON.stringify(value);\n\n\t\tcase 'function':\n\t\t\treturn 'Function';\n\t\t}\n\n\t\tconst objType = Util.toStringTag(value);\n\n\t\t// /*\n\t\t// \tHandle instances of the primitive exemplar objects (`Boolean`, `Number`, `String`).\n\t\t// */\n\t\t// if (objType === 'Boolean') {\n\t\t// \treturn `Boolean\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'Number') {\n\t\t// \treturn `Number\\u202F{${String(value)}}`;\n\t\t// }\n\t\t// if (objType === 'String') {\n\t\t// \treturn `String\\u202F{\"${String(value)}\"}`;\n\t\t// }\n\n\t\t/*\n\t\t\tHandle `Date` objects.\n\t\t*/\n\t\tif (objType === 'Date') {\n\t\t\t// return `Date\\u202F${value.toISOString()}`;\n\t\t\treturn `Date\\u202F{${value.toLocaleString()}}`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `RegExp` objects.\n\t\t*/\n\t\tif (objType === 'RegExp') {\n\t\t\treturn `RegExp\\u202F${value.toString()}`;\n\t\t}\n\n\t\tconst result = [];\n\n\t\t/*\n\t\t\tHandle `Array` & `Set` objects.\n\t\t*/\n\t\tif (value instanceof Array || value instanceof Set) {\n\t\t\tconst list = value instanceof Array ? value : Array.from(value);\n\n\t\t\t// own numeric properties\n\t\t\t// NOTE: Do not use `<Array>.forEach()` here as it skips undefined members.\n\t\t\tfor (let i = 0, len = list.length; i < len; ++i) {\n\t\t\t\tresult.push(list.hasOwnProperty(i) ? _toWatchString(list[i]) : '<empty>');\n\t\t\t}\n\n\t\t\t// own enumerable non-numeric expando properties\n\t\t\tObject.keys(list)\n\t\t\t\t.filter(key => !_numericKeyRe.test(key))\n\t\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(list[key])}`));\n\n\t\t\treturn `${objType}(${list.length})\\u202F[${result.join(', ')}]`;\n\t\t}\n\n\t\t/*\n\t\t\tHandle `Map` objects.\n\t\t*/\n\t\tif (value instanceof Map) {\n\t\t\tvalue.forEach((val, key) => result.push(`${_toWatchString(key)} \\u2192 ${_toWatchString(val)}`));\n\n\t\t\treturn `${objType}(${value.size})\\u202F{${result.join(', ')}}`;\n\t\t}\n\n\t\t/*\n\t\t\tGeneral object handling.\n\t\t*/\n\t\t// own enumerable properties\n\t\tObject.keys(value)\n\t\t\t.forEach(key => result.push(`${_toWatchString(key)}: ${_toWatchString(value[key])}`));\n\n\t\treturn `${objType}\\u202F{${result.join(', ')}}`;\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\t/*\n\t\t\tDebug Bar Functions.\n\t\t*/\n\t\tinit     : { value : debugBarInit },\n\t\tisStowed : { value : debugBarIsStowed },\n\t\tstart    : { value : debugBarStart },\n\t\tstow     : { value : debugBarStow },\n\t\ttoggle   : { value : debugBarToggle },\n\t\tunstow   : { value : debugBarUnstow },\n\n\t\t/*\n\t\t\tWatch Functions.\n\t\t*/\n\t\twatch : {\n\t\t\tvalue : Object.freeze(Object.defineProperties({}, {\n\t\t\t\tadd       : { value : debugBarWatchAdd },\n\t\t\t\tall       : { value : debugBarWatchAddAll },\n\t\t\t\tclear     : { value : debugBarWatchClear },\n\t\t\t\tdelete    : { value : debugBarWatchDelete },\n\t\t\t\tdisable   : { value : debugBarWatchDisable },\n\t\t\t\tenable    : { value : debugBarWatchEnable },\n\t\t\t\tisEnabled : { value : debugBarWatchIsEnabled },\n\t\t\t\ttoggle    : { value : debugBarWatchToggle }\n\t\t\t}))\n\t\t}\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tloadscreen.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/* global Config, Engine */\n\nvar LoadScreen = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\n\n\t// Locks collection.\n\tconst _locks = new Set();\n\n\t// Auto-incrementing lock ID.\n\tlet _autoId = 0;\n\n\n\t/*******************************************************************************************************************\n\t\tLoadScreen Functions.\n\t*******************************************************************************************************************/\n\t/*\n\t\tInitialize management of the loading screen.\n\t*/\n\tfunction loadScreenInit() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenInit()]'); }\n\n\t\t// Add a `readystatechange` listener for hiding/showing the loading screen.\n\t\tjQuery(document).on('readystatechange.SugarCube', () => {\n\t\t\tif (DEBUG) { console.log(`[LoadScreen/<readystatechange>] document.readyState: \"${document.readyState}\"; locks(${_locks.size}):`, _locks); }\n\n\t\t\tif (_locks.size > 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The value of `document.readyState` may be: 'loading' -> 'interactive' -> 'complete'.\n\t\t\t// Though, to reach this point, it must already be in, at least, the 'interactive' state.\n\t\t\tif (document.readyState === 'complete') {\n\t\t\t\tif (jQuery(document.documentElement).attr('data-init') === 'loading') {\n\t\t\t\t\tif (Config.loadDelay > 0) {\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (_locks.size === 0) {\n\t\t\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, Math.max(Engine.minDomActionDelay, Config.loadDelay));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tloadScreenHide();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tloadScreenShow();\n\t\t\t}\n\t\t});\n\t}\n\n\t/*\n\t\tClear the loading screen.\n\t*/\n\tfunction loadScreenClear() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenClear()]'); }\n\n\t\t// Remove the event listener.\n\t\tjQuery(document).off('readystatechange.SugarCube');\n\n\t\t// Clear all locks.\n\t\t_locks.clear();\n\n\t\t// Hide the loading screen.\n\t\tloadScreenHide();\n\t}\n\n\t/*\n\t\tHide the loading screen.\n\t*/\n\tfunction loadScreenHide() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenHide()]'); }\n\n\t\tjQuery(document.documentElement).removeAttr('data-init');\n\t}\n\n\t/*\n\t\tShow the loading screen.\n\t*/\n\tfunction loadScreenShow() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenShow()]'); }\n\n\t\tjQuery(document.documentElement).attr('data-init', 'loading');\n\t}\n\n\t/*\n\t\tReturns a new lock ID after locking and showing the loading screen.\n\t*/\n\tfunction loadScreenLock() {\n\t\tif (DEBUG) { console.log('[LoadScreen/loadScreenLock()]'); }\n\n\t\t++_autoId;\n\t\t_locks.add(_autoId);\n\n\t\tif (DEBUG) { console.log(`\\tacquired loading screen lock; id: ${_autoId}`); }\n\n\t\tloadScreenShow();\n\t\treturn _autoId;\n\t}\n\n\t/*\n\t\tRemove the lock associated with the given lock ID and, if no locks remain,\n\t\ttrigger a `readystatechange` event.\n\t*/\n\tfunction loadScreenUnlock(id) {\n\t\tif (DEBUG) { console.log(`[LoadScreen/loadScreenUnlock(id: ${id})]`); }\n\n\t\tif (id == null) { // lazy equality for null\n\t\t\tthrow new Error('LoadScreen.unlock called with a null or undefined ID');\n\t\t}\n\n\t\tif (_locks.has(id)) {\n\t\t\t_locks.delete(id);\n\n\t\t\tif (DEBUG) { console.log(`\\treleased loading screen lock; id: ${id}`); }\n\t\t}\n\n\t\tif (_locks.size === 0) {\n\t\t\tjQuery(document).trigger('readystatechange');\n\t\t}\n\t}\n\n\n\t/*******************************************************************************************************************\n\t\tModule Exports.\n\t*******************************************************************************************************************/\n\treturn Object.freeze(Object.defineProperties({}, {\n\t\tinit   : { value : loadScreenInit },\n\t\tclear  : { value : loadScreenClear },\n\t\thide   : { value : loadScreenHide },\n\t\tshow   : { value : loadScreenShow },\n\t\tlock   : { value : loadScreenLock },\n\t\tunlock : { value : loadScreenUnlock }\n\t}));\n})();\n\n/***********************************************************************************************************************\n\n\tsugarcube.js\n\n\tCopyright © 2013–2020 Thomas Michael Edwards <thomasmedwards@gmail.com>. All rights reserved.\n\tUse of this source code is governed by a BSD 2-clause \"Simplified\" License, which may be found in the LICENSE file.\n\n***********************************************************************************************************************/\n/*\n\tglobal Alert, Browser, Config, Dialog, Engine, Fullscreen, Has, LoadScreen, SimpleStore, L10n, Macro, Passage,\n\t       Save, Scripting, Setting, SimpleAudio, State, Story, UI, UIBar, DebugBar, Util, Visibility, Wikifier\n*/\n/* eslint-disable no-var */\n\n/*\n\tVersion object.\n*/\nvar version = Object.freeze({\n\ttitle      : 'SugarCube',\n\tmajor      : 2,\n\tminor      : 33,\n\tpatch      : 4,\n\tprerelease : null,\n\tbuild      : 1,\n\tdate       : new Date(\"2020-11-15T23:56:20.992Z\"),\n\t/* legacy */\n\textensions : {},\n\t/* /legacy */\n\n\ttoString() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.major}.${this.minor}.${this.patch}${prerelease}+${this.build}`;\n\t},\n\n\tshort() {\n\t\t'use strict';\n\n\t\tconst prerelease = this.prerelease ? `-${this.prerelease}` : '';\n\t\treturn `${this.title} (v${this.major}.${this.minor}.${this.patch}${prerelease})`;\n\t},\n\n\tlong() {\n\t\t'use strict';\n\n\t\treturn `${this.title} v${this.toString()} (${this.date.toUTCString()})`;\n\t}\n});\n\n/* eslint-disable no-unused-vars */\n/*\n\tInternal variables.\n*/\n// Temporary state object.\nvar TempState = {};\n\n// Legacy macros object.\nvar macros = {};\n\n// Post-display task callbacks object.\nvar postdisplay = {};\n\n// Post-render task callbacks object.\nvar postrender = {};\n\n// Pre-display task callbacks object.\nvar predisplay = {};\n\n// Pre-history task callbacks object.\nvar prehistory = {};\n\n// Pre-render task callbacks object.\nvar prerender = {};\n\n// Session storage manager object.\nvar session = null;\n\n// Settings object.\nvar settings = {};\n\n// Setup object.\nvar setup = {};\n\n// Persistant storage manager object.\nvar storage = null;\n\n/*\n\tLegacy aliases.\n*/\nvar browser       = Browser;\nvar config        = Config;\nvar has           = Has;\nvar History       = State;\nvar state         = State;\nvar tale          = Story;\nvar TempVariables = State.temporary;\n/* eslint-enable no-unused-vars */\n\n/*\n\tGlobal `SugarCube` object.  Allows scripts to detect if they're running in SugarCube by\n\ttesting for the object (e.g. `\"SugarCube\" in window`) and contains exported identifiers\n\tfor debugging purposes.\n*/\nwindow.SugarCube = {};\n\n/*\n\tMain function, entry point for the story.\n*/\njQuery(() => {\n\t'use strict';\n\n\tif (DEBUG) { console.log('[SugarCube/main()] Document loaded; beginning startup.'); }\n\n\t/*\n\t\tWARNING!\n\n\t\tThe ordering of the code within this function is critically important,\n\t\tso be careful when mucking around with it.\n\t*/\n\ttry {\n\t\t// Acquire an initial lock for and initialize the loading screen.\n\t\tconst lockId = LoadScreen.lock();\n\t\tLoadScreen.init();\n\n\t\t// Normalize the document.\n\t\tif (document.normalize) {\n\t\t\tdocument.normalize();\n\t\t}\n\n\t\t// Load the story data (must be done before most anything else).\n\t\tStory.load();\n\n\t\t// Instantiate the storage and session objects.\n\t\t// NOTE: `SimpleStore.create(storageId, persistent)`\n\t\tstorage = SimpleStore.create(Story.domId, true);\n\t\tsession = SimpleStore.create(Story.domId, false);\n\n\t\t// Initialize the user interface (must be done before story initialization, specifically before scripts).\n\t\tDialog.init();\n\t\tUIBar.init();\n\t\tEngine.init();\n\n\t\t// Initialize the story (largely load the user styles, scripts, and widgets).\n\t\tStory.init();\n\n\t\t// Initialize the localization (must be done after story initialization).\n\t\tL10n.init();\n\n\t\t// Alert when the browser is degrading required capabilities (must be done after localization initialization).\n\t\tif (!session.has('rcWarn') && storage.name === 'cookie') {\n\t\t\t/* eslint-disable no-alert */\n\t\t\tsession.set('rcWarn', 1);\n\t\t\twindow.alert(L10n.get('warningNoWebStorage'));\n\t\t\t/* eslint-enable no-alert */\n\t\t}\n\n\t\t// Initialize the saves (must be done after story initialization, but before engine start).\n\t\tSave.init();\n\n\t\t// Initialize the settings.\n\t\tSetting.init();\n\n\t\t// Initialize the macros.\n\t\tMacro.init();\n\n\t\t// Start the engine (should be done as late as possible, but before interface startup).\n\t\tEngine.start();\n\n\t\t// Initialize the debug bar interface (should be done as late as possible, but before interface startup).\n\t\tif (Config.debug) {\n\t\t\tDebugBar.init();\n\t\t}\n\n\t\t// Set a recurring timer to start the interfaces (necessary due to DOM readiness issues in some browsers).\n\t\tconst $window    = $(window);\n\t\tconst vprCheckId = setInterval(() => {\n\t\t\t// If `$window.width()` returns a zero value, bail out and wait.\n\t\t\tif (!$window.width()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear the recurring timer.\n\t\t\tclearInterval(vprCheckId);\n\n\t\t\t// Start the UI bar interface.\n\t\t\tUIBar.start();\n\n\t\t\t// Start the debug bar interface.\n\t\t\tif (Config.debug) {\n\t\t\t\tDebugBar.start();\n\t\t\t}\n\n\t\t\t// Trigger the `:storyready` global synthetic event.\n\t\t\tjQuery.event.trigger({ type : ':storyready' });\n\n\t\t\t// Release the loading screen lock after a short delay.\n\t\t\tsetTimeout(() => LoadScreen.unlock(lockId), Engine.minDomActionDelay * 2);\n\t\t}, Engine.minDomActionDelay);\n\n\t\t// Finally, export identifiers for debugging purposes.\n\t\tObject.defineProperty(window, 'SugarCube', {\n\t\t\t// WARNING: We need to assign new values at points, so seal it, do not freeze it.\n\t\t\tvalue : Object.seal(Object.assign(Object.create(null), {\n\t\t\t\tBrowser,\n\t\t\t\tConfig,\n\t\t\t\tDialog,\n\t\t\t\tEngine,\n\t\t\t\tFullscreen,\n\t\t\t\tHas,\n\t\t\t\tL10n,\n\t\t\t\tMacro,\n\t\t\t\tPassage,\n\t\t\t\tSave,\n\t\t\t\tScripting,\n\t\t\t\tSetting,\n\t\t\t\tSimpleAudio,\n\t\t\t\tState,\n\t\t\t\tStory,\n\t\t\t\tUI,\n\t\t\t\tUIBar,\n\t\t\t\tDebugBar,\n\t\t\t\tUtil,\n\t\t\t\tVisibility,\n\t\t\t\tWikifier,\n\t\t\t\tsession,\n\t\t\t\tsettings,\n\t\t\t\tsetup,\n\t\t\t\tstorage,\n\t\t\t\tversion\n\t\t\t}))\n\t\t});\n\n\t\tif (DEBUG) { console.log('[SugarCube/main()] Startup complete; story ready.'); }\n\t}\n\tcatch (ex) {\n\t\tconsole.error(ex);\n\t\tLoadScreen.clear();\n\t\treturn Alert.fatal(null, ex.message, ex);\n\t}\n});\n\n})(window, window.document, jQuery);\n}\n\t</script>\n</body>\n</html>\n"});
\ No newline at end of file
diff --git a/devTools/types/FC/SecExp.d.ts b/devTools/types/FC/SecExp.d.ts
index 444ae2beefd7014d842b8d3197bb43cc90894279..f49f3d622968e7e2df6d93c9d05adfd2da93962b 100644
--- a/devTools/types/FC/SecExp.d.ts
+++ b/devTools/types/FC/SecExp.d.ts
@@ -23,7 +23,7 @@ declare namespace FC {
 			commissars: number
 		}
 
-		type PlayerHumanUnitType = "bots" | "citizens" | "mercenary" | "slave";
+		type PlayerHumanUnitType = "bots" | "citizens" | "mercenary" | "slave" | "SF";
 		type EnemyUnitType = "raiders" | "free city" | "old world" | "freedom fighters";
 	}
 }
diff --git a/devTools/types/FC/arcology.d.ts b/devTools/types/FC/arcology.d.ts
index a28aece4d3a46e0384a7c935acd8616053915d4c..75413f5f629bea4761baaa586d193a00185a45f4 100644
--- a/devTools/types/FC/arcology.d.ts
+++ b/devTools/types/FC/arcology.d.ts
@@ -1,42 +1,43 @@
 declare namespace FC {
 	interface FutureSocietyIdMap {
-		FSSupremacist: {noun: "Racial Supremacism", adj: "Supremacist"};
-		FSSubjugationist: {noun: "Racial Subjugationism", adj: "Subjugationist"};
-		FSGenderRadicalist: {noun: "Gender Radicalism", adj: "Gender Radicalist"};
-		FSGenderFundamentalist: {noun: "Gender Fundamentalism", adj: "Gender Fundamentalist"};
-		FSDegradationist: {noun: "Degradationism", adj: "Degradationist"};
-		FSPaternalist: {noun: "Paternalism", adj: "Paternalist"};
-		FSBodyPurist: {noun: "Body Purism", adj: "Body Purist"};
-		FSTransformationFetishist: {noun: "Transformation Fetishism", adj: "Transformation Fetishist"};
-		FSYouthPreferentialist: {noun: "Youth Preferentialism", adj: "Youth Preferentialist"};
-		FSMaturityPreferentialist: {noun: "Maturity Preferentialism", adj: "Maturity Preferentialist"};
-		FSSlimnessEnthusiast: {noun: "Slimness Enthusiasm", adj: "Slimness Enthusiast"};
-		FSAssetExpansionist: {noun: "Asset Expansionism", adj: "Asset Expansionist"};
-		FSPastoralist: {noun: "Pastoralism", adj: "Pastoralist"};
-		FSCummunism: {noun: "Cummunism", adj: "Cummunist"};
-		FSPhysicalIdealist: {noun: "Physical Idealism", adj: "Physical Idealist"};
-		FSHedonisticDecadence: {noun: "Decadent Hedonism", adj: "Decadent Hedonist"};
-		FSChattelReligionist: {noun: "Chattel Religionism", adj: "Chattel Religionist"};
-		FSNull: {noun: "Multiculturalism", adj: "Multiculturalist"};
-		FSIncestFetishist: {noun: "Incest Fetishism", adj: "Incest Fetishist"};
-		FSRomanRevivalist: {noun: "Roman Revivalism", adj: "Roman Revivalist"};
-		FSNeoImperialist: {noun: "Neo-Imperialism", adj: "Neo-Imperialist"};
-		FSEgyptianRevivalist: {noun: "Egyptian Revivalism", adj: "Egyptian Revivalist"};
-		FSEdoRevivalist: {noun: "Edo Revivalism", adj: "Edo Revivalist"};
-		FSArabianRevivalist: {noun: "Arabian Revivalism", adj: "Arabian Revivalist"};
-		FSChineseRevivalist: {noun: "Chinese Revivalism", adj: "Chinese Revivalist"};
-		FSAztecRevivalist: {noun: "Aztec Revivalism", adj: "Aztec Revivalist"};
-		FSRepopulationFocus: {noun: "Repopulation Focus", adj: "Repopulationist"};
-		FSRestart: {noun: "Eugenics", adj: "Eugenics"};
-		FSIntellectualDependency: {noun: "Intellectual Dependency", adj: "Intellectual Dependency"};
-		FSSlaveProfessionalism: {noun: "Slave Professionalism", adj: "Slave Professional"};
-		FSPetiteAdmiration: {noun: "Petite Admiration", adj: "Petite Admiration"};
-		FSStatuesqueGlorification: {noun: "Statuesque Glorification", adj: "Statuesque Glorification"};
+		FSSupremacist: {noun: "Racial Supremacism", adj: "Supremacist", deco: "Supremacist"};
+		FSSubjugationist: {noun: "Racial Subjugationism", adj: "Subjugationist", deco: "Subjugationist"};
+		FSGenderRadicalist: {noun: "Gender Radicalism", adj: "Gender Radicalist", deco: "Gender Radicalist"};
+		FSGenderFundamentalist: {noun: "Gender Fundamentalism", adj: "Gender Fundamentalist", deco: "Gender Fundamentalist"};
+		FSDegradationist: {noun: "Degradationism", adj: "Degradationist", deco: "Degradationist"};
+		FSPaternalist: {noun: "Paternalism", adj: "Paternalist", deco: "Paternalist"};
+		FSBodyPurist: {noun: "Body Purism", adj: "Body Purist", deco: "Body Purist"};
+		FSTransformationFetishist: {noun: "Transformation Fetishism", adj: "Transformation Fetishist", deco: "Transformation Fetishist"};
+		FSYouthPreferentialist: {noun: "Youth Preferentialism", adj: "Youth Preferentialist", deco: "Youth Preferentialist"};
+		FSMaturityPreferentialist: {noun: "Maturity Preferentialism", adj: "Maturity Preferentialist", deco: "Maturity Preferentialist"};
+		FSSlimnessEnthusiast: {noun: "Slimness Enthusiasm", adj: "Slimness Enthusiast", deco: "Slimness Enthusiast"};
+		FSAssetExpansionist: {noun: "Asset Expansionism", adj: "Asset Expansionist", deco: "Asset Expansionist"};
+		FSPastoralist: {noun: "Pastoralism", adj: "Pastoralist", deco: "Pastoralist"};
+		FSCummunism: {noun: "Cummunism", adj: "Cummunist", deco: ""};
+		FSPhysicalIdealist: {noun: "Physical Idealism", adj: "Physical Idealist", deco: "Physical Idealist"};
+		FSHedonisticDecadence: {noun: "Decadent Hedonism", adj: "Decadent Hedonist", deco: "Hedonistic"};
+		FSChattelReligionist: {noun: "Chattel Religionism", adj: "Chattel Religionist", deco: "Chattel Religionist"};
+		FSNull: {noun: "Multiculturalism", adj: "Multiculturalist", deco: ""};
+		FSIncestFetishist: {noun: "Incest Fetishism", adj: "Incest Fetishist", deco: ""};
+		FSRomanRevivalist: {noun: "Roman Revivalism", adj: "Roman Revivalist", deco: "Roman Revivalist"};
+		FSNeoImperialist: {noun: "Neo-Imperialism", adj: "Neo-Imperialist", deco: "Neo-Imperialist"};
+		FSEgyptianRevivalist: {noun: "Egyptian Revivalism", adj: "Egyptian Revivalist", deco: "Egyptian Revivalist"};
+		FSEdoRevivalist: {noun: "Edo Revivalism", adj: "Edo Revivalist", deco: "Edo Revivalist"};
+		FSArabianRevivalist: {noun: "Arabian Revivalism", adj: "Arabian Revivalist", deco: "Arabian Revivalist"};
+		FSChineseRevivalist: {noun: "Chinese Revivalism", adj: "Chinese Revivalist", deco: "Chinese Revivalist"};
+		FSAztecRevivalist: {noun: "Aztec Revivalism", adj: "Aztec Revivalist", deco: "Aztec Revivalist"};
+		FSRepopulationFocus: {noun: "Repopulation Focus", adj: "Repopulationist", deco: "Repopulationist"};
+		FSRestart: {noun: "Eugenics", adj: "Eugenics", deco: "Eugenics"};
+		FSIntellectualDependency: {noun: "Intellectual Dependency", adj: "Intellectual Dependency", deco: "Intellectual Dependency"};
+		FSSlaveProfessionalism: {noun: "Slave Professionalism", adj: "Slave Professional", deco: "Slave Professionalism"};
+		FSPetiteAdmiration: {noun: "Petite Admiration", adj: "Petite Admiration", deco: "Petite Admiration"};
+		FSStatuesqueGlorification: {noun: "Statuesque Glorification", adj: "Statuesque Glorification", deco: "Statuesque Glorification"}
 	}
 
 	type FutureSociety = keyof FutureSocietyIdMap;
 	type FutureSocietyNoun = FutureSocietyIdMap[keyof FutureSocietyIdMap]["noun"];
 	type FutureSocietyAdj = FutureSocietyIdMap[keyof FutureSocietyIdMap]["adj"];
+	type FutureSocietyDeco = FutureSocietyIdMap[keyof FutureSocietyIdMap]["deco"] | "standard";
 
 	type FSPolicyValue = number | "unset";
 
diff --git a/devTools/types/FC/misc.d.ts b/devTools/types/FC/misc.d.ts
index f87d71ab846ebd2010244c04967fefbf87b62644..ffdc266b34a4ee4536ccc2c911acf4f0a428a053 100644
--- a/devTools/types/FC/misc.d.ts
+++ b/devTools/types/FC/misc.d.ts
@@ -2,9 +2,10 @@ declare namespace FC {
 	type SlaveSchoolName = "GRI" | "HA" | "NUL" | "SCP" | "TCR" | "TFS" | "TGA" | "TSS" | "LDE" | "TUO";
 	type LawlessMarkets = "generic" | "gangs and smugglers" | "heap" | "indentures" | "low tier criminals" | "military prison" | "neighbor" |
 		"wetware" | "white collar" | SlaveSchoolName;
-	type SlaveMarketName =  LawlessMarkets | "corporate";
+	type SlaveMarketName = LawlessMarkets | "corporate";
 	type Gingering = Zeroable<"antidepressant" | "depressant" | "stimulant" | "vasoconstrictor" | "vasodilator" | "aphrodisiac" | "ginger">;
 	type GingeringDetection = Zeroable<"slaver" | "mercenary" | "force">;
+	type SlaveActs = "PCChildrenFathered" | "PCKnockedUp" | "anal" | "births" | "birthsTotal" | "cum" | "laborCount" | "mammary" | "milk" | "oral" | "penetrative" | "pitKills" | "miscarriages" | "publicUse" | "slavesFathered" | "slavesKnockedUp" | "vaginal" | "abortions" | "birth";
 
 	namespace SlaveSummary {
 		interface SmartPiercing {
@@ -28,7 +29,7 @@ declare namespace FC {
 				women: string,
 				"anti-men": string,
 				"anti-women": string,
-			}
+			};
 		}
 	}
 }
diff --git a/devTools/types/SugarCubeExtensions.d.ts b/devTools/types/SugarCubeExtensions.d.ts
index 2eaf3d541c9df9fec0962bfdc873f2ede89d37c8..52eaed4fd5f7094d7ace8765ef0f122992e8e563 100644
--- a/devTools/types/SugarCubeExtensions.d.ts
+++ b/devTools/types/SugarCubeExtensions.d.ts
@@ -5,61 +5,6 @@ declare module "twine-sugarcube" {
 	}
 
 	interface SugarCubeSetupObject {
-		ArcologyNamesSupremacistWhite: string[];
-		ArcologyNamesSupremacistAsian: string[];
-		ArcologyNamesSupremacistLatina: string[];
-		ArcologyNamesSupremacistMiddleEastern: string[];
-		ArcologyNamesSupremacistBlack: string[];
-		ArcologyNamesSupremacistIndoAryan: string[];
-		ArcologyNamesSupremacistPacificIslander: string[];
-		ArcologyNamesSupremacistMalay: string[];
-		ArcologyNamesSupremacistAmerindian: string[];
-		ArcologyNamesSupremacistSouthernEuropean: string[];
-		ArcologyNamesSupremacistSemitic: string[];
-		ArcologyNamesSupremacistMixedRace: string[];
-		ArcologyNamesSubjugationistWhite: string[];
-		ArcologyNamesSubjugationistAsian: string[];
-		ArcologyNamesSubjugationistLatina: string[];
-		ArcologyNamesSubjugationistMiddleEastern: string[];
-		ArcologyNamesSubjugationistBlack: string[];
-		ArcologyNamesSubjugationistIndoAryan: string[];
-		ArcologyNamesSubjugationistPacificIslander: string[];
-		ArcologyNamesSubjugationistMalay: string[];
-		ArcologyNamesSubjugationistAmerindian: string[];
-		ArcologyNamesSubjugationistSouthernEuropean: string[];
-		ArcologyNamesSubjugationistSemitic: string[];
-		ArcologyNamesSubjugationistMixedRace: string[];
-		ArcologyNamesGenderRadicalist: string[];
-		ArcologyNamesGenderFundamentalist: string[];
-		ArcologyNamesPaternalist: string[];
-		ArcologyNamesDegradationist: string[];
-		ArcologyNamesAssetExpansionist: string[];
-		ArcologyNamesSlimnessEnthusiast: string[];
-		ArcologyNamesTransformationFetishist: string[];
-		ArcologyNamesBodyPurist: string[];
-		ArcologyNamesMaturityPreferentialist: string[];
-		ArcologyNamesYouthPreferentialistLow: string[];
-		ArcologyNamesYouthPreferentialist: string[];
-		ArcologyNamesPastoralist: string[];
-		ArcologyNamesPhysicalIdealist: string[];
-		ArcologyNamesChattelReligionist: string[];
-		ArcologyNamesRomanRevivalist: string[];
-		ArcologyNamesNeoImperialist: string[];
-		ArcologyNamesAztecRevivalist: string[];
-		ArcologyNamesEgyptianRevivalist: string[];
-		ArcologyNamesEdoRevivalist: string[];
-		ArcologyNamesArabianRevivalist: string[];
-		ArcologyNamesChineseRevivalist: string[];
-		ArcologyNamesRepopulationist: string[];
-		ArcologyNamesEugenics: string[];
-		ArcologyNamesHedonisticDecadence: string[];
-		ArcologyNamesIntellectualDependency: string[];
-		ArcologyNamesSlaveProfessionalism: string[];
-		ArcologyNamesPetiteAdmiration: string[];
-		ArcologyNamesStatuesqueGlorification: string[];
-		ArcologyNamesCummunism: string[];
-		ArcologyNamesIncestFetishist: string[];
-
 		badWords: string[];
 		badNames: string[];
 		chattelReligionistSlaveNames: string[];
@@ -75,30 +20,6 @@ declare module "twine-sugarcube" {
 		whiteAmericanSlaveNames: string[];
 		whiteAmericanSlaveSurnames: string[];
 
-		attendantCareers: string[];
-		bodyguardCareers: string[];
-		DJCareers: string[];
-		educatedCareers: string[];
-		entertainmentCareers: string[];
-		facilityCareers: string[];
-		farmerCareers: string[];
-		gratefulCareers: string[];
-		HGCareers: string[];
-		madamCareers: string[];
-		matronCareers: string[];
-		menialCareers: string[];
-		milkmaidCareers: string[];
-		nurseCareers: string[];
-		recruiterCareers: string[];
-		schoolteacherCareers: string[];
-		servantCareers: string[];
-		stewardessCareers: string[];
-		uneducatedCareers: string[];
-		veryYoungCareers: string[];
-		wardenessCareers: string[];
-		whoreCareers: string[];
-		youngCareers: string[];
-
 		fakeBellies: string[];
 		filterRaces: string[];
 		filterRacesLowercase: FC.Race[];
diff --git a/js/002-config/fc-js-init.js b/js/002-config/fc-js-init.js
index f72074b076279666181ff7becde9c40792649f31..1710cf940dfaf83d07d879f2b7744cf63bd8abc6 100644
--- a/js/002-config/fc-js-init.js
+++ b/js/002-config/fc-js-init.js
@@ -66,6 +66,7 @@ App.Reminders = {};
 App.SF = {};
 App.SecExp = {};
 App.SlaveAssignment = {};
+App.StartingGirls = {};
 App.UI = {};
 App.UI.Budget = {};
 App.UI.DOM = {};
diff --git a/js/003-data/arcologyNames.js b/js/003-data/arcologyNames.js
new file mode 100644
index 0000000000000000000000000000000000000000..d4b602701e8c4c5a1f544a43c7647c3f04908127
--- /dev/null
+++ b/js/003-data/arcologyNames.js
@@ -0,0 +1,58 @@
+App.Data.ArcologyNames = {
+	SupremacistAmerindian: ["Akilineq", "Amerindia", "Aquadoctan", "Cahokia", "Caral", "Chicora", "Cowee", "Cusco", "Dugiluyi", "Five Nations", "Gran Chaco", "Indigenismo", "Isunigu", "Moundville", "Norumbega", "Onaquaga", "Onondaga Lake", "Paititi", "Porcupine", "Pueblo de Taos", "Quito", "Red Power", "Saguenay", "Shackamaxon", "Tamoanchan", "The Confederated Tribes", "Werowocomoco"],
+	SupremacistAsian: ["Asiatic Empire", "Ciimnuai", "Eastern Sun", "Greater Asia", "Jade Empire", "Jade Library", "Kalapa", "Mahoroba", "Pan-Asia", "Penglai", "Shambhala", "Shangri-La", "Sinosphere", "The Celestial Temple", "The Orient", "Tian", "Yangtze", "Yellow River", "Zhonghua Minzu"],
+	SupremacistBlack: ["Africana", "Afrocentral", "Azania", "Benin", "Door of Return", "Great Zimbabwe", "Houssa", "Kwanzaa Island", "Liberia", "Mezzoramia", "Négritude", "New Afrika", "Nubia", "Pan-Africa", "Panther Valley", "Rhapta", "The Promised Land", "Timbuktu", "United Africa", "Wakanda", "Zazamanc"],
+	SupremacistIndoAryan: ["Alaka", "Āryāvarta", "Dvārakā", "Indomania", "Indus Valley", "Kuru Kingdom", "Muziris", "New New Delhi", "Pialral", "Saket", "Swadeshi", "Swarga Loka", "Tamralipta", "The Raj", "The Subcontinent", "Ujjain", "Vaikuntha", "Vedic Empire", "Vindhya"],
+	SupremacistLatina: ["Alcázar de Segovia", "Alhambra", "Aztlan", "Chicanismo", "Ciudad Blanca", "El Dorado", "Hispania", "Hispanismo", "La Sagrada", "Lake Parime", "Quivira", "Santa Bárbara", "Sierra de la Plata", "Tayopa", "Tenochtitlan"],
+	SupremacistMalay: ["Austronesia", "Biringan", "Brunei", "Golden Peninsula", "Kaluwalhatian", "Kebangkitan Nasional", "Ketuanan Melayu", "Malacca", "Malaya", "Maphilindo", "Melayu Raya", "Nusantara", "Patani", "Srivijaya", "Suvarnadvipa", "Tanah Melayu"],
+	SupremacistMiddleEastern: ["Arabia", "Asabiyyah", "Ba'ath", "Fertile Crescent", "Iram", "Jannah", "Kerma", "MENA", "Mesopotamia", "Mount Qaf", "New Cairo", "Pan-Arabia", "Sinai", "The Caliphate", "Ubar", "Wabar", "Wāḳwāḳ", "West Asia", "Zerzura"],
+	SupremacistMixedRace: ["Desegregation", "Exogamy", "Fusion", "Heterogeneity", "Hybrid Vigor", "Integration", "Kaleidoscope", "Meltingpot", "Mosaic", "Multination", "Plaçage", "Pluralism", "Polychrome", "Rainbow Nation", "Salad Bowl", "The Mixer", "The Swirl"],
+	SupremacistPacificIslander: ["Aotearoa", "Austronesia", "Baralku", "Burotu", "Dreamtime", "Hawai'i", "Hawaiki", "Iolani Palace", "Kibu", "King Country", "Maui", "Melanesia", "Micronesia", "Mokoia", "Oceania", "Pacifica", "Papahānaumokuākea", "Polynesia", "Pulotu", "Rapa Nui"],
+	SupremacistSemitic: ["Arimathea", "Callipolis", "Dilmun", "Garden of Eden", "Greater Jerusalem", "Israel", "Jericho", "Judah", "Judea", "New Jerusalem", "Olam Ha-Ba", "Ophir", "Paradisus Judaeorum", "Pitchipoi", "Seron", "Tarshish", "The Fifth Temple", "The Levant", "The Promised Land", "Zion"],
+	SupremacistSouthernEuropean: ["Arcadia", "Delian League", "Delphi", "Elysian Fields", "Fortunate Isles", "Hyperuranion", "Iberia", "Mare Nostrum", "Mediterranea", "New Athens", "New Rome", "Olympus", "Papal Supremacy", "Risorgimento", "Siglo de Oro", "Spazio Vitale"],
+	SupremacistWhite: ["Avalon", "Baasskap", "Buyan", "Caucasia", "Cockaigne", "Eurocentral", "Europa", "Europe a Nation", "Fiery Cross", "Fourth Reich", "Gimlé", "Hy-Brasil", "Kitezh", "Klanbake", "New Australia", "Northwest Territory", "Opona", "Orania", "Pan-Europe", "The Old Dominion", "Thule", "Turner City", "Volkstaat", "Vyraj", "White Might"],
+	SubjugationistAmerindian: ["Adlivun", "Bear River", "Cowboy Town", "Fire Waters", "Fort Laramie", "Fort Mystic", "Manifest Destiny", "Mazocoba", "Oklahoma", "Red Dead", "Río Negro", "Sand Creek", "Shobari Waka", "The Rez", "Trail of Tears", "Washita", "Worst Nation", "Wounded Knee"],
+	SubjugationistAsian: ["Asiatic Exclusion", "Beriberi", "Defense of the Realm", "Diyu", "Hells Canyon", "Hiroshima", "Luzon", "Opium Den", "Pearl of the Orient", "Rock Springs", "Shakee", "Sinking Tide", "The East India Company", "Torreón", "Yellow Error", "Youdu"],
+	SubjugationistBlack: ["Bantustan", "Crow's Nest", "Dixie", "El Corte", "Golden Circle", "Hetgwauge", "Kuzimu", "Lynchburg", "Middle Passage", "Richmond", "Rosewood", "Rubber Farm", "Sharpeville", "Soweto", "Strange Orchard", "Sundown Town", "The Confederacy", "The Plantation", "The Projects", "Three-Fifths", "Tulsa"],
+	SubjugationistIndoAryan: ["Call Center", "Convenience Store", "Goa Inquisition", "Jallianwala Bagh", "Kalichi", "Macaulayism", "Naraka", "Navarino", "Qissa Khwani Bazaar", "Sepoy Mutiny", "Slumdog Kennels", "The East India Company", "Trade Fort", "UCIL Plant", "Uva Province"],
+	SubjugationistLatina: ["All-Mexico", "Annual", "Banana Republic", "Bean Paste", "Bisbee", "Border Wall", "Chandler", "Downieville", "Fort Veracruz", "Hanigan Ranch", "La Migra", "Los Conquistados", "Los Gatos", "Porvenir", "Vergüenza", "Zoot Suit Riot"],
+	SubjugationistMalay: ["Batavia", "Bencoolen", "East Indies", "Eastern Emporium", "Fort Marlborough", "Gimokodan", "Macunat School", "Moro Crater", "Pontianak", "Pulo Prabang", "Rawagede", "Soerabaja", "Spice Mine", "Watsonville"],
+	SubjugationistMiddleEastern: ["Al-Dawayima", "Allon Plus", "Constantinople", "Countered Jihad", "Cronulla", "Frontier Wire", "Homeland Secured", "Kiryat Arba", "La Reconquista", "Lydda", "New Guantanamo", "Qibya", "Sétif", "Shu'ubiyya", "Tantura", "Vlad's Castle", "Well Fire", "Yalova Peninsula", "Zanzibar"],
+	SubjugationistMixedRace: ["Apartheid", "Barriers", "Bloodlines", "Division", "Endogamy", "Ghetto Benches", "Homogeneity", "Monochrome", "Monoculture", "One-Drop", "Purity", "Redline", "Segregation", "Separate but Equal", "Separation", "The Divide"],
+	SubjugationistPacificIslander: ["Blackbird", "Cargo Cult", "Castle Bravo", "Coniston", "Great Māhele", "Hula Hoop", "Moro Castle", "Murimuria", "Myall Creek", "Ōmiya-Jima", "Rabbit Fence", "Sapwuahfik", "The Leap", "Thurston", "Tourist Trap", "Waterloo Creek"],
+	SubjugationistSemitic: ["Auschwitz", "Devil's Island", "Exodus", "Farhud", "Gehenna", "Intifada", "Kfar Etzion", "Kristallnacht", "Mawza Exile", "Mount Scopus", "New Canaan", "Pale of Settlement", "Pogrom", "Sheol", "Six Million Mile", "Solomon's Lament", "The Ghetto"],
+	SubjugationistSouthernEuropean: ["Al-Andalus", "Apalachin", "Arandora Star", "Black Legend", "Braintree", "Carthage", "Charlestown State", "Chios", "Hades", "Istanbul", "Istria", "Kalavryta", "Parish Prison", "Smyrna", "Tartarus", "The Foibe", "Toronto Trouble"],
+	SubjugationistWhite: ["Anaon", "Anticolonialism One", "Bleach Removal", "Camp des Saints", "Cawnpore", "Decolonization", "Greater Replacement", "Kaffa", "Killough", "Ladoga", "Mayocide", "Peklo", "Reparations", "Risen Tide", "Rope Burn", "Saint-Domingue", "The World Turned Upside Down", "Trailer Park", "Tuonela", "Uffern", "WASP Spray", "White Flight"],
+	GenderRadicalist: ["Admah", "Aphroditus", "Bacchanalia", "Boeotia", "Brumalia", "Catamitus", "City of the Plain", "Crete", "Dionysia", "Ermenosity", "Gomorrah", "Hermaphroditus", "Impudicitia", "Liberalia", "Pessinus", "Saturnalia", "Sodom", "The Rosebud", "Thebes", "Vine of Sodom", "Zeboim"],
+	GenderFundamentalist: ["The Arbor", "The Center", "The Core", "The Cradle", "The Entrance", "The Essence", "The Flower", "The Fruit", "The Jewel", "The Lily", "The Love", "The Moon", "The Origin", "The Pearl", "The Petal", "The Rose", "The Sheath", "The Source", "The Warmth"],
+	Paternalist: ["Asylum", "Benevolence", "City of Refuge", "Fatherhood", "Glory", "Greater Good", "Haven", "Humanitaria", "Nanny State", "New Springfield", "Paterfamilias", "Paternalis", "Refuge", "Safe Harbor", "Safe Haven", "Safehouse", "Safety", "Sanctuary", "Sanctum", "Shelter", "The Sanctuary", "Welfare"],
+	Degradationist: ["Akelarre", "Apocalyptica", "Armageddon", "Bald Mountain", "Black Sabbath", "Blåkulla", "Château de Silling", "Cruelty", "Damnation", "Degradation", "Diabolica", "Doomsday", "Dukkha", "Golgotha", "Hell on Earth", "Hell", "Inferno", "Misery", "Pain", "Schadenfreude", "Slaughterhouse", "Suffering", "The Pit", "The Tower", "Torment", "Torture Chamber", "Well to Hell"],
+	BodyPurist: ["Antiplasto", "Au Naturel", "Elysium", "Injection Rejection", "L'Esprit Nouveau", "Natural Selection", "Natural State", "Nature Reserve", "New Eden", "Organics", "Pure Land", "Pure Shores", "Purification", "Purity Balls", "Purity Ring", "Purity", "Sanctity", "Scarless Fever", "Surgical Strike", "The Ark", "The Garden", "The Repository", "Unblemisht", "Walden"],
+	TransformationFetishist: ["Arion Laboratory", "Barbie World", "Bimboden", "Bimboland", "Dow Corning", "Gillies Suite", "Guinea Pig Club", "Implantation Station", "Mad Mods", "Modding Community", "Mods Nexus", "Niptuck", "Plastic Beach", "Plasticland", "Silicone Valley", "Silicone Zone", "Stacy Malibu", "Strained Silicone", "Surgeon Generality", "The Dollhouse", "The Hospital", "Transformation Station", "Transformational Festival", "Under-Knife"],
+	YouthPreferentialist: ["Cumfullton", "Dick U.", "Ephebophily", "Frat Party", "Fuck High", "Hebephily", "Homecoming", "Kid Row", "Prom Night", "Sex College", "Sorority Row", "Spring Break", "Sunnyside", "Teen Scene", "Teen Spirit", "Teenage Wasteland", "Teenybop", "Undergrad Pad", "Young Earth", "Youngling", "Youngtown", "Youth Culture", "Youth", "Youthanasia"],
+	YouthPreferentialistLow: ["Cherry Fields", "Cherry Hills", "Comet Ping Pong", "Cummies Kindergarten", "Cunny Junction", "Dick Elementary", "Flatsville", "Groom Range", "Hanson City", "Hebephily", "Hotel Bangkok", "Kiddie Diddlebury", "Lil' Sluts Academy", "Lolita Complex", "Loliville", "Oingo Boingo", "Partyvanistan", "Pedophily", "Pomf Town", "Prepubescence", "Savile Row", "Statutoria", "The Cake Shop"],
+	MaturityPreferentialist: ["Age Begets Beauty", "Annual Reunion", "Cougar Town", "Experience", "Fine Wine", "Gerontophily", "Mature Theme", "Maturity", "Mesophily", "MILF Haven", "MILF Heights", "MILFtown", "Old Flame", "Old Style", "Park Avenue Tower", "Phaedra Complex", "Robinsonade", "Shady Acres", "Yummy Mummy"],
+	SlimnessEnthusiast: ["Aerobica", "Cardiode", "Emaciate State", "Lean Scene", "Less Is More", "Marathon", "National Diet", "Runway Way", "Size Zero", "Skin-and-Bones Zone", "Skinny Bop", "Skinny Dip", "Skinny House", "Slim City", "Slim Shades", "Slimming World", "The Island", "The Skinny", "The Thinning", "Underweight Way", "Upskirt", "Virginland", "Weigh Down Low"],
+	AssetExpansionist: ["Asset Holdings", "Biggening", "Blow-Up", "Boobs Tower", "Expand Land", "Expansion Chamber", "Expansion Pack", "Growth Medium", "Inflation Station", "Tangible Assets", "The Bouncy Castle", "The Expanse", "The Mounds", "Twin Peaks", "Voluptuousity"],
+	Pastoralist: ["Abundance", "Big Milk", "Bounty", "Bucolica", "Cornucopia", "Dairy Farm", "Dairy Kingdom", "Friesland", "God's Country", "Green Acres", "Greener Pastures", "Lactophily", "Lactopia", "Land of Plenty", "Pastoral Romance", "Pasturelands", "Plenty", "Schleswig-Holstein", "The Dairy", "The Ranch"],
+	PhysicalIdealist: ["Aegina", "Amazonia", "Athletica", "Buff Riders", "Buffton", "Cardiode", "Dahomey", "Exercise Ball", "Exercise Bend", "Exercism", "Fitness Center", "Gargarei", "Gymnasiade", "Iron Pumps", "Midgard", "Muscle Beach", "Muscle Shoals", "Olympia", "Performance Peak", "Protein Lake", "Skid Row", "Sparta", "Sthenolagny", "The Gymnasium", "Them Gains", "Themyscira", "Valhalla", "Work Out"],
+	ChattelReligionist: ["Blessings", "City on a Hill", "Deus Vult", "Eden", "Glory", "Heaven on Earth", "Heaven", "Holiness", "Light of the World", "New Covenant", "Pilgrim's Progress", "Prayer Service", "Redemption", "Salt and Light", "Salt of the Earth", "Salvation", "The Holy City", "The Light", "World to Come", "Worship"],
+	RomanRevivalist: ["Abila", "Aeminium", "Aequum", "Agrigentum", "Ala", "Albanianis", "Ambianum", "Antaeopolis", "Antiochia", "Apulum", "Aquileia", "Argentoratum", "Ariminum", "Arsinoë", "Ascrivium", "Asculum", "Attalia", "Augusta Vindelicorum", "Barium", "Belum", "Berytus", "Biriciana", "Blestium", "Bonna", "Bononia", "Bovium", "Brixia", "Burgodunum", "Byzantium", "Caesaraugusta", "Caesarea", "Caesaromagus", "Calleva Atrebatum", "Camulodunum", "Capua", "Carthago Nova", "Catana", "Celeia", "Cibalae", "Clausentum", "Comum", "Condate", "Conimbriga", "Constantinopolis", "Corduba", "Coria", "Coriovallum", "Danum", "Deva Victrix", "Divodurum", "Dubris", "Durnovaria", "Durocornovium", "Duroliponte", "Dyrrachium", "Eboracum", "Eburobrittium", "Elysian Fields", "Emona", "Epidaurum", "Florentia", "Gerulata", "Gerunda", "Isca Augusta", "Italica", "Iuvavum", "Lacobrica", "Lagentium", "Lauri", "Lentia", "Leptis Magna", "Letocetum", "Lindinis", "Londinium", "Longaricum", "Lopodunum", "Lousonna", "Lugdunum", "Luguvalium", "Lutetia", "Mancunium", "Marsonia", "Massa", "Massalia", "Matilo", "Mediolanum", "Messana", "Mod", "Mogontiacum", "Moridunum", "Mursa", "Naissus", "Nauportus", "Neapolis", "Neviodunum", "Nicaea", "Nicomedia", "Nida", "Nova Roma", "Novaesium", "Noviomagus", "Olicana", "Olisippo", "Ostia", "Partiscum", "Patavium", "Pistoria", "Placentia", "Poetovio", "Polemonion", "Pomaria", "Pompeii", "Ragusium", "Ravenna", "Regulbium", "Rhegium", "Rutupiae", "Salernum", "Scalabis", "Segovia", "Sirmium", "Siscia", "Spalatum", "Sumelocenna", "Syracusae", "Tarraco", "Tarsus", "The City of the Seven Hills", "Theranda", "Thuburbo Majus", "Thubursicum", "Tilurium", "Tingi", "Traiectum", "Trapezus", "Turicum", "Venta Icenorum", "Verulamium", "Vesontio", "Vindobona", "Vinovia", "Volubilis"],
+	AztecRevivalist: ["Acolmiztli", "Acozac", "Amaquemecan", "Anenecuilco", "Azcapotzalco", "Aztlan", "Calixtlahuaca", "Chalco", "Chapultepec", "Chicomoztoc", "Cholula", "Coixtlahuaca", "Coyoacan", "Coyoacán", "Cuautla", "Culhuacan", "Cuzcatlan", "Ecatepec", "Huitzilopochco", "Itzcahuacan", "Itztapalapan", "Iztapalapa", "Kaminaljuyu", "Malinalco", "Mexicatzinco", "Nojpetén", "Ocotelolco", "Ocuituco", "Omeyocan", "Otompan", "Oxwitik", "Quiahuiztlan", "Tacuba", "Tamoanchan", "Tenayuca", "Tenochtitlan", "Teopanzolco", "Teotihuacan", "Tepeticpac", "Tepetlaoztoc", "Tepozteco", "Texcoco", "Texcotzingo", "The Halls of Montezuma", "Tizatlan", "Tlacopan", "Tlalmanalco", "Tlatelolco", "Tollan", "Utatlán", "Xalapa", "Xaltocan", "Xochimilco", "Zacpeten"],
+	EgyptianRevivalist: ["Aaru", "Abdju", "Abu", "Aka", "Akhetaten", "Amenemhat-itj-tawy", "Aneb-Hetch", "Ankh-Tawy", "Anpet", "Apu", "Aushamem", "Baki", "Behdet", "Behedet-jabtet", "Buhen", "Chenem-Waset", "Dehenet", "Dep", "Dja", "Djanet", "Djed-Sut", "Djedu", "Djerty", "Djew-Qa", "Gebtu", "Gesa", "Gesy", "Hapi", "Hebenu", "Henen-nesut", "Herwer", "Hut-hery-ib", "Hut-ka-ptah", "Hut-Repyt", "Hut-Sekhem", "Hut-waret", "Iken", "Imet", "Imu", "Imura", "Inbu-Hedj", "Ipet-Resyt", "Ipu", "Itjtawy", "Iu-miteru", "Iunet", "Iunu", "Iuny", "Iunyt", "Iushenshen", "Khasut", "Khem", "Khemenu", "Khent-min", "Kheny", "Khenyt", "Khito", "Khmun", "Kis", "Madu", "Men-nefer", "Menfe", "Mer-nefer", "Mesen", "Moph", "Napata", "Nay-Ta-Hut", "Nekheb", "Nekhen", "Nubt", "Pe", "Peguat", "Pekher-wer", "Per-Amun", "Per-Atum", "Per-Banebdjedet", "Per-Bast", "Per-Bastet", "Per-Hathor", "Per-Imen-mat-khent", "Per-Medjed", "Per-Nemty", "Per-Ra-mes-su", "Per-Ramesses", "Per-Sopdu", "Per-Usiri", "Per-Wadjet", "Piemro", "Pikaut", "Pikuat", "Pselqet", "Ptah", "Ptkheka", "Qedesh", "Qedshu", "Qis", "Râ-Kedet", "Raqote", "Rebu", "Saka", "Sangar", "Semabehdet", "Senet", "Sepermeru", "Seshesh", "Šetennu", "Shashotep", "Shasu", "Shedet", "Sheten", "Sumenu", "Swenett", "Ta-senet", "Tamiat", "Taremu", "Tayu-djayet", "Tepihu", "Timinhor", "Tjaru", "Tjebnutjer", "Tjebu", "Tjeku", "Tjenu", "Tpyhwt", "Waset", "Weprehwy", "Yamu", "Ypu", "Zau", "Zauti", "Zawty", "Zay"],
+	EdoRevivalist: ["Amano-Iwato", "Ando", "Asakura", "Asuka", "Dejima", "Edo", "Hakodate", "Heian-kyō", "Heijō-kyō", "Hiraizumi", "Hirakata", "Idano", "Ise", "Isonokami", "Itsukushima", "Iware", "Izakaha", "Karu", "Karushima", "Kasagiyama", "Kashihara", "Katashiha", "Kawagoe", "Kawanakajima", "Kazuraki", "Kobe", "Kokyo", "Koryo", "Kuni-kyō", "Kuruda", "Kyotanabe", "Mahoroba", "Makimuko", "Mikatagahara", "Miki", "Miyajima", "Miyako", "Muro", "Nagaoka-kyō", "Nagashima", "Nagashino", "Nakatsukuni", "Naniwa", "Nara", "Negoro", "Neo Tokyo", "New Kyoto", "New Tokyo", "Odawara", "Okazaki", "Okehazama", "Onogoro", "Osaka", "Otsu", "Ryūgū-jō", "Sakurai", "Sekigahara", "Shiga", "Shika", "Shiki", "Shikoku", "Shimonoseki", "Shuri", "Sunpu", "Tajihi", "Takama-ga-hara", "Tanegashima", "Tengoku", "Tenmokuzan", "Tenri", "The Imperial Palace", "Ujiyamada", "Urasoe", "Waki-no-kami", "Yamazaki", "Yawata", "Yoshino"],
+	ArabianRevivalist: ["Abha", "Achir", "Al Bahah", "Al-Hasa", "Al-Mansuriya", "Al-Qata'i", "Aleppo", "Alhambra", "Amadiya", "Amid", "Arar", "Arbil", "Ardabil", "Arjish", "Arzan", "Badr", "Baghdad", "Basra", "Bayt al-Hikma", "Béjaïa", "Beni Hammad", "Buraidah", "Cairo", "Córdoba", "Damascus", "Dammam", "Dhala", "Diyarbakır", "El-Mansuriya", "Faiyum", "Fes-al-Bali", "Fes", "Fez", "Fustat", "Ha'il", "Hajar an-Nasar", "Hama", "Harput", "Harran", "Hasankeyf", "Hejaz", "Ifriqiya", "Isfahan", "Jannah", "Jenin", "Jerusalem", "Jizan", "Jubayl", "Kairouan", "Karbala", "Khilat", "Kirkuk", "Kufa", "Madinah", "Madinat al-Hareer", "Madinat al-Salam", "Madinat al-Yasmin", "Madinat al-Zahra", "Mahdia", "Makkah", "Manzikart", "Maragha", "Mardin", "Marrakech", "Marrakesh", "Marsala", "Mayyafariqin", "Mecca", "Medina", "Mosul", "Murakuc", "Najran", "Nekor", "Qatif", "Qazvin", "Raqqa", "Raqqada", "Resafa", "Riyadh", "Sakakah", "Samarra", "Saqifah", "Say'un", "Sidon", "Sulaimaniyah", "Suq Abdulla", "Tabriz", "Tabuk", "Tahert", "Tarim", "Temsaman", "Tlemcen", "Tunis", "Walilli", "Zabid"],
+	ChineseRevivalist: ["Acheng", "Anyang", "Anyi", "Balasagun", "Beijing", "Bian", "Bianjing", "Bianzhou", "Binzhou", "Bogu", "Boping", "Chang'an", "Changle", "Changping", "Changsha", "Chengdu", "Chengzhou", "Chuqiu", "Dadu", "Daliang", "Daming", "Danyang", "Datong", "Daxing", "Dinglian", "Diqiu", "Dongdu", "Dongjing", "Dujianshan", "Dunhuang", "Ezhou", "Fanyang", "Feng Huang", "Fenghao", "Fengxiang", "Fuhan", "Fusang", "Guanggu", "Guangling", "Guangzhou", "Gusu", "Guzang", "Handan", "Hangzhou", "Haojing", "Hefei", "Henglong", "Hezhou", "Huanbei", "Huangquan", "Huangzhong", "Huatai", "Huokang", "Ji", "Jian", "Jiang", "Jiangling", "Jiangning", "Jiankang", "Jianye", "Jicheng", "Jin Shan", "Jinan", "Jincheng", "Jingsha", "Jingzhao", "Jingzhou", "Jinling", "Jinyang", "Jiuquan", "Kaifeng", "Khanbaliq", "Kuaiji", "Laosicheng", "Ledu", "Lianchuan", "Liaodong", "Liaoyang", "Lin'an", "Linhuang", "Linxiang", "Linzi", "Lishi", "Liting", "Longcheng", "Lujiang", "Luoyang", "Luoyi", "Luyi", "Mingfu", "Moling", "Mount Tai", "Nan'an", "Nanchang", "Nanjing", "Nanjun", "Nanyang", "Panyu", "Peking", "Pengcheng", "Pingcheng", "Pingjiang", "Pingliang", "Pingyang", "Pingzhou", "Puzi", "Qi Lin", "Qian", "Qiantang", "Qiling", "Qin", "Quanqiu", "Qufu", "Quwo", "Ruyin", "Shangcai", "Shanggui", "Shangjing", "Shangqiu", "Shengjing", "Shengle", "Shouchun", "Suzhou", "Taiyuan", "Tang", "Tanheli", "Tanjiao", "Tanzhou", "Taoqiu", "The Forbidden Palace", "The Middle Kingdom", "Tianlin", "Tongwan", "Wanchuan", "Wangcheng", "Wanqiu", "Wu", "Wuchang", "Wudu", "Xi'an", "Xiacai", "Xiangguo", "Xiangning", "Xiangping", "Xianyang", "Xibo", "Xicheng", "Xin Hua", "Xincai", "Xingqing", "Xingwang", "Xintian", "Xinzheng", "Xiping", "Xuchang", "Yangcheng", "Yangzhai", "Yanjing", "Yanshi", "Yecheng", "Yewang", "Yin", "Yinfu", "Ying", "Yingdu", "Yingqiu", "Yingtian", "Yong", "Yongshicheng", "You", "Youdu", "Youming", "Youzhou", "Yueyang", "Yuezhou", "Yuhang", "Yushan", "Zhangye", "Zhangzi", "Zhaoge", "Zhending", "Zheng", "Zhenxun", "Zhongdu", "Zhongguo", "Zhongshan", "Zibo", "Zichuan"],
+	NeoImperialist: ["Atteln", "Aermacht", "Aubevoie", "Bellechassange", "Black Rock", "Black Hollow", "Bourbon", "Colme", "Daybrook", "Drancy", "Easthaven", "Eastwatch", "Etau", "Elsing", "Essenge", "Ettenmont", "Erceti", "Fernsworth", "Hersengeux", "Ironforge", "Ironhaven", "Irontown", "Karlsberg", "Klattenhof", "Lhanbryde", "Lindham", "Marrensville", "Maushof", "Morlaincourt", "Munschwitz", "Neo-Berlin", "Neo-London", "Neo-Paris", "Neo-Madrid", "Neo-York", "Northscot", "Oberlandscheid", "Obersberg", "Poisseau", "Redwater", "Sali", "Sand and Stone", "Shominster", "Sommelie", "Strathsmore", "Sunstown", "Sonnehof", "Sonneshaven", "Tottenham", "The Rock", "The Golden Spire", "Villeurmont", "Vivonne", "Volkersch", "Volkhof", "Volksgard", "Wallenberg", "Wallenhof", "Wilderf", "Wycombe", "Zillendorf", "Zerf"],
+
+	/* pregmod FS */
+	Eugenics: ["Ascension", "Elitism", "Eugenica", "Eugeniculate", "Galton City", "Germinal Choice", "Good Stock", "Improvement", "Lebensborn", "Natural Selection", "Oneida Community", "Perfection", "Powered Elite", "Private Gene Pool", "Quality", "Rebirth", "Reprogenetics", "Second Chance", "Selection Rule", "Stirpiculture"],
+	Repopulationist: ["Cultural Mandate", "Fruitful and Multiply", "Future", "Glorious Mother", "Haven of the Pregnant", "Holders of the Future", "Hope", "Motherhood", "Multiplication", "Preggonia", "Public Gene Pool", "Quantity", "Rabbit Hole", "Repoblación", "Sacred Womb", "The Womb"],
+	HedonisticDecadence: ["All You Can Eat", "Aristippa", "Buffet", "Chubby Hole", "Cyrene", "Decadence", "Epicurea", "Gavage", "Glorious Food", "Gluttony", "Hedonic Calculator", "Hedonism Resort", "Hedonism Spot", "Indulgence", "Leblouh", "Libertinage", "New Wisconsin", "Pleasure", "Plumpland", "Sloth", "Smörgåsbord", "Stuffedtopia", "Yang"],
+	Cummunism: ["Arscrotzka", "Crusty Cummies", "Cumbria", "Cuming Inlet", "Cummins", "Cummunist Russwhore", "Cumstantine", "Cumstantinople", "Da Cumrade", "Erection Fluid", "Free Slave Central", "Jizzakh", "Jizzebel", "Jizzington upon Wank", "Mother Cumtry", "Semen Supreme", "Semenyih", "Sperm Atrium", "Sperm Banks", "Spermato Zoo", "Wankara"],
+	IncestFetishist: ["All in the Family", "Blood Relations", "Consanguinity", "East Westermarck", "Electra Complex", "Familial Embrace", "Family Fortunes", "Family Ties", "Heredity", "Incestia", "Incestral Home", "Jocasta Complex", "Kinship", "Oedipal City", "Oedipus Complex", "Oeditropolis", "Pure Blood", "Sib City", "Snokhachestvo", "Tenth Abomination", "Unlash of Clans", "Wincest"],
+	IntellectualDependency: ["Barbie World", "Bimbo Land", "Bimbotopia", "Dim City", "Dumbarton", "Dummy Thicc", "Followers of Bacchus", "Fool on the Hill", "Fun and Games", "Gump Forest", "Idiot City", "Imbecile Mile", "Loosu Pond", "Pretty in Pink", "Promiscuous", "Sex Essex", "Stupid Hoedown", "The Dropout", "Valley World"],
+	SlaveProfessionalism: ["Braintree", "Diploma Mill", "Disciplined Minds", "Einstein Beach", "Followers of Minerva", "Genius Loci", "House of Wisdom", "Ingenium", "Intellectua", "Intelligence Star", "Intelligentsia", "Library of Alexandria", "Mensa Mesa", "Secretary State", "Smart City", "Smart Grid"],
+	PetiteAdmiration: ["Bantam Battalion", "Dwarf Forest", "Dwarf Fortress", "Elf Village", "Haunchyville", "Midget Utopia", "Midgetville", "Mini World", "Munchkinland", "Napoleon Complex", "Petite Mesa", "Petite Pride", "Pygmalion", "Short Circuit", "Short Stirling", "The Short Stack", "Tiny Town"],
+	StatuesqueGlorification: ["Basketball Court", "Castelnau", "Giant's Causeway", "Giraffe Manor", "Height Is Right", "High Hopes", "Highland", "Potsdam Battalion", "Rhodes", "St. Michael's Mount", "Tall Poppy Field", "Tall Trees", "Talleres", "The Bean Stalk", "The Heights", "Valley of Elah"],
+};
diff --git a/js/003-data/careers.js b/js/003-data/careers.js
new file mode 100644
index 0000000000000000000000000000000000000000..8764824377cbd086155963e1900c364cccbd1446
--- /dev/null
+++ b/js/003-data/careers.js
@@ -0,0 +1,50 @@
+App.Data.Careers = {
+	General: {
+		veryYoung: ["a babysitter", "a beggar", "a beggar", "a bully hunter", "a bully", "a camp counselor", "a cheerleader", "a child actress", "a child prodigy", "a child prostitute", "a child prostitute", "a child soldier", "a child soldier", "a club manager", "a club recruiter", "a club treasurer", "a cum dump", "a dropout", "a dropout", "a drug mule", "a farmer's daughter", "a girl scout", "a girl scout", "a hall monitor", "a handmaiden", "a hospital volunteer", "a housesitter", "a juvenile delinquent", "a juvenile delinquent", "a latchkey kid", "a lemonade stand operator", "a marching band leader", "a meat toilet", "a military brat", "a model-UN star", "a model", "a noblewoman", "a pageant star", "a paper girl", "a part-time farm laborer", "a pick-pocket", "a refugee", "a refugee", "a refugee", "a school nurse's assistant", "a shrine maiden", "a street thug", "a street urchin", "a street urchin", "a street urchin", "a student council president", "a student from a boarding school", "a student from a private school", "a student from a public school", "a student from a public school", "a student from a public school", "a student from a public school", "a student from a public school", "a sweatshop worker", "a sweatshop worker", "a sweatshop worker", "a sweatshop worker", "a teacher's pet", "an apprentice", "an aspiring pop star", "an idol", "an orphan", "an orphan", "an orphan", "an orphan", "an orphan", "being homeschooled by her parents", "captain of the kendo club", "from a lower class family", "from a lower class family", "from a lower class family", "from a middle class family", "from a middle class family", "from an upper class family", "homeless", "homeless", "homeless"],
+
+		young: ["a babysitter", "a ballerina", "a barista", "a bartender", "a beggar", "a blogger", "a butler", "a camgirl", "a camp counselor", "a camwhore", "a cashier", "a cheerleader", "a cocktail waitress", "a comedian", "a con artist", "a cook", "a courier", "a cowgirl", "a criminal", "a croupier", "a cum dump", "a dairy worker", "a dancer", "a delivery woman", "a dominatrix", "a drug mule", "a factory worker", "a farm laborer", "a farm laborer", "a farmer's daughter", "a florist", "a gang member", "a gang member", "a gardener", "a groomer", "a gymnast", "a handmaiden", "a house DJ", "a housesitter", "a housewife", "a law enforcement officer", "a lifeguard", "a magician's assistant", "a maid", "a mail-order bride", "a masseuse", "a meat toilet", "a mechanic", "a medical student", "a mistress", "a model", "a musician", "a noblewoman", "a nun", "a nurse", "a paramedic", "a party girl", "a personal assistant", "a personal trainer", "a pirate", "a political activist", "a porn star", "a prisoner", "a programmer", "a prostitute", "a racing driver", "a reality show star", "a receptionist", "a refugee", "a ride attendant", "a saleswoman", "a school nurse", "a secretary", "a security guard", "a service worker", "a shrine maiden", "a shut-in", "a soldier", "a street performer", "a street vendor", "a stripper", "a student", "a student", "a student", "a switchboard operator", "a teaching assistant", "a tour guide", "a trophy wife", "a truck driver", "a video game streamer", "a waitress", "a wet nurse", "a yoga instructor", "an actress", "an air hostess", "an apprentice", "an arcade attendant", "an artist", "an aspiring pop star", "an assassin", "an athlete", "an au pair", "an escort", "an exotic dancer", "an idol", "an installation technician", "an intern", "an office worker", "homeless", "in a militia", "unemployed", "unemployed", "unemployed", "unemployed", "unemployed"],
+
+		educated: ["a ballerina", "a banker", "a bureaucrat", "a business owner", "a businessman", "a captain", "a chemist", "a chief of police", "a classical dancer", "a classical musician", "a coach", "a college scout", "a concierge", "a coroner", "a corporate executive", "a cosmetologist", "a counselor", "a criminal", "a critic", "a cult leader", "a dean", "a dentist", "a dentist", "a director", "a dispatch officer", "a doctor", "a historian", "a housekeeper", "a journalist", "a journalist", "a judge", "a lawyer", "a librarian", "a lobbyist", "a madam", "a manager", "a mechanic", "a mediator", "a medical student", "a mercenary", "a military officer", "a military recruiter", "a nanny", "a noblewoman", "a nun", "a painter", "a paramedic", "a personal assistant", "a pharmacist", "a photographer", "a physician", "a pilot", "a poet", "a police detective", "a police negotiator", "a police officer", "a political activist", "a politician", "a practitioner", "a principal", "a prison warden", "a private detective", "a private instructor", "a procuress", "a producer", "a professional bartender", "a professor", "a programmer", "a prostitute", "a psychologist", "a refugee", "a scholar", "a scientist", "a sculptor", "a secretary", "a serial divorcee", "a shut-in", "a stockbroker", "a surgeon", "a teacher", "a teaching assistant", "a therapist", "a train conductor", "a transporter", "a veterinarian", "a wedding planner", "a writer", "a zookeeper", "an actress", "an air hostess", "an animator", "an archaeologist", "an architect", "an artist", "an assassin", "an astronaut", "an economist", "an editor", "an engineer", "an escort", "an estate agent", "an investor", "an MS pilot", "an office worker", "an orchestra conductor", "retired", "unemployed"],
+
+		uneducated: ["a baker", "a barber", "a barista", "a bartender", "a beekeeper", "a beggar", "a blacksmith", "a blogger", "a bodyguard", "a bouncer", "a bounty hunter", "a boxer", "a brewer", "a bullfighter", "a bus driver", "a butcher", "a butler", "a camgirl", "a camp counselor", "a camwhore", "a candlestick maker", "a caregiver", "a carpenter", "a cashier", "a charity worker", "a chauffeur", "a cheerleader", "a chiropractor", "a clown", "a cobbler", "a cocktail waitress", "a comedian", "a con artist", "a construction worker", "a cook", "a cowgirl", "a criminal", "a croupier", "a cum dump", "a dairy worker", "a dancer", "a delivery woman", "a dominatrix", "a driller", "a drug mule", "a factory worker", "a farm laborer", "a farmer's daughter", "a farmer", "a firefighter", "a fisherwoman", "a florist", "a fortune teller", "a gang leader", "a gang member", "a gardener", "a gravedigger", "a groomer", "a gymnast", "a handmaiden", "a hotel manager", "a house DJ", "a housewife", "a hunter", "a janitor", "a landlady", "a launderer", "a law enforcement officer", "a lifeguard", "a local news anchor", "a lumberjack", "a magician's assistant", "a maid", "a mail carrier", "a mail-order bride", "a masseuse", "a masseuse", "a meat toilet", "a medic", "a medic", "a medium", "a messenger", "a midwife", "a milkmaid", "a mime", "a miner", "a missionary", "a mistress", "a model", "a mortician", "a musician", "a nanny", "a nurse", "a paramedic", "a park ranger", "a party girl", "a peddler", "a personal trainer", "a pimp", "a pirate", "a plumber", "a political activist", "a prison guard", "a prisoner", "a procuress", "a prostitute", "a racing driver", "a radio show host", "a rancher", "a receptionist", "a referee", "a refugee", "a repairman", "a revolutionary", "a ride attendant", "a roadie", "a rodeo star", "a sailor", "a saleswoman", "a school nurse", "a seamstress", "a secretary", "a security guard", "a service worker", "a shepherd", "a shrine maiden", "a soldier", "a stage magician", "a street performer", "a street vendor", "a stripper", "a student", "a student athlete", "a stuntwoman", "a switchboard operator", "a tailor", "a talent scout", "a taxi driver", "a teacher", "a tour guide", "a trophy wife", "a truck driver", "a waitress", "a weathergirl", "a welder", "a wet nurse", "a whaler", "a wrestler", "a zookeeper", "an acrobat", "an actress", "an arcade attendant", "an artist", "an aspiring pop star", "an athlete", "an electrician", "an enforcer", "an enforcer", "an escort", "an exotic dancer", "an exterminator", "an innkeeper", "an installation technician", "an office worker", "an orderly", "homeless", "in a militia", "retired", "unemployed", "unemployed", "unemployed", "unemployed", "unemployed"],
+
+		grateful: ["a beggar", "a drug mule", "a peddler", "a pick-pocket", "a prisoner", "a refugee", "a shut-in", "a street urchin", "a student from a boarding school", "a sweatshop worker", "a thief", "an orphan", "from a lower class family", "homeless", "unemployed"],
+
+		menial: ["a baker", "a blacksmith", "a bus driver", "a butcher", "a candlestick maker", "a carpenter", "a cashier", "a chauffeur", "a cobbler", "a construction worker", "a courier", "a croupier", "a delivery woman", "a driller", "a dropout", "a factory worker", "a farm laborer", "a firefighter", "a fisherwoman", "a florist", "a gardener", "a gravedigger", "a janitor", "a launderer", "a lumberjack", "a mail carrier", "a mechanic", "a messenger", "a miner", "a nun", "a paper girl", "a part-time farm laborer", "a pilot", "a plumber", "a private", "a programmer", "a receptionist", "a referee", "a repairman", "a ride attendant", "a roadie", "a sailor", "a seamstress", "a service worker", "a street vendor", "a student from a private school", "a student from a public school", "a student", "a switchboard operator", "a tailor", "a taxi driver", "a terrorist", "a tour guide", "a train conductor", "a truck driver", "a welder", "a whaler", "an apprentice", "an arcade attendant", "an electrician", "an engineer", "an exterminator", "an installation technician", "an intern"],
+
+		entertainment: ["a ballerina", "a blogger", "a camgirl", "a camwhore", "a cheerleader", "a child actress", "a clown", "a cocktail waitress", "a comedian", "a gymnast", "a journalist", "a local news anchor", "a magician's assistant", "a medium", "a mime", "a painter", "a party girl", "a photographer", "a poet", "a racing driver", "a sculptor", "a stage magician", "a street performer", "a student athlete", "a stuntwoman", "a video game streamer", "a waitress", "a weathergirl", "a wrestler", "a writer", "an acrobat", "an actress", "an animator", "an artist", "an athlete"],
+
+		whore: ["a bimbo", "a child prostitute", "a criminal", "a cum dump", "a Futanari Sister", "a juvenile delinquent", "a mail-order bride", "a meat toilet", "a mistress", "a model", "a pageant star", "a pirate", "a porn star", "a prostitute", "a reality show star", "a saleswoman", "a serial divorcee", "a stripper", "a trophy wife", "an escort", "an exotic dancer"],
+
+		/* 	otherCareers: ["a producer", "being homeschooled by her parents", "from a middle class family", "from an upper class family"]>> */
+	},
+	Leader: {
+		HG: ["a captain", "a corporate executive", "a director", "a dominatrix", "a gang leader", "a judge", "a lawyer", "a leading arcology citizen", "a military officer", "a model-UN star", "a noblewoman", "a politician", "a Queen", "a slaver", "a student council president"],
+
+		madam: ["a banker", "a business owner", "a businessman", "a camp counselor", "a club manager", "a hotel manager", "a landlady", "a madam", "a manager", "a park ranger", "a pimp", "a procuress", "a stockbroker", "an innkeeper"],
+
+		DJ: ["a classical dancer", "a classical musician", "a dancer", "a house DJ", "a marching band leader", "a musician", "a radio show host", "an aspiring pop star", "an idol", "an orchestra conductor"],
+
+		bodyguard: ["a bodyguard", "a boxer", "a bully hunter", "a child soldier", "a hitman", "a kunoichi", "a law enforcement officer", "a military brat", "a prince", "a revolutionary", "a sniper", "a soldier", "a transporter", "an assassin", "an MS pilot", "captain of the kendo club", "in a militia", "spec ops"],
+
+		wardeness: ["a bouncer", "a bounty hunter", "a bully", "a chief of police", "a gang member", "a hall monitor", "a mercenary", "a police detective", "a police officer", "a prison guard", "a prison warden", "a private detective", "a security guard", "a street thug", "an enforcer", "an orderly"],
+
+		nurse: ["a chemist", "a chiropractor", "a coroner", "a dentist", "a doctor", "a hospital volunteer", "a medic", "a medical student", "a midwife", "a mortician", "a nurse", "a paramedic", "a pharmacist", "a physician", "a school nurse's assistant", "a school nurse", "a surgeon"],
+
+		attendant: ["a barber", "a cosmetologist", "a counselor", "a dispatch officer", "a fortune teller", "a groomer", "a latchkey kid", "a lifeguard", "a masseuse", "a mediator", "a personal trainer", "a police negotiator", "a psychologist", "a therapist", "a yoga instructor"],
+
+		matron: ["a babysitter", "a nanny", "a practitioner", "a wet nurse", "an au pair"],
+
+		milkmaid: ["a cowgirl", "a dairy worker", "a farmer's daughter", "a milkmaid", "a shepherd", "a veterinarian"],
+
+		farmer: ["a beekeeper", "a bullfighter", "a farmer", "a farmhand", "a rancher", "a rodeo star", "a zookeeper"],
+
+		stewardess: ["a barista", "a bartender", "a brewer", "a bureaucrat", "a caregiver", "a charity worker", "a club treasurer", "a concierge", "a critic", "a housekeeper", "a housesitter", "a lemonade stand operator", "a personal assistant", "a professional bartender", "a secretary", "a wedding planner", "an air hostess", "an architect", "an editor", "an estate agent", "an investor", "an office worker"],
+
+		schoolteacher: ["a child prodigy", "a coach", "a dean", "a historian", "a librarian", "a principal", "a private instructor", "a professor", "a scholar", "a scientist", "a teacher's pet", "a teacher", "a teaching assistant", "an archaeologist", "an astronaut", "an economist"],
+
+		recruiter: ["a club recruiter", "a college scout", "a con artist", "a cult leader", "a girl scout", "a hunter", "a lobbyist", "a military recruiter", "a missionary", "a political activist", "a princess", "a spy", "a talent scout", "retired"],
+
+		servant: ["a butler", "a cook", "a handmaiden", "a housewife", "a maid", "a shrine maiden"],
+	},
+};
diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index 0a3cbb9e094c031bf45ea4aae64ee9a9640170ee..ea9907a967bfd6fd04f50546bf8307e609ec849d 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -64,6 +64,7 @@ App.Data.defaultGameStateVariables = {
 	FSNamePref: 0,
 	HGFormality: 1,
 	HGSeverity: 0,
+	HGPiercings: 1,
 	abbreviateSidebar: 1,
 	adamPrinciple: 0,
 	allowFamilyTitles: 0,
@@ -358,12 +359,10 @@ App.Data.resetOnNGPlus = {
 	thisWeeksIllegalWares: 0,
 	Sweatshops: 0,
 
-	milkTap: 0,
 	rivalID: 0,
 	eliteAuctioned: 0,
 	slavesSacrificedThisWeek: 0,
 
-	HGtraining: "",
 	mercenariesTitle: "",
 	hormones: 0,
 	FSReminder: 0,
@@ -374,12 +373,8 @@ App.Data.resetOnNGPlus = {
 
 	foughtThisWeek: 0,
 	/* rebellions */
-	slaveRebellionEventFires: 0,
-	citizenRebellionEventFires: 0,
 	slaveRebellion: 0,
 	citizenRebellion: 0,
-	engageRule: 0,
-	irregulars: 0,
 	repairTime: 3,
 	arcRepairTime: 0,
 	garrison: {},
@@ -393,26 +388,8 @@ App.Data.resetOnNGPlus = {
 	createdMilitiaUnits: 0,
 	createdMercUnits: 0,
 	/* battle relevant vars */
-	attackType: "none",
 	attackThisWeek: 0,
 	majorBattle: 0,
-	chosenTactic: "none",
-	leadingTroops: "none",
-	attackTroops: 0,
-	attackEquip: 0,
-	battleTerrain: "none",
-	maxTurns: 10,
-	battleResult: 4, // sets battleResult value outside accepted range (-3,3) to avoid evaluation problems
-	losses: 0,
-	enemyLosses: 0,
-	battleTurns: 0,
-	tacticsSuccessful: 0,
-	leaderWounded: 0,
-	gainedCombat: 0,
-	gainedWarfare: 0,
-	SFIntervention: 0,
-	rebellingID: [],
-	saveValid: 0,
 	/* units */
 	/** @type {FC.SecExp.PlayerUnitData} */
 	secBots: {},
@@ -739,9 +716,6 @@ App.Data.resetOnNGPlus = {
 	FSSingleSlaveRep: 10,
 	FSSpending: 0,
 	FSLockinLevel: 100,
-	FSPromenade: {
-		Subjugationist: 0, Supremacist: 0, GenderRadicalist: 0, GenderFundamentalist: 0, Paternalist: 0, Degradationist: 0, BodyPurist: 0, TransformationFetishist: 0, YouthPreferentialist: 0, MaturityPreferentialist: 0, SlimnessEnthusiast: 0, AssetExpansionist: 0, Pastoralist: 0, PhysicalIdealist: 0, ChattelReligionist: 0, RomanRevivalist: 0, AztecRevivalist: 0, EgyptianRevivalist: 0, EdoRevivalist: 0, ArabianRevivalist: 0, ChineseRevivalist: 0, NeoImperialist: 0, Repopulationist: 0, Eugenics: 0, Hedonism: 0, IntellectualDependency: 0, SlaveProfessionalism: 0, PetiteAdmiration: 0, StatuesqueGlorification: 0
-	},
 
 	// new corporation variables
 	newCorp: 1,
@@ -831,7 +805,7 @@ App.Data.resetOnNGPlus = {
 	cumPipeline: 0,
 	wcPiping: 0,
 	burstee: 0,
-	slaveDeath: 0,
+	slaveDeath: new Map(),
 	playerBred: 0,
 	propOutcome: 0,
 	EliteSires: ["crazy", "futa", "moves", "preggo", "quick", "virgin"],
diff --git a/js/003-data/miscData.js b/js/003-data/miscData.js
index 34d649d8b22098c1c1c3f00accfb9f14eff2f393..0c8a7ae7e12f768ba2ad37b6683e286ded90c31d 100644
--- a/js/003-data/miscData.js
+++ b/js/003-data/miscData.js
@@ -1554,9 +1554,9 @@ App.Data.misc = {
 	fakeBellies: ["a huge empathy belly", "a large empathy belly", "a medium empathy belly", "a small empathy belly"],
 	/* lets fake bellies be separated from other .bellyAccessory */
 
-	modestClothes: ["a ball gown", "a biyelgee costume", "a bunny outfit", "a burkini", "a burqa", "a comfortable bodysuit", "a cybersuit", "a dirndl", "a gothic lolita dress", "a halter top dress", "a hanbok", "a hijab and abaya", "a hijab and blouse", "a huipil", "a kimono", "a klan robe", "a latex catsuit", "a leotard", "a long qipao", "a maternity dress", "a military uniform", "a mini dress", "a mounty outfit", "a nice maid outfit", "a nice nurse outfit", "a niqab and abaya", "a one-piece swimsuit", "a penitent nuns habit", "a police uniform", "a red army uniform", "a schoolgirl outfit", "a schutzstaffel uniform", "a slave gown", "a slutty maid outfit", "a slutty nurse outfit", "a slutty qipao", "a sweater and cutoffs", "a t-shirt and jeans", "a toga", "an oversized t-shirt and boyshorts", "battlearmor", "battledress", "conservative clothing", "cutoffs and a t-shirt", "leather pants and a tube top", "lederhosen", "nice business attire", "restrictive latex", "slutty business attire", "spats and a tank top", "sport shorts and a sports bra", "sport shorts and a t-shirt", "stretch pants and a crop-top", "Imperial Plate",],
+	modestClothes: ["a ball gown", "a biyelgee costume", "a bunny outfit", "a burkini", "a burqa", "a comfortable bodysuit", "a cybersuit", "a dirndl", "a gothic lolita dress", "a halter top dress", "a hanbok", "a hijab and abaya", "a hijab and blouse", "a huipil", "a kimono", "a klan robe", "a latex catsuit", "a leotard", "a long qipao", "a maternity dress", "a military uniform", "a mini dress", "a mounty outfit", "a nice maid outfit", "a nice nurse outfit", "a niqab and abaya", "a one-piece swimsuit", "a penitent nuns habit", "a police uniform", "a red army uniform", "a schoolgirl outfit", "a schutzstaffel uniform", "a slave gown", "a slutty maid outfit", "a slutty nurse outfit", "a slutty qipao", "a sweater and cutoffs", "a t-shirt and jeans", "a toga", "an oversized t-shirt and boyshorts", "battlearmor", "battledress", "conservative clothing", "cutoffs and a t-shirt", "leather pants and a tube top", "lederhosen", "nice business attire", "restrictive latex", "slutty business attire", "spats and a tank top", "sport shorts and a sports bra", "sport shorts and a t-shirt", "stretch pants and a crop-top", "Imperial Plate"],
 
-	sluttyClothes: ["a bimbo outfit", "a chattel habit", "a cheerleader outfit", "a fallen nuns habit", "a schoolgirl outfit", "a skimpy loincloth", "a slutty klan robe", "a slutty maid outfit", "a slutty nurse outfit", "a slutty outfit", "a slutty pony outfit", "a slutty qipao", "a slutty schutzstaffel uniform", "a string bikini", "a succubus outfit", "a t-shirt and panties", "a t-shirt and thong", "a tank-top and panties", "a tube top and thong", "attractive lingerie", "attractive lingerie for a pregnant woman", "clubslut netting", "kitty lingerie", "leather pants and a tube top", "leather pants and pasties", "panties and pasties", "pasties", "slutty business attire", "slutty jewelry", "sport shorts and a sports bra", "striped underwear", "a tight Imperial bodysuit",],
+	sluttyClothes: ["a bimbo outfit", "a chattel habit", "a cheerleader outfit", "a fallen nuns habit", "a schoolgirl outfit", "a skimpy loincloth", "a slutty klan robe", "a slutty maid outfit", "a slutty nurse outfit", "a slutty outfit", "a slutty pony outfit", "a slutty qipao", "a slutty schutzstaffel uniform", "a string bikini", "a succubus outfit", "a t-shirt and panties", "a t-shirt and thong", "a tank-top and panties", "a tube top and thong", "attractive lingerie", "attractive lingerie for a pregnant woman", "clubslut netting", "kitty lingerie", "leather pants and a tube top", "leather pants and pasties", "panties and pasties", "pasties", "slutty business attire", "slutty jewelry", "sport shorts and a sports bra", "striped underwear", "a tight Imperial bodysuit"],
 
 	/* stuff that reveals genitals */
 	humiliatingClothes: ["a bra", "a button-up shirt", "a chattel habit", "a fallen nuns habit", "a skimpy loincloth", "a sports bra", "a string bikini", "a striped bra", "a succubus outfit", "a sweater", "a t-shirt", "a tank-top", "a thong", "a tube top", "clubslut netting", "pasties", "restrictive latex", "shibari ropes", "slutty jewelry", "uncomfortable straps", "Western clothing"],
@@ -1564,54 +1564,6 @@ App.Data.misc = {
 	highHeels: ["boots", "extreme heels", "extreme platform heels", "heels", "platform heels"],
 	heightBoostingShoes: ["extreme heels", "extreme platform heels", "heels", "platform heels", "platform shoes", "pumps"],
 
-	veryYoungCareers: ["a babysitter", "a beggar", "a beggar", "a bully hunter", "a bully", "a camp counselor", "a cheerleader", "a child actress", "a child prodigy", "a child prostitute", "a child prostitute", "a child soldier", "a child soldier", "a club manager", "a club recruiter", "a club treasurer", "a cum dump", "a dropout", "a dropout", "a drug mule", "a farmer's daughter", "a girl scout", "a girl scout", "a hall monitor", "a handmaiden", "a hospital volunteer", "a housesitter", "a juvenile delinquent", "a juvenile delinquent", "a latchkey kid", "a lemonade stand operator", "a marching band leader", "a meat toilet", "a military brat", "a model-UN star", "a model", "a noblewoman", "a pageant star", "a paper girl", "a part-time farm laborer", "a pick-pocket", "a refugee", "a refugee", "a refugee", "a school nurse's assistant", "a shrine maiden", "a street thug", "a street urchin", "a street urchin", "a street urchin", "a student council president", "a student from a boarding school", "a student from a private school", "a student from a public school", "a student from a public school", "a student from a public school", "a student from a public school", "a student from a public school", "a sweatshop worker", "a sweatshop worker", "a sweatshop worker", "a sweatshop worker", "a teacher's pet", "an apprentice", "an aspiring pop star", "an idol", "an orphan", "an orphan", "an orphan", "an orphan", "an orphan", "being homeschooled by her parents", "captain of the kendo club", "from a lower class family", "from a lower class family", "from a lower class family", "from a middle class family", "from a middle class family", "from an upper class family", "homeless", "homeless", "homeless"],
-	/* pregmod */
-
-	youngCareers: ["a babysitter", "a ballerina", "a barista", "a bartender", "a beggar", "a blogger", "a butler", "a camgirl", "a camp counselor", "a camwhore", "a cashier", "a cheerleader", "a cocktail waitress", "a comedian", "a con artist", "a cook", "a courier", "a cowgirl", "a criminal", "a croupier", "a cum dump", "a dairy worker", "a dancer", "a delivery woman", "a dominatrix", "a drug mule", "a factory worker", "a farm laborer", "a farm laborer", "a farmer's daughter", "a florist", "a gang member", "a gang member", "a gardener", "a groomer", "a gymnast", "a handmaiden", "a house DJ", "a housesitter", "a housewife", "a law enforcement officer", "a lifeguard", "a magician's assistant", "a maid", "a mail-order bride", "a masseuse", "a meat toilet", "a mechanic", "a medical student", "a mistress", "a model", "a musician", "a noblewoman", "a nun", "a nurse", "a paramedic", "a party girl", "a personal assistant", "a personal trainer", "a pirate", "a political activist", "a porn star", "a prisoner", "a programmer", "a prostitute", "a racing driver", "a reality show star", "a receptionist", "a refugee", "a ride attendant", "a saleswoman", "a school nurse", "a secretary", "a security guard", "a service worker", "a shrine maiden", "a shut-in", "a soldier", "a street performer", "a street vendor", "a stripper", "a student", "a student", "a student", "a switchboard operator", "a teaching assistant", "a tour guide", "a trophy wife", "a truck driver", "a video game streamer", "a waitress", "a wet nurse", "a yoga instructor", "an actress", "an air hostess", "an apprentice", "an arcade attendant", "an artist", "an aspiring pop star", "an assassin", "an athlete", "an au pair", "an escort", "an exotic dancer", "an idol", "an installation technician", "an intern", "an office worker", "homeless", "in a militia", "unemployed", "unemployed", "unemployed", "unemployed", "unemployed"],
-
-	educatedCareers: ["a ballerina", "a banker", "a bureaucrat", "a business owner", "a businessman", "a captain", "a chemist", "a chief of police", "a classical dancer", "a classical musician", "a coach", "a college scout", "a concierge", "a coroner", "a corporate executive", "a cosmetologist", "a counselor", "a criminal", "a critic", "a cult leader", "a dean", "a dentist", "a dentist", "a director", "a dispatch officer", "a doctor", "a historian", "a housekeeper", "a journalist", "a journalist", "a judge", "a lawyer", "a librarian", "a lobbyist", "a madam", "a manager", "a mechanic", "a mediator", "a medical student", "a mercenary", "a military officer", "a military recruiter", "a nanny", "a noblewoman", "a nun", "a painter", "a paramedic", "a personal assistant", "a pharmacist", "a photographer", "a physician", "a pilot", "a poet", "a police detective", "a police negotiator", "a police officer", "a political activist", "a politician", "a practitioner", "a principal", "a prison warden", "a private detective", "a private instructor", "a procuress", "a producer", "a professional bartender", "a professor", "a programmer", "a prostitute", "a psychologist", "a refugee", "a scholar", "a scientist", "a sculptor", "a secretary", "a serial divorcee", "a shut-in", "a stockbroker", "a surgeon", "a teacher", "a teaching assistant", "a therapist", "a train conductor", "a transporter", "a veterinarian", "a wedding planner", "a writer", "a zookeeper", "an actress", "an air hostess", "an animator", "an archaeologist", "an architect", "an artist", "an assassin", "an astronaut", "an economist", "an editor", "an engineer", "an escort", "an estate agent", "an investor", "an MS pilot", "an office worker", "an orchestra conductor", "retired", "unemployed"],
-
-	uneducatedCareers: ["a baker", "a barber", "a barista", "a bartender", "a beekeeper", "a beggar", "a blacksmith", "a blogger", "a bodyguard", "a bouncer", "a bounty hunter", "a boxer", "a brewer", "a bullfighter", "a bus driver", "a butcher", "a butler", "a camgirl", "a camp counselor", "a camwhore", "a candlestick maker", "a caregiver", "a carpenter", "a cashier", "a charity worker", "a chauffeur", "a cheerleader", "a chiropractor", "a clown", "a cobbler", "a cocktail waitress", "a comedian", "a con artist", "a construction worker", "a cook", "a cowgirl", "a criminal", "a croupier", "a cum dump", "a dairy worker", "a dancer", "a delivery woman", "a dominatrix", "a driller", "a drug mule", "a factory worker", "a farm laborer", "a farmer's daughter", "a farmer", "a firefighter", "a fisherwoman", "a florist", "a fortune teller", "a gang leader", "a gang member", "a gardener", "a gravedigger", "a groomer", "a gymnast", "a handmaiden", "a hotel manager", "a house DJ", "a housewife", "a hunter", "a janitor", "a landlady", "a launderer", "a law enforcement officer", "a lifeguard", "a local news anchor", "a lumberjack", "a magician's assistant", "a maid", "a mail carrier", "a mail-order bride", "a masseuse", "a masseuse", "a meat toilet", "a medic", "a medic", "a medium", "a messenger", "a midwife", "a milkmaid", "a mime", "a miner", "a missionary", "a mistress", "a model", "a mortician", "a musician", "a nanny", "a nurse", "a paramedic", "a park ranger", "a party girl", "a peddler", "a personal trainer", "a pimp", "a pirate", "a plumber", "a political activist", "a prison guard", "a prisoner", "a procuress", "a prostitute", "a racing driver", "a radio show host", "a rancher", "a receptionist", "a referee", "a refugee", "a repairman", "a revolutionary", "a ride attendant", "a roadie", "a rodeo star", "a sailor", "a saleswoman", "a school nurse", "a seamstress", "a secretary", "a security guard", "a service worker", "a shepherd", "a shrine maiden", "a soldier", "a stage magician", "a street performer", "a street vendor", "a stripper", "a student", "a student athlete", "a stuntwoman", "a switchboard operator", "a tailor", "a talent scout", "a taxi driver", "a teacher", "a tour guide", "a trophy wife", "a truck driver", "a waitress", "a weathergirl", "a welder", "a wet nurse", "a whaler", "a wrestler", "a zookeeper", "an acrobat", "an actress", "an arcade attendant", "an artist", "an aspiring pop star", "an athlete", "an electrician", "an enforcer", "an enforcer", "an escort", "an exotic dancer", "an exterminator", "an innkeeper", "an installation technician", "an office worker", "an orderly", "homeless", "in a militia", "retired", "unemployed", "unemployed", "unemployed", "unemployed", "unemployed"],
-
-	gratefulCareers: ["a beggar", "a drug mule", "a peddler", "a pick-pocket", "a prisoner", "a refugee", "a shut-in", "a street urchin", "a student from a boarding school", "a sweatshop worker", "a thief", "an orphan", "from a lower class family", "homeless", "unemployed"],
-
-	menialCareers: ["a baker", "a blacksmith", "a bus driver", "a butcher", "a candlestick maker", "a carpenter", "a cashier", "a chauffeur", "a cobbler", "a construction worker", "a courier", "a croupier", "a delivery woman", "a driller", "a dropout", "a factory worker", "a farm laborer", "a firefighter", "a fisherwoman", "a florist", "a gardener", "a gravedigger", "a janitor", "a launderer", "a lumberjack", "a mail carrier", "a mechanic", "a messenger", "a miner", "a nun", "a paper girl", "a part-time farm laborer", "a pilot", "a plumber", "a private", "a programmer", "a receptionist", "a referee", "a repairman", "a ride attendant", "a roadie", "a sailor", "a seamstress", "a service worker", "a street vendor", "a student from a private school", "a student from a public school", "a student", "a switchboard operator", "a tailor", "a taxi driver", "a terrorist", "a tour guide", "a train conductor", "a truck driver", "a welder", "a whaler", "an apprentice", "an arcade attendant", "an electrician", "an engineer", "an exterminator", "an installation technician", "an intern"],
-
-	entertainmentCareers: ["a ballerina", "a blogger", "a camgirl", "a camwhore", "a cheerleader", "a child actress", "a clown", "a cocktail waitress", "a comedian", "a gymnast", "a journalist", "a local news anchor", "a magician's assistant", "a medium", "a mime", "a painter", "a party girl", "a photographer", "a poet", "a racing driver", "a sculptor", "a stage magician", "a street performer", "a student athlete", "a stuntwoman", "a video game streamer", "a waitress", "a weathergirl", "a wrestler", "a writer", "an acrobat", "an actress", "an animator", "an artist", "an athlete"],
-
-	whoreCareers: ["a bimbo", "a child prostitute", "a criminal", "a cum dump", "a Futanari Sister", "a juvenile delinquent", "a mail-order bride", "a meat toilet", "a mistress", "a model", "a pageant star", "a pirate", "a porn star", "a prostitute", "a reality show star", "a saleswoman", "a serial divorcee", "a stripper", "a trophy wife", "an escort", "an exotic dancer"],
-
-	HGCareers: ["a captain", "a corporate executive", "a director", "a dominatrix", "a gang leader", "a judge", "a lawyer", "a leading arcology citizen", "a military officer", "a model-UN star", "a noblewoman", "a politician", "a Queen", "a slaver", "a student council president"],
-
-	madamCareers: ["a banker", "a business owner", "a businessman", "a camp counselor", "a club manager", "a hotel manager", "a landlady", "a madam", "a manager", "a park ranger", "a pimp", "a procuress", "a stockbroker", "an innkeeper"],
-
-	DJCareers: ["a classical dancer", "a classical musician", "a dancer", "a house DJ", "a marching band leader", "a musician", "a radio show host", "an aspiring pop star", "an idol", "an orchestra conductor"],
-
-	bodyguardCareers: ["a bodyguard", "a boxer", "a bully hunter", "a child soldier", "a hitman", "a kunoichi", "a law enforcement officer", "a military brat", "a prince", "a revolutionary", "a sniper", "a soldier", "a transporter", "an assassin", "an MS pilot", "captain of the kendo club", "in a militia", "spec ops"],
-
-	wardenessCareers: ["a bouncer", "a bounty hunter", "a bully", "a chief of police", "a gang member", "a hall monitor", "a mercenary", "a police detective", "a police officer", "a prison guard", "a prison warden", "a private detective", "a security guard", "a street thug", "an enforcer", "an orderly"],
-
-	nurseCareers: ["a chemist", "a chiropractor", "a coroner", "a dentist", "a doctor", "a hospital volunteer", "a medic", "a medical student", "a midwife", "a mortician", "a nurse", "a paramedic", "a pharmacist", "a physician", "a school nurse's assistant", "a school nurse", "a surgeon"],
-
-	attendantCareers: ["a barber", "a cosmetologist", "a counselor", "a dispatch officer", "a fortune teller", "a groomer", "a latchkey kid", "a lifeguard", "a masseuse", "a mediator", "a personal trainer", "a police negotiator", "a psychologist", "a therapist", "a yoga instructor"],
-
-	matronCareers: ["a babysitter", "a nanny", "a practitioner", "a wet nurse", "an au pair"],
-
-	milkmaidCareers: ["a cowgirl", "a dairy worker", "a farmer's daughter", "a milkmaid", "a shepherd", "a veterinarian"],
-
-	farmerCareers: ["a beekeeper", "a bullfighter", "a farmer", "a farmhand", "a rancher", "a rodeo star", "a zookeeper"],
-
-	stewardessCareers: ["a barista", "a bartender", "a brewer", "a bureaucrat", "a caregiver", "a charity worker", "a club treasurer", "a concierge", "a critic", "a housekeeper", "a housesitter", "a lemonade stand operator", "a personal assistant", "a professional bartender", "a secretary", "a wedding planner", "an air hostess", "an architect", "an editor", "an estate agent", "an investor", "an office worker"],
-
-	schoolteacherCareers: ["a child prodigy", "a coach", "a dean", "a historian", "a librarian", "a principal", "a private instructor", "a professor", "a scholar", "a scientist", "a teacher's pet", "a teacher", "a teaching assistant", "an archaeologist", "an astronaut", "an economist"],
-
-	recruiterCareers: ["a club recruiter", "a college scout", "a con artist", "a cult leader", "a girl scout", "a hunter", "a lobbyist", "a military recruiter", "a missionary", "a political activist", "a princess", "a spy", "a talent scout", "retired"],
-	/* pregmod */
-
-	servantCareers: ["a butler", "a cook", "a handmaiden", "a housewife", "a maid", "a shrine maiden"],
-
-	/* 	otherCareers: ["a producer", "being homeschooled by her parents", "from a middle class family", "from an upper class family"]>> */
-
 	paraphiliaList: ["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "self hating"],
 
 	baseNationalities: ["Afghan", "Albanian", "Algerian", "American", "Andorran", "Angolan", "Antiguan", "Argentinian", "Armenian", "Aruban", "Australian", "Austrian", "Azerbaijani", "Bahamian", "Bahraini", "Bangladeshi", "Barbadian", "Belarusian", "Belgian", "Belizean", "Beninese", "Bermudian", "Bhutanese", "Bissau-Guinean", "Bolivian", "Bosnian", "Brazilian", "British", "Bruneian", "Bulgarian", "Burkinabé", "Burmese", "Burundian", "Cambodian", "Cameroonian", "Canadian", "Cape Verdean", "Catalan", "Central African", "Chadian", "Chilean", "Chinese", "Colombian", "Comorian", "Congolese", "a Cook Islander", "Costa Rican", "Croatian", "Cuban", "Curaçaoan", "Cypriot", "Czech", "Danish", "Djiboutian", "Dominican", "Dominiquais", "Dutch", "East Timorese", "Ecuadorian", "Egyptian", "Emirati", "Equatoguinean", "Eritrean", "Estonian", "Ethiopian", "Fijian", "Filipina", "Finnish", "French", "French Guianan", "French Polynesian", "Gabonese", "Gambian", "Georgian", "German", "Ghanan", "Greek", "Greenlandic", "Grenadian", "Guamanian", "Guatemalan", "Guinean", "Guyanese", "Haitian", "Honduran", "Hungarian", "I-Kiribati", "Icelandic", "Indian", "Indonesian", "Iranian", "Iraqi", "Irish", "Israeli", "Italian", "Ivorian", "Jamaican", "Japanese", "Jordanian", "Kazakh", "Kenyan", "Kittitian", "Korean", "Kosovan", "Kurdish", "Kuwaiti", "Kyrgyz", "Laotian", "Latvian", "Lebanese", "Liberian", "Libyan", "a Liechtensteiner", "Lithuanian", "Luxembourgian", "Macedonian", "Malagasy", "Malawian", "Malaysian", "Maldivian", "Malian", "Maltese", "Marshallese", "Mauritanian", "Mauritian", "Mexican", "Micronesian", "Moldovan", "Monégasque", "Mongolian", "Montenegrin", "Moroccan", "Mosotho", "Motswana", "Mozambican", "Namibian", "Nauruan", "Nepalese", "New Caledonian", "a New Zealander", "Ni-Vanuatu", "Nicaraguan", "Nigerian", "Nigerien", "Niuean", "Norwegian", "Omani", "Pakistani", "Palauan", "Palestinian", "Panamanian", "Papua New Guinean", "Paraguayan", "Peruvian", "Polish", "Portuguese", "Puerto Rican", "Qatari", "Romanian", "Russian", "Rwandan", "Sahrawi", "Saint Lucian", "Salvadoran", "Sammarinese", "Samoan", "São Toméan", "Saudi", "Scottish", "Senegalese", "Serbian", "Seychellois", "Sierra Leonean", "Singaporean", "Slovak", "Slovene", "a Solomon Islander", "Somali", "South African", "South Sudanese", "Spanish", "Sri Lankan", "Sudanese", "Surinamese", "Swazi", "Swedish", "Swiss", "Syrian", "Taiwanese", "Tajik", "Tanzanian", "Thai", "Tibetan", "Togolese", "Tongan", "Trinidadian", "Tunisian", "Turkish", "Turkmen", "Tuvaluan", "Ugandan", "Ukrainian", "Uruguayan", "Uzbek", "Vatican", "Venezuelan", "Vietnamese", "Vincentian", "Yemeni", "Zairian", "Zambian", "Zimbabwean"],
@@ -1880,63 +1832,6 @@ App.Data.misc = {
 
 	FutureSocieties: ["FSArabianRevivalist", "FSAssetExpansionist", "FSAztecRevivalist", "FSBodyPurist", "FSChattelReligionist", "FSChineseRevivalist", "FSDegradationist", "FSEdoRevivalist", "FSEgyptianRevivalist", "FSGenderFundamentalist", "FSGenderRadicalist", "FSHedonisticDecadence", "FSIntellectualDependency", "FSMaturityPreferentialist", "FSNeoImperialist", "FSNull", "FSPastoralist", "FSPaternalist", "FSPetiteAdmiration", "FSPhysicalIdealist", "FSRepopulationFocus", "FSRestart", "FSRomanRevivalist", "FSSlaveProfessionalism", "FSSlimnessEnthusiast", "FSStatuesqueGlorification", "FSSubjugationist", "FSSupremacist", "FSTransformationFetishist", "FSYouthPreferentialist"],
 
-	ArcologyNamesSupremacistAmerindian: ["Akilineq", "Amerindia", "Aquadoctan", "Cahokia", "Caral", "Chicora", "Cowee", "Cusco", "Dugiluyi", "Five Nations", "Gran Chaco", "Indigenismo", "Isunigu", "Moundville", "Norumbega", "Onaquaga", "Onondaga Lake", "Paititi", "Porcupine", "Pueblo de Taos", "Quito", "Red Power", "Saguenay", "Shackamaxon", "Tamoanchan", "The Confederated Tribes", "Werowocomoco"],
-	ArcologyNamesSupremacistAsian: ["Asiatic Empire", "Ciimnuai", "Eastern Sun", "Greater Asia", "Jade Empire", "Jade Library", "Kalapa", "Mahoroba", "Pan-Asia", "Penglai", "Shambhala", "Shangri-La", "Sinosphere", "The Celestial Temple", "The Orient", "Tian", "Yangtze", "Yellow River", "Zhonghua Minzu"],
-	ArcologyNamesSupremacistBlack: ["Africana", "Afrocentral", "Azania", "Benin", "Door of Return", "Great Zimbabwe", "Houssa", "Kwanzaa Island", "Liberia", "Mezzoramia", "Négritude", "New Afrika", "Nubia", "Pan-Africa", "Panther Valley", "Rhapta", "The Promised Land", "Timbuktu", "United Africa", "Wakanda", "Zazamanc"],
-	ArcologyNamesSupremacistIndoAryan: ["Alaka", "Āryāvarta", "Dvārakā", "Indomania", "Indus Valley", "Kuru Kingdom", "Muziris", "New New Delhi", "Pialral", "Saket", "Swadeshi", "Swarga Loka", "Tamralipta", "The Raj", "The Subcontinent", "Ujjain", "Vaikuntha", "Vedic Empire", "Vindhya"],
-	ArcologyNamesSupremacistLatina: ["Alcázar de Segovia", "Alhambra", "Aztlan", "Chicanismo", "Ciudad Blanca", "El Dorado", "Hispania", "Hispanismo", "La Sagrada", "Lake Parime", "Quivira", "Santa Bárbara", "Sierra de la Plata", "Tayopa", "Tenochtitlan"],
-	ArcologyNamesSupremacistMalay: ["Austronesia", "Biringan", "Brunei", "Golden Peninsula", "Kaluwalhatian", "Kebangkitan Nasional", "Ketuanan Melayu", "Malacca", "Malaya", "Maphilindo", "Melayu Raya", "Nusantara", "Patani", "Srivijaya", "Suvarnadvipa", "Tanah Melayu"],
-	ArcologyNamesSupremacistMiddleEastern: ["Arabia", "Asabiyyah", "Ba'ath", "Fertile Crescent", "Iram", "Jannah", "Kerma", "MENA", "Mesopotamia", "Mount Qaf", "New Cairo", "Pan-Arabia", "Sinai", "The Caliphate", "Ubar", "Wabar", "Wāḳwāḳ", "West Asia", "Zerzura"],
-	ArcologyNamesSupremacistMixedRace: ["Desegregation", "Exogamy", "Fusion", "Heterogeneity", "Hybrid Vigor", "Integration", "Kaleidoscope", "Meltingpot", "Mosaic", "Multination", "Plaçage", "Pluralism", "Polychrome", "Rainbow Nation", "Salad Bowl", "The Mixer", "The Swirl"],
-	ArcologyNamesSupremacistPacificIslander: ["Aotearoa", "Austronesia", "Baralku", "Burotu", "Dreamtime", "Hawai'i", "Hawaiki", "Iolani Palace", "Kibu", "King Country", "Maui", "Melanesia", "Micronesia", "Mokoia", "Oceania", "Pacifica", "Papahānaumokuākea", "Polynesia", "Pulotu", "Rapa Nui"],
-	ArcologyNamesSupremacistSemitic: ["Arimathea", "Callipolis", "Dilmun", "Garden of Eden", "Greater Jerusalem", "Israel", "Jericho", "Judah", "Judea", "New Jerusalem", "Olam Ha-Ba", "Ophir", "Paradisus Judaeorum", "Pitchipoi", "Seron", "Tarshish", "The Fifth Temple", "The Levant", "The Promised Land", "Zion"],
-	ArcologyNamesSupremacistSouthernEuropean: ["Arcadia", "Delian League", "Delphi", "Elysian Fields", "Fortunate Isles", "Hyperuranion", "Iberia", "Mare Nostrum", "Mediterranea", "New Athens", "New Rome", "Olympus", "Papal Supremacy", "Risorgimento", "Siglo de Oro", "Spazio Vitale"],
-	ArcologyNamesSupremacistWhite: ["Avalon", "Baasskap", "Buyan", "Caucasia", "Cockaigne", "Eurocentral", "Europa", "Europe a Nation", "Fiery Cross", "Fourth Reich", "Gimlé", "Hy-Brasil", "Kitezh", "Klanbake", "New Australia", "Northwest Territory", "Opona", "Orania", "Pan-Europe", "The Old Dominion", "Thule", "Turner City", "Volkstaat", "Vyraj", "White Might"],
-	ArcologyNamesSubjugationistAmerindian: ["Adlivun", "Bear River", "Cowboy Town", "Fire Waters", "Fort Laramie", "Fort Mystic", "Manifest Destiny", "Mazocoba", "Oklahoma", "Red Dead", "Río Negro", "Sand Creek", "Shobari Waka", "The Rez", "Trail of Tears", "Washita", "Worst Nation", "Wounded Knee"],
-	ArcologyNamesSubjugationistAsian: ["Asiatic Exclusion", "Beriberi", "Defense of the Realm", "Diyu", "Hells Canyon", "Hiroshima", "Luzon", "Opium Den", "Pearl of the Orient", "Rock Springs", "Shakee", "Sinking Tide", "The East India Company", "Torreón", "Yellow Error", "Youdu"],
-	ArcologyNamesSubjugationistBlack: ["Bantustan", "Crow's Nest", "Dixie", "El Corte", "Golden Circle", "Hetgwauge", "Kuzimu", "Lynchburg", "Middle Passage", "Richmond", "Rosewood", "Rubber Farm", "Sharpeville", "Soweto", "Strange Orchard", "Sundown Town", "The Confederacy", "The Plantation", "The Projects", "Three-Fifths", "Tulsa"],
-	ArcologyNamesSubjugationistIndoAryan: ["Call Center", "Convenience Store", "Goa Inquisition", "Jallianwala Bagh", "Kalichi", "Macaulayism", "Naraka", "Navarino", "Qissa Khwani Bazaar", "Sepoy Mutiny", "Slumdog Kennels", "The East India Company", "Trade Fort", "UCIL Plant", "Uva Province"],
-	ArcologyNamesSubjugationistLatina: ["All-Mexico", "Annual", "Banana Republic", "Bean Paste", "Bisbee", "Border Wall", "Chandler", "Downieville", "Fort Veracruz", "Hanigan Ranch", "La Migra", "Los Conquistados", "Los Gatos", "Porvenir", "Vergüenza", "Zoot Suit Riot"],
-	ArcologyNamesSubjugationistMalay: ["Batavia", "Bencoolen", "East Indies", "Eastern Emporium", "Fort Marlborough", "Gimokodan", "Macunat School", "Moro Crater", "Pontianak", "Pulo Prabang", "Rawagede", "Soerabaja", "Spice Mine", "Watsonville"],
-	ArcologyNamesSubjugationistMiddleEastern: ["Al-Dawayima", "Allon Plus", "Constantinople", "Countered Jihad", "Cronulla", "Frontier Wire", "Homeland Secured", "Kiryat Arba", "La Reconquista", "Lydda", "New Guantanamo", "Qibya", "Sétif", "Shu'ubiyya", "Tantura", "Vlad's Castle", "Well Fire", "Yalova Peninsula", "Zanzibar"],
-	ArcologyNamesSubjugationistMixedRace: ["Apartheid", "Barriers", "Bloodlines", "Division", "Endogamy", "Ghetto Benches", "Homogeneity", "Monochrome", "Monoculture", "One-Drop", "Purity", "Redline", "Segregation", "Separate but Equal", "Separation", "The Divide"],
-	ArcologyNamesSubjugationistPacificIslander: ["Blackbird", "Cargo Cult", "Castle Bravo", "Coniston", "Great Māhele", "Hula Hoop", "Moro Castle", "Murimuria", "Myall Creek", "Ōmiya-Jima", "Rabbit Fence", "Sapwuahfik", "The Leap", "Thurston", "Tourist Trap", "Waterloo Creek"],
-	ArcologyNamesSubjugationistSemitic: ["Auschwitz", "Devil's Island", "Exodus", "Farhud", "Gehenna", "Intifada", "Kfar Etzion", "Kristallnacht", "Mawza Exile", "Mount Scopus", "New Canaan", "Pale of Settlement", "Pogrom", "Sheol", "Six Million Mile", "Solomon's Lament", "The Ghetto"],
-	ArcologyNamesSubjugationistSouthernEuropean: ["Al-Andalus", "Apalachin", "Arandora Star", "Black Legend", "Braintree", "Carthage", "Charlestown State", "Chios", "Hades", "Istanbul", "Istria", "Kalavryta", "Parish Prison", "Smyrna", "Tartarus", "The Foibe", "Toronto Trouble"],
-	ArcologyNamesSubjugationistWhite: ["Anaon", "Anticolonialism One", "Bleach Removal", "Camp des Saints", "Cawnpore", "Decolonization", "Greater Replacement", "Kaffa", "Killough", "Ladoga", "Mayocide", "Peklo", "Reparations", "Risen Tide", "Rope Burn", "Saint-Domingue", "The World Turned Upside Down", "Trailer Park", "Tuonela", "Uffern", "WASP Spray", "White Flight"],
-	ArcologyNamesGenderRadicalist: ["Admah", "Aphroditus", "Bacchanalia", "Boeotia", "Brumalia", "Catamitus", "City of the Plain", "Crete", "Dionysia", "Ermenosity", "Gomorrah", "Hermaphroditus", "Impudicitia", "Liberalia", "Pessinus", "Saturnalia", "Sodom", "The Rosebud", "Thebes", "Vine of Sodom", "Zeboim"],
-	ArcologyNamesGenderFundamentalist: ["The Arbor", "The Center", "The Core", "The Cradle", "The Entrance", "The Essence", "The Flower", "The Fruit", "The Jewel", "The Lily", "The Love", "The Moon", "The Origin", "The Pearl", "The Petal", "The Rose", "The Sheath", "The Source", "The Warmth"],
-	ArcologyNamesPaternalist: ["Asylum", "Benevolence", "City of Refuge", "Fatherhood", "Glory", "Greater Good", "Haven", "Humanitaria", "Nanny State", "New Springfield", "Paterfamilias", "Paternalis", "Refuge", "Safe Harbor", "Safe Haven", "Safehouse", "Safety", "Sanctuary", "Sanctum", "Shelter", "The Sanctuary", "Welfare"],
-	ArcologyNamesDegradationist: ["Akelarre", "Apocalyptica", "Armageddon", "Bald Mountain", "Black Sabbath", "Blåkulla", "Château de Silling", "Cruelty", "Damnation", "Degradation", "Diabolica", "Doomsday", "Dukkha", "Golgotha", "Hell on Earth", "Hell", "Inferno", "Misery", "Pain", "Schadenfreude", "Slaughterhouse", "Suffering", "The Pit", "The Tower", "Torment", "Torture Chamber", "Well to Hell"],
-	ArcologyNamesBodyPurist: ["Antiplasto", "Au Naturel", "Elysium", "Injection Rejection", "L'Esprit Nouveau", "Natural Selection", "Natural State", "Nature Reserve", "New Eden", "Organics", "Pure Land", "Pure Shores", "Purification", "Purity Balls", "Purity Ring", "Purity", "Sanctity", "Scarless Fever", "Surgical Strike", "The Ark", "The Garden", "The Repository", "Unblemisht", "Walden"],
-	ArcologyNamesTransformationFetishist: ["Arion Laboratory", "Barbie World", "Bimboden", "Bimboland", "Dow Corning", "Gillies Suite", "Guinea Pig Club", "Implantation Station", "Mad Mods", "Modding Community", "Mods Nexus", "Niptuck", "Plastic Beach", "Plasticland", "Silicone Valley", "Silicone Zone", "Stacy Malibu", "Strained Silicone", "Surgeon Generality", "The Dollhouse", "The Hospital", "Transformation Station", "Transformational Festival", "Under-Knife"],
-	ArcologyNamesYouthPreferentialist: ["Cumfullton", "Dick U.", "Ephebophily", "Frat Party", "Fuck High", "Hebephily", "Homecoming", "Kid Row", "Prom Night", "Sex College", "Sorority Row", "Spring Break", "Sunnyside", "Teen Scene", "Teen Spirit", "Teenage Wasteland", "Teenybop", "Undergrad Pad", "Young Earth", "Youngling", "Youngtown", "Youth Culture", "Youth", "Youthanasia"],
-	ArcologyNamesYouthPreferentialistLow: ["Cherry Fields", "Cherry Hills", "Comet Ping Pong", "Cummies Kindergarten", "Cunny Junction", "Dick Elementary", "Flatsville", "Groom Range", "Hanson City", "Hebephily", "Hotel Bangkok", "Kiddie Diddlebury", "Lil' Sluts Academy", "Lolita Complex", "Loliville", "Oingo Boingo", "Partyvanistan", "Pedophily", "Pomf Town", "Prepubescence", "Savile Row", "Statutoria", "The Cake Shop"],
-	ArcologyNamesMaturityPreferentialist: ["Age Begets Beauty", "Annual Reunion", "Cougar Town", "Experience", "Fine Wine", "Gerontophily", "Mature Theme", "Maturity", "Mesophily", "MILF Haven", "MILF Heights", "MILFtown", "Old Flame", "Old Style", "Park Avenue Tower", "Phaedra Complex", "Robinsonade", "Shady Acres", "Yummy Mummy"],
-	ArcologyNamesSlimnessEnthusiast: ["Aerobica", "Cardiode", "Emaciate State", "Lean Scene", "Less Is More", "Marathon", "National Diet", "Runway Way", "Size Zero", "Skin-and-Bones Zone", "Skinny Bop", "Skinny Dip", "Skinny House", "Slim City", "Slim Shades", "Slimming World", "The Island", "The Skinny", "The Thinning", "Underweight Way", "Upskirt", "Virginland", "Weigh Down Low"],
-	ArcologyNamesAssetExpansionist: ["Asset Holdings", "Biggening", "Blow-Up", "Boobs Tower", "Expand Land", "Expansion Chamber", "Expansion Pack", "Growth Medium", "Inflation Station", "Tangible Assets", "The Bouncy Castle", "The Expanse", "The Mounds", "Twin Peaks", "Voluptuousity"],
-	ArcologyNamesPastoralist: ["Abundance", "Big Milk", "Bounty", "Bucolica", "Cornucopia", "Dairy Farm", "Dairy Kingdom", "Friesland", "God's Country", "Green Acres", "Greener Pastures", "Lactophily", "Lactopia", "Land of Plenty", "Pastoral Romance", "Pasturelands", "Plenty", "Schleswig-Holstein", "The Dairy", "The Ranch"],
-	ArcologyNamesPhysicalIdealist: ["Aegina", "Amazonia", "Athletica", "Buff Riders", "Buffton", "Cardiode", "Dahomey", "Exercise Ball", "Exercise Bend", "Exercism", "Fitness Center", "Gargarei", "Gymnasiade", "Iron Pumps", "Midgard", "Muscle Beach", "Muscle Shoals", "Olympia", "Performance Peak", "Protein Lake", "Skid Row", "Sparta", "Sthenolagny", "The Gymnasium", "Them Gains", "Themyscira", "Valhalla", "Work Out"],
-	ArcologyNamesChattelReligionist: ["Blessings", "City on a Hill", "Deus Vult", "Eden", "Glory", "Heaven on Earth", "Heaven", "Holiness", "Light of the World", "New Covenant", "Pilgrim's Progress", "Prayer Service", "Redemption", "Salt and Light", "Salt of the Earth", "Salvation", "The Holy City", "The Light", "World to Come", "Worship"],
-	ArcologyNamesRomanRevivalist: ["Abila", "Aeminium", "Aequum", "Agrigentum", "Ala", "Albanianis", "Ambianum", "Antaeopolis", "Antiochia", "Apulum", "Aquileia", "Argentoratum", "Ariminum", "Arsinoë", "Ascrivium", "Asculum", "Attalia", "Augusta Vindelicorum", "Barium", "Belum", "Berytus", "Biriciana", "Blestium", "Bonna", "Bononia", "Bovium", "Brixia", "Burgodunum", "Byzantium", "Caesaraugusta", "Caesarea", "Caesaromagus", "Calleva Atrebatum", "Camulodunum", "Capua", "Carthago Nova", "Catana", "Celeia", "Cibalae", "Clausentum", "Comum", "Condate", "Conimbriga", "Constantinopolis", "Corduba", "Coria", "Coriovallum", "Danum", "Deva Victrix", "Divodurum", "Dubris", "Durnovaria", "Durocornovium", "Duroliponte", "Dyrrachium", "Eboracum", "Eburobrittium", "Elysian Fields", "Emona", "Epidaurum", "Florentia", "Gerulata", "Gerunda", "Isca Augusta", "Italica", "Iuvavum", "Lacobrica", "Lagentium", "Lauri", "Lentia", "Leptis Magna", "Letocetum", "Lindinis", "Londinium", "Longaricum", "Lopodunum", "Lousonna", "Lugdunum", "Luguvalium", "Lutetia", "Mancunium", "Marsonia", "Massa", "Massalia", "Matilo", "Mediolanum", "Messana", "Mod", "Mogontiacum", "Moridunum", "Mursa", "Naissus", "Nauportus", "Neapolis", "Neviodunum", "Nicaea", "Nicomedia", "Nida", "Nova Roma", "Novaesium", "Noviomagus", "Olicana", "Olisippo", "Ostia", "Partiscum", "Patavium", "Pistoria", "Placentia", "Poetovio", "Polemonion", "Pomaria", "Pompeii", "Ragusium", "Ravenna", "Regulbium", "Rhegium", "Rutupiae", "Salernum", "Scalabis", "Segovia", "Sirmium", "Siscia", "Spalatum", "Sumelocenna", "Syracusae", "Tarraco", "Tarsus", "The City of the Seven Hills", "Theranda", "Thuburbo Majus", "Thubursicum", "Tilurium", "Tingi", "Traiectum", "Trapezus", "Turicum", "Venta Icenorum", "Verulamium", "Vesontio", "Vindobona", "Vinovia", "Volubilis"],
-	ArcologyNamesAztecRevivalist: ["Acolmiztli", "Acozac", "Amaquemecan", "Anenecuilco", "Azcapotzalco", "Aztlan", "Calixtlahuaca", "Chalco", "Chapultepec", "Chicomoztoc", "Cholula", "Coixtlahuaca", "Coyoacan", "Coyoacán", "Cuautla", "Culhuacan", "Cuzcatlan", "Ecatepec", "Huitzilopochco", "Itzcahuacan", "Itztapalapan", "Iztapalapa", "Kaminaljuyu", "Malinalco", "Mexicatzinco", "Nojpetén", "Ocotelolco", "Ocuituco", "Omeyocan", "Otompan", "Oxwitik", "Quiahuiztlan", "Tacuba", "Tamoanchan", "Tenayuca", "Tenochtitlan", "Teopanzolco", "Teotihuacan", "Tepeticpac", "Tepetlaoztoc", "Tepozteco", "Texcoco", "Texcotzingo", "The Halls of Montezuma", "Tizatlan", "Tlacopan", "Tlalmanalco", "Tlatelolco", "Tollan", "Utatlán", "Xalapa", "Xaltocan", "Xochimilco", "Zacpeten"],
-	ArcologyNamesEgyptianRevivalist: ["Aaru", "Abdju", "Abu", "Aka", "Akhetaten", "Amenemhat-itj-tawy", "Aneb-Hetch", "Ankh-Tawy", "Anpet", "Apu", "Aushamem", "Baki", "Behdet", "Behedet-jabtet", "Buhen", "Chenem-Waset", "Dehenet", "Dep", "Dja", "Djanet", "Djed-Sut", "Djedu", "Djerty", "Djew-Qa", "Gebtu", "Gesa", "Gesy", "Hapi", "Hebenu", "Henen-nesut", "Herwer", "Hut-hery-ib", "Hut-ka-ptah", "Hut-Repyt", "Hut-Sekhem", "Hut-waret", "Iken", "Imet", "Imu", "Imura", "Inbu-Hedj", "Ipet-Resyt", "Ipu", "Itjtawy", "Iu-miteru", "Iunet", "Iunu", "Iuny", "Iunyt", "Iushenshen", "Khasut", "Khem", "Khemenu", "Khent-min", "Kheny", "Khenyt", "Khito", "Khmun", "Kis", "Madu", "Men-nefer", "Menfe", "Mer-nefer", "Mesen", "Moph", "Napata", "Nay-Ta-Hut", "Nekheb", "Nekhen", "Nubt", "Pe", "Peguat", "Pekher-wer", "Per-Amun", "Per-Atum", "Per-Banebdjedet", "Per-Bast", "Per-Bastet", "Per-Hathor", "Per-Imen-mat-khent", "Per-Medjed", "Per-Nemty", "Per-Ra-mes-su", "Per-Ramesses", "Per-Sopdu", "Per-Usiri", "Per-Wadjet", "Piemro", "Pikaut", "Pikuat", "Pselqet", "Ptah", "Ptkheka", "Qedesh", "Qedshu", "Qis", "Râ-Kedet", "Raqote", "Rebu", "Saka", "Sangar", "Semabehdet", "Senet", "Sepermeru", "Seshesh", "Šetennu", "Shashotep", "Shasu", "Shedet", "Sheten", "Sumenu", "Swenett", "Ta-senet", "Tamiat", "Taremu", "Tayu-djayet", "Tepihu", "Timinhor", "Tjaru", "Tjebnutjer", "Tjebu", "Tjeku", "Tjenu", "Tpyhwt", "Waset", "Weprehwy", "Yamu", "Ypu", "Zau", "Zauti", "Zawty", "Zay"],
-	ArcologyNamesEdoRevivalist: ["Amano-Iwato", "Ando", "Asakura", "Asuka", "Dejima", "Edo", "Hakodate", "Heian-kyō", "Heijō-kyō", "Hiraizumi", "Hirakata", "Idano", "Ise", "Isonokami", "Itsukushima", "Iware", "Izakaha", "Karu", "Karushima", "Kasagiyama", "Kashihara", "Katashiha", "Kawagoe", "Kawanakajima", "Kazuraki", "Kobe", "Kokyo", "Koryo", "Kuni-kyō", "Kuruda", "Kyotanabe", "Mahoroba", "Makimuko", "Mikatagahara", "Miki", "Miyajima", "Miyako", "Muro", "Nagaoka-kyō", "Nagashima", "Nagashino", "Nakatsukuni", "Naniwa", "Nara", "Negoro", "Neo Tokyo", "New Kyoto", "New Tokyo", "Odawara", "Okazaki", "Okehazama", "Onogoro", "Osaka", "Otsu", "Ryūgū-jō", "Sakurai", "Sekigahara", "Shiga", "Shika", "Shiki", "Shikoku", "Shimonoseki", "Shuri", "Sunpu", "Tajihi", "Takama-ga-hara", "Tanegashima", "Tengoku", "Tenmokuzan", "Tenri", "The Imperial Palace", "Ujiyamada", "Urasoe", "Waki-no-kami", "Yamazaki", "Yawata", "Yoshino"],
-	ArcologyNamesArabianRevivalist: ["Abha", "Achir", "Al Bahah", "Al-Hasa", "Al-Mansuriya", "Al-Qata'i", "Aleppo", "Alhambra", "Amadiya", "Amid", "Arar", "Arbil", "Ardabil", "Arjish", "Arzan", "Badr", "Baghdad", "Basra", "Bayt al-Hikma", "Béjaïa", "Beni Hammad", "Buraidah", "Cairo", "Córdoba", "Damascus", "Dammam", "Dhala", "Diyarbakır", "El-Mansuriya", "Faiyum", "Fes-al-Bali", "Fes", "Fez", "Fustat", "Ha'il", "Hajar an-Nasar", "Hama", "Harput", "Harran", "Hasankeyf", "Hejaz", "Ifriqiya", "Isfahan", "Jannah", "Jenin", "Jerusalem", "Jizan", "Jubayl", "Kairouan", "Karbala", "Khilat", "Kirkuk", "Kufa", "Madinah", "Madinat al-Hareer", "Madinat al-Salam", "Madinat al-Yasmin", "Madinat al-Zahra", "Mahdia", "Makkah", "Manzikart", "Maragha", "Mardin", "Marrakech", "Marrakesh", "Marsala", "Mayyafariqin", "Mecca", "Medina", "Mosul", "Murakuc", "Najran", "Nekor", "Qatif", "Qazvin", "Raqqa", "Raqqada", "Resafa", "Riyadh", "Sakakah", "Samarra", "Saqifah", "Say'un", "Sidon", "Sulaimaniyah", "Suq Abdulla", "Tabriz", "Tabuk", "Tahert", "Tarim", "Temsaman", "Tlemcen", "Tunis", "Walilli", "Zabid"],
-	ArcologyNamesChineseRevivalist: ["Acheng", "Anyang", "Anyi", "Balasagun", "Beijing", "Bian", "Bianjing", "Bianzhou", "Binzhou", "Bogu", "Boping", "Chang'an", "Changle", "Changping", "Changsha", "Chengdu", "Chengzhou", "Chuqiu", "Dadu", "Daliang", "Daming", "Danyang", "Datong", "Daxing", "Dinglian", "Diqiu", "Dongdu", "Dongjing", "Dujianshan", "Dunhuang", "Ezhou", "Fanyang", "Feng Huang", "Fenghao", "Fengxiang", "Fuhan", "Fusang", "Guanggu", "Guangling", "Guangzhou", "Gusu", "Guzang", "Handan", "Hangzhou", "Haojing", "Hefei", "Henglong", "Hezhou", "Huanbei", "Huangquan", "Huangzhong", "Huatai", "Huokang", "Ji", "Jian", "Jiang", "Jiangling", "Jiangning", "Jiankang", "Jianye", "Jicheng", "Jin Shan", "Jinan", "Jincheng", "Jingsha", "Jingzhao", "Jingzhou", "Jinling", "Jinyang", "Jiuquan", "Kaifeng", "Khanbaliq", "Kuaiji", "Laosicheng", "Ledu", "Lianchuan", "Liaodong", "Liaoyang", "Lin'an", "Linhuang", "Linxiang", "Linzi", "Lishi", "Liting", "Longcheng", "Lujiang", "Luoyang", "Luoyi", "Luyi", "Mingfu", "Moling", "Mount Tai", "Nan'an", "Nanchang", "Nanjing", "Nanjun", "Nanyang", "Panyu", "Peking", "Pengcheng", "Pingcheng", "Pingjiang", "Pingliang", "Pingyang", "Pingzhou", "Puzi", "Qi Lin", "Qian", "Qiantang", "Qiling", "Qin", "Quanqiu", "Qufu", "Quwo", "Ruyin", "Shangcai", "Shanggui", "Shangjing", "Shangqiu", "Shengjing", "Shengle", "Shouchun", "Suzhou", "Taiyuan", "Tang", "Tanheli", "Tanjiao", "Tanzhou", "Taoqiu", "The Forbidden Palace", "The Middle Kingdom", "Tianlin", "Tongwan", "Wanchuan", "Wangcheng", "Wanqiu", "Wu", "Wuchang", "Wudu", "Xi'an", "Xiacai", "Xiangguo", "Xiangning", "Xiangping", "Xianyang", "Xibo", "Xicheng", "Xin Hua", "Xincai", "Xingqing", "Xingwang", "Xintian", "Xinzheng", "Xiping", "Xuchang", "Yangcheng", "Yangzhai", "Yanjing", "Yanshi", "Yecheng", "Yewang", "Yin", "Yinfu", "Ying", "Yingdu", "Yingqiu", "Yingtian", "Yong", "Yongshicheng", "You", "Youdu", "Youming", "Youzhou", "Yueyang", "Yuezhou", "Yuhang", "Yushan", "Zhangye", "Zhangzi", "Zhaoge", "Zhending", "Zheng", "Zhenxun", "Zhongdu", "Zhongguo", "Zhongshan", "Zibo", "Zichuan"],
-	ArcologyNamesNeoImperialist: ["Atteln", "Aermacht", "Aubevoie", "Bellechassange", "Black Rock", "Black Hollow", "Bourbon", "Colme", "Daybrook", "Drancy", "Easthaven", "Eastwatch", "Etau", "Elsing", "Essenge", "Ettenmont", "Erceti", "Fernsworth", "Hersengeux", "Ironforge", "Ironhaven", "Irontown", "Karlsberg", "Klattenhof", "Lhanbryde", "Lindham", "Marrensville", "Maushof", "Morlaincourt", "Munschwitz", "Neo-Berlin", "Neo-London", "Neo-Paris", "Neo-Madrid", "Neo-York", "Northscot", "Oberlandscheid", "Obersberg", "Poisseau", "Redwater", "Sali", "Sand and Stone", "Shominster", "Sommelie", "Strathsmore", "Sunstown", "Sonnehof", "Sonneshaven", "Tottenham", "The Rock", "The Golden Spire", "Villeurmont", "Vivonne", "Volkersch", "Volkhof", "Volksgard", "Wallenberg", "Wallenhof", "Wilderf", "Wycombe", "Zillendorf", "Zerf"],
-
-	/* pregmod FS */
-	ArcologyNamesEugenics: ["Ascension", "Elitism", "Eugenica", "Eugeniculate", "Galton City", "Germinal Choice", "Good Stock", "Improvement", "Lebensborn", "Natural Selection", "Oneida Community", "Perfection", "Powered Elite", "Private Gene Pool", "Quality", "Rebirth", "Reprogenetics", "Second Chance", "Selection Rule", "Stirpiculture"],
-	ArcologyNamesRepopulationist: ["Cultural Mandate", "Fruitful and Multiply", "Future", "Glorious Mother", "Haven of the Pregnant", "Holders of the Future", "Hope", "Motherhood", "Multiplication", "Preggonia", "Public Gene Pool", "Quantity", "Rabbit Hole", "Repoblación", "Sacred Womb", "The Womb"],
-	ArcologyNamesHedonisticDecadence: ["All You Can Eat", "Aristippa", "Buffet", "Chubby Hole", "Cyrene", "Decadence", "Epicurea", "Gavage", "Glorious Food", "Gluttony", "Hedonic Calculator", "Hedonism Resort", "Hedonism Spot", "Indulgence", "Leblouh", "Libertinage", "New Wisconsin", "Pleasure", "Plumpland", "Sloth", "Smörgåsbord", "Stuffedtopia", "Yang"],
-	ArcologyNamesCummunism: ["Arscrotzka", "Crusty Cummies", "Cumbria", "Cuming Inlet", "Cummins", "Cummunist Russwhore", "Cumstantine", "Cumstantinople", "Da Cumrade", "Erection Fluid", "Free Slave Central", "Jizzakh", "Jizzebel", "Jizzington upon Wank", "Mother Cumtry", "Semen Supreme", "Semenyih", "Sperm Atrium", "Sperm Banks", "Spermato Zoo", "Wankara"],
-	ArcologyNamesIncestFetishist: ["All in the Family", "Blood Relations", "Consanguinity", "East Westermarck", "Electra Complex", "Familial Embrace", "Family Fortunes", "Family Ties", "Heredity", "Incestia", "Incestral Home", "Jocasta Complex", "Kinship", "Oedipal City", "Oedipus Complex", "Oeditropolis", "Pure Blood", "Sib City", "Snokhachestvo", "Tenth Abomination", "Unlash of Clans", "Wincest"],
-	ArcologyNamesIntellectualDependency: ["Barbie World", "Bimbo Land", "Bimbotopia", "Dim City", "Dumbarton", "Dummy Thicc", "Followers of Bacchus", "Fool on the Hill", "Fun and Games", "Gump Forest", "Idiot City", "Imbecile Mile", "Loosu Pond", "Pretty in Pink", "Promiscuous", "Sex Essex", "Stupid Hoedown", "The Dropout", "Valley World"],
-	ArcologyNamesSlaveProfessionalism: ["Braintree", "Diploma Mill", "Disciplined Minds", "Einstein Beach", "Followers of Minerva", "Genius Loci", "House of Wisdom", "Ingenium", "Intellectua", "Intelligence Star", "Intelligentsia", "Library of Alexandria", "Mensa Mesa", "Secretary State", "Smart City", "Smart Grid"],
-	ArcologyNamesPetiteAdmiration: ["Bantam Battalion", "Dwarf Forest", "Dwarf Fortress", "Elf Village", "Haunchyville", "Midget Utopia", "Midgetville", "Mini World", "Munchkinland", "Napoleon Complex", "Petite Mesa", "Petite Pride", "Pygmalion", "Short Circuit", "Short Stirling", "The Short Stack", "Tiny Town"],
-	ArcologyNamesStatuesqueGlorification: ["Basketball Court", "Castelnau", "Giant's Causeway", "Giraffe Manor", "Height Is Right", "High Hopes", "Highland", "Potsdam Battalion", "Rhodes", "St. Michael's Mount", "Tall Poppy Field", "Tall Trees", "Talleres", "The Bean Stalk", "The Heights", "Valley of Elah"],
-
 	badWords: ["anus", "ass", "bitch", "boob", "butt", "cock", "crap", "cum", "cunny", "cunt", "dick", "fuck", "jizz", "junk", "piss", "prick", "pussy", "shit", "slave", "slut", "tit", "trash", "whore"],
 
 	badNames: ["Ass Kisser", "Ass Licker", "Ass", "Assfucker", "Asshole", "Ballsack", "Bastard", "Beta", "Bitch", "Cock", "Cocksucker", "Coward", "Creep", "Cum Rag", "Cunt", "Degenerate", "Despoiler", "Dick", "Dickhead", "Dicksucker", "Dickweed", "Dipshit", "Douchebag", "Dumbass", "DumbFuck", "Dunderfuck", "Dyke", "Faggot", "Fucker", "Fuckface", "Fuckhead", "Fucko", "Fucktard", "Fuckwit", "Idiot", "Inbred", "Jackass", "Jerk", "Jizz Stain", "Loser", "Moron", "Motherfucker", "Nutsack", "Pissbaby", "Prick", "Pussy", "Rapist", "Ratfuck", "Retard", "Ruiner", "Schmuck", "Scumbag", "Shitbird", "Shithead", "Slave", "Slaver", "Sleazeball", "Slut", "Sodomite", "Thundercunt", "Traitor", "Trash", "Whore", "Wimp"],
@@ -2080,169 +1975,3 @@ App.Data.misc.lawlessMarkets = [
 	"white collar",
 	...App.Data.misc.schools.keys()
 ];
-
-App.Data.weather = {
-
-	hotnice: [
-		{name: "Sunny", severity: 1},
-		{name: "Warm", severity: 1},
-		{name: "Partly Cloudy", severity: 1},
-		{name: "Light Rain", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	windynice: [
-		{name: "Light Wind", severity: 1},
-		{name: "Cloudy", severity: 1},
-		{name: "Partly Cloudy", severity: 1},
-		{name: "Overcast", severity: 1},
-		{name: "Light Rain", severity: 1},
-		{name: "Heavy Rain", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	smokynice: [
-		{name: "Smoke warning", severity: 1},
-		{name: "Overcast", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	toxicnice: [
-		{name: "Cloudy", severity: 1},
-		{name: "Light Rain", severity: 1},
-		{name: "Heavy Rain", severity: 1},
-		{name: "Sunny", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	coldnice: [
-		{name: "Overcast", severity: 1},
-		{name: "Chilly", severity: 1},
-		{name: "Heavy Rain", severity: 1},
-		{name: "Gentle Snow", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	tectonicnice: [
-		{name: "Light Rain", severity: 1},
-		{name: "Partly Cloudy", severity: 1},
-		{name: "Heavy Rain", severity: 1},
-		{name: "Sunny", severity: 1},
-		{name: "Clear and Calm", severity: 1},
-	],
-
-	hotlight: [
-		{name: "Light Sandstorms", severity: 2},
-		{name: "High Heat", severity: 2},
-		{name: "Minor Wildfires", severity: 2},
-		{name: "Volcano Warning", severity: 2},
-	],
-
-	windylight: [
-		{name: "High Winds", severity: 2},
-		{name: "T-Storm Warning", severity: 2},
-		{name: "Light T-Storms", severity: 2},
-		{name: "Flood Warning", severity: 2},
-		{name: "Tornado Warning", severity: 2},
-	],
-
-	smokylight: [
-		{name: "Smoky", severity: 2},
-		{name: "Ash Storm Warning", severity: 2},
-		{name: "T-Storm Warning", severity: 2},
-		{name: "Light T-Storms", severity: 2},
-	],
-
-	toxiclight: [
-		{name: "Acid Rain", severity: 2},
-		{name: "Radiation Warning", severity: 2},
-		{name: "T-Storm Warning", severity: 2},
-		{name: "Light T-Storms", severity: 2},
-	],
-
-	coldlight: [
-		{name: "Subzero Temps", severity: 2},
-		{name: "Light Snowstorms", severity: 2},
-		{name: "Blizzard Warning", severity: 2},
-	],
-
-	tectoniclight: [
-		{name: "T-Storm Warning", severity: 2},
-		{name: "Light T-Storms", severity: 2},
-		{name: "Light Mudslides", severity: 2},
-		{name: "Earthquake Warning", severity: 2},
-	],
-
-	hotheavy: [
-		{name: "Severe Sandstorm", severity: 3},
-		{name: "Extreme Heat", severity: 3},
-		{name: "Serious Wildfires", severity: 3},
-		{name: "Volcanic Rumbling", severity: 3},
-	],
-
-	windyheavy: [
-		{name: "Extreme Winds", severity: 3},
-		{name: "Flooding", severity: 3},
-		{name: "Tornadoes", severity: 3},
-		{name: "Extreme T-storms", severity: 3},
-	],
-
-	smokyheavy: [
-		{name: "Dense Smoke", severity: 3},
-		{name: "Ash Storms", severity: 3},
-		{name: "Extreme T-storms", severity: 3},
-	],
-
-	toxicheavy: [
-		{name: "Heavy Acid Rain", severity: 3},
-		{name: "Extreme T-storms", severity: 3},
-		{name: "Radiological Drift", severity: 3},
-	],
-
-	coldheavy: [
-		{name: "Severe Snowstorm", severity: 3},
-		{name: "Blizzard", severity: 3},
-		{name: "Freezing Temps", severity: 3},
-	],
-
-	tectonicheavy: [
-		{name: "Violent Mudslides", severity: 3},
-		{name: "Rumbling Earthquake", severity: 3},
-		{name: "Extreme T-Storms", severity: 3},
-	],
-
-	hotextreme: [
-		{name: "Solar Flare", severity: 4},
-		{name: "Devastating Sandstorm", severity: 4},
-		{name: "Volcanic Eruption", severity: 4},
-	],
-
-	windyextreme: [
-		{name: "Ion Storm", severity: 4},
-		{name: "Cataclysmic Rains", severity: 4},
-		{name: "Cat 6 Hurricane", severity: 4},
-	],
-
-	smokyextreme: [
-		{name: "Cataclysmic Rains", severity: 4},
-		{name: "Suffocating Ash Storms", severity: 4},
-	],
-
-	toxicextreme: [
-		{name: "Radiological Storm", severity: 4},
-		{name: "Cataclysmic Acid Rain", severity: 4},
-	],
-
-	coldextreme: [
-		{name: "Polar Vortex", severity: 4},
-		{name: "Apocalyptic Blizzard", severity: 4},
-		{name: "Arctic Temps", severity: 4},
-	],
-
-	tectonicextreme: [
-		{name: "Hellish Earthquake", severity: 4},
-		{name: "Ion Storm", severity: 4},
-		{name: "Cataclysmic Rains", severity: 4},
-	],
-
-};
diff --git a/js/003-data/playerData.js b/js/003-data/playerData.js
new file mode 100644
index 0000000000000000000000000000000000000000..b0848899a3e7b43388d4d809d4ea4b82a4f6155c
--- /dev/null
+++ b/js/003-data/playerData.js
@@ -0,0 +1,12 @@
+App.Data.player = {
+	// Value is stored in player object as the index of this array: If V.PC.refreshmentType === 0, we should display "Smoked".
+	refreshmentType: [
+		`Smoked`,
+		`Drank`,
+		`Eaten`,
+		`Snorted`,
+		`Injected`,
+		`Popped`,
+		`Dissolved orally`,
+	]
+};
diff --git a/js/003-data/policiesData.js b/js/003-data/policiesData.js
index 9bbd142102344fd18e38db0645eb1fef4b01312d..752e67af5819e30a91e565e0d319e9412cbde984 100644
--- a/js/003-data/policiesData.js
+++ b/js/003-data/policiesData.js
@@ -309,7 +309,7 @@ App.Data.Policies.Selection = {
 				text: `underage slaves enjoy protections only against penetrative sex`,
 				activatedText: `underage slaves enjoy protections against molestation and rape.`,
 				get requirements() { return (V.extremeUnderage === 1); },
-				get hide() { return (V.extremeUnderage === 1) ? {button: 0} : {button: 1}; } // CPA is complicated.  It inits to "on".  Make sure button is hidden if extreme underage is OFF.  If a player enables extreme underage, then we can let them control it.
+				get hide() { return (V.extremeUnderage === 1) ? {button: 0} : {button: 1}; } // CPA is complicated. It inits to "on". Make sure button is hidden if extreme underage is OFF. If a player enables extreme underage, then we can let them control it.
 			}
 		],
 		"arcologies[0].FSEgyptianRevivalistIncestPolicy": [
@@ -473,20 +473,20 @@ App.Data.Policies.Selection = {
 		],
 		"policies.regularParties": [
 			{
-				title: "Regular Entertainments",
+				title: "Regular Social Events",
 				text: "you will host regular parties for prominent citizens, an expected social duty of an arcology owner.",
 				activatedText: "you are hosting regular parties for prominent citizens, an expected social duty of an arcology owner.",
 				get note() {
+					let text = `Will cost ${cashFormat(policies.cost())} weekly`;
 					if (V.rep > 18000) {
-						return `Will damage your reputation`;
-					} else {
-						return ``;
+						text += `; neglecting this has begun to damage your reputation`;
 					}
+					return text;
 				},
 				get activatedNote() {
-					let text = `Will cost ${cashFormat(policies.cost())} weekly`;
+					let text = `Costs ${cashFormat(policies.cost())} weekly`;
 					if (V.rep > 18000) {
-						text += `, and prevent damage to your reputation`;
+						text += `, and prevents damage to your reputation`;
 					}
 					return text;
 				}
@@ -568,6 +568,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine The Slave School",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.TSS.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -584,6 +585,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine The Utopian Orphanage",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.TUO.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -600,6 +602,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine the Growth Research Institute",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.GRI.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -609,6 +612,7 @@ App.Data.Policies.Selection = {
 				title: "St. Claver Preparatory subsidy",
 				text: "you will subsidize this school's branch campus in your arcology.",
 				activatedText: "you are subsidizing this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.SCP.schoolProsperity < 10 && V.SCP.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			},
@@ -616,6 +620,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine St. Claver Preparatory",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.SCP.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -632,6 +637,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine L'École des Enculées",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.LDE.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -648,6 +654,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine the Gymnasium-Academy",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.TGA.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -664,6 +671,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine The Cattle Ranch",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.TCR.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -680,6 +688,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine the Futanari Sisters",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.TFS.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -696,6 +705,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine the Hippolyta Academy",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.HA.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -712,6 +722,7 @@ App.Data.Policies.Selection = {
 				title: "Undermine Nueva Universidad de Libertad",
 				text: "you will covertly hurt this school's branch campus in your arcology.",
 				activatedText: "you are covertly hurting this school's branch campus in your arcology.",
+				enable: -1,
 				get requirements() { return (V.NUL.schoolPresent === 1); },
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
@@ -773,7 +784,7 @@ App.Data.Policies.Selection = {
 					return el;
 				},
 				get activatedText() { return `you have set your arcology's standard retirement age for sex slaves at physically ${V.retirementAge}. This policy completely supplants former age retirement policies.`; },
-				onImplementation:  function() { V.policies.retirement.customAgePolicy = 0; },
+				onImplementation: function() { V.policies.retirement.customAgePolicy = 0; },
 				onRepeal: function() { V.retirementAge = 45; },
 				note: "Set age before implementing"
 			}
diff --git a/js/003-data/slaveWearData.js b/js/003-data/slaveWearData.js
index d0fc4d7969b82d5a41070302f4a0e1ae09f358a2..8777cf308c604a65c8988b57a1ce849836c03372 100644
--- a/js/003-data/slaveWearData.js
+++ b/js/003-data/slaveWearData.js
@@ -7,7 +7,7 @@
  */
 
 /**
- * @typedef {Array<slaveWear>} slaveWearCategory
+ * @typedef {Array<slaveWear>|Array<slaveWearChastity>} slaveWearCategory
  */
 
 /** @type {Object.<string, slaveWearCategory>} */
diff --git a/js/003-data/weatherData.js b/js/003-data/weatherData.js
new file mode 100644
index 0000000000000000000000000000000000000000..5888623b3d522fec969bd603d2e02c57f4d214e5
--- /dev/null
+++ b/js/003-data/weatherData.js
@@ -0,0 +1,165 @@
+App.Data.Weather = {
+
+	hotNice: [
+		{name: "Sunny", severity: 1},
+		{name: "Warm", severity: 1},
+		{name: "Partly Cloudy", severity: 1},
+		{name: "Light Rain", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	windyNice: [
+		{name: "Light Wind", severity: 1},
+		{name: "Cloudy", severity: 1},
+		{name: "Partly Cloudy", severity: 1},
+		{name: "Overcast", severity: 1},
+		{name: "Light Rain", severity: 1},
+		{name: "Heavy Rain", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	smokyNice: [
+		{name: "Smoke warning", severity: 1},
+		{name: "Overcast", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	toxicNice: [
+		{name: "Cloudy", severity: 1},
+		{name: "Light Rain", severity: 1},
+		{name: "Heavy Rain", severity: 1},
+		{name: "Sunny", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	coldNice: [
+		{name: "Overcast", severity: 1},
+		{name: "Chilly", severity: 1},
+		{name: "Heavy Rain", severity: 1},
+		{name: "Gentle Snow", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	tectonicNice: [
+		{name: "Light Rain", severity: 1},
+		{name: "Partly Cloudy", severity: 1},
+		{name: "Heavy Rain", severity: 1},
+		{name: "Sunny", severity: 1},
+		{name: "Clear and Calm", severity: 1},
+	],
+
+	hotLight: [
+		{name: "Light Sandstorms", severity: 2},
+		{name: "High Heat", severity: 2},
+		{name: "Minor Wildfires", severity: 2},
+		{name: "Volcano Warning", severity: 2},
+	],
+
+	windyLight: [
+		{name: "High Winds", severity: 2},
+		{name: "T-Storm Warning", severity: 2},
+		{name: "Light T-Storms", severity: 2},
+		{name: "Flood Warning", severity: 2},
+		{name: "Tornado Warning", severity: 2},
+	],
+
+	smokyLight: [
+		{name: "smoky", severity: 2},
+		{name: "Ash Storm Warning", severity: 2},
+		{name: "T-Storm Warning", severity: 2},
+		{name: "Light T-Storms", severity: 2},
+	],
+
+	toxicLight: [
+		{name: "Acid Rain", severity: 2},
+		{name: "Radiation Warning", severity: 2},
+		{name: "T-Storm Warning", severity: 2},
+		{name: "Light T-Storms", severity: 2},
+	],
+
+	coldLight: [
+		{name: "Subzero Temps", severity: 2},
+		{name: "Light Snowstorms", severity: 2},
+		{name: "Blizzard Warning", severity: 2},
+	],
+
+	tectonicLight: [
+		{name: "T-Storm Warning", severity: 2},
+		{name: "Light T-Storms", severity: 2},
+		{name: "Light Mudslides", severity: 2},
+		{name: "Earthquake Warning", severity: 2},
+	],
+
+	hotHeavy: [
+		{name: "Severe Sandstorm", severity: 3},
+		{name: "Extreme Heat", severity: 3},
+		{name: "Serious Wildfires", severity: 3},
+		{name: "Volcanic Rumbling", severity: 3},
+	],
+
+	windyHeavy: [
+		{name: "Extreme Winds", severity: 3},
+		{name: "Flooding", severity: 3},
+		{name: "Tornadoes", severity: 3},
+		{name: "Extreme T-storms", severity: 3},
+	],
+
+	smokyHeavy: [
+		{name: "Dense Smoke", severity: 3},
+		{name: "Ash Storms", severity: 3},
+		{name: "Extreme T-storms", severity: 3},
+	],
+
+	toxicHeavy: [
+		{name: "Heavy Acid Rain", severity: 3},
+		{name: "Extreme T-storms", severity: 3},
+		{name: "Radiological Drift", severity: 3},
+	],
+
+	coldHeavy: [
+		{name: "Severe Snowstorm", severity: 3},
+		{name: "Blizzard", severity: 3},
+		{name: "Freezing Temps", severity: 3},
+	],
+
+	tectonicHeavy: [
+		{name: "Violent Mudslides", severity: 3},
+		{name: "Rumbling Earthquake", severity: 3},
+		{name: "Extreme T-Storms", severity: 3},
+	],
+
+	hotExtreme: [
+		{name: "Solar Flare", severity: 4},
+		{name: "Devastating Sandstorm", severity: 4},
+		{name: "Volcanic Eruption", severity: 4},
+	],
+
+	windyExtreme: [
+		{name: "Ion Storm", severity: 4},
+		{name: "Cataclysmic Rains", severity: 4},
+		{name: "Cat 6 Hurricane", severity: 4},
+	],
+
+	smokyExtreme: [
+		{name: "Cataclysmic Rains", severity: 4},
+		{name: "Suffocating Ash Storms", severity: 4},
+	],
+
+	toxicExtreme: [
+		{name: "Radiological Storm", severity: 4},
+		{name: "Cataclysmic Acid Rain", severity: 4},
+	],
+
+	coldExtreme: [
+		{name: "Polar Vortex", severity: 4},
+		{name: "Apocalyptic Blizzard", severity: 4},
+		{name: "Arctic Temps", severity: 4},
+	],
+
+	tectonicExtreme: [
+		{name: "Hellish Earthquake", severity: 4},
+		{name: "Ion Storm", severity: 4},
+		{name: "Cataclysmic Rains", severity: 4},
+	],
+
+};
diff --git a/js/artInfrastructure.js b/js/artInfrastructure.js
index a0fb2a8eae2e0d3b502ba99146492c8d575b11ba..386d5c94e263a960ec50213a898c63dc83ba4f10 100644
--- a/js/artInfrastructure.js
+++ b/js/artInfrastructure.js
@@ -92,7 +92,7 @@ App.Art.SvgQueue = class {
 	}
 
 	/** concatenate the contents of a second queue into this one.
-	 * displayClass must match.  cache and transformFunc may differ (they are used only by add).
+	 * displayClass must match. cache and transformFunc may differ (they are used only by add).
 	 * @param {App.Art.SvgQueue} queue
 	 */
 	concat(queue) {
diff --git a/player variables documentation - Pregmod.txt b/player variables documentation - Pregmod.txt
index 3536d47f5d3ddd3f79150c2642c1feb79d2ab0e2..4c807878d05c1ed972b4056f57403773e7a1fa2d 100644
--- a/player variables documentation - Pregmod.txt	
+++ b/player variables documentation - Pregmod.txt	
@@ -62,17 +62,51 @@ The method of consumption of .refreshment
 career:
 
 Your career before becoming owner
+
+"rich kid"
+"trust fund"
 "wealth"
+
+"business kid"
+"entrepreneur"
 "capitalist"
+
+"child soldier"
+"recruit"
 "mercenary"
+
+"slave tender"
+"slave overseer"
 "slaver"
+
+"worksite helper"
+"construction"
 "engineer"
+
+"nurse"
+"medical assistant"
 "medicine"
+
+"child star"
+"rising star"
 "celebrity"
+
+"child prostitute"
+"prostitute"
 "escort"
+
+"child servant"
+"handmaiden"
 "servant"
+
+"street urchin"
+"hoodlum"
 "gang"
+
+"script kiddy"
+"hacker"
 "BlackHat"
+
 "arcology owner"
 
 rumor:
diff --git a/slave variables documentation - Pregmod.txt b/slave variables documentation - Pregmod.txt
index 4a6ea4b083a0a3dba8f0c6ee3775e293e26c2969..1d740d27a4f3d56e51bbb33549cc78d0af0ecac6 100644
--- a/slave variables documentation - Pregmod.txt	
+++ b/slave variables documentation - Pregmod.txt	
@@ -570,6 +570,8 @@ what porn she is known for
 "underage"
 "weight gain"
 "big dick"
+"muscle"
+"taboo"
 "generic"
 "deepthroat"
 "unwilling"
@@ -610,6 +612,8 @@ what aspect of her the upgraded studio is focusing on for porn
 "loli"
 "gainer"
 "stud"
+"muscle"
+"incest"
 "porn"
 "gagfuck queen"
 "strugglefuck queen"
@@ -656,6 +660,16 @@ porn.fame.stud:
 well hung porn fame
 accepts int
 
+porn.fame.muscle:
+
+muscle porn fame
+accepts int
+
+porn.fame.incest:
+
+incest porn fame
+accepts int
+
 porn.fame.loli:
 
 underage porn fame
@@ -3199,10 +3213,20 @@ fertility + hyperFertility - slave will have multiples, even shorter pregnancy r
 
 superfetation - pregnancy does not block ovulation, slave can become pregnant even while pregnant
 
+polyhydramnios - fetal overproduction of amniotic fluid
+
+uterineHypersensitivity - Hyper sensitive uterus + pleasurable birth
+
+galactorrhea - inappropriate lactation
+
 gigantism - slave is abnormally tall
 dwarfism - slave is abnormally short
 gigantism + dwarfism - slave is very average
 
+neoteny - retains childlike characteristics
+progeria - rapid aging
+neoteny + progeria = progeria
+
 pFace - slave has a flawless face
 uFace - slave has a hideous face
 pFace + uFace - Depends on carrier status, may swing between average and above/below depending on it
@@ -3940,8 +3964,8 @@ Once finished, add it into "src/npc/databases/customSlavesDatabase.js".
 To test if your slave is functioning, start up a normal game, swap to cheat mode, max your rep, and view other slaveowner's stock in the slave market. If you cannot find your slave in the list, and you didn't start the game with your slave, you should double check your slave for errors. If a slave named "Blank" is present, then you likely messed up. Once you find your slave, check their description to make sure it is correct. If it is not, you messed up somewhere in setting them up.
 
 
-@@.green;				- something good or health/libido/attraction gain
-@@.red;					- something bad or health/libido/attraction loss
+@@.green;				- something good or health/attraction gain
+@@.red;					- something bad or health/attraction loss or flaw/mindbreak acquisition
 @@.hotpink;				- devotion gain
 @@.mediumorchid;		- devotion loss
 @@.mediumaquamarine;	- trust gain with higher devotion
@@ -3953,6 +3977,8 @@ To test if your slave is functioning, start up a normal game, swap to cheat mode
 @@.orange;				- shrinking/degradation of a body part (reversed in some cases)
 @@.lightsalmon;			- rivalry
 @@.lightgreen;			- relationship
+@@.violet;				- libido gain
+@@.khaki;				- libido loss
 
 
 wombJS.tw subsystem:
diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js
index 5d34499acb133f50d59815133f687494efec9c52..94bbfb53d2899effb9214edd17e2c244f9762b7c 100644
--- a/src/002-config/fc-version.js
+++ b/src/002-config/fc-version.js
@@ -2,5 +2,5 @@ App.Version = {
 	base: "0.10.7.1", // The vanilla version the mod is based off of, this should never be changed.
 	pmod: "3.8.2",
 	commitHash: null,
-	release: 1109 // When gettting close to 2000,  please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js.
+	release: 1111 // When getting close to 2000,  please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js.
 };
diff --git a/src/003-assets/CSS/slaveList.css b/src/003-assets/CSS/slaveList.css
index dab490489701ec1f77718d5cf17d8085fff3f596..1622a6a9f60b779856d1aa0468867cb8aadfaf3e 100644
--- a/src/003-assets/CSS/slaveList.css
+++ b/src/003-assets/CSS/slaveList.css
@@ -18,6 +18,7 @@ span.freeAssignment {
 /* Slave Summary Block */
 .ssb {
 	margin-right: 2em;
+	text-indent: 0;
 }
 
 .strong {
diff --git a/src/004-base/facility.js b/src/004-base/facility.js
index b88bd4b20725e8202df7fd058ac4ffca14830647..d4ef66b608f6f0e7d03f19bd3230af663fcdc5f2 100644
--- a/src/004-base/facility.js
+++ b/src/004-base/facility.js
@@ -463,7 +463,7 @@ App.Entity.Facilities.Facility = class {
 		if (jobArray.length === 1) {
 			return jobArray[0].employees();
 		}
-		return V.slaves.filter(s => jobArray.some(j => j.isEmployed(s)));
+		return [].concat(...jobArray.map(j => j.employees()));
 	}
 
 	/**
@@ -471,9 +471,9 @@ App.Entity.Facilities.Facility = class {
 	 * @returns {Set<number>}
 	 */
 	employeesIDs() {
-		const jobArray = Object.values(this._jobs);
+		const jobArray = this.jobs;
 		if (jobArray.length === 1) {
-			return this.job().employeesIDs();
+			return jobArray[0].employeesIDs();
 		}
 		const res = new Set();
 		for (const j of jobArray) {
diff --git a/src/Corporation/manageCorporation.tw b/src/Corporation/manageCorporation.tw
index b221a8bb2a8633dad51a264dea6e046ac566dbc9..520da9d04267177bc8e6998f55992c31819bc899 100644
--- a/src/Corporation/manageCorporation.tw
+++ b/src/Corporation/manageCorporation.tw
@@ -73,7 +73,7 @@
 	<br><<= _div.messageSlaveCount() >>
 
 	<<set _divMaint = _div.getDisplayMaintenanceCost()>>
-	<br>It costs @@.red;<<= cashFormat(Math.trunc(_divMaint.cost))>>@@ to run. On average that is @@.red;<<= cashFormat(Math.trunc(_divMaint.perUnit)) >>@@ per slave.
+	<br>It costs @@.cash.dec;<<= cashFormat(Math.trunc(_divMaint.cost))>>@@ to run. On average that is @@.cash.dec;<<= cashFormat(Math.trunc(_divMaint.perUnit)) >>@@ per slave.
 	<br><<= _div.messageSlaveOutput() >>
 	<<set _divSentenceStart = ["Currently the division", "It also"]>>
 	<h3>Direct Control</h3>
@@ -172,7 +172,7 @@
 
 	/* Expanding the division*/
 	<<set _depExpandCost = _div.sizeCost * 1000>>
-	<div>Expanding the division costs <span class="red"><<print cashFormat(_depExpandCost)>>.</span> Downsizing recoups 80% of the investment; slaves will be sold at the going rate.</div>
+	<div>Expanding the division costs <span class="cash dec"><<print cashFormat(_depExpandCost)>>.</span> Downsizing recoups 80% of the investment; slaves will be sold at the going rate.</div>
 	<div>
 	<<set _buyDevArray = [
 				{ 'name': 'Expand Division' , 'count':1},
@@ -250,7 +250,7 @@
 			(@@.yellowgreen;<<print cashFormat(_depCost)>>@@): A division focusing on <<= _div.focusDescription >>.
 		<<else>>
 			<<=  _div.name >>
-			(@@.red;<<print cashFormat(_depCost)>>@@):
+			(@@.cash.dec;<<print cashFormat(_depCost)>>@@):
 			The corporation cannot afford to start a division focusing on <<= _div.focusDescription >>.
 		<</if>>
 		</div>
@@ -296,7 +296,7 @@
 
 <h2>Shares</h2>
 You own <<print num($personalShares)>> shares while another <<print num($publicShares)>> shares are traded publicly. The going rate on the market for 1000 shares is currently @@.yellowgreen;<<print cashFormat(corpSharePrice())>>.@@
-<br>The corporation can buyback 1000 shares for @@.red;<<print cashFormat(corpSharePrice(-1000))>>@@ or issue 1000 shares and net @@.yellowgreen;<<print cashFormat(corpSharePrice(1000))>>.@@ The corporation will prefer to round shares to the nearest 1000 and will issue or buy shares toward that goal first.
+<br>The corporation can buyback 1000 shares for @@.cash.dec;<<print cashFormat(corpSharePrice(-1000))>>@@ or issue 1000 shares and net @@.yellowgreen;<<print cashFormat(corpSharePrice(1000))>>.@@ The corporation will prefer to round shares to the nearest 1000 and will issue or buy shares toward that goal first.
 <<if $corp.Cash > corpSharePrice(-1000)>>
 	<<if $publicShares <= $personalShares - 2000 && $publicShares > 0>> /*It won't buy back player shares if the corporation is entirely owned by the player*/
 		<<set _persExtraShares = $personalShares % 1000 || 1000>>
@@ -333,7 +333,7 @@ You own <<print num($personalShares)>> shares while another <<print num($publicS
 <<set _splitFeeValue = _splitFeeInitial - Math.floor((_splitFeeInitial * ($PC.skill.trading / 100.0) / 2.0) / 1000) * 1000>>
 <<set _splitStockConstants = App.Corporate.stockSplits >>
 
-	The corporation can perform a stock split to increase the number of stocks while maintaining the same owned value. This requires paying a market fee of @@.red;<<= cashFormat(_splitFeeValue)>>@@ plus a per-share fee depending on the type of split being done.
+	The corporation can perform a stock split to increase the number of stocks while maintaining the same owned value. This requires paying a market fee of @@.cash.dec;<<= cashFormat(_splitFeeValue)>>@@ plus a per-share fee depending on the type of split being done.
 	<<if _splitFeeValue < _splitFeeInitial>>
 		//You negotiated lower fees due to your @@.springgreen;business acumen@@.//
 	<</if>>
@@ -349,8 +349,8 @@ You own <<print num($personalShares)>> shares while another <<print num($publicS
 	<<set _splitMultiplier = _splitNumerator / _splitDenom>>
 	<<set _splitTotal	  = _splitValue * ($publicShares + $personalShares) + _splitFeeValue>>
 	<<set _splitWeek	   = _stockType['weeks']>>
-	<li><<= _splitNumerator >>-for-<<= _splitDenom>> <<if _splitDenom > _splitNumerator>>inverse<</if>> stock split at @@.red;<<= cashFormat(_splitValue) >>@@ per share.
-		Including market fees, this will cost the corporation a total of @@.red;<<= cashFormat(_splitTotal)>>,@@
+	<li><<= _splitNumerator >>-for-<<= _splitDenom>> <<if _splitDenom > _splitNumerator>>inverse<</if>> stock split at @@.cash.dec;<<= cashFormat(_splitValue) >>@@ per share.
+		Including market fees, this will cost the corporation a total of @@.cash.dec;<<= cashFormat(_splitTotal)>>,@@
 		leaving the going rate for stock at @@.yellowgreen;<<= cashFormat(Math.floor(corpSharePrice(0, $personalShares * _splitMultiplier, $publicShares * _splitMultiplier))) >>@@ per 1000 shares.
 	<<if $corp.SpecTimer == 0>>
 		<<if $publicShares % _splitDenom != 0 || $personalShares % _splitDenom != 0>>
diff --git a/src/Mods/DinnerParty/dinnerPartyExecution.tw b/src/Mods/DinnerParty/dinnerPartyExecution.tw
index 00de09aa3a718debfb0ecf3f5870ec490d68c572..54f1b1bf5a02675cd784c92db699d9125a2c7d6e 100644
--- a/src/Mods/DinnerParty/dinnerPartyExecution.tw
+++ b/src/Mods/DinnerParty/dinnerPartyExecution.tw
@@ -454,7 +454,7 @@
 			<</if>>
 			//you trained a fine obedient slave. _He2 certainly understands _his2 position well."// You grit your teeth faking a smile, mentally repeating the old adage: @@.yellow;revenge is dish best served cold.@@ You have made some enemies tonight; you begin to plan a military conquest of them all. You will enjoy storming their arcologies and making them pay for this insult.
 			<br><br>
-			The abuse your Head Girl suffers had @@.red;decreased _his2 health@@ and _he2 is @@.mediumorchid;less devoted@@ to you, as you allowed _him2 to be abused.
+			The abuse your Head Girl suffers had @@.health.dec;decreased _his2 health@@ and _he2 is @@.mediumorchid;less devoted@@ to you, as you allowed _him2 to be abused.
 			<br>
 			<<run healthDamage(_S.HeadGirl, 20)>>
 			<<set _S.HeadGirl.devotion -= 20>>
diff --git a/src/Mods/SecExp/attackGenerator.tw b/src/Mods/SecExp/attackGenerator.tw
deleted file mode 100644
index 980c5d5748079c38d196099ab9af742ef394f712..0000000000000000000000000000000000000000
--- a/src/Mods/SecExp/attackGenerator.tw
+++ /dev/null
@@ -1,176 +0,0 @@
-:: attackGenerator [nobr]
-
-/* _attackChance value is the chance out of 100 of an attack happening this week */
-/* attacks are deactivated if the arcology is in the middle of the ocean, security drones are not around yet, there is not a rebellion this week or the last attack/rebellion happened within 3 weeks */
-<<if $terrain == "oceanic" || $arcologyUpgrade.drones != 1 || $SecExp.battles.lastEncounterWeeks <= 3 || $citizenRebellion == 1 || $slaveRebellion == 1 || $SecExp.rebellions.lastEncounterWeeks <= 3>>
-	<<set _attackChance = 0>>
-<<else>>
-	<<if $week < 30>>
-		<<set _attackChance = 5>>
-	<<elseif $week < 60>>
-		<<set _attackChance = 8>>
-	<<elseif $week < 90>>
-		<<set _attackChance = 12>>
-	<<elseif $week < 120>>
-		<<set _attackChance = 16>>
-	<<else>>
-		<<set _attackChance = 20>>
-	<</if>>
-	<<if $SecExp.battles.victories + $SecExp.battles.losses > 0>>
-		<<set _attackChance = 25>>
-	<</if>>
-	<<if $SecExp.battles.lastEncounterWeeks >= 10>>
-		<<set _attackChance += 5>>
-	<</if>>
-<</if>>
-/* battle frequency */
-<<set _attackChance *= $SecExp.settings.battle.frequency>>
-
-<<if $SecExp.settings.battle.force == 1 && $SecExp.settings.rebellion.force != 1 && $foughtThisWeek == 0>>
-	<<set _attackChance = 100>>
-<</if>>
-
-/* Rolls to see if attack happens this week */
-/* raiders are attracted by low security */
-/* the old world by "degenerate" future societies */
-/* free Cities by high prosperity */
-/* freedom fighters by high slave/citizen ratio */
-<<if random(1,100) <= _attackChance>>
-	<<set $attackThisWeek = 1>>
-	<<set $SecExp.battles.lastEncounterWeeks = 0>>
-	<<set $leadingTroops = "assistant">>
-	<<set $chosenTactic = either("Bait and Bleed", "Blitzkrieg", "Choke Points", "Defense In Depth", "Guerrilla", "Human Wave", "Interior Lines", "Pincer Maneuver")>>
-	/* _type is the chance out of 100 of an attack of that type happening */
-	<<set _raider = 25>>
-	<<set _oldWorld = 25>>
-	<<set _freeCity = 25>>
-	<<set _free = 25>>
-	/* old world */
-	<<if $arcologies[0].FSRomanRevivalist != "unset" || $arcologies[0].FSEdoRevivalist != "unset" || $arcologies[0].FSArabianRevivalist != "unset" || $arcologies[0].FSChineseRevivalist != "unset" || $arcologies[0].FSEgyptianRevivalist != "unset" || $arcologies[0].FSAztecRevivalist != "unset" || $arcologies[0].FSRepopulationFocus != "unset" || $arcologies[0].FSGenderRadicalist != "unset" || $arcologies[0].FSPastoralist != "unset" || $arcologies[0].FSChattelReligionist != "unset" || $arcologies[0].FSNeoImperialist != "unset">>
-		<<set _oldWorld += 15>>
-		<<set _raider -= 5>>
-		<<set _freeCity -= 5>>
-		<<set _free -= 5>>
-	<<elseif ($arcologies[0].FSRomanRevivalist != "unset" || $arcologies[0].FSEdoRevivalist != "unset" || $arcologies[0].FSArabianRevivalist != "unset" || $arcologies[0].FSChineseRevivalist != "unset" || $arcologies[0].FSEgyptianRevivalist != "unset" || $arcologies[0].FSAztecRevivalist != "unset") && ($arcologies[0].FSRepopulationFocus != "unset" || $arcologies[0].FSGenderRadicalist != "unset" || $arcologies[0].FSPastoralist != "unset" || $arcologies[0].FSChattelReligionist != "unset" || $arcologies[0].FSNeoImperialist != "unset")>>
-		<<set _oldWorld += 24>>
-		<<set _raider -= 8>>
-		<<set _freeCity -= 8>>
-		<<set _free -= 8>>
-	<</if>>
-	/* freedom fighter */
-	<<if $ASlaves > $ACitizens * 2>>
-		<<set _oldWorld -= 8>>
-		<<set _raider -= 8>>
-		<<set _freeCity -= 8>>
-		<<set _free += 24>>
-	<<elseif $ASlaves > $ACitizens * 1.2 || $arcologies[0].FSDegradationist != "unset">>
-		<<set _oldWorld -= 5>>
-		<<set _raider -= 5>>
-		<<set _freeCity -= 5>>
-		<<set _free += 15>>
-	<</if>>
-	/* Free Cities */
-	<<if $arcologies[0].prosperity >= 10 && $arcologies[0].prosperity < 20>>
-		<<set _oldWorld -= 5>>
-		<<set _raider -= 5>>
-		<<set _freeCity += 15>>
-		<<set _free -= 5>>
-	<<elseif $arcologies[0].prosperity >= 20>>
-		<<set _oldWorld -= 8>>
-		<<set _raider -= 8>>
-		<<set _freeCity += 24>>
-		<<set _free -= 8>>
-	<</if>>
-	/* raiders */
-	<<if $SecExp.core.security <= 50>>
-		<<set _oldWorld -= 5>>
-		<<set _raider += 15>>
-		<<set _freeCity -= 5>>
-		<<set _free -= 5>>
-	<<elseif $SecExp.core.security <= 25>>
-		<<set _oldWorld -= 8>>
-		<<set _raider += 24>>
-		<<set _freeCity -= 8>>
-		<<set _free -= 8>>
-	<</if>>
-
-	/* makes the actual roll */
-	<<set _roll = random(1,100)>>
-	<<if _roll <= _raider>>
-		<<set $attackType = "raiders">>
-	<<elseif _roll <= _raider + _oldWorld>>
-		<<set $attackType = "old world">>
-	<<elseif _roll <= _raider + _oldWorld + _freeCity>>
-		<<set $attackType = "free city">>
-	<<elseif _roll <= _raider + _oldWorld + _freeCity + _free>>
-		<<set $attackType = "freedom fighters">>
-	<</if>>
-<<else>>
-	<<set $SecExp.battles.lastEncounterWeeks++>>
-<</if>>
-
-/* if an attack happens */
-<<if $attackThisWeek == 1>>
-
-	/* terrain */
-	<<if $terrain == "urban">>
-		<<set $battleTerrain = either("outskirts", "urban", "wasteland")>>
-	<<elseif $terrain == "rural">>
-		<<set $battleTerrain = either("hills", "outskirts", "rural", "wasteland")>>
-	<<elseif $terrain == "ravine">>
-		<<set $battleTerrain = either("hills", "mountains", "outskirts", "wasteland")>>
-	<<elseif $terrain == "marine">>
-		<<set $battleTerrain = either("coast", "hills", "outskirts", "wasteland")>>
-	<<elseif $terrain == "oceanic">>
-		<<set $battleTerrain = either("coast", "hills", "outskirts", "wasteland")>>
-	<<else>>
-		<<set $battleTerrain = "error">>
-	<</if>>
-
-	<<set _L=0>>
-	<<if $attackType == "raiders">>
-		<<set $attackTroops = random(40,80),_L=1>>
-	<<elseif $attackType == "free city">>
-		<<set $attackTroops = random(20,40)>>
-	<<elseif $attackType == "old world">>
-		<<set $attackTroops = random(25,50)>>
-	<<elseif $attackType == "freedom fighters">>
-		<<set $attackTroops = random(30,60)>>
-	<</if>>
-	<<if $week < 30>>
-		/*<<set $attackTroops *= Math.trunc(random( (1*(1.01+($week/100))), (2*(1.01+($week/100))) ))>>*/ <<set $attackTroops *= random(1,2)>>
-	<<elseif $week < 60>>
-		/*<<set $attackTroops *= Math.trunc(random( (1*(1.01+($week/200))), (3*(1.01+($week/200))) ))>>*/ <<set $attackTroops *= random(1,3)>>
-	<<elseif $week < 90>>
-		/*<<set $attackTroops *= Math.trunc(random( (2*(1.01+($week/300))), (3*(1.01+($week/300))) ))>>*/ <<set $attackTroops *= random(2,3)>>
-	<<elseif $week < 120>>
-		/*<<set $attackTroops *= Math.trunc(random( (2*(1.01+($week/400))), (4*(1.01+($week/400))) ))>>*/ <<set $attackTroops *= random(2,4)>>
-	<<else>>
-		<<set $attackTroops *= random(3,5)>>
-	<</if>>
-	<<if $week < 60>>
-		<<set $attackEquip = random(0,1)>>
-	<<elseif $week < 90>>
-		<<set $attackEquip = random(0,3-_L)>> /*"raiders" <<set $attackEquip = random(0,2)>>*/
-	<<elseif $week < 120>>
-		<<set $attackEquip = random(1-_L,3)>> /*"raiders" <<set $attackEquip = random(0,3)>>*/
-	<<else>>
-		<<set $attackEquip = random(2-_L,4-_L)>> /*"raiders" <<set $attackEquip = random(1,3)>>*/
-	<</if>>
-
-	/* major battles have a 50% chance of firing after week 120 */
-	<<if $SecExp.settings.battle.major.enabled == 1>>
-		<<if ($week >= 120 && $attackType != "none") || ($SecExp.settings.battle.major.force == 1 && $foughtThisWeek == 0)>>
-			<<if random(1,100) >= 50 || $SecExp.settings.battle.major.force == 1>>
-				<<set $majorBattle = 1>>
-				<<if $SF.Toggle && $SF.Active >= 1>>
-					<<set $attackTroops = Math.trunc($attackTroops * random(4,6) * $SecExp.settings.battle.major.mult)>>
-					<<set $attackEquip = either(3,4)>>
-				<<else>>
-					<<set $attackTroops = Math.trunc($attackTroops * random(2,3) * $SecExp.settings.battle.major.mult)>>
-					<<set $attackEquip = either(2,3,4)>>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
\ No newline at end of file
diff --git a/src/Mods/SecExp/attackHandler.tw b/src/Mods/SecExp/attackHandler.tw
index 9219ddbf2b6da965264c9f004e27c8ac6151f278..38c12b64779114bfe94dc2e46bdba3155c97385a 100644
--- a/src/Mods/SecExp/attackHandler.tw
+++ b/src/Mods/SecExp/attackHandler.tw
@@ -2,20 +2,20 @@
 
 <<set $nextButton = " ", $nextLink = "attackReport", $encyclopedia = "Battles">>
 
-<<if $battleResult == 1 || $battleResult == -1>>	/* bribery/surrender check */
+<<if $SecExp.war.result == 1 || $SecExp.war.result == -1>>	/* bribery/surrender check */
 	<<if $SecExp.settings.showStats == 1>>
-		<<if $battleResult == 1>>Bribery<<else>>Surrender<</if>> chosen
+		<<if $SecExp.war.result == 1>>Bribery<<else>>Surrender<</if>> chosen
 	<</if>>
-	<<if $battleResult == 1>>
+	<<if $SecExp.war.result == 1>>
 		<<if $cash >= App.SecExp.battle.bribeCost()>> /* if there's enough cash there's a 10% chance bribery fails. If there isn't there's instead a 50% chance it fails */
-			<<if $attackType == "freedom fighters" && random(1,100) <= 50 || random(1,100) <= 10>>
-				<<set $battleResult = 0>>
+			<<if $SecExp.war.attacker.type == "freedom fighters" && random(1,100) <= 50 || random(1,100) <= 10>>
+				<<set $SecExp.war.result = 0>>
 			<</if>>
 		<<else>>
-			<<if random(1,100) <= 50>> <<set $battleResult = 0>> <</if>>
+			<<if random(1,100) <= 50>> <<set $SecExp.war.result = 0>> <</if>>
 		<</if>>
 		<<if $SecExp.settings.showStats == 1>>
-			<br>Bribery <<if $battleResult == 0>>failed<<else>>Successful<</if>>!
+			<br>Bribery <<if $SecExp.war.result == 0>>failed<<else>>Successful<</if>>!
 			<br><br>[[proceed|attackReport]]
 		<<else>>
 			<<goto "attackReport">>
@@ -25,9 +25,7 @@
 	<</if>>
 <<else>>
 	/*Init*/
-	<<if $majorBattle == 0>>
-		<<set _turns = $maxTurns>>
-	<</if>>
+	<<set _turns = 10>>
 	<<set _attack = 0>>
 	<<set _defense = 0>>
 	<<set _morale = 0>>
@@ -56,7 +54,7 @@
 		<<set _mercMod = 1.5>>
 		<<set _enemyMod = 1.5>>
 		<<set _SFMod = 1.5>>
-		<<set _turns = $maxTurns * 2>>
+		<<set _turns *= 2>>
 		<<if $SF.Toggle && $SF.Active >= 1>>
 			<<if $SF.Squad.Firebase >= 7>>
 				<<set _atkMod += ($SF.Squad.Firebase - 6) * 0.05>>
@@ -77,7 +75,7 @@
 	<</if>>
 
 	/* Leaders */
-	<<if $leadingTroops == "PC">>
+	<<if $SecExp.war.commander == "PC">>
 		<<if $SecExp.core.authority <= 2500 && $SecExp.core.authority > 1000>>
 			<<set _slaveMod -= 0.10>>
 		<<elseif $SecExp.core.authority <= 1000>>
@@ -133,7 +131,7 @@
 		<</if>>
 		/* 80% chance of increasing warfare */
 		<<if $PC.skill.warfare < 100 && random(1,100) <= 80>>
-			<<set $gainedWarfare = 1>>
+			<<set $SecExp.war.gainedWarfare = 1>>
 			<<= IncreasePCSkills('warfare', 10)>>
 			<<set $PC.skill.warfare = Math.clamp($PC.skill.warfare,-100,100)>>
 		<</if>>
@@ -165,7 +163,7 @@
 			<<set _woundChance += 1>>
 		<</if>>
 		<<if random(1,100) <= _woundChance>>
-			<<set $leaderWounded = 1>>
+			<<set $SecExp.war.leaderWounded = 1>>
 			<<set _militiaMod -= 0.2>>
 			<<set _slaveMod -= 0.2>>
 			<<set _mercMod -= 0.2>>
@@ -173,7 +171,7 @@
 			<<set _enemyMod += 0.2>>
 			<<run healthDamage($PC, 60)>>
 		<</if>>
-	<<elseif $leadingTroops == "assistant">>
+	<<elseif $SecExp.war.commander == "assistant">>
 		<<if $rep < 10000 && $SecExp.core.authority < 10000>>
 			<<set _militiaMod -= 0.15>>
 			<<set _slaveMod -= 0.15>>
@@ -193,7 +191,7 @@
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.30>>
 		<</if>>
-	<<elseif $leadingTroops == "bodyguard">>
+	<<elseif $SecExp.war.commander == "bodyguard">>
 		<<if _S.Bodyguard.devotion < -20>>
 			<<set _slaveMod -= 0.15>>
 		<<elseif _S.Bodyguard.devotion > 50>>
@@ -208,7 +206,7 @@
 			<<set _mercMod += 0.10>>
 			<<set _SFMod += 0.10>>
 		<</if>>
-		<<set _BGCareerGivesBonus = setup.bodyguardCareers.includes(_S.Bodyguard.career) || setup.HGCareers.includes(_S.Bodyguard.career) || setup.secExCombatPrestige.includes(_S.Bodyguard.prestigeDesc)>>
+		<<set _BGCareerGivesBonus = App.Data.Careers.Leader.bodyguard.includes(_S.Bodyguard.career) || App.Data.Careers.Leader.HG.includes(_S.Bodyguard.career) || setup.secExCombatPrestige.includes(_S.Bodyguard.prestigeDesc)>>
 		<<set _BGTotalIntelligence = _S.Bodyguard.intelligence+_S.Bodyguard.intelligenceImplant>>
 		<<if _BGCareerGivesBonus && _BGTotalIntelligence > 95>>
 			<<set _atkMod += 0.25>>
@@ -290,7 +288,7 @@
 			<<set _woundChance += 1>>
 		<</if>>
 		<<if random(1,100) <= _woundChance>>
-			<<set $leaderWounded = 1>>
+			<<set $SecExp.war.leaderWounded = 1>>
 			<<set _militiaMod -= 0.2>>
 			<<set _slaveMod -= 0.2>>
 			<<set _mercMod -= 0.2>>
@@ -299,10 +297,10 @@
 		<</if>>
 		/* 60% chance of getting combat skill if not already have it */
 		<<if _S.Bodyguard.skill.combat == 0 && random(1,100) <= 60>>
-			<<set $gainedCombat = 1>>
+			<<set $SecExp.war.gainedCombat = 1>>
 			<<set _S.Bodyguard.skill.combat = 1>>
 		<</if>>
-	<<elseif $leadingTroops == "headGirl">>
+	<<elseif $SecExp.war.commander == "headGirl">>
 		<<if _S.HeadGirl.devotion < -20>>
 			<<set _slaveMod -= 0.15>>
 		<<elseif _S.HeadGirl.devotion > 51>>
@@ -317,7 +315,7 @@
 			<<set _mercMod += 0.10>>
 			<<set _SFMod += 0.10>>
 		<</if>>
-		<<if (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
+		<<if (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
 			<<set _atkMod += 0.25>>
 			<<set _defMod += 0.25>>
 			<<set _tacChance += 0.50>>
@@ -325,7 +323,7 @@
 			<<set _atkMod += 0.20>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.35>>
-		<<elseif (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
+		<<elseif (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
@@ -333,7 +331,7 @@
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
+		<<elseif (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.15>>
@@ -341,7 +339,7 @@
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif !(setup.bodyguardCareers.includes(_S.HeadGirl.career) && setup.HGCareers.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
+		<<elseif !(App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) && App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.30>>
@@ -349,7 +347,7 @@
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.25>>
-		<<elseif !(setup.bodyguardCareers.includes(_S.HeadGirl.career) && setup.HGCareers.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
+		<<elseif !(App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) && App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
@@ -397,7 +395,7 @@
 			<<set _woundChance += 1>>
 		<</if>>
 		<<if random(1,100) <= _woundChance>>
-			<<set $leaderWounded = 1>>
+			<<set $SecExp.war.leaderWounded = 1>>
 			<<set _militiaMod -= 0.2>>
 			<<set _slaveMod -= 0.2>>
 			<<set _mercMod -= 0.2>>
@@ -406,10 +404,10 @@
 		<</if>>
 		/* 60% chance of getting combat skill if not already have it */
 		<<if _S.HeadGirl.skill.combat == 0 && random(1,100) <= 60>>
-			<<set $gainedCombat = 1>>
+			<<set $SecExp.war.gainedCombat = 1>>
 			<<set _S.HeadGirl.skill.combat = 1>>
 		<</if>>
-	<<elseif $leadingTroops == "citizen">>
+	<<elseif $SecExp.war.commander == "citizen">>
 		<<if $arcologies[0].FSDegradationist == "unset" && $arcologies[0].FSPaternalist == "unset">>
 			<<set _militiaMod += 0.15>>
 			<<set _slaveMod -= 0.15>>
@@ -430,7 +428,7 @@
 		<<set _atkMod += either(-1,1) * random(10) * 0.1>>
 		<<set _defMod += either(-1,1) * random(10) * 0.1>>
 		<<set _tacChance += either(-1,1) * random(20) * 0.1>>
-	<<elseif $leadingTroops == "mercenary">>
+	<<elseif $SecExp.war.commander == "mercenary">>
 		<<set _mercMod += 0.10>>
 		<<set _SFMod += 0.10>>
 		<<if $arcologies[0].FSRomanRevivalist != "unset" || $arcologies[0].FSNeoImperialist != "unset">>
@@ -444,7 +442,7 @@
 		<<set _atkMod += random(15) * 0.1>>
 		<<set _defMod += random(15) * 0.1>>
 		<<set _tacChance += random(30) * 0.1>>
-	<<elseif $leadingTroops == "colonel">>
+	<<elseif $SecExp.war.commander == "colonel">>
 		<<set _mercMod += 0.10>>
 		<<set _SFMod += 0.15>>
 		<<if $arcologies[0].FSRomanRevivalist != "unset" || $arcologies[0].FSNeoImperialist != "unset">>
@@ -460,322 +458,322 @@
 		<<set _tacChance += random(40) * 0.1>>
 	<</if>>
 	/* Terrain and Tactics */
-	<<if $battleTerrain == "urban">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<if $SecExp.war.terrain == "urban">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.15>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.10>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.25>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.30>>
 		<</if>>
-	<<elseif $battleTerrain == "rural">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "rural">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.15>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.25>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.30>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod += 0.20>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.25>>
 		<</if>>
-	<<elseif $battleTerrain == "hills">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "hills">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.10>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.15>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.30>>
 		<</if>>
-	<<elseif $battleTerrain == "coast">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "coast">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.10>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod -= 0.05>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.10>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod -= 0.05>>
 		<</if>>
-	<<elseif $battleTerrain == "outskirts">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "outskirts">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.25>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.15>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance += 0.05>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod -= 0.10>>
 		<</if>>
-	<<elseif $battleTerrain == "mountains">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "mountains">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.20>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance += 0.05>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
 		<</if>>
-	<<elseif $battleTerrain == "wasteland">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "wasteland">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance -= 0.05>>
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.15>>
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.30>>
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			<<set _atkMod += 0.20>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.25>>
 		<</if>>
 	<</if>>
 
-	<<if $chosenTactic == "Bait and Bleed">>
-		<<if $attackType == "raiders">>
+	<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance -= 0.10>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.10>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance += 0.25>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance -= 0.15>>
 		<</if>>
-	<<elseif $chosenTactic == "Guerrilla">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance -= 0.20>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.15>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance += 0.25>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance -= 0.25>>
 		<</if>>
-	<<elseif $chosenTactic == "Choke Points">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Choke Points">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance += 0.25>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance -= 0.05>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance -= 0.10>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance += 0.05>>
 		<</if>>
-	<<elseif $chosenTactic == "Interior Lines">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance -= 0.15>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.15>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance += 0.20>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance -= 0.10>>
 		<</if>>
-	<<elseif $chosenTactic == "Pincer Maneuver">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance += 0.15>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.10>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance -= 0.10>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance += 0.15>>
 		<</if>>
-	<<elseif $chosenTactic == "Defense In Depth">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance -= 0.20>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.10>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance += 0.20>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance -= 0.05>>
 		<</if>>
-	<<elseif $chosenTactic == "Blitzkrieg">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance += 0.10>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance -= 0.20>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance += 0.25>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance -= 0.10>>
 		<</if>>
-	<<elseif $chosenTactic == "Human Wave">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Human Wave">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			<<set _tacChance -= 0.10>>
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			<<set _tacChance += 0.10>>
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			<<set _tacChance -= 0.15>>
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			<<set _tacChance += 0.10>>
 		<</if>>
 	<</if>>
@@ -792,7 +790,7 @@
 		<<set _mercMod += 0.20>>
 		<<set _atkMod += 0.10>>
 		<<set _defMod += 0.10>>
-		<<set $tacticsSuccessful = 1>>
+		<<set $SecExp.war.tacticsSuccessful = 1>>
 	<<else>>
 		<<set _enemyMod += 0.20>>
 		<<set _militiaMod -= 0.20>>
@@ -800,7 +798,6 @@
 		<<set _mercMod -= 0.20>>
 		<<set _atkMod -= 0.10>>
 		<<set _defMod -= 0.10>>
-		<<set $tacticsSuccessful = 0>>
 	<</if>>
 
 	/* enemy morale mods */
@@ -848,7 +845,7 @@
 		<</if>>
 	<</for>>
 
-	<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+	<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 		<<set _unit = App.SecExp.getUnit("SF")>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -880,24 +877,24 @@
 
 	<<set _moraleTroopMod = Math.clamp(App.SecExp.battle.troopCount() / 100,1,5)>>
 
-	<<set _morale = (App.SecExp.BaseDroneUnit.morale * $secBots.isDeployed + App.SecExp.BaseMilitiaUnit.morale * _militiaMod * App.SecExp.battle.deployedUnits('militia') + App.SecExp.BaseSlaveUnit.morale * _slaveMod * App.SecExp.battle.deployedUnits('slaves') + App.SecExp.BaseMercUnit.morale * _mercMod * App.SecExp.battle.deployedUnits('mercs') + App.SecExp.BaseSpecialForcesUnit.morale * $SFIntervention * _SFMod) / ($secBots.isDeployed + App.SecExp.battle.deployedUnits('militia') + App.SecExp.battle.deployedUnits('slaves') + App.SecExp.battle.deployedUnits('mercs') + $SFIntervention)>>
+	<<set _morale = (App.SecExp.BaseDroneUnit.morale * $secBots.isDeployed + App.SecExp.BaseMilitiaUnit.morale * _militiaMod * App.SecExp.battle.deployedUnits('militia') + App.SecExp.BaseSlaveUnit.morale * _slaveMod * App.SecExp.battle.deployedUnits('slaves') + App.SecExp.BaseMercUnit.morale * _mercMod * App.SecExp.battle.deployedUnits('mercs') + App.SecExp.BaseSpecialForcesUnit.morale * $SecExp.war.deploySF * _SFMod) / ($secBots.isDeployed + App.SecExp.battle.deployedUnits('militia') + App.SecExp.battle.deployedUnits('slaves') + App.SecExp.battle.deployedUnits('mercs') + $SecExp.war.deploySF)>>
 	<<if $SecExp.buildings.barracks>>
 		<<set _morale = _morale + _morale * $SecExp.buildings.barracks.luxury * 0.05>>	/* barracks bonus */
 	<</if>>
 	<<set _morale *= _moraleTroopMod>>
-	<<set _baseHp = (App.SecExp.BaseDroneUnit.hp * $secBots.isDeployed + App.SecExp.BaseMilitiaUnit.hp * App.SecExp.battle.deployedUnits('militia') + App.SecExp.BaseSlaveUnit.hp * App.SecExp.battle.deployedUnits('slaves') + App.SecExp.BaseMercUnit.hp * App.SecExp.battle.deployedUnits('mercs') + App.SecExp.BaseSpecialForcesUnit.hp * $SFIntervention) / ($secBots.isDeployed + App.SecExp.battle.deployedUnits('militia') + App.SecExp.battle.deployedUnits('slaves') + App.SecExp.battle.deployedUnits('mercs') + $SFIntervention)>>
+	<<set _baseHp = (App.SecExp.BaseDroneUnit.hp * $secBots.isDeployed + App.SecExp.BaseMilitiaUnit.hp * App.SecExp.battle.deployedUnits('militia') + App.SecExp.BaseSlaveUnit.hp * App.SecExp.battle.deployedUnits('slaves') + App.SecExp.BaseMercUnit.hp * App.SecExp.battle.deployedUnits('mercs') + App.SecExp.BaseSpecialForcesUnit.hp * $SecExp.war.deploySF) / ($secBots.isDeployed + App.SecExp.battle.deployedUnits('militia') + App.SecExp.battle.deployedUnits('slaves') + App.SecExp.battle.deployedUnits('mercs') + $SecExp.war.deploySF)>>
 
 	/* calculates enemy army stats */
 	<<if $week <= 30>>
-		<<set _armyMod = $attackTroops / 80>>
+		<<set _armyMod = $SecExp.war.attacker.troops / 80>>
 	<<elseif $week <= 60>>
-		<<set _armyMod = $attackTroops / 75>>
+		<<set _armyMod = $SecExp.war.attacker.troops / 75>>
 	<<elseif $week <= 90>>
-		<<set _armyMod = $attackTroops / 70>>
+		<<set _armyMod = $SecExp.war.attacker.troops / 70>>
 	<<elseif $week <= 120>>
-		<<set _armyMod = $attackTroops / 65>>
+		<<set _armyMod = $SecExp.war.attacker.troops / 65>>
 	<<else>>
-		<<set _armyMod = $attackTroops / 60>>
+		<<set _armyMod = $SecExp.war.attacker.troops / 60>>
 	<</if>>
 	<<set _armyMod = Math.trunc(_armyMod)>>
 	<<if $majorBattle == 1>>
@@ -907,14 +904,14 @@
 		<<set _armyMod = 1>>
 	<</if>>
 
-	<<set _enemyMoraleTroopMod = Math.clamp($attackTroops / 100,1,5)>>
+	<<set _enemyMoraleTroopMod = Math.clamp($SecExp.war.attacker.troops / 100,1,5)>>
 
-	<<set _unit = App.SecExp.getEnemyUnit($attackType, $attackTroops, $attackEquip)>>
+	<<set _unit = App.SecExp.getEnemyUnit($SecExp.war.attacker.type, $SecExp.war.attacker.troops, $SecExp.war.attacker.equip)>>
 	<<set _enemyAttack = _unit.attack * _armyMod>>
 	<<set _enemyDefense = _unit.defense * _armyMod>>
 	<<set _enemyMorale = _unit.morale * _enemyMod * _enemyMoraleTroopMod>>
 	<<set _enemyHp = _unit.hp>>
-	<<set _enemyBaseHp = _unit.hp / $attackTroops>>
+	<<set _enemyBaseHp = _unit.hp / $SecExp.war.attacker.troops>>
 
 	/* difficulty */
 	<<set _enemyAttack *= $SecExp.settings.difficulty>>
@@ -990,7 +987,7 @@
 		<br>militia morale modifier: <<if _militiaMod > 0>>+<</if>>_militiaMod%
 		<br>slaves morale modifier: <<if _slaveMod > 0>>+<</if>>_slaveMod%
 		<br>mercenaries morale modifier: <<if _mercMod > 0>>+<</if>>_mercMod%
-		<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 		<br>special force morale modifier: <<if _SFMod > 0>>+<</if>>_SFMod%
 		<</if>>
 		<<if $SecExp.buildings.barracks && $SecExp.buildings.barracks.luxury >= 1>>
@@ -1001,9 +998,9 @@
 		<</if>>
 		<br><br>__Tactics__:
 		<br>tactic chance of success: <<print num(Math.round(_tacChance * 100))>>%
-		<br>was tactic chosen successful?: <<if $tacticsSuccessful == 1>> yes <<else>> no<</if>>
+		<br>was tactic chosen successful?: <<if $SecExp.war.tacticsSuccessful == 1>> yes <<else>> no<</if>>
 		<br><br>__Enemy__:
-		<br>enemy troops: <<print num(Math.round($attackTroops))>>
+		<br>enemy troops: <<print num(Math.round($SecExp.war.attacker.troops))>>
 		<br>enemy attack: <<print num(Math.round(_enemyAttack))>>
 		<br>enemy defense: <<print num(Math.round(_enemyDefense))>>
 		<br>enemy Hp: <<print num(Math.round(_enemyHp))>>
@@ -1024,14 +1021,14 @@
 		<<if $SecExp.settings.showStats == 1>> <br>player damage: <<print num(Math.round(_damage))>><</if>>
 		<<set _enemyHp -= _damage>>
 		<<if $SecExp.settings.showStats == 1>> <br>remaining enemy Hp: <<print num(Math.round(_enemyHp))>><</if>>
-		<<set $enemyLosses += _damage / _enemyBaseHp>>
+		<<set $SecExp.war.attacker.losses += _damage / _enemyBaseHp>>
 		<<set _moraleDamage = Math.clamp(_damage / 2 + _damage / _enemyBaseHp,0,_damage*1.5)>>
 		<<set _enemyMorale -= _moraleDamage>>
 		<<if $SecExp.settings.showStats == 1>> <br>remaining enemy morale: <<print num(Math.round(_enemyMorale))>><</if>>
 		<<if _enemyHp <= 0 || _enemyMorale <= 0>>
 			<<if $SecExp.settings.showStats == 1>> <br><br>Victory!<</if>>
-			<<set $battleResult = 3>>
-			<<set $battleTurns = _i>>
+			<<set $SecExp.war.result = 3>>
+			<<set $SecExp.war.turns = _i>>
 			<<break>>
 		<</if>>
 
@@ -1043,44 +1040,44 @@
 		<<if $SecExp.settings.showStats == 1>> <br>enemy damage: <<print num(Math.round(_damage))>><</if>>
 		<<set _hp -= _damage>>
 		<<if $SecExp.settings.showStats == 1>> <br>remaining hp: <<print num(Math.round(_hp))>><</if>>
-		<<set $losses += _damage / _baseHp>>
+		<<set $SecExp.war.losses += _damage / _baseHp>>
 		<<set _moraleDamage = Math.clamp(_damage / 2 + _damage / _baseHp,0,_damage*1.5)>>
 		<<set _morale -= _moraleDamage>>
 		<<if $SecExp.settings.showStats == 1>> <br>remaining morale: <<print Math.round(_morale)>><</if>>
 		<<if _hp <= 0 || _morale <= 0>>
 			<<if $SecExp.settings.showStats == 1>> <br><br>Defeat!<</if>>
-			<<set $battleResult = -3>>
-			<<set $battleTurns = _i>>
+			<<set $SecExp.war.result = -3>>
+			<<set $SecExp.war.turns = _i>>
 			<<break>>
 		<</if>>
 	<</for>>
-	<<if $battleResult != 3 && $battleResult != -3>>
+	<<if $SecExp.war.result != 3 && $SecExp.war.result != -3>>
 		<<if _morale > _enemyMorale>>
 			<<if $SecExp.settings.showStats == 1>> <br><br>Partial victory!<</if>>
-			<<set $battleResult = 2>>
+			<<set $SecExp.war.result = 2>>
 		<<elseif _morale < _enemyMorale>>
 			<<if $SecExp.settings.showStats == 1>> <br><br>Partial defeat!<</if>>
-			<<set $battleResult = -2>>
+			<<set $SecExp.war.result = -2>>
 		<</if>>
 	<</if>>
 
 	<<if $SecExp.settings.showStats == 1>>
-		<br><br>Losses: <<print num(Math.trunc($losses))>>
-		<br>Enemy losses: <<print num(Math.trunc($enemyLosses))>>
+		<br><br>Losses: <<print num(Math.trunc($SecExp.war.losses))>>
+		<br>Enemy losses: <<print num(Math.trunc($SecExp.war.attacker.losses))>>
 	<</if>>
 
-	<<if $battleResult > 3 || $battleResult < -3>>
+	<<if $SecExp.war.result > 3 || $SecExp.war.result < -3>>
 		<br><br>@@.red;Error: failed to determine battle result@@
 	<</if>>
 
 	<<if $SecExp.settings.showStats == 1>>
-		<<if $majorBattle == 1 && $SecExp.settings.battle.major.gameOver == 1 && $battleResult == -3>>
+		<<if $majorBattle == 1 && $SecExp.settings.battle.major.gameOver == 1 && $SecExp.war.result == -3>>
 			<br><br>[[Proceed|Gameover][$gameover = "major battle defeat"]]
 		<<else>>
 			<br><br>[[Proceed|attackReport]]
 		<</if>>
 	<<else>>
-		<<if $majorBattle == 1 && $SecExp.settings.battle.major.gameOver == 1 && $battleResult == -3>>
+		<<if $majorBattle == 1 && $SecExp.settings.battle.major.gameOver == 1 && $SecExp.war.result == -3>>
 			<<set $gameover = "major battle defeat">> <<goto "Gameover">>
 		<<else>>
 			<<goto "attackReport">>
diff --git a/src/Mods/SecExp/attackOptions.tw b/src/Mods/SecExp/attackOptions.tw
index 9e883235e5561895640f07c3a6d83b3ae3816ea7..8c358d9abc96fd3b6c62da750be4888a7de0e294 100644
--- a/src/Mods/SecExp/attackOptions.tw
+++ b/src/Mods/SecExp/attackOptions.tw
@@ -3,7 +3,7 @@
 <<if ndef $SecExp.settings.unitDescriptions>>
 	<<set $SecExp.settings.unitDescriptions = 0>>
 <</if>>
-<<set $nextButton = " ", $nextLink = "attackOptions", $encyclopedia = "Battles">>
+<<set $nextButton = " ", $encyclopedia = "Battles">>
 
 <<set _options = new App.UI.OptionsGroup()>>
 <<run _options.addOption("Unit descriptions are", "unitDescriptions", $SecExp.settings)
@@ -16,7 +16,7 @@
 <<if $majorBattle == 0>>
 	<<if $SecExp.battles.victories + $SecExp.battles.losses > 0>>
 		The ominous message dominates the screens of your office, and <<print $assistant.name>> quickly gathers all information available to prepare for battle.
-		<<if $attackType == "raiders">>
+		<<if $SecExp.war.attacker.type === "raiders">>
 			<<if App.SecExp.battle.recon() >= 1>>
 				A disorganized horde of raiders is coming to your city. To such jackals your arcology surely looks like an appetizing morsel.
 				Fortunately you knew of their coming, thanks to your recon systems.
@@ -24,7 +24,7 @@
 				Some of your citizens saw the disorganized horde of raiders coming towards the city and quickly reported it. To such jackals your arcology surely looks like an appetizing morsel.
 			<</if>>
 			<br>Raiders are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type === "free city">>
 			<<if App.SecExp.battle.recon() >= 1>>
 				A menacing column of slavers and hired mercenaries is coming to your city. Another free city is ready to use their best tools to hit a dangerous competitor where it hurts.
 				Fortunately you knew of their coming, thanks to your recon systems.
@@ -32,7 +32,7 @@
 				Some of your citizens saw the menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. Another free city is ready to use their best tools to bring down a dangerous competitor.
 			<</if>>
 			<br>Free City expeditions are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have on average good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness however is their relatively low numbers.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type === "freedom fighters">>
 			<<if App.SecExp.battle.recon() >= 1>>
 				A dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.
 				Fortunately you knew of their coming, thanks to your recon systems.
@@ -40,7 +40,7 @@
 				Some of your citizens saw the dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.
 			<</if>>
 			<br>Freedom Fighters are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly they fight guerrilla style slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type === "old world">>
 			<<if App.SecExp.battle.recon() >= 1>>
 				A disciplined yet dusty, scruffy old world army is approaching the confines of your arcology. There's nothing better than a good war to unite the electorate and your arcology is just the perfect target.
 				Fortunately you knew of their coming, thanks to your recon systems.
@@ -59,7 +59,7 @@
 	<<else>>
 		Your assistant interrupted your rest to bring the grim news. You quickly rush to your console, where you can see the satellite images coming in of the force about to crash against your arcology. It's not the first time your armies fought for the survival of your empire, but this time it seems it will be a fight for life or death.
 	<</if>>
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type === "raiders">>
 		<<if App.SecExp.battle.recon() >= 1>>
 			A massive, disorganized horde of raiders is coming to your city. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.
 			Fortunately you knew of their coming, thanks to your recon systems.
@@ -67,7 +67,7 @@
 			Some of your citizens saw the massive, disorganized horde of raiders coming towards the city and quickly reported it. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.
 		<</if>>
 		<br>Raiders are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type === "free city">>
 		<<if App.SecExp.battle.recon() >= 1>>
 			A massive, menacing column of slavers and hired mercenaries is coming to your city. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.
 			Fortunately you knew of their coming, thanks to your recon systems.
@@ -75,7 +75,7 @@
 			Some of your citizens saw the massive, menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.
 		<</if>>
 		<br>Free City expeditions are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have, on average, good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness, however, is their relatively low numbers.
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type === "freedom fighters">>
 		<<if App.SecExp.battle.recon() >= 1>>
 			A massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.
 			Fortunately you knew of their coming, thanks to your recon systems.
@@ -83,7 +83,7 @@
 			Some of your citizens saw the massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.
 		<</if>>
 		<br>Freedom Fighters are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly, they fight guerrilla style, slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type === "old world">>
 		<<if App.SecExp.battle.recon() >= 1>>
 			A massive, disciplined old world army is approaching the confines of your arcology. It seems one of the nations of the old world is determined to put your arcology to rest once and for all or die trying.
 			Fortunately you knew of their coming, thanks to your recon systems.
@@ -94,27 +94,31 @@
 	<</if>>
 <</if>>
 <br><br>__Recon__:
-<<set _estimatedMen = normalRandInt($attackTroops, $attackTroops * (4 - App.SecExp.battle.recon()) * 0.05)>>
-<<set _expectedEquip = normalRandInt($attackEquip, (4 - App.SecExp.battle.recon()) * 0.25)>>
+<<set _estimatedMen = $SecExp.war.estimatedMen>>
+<<set _expectedEquip = $SecExp.war.expectedEquip>>
 <br>It seems your troops and your adversary will fight
-<<if $battleTerrain == "rural">>
+<<if $SecExp.war.terrain === "rural">>
 	in <strong>the rural land</strong> surrounding the free city.
-<<elseif $battleTerrain == "urban">>
+<<elseif $SecExp.war.terrain === "urban">>
 	in the old <strong>abandoned city</strong> surrounding the free city.
-<<elseif $battleTerrain == "hills">>
+<<elseif $SecExp.war.terrain === "hills">>
 	on <strong>the hills</strong> around the free city.
-<<elseif $battleTerrain == "coast">>
+<<elseif $SecExp.war.terrain === "coast">>
 	along <strong>the coast</strong> just outside the free city.
-<<elseif $battleTerrain == "outskirts">>
+<<elseif $SecExp.war.terrain === "outskirts">>
 	right against <strong>the walls of the arcology.</strong>
-<<elseif $battleTerrain == "mountains">>
+<<elseif $SecExp.war.terrain === "mountains">>
 	in <strong>the mountains</strong> overlooking the arcology.
-<<elseif $battleTerrain == "wasteland">>
+<<elseif $SecExp.war.terrain === "wasteland">>
 	in <strong>the wastelands</strong> outside the free city territory.
-<<elseif $battleTerrain == "error">>
-	<br>@@.red;Error: failed to assign terrain.@@ battleTerrain reads "<<print $battleTerrain>>".
+<<elseif $SecExp.war.terrain == "international waters">>
+	in <strong>the water surrounding</strong> the free city.
+<<elseif ["an underwater cave", "a sunken ship", "an island"].includes($SecExp.war.terrain)>>
+	in <strong>$SecExp.war.terrain</strong> near the free city.
+<<elseif $SecExp.war.terrain === "error">>
+	<br>@@.red;Error: failed to assign terrain.@@ SecExp.war.terrain reads: $SecExp.war.terrain.
 <<else>>
-	<br>@@.red;Error: failed to read terrain.@@ battleTerrain reads "<<print $battleTerrain>>".
+	<br>@@.red;Error: failed to read terrain.@@ SecExp.war.terrain reads: $SecExp.war.terrain.
 <</if>>
 <<if App.SecExp.battle.recon() == 3>>
 	Your recon capabilities are top notch. The information collected will be most likely correct or very close to be so:
@@ -125,7 +129,7 @@
 <<else>>
 	Your recon capabilities are almost non-existent. The information collected will be wild guesses at best:
 <</if>>
-approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem to be
+approximately <strong>_estimatedMen men</strong> are coming, they seem to be
 <<if _expectedEquip <= 0>>
 	<strong>poorly armed</strong>. Old rusty small arms are the norm with just a few barely working civilian vehicles.
 <<elseif _expectedEquip == 1>>
@@ -140,11 +144,11 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 
 <hr>__Battle Plan__:<br>
 <<set _leaderFound = 1>>
-<<if $leadingTroops === "bodyguard" && $BodyguardID === 0 || $leadingTroops === "headGirl" && $HeadGirlID === 0>>
-	@@.warning;Chosen leader $leadingTroops cannot be found, please select another.@@
+<<if $SecExp.war.commander === "bodyguard" && $BodyguardID === 0 || $SecExp.war.commander === "headGirl" && $HeadGirlID === 0>>
+	@@.warning;Chosen leader $SecExp.war.commander cannot be found, please select another.@@
 	<<set _leaderFound = 0>>
 <</if>>
-<<switch $leadingTroops>>
+<<switch $SecExp.war.commander>>
 <<case "PC">>
 	<<set _leader = "Personally">>
 <<case "assistant">>
@@ -162,127 +166,63 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 <</switch>>
 
 /* leader assignment */
-<span id="leader"><strong><<print _leader>></strong></span> lead your troops.
+<strong>_leader</strong> lead your troops.
 <br>&nbsp;&nbsp;&nbsp;&nbsp;
-<<link "Personally join the battle">>
-	<<set $leadingTroops = "PC">>
-	<<set _leader = "Personally">>
-	<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-<</link>>
-|
-<<link "Let $assistant.name lead the troops">>
-	<<set $leadingTroops = "assistant">>
-	<<set _leader = "$assistant.name will">>
-	<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-<</link>>
-<<if $BodyguardID != 0 && $SecExp.edicts.defense.slavesOfficers == 1>>
-	|
-	<<link "Let your bodyguard lead your troops">>
-		<<set $leadingTroops = "bodyguard">>
-		<<set _leader = "_S.Bodyguard.slaveName will">>
-		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-	<</link>>
+[[Personally join the battle|attackOptions][$SecExp.war.commander = "PC"]]
+|<<link "Let $assistant.name lead your troops""attackOptions">> <<set $SecExp.war.commander = "assistant">> <</link>>
+<<if $BodyguardID !== 0 && $SecExp.edicts.defense.slavesOfficers === 1>>
+	|[[Let your bodyguard lead your troops|attackOptions][$SecExp.war.commander = "bodyguard"]]
 <</if>>
-<<if $HeadGirlID != 0 && $SecExp.edicts.defense.slavesOfficers == 1>>
-	|
-	<<link "Let your Head Girl lead your troops">>
-		<<set $leadingTroops = "headGirl">>
-		<<set _leader = "_S.HeadGirl.slaveName will">>
-		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-	<</link>>
+<<if $HeadGirlID !== 0 && $SecExp.edicts.defense.slavesOfficers === 1>>
+	|[[Let your Head Girl lead your troops|attackOptions][$SecExp.war.commander = "headGirl"]]
 <</if>>
 <<if $SecExp.edicts.defense.militia >= 1>>
-	|
-	<<link "Let the citizens' militia officers lead the troops">>
-		<<set $leadingTroops = "citizen">>
-		<<set _leader = "The citizens' militia commander will">>
-		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-	<</link>>
+	|[[Let the citizens' militia officers lead the troops|attackOptions][$SecExp.war.commander = "citizen"]]
 <</if>>
 <<if $mercenaries > 0>>
-	|
-	<<link "Let the mercenary officers lead the troops">>
-		<<set $leadingTroops = "mercenary">>
-		<<set _leader = "The mercenary commander will">>
-		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-	<</link>>
+	|[[Let the mercenary officers lead the troops|attackOptions][$SecExp.war.commander = "mercenary"]]
 <</if>>
 <<if $SF.Toggle && $SF.Active >= 1 && $SF.MercCon.CanAttend === -2>>
-	|
-	<<link "Let The Colonel lead the troops">>
-		<<set $leadingTroops = "colonel">>
-		<<set _leader = capFirstChar($SF.Lower || "the special force") +"'s Colonel will">>
-		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
-	<</link>>
+	|[[Let The Colonel lead the troops|attackOptions][$SecExp.war.commander = "colonel"]]
 <</if>>
-<br>For this battle you choose to follow <span id="tactic"><strong><<print $chosenTactic>></strong></span> tactics.<br>
+<br>For this battle you choose to follow <strong>$SecExp.war.chosenTactic</strong> tactics.<br>
 <button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'defensiveTactics')" id="tab defensive tactics">Defensive tactics</button>
 <button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'offensiveTactics')" id="tab offensiveTactics">Offensive Tactics</button>
 
 <div id="defensiveTactics" class="tab-content">
 	<div class="content">
-		<<link "Bait and Bleed">>
-			<<set $chosenTactic = "Bait and Bleed">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		[[Bait and Bleed|attackOptions][$SecExp.war.chosenTactic = "Bait and Bleed"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Combines bait and switch tactics with guerrilla style assaults, with the objective of slowly bleed the enemy.//
-		<br>
-		<<link "Guerrilla">>
-			<<set $chosenTactic = "Guerrilla">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Guerrilla|attackOptions][$SecExp.war.chosenTactic = "Guerrilla"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves using terrain knowledge and small fast attacks to hinder and weaken the enemy.//
-		<br>
-		<<link "Choke Points">>
-			<<set $chosenTactic = "Choke Points">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Choke Points|attackOptions][$SecExp.war.chosenTactic = "Choke Points"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves using terrain knowledge and strong fortifications in order to stop the enemy on its track.//
-		<br>
-		<<link "Interior Lines">>
-			<<set $chosenTactic = "Interior Lines">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Interior Lines|attackOptions][$SecExp.war.chosenTactic = "Interior Lines"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves exploiting a defender's shorter logistics lines and redeployment times in order to keep the enemy pressured.//
-		<br>
-		<<link "Pincer Maneuver">>
-			<<set $chosenTactic = "Pincer Maneuver">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Pincer Maneuver|attackOptions][$SecExp.war.chosenTactic = "Pincer Maneuver"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves letting the enemy push back the center in order to envelop their formation.//
-		<br>
-		<<link "Defense In Depth">>
-			<<set $chosenTactic = "Defense In Depth">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Defense In Depth|attackOptions][$SecExp.war.chosenTactic = "Defense In Depth"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves letting the enemy gain terrain to gain tactical superiority by alternating between delaying actions and small counterattacks.//
 	</div>
 </div>
 
 <div id="offensiveTactics" class="tab-content">
 	<div class="content">
-		<<link "Blitzkrieg">>
-			<<set $chosenTactic = "Blitzkrieg">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		[[Blitzkrieg|attackOptions][$SecExp.war.chosenTactic = "Blitzkrieg"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves breaking the front of the enemy with a fast armored force concentrated into a small area.//
-		<br>
-		<<link "Human Wave">>
-			<<set $chosenTactic = "Human Wave">>
-			<<replace "#tactic">><strong><<print $chosenTactic>></strong><</replace>>
-		<</link>>
+		<br>[[Human Wave|attackOptions][$SecExp.war.chosenTactic = "Human Wave"]]
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Involves assaulting the enemy with large numbers of infantry to overwhelm their lines.//
 	</div>
 </div>
 
 <<if _leaderFound === 1>>
 	<<if App.SecExp.battle.deployedUnits() > 0>>
-		<br>[[Send your orders|attackHandler][$battleResult = 4, $foughtThisWeek = 1]] /* sets $battleResult value outside accepted range (-3, 3) to avoid evaluation problems */
+		<br>[[Send your orders|attackHandler][$SecExp.war.result = 4, $foughtThisWeek = 1]] /* sets $SecExp.war.result value outside accepted range (-3, 3) to avoid evaluation problems */
 	<<else>>
 		<br>You need at least a unit in your roster to proceed to battle.
 	<</if>>
-	<br>[[Surrender|attackReport][$battleResult = -1, $foughtThisWeek = 1]]
-	<br>[[Attempt to bribe|attackHandler][$battleResult = 1, $foughtThisWeek = 1]]
+	<br>[[Surrender|attackReport][$SecExp.war.result = -1, $foughtThisWeek = 1]]
+	<br>[[Attempt to bribe|attackHandler][$SecExp.war.result = 1, $foughtThisWeek = 1]]
 	 //Will cost around <<print cashFormat(Math.round(App.SecExp.battle.bribeCost() * (1 + either(-1,1) * random(2) * 0.1)))>> (estimate).//
 <<else>>
 	Your leader needs to be present to proceed.
@@ -290,7 +230,7 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 
 <<if $SF.Toggle && $SF.Active >= 1 && $majorBattle>> <br>
 	<<set _options = new App.UI.OptionsGroup()>>
-	<<run _options.addOption("The incoming attack's scale warrants deploying the special force.", "SFIntervention")
+	<<run _options.addOption("The incoming attack's scale warrants deploying the special force.", "SecExp.war.deploySF")
 	.addValue("Green light", 1).on().addValue("Red light", 0).off()
 	.addComment("Some upgrades will be able to support your troops even if the special force is not deployed in the fight.")>>
 	<<includeDOM _options.render()>>
@@ -303,54 +243,54 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 			<<if $SecExp.battles.lastSelection[_i] == -1>>
 				<<set $secBots.isDeployed = 1>>
 			<<else>>
-				<<for _j = 0; _j < $militiaUnits.length; _j++>>
-					<<if $SecExp.battles.lastSelection[_i] == $militiaUnits[_j].ID>>
-						<<set $militiaUnits[_j].isDeployed = 1>>
+				<<for _unit range $militiaUnits>>
+					<<if $SecExp.battles.lastSelection[_i] === _unit.ID>>
+						<<set _unit.isDeployed = 1>>
 						<<break>>
 					<</if>>
 				<</for>>
-				<<for _j = 0; _j < $slaveUnits.length; _j++>>
-					<<if $SecExp.battles.lastSelection[_i] == $slaveUnits[_j].ID>>
-						<<set $slaveUnits[_j].isDeployed = 1>>
+				<<for _unit range $slaveUnits>>
+					<<if $SecExp.battles.lastSelection[_i] === _unit.ID>>
+						<<set _unit.isDeployed = 1>>
 						<<break>>
 					<</if>>
 				<</for>>
-				<<for _j = 0; _j < $mercUnits.length; _j++>>
-					<<if $SecExp.battles.lastSelection[_i] == $mercUnits[_j].ID>>
-						<<set $mercUnits[_j].isDeployed = 1>>
+				<<for _unit range  $mercUnits>>
+					<<if $SecExp.battles.lastSelection[_i] === _unit.ID>>
+						<<set _unit.isDeployed = 1>>
 						<<break>>
 					<</if>>
 				<</for>>
 			<</if>>
 		<</for>>
-		<<set $saveValid = 1, $leadingTroops = $SecExp.battles.saved.commander, $SFIntervention = $SecExp.battles.saved.sfSupport>>
+		<<set $SecExp.war.saveValid = 1, $SecExp.war.commander = $SecExp.battles.saved.commander, $SecExp.war.deploySF = $SecExp.battles.saved.sfSupport>>
 	<</link>>
 <<else>>
 	Restore saved roster
 <</if>>
 |
-<<if $saveValid != 1>>
+<<if $SecExp.war.saveValid !== 1>>
 	<<link "Save current roster" "attackOptions">>
 		<<if App.SecExp.battle.deployedUnits('bots')>>
 			<<set _tmp = -1>>
 			<<set $SecExp.battles.lastSelection.push(_tmp)>>
 		<</if>>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].isDeployed == 1>>
-				<<set $SecExp.battles.lastSelection.push($militiaUnits[_i].ID)>>
+		<<for _unit range $militiaUnits>>
+			<<if _unit.isDeployed === 1>>
+				<<set $SecExp.battles.lastSelection.push(_unit.ID)>>
 			<</if>>
 		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].isDeployed == 1>>
-				<<set $SecExp.battles.lastSelection.push($slaveUnits[_i].ID)>>
+		<<for _unit range $slaveUnits>>
+			<<if _unit.isDeployed === 1>>
+				<<set $SecExp.battles.lastSelection.push(_unit.ID)>>
 			<</if>>
 		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].isDeployed == 1>>
-				<<set $SecExp.battles.lastSelection.push($mercUnits[_i].ID)>>
+		<<for _unit range  $mercUnits>>
+			<<if _unit.isDeployed === 1>>
+				<<set $SecExp.battles.lastSelection.push(_unit.ID)>>
 			<</if>>
 		<</for>>
-		<<set $saveValid = 1, $SecExp.battles.saved.commander = $leadingTroops, $SecExp.battles.saved.sfSupport = $SFIntervention>>
+		<<set $SecExp.war.saveValid = 1, $SecExp.battles.saved.commander = $SecExp.war.commander, $SecExp.battles.saved.sfSupport = $SecExp.war.deploySF>>
 	<</link>>
 <<else>>
 	Save current roster
@@ -359,23 +299,23 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 <<if App.SecExp.battle.deployedUnits() > 0>>
 	<<link "Clear current roster" "attackOptions">>
 		<<set $secBots.isDeployed = 0>>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<set $militiaUnits[_i].isDeployed = 0>>
+		<<for _unit range $militiaUnits>>
+			<<set _unit.isDeployed = 0>>
 		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<set $slaveUnits[_i].isDeployed = 0>>
+		<<for _unit range $slaveUnits>>
+			<<set _unit.isDeployed = 0>>
 		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<set $mercUnits[_i].isDeployed = 0>>
+		<<for _unit range $mercUnits>>
+			<<set _unit.isDeployed = 0>>
 		<</for>>
-		<<set $saveValid = 0>>
+		<<set $SecExp.war.saveValid = 0>>
 	<</link>>
 <<else>>
 	Clear current roster
 <</if>>
 |
 <<if $SecExp.battles.lastSelection.length > 0>>
-	[[Clear saved roster|attackOptions][$SecExp.battles.lastSelection = [], $saveValid = 0]]
+	[[Clear saved roster|attackOptions][$SecExp.battles.lastSelection = [], $SecExp.war.saveValid = 0]]
 <<else>>
 	Clear saved roster
 <</if>>
@@ -393,14 +333,14 @@ approximately <strong><<print _estimatedMen>> men</strong> are coming, they seem
 
 <<if App.SecExp.battle.deployableUnits() === 0>> <strong>Unit roster full.</strong> <</if>>
 <br> <<includeDOM App.SecExp.deployUnitMenu($secBots, "bots")>>
-<<if $militiaUnits.length > 0>>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'militia')" id="tab militia">Militia: ($militiaUnits.length)</button>
+<<if _mL > 0>>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'militia')" id="tab militia">Militia: (_mL)</button>
 <</if>>
-<<if $slaveUnits.length > 0>>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'slaves')" id="tab slaves">Slaves: ($slaveUnits.length)</button>
+<<if _sL > 0>>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'slaves')" id="tab slaves">Slaves: (_sL)</button>
 <</if>>
-<<if $mercUnits.length > 0>>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'mercs')" id="tab mercs">Mercs: ($mercUnits.length)</button>
+<<if _meL > 0>>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'mercs')" id="tab mercs">Mercs: (_meL)</button>
 <</if>>
 
 <div id="militia" class="tab-content">
diff --git a/src/Mods/SecExp/attackReport.tw b/src/Mods/SecExp/attackReport.tw
index 2ab7bd40ede5f84a06b6c05a60b2339569942ed1..066f182b2c11dba6cdce401a346cf6e0daae7ba8 100644
--- a/src/Mods/SecExp/attackReport.tw
+++ b/src/Mods/SecExp/attackReport.tw
@@ -5,12 +5,12 @@
 /*init*/
 <<set _oldRep = $rep>>
 <<set _oldAuth = $SecExp.core.authority>>
-<<set $enemyLosses = Math.trunc($enemyLosses)>>
-<<if $enemyLosses > $attackTroops>>
-	<<set $enemyLosses = $attackTroops>>
+<<set $SecExp.war.attacker.losses = Math.trunc($SecExp.war.attacker.losses)>>
+<<if $SecExp.war.attacker.losses > $SecExp.war.attacker.troops>>
+	<<set $SecExp.war.attacker.losses = $SecExp.war.attacker.troops>>
 <</if>>
-<<set $SecExp.core.totalKills += $enemyLosses>>
-<<set $losses = Math.trunc($losses)>>
+<<set $SecExp.core.totalKills += $SecExp.war.attacker.losses>>
+<<set $SecExp.war.losses = Math.trunc($SecExp.war.losses)>>
 <<set _loot = 0>>
 
 /* result */
@@ -20,63 +20,63 @@
 	<<set _majorBattleMod = 2>>
 	<<set $SecExp.battles.major++>>
 <</if>>
-<<if $battleResult == 3>>
+<<if $SecExp.war.result == 3>>
 	<strong>Victory!</strong>
 	<<set $SecExp.battles.lossStreak = 0>>
 	<<set $SecExp.battles.victoryStreak += 1>>
 	<<set $SecExp.battles.victories++>>
-<<elseif $battleResult == -3>>
+<<elseif $SecExp.war.result == -3>>
 	<strong>Defeat!</strong>
 	<<set $SecExp.battles.lossStreak += 1>>
 	<<set $SecExp.battles.victoryStreak = 0>>
 	<<set $SecExp.battles.losses++>>
-<<elseif $battleResult == 2>>
+<<elseif $SecExp.war.result == 2>>
 	<strong>Partial victory!</strong>
 	<<set $SecExp.battles.victories++>>
-<<elseif $battleResult == -2>>
+<<elseif $SecExp.war.result == -2>>
 	<strong>Partial defeat!</strong>
 	<<set $SecExp.battles.losses++>>
-<<elseif $battleResult == -1>>
+<<elseif $SecExp.war.result == -1>>
 	<strong>We surrendered</strong>
 	<<set $SecExp.battles.losses++>>
-<<elseif $battleResult == 0>>
+<<elseif $SecExp.war.result == 0>>
 	<strong>Failed bribery!</strong>
 	<<set $SecExp.battles.losses++>>
-<<elseif $battleResult == 1>>
+<<elseif $SecExp.war.result == 1>>
 	<strong>Successful bribery!</strong>
 	<<set $SecExp.battles.victories++>>
 <</if>>
 <hr>
 
-<<if $attackType == "raiders">>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a band of wild raiders, <<print num(Math.trunc($attackTroops))>> men strong.
-	<<if $battleResult != 1 && $battleResult != 0 && $battleResult != -1>>
+<<if $SecExp.war.attacker.type == "raiders">>
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a band of wild raiders, <<print num(Math.trunc($SecExp.war.attacker.troops))>> men strong.
+	<<if $SecExp.war.result != 1 && $SecExp.war.result != 0 && $SecExp.war.result != -1>>
 		Our defense forces, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, clashed with them
-		<<if $battleTerrain == "urban">>
-			in the streets of <<if $terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
-			<<elseif $battleTerrain == "rural">>
+		<<if $SecExp.war.terrain == "urban">>
+			in the streets of <<if $SecExp.war.terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
+			<<elseif $SecExp.war.terrain == "rural">>
 			in the rural land surrounding the free city,
-		<<elseif $battleTerrain == "hills">>
+		<<elseif $SecExp.war.terrain == "hills">>
 			on the hills around the free city,
-		<<elseif $battleTerrain == "coast">>
+		<<elseif $SecExp.war.terrain == "coast">>
 			along the coast just outside the free city,
-			<<elseif $battleTerrain == "outskirts">>
+			<<elseif $SecExp.war.terrain == "outskirts">>
 			just against the walls of the arcology,
-		<<elseif $battleTerrain == "mountains">>
+		<<elseif $SecExp.war.terrain == "mountains">>
 			in the mountains overlooking the arcology,
-		<<elseif $battleTerrain == "wasteland">>
+		<<elseif $SecExp.war.terrain == "wasteland">>
 			in the wastelands outside the free city territory,
 		<</if>>
-		<<if $enemyLosses != $attackTroops>>
-			inflicting <<print num(Math.trunc($enemyLosses))>> casualties, while sustaining <<if $losses > 1>><<print num(Math.trunc($losses))>> casualties<<elseif $losses > 0>>a casualty<<else>>zero<</if>> themselves.
+		<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+			inflicting <<print num(Math.trunc($SecExp.war.attacker.losses))>> casualties, while sustaining <<if $SecExp.war.losses > 1>><<print num(Math.trunc($SecExp.war.losses))>> casualties<<elseif $SecExp.war.losses > 0>>a casualty<<else>>zero<</if>> themselves.
 		<<else>>
-			completely annihilating their troops, while sustaining <<if $losses > 1>><<print num(Math.trunc($losses))>> casualties<<elseif $losses > 0>>a casualty<<else>>zero casualties<</if>>.
+			completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>><<print num(Math.trunc($SecExp.war.losses))>> casualties<<elseif $SecExp.war.losses > 0>>a casualty<<else>>zero casualties<</if>>.
 		<</if>>
 	<</if>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men easily stopped the disorganized horde's futile attempt at raiding your arcology<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the disorganized horde attempt at raiding your arcology<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but our men managed to stop the horde raiding party<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -86,10 +86,10 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			finally putting an end to a series of unfortunate defeats.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men were easily crushed by the barbaric horde of raiders<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the bandits proved too much to handle for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but despite their bravery the horde proved too much for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -99,46 +99,46 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			confirming the long list of recent failures our armed forces collected.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		The fight was long and hard, but in the end our men managed to repel the raiders, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. Our men in the end had to yield to the horde raiders, which was fortunately unable to capitalized on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender, obediently they stand down.
-	<<elseif $battleResult == 0>>
+	<<elseif $SecExp.war.result == 0>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
-	<<elseif $battleResult == 1>>
+	<<elseif $SecExp.war.result == 1>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
 	<</if>>
-<<elseif $attackType == "free city">>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a contingent of mercenaries hired by a competing free city, <<print num(Math.trunc($attackTroops))>> men strong.
-	<<if $battleResult != 1 && $battleResult != 0 && $battleResult != -1>>
+<<elseif $SecExp.war.attacker.type == "free city">>
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a contingent of mercenaries hired by a competing free city, <<print num(Math.trunc($SecExp.war.attacker.troops))>> men strong.
+	<<if $SecExp.war.result != 1 && $SecExp.war.result != 0 && $SecExp.war.result != -1>>
 		Our defense forces, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, clashed with them
-		<<if $battleTerrain == "urban">>
-			in the streets of <<if $terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
-		<<elseif $battleTerrain == "rural">>
+		<<if $SecExp.war.terrain == "urban">>
+			in the streets of <<if $SecExp.war.terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
+		<<elseif $SecExp.war.terrain == "rural">>
 			in the rural land surrounding the free city,
-		<<elseif $battleTerrain == "hills">>
+		<<elseif $SecExp.war.terrain == "hills">>
 			on the hills around the free city,
-		<<elseif $battleTerrain == "coast">>
+		<<elseif $SecExp.war.terrain == "coast">>
 			along the coast just outside the free city,
-		<<elseif $battleTerrain == "outskirts">>
+		<<elseif $SecExp.war.terrain == "outskirts">>
 			just against the walls of the arcology,
-		<<elseif $battleTerrain == "mountains">>
+		<<elseif $SecExp.war.terrain == "mountains">>
 			in the mountains overlooking the arcology,
-		<<elseif $battleTerrain == "wasteland">>
+		<<elseif $SecExp.war.terrain == "wasteland">>
 			in the wastelands outside the free city territory,
 		<</if>>
-		<<if $enemyLosses != $attackTroops>>
-			inflicting <<print $enemyLosses>> casualties, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties <<elseif $losses > 0>> a casualty <<else>> zero <</if>> themselves.
+		<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+			inflicting <<print $SecExp.war.attacker.losses>> casualties, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties <<elseif $SecExp.war.losses > 0>> a casualty <<else>> zero <</if>> themselves.
 		<<else>>
-			completely annihilating their troops, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties. <<elseif $losses > 0>> a casualty.<<else>> zero casualties.<</if>>
+			completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties. <<elseif $SecExp.war.losses > 0>> a casualty.<<else>> zero casualties.<</if>>
 		<</if>>
 	<</if>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men easily stopped the mercenaries dead in their tracks<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the slavers attempt at weakening your arcology<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but our men managed to stop the free city mercenaries<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -148,10 +148,10 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			finally putting an end to a series of unfortunate defeats.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men were easily crushed by the consumed mercenary veterans sent against us<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the slavers proved too much to handle for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but despite their bravery the mercenary slavers proved too much for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -161,46 +161,46 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			confirming the long list of recent failures our armed forces collected.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		The fight was long and hard, but in the end our men managed to repel the mercenaries, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. Our men in the end had to yield to the slavers, which were fortunately unable to capitalized on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender, obediently they stand down.
-	<<elseif $battleResult == 0>>
+	<<elseif $SecExp.war.result == 0>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
-	<<elseif $battleResult == 1>>
+	<<elseif $SecExp.war.result == 1>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
 	<</if>>
-<<elseif $attackType == "freedom fighters">>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a group of freedom fighters bent on the destruction of the institution of slavery, <<print num(Math.trunc($attackTroops))>> men strong.
-	<<if $battleResult != 1 && $battleResult != 0 && $battleResult != -1>>
+<<elseif $SecExp.war.attacker.type == "freedom fighters">>
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by a group of freedom fighters bent on the destruction of the institution of slavery, <<print num(Math.trunc($SecExp.war.attacker.troops))>> men strong.
+	<<if $SecExp.war.result != 1 && $SecExp.war.result != 0 && $SecExp.war.result != -1>>
 		Our defense forces, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, clashed with them
-		<<if $battleTerrain == "urban">>
-			in the streets of <<if $terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
-		<<elseif $battleTerrain == "rural">>
+		<<if $SecExp.war.terrain == "urban">>
+			in the streets of <<if $SecExp.war.terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
+		<<elseif $SecExp.war.terrain == "rural">>
 			in the rural land surrounding the free city,
-		<<elseif $battleTerrain == "hills">>
+		<<elseif $SecExp.war.terrain == "hills">>
 			on the hills around the free city,
-		<<elseif $battleTerrain == "coast">>
+		<<elseif $SecExp.war.terrain == "coast">>
 			along the coast just outside the free city,
-		<<elseif $battleTerrain == "outskirts">>
+		<<elseif $SecExp.war.terrain == "outskirts">>
 			just against the walls of the arcology,
-		<<elseif $battleTerrain == "mountains">>
+		<<elseif $SecExp.war.terrain == "mountains">>
 			in the mountains overlooking the arcology,
-		<<elseif $battleTerrain == "wasteland">>
+		<<elseif $SecExp.war.terrain == "wasteland">>
 			in the wastelands outside the free city territory,
 		<</if>>
-		<<if $enemyLosses != $attackTroops>>
-			inflicting <<print $enemyLosses>> casualties, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties <<elseif $losses > 0>> a casualty <<else>> zero <</if>> themselves.
+		<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+			inflicting <<print $SecExp.war.attacker.losses>> casualties, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties <<elseif $SecExp.war.losses > 0>> a casualty <<else>> zero <</if>> themselves.
 		<<else>>
-			completely annihilating their troops, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties. <<elseif $losses > 0>> a casualty. <<else>> zero casualties.<</if>>
+			completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties. <<elseif $SecExp.war.losses > 0>> a casualty. <<else>> zero casualties.<</if>>
 		<</if>>
 	<</if>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men easily stopped the freedom fighters dead in their tracks<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the fighters attack<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but our men managed to stop the freedom fighters<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -210,10 +210,10 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			finally putting an end to a series of unfortunate defeats.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men were easily crushed by the fanatical fury of the freedom fighters<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the freedom fighters proved too much to handle for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but despite their bravery the freedom fighters fury proved too much for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -223,46 +223,46 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			confirming the long list of recent failures our armed forces collected.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 			The fight was long and hard, but in the end our men managed to repel the freedom fighters, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. Our men in the end had to yield to the freedom fighters, which were fortunately unable to capitalized on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender, obediently they stand down.
-	<<elseif $battleResult == 0>>
+	<<elseif $SecExp.war.result == 0>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
-	<<elseif $battleResult == 1>>
+	<<elseif $SecExp.war.result == 1>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
 	<</if>>
-<<elseif $attackType == "old world">>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by an old world nation boasting a misplaced sense of superiority, <<print num(Math.trunc($attackTroops))>> men strong.
-	<<if $battleResult != 1 && $battleResult != 0 && $battleResult != -1>>
+<<elseif $SecExp.war.attacker.type == "old world">>
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was attacked by an old world nation boasting a misplaced sense of superiority, <<print num(Math.trunc($SecExp.war.attacker.troops))>> men strong.
+	<<if $SecExp.war.result != 1 && $SecExp.war.result != 0 && $SecExp.war.result != -1>>
 		Our defense forces, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, clashed with them
-		<<if $battleTerrain == "urban">>
-			in the streets of <<if $terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
-		<<elseif $battleTerrain == "rural">>
+		<<if $SecExp.war.terrain == "urban">>
+			in the streets of <<if $SecExp.war.terrain == "urban">>the old world city surrounding the arcology<<else>>of the free city<</if>>,
+		<<elseif $SecExp.war.terrain == "rural">>
 			in the rural land surrounding the free city,
-		<<elseif $battleTerrain == "hills">>
+		<<elseif $SecExp.war.terrain == "hills">>
 			on the hills around the free city,
-		<<elseif $battleTerrain == "coast">>
+		<<elseif $SecExp.war.terrain == "coast">>
 			along the coast just outside the free city,
-		<<elseif $battleTerrain == "outskirts">>
+		<<elseif $SecExp.war.terrain == "outskirts">>
 			just against the walls of the arcology,
-		<<elseif $battleTerrain == "mountains">>
+		<<elseif $SecExp.war.terrain == "mountains">>
 			in the mountains overlooking the arcology,
-		<<elseif $battleTerrain == "wasteland">>
+		<<elseif $SecExp.war.terrain == "wasteland">>
 			in the wastelands outside the free city territory,
 		<</if>>
-		<<if $enemyLosses != $attackTroops>>
-			inflicting <<print $enemyLosses>> casualties, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties <<elseif $losses > 0>> a casualty <<else>> zero <</if>> themselves.
+		<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+			inflicting <<print $SecExp.war.attacker.losses>> casualties, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties <<elseif $SecExp.war.losses > 0>> a casualty <<else>> zero <</if>> themselves.
 		<<else>>
-			completely annihilating their troops, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties. <<elseif $losses > 0>> a casualty. <<else>> zero casualties.<</if>>
+			completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties. <<elseif $SecExp.war.losses > 0>> a casualty. <<else>> zero casualties.<</if>>
 		<</if>>
 	<</if>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men easily stopped the old world soldiers dead in their tracks<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the soldiers of the old world<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but our men managed to stop the old world soldiers<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -272,10 +272,10 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			finally putting an end to a series of unfortunate defeats.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men were easily crushed by the discipline of the old world armies<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the old world proved too much to handle for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
 		<<else>>
 			The fight was long and hard, but despite their bravery the determination of the old world troops proved too much for our men<<if $SecExp.battles.victoryStreak >= 2 || $SecExp.battles.lossStreak >= 2>>,<<else>>.<</if>>
@@ -285,65 +285,65 @@
 		<<elseif $SecExp.battles.lossStreak >= 2>>
 			confirming the long list of recent failures our armed forces collected.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		The fight was long and hard, but in the end our men managed to repel the old world soldiers, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. Our men in the end had to yield to the old world soldiers, which were fortunately unable to capitalized on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender, obediently they stand down.
-	<<elseif $battleResult == 0>>
+	<<elseif $SecExp.war.result == 0>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
-	<<elseif $battleResult == 1>>
+	<<elseif $SecExp.war.result == 1>>
 		You decided in favor of a financial approach rather than open hostilities. Your troops remain inside the arcology's walls.
 	<</if>>
 <</if>>
 <br><br>
 /* calculates effects on the city */
-<<if $battleResult == 3>>
+<<if $SecExp.war.result == 3>>
 	Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ increased. You were also able to capture
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(4000 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 800 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(6000 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 1200 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(7500 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 1500 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(6000 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 1200 * _majorBattleMod>>
 	<</if>>
-	<<if $attackTroops - $enemyLosses <= 50>>
+	<<if $SecExp.war.attacker.troops - $SecExp.war.attacker.losses <= 50>>
 		a small amount of attackers,
-		<<set _captives = ($attackTroops - $enemyLosses) * 0.1 * random(1,3)>>
-	<<elseif $attackTroops - $enemyLosses <= 100>>
+		<<set _captives = ($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.1 * random(1,3)>>
+	<<elseif $SecExp.war.attacker.troops - $SecExp.war.attacker.losses <= 100>>
 		an healthy group of attackers,
-		<<set _captives = ($attackTroops - $enemyLosses) * 0.1 * random(1,3)>>
-	<<elseif $attackTroops - $enemyLosses <= 150>>
+		<<set _captives = ($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.1 * random(1,3)>>
+	<<elseif $SecExp.war.attacker.troops - $SecExp.war.attacker.losses <= 150>>
 		a big group of attackers,
-		<<set _captives = ($attackTroops - $enemyLosses) * 0.1 * random(1,3)>>
-	<<elseif $attackTroops - $enemyLosses <= 200>>
+		<<set _captives = ($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.1 * random(1,3)>>
+	<<elseif $SecExp.war.attacker.troops - $SecExp.war.attacker.losses <= 200>>
 		a huge group of attackers,
-		<<set _captives = ($attackTroops - $enemyLosses) * 0.1 * random(1,3)>>
-	<<elseif $attackTroops - $enemyLosses > 200>>
+		<<set _captives = ($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.1 * random(1,3)>>
+	<<elseif $SecExp.war.attacker.troops - $SecExp.war.attacker.losses > 200>>
 		a great amount of attackers,
-		<<set _captives = ($attackTroops - $enemyLosses) * 0.1 * random(1,3)>>
+		<<set _captives = ($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.1 * random(1,3)>>
 	<</if>>
 	and some of their equipment, which once sold produced
-	<<if $attackEquip == 0>>
+	<<if $SecExp.war.attacker.equip == 0>>
 		@@.yellowgreen;a small amount of cash.@@
 		<<set _loot += 1000 * _majorBattleMod>>
-	<<elseif $attackEquip == 1>>
+	<<elseif $SecExp.war.attacker.equip == 1>>
 		@@.yellowgreen;a moderate amount of cash.@@
 		<<set _loot += 5000 * _majorBattleMod>>
-	<<elseif $attackEquip == 2>>
+	<<elseif $SecExp.war.attacker.equip == 2>>
 		@@.yellowgreen;a good amount of cash.@@
 		<<set _loot += 10000 * _majorBattleMod>>
-	<<elseif $attackEquip == 3>>
+	<<elseif $SecExp.war.attacker.equip == 3>>
 		@@.yellowgreen;a great amount of cash.@@
 		<<set _loot += 15000 * _majorBattleMod>>
-	<<elseif $attackEquip == 4>>
+	<<elseif $SecExp.war.attacker.equip == 4>>
 		@@.yellowgreen;wealth worthy of the mightiest warlord.@@
 		<<set _loot += 20000 * _majorBattleMod>>
 	<</if>>
@@ -361,18 +361,18 @@
 		It seems your victories over the constant threats directed your way is having @@.green;a positive effect on the prosperity of the arcology,@@ due to the security your leadership affords.
 		<<set $arcologies[0].prosperity += 5 * _majorBattleMod>>
 	<</if>>
-<<elseif $battleResult == -3>>
+<<elseif $SecExp.war.result == -3>>
 	Due to your defeat, your @@.red;reputation@@ and @@.red;authority@@ decreased. Obviously your troops were not able to capture anyone or anything.
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(forceNeg(400 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 400 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(forceNeg(600 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 600 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(forceNeg(750 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 750 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(forceNeg(600 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 600 * _majorBattleMod>>
 	<</if>>
@@ -425,35 +425,35 @@
 		This only confirms the fears of many, @@.red;your arcology is not safe@@ and it is clear their business will be better somewhere else.
 		<<set $arcologies[0].prosperity -= 5 * _majorBattleMod>>
 	<</if>>
-<<elseif $battleResult == 2>>
+<<elseif $SecExp.war.result == 2>>
 	Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ slightly increased. Our men were not able to capture any combatants, however some equipment was seized during the enemy's hasty retreat,
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(1000 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 200 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(1500 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 300 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(2000 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 450 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(1500 * _majorBattleMod, "war")>>
 		<<set $SecExp.core.authority += 300 * _majorBattleMod>>
 	<</if>>
 	which once sold produced
-	<<if $attackEquip == 0>>
+	<<if $SecExp.war.attacker.equip == 0>>
 		@@.yellowgreen;a bit of cash.@@
 		<<set _loot += 500 * _majorBattleMod>>
-	<<elseif $attackEquip == 1>>
+	<<elseif $SecExp.war.attacker.equip == 1>>
 		@@.yellowgreen;a small amount of cash.@@
 		<<set _loot += 2500 * _majorBattleMod>>
-	<<elseif $attackEquip == 2>>
+	<<elseif $SecExp.war.attacker.equip == 2>>
 		@@.yellowgreen;a moderate amount of cash.@@
 		<<set _loot += 5000 * _majorBattleMod>>
-	<<elseif $attackEquip == 3>>
+	<<elseif $SecExp.war.attacker.equip == 3>>
 		@@.yellowgreen;a good amount of cash.@@
 		<<set _loot += 7500 * _majorBattleMod>>
-	<<elseif $attackEquip == 4>>
+	<<elseif $SecExp.war.attacker.equip == 4>>
 		@@.yellowgreen;a great amount of cash.@@
 		<<set _loot += 10000 * _majorBattleMod>>
 	<</if>>
@@ -472,18 +472,18 @@
 	$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 	$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
 	$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
-<<elseif $battleResult == -2>>
+<<elseif $SecExp.war.result == -2>>
 	It was a close defeat, but nonetheless your @@.red;reputation@@ and @@.red;authority@@ slightly decreased. Your troops were not able to capture anyone or anything.
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(forceNeg(40 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 40 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(forceNeg(60 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 60 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(forceNeg(75 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 75 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(forceNeg(60 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 60 * _majorBattleMod>>
 	<</if>>
@@ -533,18 +533,18 @@
 		$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 		<<set $arcologies[0].prosperity -= random(12) * _majorBattleMod>>
 	<</if>>
-<<elseif $battleResult == -1>>
+<<elseif $SecExp.war.result == -1>>
 	Rather than waste the lives of your men you decided to surrender, hoping your enemy will cause less damage if you indulge them, this is however a big hit to your status. Your @@.red;reputation@@ and @@.red;authority@@ are significantly impacted.
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(forceNeg(600 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 600 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(forceNeg(800 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 800 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(forceNeg(1000 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 1000 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(forceNeg(800 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 800 * _majorBattleMod>>
 	<</if>>
@@ -593,19 +593,19 @@
 		$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 		<<set $arcologies[0].prosperity -= random(25) * _majorBattleMod>>
 	<</if>>
-<<elseif $battleResult == 0>>
-	Unfortunately your adversary did not accept your money. <<if $attackType == "freedom fighters">> Their ideological crusade would not allow such thing<<else>>They saw your attempt as nothing more than admission of weakness<</if>>. There was no time to organize a defense and so the enemy walked into the arcology as it was his.
+<<elseif $SecExp.war.result == 0>>
+	Unfortunately your adversary did not accept your money. <<if $SecExp.war.attacker.type == "freedom fighters">> Their ideological crusade would not allow such thing<<else>>They saw your attempt as nothing more than admission of weakness<</if>>. There was no time to organize a defense and so the enemy walked into the arcology as it was his.
 	Your reputation and authority suffer a hit.
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(forceNeg(400 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 400 * _majorBattleMod>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(forceNeg(600 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 600 * _majorBattleMod>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(forceNeg(750 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 750 * _majorBattleMod>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(forceNeg(600 * _majorBattleMod), "war")>>
 		<<set $SecExp.core.authority -= 600 * _majorBattleMod>>
 	<</if>>
@@ -655,16 +655,16 @@
 		<<set $arcologies[0].prosperity -= random(25) * _majorBattleMod>>
 	<</if>>
 	<br>
-<<elseif $battleResult == 1>>
+<<elseif $SecExp.war.result == 1>>
 	The attackers wisely take the money offered them to leave your territory without further issues. The strength of the Free Cities was never in their guns but in their dollars, and today's events are the perfect demonstration of such strength.
 	Your @@.green;reputation slightly increases.@@
-	<<if $attackType == "raiders">>
+	<<if $SecExp.war.attacker.type == "raiders">>
 		<<run repX(500 * _majorBattleMod, "war")>>
-	<<elseif $attackType == "free city">>
+	<<elseif $SecExp.war.attacker.type == "free city">>
 		<<run repX(750 * _majorBattleMod, "war")>>
-	<<elseif $attackType == "freedom fighters">>
+	<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 		<<run repX(1000 * _majorBattleMod, "war")>>
-	<<elseif $attackType == "old world">>
+	<<elseif $SecExp.war.attacker.type == "old world">>
 		<<run repX(750 * _majorBattleMod, "war")>>
 	<</if>>
 	<<run cashX(forceNeg(App.SecExp.battle.bribeCost()), "war")>>
@@ -689,10 +689,10 @@
 <</if>>
 
 <br><br>
-<<if $battleResult != 1 && $battleResult != 0 && $battleResult != -1>>
+<<if $SecExp.war.result != 1 && $SecExp.war.result != 0 && $SecExp.war.result != -1>>
 
 	/* leaders */
-	<<if $leadingTroops == "PC">>
+	<<if $SecExp.war.commander == "PC">>
 		<<setPlayerPronouns>>
 		You decided to personally lead the defense of your arcology.
 		<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
@@ -802,7 +802,7 @@
 				Your reputation is so high your name carries power by itself. Having you on the battlefield puts fear even in the hardiest of warriors.
 			<</if>>
 		<</if>>
-		<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 			<<if $PC.career == "mercenary" || $PC.career == "slaver" || $PC.career == "capitalist" || $PC.career == "gang" || $PC.skill.warfare > 75>>
 				The soldiers of $SF.Lower are ready and willing to follow you into battle, confident in your past experience.
 			<<elseif $PC.career == "wealth" || $PC.career == "medicine" || $PC.career == "engineer">>
@@ -824,13 +824,13 @@
 		<<elseif $PC.skill.warfare >= 75>>
 			Your great experience in military matters has a major positive impact on your troops performance and the effectiveness of your battle plan.
 		<</if>>
-		<<if $gainedWarfare == 1>>
+		<<if $SecExp.war.gainedWarfare>>
 			Battlefield experience increased your understanding of warfare, making you a better commander.
 		<</if>>
 		<<if $PC.health.shortDamage >= 60>>
 			During the fighting @@.red;you were wounded.@@ Your medics assure you it's nothing life threatening, but you'll be weakened for a few weeks.
 		<</if>>
-	<<elseif $leadingTroops == "assistant">>
+	<<elseif $SecExp.war.commander == "assistant">>
 		<<setAssistantPronouns>>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> let your personal assistant lead the troops.
 		<<if App.SecExp.battle.deployedUnits('mercs') >= 1 || App.SecExp.battle.deployedUnits('militia') >= 1 || App.SecExp.battle.deployedUnits('slaves') >= 1>>
@@ -852,7 +852,7 @@
 		<<elseif $assistant.power >= 3>>
 			your assistant performs admirably. _HisA vast computing power allows _himA to be everywhere on the battlefield, greatly enhancing the efficiency of your troops and the effectiveness of your battle plan.
 		<</if>>
-	<<elseif $leadingTroops == "bodyguard">>
+	<<elseif $SecExp.war.commander == "bodyguard">>
 		<<setLocalPronouns _S.Bodyguard>>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> decided it will be your bodyguard that leads the troops.
 		<<if App.SecExp.battle.deployedUnits('slaves') >= 1>>
@@ -865,62 +865,62 @@
 		<<if _oldRep < 10000 && _oldAuth < 10000 || _S.Bodyguard.prestige < 1>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
-				<<if $SF.Toggle && $SF.Active >= 1 && App.SecExp.battle.deployedUnits('mercs') == 1 && $SFIntervention>>
+				<<if $SF.Toggle && $SF.Active >= 1 && App.SecExp.battle.deployedUnits('mercs') == 1 && $SecExp.war.deploySF>>
 					commander, and neither are your mercenaries or your soldiers.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					commander, and neither are your mercenaries.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your soldiers.
 				<<else>>
 					commander.
 				<</if>>
-			<<elseif $SF.Toggle && $SF.Active >= 1 && App.SecExp.battle.deployedUnits('mercs') == 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && App.SecExp.battle.deployedUnits('mercs') == 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<</if>>
 		<<elseif _S.Bodyguard.prestige >= 2>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your
-				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					volunteers, your mercenaries and your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					volunteers and your mercenaries are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					volunteers and your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<<else>>
 					volunteers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<</if>>
-			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 			<</if>>
 		<<else>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
-				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your mercenaries and soldiers, but they trust you enough not to question your decision.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					commander, and neither are your mercenaries, but they trust you enough not to question your decision.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your soldiers, but they trust you enough not to question your decision.
 				<<else>>
 					commander, but they trust you enough not to question your decision.
 				<</if>>
-			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however,<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however,<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however,<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
 			<</if>>
 		<</if>>
-		<<set _BGCareerGivesBonus = setup.bodyguardCareers.includes(_S.Bodyguard.career) || setup.HGCareers.includes(_S.Bodyguard.career) || setup.secExCombatPrestige.includes(_S.Bodyguard.prestigeDesc)>>
+		<<set _BGCareerGivesBonus = App.Data.Careers.Leader.bodyguard.includes(_S.Bodyguard.career) || App.Data.Careers.Leader.HG.includes(_S.Bodyguard.career) || setup.secExCombatPrestige.includes(_S.Bodyguard.prestigeDesc)>>
 		<<set _BGTotalIntelligence = _S.Bodyguard.intelligence+_S.Bodyguard.intelligenceImplant>>
 		<<if _BGCareerGivesBonus && _BGTotalIntelligence > 95>>
 			With $his experience and $his great intellect, $he is able to exploit the smallest of tactical advantages, making your troops very effective.
@@ -943,10 +943,10 @@
 		<<elseif _BGTotalIntelligence < -15>>
 			$He lacks the experience and the intelligence to be an effective commander, the performance of your troops suffers because of $his poor leadership.
 		<</if>>
-		<<if $gainedCombat == 1>>
+		<<if $SecExp.war.gainedCombat>>
 			During the battle, $he had to fight for $his life, giving $him experience in modern combat. $He is now proficient with modern firearms and hand to hand combat.
 		<</if>>
-		<<if $leaderWounded == 1>>
+		<<if $SecExp.war.leaderWounded>>
 			Unfortunately, @@.red;$he sustained major injuries.@@
 			<<set _woundType = App.SecExp.inflictBattleWound(_S.Bodyguard)>>
 			<<if _woundType == "voice">>
@@ -962,7 +962,7 @@
 			<</if>>
 			Your troops were greatly affected by the loss of their leader.
 		<</if>>
-		<<if $battleResult == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
+		<<if $SecExp.war.result == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
 			<<set _found = 0>>
 			<<for _i = 0; _i < $SecExp.battles.slaveVictories.length; _i++>>
 				<<if $SecExp.battles.slaveVictories[_i].ID == $BodyguardID>>
@@ -990,7 +990,7 @@
 				<<set $SecExp.battles.slaveVictories.push(_newSlave)>>
 			<</if>>
 		<</if>>
-	<<elseif $leadingTroops == "headGirl">>
+	<<elseif $SecExp.war.commander == "headGirl">>
 		<<setLocalPronouns _S.HeadGirl>>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> decided it will be your Head Girl that leads the troops.
 		<<if App.SecExp.battle.deployedUnits('slaves') >= 1>>
@@ -1003,86 +1003,86 @@
 		<<if _oldRep < 10000 && _oldAuth < 10000 || _S.HeadGirl.prestige < 1>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
-				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your mercenaries or your soldiers.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					commander, and neither are your mercenaries.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your soldiers.
 				<<else>>
 					commander.
 				<</if>>
-			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<</if>>
 		<<elseif _S.HeadGirl.prestige >= 2>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your
-				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					volunteers, your mercenaries and your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					volunteers and your mercenaries are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					volunteers and your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<<else>>
 					volunteers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 				<</if>>
-			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers are delighted to have such a prestigious individual as their commander, almost forgetting $he is a slave.
 			<</if>>
 		<<else>>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
-				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<if App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your mercenaries and soldiers, but they trust you enough not to question your decision.
 				<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 					commander, and neither are your mercenaries, but they trust you enough not to question your decision.
-				<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+				<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 					commander, and neither are your soldiers, but they trust you enough not to question your decision.
 				<<else>>
 					commander, but they trust you enough not to question your decision.
 				<</if>>
-			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif App.SecExp.battle.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your mercenaries and soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
 			<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 				Your mercenaries <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
-			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+			<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 				Your soldiers <<if App.SecExp.battle.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander, but they trust you enough not to question your decision.
 			<</if>>
 		<</if>>
-		<<if (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
+		<<if (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
 			With $his experience and $his great intellect, $he is able to exploits the smallest of tactical advantages, making your troops greatly effective.
 		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
 			While $he lacks experience, $his great intellect allows $him to seize and exploit any tactical advantage the battlefield offers $him.
-		<<elseif (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
+		<<elseif (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
 			Having both the experience and the intelligence, $he performs admirably as your commander. $His competence greatly increases the efficiency of your troops.
 		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
 			Despite not having a lot of experience as a leader, $his intelligence makes $him a good commander, increasing the efficiency of your troops.
-		<<elseif (setup.bodyguardCareers.includes(_S.HeadGirl.career) || setup.HGCareers.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
+		<<elseif (App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) || App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) || setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) && _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
 			Thanks to $his experience, $he is a decent commander, competently guiding your troops through the battle.
 		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
 			Lacking experience $his performance as a commander is rather forgettable.
-		<<elseif !(setup.bodyguardCareers.includes(_S.HeadGirl.career) && setup.HGCareers.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
+		<<elseif !(App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) && App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
 			Despite the experience $he accumulated during $his past career, $his very low intelligence is a great disadvantage for your troops.
 		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
 			Without experience and low intelligence, $he performs horribly as a commander, greatly affecting your troops.
-		<<elseif !(setup.bodyguardCareers.includes(_S.HeadGirl.career) && setup.HGCareers.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
+		<<elseif !(App.Data.Careers.Leader.bodyguard.includes(_S.HeadGirl.career) && App.Data.Careers.Leader.HG.includes(_S.HeadGirl.career) && setup.secExCombatPrestige.includes(_S.HeadGirl.prestigeDesc)) || _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
 			Despite the experience $he accumulated during $his past career, $he lacks the intelligence to apply it quickly and effectively, making for a rather poor performance in the field.
 		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
 			$He lacks the experience and the intelligence to be an effective commander, the performance of your troops suffers because of $his poor leadership.
 		<</if>>
-		<<if $gainedCombat == 1>>
+		<<if $SecExp.war.gainedCombat>>
 			During the battle, $he had to fight for $his life, giving $him experience in modern combat. $He is now proficient with modern firearms and hand to hand combat.
 		<</if>>
-		<<if $leaderWounded == 1>>
+		<<if $SecExp.war.leaderWounded>>
 			Unfortunately, @@.red;$he sustained major injuries.@@
 			<<set _woundType = App.SecExp.inflictBattleWound(_S.Bodyguard)>>
 			<<if _woundType == "voice">>
@@ -1098,7 +1098,7 @@
 			<</if>>
 			Your troops were greatly affected by the loss of their leader.
 		<</if>>
-		<<if $battleResult == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
+		<<if $SecExp.war.result == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
 			<<set _found = 0>>
 			<<for _i = 0; _i < $SecExp.battles.slaveVictories.length; _i++>>
 				<<if $SecExp.battles.slaveVictories[_i].ID == $HeadGirlID>>
@@ -1126,7 +1126,7 @@
 				<<set $SecExp.battles.slaveVictories.push(_newSlave)>>
 			<</if>>
 		<</if>>
-	<<elseif $leadingTroops == "citizen">>
+	<<elseif $SecExp.war.commander == "citizen">>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> decided to appoint one of your volunteers as the commander.
 		<<if $arcologies[0].FSDegradationist == "unset" && $arcologies[0].FSPaternalist == "unset">>
 			<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
@@ -1157,22 +1157,22 @@
 		<<elseif App.SecExp.battle.deployedUnits('mercs') >= 1>>
 			Your mercenaries are not thrilled to be lead by a civilian without any formal martial training or education.
 		<</if>>
-		<<if $arcologies[0].FSRomanRevivalist != "unset" && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<if $arcologies[0].FSRomanRevivalist != "unset" && $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 			Since you decided to revive old Rome, many of your citizens took on themselves to educate themselves in martial matters, because of this your soldiers feel safe enough in the hands of one of your volunteers.
 		<<elseif $arcologies[0].FSNeoImperialist != "unset" && App.SecExp.battle.deployedUnits('mercs') >= 1>>
 			Since having institued an Imperial society, your citizens have become adept at modern warfare and the line soldiers feel much more comfortable being commanded by one of your Imperial Knights.
-		<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<elseif $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 			You soldiers are not thrilled to be lead by a civilian without any formal martial training or education.
 		<</if>>
-		<<if $leaderWounded == 1>>
+		<<if $SecExp.war.leaderWounded>>
 			During the battle a stray bullet managed to reach the leader. Your troops were greatly affected by the loss.
 		<</if>>
-	<<elseif $leadingTroops == "mercenary">>
+	<<elseif $SecExp.war.commander == "mercenary">>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> decided to appoint one of your mercenary officers as the commander.
 		<<if App.SecExp.battle.deployedUnits('mercs') >= 1>>
 			Your mercenaries of course approve of your decision.
 		<</if>>
-		<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 			Your soldiers feel more confident going into battle with an experienced commander.
 		<</if>>
 		<<if $arcologies[0].FSRomanRevivalist != "unset" && App.SecExp.battle.deployedUnits('militia') >= 1>>
@@ -1183,15 +1183,15 @@
 		<<if $arcologies[0].FSDegradationist != "unset" && App.SecExp.battle.deployedUnits('slaves') >= 1>>
 			Because of your degradationist society, your slave soldiers are highly distrustful of the gun for hire you forced them to accept as leader.
 		<</if>>
-		<<if $leaderWounded == 1>>
+		<<if $SecExp.war.leaderWounded>>
 			During the battle a stray bullet managed to reach the mercenary. Your troops were greatly affected by the loss of their leader.
 		<</if>>
-	<<elseif $leadingTroops == "colonel">>
+	<<elseif $SecExp.war.commander == "colonel">>
 		<<if $auto == 1>>$assistant.name<<else>>You<</if>> decided to appoint The Colonel as the commander.
 		<<if App.SecExp.battle.deployedUnits('mercs') >= 1>>
 			Your mercenaries approve of such decisions, as they feel more confident by having a good, experienced commander.
 		<</if>>
-		<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+		<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 			The soldiers of $SF.Lower obviously approved of your decision.
 		<</if>>
 		<<if $arcologies[0].FSRomanRevivalist != "unset" && App.SecExp.battle.deployedUnits('militia') >= 1>>
@@ -1204,7 +1204,7 @@
 		<<if $arcologies[0].FSDegradationist != "unset" && App.SecExp.battle.deployedUnits('slaves') >= 1>>
 			Because of your degradationist society, your slave soldiers are highly distrustful of the soldier you forced them to accept as leader.
 		<</if>>
-		<<if $leaderWounded == 1>>
+		<<if $SecExp.war.leaderWounded>>
 			During the battle a stray bullet managed to reach The Colonel, wounding her slightly. Your troops were greatly affected by the loss of their leader.
 		<</if>>
 	<</if>>
@@ -1213,237 +1213,237 @@
 
 	/* tactics */
 	<<if $auto == 1>>$assistant.name<<else>>You<</if>>
-	<<if $chosenTactic == "Bait and Bleed">>
+	<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 		chose to employ "bait and bleed" tactics or relying on quick attacks and harassment to tire and wound the enemy until their surrender.
-	<<elseif $chosenTactic == "Guerrilla">>
+	<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 		chose to employ "guerrilla" tactics or relying on stealth, terrain knowledge and subterfuge to undermine and ultimately destroy the enemy.
-	<<elseif $chosenTactic == "Choke Points">>
+	<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 		chose to employ "choke points" tactics or the extensive use of fortified or highly defensive positions to slow down and eventually stop the enemy.
-	<<elseif $chosenTactic == "Interior Lines">>
+	<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 		chose to employ "interior lines" tactics or exploiting the defender's shorter front to quickly disengage and concentrate troops when and where needed.
-	<<elseif $chosenTactic == "Pincer Maneuver">>
+	<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			chose to employ "pincer maneuver" tactics or attempting to encircle the enemy by faking a collapsing center front.
-	<<elseif $chosenTactic == "Defense In Depth">>
+	<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 		chose to employ "defense in depth" tactics or relying on mobility to disengage and exploit overextended enemy troops by attacking their freshly exposed flanks.
-	<<elseif $chosenTactic == "Blitzkrieg">>
+	<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 		chose to employ "blitzkrieg" tactics or shattering the enemy's front-line with a violent, concentrated armored assault.
-	<<elseif $chosenTactic == "Human Wave">>
+	<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 		chose to employ "human wave" tactics or overwhelming the enemy's army with a massive infantry assault.
 	<</if>>
-	<<if $battleTerrain == "urban">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<if $SecExp.war.terrain == "urban">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			The urban terrain synergized well with bait and bleed tactics, slowly chipping away at the enemy's forces from the safety of the narrow streets and empty buildings.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			The urban terrain synergized well with guerrilla tactics, eroding your enemy's determination from the safety of the narrow streets and empty buildings.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			The urban environment offers many opportunities to hunker down and stop the momentum of the enemy's assault while keeping your soldiers in relative safety.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			While the urban environment offers many highly defensive position, it does restrict movement and with it the advantages of exploiting interior lines.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			The urban terrain does not allow for wide maneuvers, the attempts of your forces to encircle the attackers are mostly unsuccessful.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			While the urban environment offers many defensive positions, it limits mobility, limiting the advantages of using a defense in depth tactic.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The urban terrain is difficult to traverse, making your troops attempt at a lightning strike unsuccessful.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The urban terrain offers great advantages to the defender, your men find themselves in great disadvantage while mass assaulting the enemy's position.
 		<</if>>
-	<<elseif $battleTerrain == "rural">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "rural">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			The open terrain of rural lands does not lend itself well to bait and bleed tactics, making it harder for your men to achieve tactical superiority.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			The open terrain of rural lands does not offer many hiding spots, making it harder for your men to perform guerrilla actions effectively.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			The open terrain of rural lands does not offer many natural choke points, making it hard for your troops to funnel the enemy towards highly defended positions.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			The open terrain allows your men to easily exploit the superior mobility of the defender, making excellent use of interior lines to strike where it hurts.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			The open terrain affords your men great mobility, allowing them to easily position themselves for envelopment.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			The open terrain affords your men great mobility, allowing them to exploit overextended assaults and concentrate where and when it matters.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The open terrain affords your men great mobility, making it easier to accomplish concentrated lightning strikes.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The open terrain affords your men great mobility, making it easier to overwhelm the enemy with mass assaults.
 		<</if>>
-	<<elseif $battleTerrain == "hills">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "hills">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			While the hills offer some protection, they also make it harder to maneuver; bait and bleed tactics will not be 100% effective here.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			The hills offer protection to both your troops and your enemy's, making it harder for your men to accomplish guerrilla attacks effectively.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			While not as defensible as mountains, hills offer numerous opportunities to funnel the enemy towards highly defensible choke points.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			The limited mobility on hills hampers the capability of your troops to exploit the defender's greater mobility afforded by interior lines.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			Limited mobility due to the hills is a double edged sword, affording your men a decent shot at encirclement.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			The limited mobility on hills hampers the capability of your troops to use elastic defense tactics.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The limited mobility on hills hampers the capability of your troops to organize lightning strikes.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The defensibility of hills makes it harder to accomplish victory through mass assaults.
 		<</if>>
-	<<elseif $battleTerrain == "coast">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "coast">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			On the coast there's little space and protection to effectively employ bait and bleed tactics.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			On the coast there's little space and protection to effectively employ guerrilla tactics.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			Amphibious attacks are difficult in the best of situations; the defender has a very easy time funneling the enemy towards their key defensive positions.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			While in an amphibious landing mobility is not the defender's best weapon, exploiting interior lines still affords your troops some advantages.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			Attempting to encircle a landing party is not the best course of action, but not the worst either.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			In an amphibious assault it's very easy for the enemy to overextend, making defense in depth tactics quite effective.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The rough, restricted terrain does not lend itself well to lightning strikes, but the precarious position of the enemy still gives your mobile troops tactical superiority.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The rough, restricted terrain does not lend itself well to mass assaults, but the precarious position of the enemy still gives your troops tactical superiority.
 		<</if>>
-	<<elseif $battleTerrain == "outskirts">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "outskirts">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			Fighting just beneath the walls of the arcology does not allow for the dynamic redeployment of troops bait and bleed tactics would require.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			Fighting just beneath the walls of the arcology does not allow for the dynamic redeployment of troops guerrilla tactics would require.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			The imposing structure of the arcology itself provides plenty of opportunities to create fortified choke points from which to shatter the enemy assault.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			While the presence of the arcology near the battlefield is an advantage, it does limit maneuverability, lowering overall effectiveness of interior lines tactics.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			While the presence of the arcology near the battlefield is an advantage, it does limit maneuverability, lowering the chances of making an effective encirclement.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			Having the arcology near the battlefield means there are limited available maneuvers to your troops, who still needs to defend the structure, making defense in depth tactics not as effective.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			While an assault may save the arcology from getting involved at all, having the imposing structure so near does limit maneuverability and so the impetus of the lightning strike.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			While an attack may save the arcology from getting involved at all, having the imposing structure so near does limit maneuverability and so the impetus of the mass assault.
 		<</if>>
-	<<elseif $battleTerrain == "mountains">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "mountains">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			While the mountains offer great protection, they also limit maneuverability; bait and bleed tactics will not be quite as effective here.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			The mountains offer many excellent hiding spots and defensive positions, making guerrilla tactics very effective.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			The mountains offer plenty of opportunity to build strong defensive positions from which to shatter the enemy's assault.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			While the rough terrain complicates maneuvers, the defensive advantages offered by the mountains offsets its negative impact.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			The rough terrain complicates maneuvers; your men have a really hard time pulling off an effective encirclement in this environment.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			While mobility is limited, defensive positions are plentiful; your men are not able to fully exploit overextended assaults, but are able to better resist them.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The rough terrain complicates maneuvers; your men have a really hard time pulling off an effective lightning strike in this environment.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The rough terrain complicates maneuvers; your men have a really hard time pulling off an effective mass assault in this environment.
 		<</if>>
-	<<elseif $battleTerrain == "wasteland">>
-		<<if $chosenTactic == "Bait and Bleed">>
+	<<elseif $SecExp.war.terrain == "wasteland">>
+		<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
 			While the wastelands are mostly open terrain, there are enough hiding spots to make bait and bleed tactics work well enough.
-		<<elseif $chosenTactic == "Guerrilla">>
+		<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
 			While the wastelands are mostly open terrain, there are enough hiding spots to make guerrilla tactics work well enough.
-		<<elseif $chosenTactic == "Choke Points">>
+		<<elseif $SecExp.war.chosenTactic == "Choke Points">>
 			The wastelands are mostly open terrain; your men have a difficult time setting up effective fortified positions.
-		<<elseif $chosenTactic == "Interior Lines">>
+		<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
 			The wastelands, while rough, are mostly open terrain, where your men can exploit to the maximum the superior mobility of the defender.
-		<<elseif $chosenTactic == "Pincer Maneuver">>
+		<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
 			The wastelands, while rough, are mostly open terrain; your men can set up an effective encirclement here.
-		<<elseif $chosenTactic == "Defense In Depth">>
+		<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
 			The wastelands, while rough, are mostly open terrain, allowing your men to liberally maneuver to exploit overextended enemies.
-		<<elseif $chosenTactic == "Blitzkrieg">>
+		<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
 			The wastelands, while rough, are mostly open terrain, where your men are able to mount effective lightning strikes.
-		<<elseif $chosenTactic == "Human Wave">>
+		<<elseif $SecExp.war.chosenTactic == "Human Wave">>
 			The wastelands, while rough, are mostly open terrain, where your men are able to mount effective mass assaults.
 		<</if>>
 	<</if>>
 
-	<<if $chosenTactic == "Bait and Bleed">>
-		<<if $attackType == "raiders">>
+	<<if $SecExp.war.chosenTactic == "Bait and Bleed">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			Since the bands of raiders are used to be on high alert and on the move constantly, bait and bleed tactics are not effective against them.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			The modern armies hired by Free Cities are decently mobile, which means quick hit and run attacks will be less successful, but their discipline and confidence still make them quite susceptible to this type of attack.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			While old world armies are tough nuts to crack, their predictability makes them the perfect target for hit and run and harassment tactics.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			Freedom fighters live every day as chasing and being chased by far superior forces, they are far more experienced than your troops in this type of warfare and much less susceptible to it.
 		<</if>>
-	<<elseif $chosenTactic == "Guerrilla">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Guerrilla">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			Since the bands of raiders are used to be on high alert and on the move constantly, guerrilla tactics are not effective against them.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			The modern armies hired by Free Cities are highly mobile, which means quick hit and run attacks will be less successful, but their discipline and confidence still make them quite susceptible to this type of attack.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			While old world armies are tough nuts to crack, their predictability makes them the perfect target for hit and run and harassment tactics.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			Freedom fighters live every day as chasing and being chased by far superior forces, they are far more experienced than your troops in this type of warfare and much less susceptible to it.
 		<</if>>
-	<<elseif $chosenTactic == "Choke Points">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Choke Points">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			Raiders lack heavy weaponry or armor, so making use of fortified positions is an excellent way to dissipate the otherwise powerful momentum of their assault.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			The high tech equipment Free Cities can afford to give their guns for hire means there's no defensive position strong enough to stop them, still the relatively low numbers means they will have to take a careful approach, slowing them down.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			Old world armies have both the manpower and the equipment to conquer any defensive position, making use of strong fortifications will only bring you this far against them.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			The lack of specialized weaponry means freedom fighters have a rather hard time overcoming tough defensive positions, unfortunately they have also a lot of experience in avoiding them.
 		<</if>>
-	<<elseif $chosenTactic == "Interior Lines">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Interior Lines">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			The highly mobile horde of raiders will not give much room for your troops to maneuver, lowering their tactical superiority.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			While decently mobile, Free Cities forces are not in high enough numbers to risk maintaining prolonged contact, allowing your troops to quickly disengage and redeploy where it hurts.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			Old world armies are not famous for the mobility, which makes them highly susceptible to any tactic that exploits maneuverability and speed.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			While not the best equipped army, the experience and mobility typical of freedom fighters groups make them tough targets for an army that relies itself on mobility.
 		<</if>>
-	<<elseif $chosenTactic == "Pincer Maneuver">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Pincer Maneuver">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			While numerous, the undisciplined masses of raiders are easy prey for encirclements.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			While decently mobile, the low number of Free Cities expedition forces make them good candidates for encirclements.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			The discipline and numbers of old world armies make them quite difficult to encircle.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			While not particularly mobile, freedom fighters are used to fight against overwhelming odds, diminishing the effectiveness of the encirclement.
 		<</if>>
-	<<elseif $chosenTactic == "Defense In Depth">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Defense In Depth">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			While their low discipline makes them prime candidates for an elastic defense type of strategy, their high numbers limit your troops maneuverability.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			With their low numbers Free Cities mercenaries are quite susceptible to this type of tactic, despite their mobility.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			With their low mobility old world armies are very susceptible to this type of strategy.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			Low mobility and not particularly high numbers mean freedom fighters can be defeated by employing elastic defense tactics.
 		<</if>>
-	<<elseif $chosenTactic == "Blitzkrieg">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Blitzkrieg">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			With their low discipline and lack of heavy equipment, lightning strikes are very effective against raider hordes.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			Having good equipment and discipline on their side, Free Cities expeditions are capable of responding to even strong lightning strikes.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			While disciplined, old world armies low mobility makes them highly susceptible to lightning strikes.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			While not well equipped, freedom fighters have plenty of experience fighting small, mobile attacks, making them difficult to defeat with lightning strikes.
 		<</if>>
-	<<elseif $chosenTactic == "Human Wave">>
-		<<if $attackType == "raiders">>
+	<<elseif $SecExp.war.chosenTactic == "Human Wave">>
+		<<if $SecExp.war.attacker.type == "raiders">>
 			The hordes of raiders are much more experienced than your soldiers in executing mass assaults and they also have a lot more bodies to throw in the grinder.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type == "free city">>
 			The good equipment and mobility of Free Cities mercenaries cannot save them from an organized mass assault.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type == "old world">>
 			Unfortunately the discipline and good equipment of old world armies allow them to respond well against a mass assault.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type == "freedom fighters">>
 			The relative low numbers and not great equipment typical of freedom fighters make them susceptible to being overwhelmed by an organized mass assault.
 		<</if>>
 	<</if>>
-	In the end <<if $leadingTroops == "PC">>you were<<else>>your commander was<</if>>
-	<<if $tacticsSuccessful == 1>>
-		@@.green;able to successfully employ <<print $chosenTactic>> tactics,@@ greatly enhancing
+	In the end <<if $SecExp.war.commander == "PC">>you were<<else>>your commander was<</if>>
+	<<if $SecExp.war.tacticsSuccessful>>
+		@@.green;able to successfully employ <<print $SecExp.war.chosenTactic>> tactics,@@ greatly enhancing
 	<<else>>
-		@@.red;not able to effectively employ <<print $chosenTactic>> tactics,@@ greatly affecting
+		@@.red;not able to effectively employ <<print $SecExp.war.chosenTactic>> tactics,@@ greatly affecting
 	<</if>>
 	 the efficiency of your army. <br> <<include "unitsBattleReport">>
 
@@ -1507,21 +1507,8 @@
 <</if>>
 
 /* resets variables */
-<<set $leaderWounded = 0>>
-<<set $gainedWarfare = 0>>
-<<set $tacticsSuccessful = 0>>
-<<set $attackType = "none">>
-<<set $chosenTactic = "none">>
-<<set $leadingTroops = "none">>
-<<set $attackTroops = 0>>
-<<set $attackEquip = 0>>
-<<set $battleTerrain = "none">>
 <<set $attackThisWeek = 0>>
-<<set $enemyLosses = 0>>
-<<set $losses = 0>>
-<<set $battleTurns = 0>>
 <<set $majorBattle = 0>>
-<<set $SFIntervention = 0>>
 <<set $secBots.isDeployed = 0>>
 <<for _i = 0; _i < $militiaUnits.length; _i++>>
 	<<set $militiaUnits[_i].isDeployed = 0>>
diff --git a/src/Mods/SecExp/authorityReport.tw b/src/Mods/SecExp/authorityReport.tw
deleted file mode 100644
index 2a03cc223e371c6cd0eab14f9bd877a527d383c9..0000000000000000000000000000000000000000
--- a/src/Mods/SecExp/authorityReport.tw
+++ /dev/null
@@ -1,179 +0,0 @@
-:: authorityReport [nobr]
-
-<<if $useTabs == 0>>__Authority__<</if>>
-<br>
-Your authority is
-<<if $SecExp.core.authority > 19500>>
-	nearly absolute. The arcology is yours to command as it pleases you.
-<<elseif $SecExp.core.authority > 15000>>
-	extremely high. There's little you cannot do within the walls of your arcology.
-<<elseif $SecExp.core.authority > 10000>>
-	high. You command respect and fear in equal measure.
-<<elseif $SecExp.core.authority > 5000>>
-	moderate. You command some respect from your citizens.
-<<else>>
-	low. You command no respect or fear from your citizens.
-<</if>>
-
-<<set _authGrowth = 0>>
-
-<<if $PC.career == "wealth">>
-	As part of the idle rich, you were used to having obedience coming naturally to you. Now you find it harder to maintain authority over the arcology.
-	<<set _authGrowth -= (10 * random(5,15))>>
-<<elseif $PC.career == "slaver">>
-	Your past as a slaver helps you assert your authority over the arcology.
-	<<set _authGrowth += (10 * random(5,15))>>
-<<elseif $PC.career == "escort">>
-	Given your past career as an escort, you find it hard to assert your authority over the arcology and its inhabitants.
-	<<set _authGrowth -= (10 * random(5,15))>>
-<<elseif $PC.career == "servant">>
-	Given your past career as a servant, you find it hard to assert your authority over the arcology and its inhabitants.
-	<<set _authGrowth -= (10 * random(5,15))>>
-<<elseif $PC.career == "BlackHat">>
-	Given your past career as a (rather questionable) incursion specialist, you find it hard to assert your authority over the arcology and its inhabitants, despite what you may know about them.
-	<<set _authGrowth -= (10 * random(5,15))>>
-<<elseif $PC.career == "gang">>
-	Given your past life as a gang leader, you find it easier to assert your authority over the arcology and its inhabitants.
-	<<set _authGrowth += (10 * random(5,15))>>
-<</if>>
-
-<<if $rep >= 19500>>
-	Your legend is so well known that your mere presence commands respect and obedience, increasing your authority.
-	<<set _authGrowth += (10 * random(10,20))>>
-<<elseif $rep >= 15000>>
-	Your reputation is so high that your mere presence commands respect, increasing your authority.
-	<<set _authGrowth += (10 * random(5,15))>>
-<<elseif $rep >= 10000>>
-	Your reputation is high enough that your presence commands some respect, increasing your authority.
-	<<set _authGrowth += (10 * random(2,8))>>
-<</if>>
-
-<<if $SecExp.core.security >= 90>>
-	Your arcology is incredibly secure and your citizens know quite well who to thank, greatly increasing your authority.
-	<<set _authGrowth += (10 * random(10,20))>>
-<<elseif $SecExp.core.security >= 70>>
-	Your arcology is really secure and your citizens know quite well who to thank, increasing your authority.
-	<<set _authGrowth += (10 * random(5,15))>>
-<<elseif $SecExp.core.security >= 50>>
-	Your arcology is quite secure and your citizens know who to thank, increasing your authority.
-	<<set _authGrowth += (10 * random(2,8))>>
-<</if>>
-
-<<if $SecExp.core.crimeLow >= 90>>
-	The all powerful criminal organizations controlling the arcology have a very easy time undermining your authority.
-	<<set _authGrowth -= (10 * random(10,20))>>
-<<elseif $SecExp.core.crimeLow >= 70>>
-	Crime is king in the arcology, powerful criminals have a very easy time undermining your authority.
-	<<set _authGrowth -= (10 * random(5,15))>>
-<<elseif $SecExp.core.crimeLow >= 50>>
-	Criminal organizations have a strong foothold in the arcology, their activities undermine your authority.
-	<<set _authGrowth -= (10 * random(2,8))>>
-<</if>>
-
-<<if $averageDevotion >= 50 && $averageTrust >= 50>>
-	The high devotion and trust of your slaves speak eloquently of your leadership capabilities, helping your authority grow.
-	<<set _authGrowth += (5 * (($averageDevotion + $averageTrust) / 10))>>
-<<elseif $averageDevotion >= 50>>
-	The high devotion of your slaves speaks eloquently of your leadership capabilities, helping your authority grow.
-	<<set _authGrowth += (5 * ($averageDevotion / 10))>>
-<<elseif $averageTrust >= 50>>
-	The high trust of your slaves speaks eloquently of your leadership capabilities, helping your authority grow.
-	<<set _authGrowth += (5 * ($averageTrust / 10))>>
-<</if>>
-
-<<if $arcologies[0].ownership >= 90>>
-	You own so much of the arcology that your authority quickly increases.
-	<<set _authGrowth += (5 * Math.trunc($arcologies[0].ownership / 10))>>
-<<elseif $arcologies[0].ownership >= 70>>
-	You own a big part of the arcology, causing your authority to increase.
-	<<set _authGrowth += (5 * Math.trunc($arcologies[0].ownership / 10))>>
-<<elseif $arcologies[0].ownership >= 50>>
-	You own the majority of the arcology, causing your authority to slowly increase.
-	<<set _authGrowth += (5 * Math.trunc($arcologies[0].ownership / 10))>>
-<<else>>
-	Your low ownership of the arcology causes your authority to decrease.
-	<<set _authGrowth -= (5 * Math.trunc($arcologies[0].ownership / 10))>>
-<</if>>
-
-<<if App.SecExp.battle.activeUnits() >= 9>>
-	Your military is massive; commanding so many troops greatly increases your authority.
-<<elseif App.SecExp.battle.activeUnits() >= 7>>
-	Your military is huge; commanding such a number of soldiers increases your authority.
-<<elseif App.SecExp.battle.activeUnits() >= 4>>
-	Your military is at a decent size; commanding a small army increases your authority.
-<</if>>
-<<if App.SecExp.battle.activeUnits() >= 4>>
-	<<set _authGrowth += (12 * App.SecExp.battle.activeUnits())>>
-<</if>>
-
-<<set _size = App.SF.upgrades.total()>>
-<<if $SF.Toggle && $SF.Active >= 1 && _size > 10>>
-	Having a powerful special force increases your authority.
-	<<set _authGrowth += (_size/10)>>
-<</if>>
-
-<<if $arcologies[0].FSChattelReligionist >= 90>>
-	Religious organizations have a tight grip on the minds of your residents and their dogma greatly helps your authority grow.
-	<<set _authGrowth += $arcologies[0].FSChattelReligionist>>
-<<elseif $arcologies[0].FSChattelReligionist >= 50>>
-	Religious organizations have a tight grip on the minds of your residents and their dogma helps your authority grow.
-	<<set _authGrowth += $arcologies[0].FSChattelReligionist>>
-<</if>>
-
-<<set _elite = $arcologies[0].FSNeoImperialistLaw2 == 1 ? 'Barons' : 'Societal Elite'>>
-<<if $arcologies[0].FSRestart >= 90>>
-	The arcology's society is extremely stratified. The reliance on the _elite by the lower classes greatly increases your reputation.
-	<<set _authGrowth += $arcologies[0].FSRestart>>
-<<elseif $arcologies[0].FSRestart >= 50>>
-	The arcology's society is very stratified. The reliance on the _elite by the lower classes increases your reputation.
-	<<set _authGrowth += $arcologies[0].FSRestart>>
-<</if>>
-
-<<if $arcologies[0].FSPaternalist >= 90>>
-	Your extremely paternalistic society has the unfortunate side effects of spreading dangerous ideals in the arcology, greatly damaging your authority.
-	<<set _authGrowth -= Math.clamp($arcologies[0].FSPaternalist, 0, 100)>>
-<<elseif $arcologies[0].FSPaternalist >= 50>>
-	Your paternalistic society has the unfortunate side effects of spreading dangerous ideals in the arcology, damaging your authority.
-	<<set _authGrowth -= Math.clamp($arcologies[0].FSPaternalist, 0, 100)>>
-<</if>>
-
-<<if $arcologies[0].FSNull >= 90>>
-	Extreme cultural openness allows dangerous ideas to spread in your arcology, greatly damaging your reputation.
-	<<set _authGrowth -= $arcologies[0].FSNull>>
-<<elseif $arcologies[0].FSNull >= 50>>
-	Mild cultural openness allows dangerous ideas to spread in your arcology, damaging your reputation.
-	<<set _authGrowth -= $arcologies[0].FSNull>>
-<</if>>
-
-<<if $SecExp.buildings.propHub>>
-	<<if $SecExp.buildings.propHub.upgrades.miniTruth >= 1>>
-		Your authenticity department works tirelessly to impose your authority in all of the arcology.
-		<<set _authGrowth += (15 * $SecExp.buildings.propHub.upgrades.miniTruth)>>
-	<</if>>
-	<<if $SecExp.buildings.propHub.upgrades.secretService >= 1>>
-		Your secret services constantly keep under surveillance any potential threat, intervening when necessary. Rumors of the secretive security service and mysterious disappearances make your authority increase.
-		<<set _authGrowth += (15 * $SecExp.buildings.propHub.upgrades.secretService)>>
-	<</if>>
-<</if>>
-
-<<if App.SecExp.upkeep.edictsAuth() > 0>>
-	Some of your authority is spent maintaining your edicts.
-	<<set _authGrowth -= App.SecExp.upkeep.edictsAuth()>>
-<</if>>
-
-This week
-<<if _authGrowth > 0>>
-	@@.green;authority has increased.@@
-<<elseif _authGrowth == 0>>
-	@@.yellow;authority did not change.@@
-<<else>>
-	@@.red;authority has decreased.@@
-<</if>>
-
-<<set $SecExp.core.authority += _authGrowth>>
-<<set $SecExp.core.authority = Math.trunc(Math.clamp($SecExp.core.authority, 0, 20000))>>
-
-<<if $SecExp.settings.rebellion.enabled == 1>>
-	<br><br>
-	<<include "rebellionGenerator">> /* rebellions */
-<</if>>
diff --git a/src/Mods/SecExp/buildings/secBarracks.tw b/src/Mods/SecExp/buildings/secBarracks.tw
index 9a606abe23209f99369e3bae6d49032aeafef9db..3b5378c87a25ea870f4221273f144138e4aff72a 100644
--- a/src/Mods/SecExp/buildings/secBarracks.tw
+++ b/src/Mods/SecExp/buildings/secBarracks.tw
@@ -57,7 +57,7 @@ While this a sore sight for many citizens of $arcologies[0].name, the barracks s
 		<br> _capSF might be able to provide assistance.
 		<<if $SF.Squad.Firebase > 5 && $SecExp.edicts.SFSupportLevel >= 4 && App.SecExp.battle.maxUnits() === 18 && App.SecExp.battle.deploySpeed() <= 10>>
 			<br>_capSF [[will provide the security force their own section in the Firebase.|secBarracks][$SecExp.sectionInFirebase = 1, cashX(-_cost, "specialForcesCap")]]
-			@@.red;<<print cashFormat(_cost)>>@@
+			@@.cash.dec;<<print cashFormat(_cost)>>@@
 		<</if>>
 		<<if $SF.Squad.Firebase < 5>>
 			<<set _reasons.push('Firebase would likely require additional expansion(s)')>>
diff --git a/src/Mods/SecExp/buildings/transportHub.tw b/src/Mods/SecExp/buildings/transportHub.tw
index 53bae3e250548cba8d12798d033982607aee8d16..bef5fd6c4891c4a3ca8fc870769d223a2d9af04f 100644
--- a/src/Mods/SecExp/buildings/transportHub.tw
+++ b/src/Mods/SecExp/buildings/transportHub.tw
@@ -12,15 +12,15 @@ You quickly reach the transport hub, where a constant stream of vehicles, people
 <</if>>
 
 <<if $SecExp.buildings.transportHub.airport == 1>>
-	The arcology's airport is  relatively small and poorly equipped. It can handle some traffic, but nothing noteworthy.
+	The arcology's airport is relatively small and poorly equipped. It can handle some traffic, but nothing noteworthy.
 <<elseif $SecExp.buildings.transportHub.airport == 2>>
-	The arcology's airport is  relatively small, but well equipped. It can handle some traffic, but nothing too serious.
+	The arcology's airport is relatively small, but well equipped. It can handle some traffic, but nothing too serious.
 <<elseif $SecExp.buildings.transportHub.airport == 3>>
-	The arcology's airport is  good sized and well equipped. It can handle a good amount of traffic.
+	The arcology's airport is good sized and well equipped. It can handle a good amount of traffic.
 <<elseif $SecExp.buildings.transportHub.airport == 4>>
-	The arcology's airport is  good sized and very well equipped. It can handle a lot of traffic.
+	The arcology's airport is good sized and very well equipped. It can handle a lot of traffic.
 <<else>>
-	The arcology's airport is  huge and very well equipped. It can handle an impressive amount of traffic.
+	The arcology's airport is huge and very well equipped. It can handle an impressive amount of traffic.
 <</if>>
 
 <<if $terrain != "oceanic" && $terrain != "marine">>
diff --git a/src/Mods/SecExp/js/Unit.js b/src/Mods/SecExp/js/Unit.js
index 2c9b03766eb89ebb839dcac763baf3a7ba917fc5..16c83ff058a0716ca4040056da926aded5d9dc1d 100644
--- a/src/Mods/SecExp/js/Unit.js
+++ b/src/Mods/SecExp/js/Unit.js
@@ -51,57 +51,58 @@ App.SecExp.bulkUpgradeUnit = function(unit) {
 /** Reports changes to the supplied unit's loyalty.
  * @param {FC.SecExp.PlayerHumanUnitData} input the unit type to be checked.
  * @param {FC.SecExp.PlayerHumanUnitType} type
+ * @returns {HTMLDivElement}
  */
 App.SecExp.humanUnitLoyaltyChanges = function(input, type) {
 	let loyaltyChange = 0, el = document.createElement("div");
 
 	el.append(`${input.platoonName}: `);
 	if (V.SecExp.buildings.barracks && V.SecExp.buildings.barracks.loyaltyMod >= 1) {
-		el.append("is periodically sent to the indoctrination facility in the barracks for thought correction therapy.");
+		el.append("is periodically sent to the indoctrination facility in the barracks for thought correction therapy. ");
 		loyaltyChange += 2 * V.SecExp.buildings.barracks.loyaltyMod;
 	}
 	if (input.commissars >= 1) {
-		el.append("The commissars attached to the unit carefully monitor the officers and grunts for signs of insubordination.");
+		el.append("The commissars attached to the unit carefully monitor the officers and grunts for signs of insubordination. ");
 		loyaltyChange += 2 * input.commissars;
 	}
 	if (V.SecExp.edicts.defense.soldierWages === 2) {
 		if (type === 'slave') {
-			el.append("The slaves greatly appreciate the generous wage given to them for their service as soldiers. Occasions to earn money for a slave are scarce after all.");
+			el.append("The slaves greatly appreciate the generous wage given to them for their service as soldiers. Occasions to earn money for a slave are scarce after all. ");
 		} else if (type === 'citizens') {
-			el.append("The soldiers greatly appreciate the generous wage given to them for their service. They are proud to defend their homes while making a small fortune out of it.");
+			el.append("The soldiers greatly appreciate the generous wage given to them for their service. They are proud to defend their homes while making a small fortune out of it. ");
 		} else if (type === 'mercenary') {
-			el.append("The mercenaries greatly appreciate the generous wage given to them for their service. After all coin is the fastest way to reach their hearts.");
+			el.append("The mercenaries greatly appreciate the generous wage given to them for their service. After all coin is the fastest way to reach their hearts. ");
 		}
 		loyaltyChange += random(5, 10);
 	} else if (V.SecExp.edicts.defense.soldierWages === 1) {
 		if (type === 'slave') {
-			el.append("The slaves appreciate the wage given to them for their service as soldiers, despite it being just adequate. Occasions to earn money for a slave are scarce after all.");
+			el.append("The slaves appreciate the wage given to them for their service as soldiers, despite it being just adequate. Occasions to earn money for a slave are scarce after all. ");
 		} else if (type === 'citizens') {
-			el.append("The soldiers appreciate the wage given to them for their service, despite it being just adequate. They are proud to defend their homes, though at the cost of possible financial gains.");
+			el.append("The soldiers appreciate the wage given to them for their service, despite it being just adequate. They are proud to defend their homes, though at the cost of possible financial gains. ");
 		} else if (type === 'mercenary') {
-			el.append("The mercenaries do not appreciate the barely adequate wage given to them for their service. Still their professionalism keeps them determined to finish their contract.");
+			el.append("The mercenaries do not appreciate the barely adequate wage given to them for their service. Still their professionalism keeps them determined to finish their contract. ");
 		}
 		loyaltyChange += random(-5, 5);
 	} else {
 		if (type === 'slave') {
-			el.append("The slaves do not appreciate the low wage given to them for their service as soldiers, but occasions to earn money for a slave are scarce, so they're not too affected by it.");
+			el.append("The slaves do not appreciate the low wage given to them for their service as soldiers, but occasions to earn money for a slave are scarce, so they're not too affected by it. ");
 		} else if (type === 'citizens') {
-			el.append("The soldiers do not appreciate the low wage given to them for their service. Their sense of duty keeps them proud of their role as defenders of the arcology, but many do feel its financial weight.");
+			el.append("The soldiers do not appreciate the low wage given to them for their service. Their sense of duty keeps them proud of their role as defenders of the arcology, but many do feel its financial weight. ");
 		} else if (type === 'mercenary') {
-			el.append("The mercenaries do not appreciate the low wage given to them for their service. Their skill would be better served by a better contract and this world does not lack demand for guns for hire.");
+			el.append("The mercenaries do not appreciate the low wage given to them for their service. Their skill would be better served by a better contract and this world does not lack demand for guns for hire. ");
 		}
 		loyaltyChange -= random(5, 10);
 	}
 	if (type === 'slave' && V.SecExp.edicts.defense.privilege.slaveSoldier) {
-		el.append("Allowing them to hold material possessions earns you their devotion and loyalty.");
+		el.append("Allowing them to hold material possessions earns you their devotion and loyalty. ");
 		loyaltyChange += random(1, 2);
 	}
 	if (type === 'citizens' && V.SecExp.edicts.defense.privilege.militiaSoldier) {
-		el.append("Allowing them to avoid rent payment for their military service earns you their happiness and loyalty.");
+		el.append("Allowing them to avoid rent payment for their military service earns you their happiness and loyalty. ");
 		loyaltyChange += random(1, 2);
 	}
 	if (type === 'mercenary' && V.SecExp.edicts.defense.privilege.mercSoldier) {
-		el.append("Allowing them to keep part of the loot gained from your enemies earns you their trust and loyalty.");
+		el.append("Allowing them to keep part of the loot gained from your enemies earns you their trust and loyalty. ");
 		loyaltyChange += random(1, 2);
 	}
 
@@ -143,7 +144,7 @@ App.SecExp.fixBrokenUnit = function(input) {
  */
 App.SecExp.generateUnit = function(type) {
 	let newUnit = {
-		ID: -1, equip: 0, active: 1, isDeployed: 0, maxTroops:30,  troops: 30
+		ID: -1, equip: 0, active: 1, isDeployed: 0, maxTroops:30, troops: 30
 	};
 	if (type !== "bots") {
 		Object.assign(newUnit, {
@@ -187,7 +188,7 @@ App.SecExp.deployUnitMenu = function(input, type, count = 0) {
 		}
 		options = document.createElement("div");
 		options.append(App.UI.DOM.link(`${canDeploy ? 'Deploy' : 'Remove'} the unit`, () => {
-			input.isDeployed = canDeploy ? 1 : 0; V.saveValid = 0;
+			input.isDeployed = canDeploy ? 1 : 0; V.SecExp.war.saveValid = 0;
 		},
 		[], passage()
 		));
@@ -324,7 +325,7 @@ App.SecExp.generateUnitID = function() {
 };
 
 /** Player unit factory - get a unit based on its type and index
- * @param {string} type - "Bots", "Militia", "Slaves", "Mercs", or "SF"
+ * @param {PlayerHumanUnitType} type - "Bots", "Militia", "Slaves", "Mercs", or "SF"
  * @param {number} [index] - must be supplied if type is not "Bots"
  * @returns {App.SecExp.Unit}
  */
@@ -456,7 +457,7 @@ App.SecExp.getAppliedUpgrades = function(type) {
 				attack++; def++; hp++; morale += 10;
 			}
 			if (V.arcologies[0].FSNeoImperialistLaw1) {
- 				attack++;
+				attack++;
 			}
 		}
 	}
@@ -509,7 +510,7 @@ App.SecExp.getEdictUpgradeVal = (function() {
 
 /**
  * @interface
- *  @typedef {object} BaseUnit
+ * @typedef {object} BaseUnit
  * @property {number} attack
  * @property {number} defense
  * @property {number} morale
@@ -596,11 +597,11 @@ App.SecExp.BaseDroneUnit = class BaseDroneUnit {
 /** @implements {BaseUnit} */
 App.SecExp.BaseRaiderUnit = class BaseRaiderUnit {
 	static get attack() {
-		return 7;
+		return 7 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.raiders : 0);
 	}
 
 	static get defense() {
-		return 2;
+		return 2 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.raiders : 0);
 	}
 
 	static get morale() {
@@ -615,11 +616,11 @@ App.SecExp.BaseRaiderUnit = class BaseRaiderUnit {
 /** @implements {BaseUnit} */
 App.SecExp.BaseFreeCityUnit = class BaseFreeCityUnit {
 	static get attack() {
-		return 6;
+		return 6 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.FC : 0);
 	}
 
 	static get defense() {
-		return 4;
+		return 4 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.FC : 0);
 	}
 
 	static get morale() {
@@ -634,11 +635,11 @@ App.SecExp.BaseFreeCityUnit = class BaseFreeCityUnit {
 /** @implements {BaseUnit} */
 App.SecExp.BaseOldWorldUnit = class BaseOldWorldUnit {
 	static get attack() {
-		return 8;
+		return 8 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0);
 	}
 
 	static get defense() {
-		return 4;
+		return 4 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0);
 	}
 
 	static get morale() {
@@ -653,11 +654,11 @@ App.SecExp.BaseOldWorldUnit = class BaseOldWorldUnit {
 /** @implements {BaseUnit} */
 App.SecExp.BaseFreedomFighterUnit = class BaseFreedomFighterUnit {
 	static get attack() {
-		return 9;
+		return 9 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0);
 	}
 
 	static get defense() {
-		return 2;
+		return 2 + (V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0);
 	}
 
 	static get morale() {
@@ -883,12 +884,12 @@ App.SecExp.EnemyUnit = class SecExpEnemyUnit extends App.SecExp.Unit {
 
 	get attack() {
 		const equipmentFactor = this._data.equip * App.SecExp.equipMod;
-		return this._baseUnit.attack * (1 + equipmentFactor) + V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0;
+		return this._baseUnit.attack * (1 + equipmentFactor);
 	}
 
 	get defense() {
 		const equipmentFactor = this._data.equip * App.SecExp.equipMod;
-		return this._baseUnit.defense * (1 + equipmentFactor) + V.SecExp.buildings.weapManu ? V.SecExp.buildings.weapManu.sellTo.oldWorld : 0;
+		return this._baseUnit.defense * (1 + equipmentFactor);
 	}
 
 	get hp() {
diff --git a/src/Mods/SecExp/js/authorityReport.js b/src/Mods/SecExp/js/authorityReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..98f724c8c1161ae2a2ce2fc40ea7b5c8a9ae622d
--- /dev/null
+++ b/src/Mods/SecExp/js/authorityReport.js
@@ -0,0 +1,455 @@
+App.SecExp.authorityReport = function() {
+	let authGrowth = 0;
+	let r = V.useTabs === 0 ? [`<h2>Authority</h2>`] : [];
+	r.push(`<br>Your authority is`);
+
+	if (V.SecExp.core.authority > 19500) {
+		r.push(`nearly absolute. The arcology is yours to command as it pleases you.`);
+	} else if (V.SecExp.core.authority > 15000) {
+		r.push(`extremely high. There's little you cannot do within the walls of your arcology.`);
+	} else if (V.SecExp.core.authority > 10000) {
+		r.push(`high. You command respect and fear in equal measure.`);
+	} else if (V.SecExp.core.authority > 5000) {
+		r.push(`moderate. You command some respect from your citizens.`);
+	} else {
+		r.push(`low. You command no respect or fear from your citizens.`);
+	}
+
+	if (V.PC.career === "wealth") {
+		r.push(`As part of the idle rich, you were used to having obedience coming naturally to you. Now you find it harder to maintain authority over the arcology.`);
+	} else if (V.PC.career === "slaver") {
+		r.push(`Your past as a slaver helps you assert your authority over the arcology.`);
+	} else if (V.PC.career === "escort") {
+		r.push(`Given your past career as an escort, you find it hard to assert your authority over the arcology and its inhabitants.`);
+	} else if (V.PC.career === "servant") {
+		r.push(`Given your past career as a servant, you find it hard to assert your authority over the arcology and its inhabitants.`);
+	} else if (V.PC.career === "BlackHat") {
+		r.push(`Given your past career as a (rather questionable) incursion specialist, you find it hard to assert your authority over the arcology and its inhabitants, despite what you may know about them.`);
+	} else if (V.PC.career === "gang") {
+		r.push(`Given your past life as a gang leader, you find it easier to assert your authority over the arcology and its inhabitants.`);
+	}
+
+	if (["wealth", "escort", "servant", "BlackHat"].includes(V.PC.career)) {
+		authGrowth -= (10 * random(5, 15));
+	} else if (["slaver", "slaver"].includes(V.PC.career)){
+		authGrowth += (10 * random(5, 15));
+	}
+
+	if (V.rep >= 19500) {
+		r.push(`Your legend is so well known that your mere presence commands respect and obedience, increasing your authority.`);
+		authGrowth += (10 * random(10, 20));
+	} else if (V.rep >= 15000) {
+		r.push(`Your reputation is so high that your mere presence commands respect, increasing your authority.`);
+		authGrowth += (10 * random(5, 15));
+	} else if (V.rep >= 10000) {
+		r.push(`Your reputation is high enough that your presence commands some respect, increasing your authority.`);
+		authGrowth += (10 * random(2, 8));
+	}
+
+	if (V.SecExp.core.security >= 90) {
+		r.push(`Your arcology is incredibly secure and your citizens know quite well who to thank, greatly increasing your authority.`);
+		authGrowth += (10 * random(10, 20));
+	} else if (V.SecExp.core.security >= 70) {
+		r.push(`Your arcology is really secure and your citizens know quite well who to thank, increasing your authority.`);
+		authGrowth += (10 * random(5, 15));
+	} else if (V.SecExp.core.security >= 50) {
+		r.push(`Your arcology is quite secure and your citizens know who to thank, increasing your authority.`);
+		authGrowth += (10 * random(2, 8));
+	}
+
+	if (V.SecExp.core.crimeLow >= 90) {
+		r.push(`The all powerful criminal organizations controlling the arcology have a very easy time undermining your authority.`);
+		authGrowth -= (10 * random(10, 20));
+	} else if (V.SecExp.core.crimeLow >= 70) {
+		r.push(`Crime is king in the arcology, powerful criminals have a very easy time undermining your authority.`);
+		authGrowth -= (10 * random(5, 15));
+	} else if (V.SecExp.core.crimeLow >= 50) {
+		r.push(`Criminal organizations have a strong foothold in the arcology, their activities undermine your authority.`);
+		authGrowth -= (10 * random(2, 8));
+	}
+
+	if (V.averageDevotion >= 50 && V.averageTrust >= 50) {
+		r.push(`The high devotion and trust of your slaves speak eloquently of your leadership capabilities, helping your authority grow.`);
+		authGrowth += (5 * ((V.averageDevotion + V.averageTrust) / 10));
+	} else if (V.averageDevotion >= 50) {
+		r.push(`The high devotion of your slaves speaks eloquently of your leadership capabilities, helping your authority grow.`);
+		authGrowth += (5 * (V.averageDevotion / 10));
+	} else if (V.averageTrust >= 50) {
+		r.push(`The high trust of your slaves speaks eloquently of your leadership capabilities, helping your authority grow.`);
+		authGrowth += (5 * (V.averageTrust / 10));
+	}
+
+	if (V.arcologies[0].ownership >= 90) {
+		r.push(`You own so much of the arcology that your authority quickly increases.`);
+	} else if (V.arcologies[0].ownership >= 70) {
+		r.push(`You own a big part of the arcology, causing your authority to increase.`);
+	} else if (V.arcologies[0].ownership >= 50) {
+		r.push(`You own the majority of the arcology, causing your authority to slowly increase.`);
+	} else {
+		r.push(`Your low ownership of the arcology causes your authority to decrease.`);
+	}
+
+	if (V.arcologies[0].ownership >= 50) {
+		authGrowth += (5 * Math.trunc(V.arcologies[0].ownership / 10));
+	} else {
+		authGrowth -= (5 * Math.trunc(V.arcologies[0].ownership / 10));
+	}
+
+	const activeUnits = App.SecExp.battle.activeUnits();
+	if (activeUnits >= 4) {
+		r.push(`Your military is`);
+		if (activeUnits >= 9) {
+			r.push(`massive; commanding so many troops greatly`);
+		} else if (activeUnits >= 7) {
+			r.push(`huge; commanding such a number of soldiers`);
+		} else if (activeUnits >= 4) {
+			r.push(`at a decent size; commanding a small army`);
+		}
+		r.push(`increases your authority.`);
+		authGrowth += (12 * activeUnits);
+	}
+
+	const SF = App.SecExp.assistanceSF('authority');
+	r.push(SF.text); authGrowth += SF.bonus;
+
+	const FSChattel = V.arcologies[0].FSChattelReligionist;
+	if (FSChattel >= 50) {
+		r.push(`Religious organizations have a tight grip on the minds of your residents and their dogma ${FSChattel >= 90 ? 'greatly' : ''} helps your authority grow.`);
+		authGrowth += FSChattel;
+	}
+
+	const FSRestart = V.arcologies[0].FSRestart;
+	if (FSRestart >= 50) {
+		r.push(`The arcology's society is ${FSRestart >= 90 ? 'extremely' : 'very'} stratified. The reliance on the ${V.arcologies[0].FSNeoImperialistLaw2 === 1 ? 'Barons' : 'Societal Elite'} by the lower classes increases ${FSRestart >= 90 ? 'greatly' : ''} your reputation.`);
+		authGrowth += FSRestart;
+	}
+
+	const FSPaternalist = V.arcologies[0].FSPaternalist;
+	if (FSPaternalist >= 50) {
+		r.push(`Your ${FSPaternalist >= 90 ? 'extremely' : ''} paternalistic society has the unfortunate side effects of spreading dangerous ideals in the arcology, ${FSPaternalist >= 90 ? 'greatly' : ''} damaging your authority.`);
+		authGrowth -= Math.clamp(FSPaternalist, 0, 100);
+	}
+
+	const FSNull = V.arcologies[0].FSNull;
+	if (FSNull >= 50) {
+		r.push(`${FSNull >= 90 ? 'Extreme' : 'Mild'} cultural openness allows dangerous ideas to spread in your arcology, ${FSNull >= 90 ? 'greatly' : ''} damaging your reputation.`);
+		authGrowth -= FSNull;
+	}
+
+	if (V.SecExp.buildings.propHub) {
+		if (V.SecExp.buildings.propHub.upgrades.miniTruth >= 1) {
+			r.push(`Your authenticity department works tirelessly to impose your authority in all of the arcology.`);
+			authGrowth += (15 * V.SecExp.buildings.propHub.upgrades.miniTruth);
+		}
+		if (V.SecExp.buildings.propHub.upgrades.secretService >= 1) {
+			r.push(`Your secret services constantly keep under surveillance any potential threat, intervening when necessary. Rumors of the secretive security service and mysterious disappearances make your authority increase.`);
+			authGrowth += (15 * V.SecExp.buildings.propHub.upgrades.secretService);
+		}
+	}
+
+	if (App.SecExp.upkeep.edictsAuth() > 0) {
+		r.push(`Some of your authority is spent maintaining your edicts.`);
+		authGrowth -= App.SecExp.upkeep.edictsAuth();
+	}
+
+	if (authGrowth > 0) {
+		r.push(`This week <span class="green">authority has increased.</span>`);
+	} else if (authGrowth === 0) {
+		r.push(`This week <span class="yellow">authority did not change.</span>`);
+	} else {
+		r.push(`This week <span class="red">authority has decreased.</span>`);
+	}
+	V.SecExp.core.authority = Math.trunc(Math.clamp(V.SecExp.core.authority + authGrowth, 0, 20000));
+
+	if (V.SecExp.settings.rebellion.enabled === 1) {
+		const authorityEffects = function(group) {
+			let text, bonus;
+			if (V.SecExp.core.authority <= 3000) {
+				text = `Your very low authority allows ${group} to think too freely.`;
+				bonus = 30;
+			} else if (V.SecExp.core.authority <= 6000) {
+				text = `Your low authority allows ${group} to think too freely.`;
+				bonus = 25;
+			} else if (V.SecExp.core.authority <= 9000) {
+				text = `Your moderate authority allows ${group} to think a bit too freely.`;
+				bonus = 20;
+			} else if (V.SecExp.core.authority <= 12000) {
+				text = `Your good authority does not allow ${group} to think too freely.`;
+				bonus = 15;
+			} else if (V.SecExp.core.authority <= 15000) {
+				text = `Your high authority does not allow ${group} to think too freely.`;
+				bonus = 10;
+			} else if (V.SecExp.core.authority <= 18000) {
+				text = `Your very high authority does not allow ${group} to think too freely.`;
+				bonus = 5;
+			} else {
+				text = `Your absolute authority does not allow ${group} to have a single free thought.`;
+				bonus = 1;
+			}
+			return {text, bonus};
+		};
+
+		let slave, citizen;
+		const CSratio = V.ACitizens / V.ASlaves;
+		r.push(`<br><br><strong>Slaves security analysis:</strong>`);
+		r.push(authorityEffects('slaves').text); slave = authorityEffects('slaves').bonus;
+		if (CSratio <= 0.4) {
+			r.push(`There are a lot more slaves than citizens, making some doubt their masters are strong enough to stop them.`);
+			slave += 30;
+		} else if (CSratio <= 0.6) {
+			r.push(`There are a lot more slaves than citizens, making some doubt their masters are strong enough to stop them.`);
+			slave += 25;
+		} else if (CSratio <= 0.8) {
+			r.push(`There are more slaves than citizens, making some doubt their masters are strong enough to stop them.`);
+			slave += 20;
+		} else if (CSratio <= 1) {
+			r.push(`There are more slaves than citizens, making some doubt their masters are strong enough to stop them.`);
+			slave += 15;
+		} else if (CSratio <= 1.5) {
+			r.push(`There are fewer slaves than citizens, making some doubt they would be strong enough to defeat their masters.`);
+			slave += 10;
+		} else if (CSratio >= 3) {
+			r.push(`There are fewer slaves than citizens, making some doubt they would be strong enough to defeat their masters.`);
+			slave -= 5;
+		} else {
+			r.push(`Citizen and slave populations are sufficiently balanced not to cause problems either way.`);
+			slave -= 1;
+		}
+		if (V.SecExp.core.security <= 10) {
+			r.push(`The very low security of the arcology leaves free space for slaves to organize and agitate.`);
+			slave += 30;
+		} else if (V.SecExp.core.security <= 30) {
+			r.push(`The low security of the arcology leaves free space for slaves to organize and agitate.`);
+			slave += 20;
+		} else if (V.SecExp.core.security <= 60) {
+			r.push(`The moderate security of the arcology does not allow free space for slaves to organize and agitate.`);
+			slave += 10;
+		} else if (V.SecExp.core.security >= 90) {
+			r.push(`The high security of the arcology does not allow free space for slaves to organize and agitate.`);
+			slave -= 5;
+		} else {
+			r.push(`The high security of the arcology does not allow free space for slaves to organize and agitate.`);
+			slave -= 1;
+		}
+		if (V.arcologies[0].FSDegradationist !== "unset") {
+			r.push(`Many slaves are so disgusted by your degradationist society, that they are willing to rise up against their masters to escape.`);
+			slave += 30;
+		} else if (V.arcologies[0].FSPaternalist !== "unset") {
+			r.push(`Many slaves are content to live in your paternalist society.`);
+			slave -= 5;
+		} else {
+			slave += 5;
+		}
+		if (V.arcologies[0].FSRestart !== "unset") {
+			r.push(`Many slaves are worried by your eugenics projects and some are pushed towards radicalization.`);
+			slave += 30;
+		} else if (V.arcologies[0].FSRepopulationFocus !== "unset") {
+			r.push(`Many slaves are pleasantly happy of your repopulation effort, affording them the freedom to reproduce.`);
+			slave -= 5;
+		} else {
+			slave += 5;
+		}
+
+		r.push(`<br><br><strong>Citizens security analysis:</strong>`);
+		r.push(authorityEffects('your citizens').text); citizen = authorityEffects('your citizens').bonus;
+		if (V.SecExp.core.crimeLow >= 90) {
+			r.push(`The very high crime level of the arcology breeds extreme discontent between your citizens.`);
+			citizen += 30;
+		} else if (V.SecExp.core.crimeLow >= 60) {
+			r.push(`The high crime level of the arcology breeds high discontent between your citizens.`);
+			citizen += 15;
+		} else if (V.SecExp.core.crimeLow >= 30) {
+			r.push(`The low crime level of the arcology leaves your citizens happy and satisfied.`);
+			citizen += 5;
+		} else {
+			r.push(`The very low crime level of the arcology leaves your citizens happy and satisfied.`);
+			citizen -= 5;
+		}
+
+		const militia = V.SecExp.edicts.defense.militia;
+		const militarizedSociety = [
+			'FSRomanRevivalist', 'FSAztecRevivalist', 'FSEgyptianRevivalist',
+			'FSEdoRevivalist', 'FSArabianRevivalist', 'FSChineseRevivalist',
+			'FSNeoImperialist'].every((FS) => V.arcologies[0][FS] === "unset");
+		if (militia >= 1) {
+			if (militia >= 4) {
+				r.push(`${!militarizedSociety ? 'Many' : 'Some'} of your citizens are offended by your ${militia === 5 ? 'extreme' : ''} militarization of the arcology's society.`);
+				if (!militarizedSociety) {
+					citizen += militia === 5 ? 20 : 15;
+				} else {
+					citizen += militia === 5 ? 10 : 5;
+				}
+			} else {
+				citizen += !militarizedSociety ? 10 : -5;
+			}
+		}
+		if (V.arcologies[0].FSNull !== "unset") {
+			r.push(`Many of your more conservative citizens do not enjoy the cultural freedom you afford the residents of the arcology.`);
+			citizen += either(20, 30);
+		}
+		if (V.arcologies[0].FSRestart !== "unset") {
+			if (CSratio > 2 || CSratio > 1) {
+				r.push(`Your citizens are not happy with the ${CSratio > 2 ? 'noticeable' : ''} lack of slaves compared to their numbers.`);
+				citizen += CSratio > 2 ? 20 : 15;
+			} else if (CSratio < 0.5) {
+				citizen -= 5;
+			}
+		} else if (V.arcologies[0].FSRepopulationFocus !== "unset") {
+			if (CSratio < 0.5 || CSratio < 1) {
+				r.push(`Your citizens are not happy about being outbred by the slaves of the arcology.`);
+				citizen += CSratio < 0.5 ? 20 : 15;
+			} else if (CSratio > 1.4) {
+				citizen += 5;
+			}
+		}
+
+		Math.clamp(slave, 0, 95); Math.clamp(citizen, 0, 95); // rolls to see if event happens - there's always a min 5% chance nothing happens
+		const roll = random(1, slave + citizen);
+		slave = Math.trunc(slave * V.SecExp.settings.rebellion.speed); citizen = Math.trunc(citizen * V.SecExp.settings.rebellion.speed);
+		if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.brainImplant === 106) {
+			slave /= 2; citizen /= 2;
+		}
+
+		let rebellionEventFires;
+		if (roll <= slave && random(1, 100) < slave) {
+			rebellionEventFires = 'slave';
+		} else if (roll > slave && random(1, 100) < citizen) {
+			rebellionEventFires = 'citizen';
+		}
+
+		const oldTension = V.SecExp.rebellions.tension;
+		if (rebellionEventFires) { // if there is an advancement selects a random mini event
+			let miniEvent, rand;
+			V.SecExp.rebellions[rebellionEventFires + 'Progress'] += random(1, 5);
+			if (V.SecExp.rebellions.tension !== 0) {
+				V.SecExp.rebellions[rebellionEventFires + 'Progress'] *= Math.trunc(random(1, 5) * (V.SecExp.rebellions.tension / 100) * 10); // progress scales with tension
+			}
+			if (V.SecExp.rebellions.tension <= 33) {
+				miniEvent = rebellionEventFires === 'slave' ? 1 : 4;
+				V.SecExp.rebellions.tension += random(1, 5);
+				rand = random(0, 6);
+			} else if (V.SecExp.rebellions.tension <= 66) {
+				miniEvent = rebellionEventFires === 'slave' ? 2 : 5;
+				V.SecExp.rebellions.tension += random(5, 10);
+				rand = random(0, 5);
+			} else {
+				miniEvent = rebellionEventFires === 'slave' ? 3 : 6;
+				V.SecExp.rebellions.tension += random(10, 15);
+				rand = random(0, 4);
+			}
+
+			r.push(`<br><br>`);
+			const {HeU, heU, hisU, himU, himselfU} = getNonlocalPronouns(V.seeDicks).appendSuffix("U");
+			switch (miniEvent) {
+				case 1:
+					if (rand === 0) {
+						r.push(`This week several slaves were found plotting the death of their master. They were quickly dealt with, but their owner's choice of punishment did little to calm tensions in the arcology.`);
+					} else if (rand === 1) {
+						r.push(`This week a large group of slaves attempted to escape. Several were recaptured, but others were deemed too dangerous and were shot on sight. The unfortunate circumstances raised the disapproval of many citizens, either because of the waste of good slaves or the brutality with which the operation was carried. With a bit of luck, however, the incident will be soon forgotten.`);
+					} else if (rand === 2) {
+						r.push(`This week books of unknown origin and dangerous content were found in the possession of several slaves. They were mostly sociopolitical treaties, making it clear that the intent of the ones responsible was to fan the fire of rebellion. The books were quickly collected and archived, hopefully this affair will not have lasting consequences.`);
+					} else if (rand === 3) {
+						r.push(`This week a citizen was caught giving refuge to an escaped slave. He was not able to pay for the value of the stolen goods, so he was processed as the case required and the slave returned to their rightful master. Many questions however remain without answers.`);
+					} else if (rand === 4) {
+						r.push(`This week a member of a well known anti-slavery group was caught trying to infiltrate the arcology. During the capture attempt shots were fired and several guards were injured, and in the end the fugitive unfortunately managed to escape. Reports indicate several slaves helped the criminal, some going as far as using themselves as shields against the bullets of the security drones.`);
+					} else if (rand === 5) {
+						r.push(`This week a slave was caught attempting to sabotage a machine in one of the factories. ${HeU} explained ${hisU} action as "trying to defend ${himselfU} from a dangerous machine". Reports confirmed that the apparatus is indeed quite deadly, having killed several slaves since it was installed, but the expert way ${heU} handled the sabotage leaves open the possibility of a deliberate plan or even external help.`);
+					} else {
+						r.push(`This week a slave was found dead in one of the sewer tunnels. It seems ${heU} was stabbed repeatedly with a sharp object. ${HeU} was fairly famous for ${hisU} capabilities as a slave trainer; ${hisU} old master spent not an insignificant amount of money trying to find ${himU} once he realized ${heU} was missing. The episode might have been a simple mugging gone wrong, but ${hisU} activities as a slave breaker might have played a role in ${hisU} homicide.`);
+					}
+					break;
+				case 2:
+					if (rand === 0) {
+						r.push(`This week some strange reports came in: it seems some assemblies of slaves were observed several nights in a row. The slaves were traced and their masters notified, but many suspect there may be something deeper than a few slaves congregating in the night.`);
+					} else if (rand === 1) {
+						r.push(`This week an underground railroad was discovered. The rebels did not go down without a fight, but in the end your ${V.mercenaries >= 1 ? 'mercenaries' : 'security drones'} managed to destroy the old tunnels they were using to ship out slaves out of the arcology.`);
+					} else if (rand === 2) {
+						r.push(`This week a famous citizen was assaulted and brutally murdered by his slaves. The ones responsible were apprehended and dealt with easily enough, but the mere fact something like this could have happened is concerning. Those slaves had to be aware of their certain doom.`);
+					} else if (rand === 3) {
+						r.push(`This week a group of slavers entering the arcology was assaulted. Many reported heavy injuries, but fortunately there were no casualties. The attackers were disguised, but the security systems already identified several slaves who were likely part of the group, based on camera feeds.`);
+					} else if (rand === 4) {
+						r.push(`This week the waterways were found infected by a virulent pathogen. The cause was later found to be a diseased slave that died while in the maintenance tunnels. It's not clear if the slave was there because of orders given to ${himU} or if ${heU} was trying to escape.`);
+					} else {
+						r.push(`This week a sleeper cell of a famous anti slavery organization was discovered in the low levels of the arcology. The group, however, was aware of the coming security forces and retreated before they could be dealt with.`);
+					}
+					break;
+				case 3:
+					if (rand === 0) {
+						r.push(`This week a group of slaves took control of one of the manufacturing plants and barricaded themselves inside. It took several days of negotiations and skirmishes to finally end this little insurrection. Many of the slaves involved will be executed in the next few days.`);
+					} else if (rand === 1) {
+						r.push(`This week a number of shops were burned to the ground by rioting slaves and sympathetic citizens. It took considerable effort for the security forces to take control of the situation. Harsh punishment is required and scheduled for the instigators.`);
+					} else if (rand === 2) {
+						r.push(`This week a mass escape attempt was barely stopped before becoming a catastrophe. Many citizens were trampled by the desperate horde of slaves. It will take some time to restore the streets involved to working order.`);
+					} else if (rand === 3) {
+						r.push(`This week a number of riots inflamed the arcology. Many slaves took violent actions against citizens and security personnel. The number of victims keeps getting higher as still now the last sparks of revolt are still active.`);
+					}
+					break;
+				case 4:
+					if (rand === 0) {
+						r.push(`This week a citizen refused to pay rent, claiming ideological opposition to the arcology's ownership policies. He was quickly dealt with, but his words might not have fallen silent yet.`);
+					} else if (rand === 1) {
+						r.push(`This week books of unknown origin and dangerous content were found in the possession of several citizens. They were mostly sociopolitical treaties, making it clear that the intent of the ones responsible was to fan the fire of rebellion. Most of them were bought and archived, but a few are still circling amongst the citizens of the arcology.`);
+					} else if (rand === 2) {
+						r.push(`This week a citizen was caught giving refuge to other citizens, who would be liable to be enslaved because of their debts. The situation was quickly resolved, but the misplaced generosity of that citizen might have inflamed a few souls.`);
+					} else if (rand === 3) {
+						r.push(`This week a citizen died in one of the factories. His death sparked some outrage, even some talk of protests against the owners of the factory, but things seem to have calmed down for now.`);
+					} else if (rand === 4) {
+						r.push(`This week a citizen refused to be evicted from his house. After some negotiations the man was forcibly removed from the property by your security forces. Unfortunately the forced entry caused some damage to the building.`);
+					} else if (rand === 5) {
+						r.push(`This week a citizen refused to be enslaved as his contract established. With an impressive display of his rhetoric capabilities he managed to gather a small crowd agreeing with his position. The impromptu assembly was promptly disrupted by the drones.`);
+					} else {
+						r.push(`This week a security drone was found disabled and stripped of important electronic components. It seems the act was not dictated by greed, as the most precious parts of the drone were left on the machine, but rather to cover up something that the drone saw.`);
+					}
+					break;
+				case 5:
+					if (rand === 0) {
+						r.push(`This week a factory was subject to a strike by a group of citizens protesting against the owner. They were promptly arrested and the factory returned to its rightful proprietor by your security department.`);
+					} else if (rand === 1) {
+						r.push(`This week a group of citizens organized a protest against the systemic enslavement of the citizens of the arcology. Their little parade gathered a surprisingly large crowd, but it was nonetheless quickly suppressed by your forces.`);
+					} else if (rand === 2) {
+						r.push(`This week the security department registered the formation of several assemblies of citizens, whose purpose seems to be political in nature. For now no further steps were taken, but it's a worrying sign of further political opposition within the arcology.`);
+					} else if (rand === 3) {
+						r.push(`This week there was a protest against one of the wealthiest citizen of the arcology. Many criticize his near monopoly. Supporters of the citizen met the protesters on the streets and it was just thanks to the intervention of the security drones that violence was avoided.`);
+					} else if (rand === 4) {
+						r.push(`This week several cameras were sabotaged and in many cases damaged beyond repair. A group of anonymous citizens claims to be responsible; their motivation is apparently the excessive surveillance in the arcology and their attack a response to the breach of their privacy.`);
+					} else {
+						r.push(`This week several citizens barricaded themselves in a private brothel. It seems their intention is to protest against the use of ex-citizens in the sex trade, claiming that such a position is unfitting for them. The problem was quickly resolved with the intervention of the security department.`);
+					}
+					break;
+				case 6:
+					if (rand === 0) {
+						r.push(`This week the arcology was shaken by a number of strikes throughout the manufacturing levels. Many lament the predatory nature of Free Cities society, many other just want to cause damage to their perceived oppressors. It was a significant effort for the security department to stop all protests.`);
+					} else if (rand === 1) {
+						r.push(`This week several factories were set aflame by their workers. The security department worked day and night to control the fire and apprehend the criminals behind the act. Many are known dissidents, but there are a fair few new faces within them. This is a worrying sign.`);
+					} else if (rand === 2) {
+						r.push(`This week numerous riots exploded all over the arcology. Many citizens took to the streets to protest against the arcology owner and its supporters. The security forces slowly managed to stop the rioters, with no small amount of trouble and only through generous use of violence.`);
+					} else if (rand === 3) {
+						r.push(`This week a massive protest of citizens and slaves gathered just outside the penthouse. The crowd was dispersed only after several hours. There were several victims from both sides and no shortage of injured.`);
+					}
+					break;
+			}
+			V.SecExp.rebellions.tension = Math.clamp(V.SecExp.rebellions.tension, 0, 100);
+		} else if (V.SecExp.rebellions.tension > 0) { // otherwise SecExp.rebellions.tension decays
+			r.push(`<br><br><strong>Tension</strong>:`);
+			if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.upgrades.freeMedia >= 1) {
+				r.push(`The guaranteed free media access you offer does wonders to lower tensions in the arcology.`);
+				V.SecExp.rebellions.tension = Math.trunc(Math.clamp(V.SecExp.rebellions.tension - V.SecExp.buildings.riotCenter.upgrades.freeMedia / 2, 0, 100));
+			}
+			r.push(`In the absence of noteworthy events, tensions in the arcology are able to relax.`);
+			V.SecExp.rebellions.tension = Math.trunc(Math.clamp(V.SecExp.rebellions.tension * 0.97, 0, 100));
+		}
+
+		if (V.SecExp.rebellions.tension < oldTension) {
+			r.push(`<br><br>This week <span class="green">tensions relaxed.</span><br>`);
+		} else if (V.SecExp.rebellions.tension === oldTension && V.SecExp.rebellions.tension !== 0) {
+			r.push(`<br><br>This week <span class="yellow">tensions did not change.</span><br>`);
+		} else if (V.SecExp.rebellions.tension > oldTension) {
+			r.push(`<br><br>This week <span class="red">tension rose</span> and <span class="red">${rebellionEventFires} malcontent increased.</span><br>`);
+		} else if (!Number.isInteger(V.SecExp.rebellions.tension)) {
+			r.push(`<br><br><span class="red">Error: tension is outside accepted range.</span><br>`);
+		}
+
+		App.SecExp.generator.rebellion(); // rolls for rebellions
+	}
+	return r.join(" ");
+};
diff --git a/src/Mods/SecExp/js/buildingsJS.js b/src/Mods/SecExp/js/buildingsJS.js
index 76b52a16408e9b86c220d9763471bd03f171411e..ad1f3d367ca84845cb1577bfb0d2b24f98b94c7f 100644
--- a/src/Mods/SecExp/js/buildingsJS.js
+++ b/src/Mods/SecExp/js/buildingsJS.js
@@ -80,7 +80,7 @@ App.SecExp.weapManuUpgrade = (function() {
 				break;
 			case 7:
 				Object.assign(o, {
-				dec: `a variant of the stimulant cocktail that the ${V.SF.Lower} created`, type: "hp and morale",
+					dec: `a variant of the stimulant cocktail that the ${V.SF.Lower} created`, type: "hp and morale",
 					cost: 300000
 				});
 				break;
diff --git a/src/Mods/SecExp/js/secExp.js b/src/Mods/SecExp/js/secExp.js
index 8bd6a0d9ae605029d5a3dad95f0942a18ff1b231..9592dbabc15106671b00b2a03ffda7b307d00ddf 100644
--- a/src/Mods/SecExp/js/secExp.js
+++ b/src/Mods/SecExp/js/secExp.js
@@ -1,10 +1,252 @@
+App.SecExp.generator = (function() {
+	return {
+		attack,
+		rebellion,
+	};
+
+	function shared() {
+		V.SecExp.war = V.SecExp.war || {};
+		V.SecExp.war.commander = "assistant";
+		V.SecExp.war.losses = 0;
+		V.SecExp.war.attacker = {losses: 0};
+	}
+
+	function attack() {
+		let attackChance = 0; // attackChance value is the chance out of 100 of an attack happening this week
+		// attacks are deactivated if the arcology is in the middle of the ocean, security drones are not around yet, there is not a rebellion this week or the last attack/rebellion happened within 3 weeks
+		if (V.terrain !== "oceanic" && V.arcologyUpgrade.drones === 1 && V.citizenRebellion === 0 && V.slaveRebellion === 0 && V.SecExp.battles.lastEncounterWeeks > 3 && V.SecExp.rebellions.lastEncounterWeeks > 3) {
+			if (V.week < 30) {
+				attackChance = 5;
+			} else if (V.week < 60) {
+				attackChance = 8;
+			} else if (V.week < 90) {
+				attackChance = 12;
+			} else if (V.week < 120) {
+				attackChance = 16;
+			} else {
+				attackChance = 20;
+			}
+			if (V.SecExp.battles.victories + V.SecExp.battles.losses >= 0) {
+				attackChance = 25;
+			}
+
+			if (V.SecExp.battles.lastEncounterWeeks >= 10) {
+				attackChance += V.SecExp.battles.lastEncounterWeeks/2; // attackChance += 5;
+			}
+			/* if (V.terrain === "oceanic") {
+				attackChance -= 10;
+			} */
+			attackChance *= V.SecExp.settings.battle.frequency; // battle frequency
+		}
+
+		if (V.SecExp.settings.battle.force === 1 && V.SecExp.settings.rebellion.force === 0) {
+			attackChance = 100;
+		}
+
+		if (random(1, 100) > attackChance) { // Rolls to see if attack happens this week
+			V.SecExp.battles.lastEncounterWeeks++;
+		} else {
+			let type, terrain, troops, equip = 0, L = 0;
+			V.attackThisWeek = 1;
+			V.SecExp.battles.lastEncounterWeeks = 0;
+			let raider = 25, oldWorld = 25, freeCity = 25, free = 25; // type is the chance out of 100 of an attack of that type happening
+			// the old world attracted by "degenerate" future societies
+			const setA = [
+				'FSRomanRevivalist', 'FSEdoRevivalist', 'FSArabianRevivalist',
+				'FSChineseRevivalist', 'FSEgyptianRevivalist', 'FSAztecRevivalist'];
+			const setB = [
+				'FSRepopulationFocus', 'FSGenderRadicalist', 'FSPastoralist',
+				'FSChattelReligionist', 'FSNeoImperialist'];
+			const resultA = setA.some((FS) => V.arcologies[0][FS] !== "unset");
+			const resultB = setB.some((FS) => V.arcologies[0][FS] !== "unset");
+			if (resultA && resultB) {
+				oldWorld += 15;
+				raider -= 5;
+				freeCity -= 5;
+				free -= 5;
+			} else if (resultA || resultB) {
+				oldWorld += 24;
+				raider -= 8;
+				freeCity -= 8;
+				free -= 8;
+			}
+			// freedom fighters attracted by high slave/citizen ratio
+			if (V.ASlaves > V.ACitizens * 2) {
+				oldWorld -= 8;
+				raider -= 8;
+				freeCity -= 8;
+				free += 24;
+			} else if (V.ASlaves > V.ACitizens * 1.2 || V.arcologies[0].FSDegradationist !== "unset") {
+				oldWorld -= 5;
+				raider -= 5;
+				freeCity -= 5;
+				free += 15;
+			}
+			// free Cities attracted by high prosperity
+			if (V.arcologies[0].prosperity >= 10 && V.arcologies[0].prosperity < 20) {
+				oldWorld -= 5;
+				raider -= 5;
+				freeCity += 15;
+				free -= 5;
+			} else if (V.arcologies[0].prosperity >= 20) {
+				oldWorld -= 8;
+				raider -= 8;
+				freeCity += 24;
+				free -= 8;
+			}
+			// raiders are attracted by low security
+			if (V.SecExp.core.security <= 50) {
+				oldWorld -= 5;
+				raider += 15;
+				freeCity -= 5;
+				free -= 5;
+			} else if (V.SecExp.core.security <= 25) {
+				oldWorld -= 8;
+				raider += 24;
+				freeCity -= 8;
+				free -= 8;
+			}
+
+			const roll = random(1, 100); // makes the actual roll
+			if (roll <= raider) {
+				type = "raiders";
+				troops = random(40, 80); L = 1;
+			} else if (roll <= raider + oldWorld) {
+				type = "old world";
+				troops = random(25, 50);
+			} else if (roll <= raider + oldWorld + freeCity) {
+				type = "free city";
+				troops = random(20, 40);
+			} else if (roll <= raider + oldWorld + freeCity + free) {
+				type = "freedom fighters";
+				troops = random(30, 60);
+			}
+
+			if (V.terrain === "urban") {
+				terrain = either("outskirts", "urban", "wasteland");
+			} else if (V.terrain === "rural") {
+				terrain = either("hills", "outskirts", "rural", "wasteland");
+			} else if (V.terrain === "ravine") {
+				terrain = either("hills", "mountains", "outskirts", "wasteland");
+			} else if (V.terrain === "marine") {
+				terrain = either("coast", "hills", "outskirts", "wasteland");
+			// } else if (V.terrain === "oceanic") {
+				// terrain = either("international waters", "an underwater cave", "a sunken ship", "an island");
+			} else {
+				terrain = "error";
+			}
+
+			if (V.week < 30) {
+				troops *= random(1, 2); // troops *= Math.trunc(random( (1*(1.01+(V.week/100))), (2*(1.01+(V.week/100))) ))) {
+			}
+			if (V.week < 60) {
+				troops *= random(1, 3); // troops *= Math.trunc(random( (1*(1.01+(V.week/200))), (3*(1.01+(V.week/200))) ))) {
+				equip = random(0, 1);
+			} else if (V.week < 90) {
+				troops *= random(2, 3); // troops *= Math.trunc(random( (2*(1.01+(V.week/300))), (3*(1.01+(V.week/300))) ))) {
+				equip = random(0, 3-L); // "raiders" equip = random(0,2)) {
+			} else if (V.week < 120) {
+				troops *= random(2, 4); // troops *= Math.trunc(random( (2*(1.01+(V.week/400))), (4*(1.01+(V.week/400))) ))) {
+				equip = random(1-L, 3); // "raiders" equip = random(0,3)) {
+			} else {
+				troops *= random(3, 5);
+				equip = random(2-L, 4-L); // "raiders" equip = random(1,3)) {
+			}
+
+			if (V.SecExp.settings.battle.major.enabled === 1) { // major battles have a 50% chance of firing after week 120
+				if ((V.week >= 120 && random(1, 100) >= 50) || V.SecExp.settings.battle.major.force === 1) {
+					V.majorBattle = 1;
+					const sfActive = V.SF.Toggle && V.SF.Active >= 1;
+					troops *= sfActive ? random(4, 6) : random(2, 3);
+					equip = sfActive ? either(3, 4) : either(2, 3, 4);
+				}
+			}
+
+			shared();
+			V.SecExp.war.attacker.troops = troops;
+			V.SecExp.war.attacker.equip = equip;
+			V.SecExp.war.attacker.type = type;
+			V.SecExp.war.terrain = terrain;
+			V.SecExp.war.deploySF = 0;
+			V.SecExp.war.saveValid = 0;
+			V.SecExp.war.tacticsSuccessful = 0;
+			V.SecExp.war.chosenTactic = either("Bait and Bleed", "Blitzkrieg", "Choke Points", "Defense In Depth", "Guerrilla", "Human Wave", "Interior Lines", "Pincer Maneuver");
+			V.SecExp.war.estimatedMen = normalRandInt(troops, troops * (4 - App.SecExp.battle.recon()) * 0.05);
+			V.SecExp.war.expectedEquip = normalRandInt(equip, (4 - App.SecExp.battle.recon()) * 0.25);
+		}
+	}
+
+	function rebellion() {
+		let type;
+		if (V.SecExp.rebellions.slaveProgress >= 100) {
+			if (random(1, 100) <= 80) {	// 80% of firing a rebellion once progress is at 100
+				type = V.slaveRebellion = 1;
+				V.SecExp.rebellions.slaveProgress = 0;
+				V.SecExp.rebellions.citizenProgress *= 0.2;
+			} else {
+				V.SecExp.rebellions.slaveProgress = 100;
+			}
+		} else if (V.SecExp.rebellions.citizenProgress >= 100) {
+			if (random(1, 100) <= 80) {
+				type = V.citizenRebellion = 1;
+				V.SecExp.rebellions.citizenProgress = 0;
+				V.SecExp.rebellions.slaveProgress *= 0.2;
+			} else {
+				V.SecExp.rebellions.citizenProgress = 100;
+			}
+		}
+
+		if (V.SecExp.settings.rebellion.force === 1 && V.foughtThisWeek === 0) {
+			type = random(1, 100) <= 50 ? V.slaveRebellion = 1 : V.citizenRebellion = 1;
+		}
+
+		if (!type) {
+			V.SecExp.rebellions.lastEncounterWeeks++;
+		} else {
+			const isSlaveRebellion = V.slaveRebellion === 1;
+			let weekMod;
+			if (V.week <= 30) {
+				weekMod = 0.75 + (0.01+(V.week/200));
+			} else if (V.week <= 60) {
+				weekMod = 1 + (0.01+(V.week/300));
+			} else if (V.week <= 90) {
+				weekMod = 1.25 + (0.01+(V.week/400));
+			} else if (V.week <= 120) {
+				weekMod = 1.50 + (0.01+(V.week/500));
+			} else {
+				weekMod = 1.75;
+			}
+
+			shared();
+			V.SecExp.rebellions.lastEncounterWeeks = 0;
+			const authFactor = Math.clamp(1 - (V.SecExp.core.authority / 20000), 0.4, 0.6);
+			const repFactor = Math.clamp(V.rep / 20000, 0.4, 0.6);
+			const rebelPercent = 0.3 * authFactor;
+			const irregularPercent = 0.2 * repFactor;
+
+			const isDisloyal = (squad) => ((squad.loyalty < 10 && random(1, 100) <= 70) || (squad.loyalty < 33 && random(1, 100) <= 30) || (squad.loyalty < 66 && random(1, 100) <= 10));
+			const baseValue = Math.trunc((isSlaveRebellion ? V.ASlaves : V.ACitizens) * rebelPercent * weekMod) + random(-100, 100);
+			const highestValue = isSlaveRebellion ? V.ASlaves : V.ACitizens;
+			V.SecExp.war.attacker.troops = Math.clamp(baseValue, 50, highestValue);
+			V.SecExp.war.attacker.equip = Math.clamp(V.SecExp.edicts.weaponsLaw + random((isSlaveRebellion ? -2 : -1), 1), 0, 4);
+			V.SecExp.war.irregulars = Math.clamp(Math.trunc(V.ACitizens * irregularPercent * weekMod) + random(-100, 100), 50, V.ACitizens);
+			V.SecExp.war.engageRule = 0;
+			V.SecExp.war.rebellingID = [].concat(
+				V.slaveUnits.filter(isDisloyal).map(u => u.ID),
+				V.militiaUnits.filter(isDisloyal).map(u => u.ID),
+				V.mercUnits.filter(isDisloyal).map(u => u.ID)
+			);
+		}
+	}
+})();
+
 /**
  * Returns the effect (if any) the Special Force provides.
  * @param {string} [report] which report is this function being called from.
  * @param {string} [section=''] which sub section (if any) is this function being called from.
  * @returns {{text:string, bonus:number}}
  */
-App.SecExp.SF_effect = function(report, section = '') {
+App.SecExp.assistanceSF = function(report, section = '') {
 	const size = V.SF.Toggle && V.SF.Active >= 1 ? App.SF.upgrades.total() : 0;
 	let r = ``, bonus = 0;
 	if (size > 10) {
@@ -187,26 +429,7 @@ App.SecExp.generalInit = function() {
 			reactor: 0,
 			arc: 0
 		},
-		units: {
-			bots: {},
-			slaves: {
-				created: 0,
-				casualties: 0,
-				squads: []
-			},
-			militia: {
-				created: 0,
-				free: 0,
-				casualties: 0,
-				squads: []
-			},
-			mercs: {
-				created: 0,
-				free: 0,
-				casualties: 0,
-				squads: []
-			}
-		},
+		units: {},
 		*/
 		edicts: {
 			alternativeRents: 0,
@@ -395,7 +618,7 @@ App.SecExp.battle = (function() {
 			if (V.secBots.isDeployed > 0) {
 				bots++;
 			}
-			if (V.SF.Toggle && V.SF.Active >= 1 && V.SFIntervention) {
+			if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.deploySF) {
 				init++;
 			}
 
@@ -409,13 +632,13 @@ App.SecExp.battle = (function() {
 			if (V.SF.Toggle && V.SF.Active >= 1) {
 				init++;
 			}
-			if (V.irregulars > 0) {
+			if (V.SecExp.war.irregulars > 0) {
 				militiaC++;
 			}
 
-			militiaC += V.militiaUnits.filter((u) => u.active === 1 && !V.rebellingID.includes(u.ID)).length;
-			slavesC += V.slaveUnits.filter((u) => u.active === 1 && !V.rebellingID.includes(u.ID)).length;
-			mercsC += V.mercUnits.filter((u) => u.active === 1 && !V.rebellingID.includes(u.ID)).length;
+			militiaC += V.militiaUnits.filter((u) => u.active === 1 && !V.SecExp.war.rebellingID.includes(u.ID)).length;
+			slavesC += V.slaveUnits.filter((u) => u.active === 1 && !V.SecExp.war.rebellingID.includes(u.ID)).length;
+			mercsC += V.mercUnits.filter((u) => u.active === 1 && !V.SecExp.war.rebellingID.includes(u.ID)).length;
 		}
 
 		if (input === '') {
@@ -454,17 +677,17 @@ App.SecExp.battle = (function() {
 				troops += V.secBots.troops;
 			}
 			countHumanTroops((u) => u.isDeployed === 1);
-			if (V.SF.Toggle && V.SF.Active >= 1 && V.SFIntervention) {
+			if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.deploySF) {
 				troops += App.SecExp.troopsFromSF();
 			}
 		} else {
-			if (V.irregulars > 0) {
-				troops += V.irregulars;
+			if (V.SecExp.war.irregulars > 0) {
+				troops += V.SecExp.war.irregulars;
 			}
 			if (V.secBots.active === 1) {
 				troops += V.secBots.troops;
 			}
-			countHumanTroops((u) => u.active === 1 && !V.rebellingID.includes(u.ID));
+			countHumanTroops((u) => u.active === 1 && !V.SecExp.war.rebellingID.includes(u.ID));
 			if (V.SF.Toggle && V.SF.Active >= 1) {
 				troops += App.SecExp.troopsFromSF();
 			}
@@ -564,17 +787,17 @@ App.SecExp.battle = (function() {
 	function bribeCost() {
 		let cost; const baseBribePerAttacker = 5;
 		if (V.week <= 30) {
-			cost = 5000 + baseBribePerAttacker * V.attackTroops;
+			cost = 5000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		} else if (V.week <= 40) {
-			cost = 10000 + baseBribePerAttacker * V.attackTroops;
+			cost = 10000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		} else if (V.week <= 50) {
-			cost = 15000 + baseBribePerAttacker * V.attackTroops;
+			cost = 15000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		} else if (V.week <= 60) {
-			cost = 20000 + baseBribePerAttacker * V.attackTroops;
+			cost = 20000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		} else if (V.week <= 70) {
-			cost = 25000 + baseBribePerAttacker * V.attackTroops;
+			cost = 25000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		} else {
-			cost = 30000 + baseBribePerAttacker * V.attackTroops;
+			cost = 30000 + baseBribePerAttacker * V.SecExp.war.attacker.troops;
 		}
 		cost *= V.majorBattle > 0 ? 3 : 1;
 		return Math.trunc(Math.clamp(cost, 0, 1000000));
@@ -727,7 +950,7 @@ App.SecExp.inflictBattleWound = (function() {
 		}
 	}
 
-	/** Inflicts a wound upon a slave during a battle.  Returns the wound type from the wound table (see above) so it can be described.
+	/** Inflicts a wound upon a slave during a battle. Returns the wound type from the wound table (see above) so it can be described.
 	 * @param {App.Entity.SlaveState} slave
 	 * @returns {string}
 	 */
diff --git a/src/Mods/SecExp/js/secExpBC.js b/src/Mods/SecExp/js/secExpBC.js
index 2fb8117f3f580e899c1846909ce1c711b0ebeb5b..ab7ac1ef1ae6e4b4cfe4b9761eefe34b254a8141 100644
--- a/src/Mods/SecExp/js/secExpBC.js
+++ b/src/Mods/SecExp/js/secExpBC.js
@@ -33,7 +33,7 @@ App.SecExp.generalBC = function() {
 		V.SecExp.edicts.defense = V.SecExp.edicts.defense || {};
 		V.SecExp.edicts.defense.soldierWages = V.SecExp.edicts.defense.soldierWages || V.soldierWages || 1;
 		V.SecExp.edicts.defense.slavesOfficers = V.SecExp.edicts.defense.slavesOfficers || V.slavesOfficers || 0;
-		V.SecExp.edicts.defense.discountMercenaries = V.SecExp.edicts.defense.discountMercenaries  || V.discountMercenaries || 0;
+		V.SecExp.edicts.defense.discountMercenaries = V.SecExp.edicts.defense.discountMercenaries || V.discountMercenaries || 0;
 
 		V.SecExp.edicts.defense.militia = V.SecExp.edicts.defense.militia || 0;
 		if (V.militiaFounded) {
diff --git a/src/Mods/SecExp/js/securityReport.js b/src/Mods/SecExp/js/securityReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f6552138319cda81673e2727c74c8a975e0efd8
--- /dev/null
+++ b/src/Mods/SecExp/js/securityReport.js
@@ -0,0 +1,519 @@
+App.SecExp.securityReport = function() {
+	let immigration = 0, emigration = 0, secGrowth = 0, crimeGrowth = 0;
+	if (V.ACitizens > V.oldACitizens) {
+		immigration = V.ACitizens - V.oldACitizens;
+	} else {
+		emigration = V.oldACitizens - V.ACitizens; // takes into account citizens leaving and those getting enslaved
+	}
+	const activeUnits = App.SecExp.battle.activeUnits();
+	/** @type {(string|HTMLElement|DocumentFragment)[]} */
+	let r = V.useTabs === 0 ? ['<h2>Security</h2>'] : [];
+
+	if (V.SecExp.buildings.secHub && V.SecExp.buildings.secHub.menials > 0) {
+		r.push(`${num(V.SecExp.buildings.secHub.menials)} slaves work to improve the security of your arcology, while your`);
+	} else {
+		r.push(`Your`);
+	}
+
+	if (V.mercenaries >= 1 && V.arcologyUpgrade.drones === 1) {
+		r.push(`mercenaries and security drones tirelessly patrol the streets to keep them safe.`);
+	} else if (V.arcologyUpgrade.drones === 1) {
+		r.push(`security drones tirelessly patrol the arcology to keep it safe.`);
+	} else {
+		r.push(`loyal subordinates try to keep the arcology safe to the best of their abilities.`);
+	}
+
+	// security modifiers
+	if (V.PC.career === "mercenary") {
+		r.push(`Your past life as a mercenary makes it easier to manage the security of the arcology.`);
+		secGrowth += 1;
+	}
+	if (V.SecExp.smilingMan.progress === 10) {
+		r.push(`The ex-criminal known to the world as The Smiling Man puts their impressive skills to work, dramatically increasing the efficiency of your security measures.`);
+		secGrowth += 2;
+	}
+	if (V.ACitizens + V.ASlaves <= 5000) {
+		r.push(`The small number of residents makes their job easier.`);
+		secGrowth += 2;
+	} else if (V.ACitizens + V.ASlaves <= 7500) {
+		r.push(`The fairly low number of residents makes their job a little easier.`);
+		secGrowth += 1;
+	} else if (V.ACitizens + V.ASlaves <= 10000) {
+		r.push(`The fairly high number of residents makes their job a little harder.`);
+		secGrowth -= -0.5;
+	} else if (V.ACitizens + V.ASlaves <= 15000) {
+		r.push(`The high number of residents makes their job harder.`);
+		secGrowth -= 1;
+	} else {
+		r.push(`The extremely high number of residents makes their job a lot harder.`);
+		secGrowth -= 2;
+	}
+	if (immigration >= 0 && emigration === 0) {
+		if (immigration < 50) {
+			r.push(`The limited number of immigrants that reached the arcology this week does not have any serious impact on the efficiency of current security measures.`);
+			secGrowth += 0.5;
+		} else if (immigration < 150) {
+			r.push(`The number of immigrants that reached the arcology this week is high enough to complicate security protocols.`);
+			secGrowth -= 0.2;
+		} else if (immigration < 300) {
+			r.push(`The high number of immigrants that reached the arcology this week complicates security protocols.`);
+			secGrowth -= 0.5;
+		} else if (immigration < 500) {
+			r.push(`The high number of immigrants that reached the arcology this week severely complicates security protocols.`);
+			secGrowth -= 1;
+		} else {
+			r.push(`The extremely high number of immigrants that reached the arcology this week severely complicates security protocols.`);
+			secGrowth -= 2;
+		}
+	}
+	if (V.visitors < 300) {
+		r.push(`The limited number of visitors coming and going did not have any serious impact on the efficiency of current security measures.`);
+		secGrowth += 0.5;
+	} else if (immigration < 750) {
+		r.push(`The number of visitors coming and going somewhat complicates security protocols.`);
+		secGrowth -= 0.2;
+	} else if (immigration < 1500) {
+		r.push(`The high number of visitors coming and going complicates security protocols.`);
+		secGrowth -= 0.5;
+	} else if (immigration < 2500) {
+		r.push(`The high number of visitors coming and going greatly complicates security protocols.`);
+		secGrowth -= 1;
+	} else {
+		r.push(`The extremely high number of visitors coming and going severely complicates security protocols.`);
+		secGrowth -= 2;
+	}
+	if (emigration !== 0 && immigration === 0) {
+		if (emigration < 100) {
+			r.push(`The limited reduction in citizens this week does not have any serious impact on the efficiency of current security measures.`);
+			secGrowth += 0.5;
+		} else if (emigration < 300) {
+			r.push(`The reduction in citizens this week is high enough to complicate security protocols.`);
+			secGrowth -= 0.2;
+		} else if (emigration < 600) {
+			r.push(`The large reduction in citizens this week complicates security protocols.`);
+			secGrowth -= 0.5;
+		} else if (emigration < 1000) {
+			r.push(`The huge reduction in citizens this week severely complicates security protocols.`);
+			secGrowth -= 1;
+		} else {
+			r.push(`The extreme reduction in citizens this week severely complicates security protocols.`);
+			secGrowth -= 2;
+		}
+	}
+	if (V.SecExp.core.crimeLow < 20) {
+		r.push(`Crime is a distant problem in the arcology, which makes improving security easier.`);
+		secGrowth += 1;
+	} else if (V.SecExp.core.crimeLow < 40) {
+		r.push(`Crime is a minor problem in the arcology, not serious enough to disrupt security efforts.`);
+	} else if (V.SecExp.core.crimeLow < 60) {
+		r.push(`Crime is an issue in the arcology, which makes improving security harder.`);
+		secGrowth -= 0.5;
+	} else if (V.SecExp.core.crimeLow < 80) {
+		r.push(`Crime is an overbearing problem in the arcology, which makes improving security a lot harder.`);
+		secGrowth -= 1;
+	} else {
+		r.push(`Crime is sovereign in the arcology, which makes improving security extremely difficult.`);
+		secGrowth -= 2;
+	}
+	if (V.SecExp.core.authority < 5000) {
+		r.push(`The low authority you hold on the arcology hampers the efforts of your security department.`);
+		secGrowth -= 1;
+	} else if (V.SecExp.core.authority < 7500) {
+		r.push(`The limited authority you hold on the arcology hampers the efforts of your security department.`);
+		secGrowth -= 0.5;
+	} else if (V.SecExp.core.authority < 10000) {
+		r.push(`The authority you hold on the arcology does not significantly impact the efforts of your security department.`);
+	} else if (V.SecExp.core.authority < 15000) {
+		r.push(`The high authority you hold on the arcology facilitates the security department's work.`);
+		secGrowth += 0.5;
+	} else {
+		r.push(`The absolute authority you hold on the arcology makes the security department's work a lot easier.`);
+		secGrowth += 1;
+	}
+	if (activeUnits >= 6) {
+		r.push(`Your military is the size of a small army. Security is easier to maintain with such forces at your disposal.`);
+		secGrowth += 0.5;
+	}
+	if (V.SecExp.battles.victories + V.SecExp.battles.losses >= 1) {
+		if (V.SecExp.battles.lastEncounterWeeks < 3) {
+			r.push(`The recent attack has a negative effect on the security of the arcology.`);
+			secGrowth -= 1;
+		} else if (V.SecExp.battles.lastEncounterWeeks < 5) {
+			r.push(`While some time has passed, the last attack still has a negative effect on the security of the arcology.`);
+			secGrowth -= 0.5;
+		} else {
+			r.push(`The arcology has not been attacked in a while, which has a positive effect on security.`);
+			secGrowth += 0.5;
+		}
+	}
+
+	if (V.SecExp.buildings.transportHub) {
+		secGrowth -= (V.SecExp.buildings.transportHub.airport + V.SecExp.buildings.transportHub.surfaceTransport - V.SecExp.buildings.transportHub.security * 3) / 2;
+		r.push(`The transport hub, for all its usefulness, is a hotspot of malicious`);
+		if (V.SecExp.buildings.transportHub.airport + V.SecExp.buildings.transportHub.surfaceTransport > V.SecExp.buildings.transportHub.security * 3) {
+			r.push(`activity and hub security forces are not sufficient to keep up with all threats.`);
+		} else {
+			r.push(`activity, but the hub security forces are up to the task.`);
+		}
+	}
+
+	if (V.SecExp.buildings.propHub && V.SecExp.buildings.propHub.upgrades.blackOps > 0) {
+		r.push(`Your black ops team proves to be a formidable tool against anyone threatening the security of your arcology.`);
+		secGrowth += 0.5 * random(1, 2);
+	}
+
+	const assistantDamaged = V.garrison.assistantTime;
+	if (assistantDamaged > 0) {
+		r.push(`With the central CPU core of the assistant down, managing security is a much harder task. Inevitably some little but important details will slip past your agents.`);
+		r.push(`${numberWithPluralOne(assistantDamaged, 'week')} to finish repair works.`);
+		secGrowth--;
+		crimeGrowth++;
+		V.garrison.assistantTime--; IncreasePCSkills('engineering', 0.1);
+	}
+
+	if (V.SF.Toggle && V.SF.Active >= 1) {
+		if (V.SecExp.edicts.SFSupportLevel >= 3) {
+			r.push(`The two squads of ${V.SF.Lower} assigned to the Security HQ provide an essential help to the security department.`);
+		}
+		if (V.SecExp.edicts.SFSupportLevel >= 2) {
+			r.push(`The training officers of ${V.SF.Lower} assigned to the Security HQ improve its effectiveness.`);
+		}
+		if (V.SecExp.edicts.SFSupportLevel >= 1) {
+			r.push(`Providing your Security Department with equipment from ${V.SF.Lower} slightly boosts the security of your arcology.`);
+		}
+		secGrowth *= 1+(V.SecExp.edicts.SFSupportLevel/10);
+	}
+
+	let secRest = App.SecExp.Check.secRestPoint() * (Math.clamp(V.SecExp.buildings.secHub ? V.SecExp.buildings.secHub.menials : 0, 0, App.SecExp.Check.reqMenials()) / App.SecExp.Check.reqMenials());
+	secRest = Math.max(20, secRest);
+	if (secRest < App.SecExp.Check.reqMenials() && V.SecExp.buildings.secHub) {
+		r.push(`The limited staff assigned to the HQ hampered the improvements to security achieved this week.`);
+	} else if (secRest < App.SecExp.Check.reqMenials()) {
+		r.push(`The limited infrastructure available slowly erodes away the security level of the arcology.`);
+	}
+
+	r.push(`The security level of the arcology is`);
+	if (V.SecExp.core.security > (secRest + 5)) {
+		r.push(`over its effective resting point, limiting the achievable growth this week.`);
+		secGrowth *= 0.5;
+	} else if (V.SecExp.core.security < (secRest - 5)) {
+		r.push(`under its effective resting point, speeding up its growth.`);
+		secGrowth *= 1.5;
+	} else if (V.SecExp.core.security === secRest) {
+		r.push(`at its effective resting point, this severely limits the influence of external factors on the change achievable this week.`);
+		secGrowth *= 0.3;
+	} else {
+		r.push(`near its effective resting point, this severely limits the influence of external factors on the change achievable this week.`);
+		if (secGrowth < 0) {
+			secGrowth *= 0.3;
+		}
+	}
+
+	const restGrowth = (secRest - V.SecExp.core.security) * 0.2;
+	const newSec = Math.trunc(V.SecExp.core.security + secGrowth + restGrowth);
+	if (newSec < V.SecExp.core.security) {
+		r.push(`This week <span class="red">security decreased.</span>`);
+	} else if (newSec === V.SecExp.core.security) {
+		r.push(`This week <span class="yellow">security did not change.</span>`);
+	} else {
+		r.push(`This week <span class="green">security improved.</span>`);
+	}
+	V.SecExp.core.security = Math.clamp(newSec, 0, 100);
+
+	r.push(`<br><br><strong>Crime</strong>:`);
+	r.push(`Due to the deterioration of the old world countries, organized crime focuses more and more on the prosperous Free Cities, yours included. This has a`);
+	if (V.week < 30) {
+		r.push(`small`);
+		crimeGrowth += 0.5;
+	} else if (V.week < 60) {
+		r.push(`noticeable`);
+		crimeGrowth += 1;
+	} else if (V.week < 90) {
+		r.push(`moderate`);
+		crimeGrowth += 1.5;
+	} else if (V.week < 120) {
+		r.push(`big`);
+		crimeGrowth += 2;
+	} else {
+		r.push(`huge`);
+		crimeGrowth += 2.5;
+	}
+	r.push(`impact on the growth of criminal activities in your arcology.`);
+
+	if (V.arcologies[0].prosperity < 50) {
+		r.push(`The low prosperity of the arcology facilitates criminal recruitment and organization.`);
+		crimeGrowth += 1;
+	} else if (V.arcologies[0].prosperity < 80) {
+		r.push(`The fairly low prosperity of the arcology facilitates criminal recruitment and organization.`);
+		crimeGrowth += 0.5;
+	} else if (V.arcologies[0].prosperity < 120) {
+		r.push(`The prosperity of the arcology is not high or low enough to have significant effects on criminal recruitment and organization.`);
+	} else if (V.arcologies[0].prosperity < 160) {
+		r.push(`The prosperity of the arcology is high enough to provide its citizens a decent life, hampering criminal recruitment and organization.`);
+		crimeGrowth -= 0.5;
+	} else if (V.arcologies[0].prosperity < 180) {
+		r.push(`The prosperity of the arcology is high enough to provide its citizens a decent life, significantly hampering criminal recruitment and organization.`);
+		crimeGrowth -= 1;
+	} else {
+		r.push(`The prosperity of the arcology is high enough to provide its citizens a very good life, significantly hampering criminal recruitment and organization.`);
+		crimeGrowth -= 2;
+	}
+	if (V.ASlaves < 1000) {
+		r.push(`The low number of slaves in the arcology does not hinder the activity of law enforcement, limiting crime growth.`);
+		crimeGrowth -= 1;
+	} else if (V.ASlaves < 2000) {
+		r.push(`The fairly low number of slaves in the arcology does not hinder significantly the activity of law enforcement, limiting crime growth.`);
+		crimeGrowth -= 0.5;
+	} else if (V.ASlaves < 3000) {
+		r.push(`The number of slaves in the arcology is becoming an impediment for law enforcement, facilitating crime growth.`);
+		crimeGrowth += 1;
+	} else {
+		r.push(`The number of slaves in the arcology is becoming a big issue for law enforcement, facilitating crime growth.`);
+		crimeGrowth += 1.5;
+	}
+	if (V.SecExp.core.security <= 20) {
+		r.push(`The security measures in place are severely limited, allowing crime to grow uncontested.`);
+	} else if (V.SecExp.core.security <= 50) {
+		r.push(`The security measures in place are of limited effect and use, giving only mixed results in their fight against crime.`);
+		crimeGrowth -= 1.5;
+	} else if (V.SecExp.core.security <= 75) {
+		r.push(`The security measures in place are well developed and effective, making a serious dent in the profitability of criminal activity in your arcology.`);
+		crimeGrowth -= 3;
+	} else {
+		r.push(`The security measures in place are extremely well developed and very effective, posing a serious threat even to the most powerful criminal organizations in existence.`);
+		crimeGrowth -= 5.5;
+	}
+	if (V.SecExp.core.authority < 5000) {
+		r.push(`Your low authority allows crime to grow undisturbed.`);
+		crimeGrowth += 1;
+	} else if (V.SecExp.core.authority < 7500) {
+		r.push(`Your relatively low authority facilitates criminal activities.`);
+		crimeGrowth += 0.5;
+	} else if (V.SecExp.core.authority < 10000) {
+		r.push(`Your authority is not high enough to discourage criminal activity.`);
+	} else if (V.SecExp.core.authority < 15000) {
+		r.push(`Your high authority is an effective tool against crime.`);
+		crimeGrowth -= 1;
+	} else {
+		r.push(`Your absolute authority is an extremely effective tool against crime.`);
+		crimeGrowth -= 2;
+	}
+	if (V.cash >= 100000) {
+		r.push(`Your great wealth acts as a beacon for the greediest criminals, calling them to your arcology as moths to a flame.`);
+		crimeGrowth += 0.5;
+	}
+	if (V.SecExp.buildings.propHub && V.SecExp.buildings.propHub.upgrades.marketInfiltration > 0) {
+		crimeGrowth += 0.5 * random(1, 2);
+	}
+
+	const crimeCap = Math.trunc(Math.clamp(App.SecExp.Check.crimeCap() + (App.SecExp.Check.crimeCap() - App.SecExp.Check.crimeCap() * ((V.SecExp.buildings.secHub ? V.SecExp.buildings.secHub.menials : 0) / App.SecExp.Check.reqMenials())), 0, 100));
+	if (crimeCap > App.SecExp.Check.crimeCap() && V.SecExp.buildings.secHub) {
+		r.push(`The limited staff assigned to the HQ allows more space for criminals to act.`);
+	}
+	if (V.SecExp.core.authority > 12000) {
+		if (V.SecExp.buildings.secHub.coldstorage < 6) {
+			if (V.SecExp.buildings.secHub.coldstorage === 0) {
+				r.push(`Adding a facility`);
+			} else {
+				r.push(`Improving the cold storage facility attached`);
+			}
+			r.push(`to the SecurityHQ should allow the staff to be more efficient in dealing with crime.`);
+		} else {
+			r.push(`The cold storage facility attached to SecurityHQ allows the staff to be more efficient in dealing with crime.`);
+		}
+	}
+	const newCrime = Math.trunc(Math.clamp(V.SecExp.core.crimeLow + crimeGrowth, 0, crimeCap));
+	if (newCrime > V.SecExp.core.crimeLow) {
+		r.push(`This week <span class="red">crime increased.</span>`);
+	} else if (newCrime === V.SecExp.core.crimeLow) {
+		r.push(`This week <span class="yellow">crime did not change.</span>`);
+	} else {
+		r.push(`This week <span class="green">crime decreased.</span>`);
+	}
+	V.SecExp.core.crimeLow = Math.clamp(newCrime, 0, 100);
+
+	if (V.SecExp.edicts.defense.militia >= 1 || activeUnits >= 1) {
+		r.push(`<br><br> <strong>Military</strong>:`);
+		if (V.SecExp.edicts.defense.militia >= 1) {
+			let recruitsMultiplier = 1, recruitLimit = 0, adjst;
+
+			const SF = App.SecExp.assistanceSF('security', 'militia');
+			r.push(SF.text); recruitsMultiplier += SF.bonus;
+
+			const propagandaEffects = App.SecExp.propagandaEffects("recruitment");
+			r.push(`${propagandaEffects.text}`);
+			recruitsMultiplier *= (1 + propagandaEffects.effect);
+			if (V.SecExp.edicts.defense.militia === 2) {
+				r.push(`Your militia accepts only volunteering citizens, ready to defend their arcology.`);
+				recruitLimit = App.SecExp.militiaCap(); adjst = 0.0025;
+				if (V.rep >= 10000) {
+					r.push(`Many citizens volunteer just to fight for someone of your renown.`);
+					recruitLimit += adjst;
+				}
+				if (V.SecExp.core.authority >= 10000) {
+					r.push(`Many citizens feel it is their duty to fight for you, boosting volunteer enrollment.`);
+					recruitLimit += adjst;
+				}
+			} else if (V.SecExp.edicts.defense.militia === 3) {
+				r.push(`Adult citizens are required to join the militia for a period of time.`);
+				recruitLimit = App.SecExp.militiaCap(); adjst = 0.005;
+			} else if (V.SecExp.edicts.defense.militia === 4) {
+				r.push(`Adult citizens are required to register and serve in the militia whenever necessary.`);
+				recruitLimit = App.SecExp.militiaCap(); adjst = 0.01;
+			} else if (V.SecExp.edicts.defense.militia === 5) {
+				r.push(`Every citizen is required to train and participate in the military activities of the arcology.`);
+				recruitLimit = App.SecExp.militiaCap(); adjst = 0.02;
+			}
+			if (V.SecExp.edicts.defense.lowerRequirements === 1) {
+				r.push(`Your lax physical requirements to enter the militia allows for a greater number of citizens to join.`);
+				recruitLimit += adjst;
+			}
+			if (V.SecExp.edicts.defense.militia >= 3) {
+				if (V.SecExp.edicts.defense.militaryExemption === 1) {
+					r.push(`Some citizens prefer to contribute to the arcology's defense through financial support rather than military service, making you <span class="yellowgreen">a small sum.</span>`);
+					recruitLimit -= adjst;
+					cashX(250, "securityExpansion");
+				}
+				if (V.SecExp.edicts.defense.noSubhumansInArmy === 1) {
+					r.push(`Guaranteeing the purity of your armed forces comes with a small loss of potential recruits.`);
+					recruitLimit -= adjst;
+				}
+				if (V.SecExp.edicts.defense.pregExemption === 1) {
+					r.push(`Many pregnant citizens prefer to avoid military service not to endanger themselves and their children.`);
+					recruitLimit -= adjst;
+				}
+			}
+
+			const recruits = Math.trunc((recruitLimit * V.ACitizens - App.SecExp.Manpower.totalMilitia) / 20 * recruitsMultiplier);
+			if (recruits > 0) {
+				V.militiaFreeManpower += recruits;
+				r.push(`This week ${recruits} citizens joined the militia.<br>`);
+			} else if (V.SecExp.edicts.defense.militia === 5) {
+				r.push(`No citizens joined your militia this week because your society is as militarized as it can get.<br>`);
+			} else if (V.SecExp.edicts.defense.militia === 2) {
+				r.push(`There are no more citizens willing to join the arcology armed forces. You'll need to enact higher recruitment edicts if you need more manpower.<br>`);
+			} else {
+				r.push(`No more citizens could be drafted into your militia. You'll need to enact higher recruitment edicts if you need more manpower.<br>`);
+			}
+		}
+
+		if (V.mercenaries >= 1) { // mercs
+			let newMercs = random(0, 3);
+			if (V.rep < 6000) {
+				r.push(`Your low reputation turns some mercenaries away, hoping to find contracts that would bring them more renown.`);
+				newMercs -= 1;
+			} else if (V.rep < 12000) {
+				r.push(`Your reputation is not high enough to attract many mercenaries to your free city.`);
+			} else {
+				r.push(`Your reputation attracts many guns for hire who would be proud to have such distinct character on their resume.`);
+				newMercs += 1;
+			}
+			if (V.arcologies[0].prosperity < 50) {
+				r.push(`The low prosperity of the arcology discourages new guns for hire from coming to your arcology.`);
+				newMercs -= 1;
+			} else if (V.arcologies[0].prosperity < 80) {
+				r.push(`The fairly low prosperity of the arcology discourages new guns for hire from coming to your arcology.`);
+				newMercs += 1;
+			} else if (V.arcologies[0].prosperity < 120) {
+				r.push(`The prosperity of the arcology attracts a few mercenaries, hopeful to find lucrative contracts within its walls.`);
+				newMercs += random(1, 2);
+			} else if (V.arcologies[0].prosperity < 160) {
+				r.push(`The fairly high prosperity of the arcology attracts some mercenaries, hopeful to find lucrative contracts within its walls.`);
+				newMercs += random(2, 3);
+			} else if (V.arcologies[0].prosperity < 180) {
+				r.push(`The high prosperity of the arcology is attracts some mercenaries, hopeful to find lucrative contracts within its walls.`);
+				newMercs += random(2, 4);
+			} else {
+				r.push(`The very high prosperity of the arcology attracts a lot of mercenaries, hopeful to find lucrative contracts within its walls.`);
+				newMercs += random(3, 5);
+			}
+			if (V.SecExp.core.crimeLow > 60) {
+				r.push(`The powerful crime organizations that nested themselves in the arcology have an unending need for cheap guns for hire, many mercenaries flock to your free city in search of employment.`);
+				newMercs += random(1, 2);
+			}
+
+			const SF = App.SecExp.assistanceSF('security', 'mercs');
+			r.push(SF.text); newMercs += SF.bonus;
+
+			if (V.SecExp.edicts.defense.discountMercenaries > 0) {
+				r.push(`More mercenaries are attracted to your arcology as a result of the reduced rent.`);
+				newMercs += random(2, 4);
+			}
+			newMercs = Math.trunc(newMercs / 2);
+			if (newMercs > 0) {
+				V.mercFreeManpower += newMercs;
+				r.push(`This week ${newMercs} mercenaries reached the arcology.<br>`);
+			} else {
+				r.push(`This week no new mercenaries reached the arcology.<br>`);
+			}
+			V.mercFreeManpower = Math.clamp(V.mercFreeManpower, 0, 2000);
+		}
+
+		if (activeUnits > 0) { // loyalty and training
+			for (const unit of V.slaveUnits) {
+				r.push(App.SecExp.humanUnitLoyaltyChanges(unit, 'slave'));
+			}
+			for (const unit of V.militiaUnits) {
+				r.push(App.SecExp.humanUnitLoyaltyChanges(unit, 'citizens'));
+			}
+			for (const unit of V.mercUnits) {
+				r.push(App.SecExp.humanUnitLoyaltyChanges(unit, 'mercenary'));
+			}
+		}
+	}
+
+	if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.brainImplantProject > 0 && V.SecExp.buildings.riotCenter.brainImplant < 106) {
+		r.push(`<br><br>`);
+		V.SecExp.buildings.riotCenter.brainImplant += V.SecExp.buildings.riotCenter.brainImplantProject;
+		if (100 - V.SecExp.buildings.riotCenter.brainImplant <= 0) {
+			r.push(`The project has been completed!`);
+			V.SecExp.buildings.riotCenter.brainImplant = 106;
+		} else {
+			r.push(`The great brain implant project is proceeding steadily. This week we made`);
+			if (V.SecExp.buildings.riotCenter.brainImplantProject <= 2) {
+				r.push(`some small progress.`);
+			} else if (V.SecExp.buildings.riotCenter.brainImplantProject <= 4) {
+				r.push(`some progress.`);
+			} else {
+				r.push(`good progress.`);
+			}
+		}
+	}
+
+	if (V.SecExp.buildings.weapManu) {
+		if (App.SecExp.weapManuUpgrade.fully().bots && App.SecExp.weapManuUpgrade.fully().human) {
+			delete V.SecExp.buildings.weapManu.upgrades.queue;
+		}
+		if (jsDef(V.SecExp.buildings.weapManu.upgrades.queue) && V.SecExp.buildings.weapManu.upgrades.queue.length > 0 && V.SecExp.buildings.weapManu.upgrades.queue[0].time > 0) {
+			let current = App.SecExp.weapManuUpgrade.current(); V.SecExp.buildings.weapManu.upgrades.queue[0].time--;
+			r.push(`<br>In the research lab, ${current.dec}`);
+			switch (current.dec) {
+				case "adaptive armored frames":
+				case "advanced synthetic alloys":
+				case "ceramo-metallic alloys":
+				case "rapid action stimulants":
+				case "universal cyber enhancements":
+				case "remote neural links":
+				case "combined training regimens with the special force":
+					r.push(`are`); break;
+				default: r.push(`is`);
+			}
+			r.push(`being developed with the aim of enhancing ${current.unit}' ${current.purpose}.`);
+			if (V.SecExp.buildings.weapManu.upgrades.queue[0].time <= 0) {
+				r.push(`Reports indicate it is ready for deployment and will be issued in the following days.`);
+				V.SecExp.buildings.weapManu.upgrades.completed.push(current.ID);
+				V.SecExp.buildings.weapManu.upgrades.queue.splice(0, 1);
+			} else {
+				r.push(`It will be finished in ${numberWithPluralOne(V.SecExp.buildings.weapManu.upgrades.queue[0].time, "week")}.`);
+			}
+			for (let i = 1; i < V.SecExp.buildings.weapManu.upgrades.queue.length; i++) {
+				current = App.SecExp.weapManuUpgrade.current(V.SecExp.buildings.weapManu.upgrades.queue[i].ID);
+				r.push(`<br>${ordinalSuffix(i + 1)} in queue: ${current.dec} for ${current.unit}. It will enhance their ${current.purpose}.`);
+			}
+		}
+	}
+
+	const frag = new DocumentFragment();
+	$(frag).append(...App.Events.spaceSentences(r));
+	return frag;
+};
diff --git a/src/Mods/SecExp/js/tradeReport.js b/src/Mods/SecExp/js/tradeReport.js
index 582c987ab68eb560f4c2edc4dde6714d93d88f8f..eb8e0dc19a127d5060a419e02b2381fa075f0bee 100644
--- a/src/Mods/SecExp/js/tradeReport.js
+++ b/src/Mods/SecExp/js/tradeReport.js
@@ -117,7 +117,7 @@ App.SecExp.tradeReport = function() {
 		}
 	}
 
-	const SF = App.SecExp.SF_effect('trade');
+	const SF = App.SecExp.assistanceSF('trade');
 	r.push(SF.text); tradeChange += SF.bonus;
 
 	if (tradeChange > 0) {
diff --git a/src/Mods/SecExp/rebellionEvents.tw b/src/Mods/SecExp/rebellionEvents.tw
deleted file mode 100644
index fcf207cfee32573b25c100a950f32103ffe0f069..0000000000000000000000000000000000000000
--- a/src/Mods/SecExp/rebellionEvents.tw
+++ /dev/null
@@ -1,120 +0,0 @@
-:: rebellionEvents [nobr]
-
-<<if $slaveRebellionEventFires == 1>>
-	<<if $SecExp.rebellions.tension <= 33>>
-		<<set _event = 1>>
-	<<elseif $SecExp.rebellions.tension <= 66>>
-		<<set _event = 2>>
-	<<else>>
-		<<set _event = 3>>
-	<</if>>
-<<elseif $citizenRebellionEventFires == 1>>
-	<<if $SecExp.rebellions.tension <= 33>>
-		<<set _event = 4>>
-	<<elseif $SecExp.rebellions.tension <= 66>>
-		<<set _event = 5>>
-	<<else>>
-		<<set _event = 6>>
-	<</if>>
-<</if>>
-
-<<setNonlocalPronouns $seeDicks>>
-<<switch _event>>
-<<case 1>>
-	/* low tension slave rebellion events */
-	<<set _rand = random(0,6)>>
-	<<if _rand == 0>>
-		This week several slaves were found plotting the death of their master. They were quickly dealt with, but their owner's choice of punishment did little to calm tensions in the arcology.
-	<<elseif _rand == 1>>
-		This week a large group of slaves attempted to escape. Several were recaptured, but others were deemed too dangerous and were shot on sight. The unfortunate circumstances raised the disapproval of many citizens, either because of the waste of good slaves or the brutality with which the operation was carried. With a bit of luck, however, the incident will be soon forgotten.
-	<<elseif _rand == 2>>
-		This week books of unknown origin and dangerous content were found in the possession of several slaves. They were mostly sociopolitical treaties, making it clear that the intent of the ones responsible was to fan the fire of rebellion. The books were quickly collected and archived, hopefully this affair will not have lasting consequences.
-	<<elseif _rand == 3>>
-		This week a citizen was caught giving refuge to an escaped slave. He was not able to pay for the value of the stolen goods, so he was processed as the case required and the slave returned to their rightful master. Many questions however remain without answers.
-	<<elseif _rand == 4>>
-		This week a member of a well known anti-slavery group was caught trying to infiltrate the arcology. During the capture attempt shots were fired and several guards were injured, and in the end the fugitive unfortunately managed to escape. Reports indicate several slaves helped the criminal, some going as far as using themselves as shields against the bullets of the security drones.
-	<<elseif _rand == 5>>
-		This week a slave was caught attempting to sabotage a machine in one of the factories. _HeU explained _hisU action as "trying to defend _himselfU from a dangerous machine". Reports confirmed that the apparatus is indeed quite deadly, having killed several slaves since it was installed, but the expert way _heU handled the sabotage leaves open the possibility of a deliberate plan or even external help.
-	<<else>>
-		This week a slave was found dead in one of the sewer tunnels. It seems _heU was stabbed repeatedly with a sharp object. _HeU was fairly famous for _hisU capabilities as a slave trainer; _hisU old master spent not an insignificant amount of money trying to find _himU once he realized _heU was missing. The episode might have been a simple mugging gone wrong, but _hisU activities as a slave breaker might have played a role in _hisU homicide.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(1,5)>>
-<<case 2>>
-	/* med tension slave rebellion events */
-	<<set _rand = random(0,5)>>
-	<<if _rand == 0>>
-		This week some strange reports came in: it seems some assemblies of slaves were observed several nights in a row. The slaves were traced and their masters notified, but many suspect there may be something deeper than a few slaves congregating in the night.
-	<<elseif _rand == 1>>
-		This week an underground railroad was discovered. The rebels did not go down without a fight, but in the end <<if $mercenaries >= 1>>your mercenaries<<else>>your security drones<</if>> managed to destroy the old tunnels they were using to ship out slaves out of the arcology.
-	<<elseif _rand == 2>>
-		This week a famous citizen was assaulted and brutally murdered by his slaves. The ones responsible were apprehended and dealt with easily enough, but the mere fact something like this could have happened is concerning. Those slaves had to be aware of their certain doom.
-	<<elseif _rand == 3>>
-		This week a group of slavers entering the arcology was assaulted. Many reported heavy injuries, but fortunately there were no casualties. The attackers were disguised, but the security systems already identified several slaves who were likely part of the group, based on camera feeds.
-	<<elseif _rand == 4>>
-		This week the waterways were found infected by a virulent pathogen. The cause was later found to be a diseased slave that died while in the maintenance tunnels. It's not clear if the slave was there because of orders given to _himU or if _heU was trying to escape.
-	<<else>>
-		This week a sleeper cell of a famous anti slavery organization was discovered in the low levels of the arcology. The group, however, was aware of the coming security forces and retreated before they could be dealt with.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(5,10)>>
-<<case 3>>
-	/* high tension slave rebellion events */
-	<<set _rand = random(0,4)>>
-	<<if _rand == 0>>
-		This week a group of slaves took control of one of the manufacturing plants and barricaded themselves inside. It took several days of negotiations and skirmishes to finally end this little insurrection. Many of the slaves involved will be executed in the next few days.
-	<<elseif _rand == 1>>
-		This week a number of shops were burned to the ground by rioting slaves and sympathetic citizens. It took considerable effort for the security forces to take control of the situation. Harsh punishment is required and scheduled for the instigators.
-	<<elseif _rand == 2>>
-		This week a mass escape attempt was barely stopped before becoming a catastrophe. Many citizens were trampled by the desperate horde of slaves. It will take some time to restore the streets involved to working order.
-	<<elseif _rand == 3>>
-		This week a number of riots inflamed the arcology. Many slaves took violent actions against citizens and security personnel. The number of victims keeps getting higher as still now the last sparks of revolt are still active.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(10,15)>>
-<<case 4>>
-	<<set _rand = random(0,6)>>
-	<<if _rand == 0>>
-		This week a citizen refused to pay rent, claiming ideological opposition to the arcology's ownership policies. He was quickly dealt with, but his words might not have fallen silent yet.
-	<<elseif _rand == 1>>
-		This week books of unknown origin and dangerous content were found in the possession of several citizens. They were mostly sociopolitical treaties, making it clear that the intent of the ones responsible was to fan the fire of rebellion. Most of them were bought and archived, but a few are still circling amongst the citizens of the arcology.
-	<<elseif _rand == 2>>
-		This week a citizen was caught giving refuge to other citizens, who would be liable to be enslaved because of their debts. The situation was quickly resolved, but the misplaced generosity of that citizen might have inflamed a few souls.
-	<<elseif _rand == 3>>
-		This week a citizen died in one of the factories. His death sparked some outrage, even some talk of protests against the owners of the factory, but things seem to have calmed down for now.
-	<<elseif _rand == 4>>
-		This week a citizen refused to be evicted from his house. After some negotiations the man was forcibly removed from the property by your security forces. Unfortunately the forced entry caused some damage to the building.
-	<<elseif _rand == 5>>
-		This week a citizen refused to be enslaved as his contract established. With an impressive display of his rhetoric capabilities he managed to gather a small crowd agreeing with his position. The impromptu assembly was promptly disrupted by the drones.
-	<<else>>
-		This week a security drone was found disabled and stripped of important electronic components. It seems the act was not dictated by greed, as the most precious parts of the drone were left on the machine, but rather to cover up something that the drone saw.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(1,5)>>
-<<case 5>>
-	<<set _rand = random(0,5)>>
-	<<if _rand == 0>>
-		This week a factory was subject to a strike by a group of citizens protesting against the owner. They were promptly arrested and the factory returned to its rightful proprietor by your security department.
-	<<elseif _rand == 1>>
-		This week a group of citizens organized a protest against the systemic enslavement of the citizens of the arcology. Their little parade gathered a surprisingly large crowd, but it was nonetheless quickly suppressed by your forces.
-	<<elseif _rand == 2>>
-		This week the security department registered the formation of several assemblies of citizens, whose purpose seems to be political in nature. For now no further steps were taken, but it's a worrying sign of further political opposition within the arcology.
-	<<elseif _rand == 3>>
-		This week there was a protest against one of the wealthiest citizen of the arcology. Many criticize his near monopoly. Supporters of the citizen met the protesters on the streets and it was just thanks to the intervention of the security drones that violence was avoided.
-	<<elseif _rand == 4>>
-		This week several cameras were sabotaged and in many cases damaged beyond repair. A group of anonymous citizens claims to be responsible; their motivation is apparently the excessive surveillance in the arcology and their attack a response to the breach of their privacy.
-	<<else>>
-		This week several citizens barricaded themselves in a private brothel. It seems their intention is to protest against the use of ex-citizens in the sex trade, claiming that such a position is unfitting for them. The problem was quickly resolved with the intervention of the security department.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(5,10)>>
-<<case 6>>
-	<<set _rand = random(0,4)>>
-	<<if _rand == 0>>
-		This week the arcology was shaken by a number of strikes throughout the manufacturing levels. Many lament the predatory nature of Free Cities society, many other just want to cause damage to their perceived oppressors. It was a significant effort for the security department to stop all protests.
-	<<elseif _rand == 1>>
-		This week several factories were set aflame by their workers. The security department worked day and night to control the fire and apprehend the criminals behind the act. Many are known dissidents, but there are a fair few new faces within them. This is a worrying sign.
-	<<elseif _rand == 2>>
-		This week numerous riots exploded all over the arcology. Many citizens took to the streets to protest against the arcology owner and its supporters. The security forces slowly managed to stop the rioters, with no small amount of trouble and only through generous use of violence.
-	<<elseif _rand == 3>>
-		This week a massive protest of citizens and slaves gathered just outside the penthouse. The crowd was dispersed only after several hours. There were several victims from both sides and no shortage of injured.
-	<</if>>
-	<<set $SecExp.rebellions.tension += random(10,15)>>
-<</switch>>
-
-<<set $SecExp.rebellions.tension = Math.clamp($SecExp.rebellions.tension,0,100)>>
\ No newline at end of file
diff --git a/src/Mods/SecExp/rebellionGenerator.tw b/src/Mods/SecExp/rebellionGenerator.tw
deleted file mode 100644
index 43fb8ee501970b0bae5604a550366b031f10916a..0000000000000000000000000000000000000000
--- a/src/Mods/SecExp/rebellionGenerator.tw
+++ /dev/null
@@ -1,410 +0,0 @@
-:: rebellionGenerator [nobr]
-
-<<set _slave = 0>>
-<<set _citizen = 0>>
-<<set _CSratio = $ACitizens / ($ASlaves)>>
-
-<strong>Slaves security analysis:</strong>
-<<if $SecExp.core.authority <= 3000>>
-	Your very low authority allows slaves to think too freely.
-	<<set _slave += 30>>
-<<elseif $SecExp.core.authority <= 6000>>
-	Your low authority allows slaves to think too freely.
-	<<set _slave += 25>>
-<<elseif $SecExp.core.authority <= 9000>>
-	Your moderate authority allows slaves to think a bit too freely.
-	<<set _slave += 20>>
-<<elseif $SecExp.core.authority <= 12000>>
-	Your good authority does not allow slaves to think too freely.
-	<<set _slave += 15>>
-<<elseif $SecExp.core.authority <= 15000>>
-	Your high authority does not allow slaves to think too freely.
-	<<set _slave += 10>>
-<<elseif $SecExp.core.authority <= 18000>>
-	Your very high authority does not allow slaves to think too freely.
-	<<set _slave += 5>>
-<<else>>
-	Your absolute authority does not allow slaves to have a single free thought.
-	<<set _slave += 1>>
-<</if>>
-<<if _CSratio <= 0.4>>
-	There are a lot more slaves than citizens, making some doubt their masters are strong enough to stop them.
-	<<set _slave += 30>>
-<<elseif _CSratio <= 0.6>>
-	There are a lot more slaves than citizens, making some doubt their masters are strong enough to stop them.
-	<<set _slave += 25>>
-<<elseif _CSratio <= 0.8>>
-	There are more slaves than citizens, making some doubt their masters are strong enough to stop them.
-	<<set _slave += 20>>
-<<elseif _CSratio <= 1>>
-	There are more slaves than citizens, making some doubt their masters are strong enough to stop them.
-	<<set _slave += 15>>
-<<elseif _CSratio <= 1.5>>
-	There are fewer slaves than citizens, making some doubt they would be strong enough to defeat their masters.
-	<<set _slave += 10>>
-<<elseif _CSratio >= 3>>
-	There are fewer slaves than citizens, making some doubt they would be strong enough to defeat their masters.
-	<<set _slave -= 5>>
-<<else>>
-	Citizen and slave populations are sufficiently balanced not to cause problems either way.
-	<<set _slave -= 1>>
-<</if>>
-<<if $SecExp.core.security <= 10>>
-	The very low security of the arcology leaves free space for slaves to organize and agitate.
-	<<set _slave += 30>>
-<<elseif $SecExp.core.security <= 30>>
-	The low security of the arcology leaves free space for slaves to organize and agitate.
-	<<set _slave += 20>>
-<<elseif $SecExp.core.security <= 60>>
-	The moderate security of the arcology does not allow free space for slaves to organize and agitate.
-	<<set _slave += 10>>
-<<elseif $SecExp.core.security >= 90>>
-	The high security of the arcology does not allow free space for slaves to organize and agitate.
-	<<set _slave -= 5>>
-<<else>>
-	The high security of the arcology does not allow free space for slaves to organize and agitate.
-	<<set _slave -= 1>>
-<</if>>
-<<if $arcologies[0].FSDegradationist != "unset">>
-	Many slaves are so disgusted by your degradationist society, that they are willing to rise up against their masters to escape.
-	<<set _slave += 30>>
-<<elseif $arcologies[0].FSPaternalist != "unset">>
-	Many slaves are content to live in your paternalist society.
-	<<set _slave -= 5>>
-<<else>>
-	<<set _slave += 5>>
-<</if>>
-<<if $arcologies[0].FSRestart != "unset">>
-	Many slaves are worried by your eugenics projects and some are pushed towards radicalization.
-	<<set _slave += 30>>
-<<elseif $arcologies[0].FSRepopulationFocus != "unset">>
-	Many slaves are pleasantly happy of your repopulation effort, affording them the freedom to reproduce.
-	<<set _slave -= 5>>
-<<else>>
-	<<set _slave += 5>>
-<</if>>
-<br><br>
-<strong>Citizens security analysis:</strong>
-<<if $SecExp.core.authority <= 3000>>
-	Your very low authority allows your citizens to think too freely.
-	<<set _citizen += 30>>
-<<elseif $SecExp.core.authority <= 6000>>
-	Your very low authority allows your citizens to think too freely.
-	<<set _citizen += 25>>
-<<elseif $SecExp.core.authority <= 9000>>
-	Your moderate authority allows your citizens to think a bit too freely.
-	<<set _citizen += 20>>
-<<elseif $SecExp.core.authority <= 12000>>
-	Your good authority does not allow your citizens to think too freely.
-	<<set _citizen += 15>>
-<<elseif $SecExp.core.authority <= 15000>>
-	Your high authority does not allow your citizens to think too freely.
-	<<set _citizen += 10>>
-<<elseif $SecExp.core.authority <= 18000>>
-	Your very high authority does not allow your citizens to think too freely.
-	<<set _citizen += 5>>
-<<else>>
-	Your absolute authority does not allow your citizens to have a single free thought.
-	<<set _citizen += 1>>
-<</if>>
-<<if $SecExp.core.crimeLow >= 90>>
-	The very high crime level of the arcology breeds extreme discontent between your citizens.
-	<<set _citizen += 30>>
-<<elseif $SecExp.core.crimeLow >= 60>>
-	The high crime level of the arcology breeds high discontent between your citizens.
-	<<set _citizen += 15>>
-<<elseif $SecExp.core.crimeLow >= 30>>
-	The low crime level of the arcology leaves your citizens happy and satisfied.
-	<<set _citizen += 5>>
-<<else>>
-	The very low crime level of the arcology leaves your citizens happy and satisfied.
-	<<set _citizen -= 5>>
-<</if>>
-<<if $SecExp.edicts.defense.militia >= 1>>
-	<<if ($arcologies[0].FSRomanRevivalist == "unset" && $arcologies[0].FSAztecRevivalist == "unset" && $arcologies[0].FSEgyptianRevivalist == "unset" && $arcologies[0].FSEdoRevivalist == "unset" && $arcologies[0].FSArabianRevivalist == "unset" && $arcologies[0].FSChineseRevivalist == "unset" && $arcologies[0].FSNeoImperialist == "unset")>>
-		<<if $SecExp.edicts.defense.militia === 5>>
-			Many of your citizens are offended by your extreme militarization of the arcology's society.
-			<<set _citizen += 20>>
-		<<elseif $SecExp.edicts.defense.militia === 4>>
-			Many of your citizens are offended by your militarization of the arcology's society.
-			<<set _citizen += 15>>
-		<<else>>
-			<<set _citizen += 10>>
-		<</if>>
-	<<else>>
-		<<if $SecExp.edicts.defense.militia === 5>>
-			Some of your citizens are offended by your extreme militarization of the arcology's society.
-			<<set _citizen += 10>>
-		<<elseif $SecExp.edicts.defense.militia === 4>>
-			Some of your citizens are offended by your militarization of the arcology's society.
-			<<set _citizen += 5>>
-		<<else>>
-			<<set _citizen -= 5>>
-		<</if>>
-	<</if>>
-<</if>>
-<<if $arcologies[0].FSNull != "unset">>
-	Many of your more conservative citizens do not enjoy the cultural freedom you afford the residents of the arcology.
-	<<set _citizen += either(20,30)>>
-<</if>>
-<<if $arcologies[0].FSRestart != "unset">>
-	<<if _CSratio > 2>>
-		Your citizens are not happy with the noticeable lack of slaves compared to their numbers.
-		<<set _citizen += 20>>
-	<<elseif _CSratio > 1>>
-		Your citizens are not happy with the lack of slaves compared to their numbers.
-		<<set _citizen += 15>>
-	<<elseif _CSratio < 0.5>>
-		<<set _citizen -= 5>>
-	<</if>>
-<<elseif $arcologies[0].FSRepopulationFocus != "unset">>
-	<<if _CSratio < 0.5>>
-		Your citizens are not happy about being outbred by the slaves of the arcology.
-		<<set _citizen += 20>>
-	<<elseif _CSratio < 1>>
-		Your citizens are not happy about being outbred by the slaves of the arcology.
-		<<set _citizen += 15>>
-	<<elseif _CSratio > 1.4>>
-		<<set _citizen += 5>>
-	<</if>>
-<</if>>
-
-/* rolls to see if event happens */
-<<if _slave < 0>>
-	<<set _slave = 0>>
-<<elseif _slave >= 95>>
-	<<set _slave = 95>>																	/* there's always a min 5% chance nothing happens */
-<</if>>
-<<if _citizen < 0>>
-	<<set _citizen = 0>>
-<<elseif _citizen >= 95>>
-	<<set _citizen = 95>>
-<</if>>
-<<set _roll = random(1,_slave + _citizen)>>
-<<if $SecExp.buildings.riotCenter && $SecExp.buildings.riotCenter.brainImplant == 106>>
-	<<set _slave = Math.trunc(_slave * 0.5 * $SecExp.settings.rebellion.speed), _citizen = Math.trunc(_citizen * 0.5 * $SecExp.settings.rebellion.speed)>>
-<<else>>
-	<<set _slave = Math.trunc(_slave * $SecExp.settings.rebellion.speed), _citizen = Math.trunc(_citizen * $SecExp.settings.rebellion.speed)>>
-<</if>>
-<<if _roll <= _slave>>
-	<<if random(1,100) < _slave>>
-		<<set $slaveRebellionEventFires = 1>>
-		<<set $citizenRebellionEventFires = 0>>
-		<<if $SecExp.rebellions.tension != 0>>
-			<<set $SecExp.rebellions.slaveProgress += Math.trunc(random(1,5) * ($SecExp.rebellions.tension / 100) * 10)>>		/* progress scales with tension */
-		<<else>>
-			<<set $SecExp.rebellions.slaveProgress += random(1,5)>>
-		<</if>>
-	<</if>>
-<<else>>
-	<<if random(1,100) < _citizen>>
-		<<set $slaveRebellionEventFires = 0>>
-		<<set $citizenRebellionEventFires = 1>>
-		<<if $SecExp.rebellions.tension != 0>>
-			<<set $SecExp.rebellions.citizenProgress += Math.trunc(random(1,5) * ($SecExp.rebellions.tension / 100) * 10)>>
-		<<else>>
-			<<set $SecExp.rebellions.citizenProgress += random(1,5)>>
-		<</if>>
-	<</if>>
-<</if>>
-
-/* if there is an advancement selects a random mini event */
-<<set _oldTension = $SecExp.rebellions.tension>>
-<<if $slaveRebellionEventFires == 1 || $citizenRebellionEventFires == 1>>
-	<br><br> <<include "rebellionEvents">>
-<<elseif $SecExp.rebellions.tension > 0>>
-	/* otherwise tension decays */
-	<br><br>
-	<strong>Tension</strong>:
-	<<if $SecExp.buildings.riotCenter && $SecExp.buildings.riotCenter.upgrades.freeMedia >= 1>>
-		The guaranteed free media access you offer does wonders to lower tensions in the arcology.
-		<<set $SecExp.rebellions.tension = Math.trunc(Math.clamp($SecExp.rebellions.tension - $SecExp.buildings.riotCenter.upgrades.freeMedia / 2,0,100))>>
-	<</if>>
-	In the absence of noteworthy events, tensions in the arcology are able to relax.
-	<<set $SecExp.rebellions.tension = Math.trunc(Math.clamp($SecExp.rebellions.tension * 0.97,0,100))>>
-<</if>>
-<br>
-<<if $SecExp.rebellions.tension < _oldTension>>
-	<br>This week @@.green;tensions relaxed.@@<br>
-<<elseif $SecExp.rebellions.tension == _oldTension && $SecExp.rebellions.tension != 0>>
-	<br>This week @@.yellow;tensions did not change.@@<br>
-<<elseif $SecExp.rebellions.tension > _oldTension>>
-	<br>This week @@.red;tension rose@@ and
-	<<if $slaveRebellionEventFires == 1>>
-		@@.red;slave malcontent increased.@@<br>
-	<<elseif $citizenRebellionEventFires == 1>>
-		@@.red;citizen malcontent increased.@@<br>
-	<</if>>
-<<elseif !Number.isInteger($SecExp.rebellions.tension)>>
-	<br>Error: tension is outside accepted range.<br>
-<</if>>
-
-/* resets flags */
-<<set $slaveRebellionEventFires = 0>>
-<<set $citizenRebellionEventFires = 0>>
-
-/* rolls for rebellions */
-<<if $SecExp.rebellions.slaveProgress >= 100>>
-	<<if random(1,100) <= 80>>												/* 80% of firing a rebellion once progress is at 100 */
-		<<set $slaveRebellion = 1>>
-		<<set $SecExp.rebellions.slaveProgress = 0>>
-		<<set $SecExp.rebellions.citizenProgress *= 0.2>>
-	<<else>>
-		<<set $SecExp.rebellions.slaveProgress = 100>>
-	<</if>>
-<<elseif $SecExp.rebellions.citizenProgress >= 100>>
-	<<if random(1,100) <= 80>>
-		<<set $citizenRebellion = 1>>
-		<<set $SecExp.rebellions.citizenProgress = 0>>
-		<<set $SecExp.rebellions.slaveProgress *= 0.2>>
-	<<else>>
-		<<set $SecExp.rebellions.citizenProgress = 100>>
-	<</if>>
-<</if>>
-
-<<if $SecExp.settings.rebellion.force == 1 && $foughtThisWeek == 0>>
-	<<if random(1,100) <= 50>>
-		<<set $slaveRebellion = 1>>
-		<<set $citizenRebellion = 0>>
-	<<else>>
-		<<set $slaveRebellion = 0>>
-		<<set $citizenRebellion = 1>>
-	<</if>>
-<</if>>
-
-<<set _weekMod = 0.50>>
-<<if $week <= 30>>
-	<<set _weekMod = 0.75 + (0.01+($week/200))>>
-<<elseif $week <= 60>>
-	<<set _weekMod = 1 + (0.01+($week/300))>>
-<<elseif $week <= 90>>
-	<<set _weekMod = 1.25 + (0.01+($week/400))>>
-<<elseif $week <= 120>>
-	<<set _weekMod = 1.50 + (0.01+($week/500))>>
-<<else>>
-	<<set _weekMod = 1.75>>
-<</if>>
-
-/* resetting ID list */
-<<set $rebellingID = []>>
-
-/* if a rebellion fires determine amount of rebels and rebelling units */
-<<if $slaveRebellion == 1>>
-	<<set $engageRule = 0>>
-	<<set $SecExp.rebellions.lastEncounterWeeks = 0>>
-	<<set $leadingTroops = "assistant">>
-	/* calc how many slaves and citizens participate */
-	<<set _authFactor = Math.clamp(1 - ($SecExp.core.authority / 20000),0.4,0.6)>>
-	<<set _repFactor = Math.clamp($rep / 20000,0.4,0.6)>>
-	<<set _rebelPercent = 0.3 * _authFactor>>
-	<<set _irregularPercent = 0.2 * _repFactor>>
-	<<set $attackTroops = Math.clamp(Math.trunc($ASlaves * _rebelPercent * _weekMod) + random(-100,100),50,$ASlaves)>>
-	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
-	/* calc if units rebel */
-	<<for _i = 0; _i < $slaveUnits.length; _i++>>
-		<<if $slaveUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<<elseif $slaveUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<<elseif $slaveUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<for _i = 0; _i < $militiaUnits.length; _i++>>
-		<<if $militiaUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<<elseif $militiaUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<<elseif $militiaUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<for _i = 0; _i < $mercUnits.length; _i++>>
-		<<if $mercUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<<elseif $mercUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<<elseif $mercUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<set $attackEquip = Math.clamp($SecExp.edicts.weaponsLaw + random(-2,1),0,4)>>
-<<elseif $citizenRebellion == 1>>
-	<<set $engageRule = 0>>
-	<<set $SecExp.rebellions.lastEncounterWeeks = 0>>
-	<<set $leadingTroops = "assistant">>
-	/* calc how many citizens participate */
-	<<set _authFactor = Math.clamp(1 - ($SecExp.core.authority / 20000),0.4,0.6)>>
-	<<set _repFactor = Math.clamp($rep / 20000,0.4,0.6)>>
-	<<set _rebelPercent = 0.3 * _authFactor>>
-	<<set _irregularPercent = 0.2 * _repFactor>>
-	<<set $attackTroops = Math.clamp(Math.trunc($ACitizens * _rebelPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
-	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
-	/* calc if units rebel */
-	<<for _i = 0; _i < $militiaUnits.length; _i++>>
-		<<if $militiaUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<<elseif $militiaUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<<elseif $militiaUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($militiaUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<for _i = 0; _i < $slaveUnits.length; _i++>>
-		<<if $slaveUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<<elseif $slaveUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<<elseif $slaveUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($slaveUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<for _i = 0; _i < $mercUnits.length; _i++>>
-		<<if $mercUnits[_i].loyalty < 10>>
-			<<if random(1,100) <= 70>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<<elseif $mercUnits[_i].loyalty < 33>>
-			<<if random(1,100) <= 30>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<<elseif $mercUnits[_i].loyalty < 66>>
-			<<if random(1,100) <= 10>>
-				<<set $rebellingID.push($mercUnits[_i].ID)>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<set $attackEquip = Math.clamp($SecExp.edicts.weaponsLaw + random(-1,1),0,4)>>
-<<else>>
-	<<set $SecExp.rebellions.lastEncounterWeeks++>>
-<</if>>
\ No newline at end of file
diff --git a/src/Mods/SecExp/rebellionHandler.tw b/src/Mods/SecExp/rebellionHandler.tw
index 6bee0a3f1933229e6b4f8ca0b3bc9d50213a6ad2..aaeae9668b5faf77ec5a8c42ca695538bbad3912 100644
--- a/src/Mods/SecExp/rebellionHandler.tw
+++ b/src/Mods/SecExp/rebellionHandler.tw
@@ -2,7 +2,7 @@
 
 <<set $nextButton = " ", $nextLink = "attackReport", $encyclopedia = "Battles">>
 
-<<set _turns = $maxTurns * 2>>
+<<set _turns = 10 * 2>>
 <<set _attack = 0>>
 <<set _defense = 0>>
 <<set _morale = 0>>
@@ -18,30 +18,30 @@
 <<set _armyMod = 0>>
 
 /* calculates PC army stats */
-<<if $engageRule == 0>>
+<<if $SecExp.war.engageRule == 0>>
 	<<set _engageMod = 0.5>>
-<<elseif $engageRule == 1>>
+<<elseif $SecExp.war.engageRule == 1>>
 	<<set _engageMod = 0.75>>
-<<elseif $engageRule == 2>>
+<<elseif $SecExp.war.engageRule == 2>>
 	<<set _engageMod = 1>>
 <<else>>
 	<<set _engageMod = 1.4>>
 <</if>>
 
 <<if $week <= 30>>
-	<<set _irregularMod = $irregulars / 60>>
+	<<set _irregularMod = $SecExp.war.irregulars / 60>>
 <<elseif $week <= 60>>
-	<<set _irregularMod = $irregulars / 50>>
+	<<set _irregularMod = $SecExp.war.irregulars / 50>>
 <<elseif $week <= 90>>
-	<<set _irregularMod = $irregulars / 40>>
+	<<set _irregularMod = $SecExp.war.irregulars / 40>>
 <<elseif $week <= 120>>
-	<<set _irregularMod = $irregulars / 30>>
+	<<set _irregularMod = $SecExp.war.irregulars / 30>>
 <<else>>
-	<<set _irregularMod = $irregulars / 20>>
+	<<set _irregularMod = $SecExp.war.irregulars / 20>>
 <</if>>
-<<if $irregulars > 0>>
+<<if $SecExp.war.irregulars > 0>>
 	<<set _irregularMod = Math.trunc(_irregularMod)>>
-	<<set _unit = App.SecExp.getIrregularUnit("Militia", $irregulars, $attackEquip)>>
+	<<set _unit = App.SecExp.getIrregularUnit("Militia", $SecExp.war.irregulars, $SecExp.war.attacker.equip)>>
 	<<set _attack += _unit.attack * _irregularMod * 0.80>>
 	<<set _defense += _unit.defense * _irregularMod * 0.80>>
 	<<set _hp += _unit.hp>>
@@ -55,7 +55,7 @@
 <</if>>
 
 <<for _i = 0; _i < $militiaUnits.length; _i++>>
-	<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
+	<<if $militiaUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($militiaUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Militia", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -63,7 +63,7 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $slaveUnits.length; _i++>>
-	<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
+	<<if $slaveUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($slaveUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Slaves", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -71,7 +71,7 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $mercUnits.length; _i++>>
-	<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
+	<<if $mercUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($mercUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Mercs", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -121,34 +121,34 @@
 
 /* calculates rebelling army stats */
 <<if $week <= 30>>
-	<<set _armyMod = $attackTroops / 100>>
+	<<set _armyMod = $SecExp.war.attacker.troops / 100>>
 <<elseif $week <= 60>>
-	<<set _armyMod = $attackTroops / 90>>
+	<<set _armyMod = $SecExp.war.attacker.troops / 90>>
 <<elseif $week <= 90>>
-	<<set _armyMod = $attackTroops / 80>>
+	<<set _armyMod = $SecExp.war.attacker.troops / 80>>
 <<elseif $week <= 120>>
-	<<set _armyMod = $attackTroops / 70>>
+	<<set _armyMod = $SecExp.war.attacker.troops / 70>>
 <<else>>
-	<<set _armyMod = $attackTroops / 60>>
+	<<set _armyMod = $SecExp.war.attacker.troops / 60>>
 <</if>>
 <<set _armyMod = Math.trunc(_armyMod)>>
 
 <<set _rebellingSlaves = 0, _rebellingMilitia = 0, _rebellingMercs = 0>>
 <<if $slaveRebellion == 1>>
 	<<set _rebellingSlaves = 1>>
-	<<set _unit = App.SecExp.getIrregularUnit("Slaves", $attackTroops, $attackEquip)>>
+	<<set _unit = App.SecExp.getIrregularUnit("Slaves", $SecExp.war.attacker.troops, $SecExp.war.attacker.equip)>>
 <<else>>
 	<<set _rebellingMilitia = 1>>
-	<<set _unit = App.SecExp.getIrregularUnit("Militia", $attackTroops, $attackEquip)>>
+	<<set _unit = App.SecExp.getIrregularUnit("Militia", $SecExp.war.attacker.troops, $SecExp.war.attacker.equip)>>
 <</if>>
 <<set _enemyAttack += _unit.attack * _armyMod>>
 <<set _enemyDefense += _unit.defense * _armyMod>>
 <<set _enemyHp += _unit.hp>>
 
 <<for _i = 0; _i < $militiaUnits.length; _i++>>
-	<<if $militiaUnits[_i].active == 1 && $rebellingID.includes($militiaUnits[_i].ID)>>
+	<<if $militiaUnits[_i].active == 1 && $SecExp.war.rebellingID.includes($militiaUnits[_i].ID)>>
 		<<set _rebellingMilitia = 1>>
-		<<set $attackTroops += $militiaUnits[_i].troops>>
+		<<set $SecExp.war.attacker.troops += $militiaUnits[_i].troops>>
 		<<set $militiaUnits[_i].loyalty = 0>>
 		<<set _unit = App.SecExp.getUnit("Militia", _i)>>
 		<<set _enemyAttack += _unit.attack>>
@@ -157,9 +157,9 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $slaveUnits.length; _i++>>
-	<<if $slaveUnits[_i].active == 1 && $rebellingID.includes($slaveUnits[_i].ID)>>
+	<<if $slaveUnits[_i].active == 1 && $SecExp.war.rebellingID.includes($slaveUnits[_i].ID)>>
 		<<set _rebellingSlaves = 1>>
-		<<set $attackTroops += $slaveUnits[_i].troops>>
+		<<set $SecExp.war.attacker.troops += $slaveUnits[_i].troops>>
 		<<set $slaveUnits[_i].loyalty = 0>>
 		<<set _unit = App.SecExp.getUnit("Slaves", _i)>>
 		<<set _enemyAttack += _unit.attack>>
@@ -168,9 +168,9 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $mercUnits.length; _i++>>
-	<<if $mercUnits[_i].active == 1 && $rebellingID.includes($mercUnits[_i].ID)>>
+	<<if $mercUnits[_i].active == 1 && $SecExp.war.rebellingID.includes($mercUnits[_i].ID)>>
 		<<set _rebellingMercs = 1>>
-		<<set $attackTroops += $mercUnits[_i].troops>>
+		<<set $SecExp.war.attacker.troops += $mercUnits[_i].troops>>
 		<<set $mercUnits[_i].loyalty = 0>>
 		<<set _unit = App.SecExp.getUnit("Mercs", _i)>>
 		<<set _enemyAttack += _unit.attack>>
@@ -179,7 +179,7 @@
 	<</if>>
 <</for>>
 
-<<set _enemyMoraleTroopMod = Math.clamp($attackTroops / 100,1,10)>>
+<<set _enemyMoraleTroopMod = Math.clamp($SecExp.war.attacker.troops / 100,1,10)>>
 <<set _enemyMorale = 1.5 * (App.SecExp.BaseMilitiaUnit.morale * _rebellingMilitia + App.SecExp.BaseSlaveUnit.morale * _rebellingSlaves + App.SecExp.BaseMercUnit.morale * _rebellingMercs) / (_rebellingMilitia + _rebellingSlaves + _rebellingMercs)>>
 <<set _enemyMorale *= _enemyMoraleTroopMod>>
 <<set _enemyBaseHp = (App.SecExp.BaseMilitiaUnit.hp * _rebellingMilitia + App.SecExp.BaseSlaveUnit.hp * _rebellingSlaves + App.SecExp.BaseMercUnit.hp * _rebellingMercs) / (_rebellingMilitia + _rebellingSlaves + _rebellingMercs)>>
@@ -247,7 +247,7 @@ __Difficulty__:<br>
 	<br>morale increase due to troop numbers: +<<print _moraleTroopMod>>%
 <</if>>
 <br><br>__Rebels__:
-<br>enemy troops: <<print num(Math.round($attackTroops))>>
+<br>enemy troops: <<print num(Math.round($SecExp.war.attacker.troops))>>
 <br>enemy attack: <<print num(Math.round(_enemyAttack))>>
 <br>enemy defense: <<print num(Math.round(_enemyDefense))>>
 <br>enemy Hp: <<print num(Math.round(_enemyHp))>>
@@ -267,14 +267,14 @@ __Difficulty__:<br>
 	<<if $SecExp.settings.showStats == 1>> <br>player damage: <<print num(Math.round(_damage))>><</if>>
 	<<set _enemyHp -= _damage>>
 	<<if $SecExp.settings.showStats == 1>> <br>remaining enemy Hp: <<print num(Math.round(_enemyHp))>><</if>>
-	<<set $enemyLosses += _damage / _enemyBaseHp>>
+	<<set $SecExp.war.attacker.losses += _damage / _enemyBaseHp>>
 	<<set _moraleDamage = Math.clamp(_damage/ 2 + _damage / _enemyBaseHp,0,_damage*1.5)>>
 	<<set _enemyMorale -= _moraleDamage>>
 	<<if $SecExp.settings.showStats == 1>> <br>remaining enemy morale: <<print num(Math.round(_enemyMorale))>><</if>>
 	<<if _enemyHp <= 0 || _enemyMorale <= 0>>
 		<<if $SecExp.settings.showStats == 1>> <br>Victory!<</if>>
-		<<set $battleResult = 3>>
-		<<set $battleTurns = _i>>
+		<<set $SecExp.war.result = 3>>
+		<<set $SecExp.war.turns = _i>>
 		<<break>>
 	<</if>>
 
@@ -286,39 +286,39 @@ __Difficulty__:<br>
 	<<if $SecExp.settings.showStats == 1>> <br>enemy damage: <<print num(Math.round(_damage))>><</if>>
 	<<set _hp -= _damage*($SecExp.rebellions.sfArmor ? 0.85 : 1)>>
 	<<if $SecExp.settings.showStats == 1>> <br>remaining hp: <<print num(Math.round(_hp))>><</if>>
-	<<set $losses += _damage / _baseHp>>
+	<<set $SecExp.war.losses += _damage / _baseHp>>
 	<<set _moraleDamage = Math.clamp(_damage / 2 + _damage / _baseHp,0,_damage*1.5)>>
 	<<set _morale -= _moraleDamage>>
 	<<if $SecExp.settings.showStats == 1>> <br>remaining morale: <<print num(Math.round(_morale))>><</if>>
 	<<if _hp <= 0 || _morale <= 0>>
 		<<if $SecExp.settings.showStats == 1>> <br>Defeat!<</if>>
-		<<set $battleResult = -3>>
-		<<set $battleTurns = _i>>
+		<<set $SecExp.war.result = -3>>
+		<<set $SecExp.war.turns = _i>>
 		<<break>>
 	<</if>>
 <</for>>
-<<if $battleResult != 3 && $battleResult != -3>>
+<<if $SecExp.war.result != 3 && $SecExp.war.result != -3>>
 	<<if _morale > _enemyMorale>>
 		<<if $SecExp.settings.showStats == 1>> <br>Partial victory!<</if>>
-		<<set $battleResult = 2>>
+		<<set $SecExp.war.result = 2>>
 	<<elseif _morale < _enemyMorale>>
 		<<if $SecExp.settings.showStats == 1>> <br>Partial defeat!<</if>>
-		<<set $battleResult = -2>>
+		<<set $SecExp.war.result = -2>>
 	<</if>>
 <</if>>
 
-<<if $battleResult > 3 || $battleResult < -3>>
+<<if $SecExp.war.result > 3 || $SecExp.war.result < -3>>
 	<br><br>@@.red;Error: failed to determine battle result@@
 <</if>>
 
 <<if $SecExp.settings.showStats == 1>>
-	<<if $SecExp.settings.rebellion.gameOver == 1 && $battleResult == -3>>
+	<<if $SecExp.settings.rebellion.gameOver == 1 && $SecExp.war.result == -3>>
 		<br>[[Proceed|Gameover][$gameover = "Rebellion defeat"]]
 	<<else>>
 		<br>[[Proceed|rebellionReport]]
 	<</if>>
 <<else>>
-	<<if $SecExp.settings.rebellion.gameOver == 1 && $battleResult == -3>>
+	<<if $SecExp.settings.rebellion.gameOver == 1 && $SecExp.war.result == -3>>
 		<<set $gameover = "Rebellion defeat">> <<goto "Gameover">>
 	<<else>>
 		<<goto "rebellionReport">>
diff --git a/src/Mods/SecExp/rebellionOptions.tw b/src/Mods/SecExp/rebellionOptions.tw
index 8616b3b5c29f3b51ef81349ab1afff450c42be72..8dec36223fc81d66f1f8ee395fac74ce5cec7646 100644
--- a/src/Mods/SecExp/rebellionOptions.tw
+++ b/src/Mods/SecExp/rebellionOptions.tw
@@ -1,281 +1,149 @@
 :: rebellionOptions [nobr]
 
-<<set $nextButton = " ", $nextLink = "rebellionOptions", $encyclopedia = "Battles">>
+<<set $nextButton = " ", $encyclopedia = "Battles">>
+<<set _isSlaveRebellion = $slaveRebellion === 1, _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $SecExp.war.rebellingID.length>>
 
-<strong> <<if $slaveRebellion == 1>>Slave<<else>>Citizen<</if>> Rebellion!</strong>
+<strong> <<if _isSlaveRebellion>>Slave<<else>>Citizen<</if>> Rebellion!</strong>
 <hr>
 
-<<if $slaveRebellion == 1>>
-	In the end it happened, the slaves of your arcology dared took up arms and rose up against their betters. Your penthouse is flooded with reports from all over the arcology of small skirmishes between the rioting slaves and the security forces.
-	It appears <strong><<print num(Math.trunc($attackTroops))>></strong> rebels are in the streets right now, building barricades and freeing their peers. They are
-	<<if $attackEquip <= 0>>
-		<strong>poorly armed</strong>.
-	<<elseif $attackEquip == 1>>
-		<strong>lightly armed</strong>.
-	<<elseif $attackEquip == 2>>
-		<strong>decently armed</strong>.
-	<<elseif $attackEquip == 3>>
-		<strong>well armed</strong>.
-	<<elseif $attackEquip >= 4>>
-		<strong>extremely well armed</strong>.
-	<</if>>
-	<<if $irregulars > 0>>
-		<<print num(Math.trunc($irregulars))>> of your citizens took up arms to defend their arcology owner.
-	<</if>>
-	<<set _count = 0>>
-	<<if $rebellingID.length > 0>>
-		<br><br>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && ($rebellingID.includes($militiaUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$militiaUnits[_i].platoonName,
-				<<else>>
-					$militiaUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && ($rebellingID.includes($slaveUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$slaveUnits[_i].platoonName,
-				<<else>>
-					$slaveUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && ($rebellingID.includes($mercUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$mercUnits[_i].platoonName,
-				<<else>>
-					$mercUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		betrayed you and joined the insurrection.
-	<</if>>
-	<<set _count = 0>>
-	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $rebellingID.length>>
-	<<if _loyalUnits > 0>>
-		<br><br>
-		<<if $arcologyUpgrade.drones == 1>>Your security drones,<</if>>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$militiaUnits[_i].platoonName,
-				<<else>>
-					$militiaUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$slaveUnits[_i].platoonName,
-				<<else>>
-					$slaveUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$mercUnits[_i].platoonName,
-				<<else>>
-					$mercUnits[_i].platoonName
-				<</if>>
-			<</if>>
-		<</for>>
-		<<if $SF.Toggle && $SF.Active >= 1>>and $SF.Lower, <<print num($SF.ArmySize)>> strong<</if>>
-		are called to defend the arcology from this menace.
-	<<else>>
-		<<if $arcologyUpgrade.drones == 1>>Your security drones<<if $SF.Toggle && $SF.Active >= 1>>and $SF.Lower, <<print num($SF.ArmySize)>> strong<</if>> <</if>>
-		are called to defend the arcology from this menace.
-	<</if>>
-	<hr>
-<<else>>
-	In the end it happened, the citizens of your arcology dared took up arms and rose up against their betters. Your penthouse is flooded with reports from all over the arcology of small skirmishes between the rioting residents and the security forces.
-	It appears <<print num(Math.trunc($attackTroops))>> rebels are in the streets right now, building barricades and destroying your property. They are
-	<<if $attackEquip <= 0>>
-		<strong>poorly armed</strong>.
-	<<elseif $attackEquip == 1>>
-		<strong>lightly armed</strong>.
-	<<elseif $attackEquip == 2>>
-		<strong>decently armed</strong>.
-	<<elseif $attackEquip == 3>>
-		<strong>well armed</strong>.
-	<<elseif $attackEquip >= 4>>
-		<strong>extremely well armed</strong>.
-	<</if>>
-	<<if $irregulars > 0>>
-		<<print num(Math.trunc($irregulars))>> of your citizens took up arms to defend their arcology owner.
-	<</if>>
-	<<set _count = 0>>
-	<<if $rebellingID.length > 0>>
-		<br><br>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && ($rebellingID.includes($militiaUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$militiaUnits[_i].platoonName,
-				<<else>>
-					$militiaUnits[_i].platoonName
-				<</if>>
+In the end it happened, the <<if _isSlaveRebellion>>slaves<<else>>citizens<</if>>
+of your arcology dared took up arms and rose up against their betters. Your penthouse is flooded with reports from all over the arcology of small skirmishes between the rioting slaves and the security forces.
+It appears <strong><<print num(Math.trunc($SecExp.war.attacker.troops))>></strong> rebels are in the streets right now, building barricades and 
+<<if _isSlaveRebellion>>freeing their peers<<else>>destroying your property<</if>>.
+<<if $SecExp.war.attacker.equip <= 0>>
+	They are <strong>poorly armed</strong>.
+<<elseif $SecExp.war.attacker.equip == 1>>
+	They are <strong>lightly armed</strong>.
+<<elseif $SecExp.war.attacker.equip == 2>>
+	They are <strong>decently armed</strong>.
+<<elseif $SecExp.war.attacker.equip == 3>>
+	They are <strong>well armed</strong>.
+<<elseif $SecExp.war.attacker.equip >= 4>>
+	They are <strong>extremely well armed</strong>.
+<</if>>
+<<if $SecExp.war.irregulars > 0>>
+	<<print num(Math.trunc($SecExp.war.irregulars))>> of your citizens took up arms to defend their arcology owner.
+<</if>>
+<<set _count = 0>>
+<<if $SecExp.war.rebellingID.length > 0>>
+	<br><br>
+	<<for _unit range $militiaUnits>>
+		<<if _unit.active == 1 && ($SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < $SecExp.war.rebellingID.length>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && ($rebellingID.includes($slaveUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$slaveUnits[_i].platoonName,
-				<<else>>
-					$slaveUnits[_i].platoonName
-				<</if>>
+		<</if>>
+	<</for>>
+	<<for _unit range $slaveUnits>>
+		<<if _unit.active == 1 && ($SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < $SecExp.war.rebellingID.length>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && ($rebellingID.includes($mercUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < $rebellingID.length>>
-					$mercUnits[_i].platoonName,
-				<<else>>
-					$mercUnits[_i].platoonName
-				<</if>>
+		<</if>>
+	<</for>>
+	<<for _unit range $mercUnits>>
+		<<if _unit.active == 1 && ($SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < $SecExp.war.rebellingID.length>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		betrayed you and joined the insurrection.
-	<</if>>
-
-	<<set _count = 0>>
-	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $rebellingID.length>>
-	<<if _loyalUnits > 0>>
-		<br><br>
-		<<if $arcologyUpgrade.drones == 1>>Your security drones,<</if>>
-		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$militiaUnits[_i].platoonName,
-				<<else>>
-					$militiaUnits[_i].platoonName
-				<</if>>
+		<</if>>
+	<</for>>
+	betrayed you and joined the insurrection.
+<</if>>
+<<set _count = 0, _dualUnits = $arcologyUpgrade.drones === 1 && $SF.Toggle && $SF.Active >= 1>>
+<br><br> <<if $arcologyUpgrade.drones === 1>>Your security drones<</if>><<if _loyalUnits > 0>>,<</if>>
+<<if _loyalUnits > 0>>
+	<<for _unit range $militiaUnits>>
+		<<if _unit.active == 1 && (!$SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < _loyalUnits>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$slaveUnits[_i].platoonName,
-				<<else>>
-					$slaveUnits[_i].platoonName
-				<</if>>
+		<</if>>
+	<</for>>
+	<<for _unit range $slaveUnits>>
+		<<if _unit.active == 1 && (!$SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < _loyalUnits>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
-				<<set _count++>>
-				<<if _count < _loyalUnits>>
-					$mercUnits[_i].platoonName,
-				<<else>>
-					$mercUnits[_i].platoonName
-				<</if>>
+		<</if>>
+	<</for>>
+	<<for _unit range $mercUnits>>
+		<<if _unit.active == 1 && (!$SecExp.war.rebellingID.includes(_unit.ID))>>
+			<<set _count++>>
+			<<if _count < _loyalUnits>>
+				_unit.platoonName,
+			<<else>>
+				_unit.platoonName
 			<</if>>
-		<</for>>
-		<<if $SF.Toggle && $SF.Active >= 1>>and $SF.Lower, <<print num($SF.ArmySize)>> strong<</if>>
-		are called to defend the arcology from this menace.
-	<<else>>
-		<<if $arcologyUpgrade.drones == 1>>Your security drones<<if $SF.Toggle && $SF.Active >= 1>>and $SF.Lower, <<print num($SF.ArmySize)>> strong<</if>><</if>>
-		are called to defend the arcology from this menace.
+		<</if>>
+	<</for>>
+<</if>>
+<<if $arcologyUpgrade.drones === 1 || $SF.Toggle && $SF.Active >= 1>>
+	<<if $SF.Toggle && $SF.Active >= 1>>
+		<<if _dualUnits || _loyalUnits > 0>>and $SF.Lower,<<else>><<= capFirstChar($SF.Lower)>><</if>> <<print num($SF.ArmySize)>> strong
 	<</if>>
-	<hr>
+	are called to defend the arcology from this menace.
 <</if>>
-
 The confined spaces of the arcology and the number of vital yet delicate systems within its walls do not allow a lot of tactical flexibility. This will be a long and strenuous fight, street after street, barricade after barricade.
 In order to preserve the structural integrity of the building and the lives of our civilians, we will have to limit our firepower.
-<br><<link "Only light firearms and nonlethal weapons">>
-	<<set $engageRule = 0>>
-	<<replace "#engage">>
-		<br>Your troops will use only nonlethal weapons or light firearms to limit to the maximum the collateral damage. This will however weaken our troops considerably.
-	<</replace>>
-<</link>>
-<br><<link "No heavy ordnance">>
-	<<set $engageRule = 1>>
-	<<replace "#engage">>
-		<br>Your troops will limit the use of explosives and heavy weapons to limit considerably the collateral damage. This will however weaken our troops.
-	<</replace>>
-<</link>>
-<br><<link "Normal engagement rules">>
-	<<set $engageRule = 2>>
-	<<replace "#engage">>
-		<br>Your troops will not limit their arsenal. This will put the structure and your citizens at risk, but our troops will be at full capacity.
-	<</replace>>
-<</link>>
+<br>[[Only light firearms and nonlethal weapons|rebellionOptions][$SecExp.war.engageRule = 0]]
+<br>[[No heavy ordnance|rebellionOptions][$SecExp.war.engageRule = 1]]
+<br>[[Normal engagement rules|rebellionOptions][$SecExp.war.engageRule = 2]]
 <<if $SecExp.buildings.riotCenter && $SecExp.buildings.riotCenter.advancedRiotEquip == 1>>
-	<br><<link "Advanced riot protocol">>
-		<<set $engageRule = 3>>
-		<<replace "#engage">>
-			<br>Your troops will make use of the special weaponry, equipment and infrastructure developed by the riot control center to surgically eliminate rebels and dissidents with little to no collateral damage.
-		<</replace>>
-	<</link>>
+	<br>[[Advanced riot protocol|rebellionOptions][$SecExp.war.engageRule = 3]]
 <</if>>
 
-<span id="engage">
+<<if $SecExp.war.engageRule === 0>>
 	<br>Your troops will use only nonlethal weapons or light firearms to limit to the maximum the collateral damage. This will however weaken our troops considerably.
-</span>
+<<elseif $SecExp.war.engageRule === 1>>
+	<br>Your troops will limit the use of explosives and heavy weapons to limit considerably the collateral damage. This will however weaken our troops.
+<<elseif $SecExp.war.engageRule === 2>>
+	<br>Your troops will not limit their arsenal. This will put the structure and your citizens at risk, but our troops will be at full capacity.
+<<elseif $SecExp.war.engageRule === 3>>
+	<br>Your troops will make use of the special weaponry, equipment and infrastructure developed by the riot control center to surgically eliminate rebels and dissidents with little to no collateral damage.
+<</if>>
 
 <br><br>We can dedicate some of our forces to the protection of the vital parts of the arcology, doing so will prevent the failure of said systems, but will also take away strength from our assault.
 <<if $garrison.penthouse == 0>>
-	<br><<link "Garrison the penthouse" "rebellionOptions">>
-		<<set $garrison.penthouse = 1>>
-	<</link>>
+	<br>[[Garrison the penthouse|rebellionOptions][$garrison.penthouse = 1]]
 <<else>>
 	<br>Troops will be dispatched to the penthouse.
-	<<link "Discard the order" "rebellionOptions">>
-		<<set $garrison.penthouse = 0>>
-	<</link>>
+	[[Discard the order|rebellionOptions][$garrison.penthouse = 0]]
 <</if>>
 <<if $garrison.reactor == 0>>
-	<br><<link "Garrison the reactors" "rebellionOptions">>
-		<<set $garrison.reactor = 1>>
-	<</link>>
+	<br>[[Garrison the reactors|rebellionOptions][$garrison.reactor = 1]]
 <<else>>
 	<br>Troops will be dispatched to the reactors.
-	<<link "Discard the order" "rebellionOptions">>
-		<<set $garrison.reactor = 0>>
-	<</link>>
+	[[Discard the order|rebellionOptions][$garrison.reactor = 0]]
 <</if>>
 <<if $garrison.assistant == 0>>
-	<br><<link "Garrison the assistant's central CPU" "rebellionOptions">>
-		<<set $garrison.assistant = 1>>
-	<</link>>
+	<br>[[Garrison the assistant's central CPU|rebellionOptions][$garrison.assistant = 1]]
 <<else>>
 	<br>Troops will be dispatched to the assistant's central CPU.
-	<<link "Discard the order" "rebellionOptions">>
-		<<set $garrison.assistant = 0>>
-	<</link>>
+	[[Discard the order|rebellionOptions][$garrison.assistant = 0]]
 <</if>>
 <<if $garrison.waterway == 0>>
-	<br><<link "Garrison the waterways" "rebellionOptions">>
-		<<set $garrison.waterway = 1>>
-	<</link>>
+	<br>[[Garrison the waterways|rebellionOptions][$garrison.waterway = 1]]
 <<else>>
 	<br>Troops will be dispatched to the waterways.
-	<<link "Discard the order" "rebellionOptions">>
-		<<set $garrison.waterway = 0>>
-	<</link>>
+	[[Discard the order|rebellionOptions][$garrison.waterway = 0]]
 <</if>>
 
-<br><br> <<replenishAllUnits>>
-<<link "Proceed" "rebellionHandler">>
-	<<set $battleResult = 4, $foughtThisWeek = 1>>	/* sets $battleResult value outside accepted range (-3, 3) to avoid evaluation problems */
-<</link>>
-<br>
-<<link "Surrender" "rebellionReport">>
-	<<set $battleResult = -1, $foughtThisWeek = 1>>
-<</link>>
+<br><br> <<replenishAllUnits>> <br>
+[[Proceed|rebellionHandler][$SecExp.war.result = 4, $foughtThisWeek = 1]] /* sets $SecExp.war.result to a value outside accepted range (-3,3) to avoid evaluation problems */
+<br>[[Surrender|rebellionReport][$SecExp.war.result = -1, $foughtThisWeek = 1]]
\ No newline at end of file
diff --git a/src/Mods/SecExp/rebellionReport.tw b/src/Mods/SecExp/rebellionReport.tw
index 382d12c303bc8d06a1b9abbe64aeb1d1fc0b78dc..75d8742a2c7a5f00f02f44d3a35db3fa1d303210 100644
--- a/src/Mods/SecExp/rebellionReport.tw
+++ b/src/Mods/SecExp/rebellionReport.tw
@@ -3,106 +3,106 @@
 <<set $nextButton = "Continue", $nextLink = "Scheduled Event", $encyclopedia = "Battles">>
 <<set _oldRep = $rep>>
 <<set _oldAuth = $SecExp.core.authority>>
-<<set $enemyLosses = Math.trunc($enemyLosses)>>
-<<if $enemyLosses > $attackTroops>>
-	<<set $enemyLosses = $attackTroops>>
+<<set $SecExp.war.attacker.losses = Math.trunc($SecExp.war.attacker.losses)>>
+<<if $SecExp.war.attacker.losses > $SecExp.war.attacker.troops>>
+	<<set $SecExp.war.attacker.losses = $SecExp.war.attacker.troops>>
 <</if>>
-<<set $SecExp.core.totalKills += $enemyLosses>>
-<<set $losses = Math.trunc($losses)>>
-<<if $battleResult == 3>>
+<<set $SecExp.core.totalKills += $SecExp.war.attacker.losses>>
+<<set $SecExp.war.losses = Math.trunc($SecExp.war.losses)>>
+<<if $SecExp.war.result == 3>>
 	<strong>Victory!</strong>
 	<<set $SecExp.rebellions.victories++>>
-<<elseif $battleResult == -3>>
+<<elseif $SecExp.war.result == -3>>
 	<strong>Defeat!</strong>
 	<<set $SecExp.rebellions.losses++>>
-<<elseif $battleResult == 2>>
+<<elseif $SecExp.war.result == 2>>
 	<strong>Partial victory!</strong>
 	<<set $SecExp.rebellions.victories++>>
-<<elseif $battleResult == -2>>
+<<elseif $SecExp.war.result == -2>>
 	<strong>Partial defeat!</strong>
 	<<set $SecExp.rebellions.losses++>>
-<<elseif $battleResult == -1>>
+<<elseif $SecExp.war.result == -1>>
 	<strong>We surrendered</strong>
 	<<set $SecExp.rebellions.losses++>>
 <</if>>
 <hr>
 
 <<if $slaveRebellion == 1>>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was inflamed by the fires of rebellion. <<print num(Math.trunc($attackTroops))>> rebels from all over the structure dared rise up against their owners and conquer their freedom through blood. Our defense force, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, fought with them street by street
-	<<if $enemyLosses != $attackTroops>>
-		inflicting <<print num(Math.trunc($enemyLosses))>> casualties, while sustaining <<if $losses > 1>><<print num(Math.trunc($losses))>> casualties<<elseif $losses > 0>>a casualty<<else>>zero<</if>> themselves.
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was inflamed by the fires of rebellion. <<print num(Math.trunc($SecExp.war.attacker.troops))>> rebels from all over the structure dared rise up against their owners and conquer their freedom through blood. Our defense force, <<print num(Math.trunc(App.SecExp.battle.troopCount()))>> strong, fought with them street by street
+	<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+		inflicting <<print num(Math.trunc($SecExp.war.attacker.losses))>> casualties, while sustaining <<if $SecExp.war.losses > 1>><<print num(Math.trunc($SecExp.war.losses))>> casualties<<elseif $SecExp.war.losses > 0>>a casualty<<else>>zero<</if>> themselves.
 	<<else>>
-		completely annihilating their troops, while sustaining <<if $losses > 1>><<print num(Math.trunc($losses))>> casualties<<elseif $losses > 0>>a casualty<<else>>zero casualties<</if>>.
+		completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>><<print num(Math.trunc($SecExp.war.losses))>> casualties<<elseif $SecExp.war.losses > 0>>a casualty<<else>>zero casualties<</if>>.
 	<</if>>
 	<<if $SecExp.rebellions.sfArmor>>
 		More units were able to survive thanks to wearing $SF.Lower's combat armor suits.
 	<</if>>
-	<<set $NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * $enemyLosses),
-	$menials -= Math.trunc(($menials / $ASlaves) * $enemyLosses),
-	$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * $enemyLosses),
-	$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * $enemyLosses)>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<set $NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * $SecExp.war.attacker.losses),
+	$menials -= Math.trunc(($menials / $ASlaves) * $SecExp.war.attacker.losses),
+	$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * $SecExp.war.attacker.losses),
+	$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * $SecExp.war.attacker.losses)>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided: our men easily stopped the disorganized revolt in a few well aimed assaults.
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the disorganized revolt with several well aimed assaults.
 		<<else>>
 			The fight was long and hard, but in the end our men stopped the revolt before it could accumulate momentum.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided: our men were easily crushed by the furious charge of the rebels.
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the rebels proved too much to handle for our men.
 		<<else>>
 			The fight was long and hard, but despite their bravery the rebels proved too much for our men.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		The fight was long and hard, but in the end our men managed to stop the revolt, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. In the end, our men had to yield to the rebelling slaves, which were fortunately unable to capitalize on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender; they obediently stand down.
 	<</if>>
 
 	/* effects */
-	<<if $battleResult == 3>>
+	<<if $SecExp.war.result == 3>>
 		Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ increased.
 		<<run repX(random(800,1000), "war")>>
 		<<set $SecExp.core.authority += random(800,1000)>>
 		<br>Many of the rebelling slaves were recaptured and punished. The instigators were executed one after another in a public trial that lasted for almost three days.
 		<<set $NPCSlaves -= random(10,30)>>
-	<<elseif $battleResult == -3>>
+	<<elseif $SecExp.war.result == -3>>
 		Due to your defeat, your @@.red;reputation@@ and @@.red;authority@@ decreased.
 		<<run repX(random(-800,-1000), "war")>>
 		<<set $SecExp.core.authority -= random(800,1000)>>
 		<br>After the battle most of the rebelling slaves managed to escape, while others remained in the arcology for days looting and hunting their former masters. The arcology will bear the scars of this day for a long time.
 		<<set $lowerClass -= random(50,100)>>
-		<<set _lostSlaves = Math.trunc(($attackTroops - $enemyLosses) * 0.8),
+		<<set _lostSlaves = Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.8),
 		$NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * _lostSlaves),
 		$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 		$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
 		$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 		<<set $rebelDefeatAftermath = 5>>
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		Due to your defeat, your @@.red;reputation@@ and @@.red;authority@@ decreased.
 		<<run repX(random(-600,-800), "war")>>
 		<<set $SecExp.core.authority -= random(600,800)>>
 		<br>After the battle most of the rebelling slaves managed to escape, while others remained in the arcology for days looting and hunting their former masters. The arcology will bear the scars of this day for a long time.
 		<<set $lowerClass -= random(40,80)>>
-		<<set _lostSlaves = Math.trunc(($attackTroops - $enemyLosses) * 0.8),
+		<<set _lostSlaves = Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.8),
 		$NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * _lostSlaves),
 		$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 		$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
 		$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 		<<set $rebelDefeatAftermath = 3>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ increased.
 		<<run repX(random(600,180), "war")>>
 		<<set $SecExp.core.authority += random(600,800)>>
 		<br>Many of the rebelling slaves were recaptured and punished. The instigators were executed one after another in a public trial that lasted for almost three days.
 		<<set $NPCSlaves -= random(10,30)>>
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		Rather than waste the lives of your men you decided to surrender, hoping the rebels will cause less damage if you indulge them, this is however a big hit to your status. Your @@.red;reputation@@ and @@.red;authority@@ are significantly impacted.
 		<<run repX(random(-1000,-1200), "war")>>
 		<<set $SecExp.core.authority -= random(1000,1200)>>
@@ -152,7 +152,7 @@
 		<</if>>
 		<br>After the battle most of the rebelling slaves managed to escape, while others remained in the arcology for days looting and hunting their former masters. The arcology will bear the scars of this day for a long time.
 		<<set $lowerClass -= random(50,100)>>
-		<<set _lostSlaves = Math.trunc(($attackTroops - $enemyLosses) * 0.8),
+		<<set _lostSlaves = Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.8),
 		$NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * _lostSlaves),
 		$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 		$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
@@ -160,76 +160,76 @@
 		<<set $rebelDefeatAftermath = 5>>
 	<</if>>
 <<else>>
-	Today, <<= asDateString($week, random(0,7))>>, our arcology was inflamed by the fires of rebellion. <<print num(Math.trunc($attackTroops))>> rebels from all over the structure dared rise up to dethrone their arcology owner. Our defense force, <<print num(App.SecExp.battle.troopCount())>> strong, fought with them street by street
-	<<if $enemyLosses != $attackTroops>>
-		inflicting <<print num(Math.trunc($enemyLosses))>> casualties, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties <<else>> a casualty<</if>> themselves.
+	Today, <<= asDateString($week, random(0,7))>>, our arcology was inflamed by the fires of rebellion. <<print num(Math.trunc($SecExp.war.attacker.troops))>> rebels from all over the structure dared rise up to dethrone their arcology owner. Our defense force, <<print num(App.SecExp.battle.troopCount())>> strong, fought with them street by street
+	<<if $SecExp.war.attacker.losses != $SecExp.war.attacker.troops>>
+		inflicting <<print num(Math.trunc($SecExp.war.attacker.losses))>> casualties, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties <<else>> a casualty<</if>> themselves.
 	<<else>>
-		completely annihilating their troops, while sustaining <<if $losses > 1>> <<print num(Math.trunc($losses))>> casualties <<else>> a casualty.<</if>>
+		completely annihilating their troops, while sustaining <<if $SecExp.war.losses > 1>> <<print num(Math.trunc($SecExp.war.losses))>> casualties <<else>> a casualty.<</if>>
 	<</if>>
 	<<if $SecExp.rebellions.sfArmor>>
 		More units were able to survive thanks to wearing $SF.Lower's combat armor suits.
 	<</if>>
-	<<set $NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * $enemyLosses),
-	$menials -= Math.trunc(($menials / $ASlaves) * $enemyLosses),
-	$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * $enemyLosses),
-	$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * $enemyLosses)>>
-	<<if $battleResult == 3>>
-		<<if $battleTurns <= 5>>
+	<<set $NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * $SecExp.war.attacker.losses),
+	$menials -= Math.trunc(($menials / $ASlaves) * $SecExp.war.attacker.losses),
+	$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * $SecExp.war.attacker.losses),
+	$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * $SecExp.war.attacker.losses)>>
+	<<if $SecExp.war.result == 3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men easily stopped the disorganized revolt in a few well aimed assaults.
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard, but in the end our men stopped the disorganized revolt with several well aimed assaults.
 		<<else>>
 			The fight was long and hard, but in the end our men stopped the revolt before it could accumulate momentum.
 		<</if>>
-	<<elseif $battleResult == -3>>
-		<<if $battleTurns <= 5>>
+	<<elseif $SecExp.war.result == -3>>
+		<<if $SecExp.war.turns <= 5>>
 			The fight was quick and one sided, our men were easily crushed by the furious charge of the rebels.
-		<<elseif $battleTurns <= 7>>
+		<<elseif $SecExp.war.turns <= 7>>
 			The fight was hard and in the end the rebels proved too much to handle for our men.
 		<<else>>
 			The fight was long and hard, but despite their bravery the rebels proved too much for our men.
 		<</if>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		The fight was long and hard, but in the end our men managed to stop the revolt, though not without difficulty.
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		The fight was long and hard. Our men in the end had to yield to the rebelling slaves, which were fortunately unable to fully capitalize on their victory.
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		You gave your troops the order to surrender, obediently they stand down.
 	<</if>>
 
 	/* effects */
-	<<if $battleResult == 3>>
+	<<if $SecExp.war.result == 3>>
 		Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ increased.
 		<<run repX(random(800,1000), "war")>>
 		<<set $SecExp.core.authority += random(800,1000)>>
 		<br>Many of the rebelling citizens were captured and punished, many others enslaved. The instigators were executed one after another in a public trial that lasted for almost three days.
 		<<set $lowerClass -= random(10,30)>>
-	<<elseif $battleResult == -3>>
+	<<elseif $SecExp.war.result == -3>>
 		Due to your defeat, your @@.red;reputation@@ and @@.red;authority@@ decreased.
 		<<run repX(random(-800,-1000), "war")>>
 		<<set $SecExp.core.authority -= random(800,1000)>>
 		<br>After the battle most of the rebelling citizens remained in the arcology for days looting and hunting their former arcology. We will bear the scars of this day for a long time.
-		<<set $lowerClass -= Math.trunc(($attackTroops - $enemyLosses) * 0.8)>>
+		<<set $lowerClass -= Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.8)>>
 		<<set $rebelDefeatAftermath = 5>>
-	<<elseif $battleResult == -2>>
+	<<elseif $SecExp.war.result == -2>>
 		Due to your defeat, your @@.red;reputation@@ and @@.red;authority@@ decreased.
 		<<run repX(random(-600,-800), "war")>>
 		<<set $SecExp.core.authority -= random(600,800)>>
 		<br>After the battle most of the rebelling citizens remained in the arcology for days looting and hunting their former arcology. We will bear the scars of this day for a long time.
 		<<set $lowerClass -= random(40,80)>>
-		<<set _lostSlaves = Math.trunc(($attackTroops - $enemyLosses) * 0.6),
+		<<set _lostSlaves = Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.6),
 		$NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * _lostSlaves),
 		$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 		$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
 		$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 		<<set $rebelDefeatAftermath = 3>>
-	<<elseif $battleResult == 2>>
+	<<elseif $SecExp.war.result == 2>>
 		Thanks to your victory, your @@.green;reputation@@ and @@.darkviolet;authority@@ increased.
 		<<run repX(random(600,180), "war")>>
 		<<set $SecExp.core.authority += random(600,800)>>
 		<br>Many of the rebelling citizens were captured and punished, many others enslaved. The instigators were executed one after another in a public trial that lasted for almost three days.
 		<<set $NPCSlaves -= random(10,30)>>
-	<<elseif $battleResult == -1>>
+	<<elseif $SecExp.war.result == -1>>
 		Rather than waste the lives of your men you decided to surrender, hoping the rebels will cause less damage if you indulge them, this is however a big hit to your status. Your @@.red;reputation@@ and @@.red;authority@@ are significantly impacted.
 		<<run repX(random(-1000,-1200), "war")>>
 		<<set $SecExp.core.authority -= random(1000,1200)>>
@@ -279,7 +279,7 @@
 		<</if>>
 		<br>After the battle most of the rebelling citizens remained in the arcology for days looting and hunting their former arcology. We will bear the scars of this day for a long time.
 		<<set $lowerClass -= random(50,100)>>
-		<<set _lostSlaves = Math.trunc(($attackTroops - $enemyLosses) * 0.8),
+		<<set _lostSlaves = Math.trunc(($SecExp.war.attacker.troops - $SecExp.war.attacker.losses) * 0.8),
 		$NPCSlaves -= Math.trunc(($NPCSlaves / $ASlaves) * _lostSlaves),
 		$menials -= Math.trunc(($menials / $ASlaves) * _lostSlaves),
 		$fuckdolls -= Math.trunc(($fuckdolls / $ASlaves) * _lostSlaves),
@@ -289,8 +289,8 @@
 <</if>>
 
 /* engage rules */
-<<if $battleResult != -1>>
-	<<if $engageRule == 0>>
+<<if $SecExp.war.result != -1>>
+	<<if $SecExp.war.engageRule == 0>>
 		Since you ordered your troops to limit their weaponry to low caliber or nonlethal, the arcology reported only @@.red;minor damage.@@ Most citizens and non involved slaves remained unharmed, though some casualties between the civilians were inevitable.
 		A few businesses were looted and burned, but the damage was pretty limited.
 		<<set $arcRepairTime += 3, IncreasePCSkills('engineering', 0.1)>>
@@ -335,7 +335,7 @@
 			$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 			<<set $arcologies[0].prosperity -= random(12)>>
 		<</if>>
-	<<elseif $engageRule == 1>>
+	<<elseif $SecExp.war.engageRule == 1>>
 		You ordered your troops to limit their weaponry to non-heavy, non-explosive, because of this the arcology reported @@.red;moderate damage.@@ Most citizens and non involved slaves remained unharmed or only lightly wounded, but many others did not make it. Unfortunately casualties between the civilians were inevitable.
 		A few businesses were looted and burned, but the damage was pretty limited.
 		<<set $arcRepairTime += 5, IncreasePCSkills('engineering', 0.1)>>
@@ -380,7 +380,7 @@
 			$menialBioreactors -= Math.trunc(($menialBioreactors / $ASlaves) * _lostSlaves)>>
 			<<set $arcologies[0].prosperity -= random(14)>>
 		<</if>>
-	<<elseif $engageRule == 2>>
+	<<elseif $SecExp.war.engageRule == 2>>
 		Since you did not apply any restriction on the weapons your forces should use, the arcology reported @@.red;heavy damage.@@ Many citizens and uninvolved slaves are reported killed or missing. Casualties between the civilians were inevitable.
 		Many businesses were damaged during the battle either by the fight itself, by fires which spread unchecked for hours or by looters.
 		<<set $arcRepairTime += 7, IncreasePCSkills('engineering', 0.1)>>
@@ -473,12 +473,8 @@
 		<</if>>
 	<</if>>
 <</if>>
-<<if $lowerClass < 0>>
-	<<set $lowerClass = 0>>
-<</if>>
-<<if $NPCSlaves < 0>>
-	<<set $NPCSlaves = 0>>
-<</if>>
+<<set $lowerClass = Math.max($lowerClass, 0)>>
+<<set $NPCSlaves = Math.max($NPCSlaves, 0)>>
 
 /* garrisons */
 <<if $garrison.reactor == 0>>
@@ -885,12 +881,6 @@
 <<include "unitsRebellionReport">>
 
 /* resets variables and flags */
-<<set $attackTroops = 0>>
-<<set $attackEquip = 0>>
-<<set $enemyLosses = 0>>
-<<set $losses = 0>>
-<<set $battleTurns = 0>>
-<<set $irregulars = 0>>
 <<if $slaveRebellion == 1>>
 	<<set $SecExp.rebellions.slaveProgress = 0>>
 	<<set $SecExp.rebellions.citizenProgress = Math.clamp($SecExp.rebellions.citizenProgress - random(50,100), 0, 100)>>
diff --git a/src/Mods/SecExp/securityReport.tw b/src/Mods/SecExp/securityReport.tw
deleted file mode 100644
index ce5a7a471e275b47d0102884ba6219598cabf544..0000000000000000000000000000000000000000
--- a/src/Mods/SecExp/securityReport.tw
+++ /dev/null
@@ -1,517 +0,0 @@
-:: securityReport [nobr]
-
-/* init */
-<<if $ACitizens > $oldACitizens>>
-	<<set _immigration = $ACitizens - $oldACitizens, _emigration = 0>>
-<<else>>
-	<<set _emigration = $oldACitizens - $ACitizens, _immigration = 0>> /*takes into account citizens leaving and those getting enslaved*/
-<</if>>
-<<set _secGrowth = 0>>
-<<set _crimeGrowth = 0>>
-<<set _recruitsMultiplier = 1>>
-
-<<if $useTabs == 0>>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>__Security__<br><</if>>
-<<if $terrain === "oceanic">>
-	Due to the @@.green;massive economic and logistical challenges@@ of attacking an oceanic arcology, your security force(s)
-<<else>>
-	The @@.red;easily manageable economic and logistical challenges@@ of attacking an $terrain arcology ensure that your security force(s) do not
-<</if>>
- have the luxury of focusing exclusively on internal matters.<br>
-<<if (def $SecExp.buildings.secHub) && ($SecExp.buildings.secHub.menials > 0)>>
-	<<print num($SecExp.buildings.secHub.menials)>> slaves work to improve the security of your arcology, while your
-<<else>>
-	Your
-<</if>>
-
-<<if $mercenaries >= 1 && $arcologyUpgrade.drones == 1>>
-	mercenaries and security drones tirelessly patrol the streets to keep them safe.
-<<elseif $arcologyUpgrade.drones == 1>>
-	security drones tirelessly patrol the arcology to keep it safe.
-<<else>>
-	loyal subordinates try to keep the arcology safe to the best of their abilities.
-<</if>>
-
-/* security modifiers */
-<<if $PC.career == "mercenary">>
-	Your past life as a mercenary makes it easier to manage the security of the arcology.
-	<<set _secGrowth += 1>>
-<</if>>
-<<if $SecExp.smilingMan.progress === 10>>
-	The ex-criminal known to the world as The Smiling Man puts their impressive skills to work, dramatically increasing the efficiency of your security measures.
-	<<set _secGrowth += 2>>
-<</if>>
-<<if $ACitizens + $ASlaves <= 5000>>
-	The small number of residents makes their job easier.
-	<<set _secGrowth += 2>>
-<<elseif $ACitizens + $ASlaves <= 7500>>
-	The fairly low number of residents makes their job a little easier.
-	<<set _secGrowth += 1>>
-<<elseif $ACitizens + $ASlaves <= 10000>>
-	The fairly high number of residents makes their job a little harder.
-	<<set _secGrowth -= -0.5>>
-<<elseif $ACitizens + $ASlaves <= 15000>>
-	The high number of residents makes their job harder.
-	<<set _secGrowth -= 1>>
-<<else>>
-	The extremely high number of residents makes their job a lot harder.
-	<<set _secGrowth -= 2>>
-<</if>>
-<<if _immigration >= 0 && _emigration == 0>>
-	<<if _immigration < 50>>
-		The limited number of immigrants that reached the arcology this week does not have any serious impact on the efficiency of current security measures.
-		<<set _secGrowth += 0.5>>
-	<<elseif _immigration < 150>>
-		The number of immigrants that reached the arcology this week is high enough to complicate security protocols.
-		<<set _secGrowth -= 0.2>>
-	<<elseif _immigration < 300>>
-		The high number of immigrants that reached the arcology this week complicates security protocols.
-		<<set _secGrowth -= 0.5>>
-	<<elseif _immigration < 500>>
-		The high number of immigrants that reached the arcology this week severely complicates security protocols.
-		<<set _secGrowth -= 1>>
-	<<else>>
-		The extremely high number of immigrants that reached the arcology this week severely complicates security protocols.
-		<<set _secGrowth -= 2>>
-	<</if>>
-<</if>>
-<<if $visitors < 300>>
-	The limited number of visitors coming and going did not have any serious impact on the efficiency of current security measures.
-	<<set _secGrowth += 0.5>>
-<<elseif _immigration < 750>>
-	The number of visitors coming and going somewhat complicates security protocols.
-	<<set _secGrowth -= 0.2>>
-<<elseif _immigration < 1500>>
-	The high number of visitors coming and going complicates security protocols.
-	<<set _secGrowth -= 0.5>>
-<<elseif _immigration < 2500>>
-	The high number of visitors coming and going greatly complicates security protocols.
-	<<set _secGrowth -= 1>>
-<<else>>
-	The extremely high number of visitors coming and going severely complicates security protocols.
-	<<set _secGrowth -= 2>>
-<</if>>
-<<if _emigration != 0 && _immigration == 0>>
-	<<if _emigration < 100>>
-		The limited reduction in citizens this week does not have any serious impact on the efficiency of current security measures.
-		<<set _secGrowth += 0.5>>
-	<<elseif _emigration < 300>>
-		The reduction in citizens this week is high enough to complicate security protocols.
-		<<set _secGrowth -= 0.2>>
-	<<elseif _emigration < 600>>
-		The large reduction in citizens this week complicates security protocols.
-		<<set _secGrowth -= 0.5>>
-	<<elseif _emigration < 1000>>
-		The huge reduction in citizens this week severely complicates security protocols.
-		<<set _secGrowth -= 1>>
-	<<else>>
-		The extreme reduction in citizens this week severely complicates security protocols.
-		<<set _secGrowth -= 2>>
-	<</if>>
-<</if>>
-<<if $SecExp.core.crimeLow < 20>>
-	Crime is a distant problem in the arcology, which makes improving security easier.
-	<<set _secGrowth += 1>>
-<<elseif $SecExp.core.crimeLow < 40>>
-	Crime is a minor problem in the arcology, not serious enough to disrupt security efforts.
-<<elseif $SecExp.core.crimeLow < 60>>
-	Crime is an issue in the arcology, which makes improving security harder.
-	<<set _secGrowth -= 0.5>>
-<<elseif $SecExp.core.crimeLow < 80>>
-	Crime is an overbearing problem in the arcology, which makes improving security a lot harder.
-	<<set _secGrowth -= 1>>
-<<else>>
-	Crime is sovereign in the arcology, which makes improving security extremely difficult.
-	<<set _secGrowth -= 2>>
-<</if>>
-<<if $SecExp.core.authority < 5000>>
-	The low authority you hold on the arcology hampers the efforts of your security department.
-	<<set _secGrowth -= 1>>
-<<elseif $SecExp.core.authority < 7500>>
-	The limited authority you hold on the arcology hampers the efforts of your security department.
-	<<set _secGrowth -= 0.5>>
-<<elseif $SecExp.core.authority < 10000>>
-	The authority you hold on the arcology does not significantly impact the efforts of your security department.
-<<elseif $SecExp.core.authority < 15000>>
-	The high authority you hold on the arcology facilitates the security department's work.
-	<<set _secGrowth += 0.5>>
-<<else>>
-	The absolute authority you hold on the arcology makes the security department's work a lot easier.
-	<<set _secGrowth += 1>>
-<</if>>
-<<if App.SecExp.battle.activeUnits() >= 6>>
-	Your military is the size of a small army. Security is easier to maintain with such forces at your disposal.
-	<<set _secGrowth += 0.5>>
-<</if>>
-<<set _count = $SecExp.battles.victories + $SecExp.battles.losses>>
-<<if $SecExp.battles.lastEncounterWeeks < 3 && _count > 0>>
-	The recent attack has a negative effect on the security of the arcology.
-	<<set _secGrowth -= 1>>
-<<elseif $SecExp.battles.lastEncounterWeeks < 5 && _count > 0>>
-	While some time has passed, the last attack still has a negative effect on the security of the arcology.
-	<<set _secGrowth -= 0.5>>
-<<elseif _count > 0>>
-	The arcology has not been attacked in a while, which has a positive effect on security.
-	<<set _secGrowth += 0.5>>
-<</if>>
-
-<<if $SecExp.buildings.transportHub>>
-	<<set _secGrowth -= ($SecExp.buildings.transportHub.airport + $SecExp.buildings.transportHub.surfaceTransport - $SecExp.buildings.transportHub.security * 3) / 2>>
-	The transport hub, for all its usefulness, is a hotspot of malicious
-	<<if $SecExp.buildings.transportHub.airport + $SecExp.buildings.transportHub.surfaceTransport > $SecExp.buildings.transportHub.security * 3>>
-		activity and hub security forces are not sufficient to keep up with all threats.
-	<<else>>
-		activity, but the hub security forces are up to the task.
-	<</if>>
-<</if>>
-
-<<if $SecExp.buildings.propHub && $SecExp.buildings.propHub.upgrades.blackOps > 0>>
-	Your black ops team proves to be a formidable tool against anyone threatening the security of your arcology.
-	<<set _secGrowth += 0.5 * random(1,2)>>
-<</if>>
-
-<<if $garrison.assistantTime > 0>>
-	With the central CPU core of the assistant down, managing security is a much harder task. Inevitably some little but important details will slip past your agents.
-	It will still take <<if $garrison.assistantTime> 1>>$garrison.assistantTime weeks<<else>>a week<</if>> to finish repair works.
-	<<set _secGrowth-->>
-	<<set _crimeGrowth++>>
-	<<set $garrison.assistantTime--, IncreasePCSkills('engineering', 0.1)>>
-<</if>>
-
-<<if $SF.Toggle && $SF.Active >= 1>>
-	<<if $SecExp.edicts.SFSupportLevel >= 3>>
-		The two squads of $SF.Lower assigned to the Security HQ provide an essential help to the security department.
-	<</if>>
-	<<if $SecExp.edicts.SFSupportLevel >= 2>>
-		The training officers of $SF.Lower assigned to the Security HQ improve its effectiveness.
-	<</if>>
-	<<if $SecExp.edicts.SFSupportLevel >= 1>>
-		Providing your Security Department with equipment from $SF.Lower slightly boosts the security of your arcology.
-	<</if>>
-	<<set _secGrowth *= 1+($SecExp.edicts.SFSupportLevel/10)>>
-<</if>>
-
-/* resting point */
-<<set _secRest = App.SecExp.Check.secRestPoint() * (Math.clamp($SecExp.buildings.secHub ? $SecExp.buildings.secHub.menials : 0 ,0 , App.SecExp.Check.reqMenials()) / App.SecExp.Check.reqMenials())>>
-<<if _secRest < 0>> <<set _secRest = 20>> <</if>>
-<<if _secRest < App.SecExp.Check.reqMenials() && $SecExp.buildings.secHub>>
-	The limited staff assigned to the HQ hampered the improvements to security achieved this week.
-<<elseif _secRest < App.SecExp.Check.reqMenials()>>
-	The limited infrastructure available slowly erodes away the security level of the arcology.
-<</if>>
-
-The security level of the arcology is
-<<if $SecExp.core.security > (_secRest + 5)>>
-	over its effective resting point, limiting the achievable growth this week.
-	<<set _secGrowth *= 0.5>>
-<<elseif $SecExp.core.security < (_secRest - 5)>>
-	under its effective resting point, speeding up its growth.
-	<<set _secGrowth *= 1.5>>
-<<elseif $SecExp.core.security == _secRest>>
-	at its effective resting point, this severely limits the influence of external factors on the change achievable this week.
-	<<set _secGrowth *= 0.3>>
-<<else>>
-	near its effective resting point, this severely limits the influence of external factors on the change achievable this week.
-	<<if _secGrowth < 0>> <<set _secGrowth *= 0.3>> <</if>>
-<</if>>
-<<set _restGrowth = (_secRest - $SecExp.core.security) * 0.2>>
-<<set _newSec = Math.trunc($SecExp.core.security + _secGrowth + _restGrowth)>>
-<<if _newSec < $SecExp.core.security>>
-	This week @@.red;security decreased.@@
-<<elseif _newSec == $SecExp.core.security>>
-	This week @@.yellow;security did not change.@@
-<<else>>
-	This week @@.green;security improved.@@
-<</if>>
-<<set $SecExp.core.security = Math.clamp(_newSec, 0, 100)>>
-
-<br><br>
-<strong>Crime</strong>:
-/* crime modifiers */
-Due to the deterioration of the old world countries, organized crime focuses more and more on the prosperous Free Cities, yours included. This has a
-<<if $week < 30>>
-	small
-	<<set _crimeGrowth += 0.5>>
-<<elseif $week < 60>>
-	noticeable
-	<<set _crimeGrowth += 1>>
-<<elseif $week < 90>>
-	moderate
-	<<set _crimeGrowth += 1.5>>
-<<elseif $week < 120>>
-	big
-	<<set _crimeGrowth += 2>>
-<<else>>
-	huge
-	<<set _crimeGrowth += 2.5>>
-<</if>>
- impact on the growth of criminal activities in your arcology.
-
-<<if $arcologies[0].prosperity < 50>>
-	The low prosperity of the arcology facilitates criminal recruitment and organization.
-	<<set _crimeGrowth += 1>>
-<<elseif $arcologies[0].prosperity < 80>>
-	The fairly low prosperity of the arcology facilitates criminal recruitment and organization.
-	<<set _crimeGrowth += 0.5>>
-<<elseif $arcologies[0].prosperity < 120>>
-	The prosperity of the arcology is not high or low enough to have significant effects on criminal recruitment and organization.
-<<elseif $arcologies[0].prosperity < 160>>
-	The prosperity of the arcology is high enough to provide its citizens a decent life, hampering criminal recruitment and organization.
-	<<set _crimeGrowth -= 0.5>>
-<<elseif $arcologies[0].prosperity < 180>>
-	The prosperity of the arcology is high enough to provide its citizens a decent life, significantly hampering criminal recruitment and organization.
-	<<set _crimeGrowth -= 1>>
-<<else>>
-	The prosperity of the arcology is high enough to provide its citizens a very good life, significantly hampering criminal recruitment and organization.
-	<<set _crimeGrowth -= 2>>
-<</if>>
-<<if $ASlaves < 1000>>
-	The low number of slaves in the arcology does not hinder the activity of law enforcement, limiting crime growth.
-	<<set _crimeGrowth -= 1>>
-<<elseif $ASlaves < 2000>>
-	The fairly low number of slaves in the arcology does not hinder significantly the activity of law enforcement, limiting crime growth.
-	<<set _crimeGrowth -= 0.5>>
-<<elseif $ASlaves < 3000>>
-	The number of slaves in the arcology is becoming an impediment for law enforcement, facilitating crime growth.
-	<<set _crimeGrowth += 1>>
-<<else>>
-	The number of slaves in the arcology is becoming a big issue for law enforcement, facilitating crime growth.
-	<<set _crimeGrowth += 1.5>>
-<</if>>
-<<if $SecExp.core.security <= 20>>
-	The security measures in place are severely limited, allowing crime to grow uncontested.
-<<elseif $SecExp.core.security <= 50>>
-	The security measures in place are of limited effect and use, giving only mixed results in their fight against crime.
-	<<set _crimeGrowth -= 1.5>>
-<<elseif $SecExp.core.security <= 75>>
-	The security measures in place are well developed and effective, making a serious dent in the profitability of criminal activity in your arcology.
-	<<set _crimeGrowth -= 3>>
-<<else>>
-	The security measures in place are extremely well developed and very effective, posing a serious threat even to the most powerful criminal organizations in existence.
-	<<set _crimeGrowth -= 5.5>>
-<</if>>
-<<if $SecExp.core.authority < 5000>>
-	Your low authority allows crime to grow undisturbed.
-	<<set _crimeGrowth += 1>>
-<<elseif $SecExp.core.authority < 7500>>
-	Your relatively low authority facilitates criminal activities.
-	<<set _crimeGrowth += 0.5>>
-<<elseif $SecExp.core.authority < 10000>>
-	Your authority is not high enough to discourage criminal activity.
-<<elseif $SecExp.core.authority < 15000>>
-	Your high authority is an effective tool against crime.
-	<<set _crimeGrowth -= 1>>
-<<else>>
-	Your absolute authority is an extremely effective tool against crime.
-	<<set _crimeGrowth -= 2>>
-<</if>>
-<<if $cash >= 100000>>
-	Your great wealth acts as a beacon for the greediest criminals, calling them to your arcology as moths to a flame.
-	<<set _crimeGrowth += 0.5>>
-<</if>>
-<<if $SecExp.buildings.propHub && $SecExp.buildings.propHub.upgrades.marketInfiltration > 0>>
-	<<set _crimeGrowth += 0.5 * random(1,2)>>
-<</if>>
-
-/* crime cap */
-<<set _crimeCap = Math.trunc(Math.clamp(App.SecExp.Check.crimeCap() + (App.SecExp.Check.crimeCap() - App.SecExp.Check.crimeCap() * (($SecExp.buildings.secHub ? $SecExp.buildings.secHub.menials : 0) / App.SecExp.Check.reqMenials())), 0, 100))>>
-<<if _crimeCap > App.SecExp.Check.crimeCap() && $SecExp.buildings.secHub>>
-	The limited staff assigned to the HQ allows more space for criminals to act.
-<</if>>
-<<if $SecExp.core.authority > 12000>>
-	<<if $SecExp.buildings.secHub.coldstorage < 6>>
-		<<if $SecExp.buildings.secHub.coldstorage === 0>>Adding a facility<<else>>Improving the cold storage facility attached<</if>> to the SecurityHQ should allow the staff to be more efficient in dealing with crime.
-	<<else>>
-		The cold storage facility attached to SecurityHQ allows the staff to be more efficient in dealing with crime.
-	<</if>>
-<</if>>
-<<set _newCrime = Math.trunc(Math.clamp($SecExp.core.crimeLow + _crimeGrowth,0,_crimeCap))>>
-<<if _newCrime > $SecExp.core.crimeLow>>
-	This week @@.red;crime increased.@@
-<<elseif _newCrime == $SecExp.core.crimeLow>>
-	This week @@.yellow;crime did not change.@@
-<<else>>
-	This week @@.green;crime decreased.@@
-<</if>>
-<<set $SecExp.core.crimeLow = Math.clamp(_newCrime,0,100)>>
-
-<<if $SecExp.edicts.defense.militia >= 1 || App.SecExp.battle.activeUnits() >= 1>>
-	<<set _size = App.SF.upgrades.total()>>
-	<br><br> <strong>Military</strong>:
-	<<if $SecExp.edicts.defense.militia >= 1>>
-		<<if $SF.Toggle && $SF.Active >= 1 && _size > 10>>
-			Having a powerful special force attracts a lot of citizens, hopeful that they may be able to fight along side it.
-			<<set _recruitsMultiplier *= 1 + (random(1, (Math.round(_size / 10))) / 20)>> /* not sure how high _size goes, so I hope this makes sense */
-		<</if>>
-		<<set _propagandaEffects = App.SecExp.propagandaEffects("recruitment")>>
-		_propagandaEffects.text
-		<<set _recruitsMultiplier *= (1 + _propagandaEffects.effect)>>
-		<<if $SecExp.edicts.defense.militia === 2>>
-			Your militia accepts only volunteering citizens, ready to defend their arcology.
-			<<set _recruitLimit = App.SecExp.militiaCap(), _adjst = 0.0025>>
-			<<if $rep >= 10000>>
-				Many citizens volunteer just to fight for someone of your renown.
-				<<set _recruitLimit += _adjst>>
-			<</if>>
-			<<if $SecExp.core.authority >= 10000>>
-				Many citizens feel it is their duty to fight for you, boosting volunteer enrollment.
-				<<set _recruitLimit += _adjst>>
-			<</if>>
-		<<elseif $SecExp.edicts.defense.militia === 3>>
-			Adult citizens are required to join the militia for a period of time.
-			<<set _recruitLimit = App.SecExp.militiaCap(), _adjst = 0.005>>
-		<<elseif $SecExp.edicts.defense.militia === 4>>
-			Adult citizens are required to register and serve in the militia whenever necessary.
-			<<set _recruitLimit = App.SecExp.militiaCap(), _adjst = 0.01>>
-		<<elseif $SecExp.edicts.defense.militia === 5>>
-			Every citizen is required to train and participate in the military activities of the arcology.
-			<<set _recruitLimit = App.SecExp.militiaCap(), _adjst = 0.02>>
-		<</if>>
-		<<if $SecExp.edicts.defense.lowerRequirements == 1>>
-			Your lax physical requirements to enter the militia allows for a greater number of citizens to join.
-			<<set _recruitLimit += _adjst>>
-		<</if>>
-		<<if $SecExp.edicts.defense.militia >= 3>>
-			<<if $SecExp.edicts.defense.militaryExemption === 1>>
-				Some citizens prefer to contribute to the arcology's defense through financial support rather than military service, making you @@.yellowgreen;a small sum.@@
-				<<set _recruitLimit -= _adjst>>
-				<<run cashX(250, "securityExpansion")>>
-			<</if>>
-			<<if $SecExp.edicts.defense.noSubhumansInArmy === 1>>
-				Guaranteeing the purity of your armed forces comes with a small loss of potential recruits.
-				<<set _recruitLimit -= _adjst>>
-			<</if>>
-			<<if $SecExp.edicts.defense.pregExemption === 1>>
-				Many pregnant citizens prefer to avoid military service not to endanger themselves and their children.
-				<<set _recruitLimit -= _adjst>>
-			<</if>>
-		<</if>>
-
-		<<set _recruits = Math.trunc((_recruitLimit * $ACitizens - App.SecExp.Manpower.totalMilitia) / 20 * _recruitsMultiplier)>>
-		<<if _recruits > 0>>
-			<<set $militiaFreeManpower += _recruits>>
-			This week <<print _recruits>> citizens joined the militia.
-		<<elseif $SecExp.edicts.defense.militia === 5>>
-			No citizens joined your militia this week because your society is as militarized as it can get.
-		<<elseif $SecExp.edicts.defense.militia === 2>>
-			There are no more citizens willing to join the arcology armed forces. You'll need to enact higher recruitment edicts if you need more manpower.
-		<<else>>
-			No more citizens could be drafted into your militia. You'll need to enact higher recruitment edicts if you need more manpower.
-		<</if>>
-		<br>
-	<</if>>
-
-	/* mercs */
-	<<if $mercenaries >= 1>>
-		<<set _newMercs = random(0,3)>>
-		<<if $rep < 6000>>
-			Your low reputation turns some mercenaries away, hoping to find contracts that would bring them more renown.
-			<<set _newMercs -= 1>>
-		<<elseif $rep < 12000>>
-			Your reputation is not high enough to attract many mercenaries to your free city.
-		<<else>>
-			Your reputation attracts many guns for hire who would be proud to have such distinct character on their resume.
-			<<set _newMercs += 1>>
-		<</if>>
-		<<if $arcologies[0].prosperity < 50>>
-			The low prosperity of the arcology discourages new guns for hire from coming to your arcology.
-			<<set _newMercs -= 1>>
-		<<elseif $arcologies[0].prosperity < 80>>
-			The fairly low prosperity of the arcology discourages new guns for hire from coming to your arcology.
-			<<set _newMercs += 1>>
-		<<elseif $arcologies[0].prosperity < 120>>
-			The prosperity of the arcology attracts a few mercenaries, hopeful to find lucrative contracts within its walls.
-			<<set _newMercs += random(1,2)>>
-		<<elseif $arcologies[0].prosperity < 160>>
-			The fairly high prosperity of the arcology attracts some mercenaries, hopeful to find lucrative contracts within its walls.
-			<<set _newMercs += random(2,3)>>
-		<<elseif $arcologies[0].prosperity < 180>>
-			The high prosperity of the arcology is attracts some mercenaries, hopeful to find lucrative contracts within its walls.
-			<<set _newMercs += random(2,4)>>
-		<<else>>
-			The very high prosperity of the arcology attracts a lot of mercenaries, hopeful to find lucrative contracts within its walls.
-			<<set _newMercs += random(3,5)>>
-		<</if>>
-		<<if $SecExp.core.crimeLow > 60>>
-			The powerful crime organizations that nested themselves in the arcology have an unending need for cheap guns for hire, many mercenaries flock to your free city in search of employment.
-			<<set _newMercs += random(1,2)>>
-		<</if>>
-		<<if $SF.Toggle && $SF.Active >= 1 && _size > 10>>
-			Having a powerful special force attracts a lot of mercenaries, hopeful that they may be able to fight along side it.
-			<<set _newMercs += random(1,Math.round(_size/10))>>
-		<</if>>
-		<<if $SecExp.edicts.defense.discountMercenaries > 0>>
-			More mercenaries are attracted to your arcology as a result of the reduced rent.
-			<<set _newMercs += random(2,4)>>
-		<</if>>
-		<<set _newMercs = Math.trunc(_newMercs / 2)>>
-		<<if _newMercs > 0>>
-			<<set $mercFreeManpower += _newMercs>>
-			This week <<print _newMercs>> mercenaries reached the arcology.
-		<<else>>
-			This week no new mercenaries reached the arcology.
-		<</if>>
-		<<set $mercFreeManpower = Math.clamp($mercFreeManpower, 0, 2000)>>
-		<br>
-	<</if>>
-
-	<<if App.SecExp.battle.activeUnits() > 0>>
-		/* loyalty and training */
-		<<set _sL = $slaveUnits.length, _mL = $militiaUnits.length, _meL = $mercUnits.length>>
-		<<for _i = 0; _i < _sL; _i++>>
-			<<includeDOM App.SecExp.humanUnitLoyaltyChanges($slaveUnits[_i], 'slave')>>
-		<</for>>
-		<<for _i = 0; _i < _mL; _i++>>
-			<<includeDOM App.SecExp.humanUnitLoyaltyChanges($militiaUnits[_i], 'citizens')>>
-		<</for>>
-		<<for _i = 0; _i < _meL; _i++>>
-			<<includeDOM App.SecExp.humanUnitLoyaltyChanges($mercUnits[_i], 'mercenary')>>
-		<</for>>
-	<</if>>
-<</if>>
-
-
-<<if $SecExp.buildings.riotCenter && $SecExp.buildings.riotCenter.brainImplantProject > 0 && $SecExp.buildings.riotCenter.brainImplant < 106>>
-	<br><br>
-	<<set $SecExp.buildings.riotCenter.brainImplant += $SecExp.buildings.riotCenter.brainImplantProject>>
-	<<if 100 - $SecExp.buildings.riotCenter.brainImplant <= 0>>
-		The project has been completed!
-		<<set $SecExp.buildings.riotCenter.brainImplant = 106>>
-	<<else>>
-		The great brain implant project is proceeding steadily. This week we made
-		<<if $SecExp.buildings.riotCenter.brainImplantProject <= 2>>
-			some small
-		<<elseif $SecExp.buildings.riotCenter.brainImplantProject <= 4>>
-			some
-		<<else>>
-			good
-		<</if>>
-		progress.
-	<</if>>
-<</if>>
-
-<<if $SecExp.buildings.weapManu>>
-	<<if App.SecExp.weapManuUpgrade.fully().bots && App.SecExp.weapManuUpgrade.fully().human>> <<run delete $SecExp.buildings.weapManu.upgrades.queue>> <</if>>
-	<<if jsDef($SecExp.buildings.weapManu.upgrades.queue) && $SecExp.buildings.weapManu.upgrades.queue.length > 0 && $SecExp.buildings.weapManu.upgrades.queue[0].time > 0>>
-		<<set _current = App.SecExp.weapManuUpgrade.current(), $SecExp.buildings.weapManu.upgrades.queue[0].time-->>
-		<br>In the research lab, _current.dec
-		<<switch _current.dec>>
-		<<case "adaptive armored frames" "advanced synthetic alloys" "ceramo-metallic alloys" "rapid action stimulants" "universal cyber enhancements" "remote neural links" "combined training regimens with the special force">>
-			are
-		<<default>>
-			is
-		<</switch>>
-		being developed with the aim of enhancing _current.unit' _current.purpose.
-		<<if $SecExp.buildings.weapManu.upgrades.queue[0].time <= 0>>
-			Reports indicate it is ready for deployment and will be issued in the following days.
-			<<set $SecExp.buildings.weapManu.upgrades.completed.push(_current.ID)>>
-			<<run $SecExp.buildings.weapManu.upgrades.queue.splice(0, 1)>>
-		<<else>>
-			It will be finished in <<= numberWithPluralOne($SecExp.buildings.weapManu.upgrades.queue[0].time, "week")>>.
-		<</if>>
-		<<for _i = 1; _i < $SecExp.buildings.weapManu.upgrades.queue.length; _i++>>
-			<<set _current = App.SecExp.weapManuUpgrade.current($SecExp.buildings.weapManu.upgrades.queue[_i].ID)>>
-			<br><<= ordinalSuffix(_i + 1)>> in queue:
-			_current.dec for _current.unit. It will enhance their _current.purpose.
-		<</for>>
-	<</if>>
-<</if>>
diff --git a/src/Mods/SecExp/unitsBattleReport.tw b/src/Mods/SecExp/unitsBattleReport.tw
index 7d92a211e5cc14e990daa1cda3e75e2c87e8e989..871323bcdc45436e281aeeda423776f3b3fc4003 100644
--- a/src/Mods/SecExp/unitsBattleReport.tw
+++ b/src/Mods/SecExp/unitsBattleReport.tw
@@ -1,10 +1,10 @@
 :: unitsBattleReport [nobr]
 
-<<if $losses == 0>>
+<<if $SecExp.war.losses == 0>>
 	<<if App.SecExp.battle.deployedUnits('bots')>>
 		<br>Security Drones: no casualties.
 	<</if>>
-	<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+	<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 		<br><<print num($SF.ArmySize)>> soldiers from $SF.Lower joined the battle: no casualties suffered.
 	<</if>>
 	<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
@@ -49,10 +49,10 @@
 			<</if>>
 		<</for>>
 	<</if>>
-<<elseif $losses > 0>>
+<<elseif $SecExp.war.losses > 0>>
 	/* if the losses are more than zero */
 	/* generates a list of randomized losses, from which each unit picks one at random */
-	<<set _losses = $losses>>
+	<<set _losses = $SecExp.war.losses>>
 	<<set _averageLosses = Math.trunc(_losses / App.SecExp.battle.deployedUnits())>>
 	<<set _lossesList = []>>
 	<<set _validityCount = 0>>
@@ -80,11 +80,11 @@
 		<</if>>
 		<<set _count += _lossesList[_i]>>
 	<</for>>
-	<<if _count < $losses>>
+	<<if _count < $SecExp.war.losses>>
 		<<set _rand = random(_lossesList.length - 1)>>
-		<<set _lossesList[_rand] += $losses - _count>>
-	<<elseif _count > $losses>>
-		<<set _diff = _count - $losses>>
+		<<set _lossesList[_rand] += $SecExp.war.losses - _count>>
+	<<elseif _count > $SecExp.war.losses>>
+		<<set _diff = _count - $SecExp.war.losses>>
 		<<set _rand = random(_lossesList.length - 1)>>
 		<<set _lossesList[_rand] = Math.trunc(_lossesList[_rand]-_diff,0,100)>>
 	<</if>>
@@ -115,7 +115,7 @@
 		<</if>>
 		<br>
 	<</if>>
-	<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
+	<<if $SF.Toggle && $SF.Active >= 1 && $SecExp.war.deploySF>>
 		<<set _loss = _lossesList.pluck()>>
 		<<set _loss = Math.clamp(_loss,0,$SF.ArmySize)>>
 		<br> <<print num($SF.ArmySize)>> soldiers from $SF.Lower joined the battle:
diff --git a/src/Mods/SecExp/unitsRebellionReport.tw b/src/Mods/SecExp/unitsRebellionReport.tw
index b2549223e94d38e93822f287a34735e9a398a3c8..4d9efb039a6ad2a9909c327f022b72dbd18961fd 100644
--- a/src/Mods/SecExp/unitsRebellionReport.tw
+++ b/src/Mods/SecExp/unitsRebellionReport.tw
@@ -1,7 +1,7 @@
 :: unitsRebellionReport [nobr]
 
-<<if $losses == 0>>
-	<<if $irregulars > 0>>
+<<if $SecExp.war.losses == 0>>
+	<<if $SecExp.war.irregulars > 0>>
 		<p>The volunteering citizens were quickly organized into an irregular militia unit and deployed in the arcology. No casualties suffered.</p>
 	<</if>>
 	<<if App.SecExp.battle.deployedUnits('bots') >= 1>>
@@ -11,11 +11,11 @@
 		<br>$SF.Lower, <<print num($SF.ArmySize)>> strong, was called to join the battle: no casualties suffered.
 	<</if>>
 	<<set _count = 0>>
-	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $rebellingID.length>>
+	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $SecExp.war.rebellingID.length>>
 	<<if _loyalUnits > 0>>
 		<p>
 		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
+			<<if $militiaUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($militiaUnits[_i].ID))>>
 				<<set $militiaUnits[_i].battlesFought++>>
 				<<set _count++>>
 				<<if _count < _loyalUnits>>
@@ -26,7 +26,7 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
+			<<if $slaveUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($slaveUnits[_i].ID))>>
 				<<set $slaveUnits[_i].battlesFought++>>
 				<<set _count++>>
 				<<if _count < _loyalUnits>>
@@ -37,7 +37,7 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
+			<<if $mercUnits[_i].active == 1 && (!$SecExp.war.rebellingID.includes($mercUnits[_i].ID))>>
 				<<set $mercUnits[_i].battlesFought++>>
 				<<set _count++>>
 				<<if _count < _loyalUnits>>
@@ -50,23 +50,23 @@
 		participated in the battle without taking any casualties. They remained loyal until the end.
 		</p>
 	<</if>>
-<<elseif $losses > 0>>
+<<elseif $SecExp.war.losses > 0>>
 	/* if the losses are more than zero */
 	/* generates a list of randomized losses, from which each unit picks one at random */
-	<<set _averageLosses = Math.trunc($losses / App.SecExp.battle.deployedUnits())>>
+	<<set _averageLosses = Math.trunc($SecExp.war.losses / App.SecExp.battle.deployedUnits())>>
 	<<set _lossesList = []>>
 	<<for _i = 0; _i < App.SecExp.battle.deployedUnits(); _i++>>
 		<<set _assignedLosses = Math.trunc(Math.clamp(_averageLosses + random(-5,5), 0, 100))>>
-		<<if _assignedLosses > $losses>>
-			<<set _assignedLosses = $losses>>
-			<<set $losses = 0>>
+		<<if _assignedLosses > $SecExp.war.losses>>
+			<<set _assignedLosses = $SecExp.war.losses>>
+			<<set $SecExp.war.losses = 0>>
 		<<else>>
-			<<set $losses -= _assignedLosses>>
+			<<set $SecExp.war.losses -= _assignedLosses>>
 		<</if>>
 		<<set _lossesList.push(_assignedLosses)>>
 	<</for>>
-	<<if $losses > 0>>
-		<<set _lossesList[random(_lossesList.length - 1)] += $losses>>
+	<<if $SecExp.war.losses > 0>>
+		<<set _lossesList[random(_lossesList.length - 1)] += $SecExp.war.losses>>
 	<</if>>
 	<<set _lossesList.shuffle()>>
 
@@ -78,17 +78,17 @@
 		<</if>>
 		<<set _count += _lossesList[_i]>>
 	<</for>>
-	<<if _count < $losses>>
+	<<if _count < $SecExp.war.losses>>
 		<<set _rand = random(_lossesList.length - 1)>>
-		<<set _lossesList[_rand] += $losses - _count>>
-	<<elseif _count > $losses>>
-		<<set _diff = _count - $losses>>
+		<<set _lossesList[_rand] += $SecExp.war.losses - _count>>
+	<<elseif _count > $SecExp.war.losses>>
+		<<set _diff = _count - $SecExp.war.losses>>
 		<<set _rand = random(_lossesList.length - 1)>>
 		<<set _lossesList[_rand] = Math.trunc(_lossesList[_rand]-_diff,0,100)>>
 	<</if>>
 
 	/* assigns the losses and notify the player */
-	<<if $irregulars > 0>>
+	<<if $SecExp.war.irregulars > 0>>
 		<<set _loss = _lossesList.pluck()>>
 		<<if _loss > $ACitizens * 0.95>>
 			<<set _loss = Math.trunc($ACitizens * 0.95)>> /* this is unlikely to happen, but might as well be safe*/
@@ -178,7 +178,7 @@
 	<<if App.SecExp.battle.deployedUnits('militia') >= 1>>
 		<p> <<set _med = 0>>
 		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && !$rebellingID.includes($militiaUnits[_j].ID)>>
+			<<if $militiaUnits[_j].active == 1 && !$SecExp.war.rebellingID.includes($militiaUnits[_j].ID)>>
 				<<set $militiaUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
 				<<set _loss = Math.clamp(_loss,0,$militiaUnits[_j].troops)>>
@@ -220,7 +220,7 @@
 	<<if App.SecExp.battle.deployedUnits('slaves') >= 1>>
 		<p> <<set _med = 0>>
 		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && !$rebellingID.includes($slaveUnits[_j].ID)>>
+			<<if $slaveUnits[_j].active == 1 && !$SecExp.war.rebellingID.includes($slaveUnits[_j].ID)>>
 				<<set $slaveUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
 				<<if !(Number.isInteger(_loss))>>
@@ -266,7 +266,7 @@
 	<<if App.SecExp.battle.deployedUnits('mercs') >= 1>>
 		<p> <<set _med = 0>>
 		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && !$rebellingID.includes($mercUnits[_j].ID)>>
+			<<if $mercUnits[_j].active == 1 && !$SecExp.war.rebellingID.includes($mercUnits[_j].ID)>>
 				<<set $mercUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
 				<<set _loss = Math.clamp(_loss,0,$mercUnits[_j].troops)>>
@@ -309,10 +309,10 @@
 	<br>@@.red;Error: losses are a negative number or NaN@@
 <</if>>
 
-<<if $rebellingID.length > 0 && $battleResult >= 2>> /* rebellion win */
+<<if $SecExp.war.rebellingID.length > 0 && $SecExp.war.result >= 2>> /* rebellion win */
 	<br> <<set _militiaRebelledID = [], _militiaManpower = 0>>
 	<<for _j = 0; _j < $militiaUnits.length; _j++>>
-		<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
+		<<if $militiaUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($militiaUnits[_j].ID)>>
 			<br>$militiaUnits[_j].platoonName,
 			<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
 			<<set _militiaManpower += Math.clamp($militiaUnits[_j].troops - random(_averageLosses),0,$militiaUnits[_j].troops)>>
@@ -359,7 +359,7 @@
 
 	<br> <<set _slaveRebelledID = [], _slaveManpower = 0>>
 	<<for _j = 0; _j < $slaveUnits.length; _j++>>
-		<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
+		<<if $slaveUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($slaveUnits[_j].ID)>>
 			<br>$slaveUnits[_j].platoonName,
 			<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
 			<<set _slaveManpower += Math.clamp($slaveUnits[_j].troops - random(_averageLosses),0,$slaveUnits[_j].troops)>>
@@ -406,7 +406,7 @@
 
 	<br> <<set _mercRebelledID = [], _mercManpower = 0>>
 	<<for _j = 0; _j < $mercUnits.length; _j++>>
-		<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
+		<<if $mercUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($mercUnits[_j].ID)>>
 			<br>$mercUnits[_j].platoonName,
 			<<set _mercRebelledID.push($mercUnits[_j].ID)>>
 			<<set _mercManpower += Math.clamp($mercUnits[_j].troops - random(_averageLosses),0,$mercUnits[_j].troops)>>
@@ -450,12 +450,12 @@
 			<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
 		</span>
 	<</if>>
-<<elseif $rebellingID.length > 0>>
+<<elseif $SecExp.war.rebellingID.length > 0>>
 	/* rebellion loss */
 	<br><br>
 	<<set _militiaRebelledID = []>>
 	<<for _j = 0; _j < $militiaUnits.length; _j++>>
-		<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
+		<<if $militiaUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($militiaUnits[_j].ID)>>
 			<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
 			$militiaUnits[_j].platoonName,
 		<</if>>
@@ -468,7 +468,7 @@
 	<br>
 	<<set _slaveRebelledID = []>>
 	<<for _j = 0; _j < $slaveUnits.length; _j++>>
-		<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
+		<<if $slaveUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($slaveUnits[_j].ID)>>
 			<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
 			$slaveUnits[_j].platoonName,
 		<</if>>
@@ -482,7 +482,7 @@
 	<<set _mercRebelledID = []>>
 	<<set _count = 0>>
 	<<for _j = 0; _j < $mercUnits.length; _j++>>
-		<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
+		<<if $mercUnits[_j].active == 1 && $SecExp.war.rebellingID.includes($mercUnits[_j].ID)>>
 			<<set _mercRebelledID.push($mercUnits[_j].ID)>>
 			<<set _count++>>
 			$mercUnits[_j].platoonName,
diff --git a/src/Mods/SpecialForce/Firebase.tw b/src/Mods/SpecialForce/Firebase.tw
index 5bdca67f793255bf793cf58f13ed21cfd2ac6c5a..1864ca058c5058834ae2a7892161c8b3eeca2ce9 100644
--- a/src/Mods/SpecialForce/Firebase.tw
+++ b/src/Mods/SpecialForce/Firebase.tw
@@ -43,7 +43,7 @@
 
 			<<if $SF.Upgrade > 0>>
 				<<set _cost = Math.ceil(10000 + ((0.03 - (_size/_max)/100)  * $SF.CreditsInvested))>>
-				<br>[[Re-unlock upgrading.|Firebase][$SF.Upgrade = 0,cashX(-(_cost), "specialForcesCap")]] @@.red;<<print cashFormat(_cost)>>@@
+				<br>[[Re-unlock upgrading.|Firebase][$SF.Upgrade = 0,cashX(-(_cost), "specialForcesCap")]] @@.cash.dec;<<print cashFormat(_cost)>>@@
 			<<elseif _size < 30 || _size !== _max>>
 				<<include "Upgrades">>
 			<</if>>
diff --git a/src/Mods/SpecialForce/SpecialForce.js b/src/Mods/SpecialForce/SpecialForce.js
index de04d0ebb50787169d9e0d3b0eecbea9d80f52a7..3de44d1b05d5f6cfbf9d34cdb2f24b2ba58c1a75 100644
--- a/src/Mods/SpecialForce/SpecialForce.js
+++ b/src/Mods/SpecialForce/SpecialForce.js
@@ -7,24 +7,19 @@ App.SF.weeklyGift = function(input) {
 		size = App.SF.upgrades.total();
 
 	switch(input) {
-		case 1:
-			value = Math.ceil(25000 * (size/10) * env);
-			value = (value > 5000 ? value : 5000);
-			break;
-		case 2:
+		case 1: // Request cash
+			return Math.max(Math.ceil(25000 * (size/10) * env), 5000);
+		case 2: // Request military parade
 			value = 50 * (Math.ceil(size * 0.03 * env));
-			value = (Number(value) ? value : 500);
-			break;
-		case 3:
+			return (Number(value) ? value : 500);
+		case 3: // Request sabotage
 			switch(env) {
 				case 4: EnvProsp = 3; break;
 				case 3: EnvProsp = 5; break;
 				case 2: EnvProsp = 7; break;
 			}
-			value = EnvProsp + (Math.ceil(size/100 * env));
-			break;
+			return EnvProsp + (Math.ceil(size/100 * env));
 	}
-	return value;
 };
 
 App.SF.unlocked = (function() {
@@ -42,19 +37,19 @@ App.SF.unlocked = (function() {
 	}
 
 	function garage() {
-		return (V.SF.Squad.Firebase >= 1 && V.terrain !== "oceanic") ? true : false;
+		return V.SF.Squad.Firebase >= 1 && V.terrain !== "oceanic";
 	}
 
 	function hangar() {
-		return V.SF.Squad.Firebase >= 4 ? true : false;
+		return V.SF.Squad.Firebase >= 4;
 	}
 
 	function launchBay() {
-		return secondTier() ? true : false;
+		return secondTier();
 	}
 
 	function navalYard() {
-		return (secondTier() && (V.terrain === "oceanic" || V.terrain === "marine")) ? true : false;
+		return secondTier() && (V.terrain === "oceanic" || V.terrain === "marine");
 	}
 })();
 
@@ -2478,7 +2473,6 @@ App.SF.UnitText = function(input) {
 					armor10 = `The armor protecting its cargo has been increased.`;
 				}
 				if (S.HAT >= 6) {
-					tons = `300`;
 					fans = `The turbines powering the rear fans and impeller`;
 					speed = `acceleration, speed, and carrying capacity.`;
 				}
diff --git a/src/Mods/SpecialForce/Upgrades.tw b/src/Mods/SpecialForce/Upgrades.tw
index 9e49660808b28e0f93c772ff0b9656417bc041e1..56892c68fbaa02521f3fbb9c9634651dff65cc58 100644
--- a/src/Mods/SpecialForce/Upgrades.tw
+++ b/src/Mods/SpecialForce/Upgrades.tw
@@ -10,7 +10,7 @@
 	<<else>>
 		//Cannot afford to upgrade the Firebase.//
 	<</if>>
-	//Costs @@.red;<<print cashFormat(_cF)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Firebase)>> </span> <br>
+	//Costs @@.cash.dec;<<print cashFormat(_cF)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Firebase)>> </span> <br>
 <<elseif $SF.Squad.Firebase === 10>>
 	<<set _fullyUpgraded.push('Firebase')>>
 <</if>>
@@ -22,7 +22,7 @@
 	<<else>>
 		//Cannot afford to upgrade the Armory.//
 	<</if>>
-	//Costs @@.red;<<print cashFormat(_cA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Armoury)>> </span> <br>
+	//Costs @@.cash.dec;<<print cashFormat(_cA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Armoury)>> </span> <br>
 <<elseif $SF.Squad.Armoury === 10>>
 	<<set _fullyUpgraded.push('Armory')>>
 <</if>>
@@ -34,7 +34,7 @@
 	<<else>>
 			//Cannot afford to upgrade the Drug Lab.//
 	<</if>>
-	//Costs @@.red;<<print cashFormat(_cDrugs)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Drugs)>> </span> <br>
+	//Costs @@.cash.dec;<<print cashFormat(_cDrugs)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Drugs)>> </span> <br>
 <<elseif $SF.Squad.Drugs === 10>>
 	<<set _fullyUpgraded.push('Drug Lab')>>
 <</if>>
@@ -46,7 +46,7 @@
 	<<else>>
 		//Cannot afford to upgrade the Drone Bay.//
 	<</if>>
-	//Costs @@.red;<<print cashFormat(_cDrones)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Drones)>> </span> <br>
+	//Costs @@.cash.dec;<<print cashFormat(_cDrones)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.Drones)>> </span> <br>
 <<elseif $SF.Squad.Drones === 10>>
 	<<set _fullyUpgraded.push('Drone Bay')>>
 <</if>>
@@ -60,7 +60,7 @@
 		<<else>>
 			//Cannot afford to upgrade the Attack Vehicle Fleet.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cAV)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.AV)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cAV)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.AV)>> </span> <br>
 	<<elseif $SF.Squad.AV === 10>>
 		<<set _fullyUpgraded.push('Attack Vehicle Fleet')>>
 	<</if>>
@@ -74,7 +74,7 @@
 		<<else>>
 			//Cannot afford to upgrade Transport Vehicle Fleet.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cTV)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.TV)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cTV)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.TV)>> </span> <br>
 	<<elseif $SF.Squad.TV === 10>>
 		<<set _fullyUpgraded.push('Transport Vehicle Fleet')>>
 	<</if>>
@@ -89,7 +89,7 @@
 		<<else>>
 			//Cannot afford to upgrade Prototype Goliath Tank.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cPGT)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.PGT)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cPGT)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.PGT)>> </span> <br>
 	<<elseif $SF.Squad.PGT === _PGTU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.PGT)>> </span> <br>
 	<<elseif $SF.Squad.PGT === _PGTU>>
@@ -107,7 +107,7 @@
 		<<else>>
 			//Cannot afford to upgrade Attack Aircraft Fleet.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cAA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.AA)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cAA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.AA)>> </span> <br>
 	<<elseif $SF.Squad.AA === 10>>
 		<<set _fullyUpgraded.push('Attack Aircraft Fleet')>>
 	<</if>>
@@ -121,7 +121,7 @@
 		<<else>>
 			//Cannot afford to upgrade the Transport Aircraft Fleet.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cTA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.TA)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cTA)>>@@// <span style="float:right;"> <<print App.SF.progress($SF.Squad.TA)>> </span> <br>
 	<<elseif $SF.Squad.TA === 10>>
 		<<set _fullyUpgraded.push('Transport Aircraft Fleet')>>
 	<</if>>
@@ -136,7 +136,7 @@
 		<<else>>
 			//Cannot afford to upgrade the Spaceplane.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cSP)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.SpacePlane)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cSP)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.SpacePlane)>> </span> <br>
 	<<elseif $SF.Squad.SpacePlane === _SPU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.SpacePlane)>> </span> <br>
 	<<elseif $SF.Squad.SpacePlane === _SPU>>
@@ -153,7 +153,7 @@
 		<<else>>
 			//Cannot afford to upgrade Gunship.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cGunS)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GunS)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cGunS)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GunS)>> </span> <br>
 	<<elseif $SF.Squad.GunS === _GunSU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GunS)>> </span> <br>
 	<<elseif $SF.Squad.GunS === _GunSU>>
@@ -172,7 +172,7 @@
 		<<else>>
 			//Cannot afford to upgrade Satellite.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cSat)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Satellite)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cSat)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Satellite)>> </span> <br>
 	<<elseif $SF.Squad.Satellite === _SatU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Satellite)>> </span> <br>
 	<<else>>
@@ -190,7 +190,7 @@
 			<<else>>
 				//Cannot afford to upgrade the Giant Robot.//
 			<</if>>
-			//Costs @@.red;<<print cashFormat(_cGR)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GiantRobot)>> </span> <br>
+			//Costs @@.cash.dec;<<print cashFormat(_cGR)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GiantRobot)>> </span> <br>
 		<<elseif $SF.Squad.GiantRobot === _GRU && $PC.skill.warfare < 75>>
 			//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.GiantRobot)>> </span> <br>
 		<<else>>
@@ -207,7 +207,7 @@
 			<<else>>
 				//Cannot afford to upgrade Cruise Missile.//
 			<</if>>
-			//Costs @@.red;<<print cashFormat(_cMS)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.MissileSilo)>> </span> <br>
+			//Costs @@.cash.dec;<<print cashFormat(_cMS)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.MissileSilo)>> </span> <br>
 		<<elseif $SF.Squad.MissileSilo === _MSU && $PC.skill.warfare < 75>>
 			//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.MissileSilo)>> </span> <br>
 		<<else>>
@@ -227,7 +227,7 @@
 		<<else>>
 			//Cannot afford to upgrade Aircraft Carrier.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cAC)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.AircraftCarrier)>> </span> <br>
+		//Costs @@.cash.dec;<<print cashFormat(_cAC)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.AircraftCarrier)>> </span> <br>
 	<<elseif $SF.Squad.AircraftCarrier === _ACU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.AircraftCarrier)>> </span> <br>
 	<<else>>
@@ -244,7 +244,7 @@
 		<<else>>
 			//Cannot afford to upgrade Submarine//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cSub)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Sub)>> </span>
+		//Costs @@.cash.dec;<<print cashFormat(_cSub)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Sub)>> </span>
 	<<elseif $SF.Squad.Sub === _SubU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.Sub)>> </span>
 	<<else>>
@@ -261,7 +261,7 @@
 		<<else>>
 			//Cannot afford to upgrade Amphibious Transport.//
 		<</if>>
-		//Costs @@.red;<<print cashFormat(_cHAT)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.HAT)>> </span>
+		//Costs @@.cash.dec;<<print cashFormat(_cHAT)>>@@//<span style="float:right;"> <<print App.SF.progress($SF.Squad.HAT)>> </span>
 	<<elseif $SF.Squad.HAT === _HATU && $PC.skill.warfare < 75>>
 		//Your warfare skill is not high enough unlock the next upgrade.//<span style="float:right;"> <<print App.SF.progress($SF.Squad.HAT)>> </span> <br>
 	<<else>>
diff --git a/src/arcologyBuilding/base.js b/src/arcologyBuilding/base.js
index 43209901ab5b50661f696f424c921b9108f3c9ce..ccb978502ef34b9ef487706016d48b83b85efd4c 100644
--- a/src/arcologyBuilding/base.js
+++ b/src/arcologyBuilding/base.js
@@ -47,6 +47,15 @@ App.Arcology.defaultBuilding = function(terrain = "default") {
 	return candidates.pluck().construct(env).building;
 };
 
+/**
+ * Determine whether the arcology has a shop of a particular type
+ * @param {string} type
+ * @returns {boolean}
+ */
+App.Arcology.hasShopOfType = function(type) {
+	return V.building.findCells(cell => (cell instanceof App.Arcology.Cell.Shop) && cell.type === type).length > 0;
+};
+
 /**
  * Order of the building sections. All possible sections have to be added here.
  *
diff --git a/src/arcologyBuilding/penthouse.js b/src/arcologyBuilding/penthouse.js
index 9f810a8cf8f9f996dcc9ccfb2f191d3fb2727cc5..3b151dcd3dd38985e94453f51195dc9d6f5cdf56 100644
--- a/src/arcologyBuilding/penthouse.js
+++ b/src/arcologyBuilding/penthouse.js
@@ -67,7 +67,7 @@ App.Arcology.Cell.Penthouse = class extends App.Arcology.Cell.BaseCell {
 			const desc = `(${numberWithPluralOne(inc.capacity - V.tanks.length, "empty tank")})`;
 
 			if (V.readySlaves > 0) {
-				wrapper.append(createFacilityDiv(link, desc, App.UI.DOM.makeElement("span", "[!]", "noteworthy")));
+				wrapper.append(createFacilityDiv(link, App.UI.DOM.combineNodes(desc, App.UI.DOM.makeElement("span", "[!]", "noteworthy"))));
 			} else {
 				wrapper.append(createFacilityDiv(link, desc));
 			}
diff --git a/src/arcologyBuilding/presets.js b/src/arcologyBuilding/presets.js
index 9157e8bfc5cf6fb268265fd17f12adebecd23541..112aa9de1c3f034c2a660dbbfe60d37677503c11 100644
--- a/src/arcologyBuilding/presets.js
+++ b/src/arcologyBuilding/presets.js
@@ -164,17 +164,15 @@ App.Arcology.presets = (function() {
 	 * @returns {{building: App.Arcology.Building, apply: function()}}
 	 */
 	function randomizeBuilding(building, environment) {
-		const apply = [];
 		if (Math.random() < 0.5) {
-			apply.push(addFsShop(building, environment.fs));
+			addFsShop(building, environment.fs);
 		}
-		return {building: building, apply: () => { apply.forEach(a => { a(); }); }};
+		return {building: building, apply: () => { }};
 	}
 
 	/**
 	 * @param {App.Arcology.Building} building
 	 * @param {string} fs
-	 * @returns {function():void}
 	 */
 	function addFsShop(building, fs) {
 		function randomShop() {
@@ -184,93 +182,93 @@ App.Arcology.presets = (function() {
 		switch (fs) {
 			case "Supremacist":
 				randomShop().type = "Supremacist";
-				return () => { V.FSPromenade.Supremacist = 1; };
+				return;
 			case "Subjugationist":
 				randomShop().type = "Subjugationist";
-				return () => { V.FSPromenade.Subjugationist = 1; };
+				return;
 			case "GenderRadicalist":
 				randomShop().type = "Gender Radicalist";
-				return () => { V.FSPromenade.GenderRadicalist = 1; };
+				return;
 			case "GenderFundamentalist":
 				randomShop().type = "Gender Fundamentalist";
-				return () => { V.FSPromenade.GenderFundamentalist = 1; };
+				return;
 			case "Paternalist":
 				randomShop().type = "Paternalist";
-				return () => { V.FSPromenade.Paternalist = 1; };
+				return;
 			case "Degradationist":
 				randomShop().type = "Degradationist";
-				return () => { V.FSPromenade.Degradationist = 1; };
+				return;
 			case "AssetExpansionist":
 				randomShop().type = "Asset Expansionist";
-				return () => { V.FSPromenade.AssetExpansionist = 1; };
+				return;
 			case "SlimnessEnthusiast":
 				randomShop().type = "Slimness Enthusiast";
-				return () => { V.FSPromenade.SlimnessEnthusiast = 1; };
+				return;
 			case "TransformationFetishist":
 				randomShop().type = "Transformation Fetishist";
-				return () => { V.FSPromenade.TransformationFetishist = 1; };
+				return;
 			case "BodyPurist":
 				randomShop().type = "Body Purist";
-				return () => { V.FSPromenade.BodyPurist = 1; };
+				return;
 			case "MaturityPreferentialist":
 				randomShop().type = "Maturity Preferentialist";
-				return () => { V.FSPromenade.MaturityPreferentialist = 1; };
+				return;
 			case "YouthPreferentialist":
 				randomShop().type = "Youth Preferentialist";
-				return () => { V.FSPromenade.YouthPreferentialist = 1; };
+				return;
 			case "Pastoralist":
 				randomShop().type = "Pastoralist";
-				return () => { V.FSPromenade.Pastoralist = 1; };
+				return;
 			case "PhysicalIdealist":
 				randomShop().type = "Physical Idealist";
-				return () => { V.FSPromenade.PhysicalIdealist = 1; };
+				return;
 			case "ChattelReligionist":
 				randomShop().type = "Chattel Religionist";
-				return () => { V.FSPromenade.ChattelReligionist = 1; };
+				return;
 			case "RomanRevivalist":
 				randomShop().type = "Roman Revivalist";
-				return () => { V.FSPromenade.RomanRevivalist = 1; };
+				return;
 			case "NeoImperialist":
 				randomShop().type = "Neo-Imperialist";
-				return () => { V.FSPromenade.NeoImperialist = 1; };
+				return;
 			case "AztecRevivalist":
 				randomShop().type = "Aztec Revivalist";
-				return () => { V.FSPromenade.AztecRevivalist = 1; };
+				return;
 			case "EgyptianRevivalist":
 				randomShop().type = "Egyptian Revivalist";
-				return () => { V.FSPromenade.EgyptianRevivalist = 1; };
+				return;
 			case "EdoRevivalist":
 				randomShop().type = "Edo Revivalist";
-				return () => { V.FSPromenade.EdoRevivalist = 1; };
+				return;
 			case "ArabianRevivalist":
 				randomShop().type = "Arabian Revivalist";
-				return () => { V.FSPromenade.ArabianRevivalist = 1; };
+				return;
 			case "ChineseRevivalist":
 				randomShop().type = "Chinese Revivalist";
-				return () => { V.FSPromenade.Degradationist = 1; };
+				return;
 			case "Repopulationist":
 				randomShop().type = "Repopulationist";
-				return () => { V.FSPromenade.Repopulationist = 1; };
+				return;
 			case "Eugenics":
 				randomShop().type = "Eugenics";
-				return () => { V.FSPromenade.Eugenics = 1; };
+				return;
 			case "HedonisticDecadence":
 				randomShop().type = "Hedonism";
-				return () => { V.FSPromenade.Hedonism = 1; };
+				return;
 			case "IntellectualDependency":
 				randomShop().type = "Intellectual Dependency";
-				return () => { V.FSPromenade.IntellectualDependency = 1; };
+				return;
 			case "SlaveProfessionalism":
 				randomShop().type = "Slave Professionalism";
-				return () => { V.FSPromenade.SlaveProfessionalism = 1; };
+				return;
 			case "PetiteAdmiration":
 				randomShop().type = "Petite Admiration";
-				return () => { V.FSPromenade.PetiteAdmiration = 1; };
+				return;
 			case "StatuesqueGlorification":
 				randomShop().type = "Statuesque Glorification";
-				return () => { V.FSPromenade.StatuesqueGlorification = 1; };
+				return;
 			default:
-				return () => {};
+				return;
 		}
 	}
 
diff --git a/src/arcologyBuilding/shops.js b/src/arcologyBuilding/shops.js
index 725b030be4ae65a846a7ad4ed4c41600913f63da..7d2191a16f4f5f3a52cde0f78cc268a94e969617 100644
--- a/src/arcologyBuilding/shops.js
+++ b/src/arcologyBuilding/shops.js
@@ -318,7 +318,6 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 			fragment.append(this._makeUpgrade(
 				"Convert this sector of the promenade into a brothel.",
 				() => {
-					this._clearFsStyle();
 					V.brothel = 5;
 					this.type = "Brothel";
 				}, cost, "and will incur upkeep costs"
@@ -329,355 +328,19 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 			fragment.append(this._makeUpgrade(
 				"Build a club to serve as a focal point for public sluts.",
 				() => {
-					this._clearFsStyle();
 					V.club = 5;
 					this.type = "Club";
 				}, cost, "and will incur upkeep costs"
 			));
 		}
 
-		if (A.FSSubjugationist !== "unset") {
-			if (V.FSPromenade.Subjugationist === 0) {
+		for (const FS of FutureSocieties.activeFSes(A)) {
+			const decorationName = FutureSocieties.decorationName(FS);
+			if (decorationName && !App.Arcology.hasShopOfType(decorationName)) {
 				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Subjugationist establishments.",
+					`Upgrade this sector to appeal to ${FutureSocieties.displayAdj(FS)} establishments.`,
 					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Subjugationist = 1;
-						this.type = "Subjugationist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSSupremacist !== "unset") {
-			if (V.FSPromenade.Supremacist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Supremacist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Supremacist = 1;
-						this.type = "Supremacist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSGenderRadicalist !== "unset") {
-			if (V.FSPromenade.GenderRadicalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Gender Radicalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.GenderRadicalist = 1;
-						this.type = "Gender Radicalist";
-					}, cost
-				));
-			}
-		} else if (A.FSGenderFundamentalist !== "unset") {
-			if (V.FSPromenade.GenderFundamentalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Gender Fundamentalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.GenderFundamentalist = 1;
-						this.type = "Gender Fundamentalist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSPaternalist !== "unset") {
-			if (V.FSPromenade.Paternalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Paternalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Paternalist = 1;
-						this.type = "Paternalist";
-					}, cost
-				));
-			}
-		} else if (A.FSDegradationist !== "unset") {
-			if (V.FSPromenade.Degradationist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Degradationist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Degradationist = 1;
-						this.type = "Degradationist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSIntellectualDependency !== "unset") {
-			if (V.FSPromenade.IntellectualDependency === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Intellectual Dependency establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.IntellectualDependency = 1;
-						this.type = "Intellectual Dependency";
-					}, cost
-				));
-			}
-		} else if (A.FSSlaveProfessionalism !== "unset") {
-			if (V.FSPromenade.SlaveProfessionalism === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Slave Professionalism establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.SlaveProfessionalism = 1;
-						this.type = "Slave Professionalism";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSBodyPurist !== "unset") {
-			if (V.FSPromenade.BodyPurist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Body Purist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.BodyPurist = 1;
-						this.type = "Body Purist";
-					}, cost
-				));
-			}
-		} else if (A.FSTransformationFetishist !== "unset") {
-			if (V.FSPromenade.TransformationFetishist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Transformation Fetishist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.TransformationFetishist = 1;
-						this.type = "Transformation Fetishist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSYouthPreferentialist !== "unset") {
-			if (V.FSPromenade.YouthPreferentialist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Youth Preferentialist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.YouthPreferentialist = 1;
-						this.type = "Youth Preferentialist";
-					}, cost
-				));
-			}
-		} else if (A.FSMaturityPreferentialist !== "unset") {
-			if (V.FSPromenade.MaturityPreferentialist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Maturity Preferentialist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.MaturityPreferentialist = 1;
-						this.type = "Maturity Preferentialist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSPetiteAdmiration !== "unset") {
-			if (V.FSPromenade.PetiteAdmiration === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Petite Admiration establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.PetiteAdmiration = 1;
-						this.type = "Petite Admiration";
-					}, cost
-				));
-			}
-		} else if (A.FSStatuesqueGlorification !== "unset") {
-			if (V.FSPromenade.StatuesqueGlorification === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Statuesque Glorification establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.StatuesqueGlorification = 1;
-						this.type = "Statuesque Glorification";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSSlimnessEnthusiast !== "unset") {
-			if (V.FSPromenade.SlimnessEnthusiast === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Slimness Enthusiast establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.SlimnessEnthusiast = 1;
-						this.type = "Slimness Enthusiast";
-					}, cost
-				));
-			}
-		} else if (A.FSAssetExpansionist !== "unset") {
-			if (V.FSPromenade.AssetExpansionist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Asset Expansionist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.AssetExpansionist = 1;
-						this.type = "Asset Expansionist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSPastoralist !== "unset") {
-			if (V.FSPromenade.Pastoralist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Pastoralist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Pastoralist = 1;
-						this.type = "Pastoralist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSPhysicalIdealist !== "unset") {
-			if (V.FSPromenade.PhysicalIdealist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Physical Idealist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.PhysicalIdealist = 1;
-						this.type = "Physical Idealist";
-					}, cost
-				));
-			}
-		} else if (A.FSHedonisticDecadence !== "unset") {
-			if (V.FSPromenade.Hedonism === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Hedonistic establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Hedonism = 1;
-						this.type = "Hedonism";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSRepopulationFocus !== "unset") {
-			if (V.FSPromenade.Repopulationist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Repopulationist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Repopulationist = 1;
-						this.type = "Repopulationist";
-					}, cost
-				));
-			}
-		} else if (A.FSRestart !== "unset") {
-			if (V.FSPromenade.Eugenics === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Eugenics establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.Eugenics = 1;
-						this.type = "Eugenics";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSChattelReligionist !== "unset") {
-			if (V.FSPromenade.ChattelReligionist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Chattel Religionist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.ChattelReligionist = 1;
-						this.type = "Chattel Religionist";
-					}, cost
-				));
-			}
-		}
-
-		if (A.FSRomanRevivalist !== "unset") {
-			if (V.FSPromenade.RomanRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Roman Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.RomanRevivalist = 1;
-						this.type = "Roman Revivalist";
-					}, cost
-				));
-			}
-		} else if (A.FSAztecRevivalist !== "unset") {
-			if (V.FSPromenade.AztecRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Aztec Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.AztecRevivalist = 1;
-						this.type = "Aztec Revivalist";
-					}, cost
-				));
-			}
-		} else if (A.FSNeoImperialist !== "unset") {
-			if (V.FSPromenade.NeoImperialist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Neo-Imperialist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.NeoImperialist = 1;
-						this.type = "Neo-Imperialist";
-					}, cost
-				));
-			}
-		} else if (A.FSEgyptianRevivalist !== "unset") {
-			if (V.FSPromenade.EgyptianRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Egyptian Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.EgyptianRevivalist = 1;
-						this.type = "Egyptian Revivalist";
-					}, cost
-				));
-			}
-		} else if (A.FSEdoRevivalist !== "unset") {
-			if (V.FSPromenade.EdoRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Edo Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.EdoRevivalist = 1;
-						this.type = "Edo Revivalist";
-					}, cost
-				));
-			}
-		} else if (A.FSArabianRevivalist !== "unset") {
-			if (V.FSPromenade.ArabianRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Arabian Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.ArabianRevivalist = 1;
-						this.type = "Arabian Revivalist";
-					}, cost
-				));
-			}
-		} else if (A.FSChineseRevivalist !== "unset") {
-			if (V.FSPromenade.ChineseRevivalist === 0) {
-				fragment.append(this._makeUpgrade(
-					"Upgrade this sector to appeal to Chinese Revivalist establishments.",
-					() => {
-						this._clearFsStyle();
-						V.FSPromenade.ChineseRevivalist = 1;
-						this.type = "Chinese Revivalist";
+						this.type = decorationName;
 					}, cost
 				));
 			}
@@ -687,7 +350,6 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 			fragment.append(this._makeUpgrade(
 				"Return this sector to standard outlets",
 				() => {
-					this._clearFsStyle();
 					this.type = "Shops";
 				}, cost
 			));
@@ -696,13 +358,6 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 		return fragment;
 	}
 
-	_clearFsStyle() {
-		if (!["Shops", "Brothel", "Club"].includes(this.type)) {
-			const currentFSStyle = this.type.replace(/\s+/g, '');
-			V.FSPromenade[currentFSStyle] = 0;
-		}
-	}
-
 	/**
 	 * @returns {boolean}
 	 */
diff --git a/src/art/vector/VectorArtJS.js b/src/art/vector/VectorArtJS.js
index 31bd488ddafce88546e59b61be8f6899afd20143..7edef164b7e331f554c5b8a5fa2f7f5772870ec5 100644
--- a/src/art/vector/VectorArtJS.js
+++ b/src/art/vector/VectorArtJS.js
@@ -1924,12 +1924,12 @@ App.Art.vectorArtElement = (function() {
 		}
 		if (outfit !== undefined) {
 			if (hasAnyLegs(slave)) {
-				if (slave.clothes === "Imperial Plate"){
+				if (slave.clothes === "Imperial Plate") {
 					svgQueue.add(`Art_Vector_Butt_Outfit_Battlearmor_${buttSize}`);
 				} else if (slave.clothes !== "a slutty qipao" && slave.clothes !== "harem gauze" && slave.clothes !== "slutty jewelry" && slave.clothes !== "Western clothing") { /* these clothes have a stump/leg outfit, but no butt outfit */
 					svgQueue.add(`Art_Vector_Butt_Outfit_${outfit}_${buttSize}`);
 				}
-				if (slave.clothes === "Imperial Plate"){
+				if (slave.clothes === "Imperial Plate") {
 					svgQueue.add(`Art_Vector_Leg_Outfit_Battlearmor_${legSize}`);
 				} else if (slave.clothes !== "a schoolgirl outfit") { /* file is there, but contains no artwork */
 					svgQueue.add(`Art_Vector_Leg_Outfit_${outfit}_${legSize}`);
@@ -2014,7 +2014,7 @@ App.Art.vectorArtElement = (function() {
 			}
 		}
 		/* note: latex clothing actually shows some hair, but there is no appropriate art for it */
-		if (slave.faceAccessory  === "cat ears") {
+		if (slave.faceAccessory === "cat ears") {
 			svgQueue.add("Art_Vector_Cat_Ear_Back");
 		}
 	}
@@ -2091,7 +2091,7 @@ App.Art.vectorArtElement = (function() {
 			}
 		}
 		/* note: latex clothing actually shows some hair, but there is no appropriate art for it */
-		if (slave.faceAccessory  === "cat ears") {
+		if (slave.faceAccessory === "cat ears") {
 			svgQueue.add("Art_Vector_Cat_Ear_Fore");
 		}
 	}
@@ -2561,17 +2561,17 @@ App.Art.vectorArtElement = (function() {
 
 		/* ADDONS */
 		if (slave.fuckdoll === 0) { /* Fuckdolls cannot be decorated */
-			if (slave.mouthAccessory   === "dildo gag") {
+			if (slave.mouthAccessory === "dildo gag") {
 				svgQueue.add("Art_Vector_Dildo_Gag");
-			} else if (slave.mouthAccessory  === "ball gag") {
+			} else if (slave.mouthAccessory === "ball gag") {
 				svgQueue.add("Art_Vector_Ball_Gag");
-			} else if (slave.mouthAccessory  === "bit gag") {
+			} else if (slave.mouthAccessory === "bit gag") {
 				svgQueue.add("Art_Vector_Bit_Gag");
-			} else if (slave.mouthAccessory  === "massive dildo gag") {
+			} else if (slave.mouthAccessory === "massive dildo gag") {
 				svgQueue.add("Art_Vector_Massive_Dildo_Gag");
 			}
 
-			if (slave.faceAccessory   === "porcelain mask") {
+			if (slave.faceAccessory === "porcelain mask") {
 				svgQueue.add("Art_Vector_Porcelain_Mask");
 			}
 
@@ -2915,7 +2915,7 @@ App.Art.vectorArtElement = (function() {
 	}
 
 	return VectorArt;
-}) ();
+})();
 
 
 App.Art.legacyVectorArtElement = function() {
@@ -3073,7 +3073,7 @@ App.Art.legacyVectorArtElement = function() {
 				addSkinImg(res, "feet", skinFilter);
 			}
 			if (slave.shoes === "extreme heels" || slave.shoes === "boots") {
-				addImg(res, `outfit/${shoesType}${wearingLatex ? " latex": ""}`);
+				addImg(res, `outfit/${shoesType}${wearingLatex ? " latex" : ""}`);
 			} else if (slave.shoes === "heels" || slave.shoes === "flats") {
 				if (wearingLatex === true) {
 					addImg(res, `outfit/${shoesType} latex`);
diff --git a/src/cheats/PCCheatMenu.tw b/src/cheats/PCCheatMenu.tw
index c1257c0f6085e02a8f1644033a3b4e2d1b188694..8b5cddb2caee8a0f822c829ce6890d6d58e622ea 100644
--- a/src/cheats/PCCheatMenu.tw
+++ b/src/cheats/PCCheatMenu.tw
@@ -1,341 +1,14 @@
 :: PCCheatMenu [nobr]
 
 <<set $nextButton = "Apply", $nextLink = "PCCheatMenuCheatDatatypeCleanup", $encyclopedia = "Design Your Master">>
-<<set $tempSlave = clone($PC)>>
 
-''Cheat Editing Player Character'' <<link "[Cancel]" "Manage Personal Affairs">><<unset $tempSlave, $customEvalCode>><</link>>
+''Cheat Editing Player Character''
+<<link "[Cancel]" "Manage Personal Affairs">>
+	<<unset $tempSlave, $customEvalCode>>
+	<<set $PC = clone($backupSlave)>>
+<</link>>
 
-<br><br>
-
-Title: ''<<if $tempSlave.title == 1>>Master<<else>>Mistress<</if>>''
-<br><<radiobutton "$tempSlave.title" 1>> Master
-	<<radiobutton "$tempSlave.title" 0>> Mistress
-<br>
-Sex: ''$tempSlave.genes''
-<br><<radiobutton "$tempSlave.genes" "XY">> XY
-	<<radiobutton "$tempSlave.genes" "XX">> XX
-<br>''Name'': <<textbox "$tempSlave.slaveName" $tempSlave.slaveName>>
-<br>''Birth Name'': <<textbox "$tempSlave.birthName" $tempSlave.birthName>>
-<br>''Surname'': <<textbox "$tempSlave.slaveSurname" $tempSlave.slaveSurname>>
-<br>''Birth Surname'': <<textbox "$tempSlave.birthSurname" $tempSlave.birthSurname>>
-<br>''Custom Title'': <<textbox "$tempSlave.custom.title" $tempSlave.custom.title>>
-
-<br>Nationality: ''$tempSlave.nationality''.
-<<textbox "$tempSlave.nationality" $tempSlave.nationality>>
-
-<br>Career: ''$tempSlave.career''.
-<br><<radiobutton "$tempSlave.career" "wealth">> Wealth
-	<<radiobutton "$tempSlave.career" "capitalist">> Capitalist
-	<<radiobutton "$tempSlave.career" "mercenary">> Mercenary
-	<<radiobutton "$tempSlave.career" "slaver">> Slaver
-	<<radiobutton "$tempSlave.career" "engineer">> Engineer
-	<<radiobutton "$tempSlave.career" "medicine">> Medicine
-	<<radiobutton "$tempSlave.career" "celebrity">> Celebrity
-	<<radiobutton "$tempSlave.career" "escort">> Escort
-	<<radiobutton "$tempSlave.career" "servant">> Servant
-	<<radiobutton "$tempSlave.career" "gang">> Gang
-	<<radiobutton "$tempSlave.career" "BlackHat">> BlackHat
-
-<br>Method of acquiring the arcology: ''$tempSlave.rumor''.
-<br><<radiobutton "$tempSlave.rumor" "wealth">> Wealth
-	<<radiobutton "$tempSlave.rumor" "diligence">> Diligence
-	<<radiobutton "$tempSlave.rumor" "force">> Force
-	<<radiobutton "$tempSlave.rumor" "social engineering">> Social Engineering
-	<<radiobutton "$tempSlave.rumor" "luck">> Luck
-
-<br>Preferred refreshment: <<textbox "$tempSlave.refreshment" $tempSlave.refreshment>>
-<br><<radiobutton "$tempSlave.refreshment" "cigar">> Cigar
-	<<radiobutton "$tempSlave.refreshment" "whiskey">> Whiskey
-
-<br>Preferred method of consumption: <<if $tempSlave.refreshmentType == 0>>Smoked<<elseif $tempSlave.refreshmentType == 1>>Drank<<elseif $tempSlave.refreshmentType == 2>>Eaten<<elseif $tempSlave.refreshmentType == 3>>Snorted<<elseif $tempSlave.refreshmentType == 4>>Injected<<elseif $tempSlave.refreshmentType == 5>>Popped<<else>>Orally Dissolved<</if>>
-<br><<radiobutton "$tempSlave.refreshmentType" 0>> Smoked
-	<<radiobutton "$tempSlave.refreshmentType" 1>> Drank
-	<<radiobutton "$tempSlave.refreshmentType" 2>> Eaten
-	<<radiobutton "$tempSlave.refreshmentType" 3>> Snorted
-	<<radiobutton "$tempSlave.refreshmentType" 4>> Injected
-	<<radiobutton "$tempSlave.refreshmentType" 5>> Popped
-	<<radiobutton "$tempSlave.refreshmentType" 6>> Orally Dissolved
-<br>
-
-<br>''Skin'': <<textbox "$tempSlave.skin" $tempSlave.skin>>
-<br>''Genetic Skin'': <<textbox "$tempSlave.origSkin" $tempSlave.origSkin>>
-<br>''Race'': <<textbox "$tempSlave.race" $tempSlave.race>>
-<br>''Genetic Race'': <<textbox "$tempSlave.origRace" $tempSlave.origRace>>
-<br>''Eye Color'': <<textbox "$tempSlave.eye.right.iris" $tempSlave.eye.right.iris>>
-<br>''Genetic Eye Color'': <<textbox "$tempSlave.eye.origColor" $tempSlave.eye.origColor>>
-<br>''Pupil Shape'': <<textbox "$tempSlave.eye.right.pupil" $tempSlave.eye.right.pupil>>
-<br>''Sclera Color'': <<textbox "$tempSlave.eye.right.sclera" $tempSlave.eye.right.sclera>>
-<br>''Hair Color'': <<textbox "$tempSlave.hColor" $tempSlave.hColor>>
-<br>''Genetic Hair Color'': <<textbox "$tempSlave.origHColor" $tempSlave.origHColor>>
-<br>
-<br>''Boobs'': <<textbox "$tempSlave.boobs" $tempSlave.boobs>> //100: masculine chest (if title = 1) or flat chested (if title = 0)(WIP), 300+: A cup, 400+: B cup, 500+: C cup, 650+: D cup, 800+: DD cup, 1000+: F cup, 1200+: G cup, 1400+: H cup//
-<br>''Boobs Implant'': <<textbox "$tempSlave.boobsImplant" $tempSlave.boobsImplant>> //do you have breast implants - size in CCs//
-<br>''Lactation'': <<textbox "$tempSlave.lactation" $tempSlave.lactation>> //are you lactating - 0: no, 1: yes//
-<br>
-<br>''Butt Size'': <<textbox "$tempSlave.butt" $tempSlave.butt>> //2: normal, 3: big, 4: huge, 5: enormous//
-<br>''Butt Implant'': <<textbox "$tempSlave.buttImplant" $tempSlave.buttImplant>> //do you have butt implants - 0: no, 1+: yes//
-<br>
-<br>''Vagina'': <<textbox "$tempSlave.vagina" $tempSlave.vagina>>
-<br>''New Vagina'': <<textbox "$tempSlave.newVag" $tempSlave.newVag>> //0: no, 1: yes//
-<br>
-<br>''Dick'': <<textbox "$tempSlave.dick" $tempSlave.dick>>
-<br>''Balls Size'': <<textbox "$tempSlave.balls" $tempSlave.balls>> //3: normal, 5: big, 9: huge, 14: enormous, 30: monstrous//
-<br>''Balls Implant'': <<textbox "$tempSlave.ballsImplant" $tempSlave.ballsImplant>> //0: none, 1+ yes//
-
-<br><br>
-__Age__
-<br>''Actual Age'': <<textbox "$tempSlave.actualAge" $tempSlave.actualAge>>
-<br>''Physical Age'': <<textbox "$tempSlave.physicalAge" $tempSlave.physicalAge>>
-<br>''Visual Age'': <<textbox "$tempSlave.visualAge" $tempSlave.visualAge>>
-<br>''Ovary Age'': <<textbox "$tempSlave.ovaryAge" $tempSlave.ovaryAge>>
-<br>''Age Implant'': <<textbox "$tempSlave.ageImplant" $tempSlave.ageImplant>> //0: no surgery, 1: age altering surgery//
-<br>''Player Aging'': <<textbox "$playerAging" $playerAging>> //0: no aging, 1: no aging, but birthdays, 2: aging//
-
-<br><br>
-__Pregnancy__
-<br>''Pregnancy Length'': <<textbox "$tempSlave.preg" $tempSlave.preg>> //how far along your pregnancy is (pregMood kicks in at 24+ weeks) - -2: infertile, -1: contraceptives, 0: not pregnant, 1 - 42: pregnant, 43+: giving birth//
-<br>''Fetus Count'': <<textbox "$tempSlave.pregType" $tempSlave.pregType>> //how many you're having (1-8)//
-<br>''PregSource'': <<textbox "$tempSlave.pregSource" $tempSlave.pregSource>> //who knocked you up - 0: unknown, -1: self-impreg, -2: citizen, -3: former Master, -4: male arc owner, -5: client, -6: Societal Elite, -7: designer baby, -9: Futanari Sister//
-<br>''PregMood'': <<textbox "$tempSlave.pregMood" $tempSlave.pregMood>> //how you act when heavily pregnant - 0: no change, 1: submissive and motherly, 2: aggressive and dominant//
-
-<br><br>
-''Skills:''
-<br>
-<br>Trading:
-''<<if $tempSlave.skill.trading >= 100>>
-	You are a master at economics and trading.
-<<elseif $tempSlave.skill.trading >= 80>>
-	You are an expert at economics and trading.
-<<elseif $tempSlave.skill.trading >= 60>>
-	You are skilled in economics and trading.
-<<elseif $tempSlave.skill.trading >= 40>>
-	You know some things about economics and trading.
-<<elseif $tempSlave.skill.trading >= 20>>
-	You are a beginner in economics.
-<<elseif $tempSlave.skill.trading >= 0>>
-	You know only the basics of trading.
-<<elseif $tempSlave.skill.trading >= -20>>
-	You know how to haggle a little.
-<<elseif $tempSlave.skill.trading >= -40>>
-	You know how to shop around.
-<<elseif $tempSlave.skill.trading >= -60>>
-	You know not to pay sticker price.
-<<elseif $tempSlave.skill.trading >= -80>>
-	People always give you discounts, but you never save any money.
-<<else>>
-	They said it was a bear market, so where are the bears?
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.trading" 100>> Economics master
-<<radiobutton "$tempSlave.skill.trading" 90>> Economics expert
-<<radiobutton "$tempSlave.skill.trading" 70>> Skilled in economics
-<<radiobutton "$tempSlave.skill.trading" 50>> Amateur economist
-<<radiobutton "$tempSlave.skill.trading" 30>> Economics beginner
-<<radiobutton "$tempSlave.skill.trading" 0>> Basic trader
-<<radiobutton "$tempSlave.skill.trading" -10>> Haggler
-<<radiobutton "$tempSlave.skill.trading" -30>> Shopper
-<<radiobutton "$tempSlave.skill.trading" -50>> Weak saver
-<<radiobutton "$tempSlave.skill.trading" -70>> Money sieve
-<<radiobutton "$tempSlave.skill.trading" -90>> What's a trading?
-<br>
-<br>Warfare:
-''<<if $tempSlave.skill.warfare >= 100>>
-	You are a master of warfare.
-<<elseif $tempSlave.skill.warfare >= 80>>
-	You are an expert at tactics and strategy.
-<<elseif $tempSlave.skill.warfare >= 60>>
-	You are skilled in combat.
-<<elseif $tempSlave.skill.warfare >= 40>>
-	You know some things about combat.
-<<elseif $tempSlave.skill.warfare >= 20>>
-	You are a beginner in tactics and strategy.
-<<elseif $tempSlave.skill.warfare >= 0>>
-	You know only the basics of fighting.
-<<elseif $tempSlave.skill.warfare >= -20>>
-	You know how to hold a gun.
-<<elseif $tempSlave.skill.warfare >= -40>>
-	You know how to stab with a knife.
-<<elseif $tempSlave.skill.warfare >= -60>>
-	Go for the throat?
-<<elseif $tempSlave.skill.warfare >= -80>>
-	Just kick them in the balls, right?
-<<else>>
-	People like you are usually the first raped in a war.
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.warfare" 100>> Warfare master
-<<radiobutton "$tempSlave.skill.warfare" 90>> Warfare expert
-<<radiobutton "$tempSlave.skill.warfare" 70>> Skilled in warfare
-<<radiobutton "$tempSlave.skill.warfare" 50>> Amateur combatant
-<<radiobutton "$tempSlave.skill.warfare" 30>> Combat beginner
-<<radiobutton "$tempSlave.skill.warfare" 0>> Basic fighter
-<<radiobutton "$tempSlave.skill.warfare" -10>> Gun haver
-<<radiobutton "$tempSlave.skill.warfare" -30>> Knife holder
-<<radiobutton "$tempSlave.skill.warfare" -50>> Throat puncher
-<<radiobutton "$tempSlave.skill.warfare" -70>> Groin kicker?
-<<radiobutton "$tempSlave.skill.warfare" -90>> Most likely to be raped
-<br>
-<br>Slaving:
-''<<if $tempSlave.skill.slaving >= 100>>
-	You are a master slaver.
-<<elseif $tempSlave.skill.slaving >= 80>>
-	You are an expert at enslaving.
-<<elseif $tempSlave.skill.slaving >= 60>>
-	You are skilled in slaving.
-<<elseif $tempSlave.skill.slaving >= 40>>
-	You know some things about getting slaves.
-<<elseif $tempSlave.skill.slaving >= 20>>
-	You are a beginner in slaving.
-<<elseif $tempSlave.skill.slaving >= 0>>
-	You know only the basics of slaving.
-<<elseif $tempSlave.skill.slaving >= -20>>
-	You know how to avoid becoming a slave.
-<<elseif $tempSlave.skill.slaving >= -40>>
-	You know to read contracts before you sign them.
-<<elseif $tempSlave.skill.slaving >= -60>>
-	You know to be careful.
-<<elseif $tempSlave.skill.slaving >= -80>>
-	You know better than to trust anyone.
-<<else>>
-	It would be easy to enslave you.
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.slaving" 100>> Master slaver
-<<radiobutton "$tempSlave.skill.slaving" 90>> Expert slaver
-<<radiobutton "$tempSlave.skill.slaving" 70>> Skilled in slaving
-<<radiobutton "$tempSlave.skill.slaving" 50>> Amateur slaver
-<<radiobutton "$tempSlave.skill.slaving" 30>> Slaving beginner
-<<radiobutton "$tempSlave.skill.slaving" 0>> Basic slaver
-<<radiobutton "$tempSlave.skill.slaving" -10>> Can't make me a slave
-<<radiobutton "$tempSlave.skill.slaving" -30>> Can read contracts
-<<radiobutton "$tempSlave.skill.slaving" -50>> Careful now
-<<radiobutton "$tempSlave.skill.slaving" -70>> Don't trust that guy
-<<radiobutton "$tempSlave.skill.slaving" -90>> Potential slave
-<br>
-<br>Engineering:
-''<<if $tempSlave.skill.engineering >= 100>>
-	You are a master engineer.
-<<elseif $tempSlave.skill.engineering >= 80>>
-	You are an expert at engineering.
-<<elseif $tempSlave.skill.engineering >= 60>>
-	You are skilled in engineering.
-<<elseif $tempSlave.skill.engineering >= 40>>
-	You know some things about engineering.
-<<elseif $tempSlave.skill.engineering >= 20>>
-	You are a beginner in engineering.
-<<elseif $tempSlave.skill.engineering >= 0>>
-	You know only the basics of engineering.
-<<elseif $tempSlave.skill.engineering >= -20>>
-	You can build a gingerbread house that doesn't collapse.
-<<elseif $tempSlave.skill.engineering >= -40>>
-	You can tie a tight knot, does that count?
-<<elseif $tempSlave.skill.engineering >= -60>>
-	Glue is your friend; lots of it.
-<<elseif $tempSlave.skill.engineering >= -80>>
-	You know better than to even try to build something.
-<<else>>
-	You can cook; that's sort of like building something, right?
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.engineering" 100>> Master engineer
-<<radiobutton "$tempSlave.skill.engineering" 90>> Expert engineer
-<<radiobutton "$tempSlave.skill.engineering" 70>> Skilled in engineering
-<<radiobutton "$tempSlave.skill.engineering" 50>> Amateur engineer
-<<radiobutton "$tempSlave.skill.engineering" 30>> Engineering beginner
-<<radiobutton "$tempSlave.skill.engineering" 0>> Basic engineer
-<<radiobutton "$tempSlave.skill.engineering" -10>> Gingerbread house
-<<radiobutton "$tempSlave.skill.engineering" -30>> Knot tyer
-<<radiobutton "$tempSlave.skill.engineering" -50>> You can use glue
-<<radiobutton "$tempSlave.skill.engineering" -70>> You aren't handy
-<<radiobutton "$tempSlave.skill.engineering" -90>> My hovercraft is full of eels
-<br>
-<br>Medicine:
-''<<if $tempSlave.skill.medicine >= 100>>
-	You are a master surgeon.
-<<elseif $tempSlave.skill.medicine >= 80>>
-	You are an expert at medicine and surgery.
-<<elseif $tempSlave.skill.medicine >= 60>>
-	You are skilled in surgery.
-<<elseif $tempSlave.skill.medicine >= 40>>
-	You know some things about medicine.
-<<elseif $tempSlave.skill.medicine >= 20>>
-	You are a beginner in medicine.
-<<elseif $tempSlave.skill.medicine >= 0>>
-	You know the basics of treating injuries.
-<<elseif $tempSlave.skill.medicine >= -20>>
-	You can stop a wound from getting infected.
-<<elseif $tempSlave.skill.medicine >= -40>>
-	Gauze is your friend. Just keep wrapping.
-<<elseif $tempSlave.skill.medicine >= -60>>
-	You know how to apply a band-aid.
-<<elseif $tempSlave.skill.medicine >= -80>>
-	Cure-alls are wonderful. Why aren't they sold in stores, though?
-<<else>>
-	Alcohol makes pain go away, right?
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.medicine" 100>> Master surgeon
-<<radiobutton "$tempSlave.skill.medicine" 90>> Expert surgeon
-<<radiobutton "$tempSlave.skill.medicine" 70>> Skilled in medicine
-<<radiobutton "$tempSlave.skill.medicine" 50>> Amateur surgeon
-<<radiobutton "$tempSlave.skill.medicine" 30>> Medical beginner
-<<radiobutton "$tempSlave.skill.medicine" 0>> Basic medic
-<<radiobutton "$tempSlave.skill.medicine" -10>> Can treat wounds
-<<radiobutton "$tempSlave.skill.medicine" -30>> First-aid kit user
-<<radiobutton "$tempSlave.skill.medicine" -50>> Band-aid applier
-<<radiobutton "$tempSlave.skill.medicine" -70>> MEDIC!
-<<radiobutton "$tempSlave.skill.medicine" -90>> Give me another beer
-<br>
-<br>Hacking:
-''<<if $tempSlave.skill.hacking >= 100>>
-	You are a master of hacking.
-<<elseif $tempSlave.skill.hacking >= 80>>
-	You are an expert at hacking.
-<<elseif $tempSlave.skill.hacking >= 60>>
-	You are skilled in hacking.
-<<elseif $tempSlave.skill.hacking >= 40>>
-	You know some things about hacking.
-<<elseif $tempSlave.skill.hacking >= 20>>
-	You are a beginner in hacking.
-<<elseif $tempSlave.skill.hacking >= 0>>
-	You know only the basics of hacking.
-<<elseif $tempSlave.skill.hacking >= -20>>
-	You know how to click a mouse.
-<<elseif $tempSlave.skill.hacking >= -40>>
-	Enter does something?
-<<elseif $tempSlave.skill.hacking >= -60>>
-	Where is the "any" key?
-<<elseif $tempSlave.skill.hacking >= -80>>
-	You can push the power button, good job.
-<<else>>
-	This black box thingy is magical.
-<</if>>''
-<br>
-<<radiobutton "$tempSlave.skill.hacking" 100>> Master hacker
-<<radiobutton "$tempSlave.skill.hacking" 90>> Expert hacker
-<<radiobutton "$tempSlave.skill.hacking" 70>> Skilled hacker
-<<radiobutton "$tempSlave.skill.hacking" 50>> Amateur hacker
-<<radiobutton "$tempSlave.skill.hacking" 30>> Hacking beginner
-<<radiobutton "$tempSlave.skill.hacking" 0>> Basic hacker
-<<radiobutton "$tempSlave.skill.hacking" -10>> Mouse clicker
-<<radiobutton "$tempSlave.skill.hacking" -30>> You can press Enter
-<<radiobutton "$tempSlave.skill.hacking" -50>> Where's the "any" key?
-<<radiobutton "$tempSlave.skill.hacking" -70>> Main screen turn on?
-<<radiobutton "$tempSlave.skill.hacking" -90>> Ooh, cool glowy thingy!
-
-<br>
-<br>Your mother ID:
-<<textbox "$tempSlave.mother" $tempSlave.mother>>
-<br>Your father ID:
-<<textbox "$tempSlave.father" $tempSlave.father>>
-
-<br>
-<br>''Sexual Energy'': <<textbox "$tempSlave.sexualEnergy" $tempSlave.sexualEnergy>>
-<br>''Cum Tap'': <<textbox "$tempSlave.skill.cumTap" $tempSlave.skill.cumTap>>
-<br>''Stored Cum'': <<textbox "$tempSlave.counter.storedCum" $tempSlave.counter.storedCum>>
-<br>''Fertility Drugs'': <<textbox "$tempSlave.fertDrugs" $tempSlave.fertDrugs>> //0: no, 1: yes//
-<br>''Forced Fertility Drugs'': <<textbox "$tempSlave.forcedFertDrugs" $tempSlave.forcedFertDrugs>> //time left in body system//
-<br>''Stamina Pills'': <<textbox "$tempSlave.staminaPills" $tempSlave.staminaPills>> //0: no, 1: yes//
+<<includeDOM App.UI.Player.design()>>
 
 <br><br>Custom Cheat:
 <br><<textarea "$customEvalCode" "">>
diff --git a/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
index 2d5b64ae8023dbb82b5c4f71e859cf27500d56de..46c8957c0af46ff4ddff3f5de80ff90374072133 100644
--- a/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
+++ b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
@@ -1,80 +1,17 @@
 :: PCCheatMenuCheatDatatypeCleanup [nobr]
 
 <<set $nextButton = "Continue", $nextLink = "Manage Personal Affairs">>
-/* Cancel Option Check */
-<<if $tempSlave == "unset">>
-	<<unset $tempSlave>>
-	<<goto "Manage Personal Affairs">>
-<</if>>
 
 <<if $customEvalCode>>
 	<<if $customEvalCode.charAt(0) != "(" || $nextLink == ")">> /* second condition is only there for sanityCheck */
 		<<set $customEvalCode = "(" + $customEvalCode + ")">>
 	<</if>>
 	<<if typeof eval($customEvalCode) === "function">>
-		<<run (eval($customEvalCode))($tempSlave)>>
+		<<run (eval($customEvalCode))($PC)>>
 	<</if>>
 <</if>>
-<<unset $customEvalCode>>
-
-<<set $tempSlave.preg = Number($tempSlave.preg) || 0>>
-<<set $tempSlave.pregSource = Number($tempSlave.pregSource) || 0>>
-<<set $tempSlave.pregType = Number($tempSlave.pregType) || 0>>
-<<set WombInit($tempSlave)>> /* just to make sure */
-<<set $tempSlave.womb.length = 0>> /* simple way to delete all fetuses */
-<<set WombImpregnate($tempSlave, $tempSlave.pregType, $tempSlave.pregSource, $tempSlave.preg)>> /* recreates fetuses */
-<<if $tempSlave.preg > 0>>
-	<<set $tempSlave.belly = WombGetVolume($tempSlave)>>
-	<<set $tempSlave.pregWeek = $tempSlave.preg>>
-<<else>>
-	<<set $tempSlave.belly = 0>>
-	<<set $tempSlave.pregWeek = 0>>
-<</if>>
+<<unset $customEvalCode, $backupSlave>>
 
-<<if $tempSlave.boobs < 300>>
-	<<set $tempSlave.boobs = 100>>
-	<<set $tempSlave.boobsImplant = 0>>
-<</if>>
-<<if $tempSlave.boobsImplant > $tempSlave.boobs>>
-	<<set $tempSlave.boobsImplant = $tempSlave.boobs>>
-<</if>>
-<<if $tempSlave.butt < 2>>
-	<<set $tempSlave.butt = 2>>
-	<<set $tempSlave.buttImplant = 0>>
-<</if>>
-<<if $tempSlave.buttImplant > $tempSlave.butt>>
-	<<set $tempSlave.buttImplant = $tempSlave.butt>>
-<</if>>
-<<if $tempSlave.dick == 0>>
-	<<set $tempSlave.balls = 0>>
-	<<set $tempSlave.ballsImplant = 0>>
-	<<set $tempSlave.scrotum = 0>>
-	<<set $tempSlave.prostate = 0>>
-<</if>>
-<<if $tempSlave.ballsImplant > $tempSlave.balls>>
-	<<set $tempSlave.ballsImplant = $tempSlave.balls>>
-<</if>>
-<<if $tempSlave.vagina == -1>>
-	<<set $tempSlave.newVag = 0>>
-	<<set $tempSlave.ovaries = 0>>
-	<<set $tempSlave.vaginaLube = 0>>
-<</if>>
-<<if $tempSlave.lactation > 0 && $tempSlave.lactationDuration == 0>>
-	<<set $tempSlave.lactationDuration = 2>>
-<<elseif $tempSlave.lactation == 0 && $tempSlave.lactationDuration > 0>>
-	<<set $tempSlave.lactationDuration = 0>>
-<</if>>
-<<set $tempSlave.eye.left.iris = $tempSlave.eye.right.iris>>
-<<set $tempSlave.eye.left.pupil = $tempSlave.eye.right.pupil>>
-<<set $tempSlave.eye.left.sclera = $tempSlave.eye.right.sclera>>
+<<run App.Entity.Utils.PCCheatCleanup()>>
 
 You perform the dark rituals, pray to the dark gods, and sell your soul for the power to reshape your body and life at will. What a cheater!
-
-<<set $PC = clone($tempSlave)>>
-<<run ibc.recalculate_coeff_id(-1)>>
-<<run PCDatatypeCleanup()>>
-<<set $upgradeMultiplierArcology = upgradeMultiplier('engineering')>>
-<<set $upgradeMultiplierMedicine = upgradeMultiplier('medicine')>>
-<<set $upgradeMultiplierTrade = upgradeMultiplier('trading')>>
-<<set $HackingSkillMultiplier = upgradeMultiplier('hacking')>>
-<<unset $tempSlave>>
diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
index fd55cbd63b4d62d96f2d1a73f3312335c242cadb..a5ae1078947a4584ce1f9752af7e8e34c11896e1 100644
--- a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
+++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
@@ -204,7 +204,16 @@
 <<if $tempSlave.geneticQuirks.albinism === 2 && $activeSlave.albinismOverride === null>>
 	<<run induceAlbinism($tempSlave, 2)>>
 <</if>>
-
+<<if $studio == 0>>
+	<<set $tempSlave.porn.feed = 0>>
+	<<set $tempSlave.porn.spending = 0>>
+<</if>>
+<<set $tempSlave.porn.viewerCount = Number($tempSlave.porn.viewerCount) || 0>>
+<<set $tempSlave.porn.feed = Number($tempSlave.porn.feed) || 0>>
+<<set $tempSlave.porn.spending = Number($tempSlave.porn.spending) || 0>>
+<<for _genre range App.Porn.getAllGenres()>>
+    <<set $tempSlave.porn.fame[_genre.fameVar] = Number($tempSlave.porn.fame[_genre.fameVar]) || 0>>
+<</for>>
 <br>
 You perform the dark rituals, pray to the dark gods, and sell your soul for the power to change and mold slaves to your will.
 
diff --git a/src/cheats/mod_editSlaveCheatNew.tw b/src/cheats/mod_editSlaveCheatNew.tw
index 48db8c5b65e948de4e742abcb333f09a2c4b7969..f71dd507fbbea5c06267f128a041fa76f4ad812a 100644
--- a/src/cheats/mod_editSlaveCheatNew.tw
+++ b/src/cheats/mod_editSlaveCheatNew.tw
@@ -4257,15 +4257,175 @@
 
 	''Prestige:''
 	<<textbox "$tempSlave.prestige" $tempSlave.prestige>>
-	<<radiobutton "$tempSlave.prestige" 0>> 0
-	<<radiobutton "$tempSlave.prestige" 1>> 1
-	<<radiobutton "$tempSlave.prestige" 2>> 2
-	<<radiobutton "$tempSlave.prestige" 3>> 3
+	<<radiobutton "$tempSlave.prestige" 0>> @@.yellow;0@@
+	<<radiobutton "$tempSlave.prestige" 1>> @@.yellow;1@@
+	<<radiobutton "$tempSlave.prestige" 2>> @@.yellow;2@@
+	<<radiobutton "$tempSlave.prestige" 3>> @@.yellow;3@@
 
 	<br>
 	''Prestige description:''
 	<<textbox "$tempSlave.prestigeDesc" $tempSlave.prestigeDesc>>
 	<br>
+
+	<br>
+	'' Porn:''
+	<br>
+	''Porn Feed: ''
+	<<switch $tempSlave.porn.feed>>
+		<<case 0>>
+			''@@.yellow;No @@ | ''
+		<<case 1>>
+			''@@.yellow;Yes @@ | ''
+	<</switch>>
+	<<radiobutton "$tempSlave.porn.feed" 0>> @@.yellow;No@@
+	<<radiobutton "$tempSlave.porn.feed" 1>> @@.yellow;Yes@@
+	'' (is the studio broadcasting porn of her ?) ''
+	<br>
+	''Viewercount: ''
+	<<textbox "$tempSlave.porn.viewerCount" $tempSlave.porn.viewerCount>>
+	<br>
+	''Cash spending per week: ''
+	<<textbox "$tempSlave.porn.spending" $tempSlave.porn.spending>>
+	<br>
+	''Porn Prestige: ''
+	<<switch $tempSlave.porn.prestige>>
+		<<case 0>>
+			''@@.yellow;not @@ | ''
+		<<case 1>>
+			''@@.yellow;some @@ | ''
+		<<case 2>>
+			''@@.yellow;recognized @@ | ''
+		<<case 3>>
+			''@@.yellow;world-renowned @@ | ''
+	<</switch>>
+	<<radiobutton "$tempSlave.porn.prestige" 0>> @@.yellow;not@@
+	<<radiobutton "$tempSlave.porn.prestige" 1>> @@.yellow;some@@
+	<<radiobutton "$tempSlave.porn.prestige" 2>> @@.yellow;recognized@@
+	<<radiobutton "$tempSlave.porn.prestige" 3>> @@.yellow;world-renowned@@
+	<br>
+	<<link "Porn Fametype:">>
+		<<if (ndef _pfamR) || (_pfamR == 0) >>
+			<<replace "#famTypes">>
+				<<set _pfamR = 1>>
+				<br>
+				<<setVar "none" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "orgasm denial" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "cum addiction" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "anal addiction" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "exhibition" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "breast expansion" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "abuse" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "sexual torture" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "self hating" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "breeder" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "submissive" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "cum" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<br>
+				<<setVar "buttslut" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "humiliating" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "breast" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "dominant" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "sadistic" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "masochistic" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "pregnancy fetish" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "fuckdoll" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "rape" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "preggo" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "BBW" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "underage" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "weight gain" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "big dick" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<br>
+				<<setVar "generic" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "deepthroat" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "unwilling" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "hardcore anal" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "softcore" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "romantic" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "really perverted" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "voyeur" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "unspeakable" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+				<<setVar "huge insertion" "$tempSlave.porn.fameType" "_pfamR" "#famTypes">>
+			<</replace>>
+		<<else>>
+			<<replace "#famTypes">>
+				<<set _pfamR = 0>>
+			<</replace>>
+		<</if>>
+	<</link>>
+	<span id="famTypes"></span><br>
+	''Current Fametype: @@.yellow;$tempSlave.porn.fameType@@''
+	<br>
+	<<link "Porn Focus:">>
+		<<if (ndef _pfocusR) || (_pfocusR == 0) >>
+			<<replace "#pornFocus">>
+				<<set _pfocusR = 1>>
+				<br>
+				<<setVar "none" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "neglectful" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "cum addict" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "anal addict" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "attention whore" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "breast growth" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "abusive" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "malicious" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "self hating" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "breeder" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "submissive" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "cumslut" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "buttslut" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "humiliation" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "boobs" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "dom" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "sadist" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "masochist" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "pregnancy" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "fuckdoll" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "rape" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "preggo" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "BBW" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "loli" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "gainer" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "stud" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "porn" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "gagfuck queen" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "strugglefuck queen" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "painal queen" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "tease" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "romantic" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "perverted" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "caring" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "unflinching" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+				<<setVar "size queen" "$tempSlave.porn.focus" "_pfocusR" "#pornFocus">>
+			<</replace>>
+		<<else>>
+			<<replace "#pornFocus">>
+				<<set _pfocusR = 0>>
+			<</replace>>
+		<</if>>
+	<</link>>
+	<span id="pornFocus"></span><br>
+	''Current Focus: @@.yellow;$tempSlave.porn.focus@@''
+	<br>
+	<<link "Category fame values:">>
+		<<if (ndef _pcatR) || (_pcatR == 0) >>
+			<<replace "#pornFame">>
+				<<set _pcatR = 1>>
+				<br>
+				<<for _genre range App.Porn.getAllGenres()>>
+					<<capture _genre>>
+						<<print _genre.uiName()>> <br>
+						<<textbox "$tempSlave.porn.fame[_genre.fameVar]" $tempSlave.porn.fame[_genre.fameVar]>><br>
+					<</capture>>
+				<</for>>
+			<</replace>>
+		<<else>>
+			<<replace "#pornFame">>
+				<<set _pcatR = 0>>
+			<</replace>>
+		<</if>>
+	<</link>>
+	<span id="pornFame"></span><br>
 <</widget>>
 
 <<widget TattooTab>>
@@ -4640,7 +4800,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 1>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 			<<NameTab>>
 			<<FamilyTab>>
@@ -4684,7 +4844,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<NameTab>>
 	<</replace>>
@@ -4703,7 +4863,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<FamilyTab>>
 	<</replace>>
@@ -4722,7 +4882,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<NationalTab>>
 	<</replace>>
@@ -4741,7 +4901,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<AgeTab>>
 	<</replace>>
@@ -4760,7 +4920,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<DevoTab>>
 	<</replace>>
@@ -4779,7 +4939,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<FaceTab>>
 	<</replace>>
@@ -4798,7 +4958,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<HairTab>>
 	<</replace>>
@@ -4817,7 +4977,7 @@
 		<<set _descR = 0>><<set _eyesR = 1>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<EyesTab>>
 	<</replace>>
@@ -4836,7 +4996,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 1>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<MouthTab>>
 	<</replace>>
@@ -4855,7 +5015,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 1>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<BodyTab>>
 	<</replace>>
@@ -4874,7 +5034,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 1>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<BellyTab>>
 	<</replace>>
@@ -4893,7 +5053,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 1>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<BreastsTab>>
 	<</replace>>
@@ -4912,7 +5072,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 1>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<ButtTab>>
 	<</replace>>
@@ -4931,7 +5091,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 1>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<VagTab>>
 	<</replace>>
@@ -4950,7 +5110,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 1>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<PregTab>>
 	<</replace>>
@@ -4969,7 +5129,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 1>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<PenisTab>>
 	<</replace>>
@@ -4988,7 +5148,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 1>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<SkillsTab>>
 	<</replace>>
@@ -5007,7 +5167,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 1>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<InteliTab>>
 	<</replace>>
@@ -5026,7 +5186,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 1>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<FetSexTab>>
 	<</replace>>
@@ -5045,7 +5205,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 1>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<FlaQuiTab>>
 	<</replace>>
@@ -5064,7 +5224,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 1>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<GenQuiTab>>
 	<</replace>>
@@ -5083,7 +5243,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 1>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<StatisticTab>>
 	<</replace>>
@@ -5102,7 +5262,7 @@
 		<<set _descR = 1>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<br>
 		<<PrestTab>>
@@ -5122,7 +5282,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 1>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<TattooTab>>
 	<</replace>>
@@ -5141,7 +5301,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 0>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 1>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<BrandTab>>
 	<</replace>>
@@ -5160,7 +5320,7 @@
 		<<set _descR = 0>><<set _eyesR = 0>><<set _mouthR = 0>><<set _bodyR = 0>><<set _titsR = 0>><<set _buttR = 0>><<set _vagR = 0>><<set _pregR = 0>><<set _genqR = 0>>
 		<<set _dickR = 0>><<set _skillsR = 0>><<set _intR = 0>><<set _sexR = 0>><<set _flawR = 0>><<set _tatsR = 0>><<set _pierceR = 1>><<set _ecol3R = 0>>
 		<<set _sAllR = 0>><<set _natR = 0>><<set _carR = 0>><<set _bellyR = 0>><<set _useR = 0>><<set _brandR = 0>><<set _hcol5R = 0>><<set _hcol4R = 0>>
-		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>>
+		<<set _hcol3R = 0>><<set _hcol2R = 0>><<set _hcol1R = 0>><<set _ecol1RR = 0>><<set _ecol1RL = 0>><<set _ecol2R = 0>><<set _ppilRR = 0>><<set _ppilRL = 0>> <<set _pfocusR = 0>> <<set _pfamR = 0>> <<set _pcatR = 0>>
 		<<set _sclrRR = 0>><<set _sclrRL = 0>>
 		<<PierceTab>>
 	<</replace>>
diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index d44fb8d7f8317813b21a126c54a2bdf28afb5454..59b30d4611eb50cc959109be7cb7ef45f5d3874e 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -46,8 +46,7 @@ App.Update.setExistentProperties = function(obj, array) {
 			obj[p] = Array.from(array[p]);
 		} else if (typeof array[p] === "object" && typeof array[p] !== undefined && array[p] !== null) {
 			console.log("forcing V." + p + " to ", array[p]);
-			obj[p] = {};
-			Object.assign(obj[p], array[p]);
+			obj[p] = clone(array[p]);
 		} else {
 			obj[p] = array[p];
 		}
@@ -158,6 +157,9 @@ App.Update.globalVariables = function(node) {
 		if (jQuery.isEmptyObject(V.scarDesign)) {
 			V.scarDesign = {primary: "generic", local: "generic"};
 		}
+		if (V.releaseID <= 1110) {
+			V.researchLab.tasks = V.researchLab.tasks.filter((t) => (!(t.hasOwnProperty("slaveID")) || Object.keys(V.slaveIndices).includes(t.slaveID)));
+		}
 	}
 
 	// Reminders
@@ -978,39 +980,6 @@ App.Update.globalVariables = function(node) {
 			assistant.object();
 		}
 		App.Update.FCTV();
-		if (jQuery.isEmptyObject(V.FSPromenade)) {
-			V.FSPromenade = {
-				Subjugationist: 0,
-				Supremacist: 0,
-				GenderRadicalist: 0,
-				GenderFundamentalist: 0,
-				Paternalist: 0,
-				Degradationist: 0,
-				BodyPurist: 0,
-				TransformationFetishist: 0,
-				YouthPreferentialist: 0,
-				MaturityPreferentialist: 0,
-				SlimnessEnthusiast: 0,
-				AssetExpansionist: 0,
-				Pastoralist: 0,
-				PhysicalIdealist: 0,
-				ChattelReligionist: 0,
-				RomanRevivalist: 0,
-				NeoImperialist: 0,
-				AztecRevivalist: 0,
-				EgyptianRevivalist: 0,
-				EdoRevivalist: 0,
-				ArabianRevivalist: 0,
-				ChineseRevivalist: 0,
-				Repopulationist: 0,
-				Eugenics: 0,
-				Hedonism: 0,
-				IntellectualDependency: 0,
-				SlaveProfessionalism: 0,
-				PetiteAdmiration: 0,
-				StatuesqueGlorification: 0
-			};
-		}
 		if (jQuery.isEmptyObject(V.arcologyUpgrade)) {
 			V.arcologyUpgrade = {
 				drones: 0,
@@ -1143,6 +1112,11 @@ App.Update.globalVariables = function(node) {
 		V.completedOrgans = newOrgans;
 	}
 
+	// Slave death
+	if (!(V.slaveDeath instanceof Map)) {
+		V.slaveDeath = new Map();
+	}
+
 	FacilityDatatypeCleanup();
 
 	if (typeof V.TFS.compromiseWeek === "undefined") {
@@ -1234,17 +1208,17 @@ App.Update.globalVariables = function(node) {
 	if (!(typeof V.nicaea === 'object' && V.nicaea !== null)) { // Taking over the old V.nicaea which defaulted to 0 and was unused.
 		V.nicaea = {};
 	}
-	V.nicaea.announceable = V.nicaea.announceable || V.nicaea.announceable || 0;
-	V.nicaea.announced = V.nicaea.announced || V.nicaea.announced || 0;
-	V.nicaea.preparation = V.nicaea.preparation || V.nicaea.preparation|| 0;
-	V.nicaea.involvement = V.nicaea.involvement || V.nicaea.involvement|| -2;
-	V.nicaea.power = V.nicaea.power || V.nicaea.power || 0;
-	V.nicaea.held = V.nicaea.held || V.nicaea.held || 0;
-	V.nicaea.focus = V.nicaea.focus || V.nicaea.focus || "";
-	V.nicaea.assignment = V.nicaea.assignment || V.nicaea.assignment || "";
-	V.nicaea.achievement = V.nicaea.achievement || V.nicaea.achievement  || "";
-	V.nicaea.name = V.nicaea.name || V.nicaea.name || "";
-	V.nicaea.influence = V.nicaea.influence || V.nicaea.influence || 0;
+	V.nicaea.announceable = V.nicaea.announceable || V.nicaeaAnnounceable || 0;
+	V.nicaea.announced = V.nicaea.announced || V.nicaeaAnnounced || 0;
+	V.nicaea.preparation = V.nicaea.preparation || V.nicaeaPreparation|| 0;
+	V.nicaea.involvement = V.nicaea.involvement || V.nicaeaInvolvement|| -2;
+	V.nicaea.power = V.nicaea.power || V.nicaeaPower || 0;
+	V.nicaea.held = V.nicaea.held || V.nicaeaHeld || 0;
+	V.nicaea.focus = V.nicaea.focus || V.nicaeaFocus || "";
+	V.nicaea.assignment = V.nicaea.assignment || V.nicaeaAssignment || "";
+	V.nicaea.achievement = V.nicaea.achievement || V.nicaeaAchievement  || "";
+	V.nicaea.name = V.nicaea.name || V.nicaeaName || "";
+	V.nicaea.influence = V.nicaea.influence || V.nicaeaInfluence || 0;
 
 	EconomyDatatypeCleanup();
 	ArcologyDatatypeCleanup();
@@ -1317,8 +1291,9 @@ App.Update.globalVariables = function(node) {
 	V.boughtItem.toys.enema = V.boughtItem.toys.enema || V.enema || 0;
 	V.boughtItem.toys.medicalEnema = V.boughtItem.toys.medicalEnema || V.medicalEnema || 0;
 
-	V.FSPromenade.Hedonism = V.FSPromenade.Hedonism || V.FSPromenade.HedonisticDecadence || 0;
-	V.building.findCells(cell => cell instanceof App.Arcology.Cell.Shop && cell.type === "Hedonistic Decadence").forEach(cell => cell.type = "Hedonism");
+	V.building.findCells(cell => cell instanceof App.Arcology.Cell.Shop && (cell.type === "Hedonistic Decadence" || cell.type === "Hedonism")).forEach(cell => cell.type = "Hedonistic");
+	V.building.findCells(cell => cell instanceof App.Arcology.Cell.Shop && cell.type === "Repopulation Focus").forEach(cell => cell.type = "Repopulationist");
+	V.building.findCells(cell => cell instanceof App.Arcology.Cell.Shop && cell.type === "Neo Imperialist").forEach(cell => cell.type = "Neo-Imperialist");
 
 	node.append(`Done!`);
 };
@@ -1549,26 +1524,26 @@ App.Update.RAassistantData = function(node) {
 App.Update.arcologyLocation = function(node) {
 	if (V.continent === "Europe") {
 		const prompt = App.UI.DOM.appendNewElement('div', node);
-prompt.id = "location-prompt"; // so we can replace the whole prompt later after the user clicks a link
-V.continent = "Central Europe"; // picks a valid default right now in case the user doesn't interact
-const altLocations = [
-    "Southern Europe",
-    "Western Europe",
-    "Eastern Europe",
-	"Scandinavia",
-	"Central Europe"
-];
-function makeLinkForLocation(l) {
-    return App.UI.DOM.link(l, () => {
-        V.continent = l;
-        App.UI.DOM.replace("#location-prompt", `Arcology location specified at ${l}.`);
-    });
-};
-prompt.append(`General Arcology location detected: Europe. Please specify exact location of arcology. Currently selected: ${V.continent}. Other Possibilities: `,
-    App.UI.DOM.generateLinksStrip(altLocations.map(l => makeLinkForLocation(l))));
-		}
-	else {
-		node.append(`Done!`); }
+		prompt.id = "location-prompt"; // so we can replace the whole prompt later after the user clicks a link
+		V.continent = "Central Europe"; // picks a valid default right now in case the user doesn't interact
+		const altLocations = [
+			"Southern Europe",
+			"Western Europe",
+			"Eastern Europe",
+			"Scandinavia",
+			"Central Europe"
+		];
+		prompt.append(`General Arcology location detected: Europe. Please specify exact location of arcology. Currently selected: ${V.continent}. Other Possibilities: `,
+			App.UI.DOM.generateLinksStrip(altLocations.map(l => makeLinkForLocation(l))));
+	} else {
+		node.append(`Done!`);
+	}
+	function makeLinkForLocation(l) {
+		return App.UI.DOM.link(l, () => {
+			V.continent = l;
+			App.UI.DOM.replace("#location-prompt", `Arcology location specified at ${l}.`);
+		});
+	}
 };
 
 App.Update.oldVersions = function(node) {
diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js
index b748bfaf42ff7d5a0bcfa3e231e73a69fbbdfb1d..89659896077c38ff0ae7ff92c64300cd63c95a55 100644
--- a/src/data/backwardsCompatibility/datatypeCleanup.js
+++ b/src/data/backwardsCompatibility/datatypeCleanup.js
@@ -1135,8 +1135,18 @@ globalThis.SlaveDatatypeCleanup = (function SlaveDatatypeCleanup() {
 			slave.origBodyOwner = "";
 		}
 		slave.origBodyOwnerID = Math.max(+slave.origBodyOwnerID, 0) || 0;
-		if (typeof slave.death !== "string") {
-			slave.death = "";
+		if (slave.hasOwnProperty("death")) {
+			switch (slave.death) {
+				case "health":
+					planDeath(slave, "lowHealth");
+					break;
+				case "od":
+					planDeath(slave, "overdosed");
+					break;
+				case "old":
+					planDeath(slave, "oldAge");
+			}
+			delete slave.death;
 		}
 		if (slave.slaveCost !== 0) {
 			slave.slaveCost = Math.min(+slave.slaveCost, 1) || 1;
@@ -1693,7 +1703,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		V.arcade = Math.max(+V.arcade, 0) || 0;
 		V.arcadeUpgradeInjectors = Math.clamp(+V.arcadeUpgradeInjectors, 0, 1) || 0;
 		V.arcadeUpgradeCollectors = Math.clamp(+V.arcadeUpgradeCollectors, 0, 1.5) || 0;
-		V.arcadeUpgradeFuckdolls = Math.clamp(+V.arcadeUpgradeFuckdolls, 0, 2) || 0;
+		V.arcadeUpgradeFuckdolls = Math.clamp(+V.arcadeUpgradeFuckdolls, 0, 3) || 0;
 		V.arcadeUpgradeHealth = Math.clamp(+V.arcadeUpgradeHealth, -1, 2) || 0;
 	}
 
@@ -2469,3 +2479,60 @@ App.Update.neighborArcologyCheatDatatypeCleanup = function() {
 		V.arcologies[_i].rival = Number(V.arcologies[_i].rival) || 0;
 	}
 };
+
+App.Entity.Utils.PCCheatCleanup = function() {
+	V.PC.preg = Number(V.PC.preg) || 0;
+	V.PC.pregSource = Number(V.PC.pregSource) || 0;
+	V.PC.pregType = Number(V.PC.pregType) || 0;
+	WombInit(V.PC); // just to make sure
+	V.PC.womb.length = 0; // simple way to delete all fetuses
+	WombImpregnate(V.PC, V.PC.pregType, V.PC.pregSource, V.PC.preg);// recreates fetuses
+	if (V.PC.preg > 0) {
+		V.PC.belly = WombGetVolume(V.PC);
+		V.PC.pregWeek = V.PC.preg;
+	} else {
+		V.PC.belly = 0;
+		V.PC.pregWeek = 0;
+	}
+
+	if (V.PC.boobs < 300) {
+		V.PC.boobs = 100;
+		V.PC.boobsImplant = 0;
+	}
+	if (V.PC.boobsImplant > V.PC.boobs) {
+		V.PC.boobsImplant = V.PC.boobs;
+	}
+	if (V.PC.butt < 2) {
+		V.PC.butt = 2;
+		V.PC.buttImplant = 0;
+	}
+	if (V.PC.buttImplant > V.PC.butt) {
+		V.PC.buttImplant = V.PC.butt;
+	}
+	if (V.PC.dick === 0) {
+		V.PC.balls = 0;
+		V.PC.ballsImplant = 0;
+		V.PC.scrotum = 0;
+		V.PC.prostate = 0;
+	}
+	if (V.PC.ballsImplant > V.PC.balls) {
+		V.PC.ballsImplant = V.PC.balls;
+	}
+	if (V.PC.vagina === -1) {
+		V.PC.newVag = 0;
+		V.PC.ovaries = 0;
+		V.PC.vaginaLube = 0;
+	}
+	if (V.PC.lactation > 0 && V.PC.lactationDuration === 0) {
+		V.PC.lactationDuration = 2;
+	} else if (V.PC.lactation === 0 && V.PC.lactationDuration > 0) {
+		V.PC.lactationDuration = 0;
+	}
+
+	ibc.recalculate_coeff_id(-1);
+	PCDatatypeCleanup();
+	V.upgradeMultiplierArcology = upgradeMultiplier('engineering');
+	V.upgradeMultiplierMedicine = upgradeMultiplier('medicine');
+	V.upgradeMultiplierTrade = upgradeMultiplier('trading');
+	V.HackingSkillMultiplier = upgradeMultiplier('hacking');
+};
diff --git a/src/descriptions/familySummaries.js b/src/descriptions/familySummaries.js
index 9097ecfa48686fc3b93d292474ecd7e54d240fe6..b23198a38f2ba063cc7912d232f75d57ee26d489 100644
--- a/src/descriptions/familySummaries.js
+++ b/src/descriptions/familySummaries.js
@@ -527,7 +527,7 @@ App.Desc.family = (function() {
 				parents.push(V.missingTable[V.PC.father]);
 			}
 		}
-		parents.concat(V.slaves.filter((s) => isParentP(V.PC, s)));
+		parents = parents.concat(V.slaves.filter((s) => isParentP(V.PC, s)));
 
 		if (parents.length > 1) {
 			r.push(`<br>Your parents are <span class="lightgreen">${knownSlave(parents[0].ID)} and ${knownSlave(parents[1].ID)}.</span>`);
diff --git a/src/endWeek/death.js b/src/endWeek/death.js
new file mode 100644
index 0000000000000000000000000000000000000000..78cf2bfb8290c555cafd81559a07b48f30b0a7d6
--- /dev/null
+++ b/src/endWeek/death.js
@@ -0,0 +1,174 @@
+/**
+ *
+ * @param {App.Entity.SlaveState} slave
+ * @param {"oldAge"|"overdosed"|"lowHealth"} deathType
+ */
+globalThis.planDeath = function(slave, deathType) {
+	V.slaveDeath.set(slave.ID, deathType);
+};
+
+globalThis.allDeaths = function() {
+	const el = new DocumentFragment();
+
+	for (const [id, deathType] of V.slaveDeath) {
+		const deceased = getSlave(id);
+		if (deceased) {
+			App.UI.DOM.appendNewElement("p", el, death(deceased, deathType));
+			el.append(sectionBreak());
+			removeSlave(deceased);
+		}
+	}
+
+	V.slaveDeath = new Map();
+
+	return el;
+
+	function sectionBreak() {
+		const hr = document.createElement("hr");
+		hr.style.margin = "0";
+		return hr;
+	}
+};
+
+/**
+ *
+ * @param {App.Entity.SlaveState} slave
+ * @param {"oldAge"|"overdosed"|"lowHealth"} deathType
+ */
+globalThis.death = function(slave, deathType) {
+	const el = new DocumentFragment();
+	const r = [];
+	const {
+		He, His,
+		he, his
+	} = getPronouns(slave);
+	App.UI.DOM.appendNewElement("div", el, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
+
+	switch (deathType) {
+		case "oldAge": {
+			if (slave.assignment === "be confined in the arcade") {
+				r.push(`You are notified by ${V.arcadeName} staff that one of the cabinets has broken down and will need to be replaced. It would seem <span class="pink bold">${slave.slaveName},</span> the fucktoy encased in it, died`);
+				if (slave.physicalAge >= 70) {
+					r.push(`naturally of old age despite`);
+				} else {
+					r.push(`suddenly, unrelated to`);
+				}
+				r.push(`${his} living conditions. ${He} was a good unit; logs show ${he} was taking dick up until the very end.`);
+			} else if (slave.assignment === "work in the dairy" && V.dairyRestraintsSetting > 1) {
+				r.push(`You are notified by ${V.dairyName} staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem <span class="pink bold">${slave.slaveName},</span> the cow restrained in it, died`);
+				if (slave.physicalAge >= 70) {
+					r.push(`naturally of old age despite`);
+				} else {
+					r.push(`suddenly, unrelated to`);
+				}
+				r.push(`${his} living conditions. ${He} was a good cow; ${he} gave milk up until ${his} death.`);
+			} else if (slave.fuckdoll > 0) {
+				r.push(`One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem <span class="pink bold">${slave.slaveName}</span> has died`);
+				if (slave.physicalAge >= 70) {
+					r.push(`naturally of old age despite`);
+				} else {
+					r.push(`suddenly, unrelated to`);
+				}
+				r.push(`${his} living conditions. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally.`);
+			} else {
+				r.push(`<span class="pink bold">${slave.slaveName}</span> failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.`);
+				const deathSeed = random(1, 100);
+				if (deathSeed > 75) {
+					r.push(`${He} is found dead in ${his} bed, having died sometime during the previous night.`);
+				} else if (deathSeed > 50) {
+					r.push(`${He} is found dead in a stairwell, having had a heart attack while trying to climb it.`);
+				} else if (deathSeed > 25) {
+					r.push(`${He} is found dead in the showers, having slipped sometime earlier.`);
+				} else {
+					if (App.Utils.hasNonassignmentSex(slave)) {
+						r.push(`${He} is found dead in the bed of another slave, having died during intercourse. ${His} lover is not taking it well.`);
+					} else {
+						r.push(`${He} is found dead in the cafeteria, having died during breakfast; ${he} ruined the day for a number of your slaves.`);
+					}
+				}
+				r.push(`${slave.slaveName} died at the age of ${slave.actualAge};`);
+				if (slave.actualAge < 10) {
+					r.push(`${he} lived a tragically short life.`);
+				} else if (slave.actualAge < 20) {
+					r.push(`${he} died far too young.`);
+				} else if (slave.actualAge < 30) {
+					r.push(`${he} died in what would be a sex slave's prime.`);
+				} else if (slave.actualAge < 50) {
+					r.push(`${he} died in ${his} prime.`);
+				} else if (slave.actualAge < 65) {
+					r.push(`${he} lived a fair life, good or bad.`);
+				} else if (slave.actualAge < 90) {
+					r.push(`${he} lived a long life and experienced much during it.`);
+				} else {
+					r.push(`${he} lived a very long life that few get to see.`);
+				}
+			}
+			break;
+		}
+
+		case "overdosed": {
+			if (slave.assignment === "be confined in the arcade") {
+				r.push(`You are notified by ${V.arcadeName} staff that one of the cabinets has broken down and will need to be replaced. It would seem <span class="pink bold">${slave.slaveName},</span> the fucktoy encased in it, died of an aphrodisiac overdose from the constant aphrodisiac injections. ${He} was a good unit; logs show ${he} was taking dick up until the very end.`);
+			} else if (slave.assignment === "work in the dairy" && V.dairyRestraintsSetting > 1) {
+				r.push(`You are notified by ${V.dairyName} staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem <span class="pink bold">${slave.slaveName},</span> the cow restrained in it, died of an aphrodisiac overdose. How ${he} managed to get them is unknown, but ${he} was a good cow; ${he} gave milk up until ${his} death.`);
+			} else if (slave.fuckdoll > 0) {
+				r.push(`One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem <span class="pink bold">${slave.slaveName}</span> has died of an aphrodisiac overdose. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally. It does little to deal with the resulting mess of the orgasm ${he} died during, however.`);
+			} else {
+				r.push(`<span class="pink bold">${slave.slaveName}</span> failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.`);
+				r.push(`${He} is found dead in ${his} bed, having died sometime earlier. Judging by the mess and the expression on ${his} face, ${he} died of a heart attack during a particularly intense orgasm bought about by the massive amount of aphrodisiacs in ${his} system. ${slave.slaveName} died at the age of ${slave.actualAge};`);
+				if (slave.actualAge < 10) {
+					r.push(`${he} lived a tragically short life.`);
+				} else if (slave.actualAge < 20) {
+					r.push(`${he} died far too young.`);
+				} else if (slave.actualAge < 30) {
+					r.push(`${he} died in what would be a sex slave's prime.`);
+				} else if (slave.actualAge < 50) {
+					r.push(`${he} died in ${his} prime.`);
+				} else if (slave.actualAge < 65) {
+					r.push(`${he} lived a fair life, good or bad.`);
+				} else if (slave.actualAge < 90) {
+					r.push(`${he} lived a long life and experienced much during it.`);
+				} else {
+					r.push(`${he} lived a very long life that few get to see.`);
+				}
+			}
+			break;
+		}
+
+		case "lowHealth": {
+			if (slave.assignment === "be confined in the arcade") {
+				r.push(`You are notified by ${V.arcadeName} staff that one of the cabinets has broken down and will need to be replaced. It would seem <span class="pink bold">${slave.slaveName},</span> the fucktoy encased in it, died to poor health caused by ${his} living conditions. ${He} was a good unit; logs show ${he} was taking dick up until the very end.`);
+			} else if (slave.assignment === "work in the dairy" && V.dairyRestraintsSetting > 1) {
+				r.push(`You are notified by ${V.dairyName} staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem <span class="pink bold">${slave.slaveName},</span> the cow restrained in it, died to poor health caused by ${his} living conditions. ${He} was a good cow; ${he} gave milk up until ${his} death.`);
+			} else if (slave.fuckdoll > 0) {
+				r.push(`One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem <span class="pink bold">${slave.slaveName}</span> has died of general poor health. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally. Clean up is easy enough, however.`);
+			} else {
+				r.push(`<span class="pink bold">${slave.slaveName}</span> failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.`);
+				r.push(`${He} is found dead in ${his} bed, having died sometime during the night. ${He} has been in very poor health lately, so you knew this was a possibility. ${slave.slaveName} died at the age of ${slave.actualAge};`);
+				if (slave.actualAge < 10) {
+					r.push(`${he} lived a tragically short life.`);
+				} else if (slave.actualAge < 20) {
+					r.push(`${he} died far too young.`);
+				} else if (slave.actualAge < 30) {
+					r.push(`${he} died in what would be a sex slave's prime.`);
+				} else if (slave.actualAge < 50) {
+					r.push(`${he} died in ${his} prime.`);
+				} else if (slave.actualAge < 65) {
+					r.push(`${he} lived a fair life, good or bad.`);
+				} else if (slave.actualAge < 90) {
+					r.push(`${he} lived a long life and experienced much during it.`);
+				} else {
+					r.push(`${he} lived a very long life that few get to see.`);
+				}
+				if (V.arcologies[0].FSPaternalist !== "unset" && slave.actualAge < 75) {
+					r.push(`Allowing a slave to die under your care <span class="red">severely damages</span> your image as a caring slaveowner and <span class="red">calls into question</span> your paternalistic resolve.`);
+					FutureSocieties.Change("Paternalist", -10);
+				}
+			}
+			break;
+		}
+	}
+	App.Events.addNode(el, r);
+
+	return el;
+};
diff --git a/src/endWeek/economics/arcmgmt.js b/src/endWeek/economics/arcmgmt.js
index a9b96bbd2e89f5a7983fd9aa14ae1589191b6900..b90325bf7d02e81d38cf7547baba9056a611c8e5 100644
--- a/src/endWeek/economics/arcmgmt.js
+++ b/src/endWeek/economics/arcmgmt.js
@@ -1,5 +1,6 @@
 App.EndWeek.arcManagement = function() {
 	const el = new DocumentFragment();
+	const secExpImigrationBonus = App.SecExp.propagandaEffects("immigration");
 	let r;
 	let _enslaved;
 	let _crime;
@@ -116,20 +117,20 @@ App.EndWeek.arcManagement = function() {
 	_percUpperClass = Math.trunc((V.upperClass / (V.ACitizens + V.ASlaves)) * 1000) / 10;
 	_percTopClass = Math.trunc((V.topClass / (V.ACitizens + V.ASlaves)) * 1000) / 10;
 	if (V.cheatMode === 1 || V.debugMode === 1) {
-		appendDiv(`${V.arcologies[0].prosperity} Prosperity | ${_FSScore}  FS Score | ${_honeymoon} Honeymoon | ${_transportHub}  Transporthub | ${_terrain}  Terrain | ${_crime}  Crime`);
-		appendDiv(`${_LSCD} Lower + Slave Class Demand | ${_SCD}  Slave Class Demand | ${_slaveProductivity} Slave Productivity`);
+		appendDiv(`${V.arcologies[0].prosperity} Prosperity | ${_FSScore} FS Score | ${_honeymoon} Honeymoon | ${_transportHub} Transporthub | ${_terrain} Terrain | ${_crime} Crime`);
+		appendDiv(`${_LSCD} Lower + Slave Class Demand | ${_SCD} Slave Class Demand | ${_slaveProductivity} Slave Productivity`);
 		appendDiv(`${_LCD} Lower Class Demand | ${_lowerClassP} LC Multiplier`);
 		appendDiv(`${_MCD} Middle Class Demand | ${_middleClassP} MC Multiplier`);
-		appendDiv(`${_UCD} Upper Class Demand | ${_upperClassP}  UC Multiplier`);
+		appendDiv(`${_UCD} Upper Class Demand | ${_upperClassP} UC Multiplier`);
 		appendDiv(`${_TCD} Top Class Demand | ${_topClassP} TC Multiplier`);
 	}
 	appendDiv(`${V.arcologies[0].name} is home to the following:`);
-	appendDiv(`Citizens | ${V.ACitizens}  |  ${_percACitizens}%`);
-	appendDiv(`Lower Class Citizens | ${V.lowerClass}  |  ${_percLowerClass}%`);
-	appendDiv(`Middle Class Citizens | ${V.middleClass}  |  ${_percMiddleClass}%`);
-	appendDiv(`Upper Class Citizens | ${V.upperClass}  |  ${_percUpperClass}%`);
-	appendDiv(`Millionaires |  ${V.topClass} | ${_percTopClass}%`);
-	appendDiv(`Slaves | ${V.ASlaves}  |  ${_percASlaves}%`);
+	appendDiv(`Citizens | ${V.ACitizens} | ${_percACitizens}%`);
+	appendDiv(`Lower Class Citizens | ${V.lowerClass} | ${_percLowerClass}%`);
+	appendDiv(`Middle Class Citizens | ${V.middleClass} | ${_percMiddleClass}%`);
+	appendDiv(`Upper Class Citizens | ${V.upperClass} | ${_percUpperClass}%`);
+	appendDiv(`Millionaires | ${V.topClass} | ${_percTopClass}%`);
+	appendDiv(`Slaves | ${V.ASlaves} | ${_percASlaves}%`);
 
 	r = [];
 	if (V.arcologies[0].FSSupremacistLawME === 1) {
@@ -397,7 +398,7 @@ App.EndWeek.arcManagement = function() {
 		if (_menialEarnings + _bioreactorEarnings + _fuckdollsEarnings > 0) {
 			r.push(`earning you <span class="yellowgreen">${cashFormat(_menialEarnings + _bioreactorEarnings + _fuckdollsEarnings)}.</span>`);
 		} else {
-			r.push(`costing you <span class="red">${cashFormat(_menialEarnings + _bioreactorEarnings + _fuckdollsEarnings)}</span> on account of your free Fuckdoll policy.`);
+			r.push(`costing you <span class="cash dec">${cashFormat(_menialEarnings + _bioreactorEarnings + _fuckdollsEarnings)}</span> on account of your free Fuckdoll policy.`);
 		}
 		if (V.illegalDeals.menialDrug === 1) {
 			r.push(`Your menial slave productivity has been boosted by performance enhancing drugs.`);
@@ -559,12 +560,12 @@ App.EndWeek.arcManagement = function() {
 
 		let _desc = [];
 		let _descNeg = [];
-		for (let i = 1; i < V.arcologies.length; i++) {
-			const _opinion = App.Neighbor.opinion(0, i);
+		for (const arcology of V.arcologies) {
+			const _opinion = App.Neighbor.opinion(V.arcologies[0], arcology);
 			if (_opinion >= 100) {
-				_desc.push(V.arcologies[i].name);
+				_desc.push(arcology.name);
 			} else if (_opinion <= -100) {
-				_descNeg.push(V.arcologies[i].name);
+				_descNeg.push(arcology.name);
 			}
 		}
 		if (_desc.length > 0) {
@@ -625,15 +626,9 @@ App.EndWeek.arcManagement = function() {
 					delete V.SecExp.smilingMan.globalCrisisWeeks;
 				}
 			}
-			if (V.garrison.reactorTime > 0) {
-				r.push(`The damage to the reactor caused by the last rebellion is extensive. Businesses and private citizens struggle to operate with the unreliable and limited energy production offered by the auxiliary generators.`);
-				r.push(`It will still take`);
-				if (V.garrison.reactorTime > 1) {
-					r.push(`${V.garrison.reactorTime} weeks`);
-				} else {
-					r.push(`a week`);
-				}
-				r.push(`to finish repair works.`);
+			const reactorDamaged = V.garrison.reactorTime;
+			if (reactorDamaged > 0) {
+				r.push(`The damage to the reactor caused by the last rebellion is extensive. Businesses and private citizens struggle to operate with the unreliable and limited energy production offered by the auxiliary generators. It will still take ${numberWithPluralOne(reactorDamaged, 'week')} to finish repair works.`);
 				_AWeekGrowth -= random(1, 2);
 				V.garrison.reactorTime--;
 				IncreasePCSkills('engineering', 0.1);
@@ -1112,7 +1107,7 @@ App.EndWeek.arcManagement = function() {
 			_upperClass += -13.5;
 			_upperClassP *= 0.995;
 			_topClass += -5;
-			_topClass *= 0.99;
+			_topClassP *= 0.99;
 			r.push(`The rent promotion for new immigrants brings new citizens to the arcology.`);
 		}
 		if (V.policies.immigrationRep === 1) {
@@ -1123,7 +1118,7 @@ App.EndWeek.arcManagement = function() {
 			_upperClass += -13.5;
 			_upperClassP *= 0.995;
 			_topClass += -5;
-			_topClass *= 0.99;
+			_topClassP *= 0.99;
 			r.push(`Your welcome program for new citizens helps encourage wealthy people from the old world to immigrate, but <span class="red">annoys some longstanding citizens.</span>`);
 			repX(forceNeg(100), "policies");
 		}
@@ -1135,7 +1130,7 @@ App.EndWeek.arcManagement = function() {
 			_upperClass += 13.5;
 			_upperClassP *= 1.005;
 			_topClass += 5;
-			_topClass *= 1.01;
+			_topClassP *= 1.01;
 			r.push(`You covertly <span class="yellowgreen">sell</span> the private information of potential arcology immigrants on the old world black market.`);
 			cashX(random(500, 1500), "policies");
 		}
@@ -1147,7 +1142,7 @@ App.EndWeek.arcManagement = function() {
 			_upperClass += 13.5;
 			_upperClassP *= 1.005;
 			_topClass += 5;
-			_topClass *= 1.01;
+			_topClassP *= 1.01;
 			r.push(`You allow citizens input on potential immigrants, a <span class="green">popular</span> program.`);
 			repX(100, "policies");
 		}
@@ -1157,7 +1152,7 @@ App.EndWeek.arcManagement = function() {
 			_lowerClass += -200;
 			_lowerClassP *= .99;
 			_topClass += 5;
-			_topClass *= 1.01;
+			_topClassP *= 1.01;
 			r.push(`You <span class="yellowgreen">take kickbacks</span> for ignoring enslavement of citizens.`);
 			cashX(random(500, 1500), "policies");
 		}
@@ -1167,7 +1162,7 @@ App.EndWeek.arcManagement = function() {
 			_lowerClass += -200;
 			_lowerClassP *= 0.99;
 			_topClass += 5;
-			_topClass *= 1.01;
+			_topClassP *= 1.01;
 			r.push(`You <span class="green">make friends</span> by tacitly supporting enslavement of upstart citizens.`);
 			repX(100, "policies");
 		}
@@ -1177,7 +1172,7 @@ App.EndWeek.arcManagement = function() {
 			_lowerClass += 200;
 			_lowerClassP *= 1.02;
 			_topClass += -5;
-			_topClass *= 0.98;
+			_topClassP *= 0.98;
 			r.push(`Your charity purse prevents a few citizens from falling into slavery.`);
 		}
 		if (V.policies.enslavementRep === -1) {
@@ -1186,7 +1181,7 @@ App.EndWeek.arcManagement = function() {
 			_lowerClass += 200;
 			_lowerClassP *= 1.01;
 			_topClass += -5;
-			_topClass *= 0.99;
+			_topClassP *= 0.99;
 			r.push(`You use your personal influence to help struggling citizens.`);
 			repX(forceNeg(100), "policies");
 		}
@@ -1640,7 +1635,7 @@ App.EndWeek.arcManagement = function() {
 			}
 			r = [];
 			if (V.lowerClass < _LCD) {
-				let _LCImmigration = Math.trunc((_LCD - V.lowerClass) * (0.3 * _terrain)) + 1 + App.SecExp.propagandaEffects("immigration").effect;
+				let _LCImmigration = Math.trunc((_LCD - V.lowerClass) * (0.3 * _terrain)) + 1 + secExpImigrationBonus.effect;
 				if (V.arcologies[0].FSIntellectualDependencyLaw === 1) { /* Enslaving the dumb lower class immigrants*/
 					const _intellectualDependencyEnslaved = Math.trunc(_LCImmigration * 0.25);
 					_LCImmigration -= _intellectualDependencyEnslaved;
@@ -1705,7 +1700,7 @@ App.EndWeek.arcManagement = function() {
 				appendDiv(`Your middle class is <span class="green">sexually satiated</span> and their happiness attracts others.`);
 			}
 			if (V.middleClass < _MCD) {
-				let _MCImmigration = Math.trunc((_MCD - V.middleClass) * (0.3 * _terrain)) + 1 + App.SecExp.propagandaEffects("immigration").effect;
+				let _MCImmigration = Math.trunc((_MCD - V.middleClass) * (0.3 * _terrain)) + 1 + secExpImigrationBonus.effect;
 
 				V.middleClass += _MCImmigration;
 				if (_MCImmigration > 1) {
@@ -1744,7 +1739,7 @@ App.EndWeek.arcManagement = function() {
 				appendDiv(`Your upper class is <span class="green">sexually satiated</span> and their happiness attracts others.`);
 			}
 			if (V.upperClass < _UCD) {
-				let _UCImmigration = Math.trunc((_UCD - V.upperClass) * (0.3 * _terrain)) + 1 + App.SecExp.propagandaEffects("immigration").effect;
+				let _UCImmigration = Math.trunc((_UCD - V.upperClass) * (0.3 * _terrain)) + 1 + secExpImigrationBonus.effect;
 				V.upperClass += _UCImmigration;
 
 				if (_UCImmigration > 1) {
@@ -1789,7 +1784,7 @@ App.EndWeek.arcManagement = function() {
 				appendDiv(`Your millionaires are <span class="green">sexually satiated</span> and their happiness attracts others.`);
 			}
 			if (V.topClass < _TCD) {
-				let _TCImmigration = Math.trunc((_TCD - V.topClass) * (0.3 * _terrain)) + 1 + App.SecExp.propagandaEffects("immigration").effect;
+				let _TCImmigration = Math.trunc((_TCD - V.topClass) * (0.3 * _terrain)) + 1 + secExpImigrationBonus.effect;
 
 				V.topClass += _TCImmigration;
 				if (_TCImmigration > 1) {
@@ -1808,7 +1803,7 @@ App.EndWeek.arcManagement = function() {
 				}
 			}
 		}
-		appendDiv(App.SecExp.propagandaEffects("immigration").text);
+		appendDiv(secExpImigrationBonus.text);
 	}
 
 	function slaveRetirement() {
diff --git a/src/endWeek/economics/corporationDevelopments.js b/src/endWeek/economics/corporationDevelopments.js
index 032dc0d1072bbd60d2a4d530c55db75baedf2d1f..b181b6982518e30dddade72540039a6917a26e19 100644
--- a/src/endWeek/economics/corporationDevelopments.js
+++ b/src/endWeek/economics/corporationDevelopments.js
@@ -1,6 +1,6 @@
 App.EndWeek.corporationDevelopments = function() {
 	const el = document.createElement("p");
-	let r = [];
+	let r;
 	/* Main Corporation Pass*/
 
 	if (App.Corporate.cash < 0) {
@@ -13,27 +13,32 @@ App.EndWeek.corporationDevelopments = function() {
 	for (const i in _weekLedger.divisionLedgers) {
 		const _d = _weekLedger.divisionLedgers[i];
 		const _div = _d.division;
-		const r = [];
+		let r;
 		/* Reporting on number of slaves being processed or completed processing */
-		r.push(`${_div.name}: The division ${_div.message_endWeek_Slaves(_d)}`);
+		App.Events.addNode(el, [`${_div.name}: The division ${_div.message_endWeek_Slaves(_d)}`], "div");
+
+		r = [];
 		if (_d.market.originalBuy != null) {
 			if (_d.market.buy === 0) {
-				r.push(App.UI.DOM.makeElement("div", `It couldn't purchase ${numberWithPlural(_d.market.originalBuy, "slave")} to replenish its stock from the market because it couldn't afford to purchase price.`));
+				r.push(`It couldn't purchase ${numberWithPlural(_d.market.originalBuy, "slave")} to replenish its stock from the market because it couldn't afford to purchase price.`);
 			} else {
-				r.push(App.UI.DOM.makeElement("div", `It needed to replenish its slave stock of ${numberWithPlural(_d.market.originalBuy, "slave")}, but couldn't afford to buy all of them. It bought ${numberWithPlural(_d.market.buy, _div.nounSlaveFromMarket)} for ${cashFormatColor(_d.market.finalPurchase, true)}.`));
+				r.push(`It needed to replenish its slave stock of ${numberWithPlural(_d.market.originalBuy, "slave")}, but couldn't afford to buy all of them. It bought ${numberWithPlural(_d.market.buy, _div.nounSlaveFromMarket)} for ${cashFormatColor(_d.market.finalPurchase, true)}.`);
 			}
 		} else if (_d.market.buy > 0) {
-			r.push(App.UI.DOM.makeElement("div", `It replenished its slave stock and bought ${numberWithPlural(_d.market.buy, _div.nounSlaveFromMarket)} from the market for ${cashFormatColor(_d.market.finalPurchase, true)}.`));
+			r.push(`It replenished its slave stock and bought ${numberWithPlural(_d.market.buy, _div.nounSlaveFromMarket)} from the market for ${cashFormatColor(_d.market.finalPurchase, true)}.`);
 		}
+		App.Events.addNode(el, r, "div");
+
 		if (_d.transfer.total > 0) {
 			for (const i in _d.transfer.divisions) {
 				const _nextDivLedger = _d.transfer.divisions[i];
 				const _nextDiv = _nextDivLedger.division;
 				const _slavesToNext = _nextDivLedger.fill;
-				r.push(`It moved ${numberWithPlural(_slavesToNext, "slave")} to the ${_nextDiv.name} Division.`);
+				App.Events.addNode(el, [`It moved ${numberWithPlural(_slavesToNext, "slave")} to the ${_nextDiv.name} Division.`], "div");
 			}
 		}
 		if (_div.toMarket) {
+			r = [];
 			if (_div.heldSlaves === 0) {
 				if (_d.market.sell > 0) {
 					r.push(`It immediately sold ${numberWithPlural(_d.market.sell, _div.nounFinishedSlave)} to the market and made ${cashFormatColor(_d.market.finalSale)}.`);
@@ -46,13 +51,12 @@ App.EndWeek.corporationDevelopments = function() {
 					r.push(`week.`);
 				}
 			}
+			App.Events.addNode(el, r, "div");
 		}
 		if (_d.revenue.value > 0) {
-			r.push(`It earned ${cashFormatColor(_d.revenue.value)} in revenue.`);
+			App.Events.addNode(el, [`It earned ${cashFormatColor(_d.revenue.value)} in revenue.`], "div");
 		}
-		App.Events.addNode(el, r, "div");
 	}
-	App.Events.addNode(el, r);
 
 	/* Aggregate Corporation Results*/
 	el.append(App.Corporate.writeLedger(App.Corporate.ledger.current, V.week));
@@ -90,7 +94,7 @@ App.EndWeek.corporationDevelopments = function() {
 	}
 
 	if (_weekLedger.hasPayout) {
-		App.UI.DOM.appendNewElement("div", el, `This week the dividends were paid out, you received ${cashFormatColor(_weekLedger.payout)}.`);
+		App.Events.addNode(el, [`This week the dividends were paid out, you received ${cashFormatColor(_weekLedger.payout)}.`], "div");
 	}
 
 	/* Bankrupted the Corporation*/
diff --git a/src/endWeek/economics/fsDevelopments.js b/src/endWeek/economics/fsDevelopments.js
index 54bc362f3737f342e0f63e9453e24ff69d27903c..13c797d63ef0f7e8e4add798daa286f204d987fc 100644
--- a/src/endWeek/economics/fsDevelopments.js
+++ b/src/endWeek/economics/fsDevelopments.js
@@ -28,7 +28,7 @@ App.EndWeek.FSDevelopments = function() {
 	}
 
 	/* Count adopted FS */
-	let societies = FutureSocieties.activeCount(0);
+	const societies = FutureSocieties.activeCount(V.arcologies[0]);
 
 	/* Spending, terrain, rep effects */
 	let broadProgress = 0;
@@ -88,18 +88,18 @@ App.EndWeek.FSDevelopments = function() {
 	} else {
 		r.push(`The <span class="yellow">oceanic location</span> of the arcology almost eliminates cultural interchange, allowing ${V.arcologies[0].name} to independently develop its culture.`);
 	}
-	if (V.rep < 3000) {
+	if (V.rep < 3000 - V.enduringRep) {
 		r.push(`<span class="red">Your weak reputation</span> reflects badly on your social projects.`);
 		broadProgress -= 2;
-	} else if (V.rep < 6000) {
+	} else if (V.rep < 6000 - V.enduringRep) {
 		r.push(`<span class="red">Your mediocre reputation</span> engenders skepticism towards your social development.`);
 		broadProgress -= 1;
-	} else if (V.rep < 9000) {
+	} else if (V.rep < 9000 - V.enduringRep) {
 		r.push(`<span class="yellow">Your reputation</span> is neither weak enough or strong enough to affect social development.`);
-	} else if (V.rep < 12000) {
+	} else if (V.rep < 12000 - V.enduringRep) {
 		r.push(`<span class="green">Your strong reputation</span> helps support social development.`);
 		broadProgress += 1;
-	} else if (V.rep < 16000) {
+	} else if (V.rep < 16000 - V.enduringRep) {
 		r.push(`<span class="green">Your very strong reputation</span> increases acceptance of your social development.`);
 		broadProgress += 2;
 	} else {
@@ -207,7 +207,7 @@ App.EndWeek.FSDevelopments = function() {
 
 		if (V.SecExp.edicts.defense.pregExemption === 1) {
 			r.push(`Pregnant citizens are allowed and encouraged to avoid military service, making their value evident to all citizens.`);
-			FutureSocieties.Change("RepopulationFocus", 2);
+			FutureSocieties.Change("Repopulationist", 2);
 		}
 
 		if (V.SecExp.edicts.defense.eliteOfficers === 1) {
@@ -225,8 +225,7 @@ App.EndWeek.FSDevelopments = function() {
 	const cells = V.building.findCells(cell => cell instanceof App.Arcology.Cell.Shop && !["Brothel", "Club", "Shops"].includes(cell.type));
 	for (const cell of cells) {
 		r.push(`The ${cell.type} establishments on the Promenade help develop society.`);
-		const changedFS = cell.type.replace(/[- ]/g, "");
-		FutureSocieties.Change(changedFS, 4);
+		FutureSocieties.Change(cell.type, 4);
 	}
 
 	/* PA FS bonuses */
diff --git a/src/endWeek/economics/neighborsDevelopment.js b/src/endWeek/economics/neighborsDevelopment.js
index 899cb539ae480e300e8b6781dca74e6608ceee09..b34c0024e207edb7cdaf7128ea3c3da83630eedb 100644
--- a/src/endWeek/economics/neighborsDevelopment.js
+++ b/src/endWeek/economics/neighborsDevelopment.js
@@ -3,12 +3,10 @@
  */
 App.EndWeek.neighborsDevelopment = function() {
 	const el = document.createElement("p");
-	let He, his;
 
-	const averageProsperity = _.mean(V.arcologies.map(a => Math.min(a.prosperity, 10)));
+	const averageProsperity = _.mean(V.arcologies.map(a => a.prosperity));
 	const corpBonus = V.corp.Incorporated ? Math.trunc(1000 * Math.pow(App.Corporate.value, 0.1)) : 0;
 	let agentBonusValue = 0;
-	let desc;
 
 	if (V.useTabs === 0) {
 		App.UI.DOM.appendNewElement("h2", el, `Arcologies in the Free City`);
@@ -57,7 +55,7 @@ App.EndWeek.neighborsDevelopment = function() {
 		}
 		const agent = App.currentAgent(i);
 		if (arc.government === "your agent") {
-			({He} = getPronouns(agent));
+			const {He} = getPronouns(agent);
 			r.push(`is being run by your agent <span class="deeppink">${SlaveFullName(agent)}.</span>`);
 			if (agent && agent.assignment !== "be your agent") {
 				r.push(`<span class="red">BUG: ${agent} also was ${agent.assignment}!</span>`);
@@ -93,7 +91,7 @@ App.EndWeek.neighborsDevelopment = function() {
 			}
 			r.push(`<span class="yellow">After a brief power struggle, it undergoes a change of government.</span>`);
 			if (agent) {
-				({his} = getPronouns(agent));
+				const {his} = getPronouns(agent);
 				r.push(`<span class="deeppink">${agent.slaveName}</span> manages to escape with the help of a few loyal citizens and returns to you <span class="gold">fearing your displeasure at ${his} failure.</span>`);
 				agent.trust -= 40;
 				assignJob(agent, "rest"); // this takes care of necessary cleanup for agent and agent companion (if any)
@@ -201,13 +199,13 @@ App.EndWeek.neighborsDevelopment = function() {
 						r.push(`Its direct democracy votes to empower some elected officials in the hope they can lead the arcology out of its problems.`);
 						arc.government = "elected officials";
 				}
-				desc = FutureSocieties.decay(i).map((fs) => FutureSocieties.displayName(fs));
-				if (desc.length > 2) {
-					r.push(`Its citizens take the opportunity to make radical social changes, <span class="cyan">purging the ${arrayToSentence(desc)}</span> favored by the old government.`);
-				} else if (desc.length === 2) {
-					r.push(`Its citizens take the opportunity to make social changes, <span class="cyan">discarding the ${desc[0]} and ${desc[1]}</span> favored by the old government.`);
-				} else if (desc.length === 1) {
-					r.push(`Its citizens take the opportunity to make social change and <span class="cyan">abandon the ${desc[0]}</span> favored by the old government.`);
+				const decayedFSes = FutureSocieties.decay(i).map((fs) => FutureSocieties.displayName(fs));
+				if (decayedFSes.length > 2) {
+					r.push(`Its citizens take the opportunity to make radical social changes, <span class="cyan">purging the ${arrayToSentence(decayedFSes)}</span> favored by the old government.`);
+				} else if (decayedFSes.length === 2) {
+					r.push(`Its citizens take the opportunity to make social changes, <span class="cyan">discarding the ${decayedFSes[0]} and ${decayedFSes[1]}</span> favored by the old government.`);
+				} else if (decayedFSes.length === 1) {
+					r.push(`Its citizens take the opportunity to make social change and <span class="cyan">abandon the ${decayedFSes[0]}</span> favored by the old government.`);
 				}
 			} else {
 				r.push(`The arcology is paralyzed by internal dissension over how to respond.`);
@@ -418,7 +416,7 @@ App.EndWeek.neighborsDevelopment = function() {
 
 		/* FUTURE SOCIETY PROGRESS */
 
-		const societiesAdopted = FutureSocieties.activeCount(i);
+		const societiesAdopted = FutureSocieties.activeCount(arc);
 		let efficiency;
 		switch (arc.government) {
 			case "elected officials":
@@ -460,40 +458,40 @@ App.EndWeek.neighborsDevelopment = function() {
 						r.push(`Racial Supremacy for ${arc.FSSupremacistRace} people has reached stability and acceptance there. The arcology has been renamed`);
 						switch (arc.FSSupremacistRace) {
 							case "white":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistWhite);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistWhite);
 								break;
 							case "asian":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistAsian);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistAsian);
 								break;
 							case "latina":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistLatina);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistLatina);
 								break;
 							case "middle eastern":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistMiddleEastern);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistMiddleEastern);
 								break;
 							case "black":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistBlack);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistBlack);
 								break;
 							case "indo-aryan":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistIndoAryan);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistIndoAryan);
 								break;
 							case "pacific islander":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistPacificIslander);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistPacificIslander);
 								break;
 							case "malay":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistMalay);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistMalay);
 								break;
 							case "amerindian":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistAmerindian);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistAmerindian);
 								break;
 							case "southern european":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistSouthernEuropean);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistSouthernEuropean);
 								break;
 							case "semitic":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistSemitic);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistSemitic);
 								break;
 							default:
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSupremacistMixedRace);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SupremacistMixedRace);
 						}
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
@@ -521,40 +519,40 @@ App.EndWeek.neighborsDevelopment = function() {
 						r.push(`Racial Subjugationism of ${arc.FSSubjugationistRace} people has reached stability and acceptance there. The arcology has been renamed`);
 						switch (arc.FSSubjugationistRace) {
 							case "white":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistWhite);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistWhite);
 								break;
 							case "asian":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistAsian);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistAsian);
 								break;
 							case "latina":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistLatina);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistLatina);
 								break;
 							case "middle eastern":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistMiddleEastern);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistMiddleEastern);
 								break;
 							case "black":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistBlack);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistBlack);
 								break;
 							case "indo-aryan":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistIndoAryan);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistIndoAryan);
 								break;
 							case "pacific islander":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistPacificIslander);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistPacificIslander);
 								break;
 							case "malay":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistMalay);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistMalay);
 								break;
 							case "amerindian":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistAmerindian);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistAmerindian);
 								break;
 							case "southern european":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistSouthernEuropean);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistSouthernEuropean);
 								break;
 							case "semitic":
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistSemitic);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistSemitic);
 								break;
 							default:
-								arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSubjugationistMixedRace);
+								arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SubjugationistMixedRace);
 						}
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
@@ -580,7 +578,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSRepopulationFocus >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Repopulationism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesRepopulationist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Repopulationist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSRepopulationFocus < 0) {
@@ -612,7 +610,7 @@ App.EndWeek.neighborsDevelopment = function() {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSRestartResearch = 1;
 						r.push(`Eugenics has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesEugenics);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Eugenics);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSRestart < 0) {
@@ -646,7 +644,7 @@ App.EndWeek.neighborsDevelopment = function() {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSGenderRadicalistResearch = 1;
 						r.push(`Gender Radicalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesGenderRadicalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.GenderRadicalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSGenderRadicalist < 0) {
@@ -683,7 +681,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSGenderFundamentalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Gender Fundamentalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesGenderFundamentalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.GenderFundamentalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSGenderFundamentalist < 0) {
@@ -708,7 +706,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSPaternalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Paternalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesPaternalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Paternalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSPaternalist < 0) {
@@ -740,7 +738,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSDegradationist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Degradationism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesDegradationist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Degradationist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSDegradationist < 0) {
@@ -774,7 +772,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSIntellectualDependency >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Intellectual Dependency has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesIntellectualDependency);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.IntellectualDependency);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSIntellectualDependency < 0) {
@@ -801,7 +799,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSSlaveProfessionalism >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSSlaveProfessionalismResearch = 1;
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSlaveProfessionalism);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SlaveProfessionalism);
 						r.push(`Slave Professionalism has reached stability and acceptance there. The arcology has been renamed <span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSSlaveProfessionalism < 0) {
@@ -843,7 +841,7 @@ App.EndWeek.neighborsDevelopment = function() {
 			if (arc.direction !== 0) {
 				if (arc.FSBodyPurist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesBodyPurist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.BodyPurist);
 						r.push(`Body Purism has reached stability and acceptance there. The arcology has been renamed <span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSBodyPurist < 0) {
@@ -868,7 +866,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSTransformationFetishist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSTransformationFetishistResearch = 1;
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesTransformationFetishist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.TransformationFetishist);
 						r.push(`Transformation Fetishism has reached stability and acceptance there. The arcology has been renamed <span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSTransformationFetishist < 0) {
@@ -898,11 +896,11 @@ App.EndWeek.neighborsDevelopment = function() {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Youth Preferentialism has reached stability and acceptance there. The arcology has been renamed`);
 						if (V.pedo_mode === 1 || V.minimumSlaveAge < 6) {
-							arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesYouthPreferentialistLow);
+							arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.YouthPreferentialistLow);
 						} else if (V.minimumSlaveAge < 14) {
-							arc.name = App.Neighbor.getUnusedName(either(setup.ArcologyNamesYouthPreferentialist, setup.ArcologyNamesYouthPreferentialistLow));
+							arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.YouthPreferentialist.concat(App.Data.ArcologyNames.YouthPreferentialistLow));
 						} else {
-							arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesYouthPreferentialist);
+							arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.YouthPreferentialist);
 						}
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 						arc.FSYouthPreferentialistResearch = 1;
@@ -927,7 +925,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSMaturityPreferentialist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Maturity Preferentialism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesMaturityPreferentialist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.MaturityPreferentialist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSMaturityPreferentialist < 0) {
@@ -952,7 +950,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSPetiteAdmiration >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Petite Admiration has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesPetiteAdmiration);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.PetiteAdmiration);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSPetiteAdmiration < 0) {
@@ -979,7 +977,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSStatuesqueGlorification >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Statuesque Glorification has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesStatuesqueGlorification);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.StatuesqueGlorification);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSStatuesqueGlorification < 0) {
@@ -1008,7 +1006,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSSlimnessEnthusiast >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Slimness Enthusiasm has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesSlimnessEnthusiast);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.SlimnessEnthusiast);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 						arc.FSSlimnessEnthusiastResearch = 1;
 					}
@@ -1035,7 +1033,7 @@ App.EndWeek.neighborsDevelopment = function() {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSAssetExpansionistResearch = 1;
 						r.push(`Asset Expansionism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesAssetExpansionist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.AssetExpansionist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSAssetExpansionist < 0) {
@@ -1068,7 +1066,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSPastoralist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Pastoralism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesPastoralist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Pastoralist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSPastoralist < 0) {
@@ -1095,7 +1093,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSCummunism >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Cummunism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesCummunism);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.Cummunism);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSCummunism < 0) {
@@ -1128,7 +1126,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSPhysicalIdealist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Physical Idealism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesPhysicalIdealist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.PhysicalIdealist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSPhysicalIdealist < 0) {
@@ -1160,7 +1158,7 @@ App.EndWeek.neighborsDevelopment = function() {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						arc.FSHedonisticDecadenceResearch = 1;
 						r.push(`Decadent Hedonism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesHedonisticDecadence);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.HedonisticDecadence);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSHedonisticDecadence < 0) {
@@ -1189,7 +1187,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSIncestFetishist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Incest Fetishism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesIncestFetishist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.IncestFetishist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSIncestFetishist < 0) {
@@ -1214,7 +1212,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSChattelReligionist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Chattel Religionism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesChattelReligionist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.ChattelReligionist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSChattelReligionist < 0) {
@@ -1243,7 +1241,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSRomanRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Roman Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesRomanRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.RomanRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSRomanRevivalist < 0) {
@@ -1266,7 +1264,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSNeoImperialist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Neo-Imperialism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesNeoImperialist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.NeoImperialist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSNeoImperialist < 0) {
@@ -1289,7 +1287,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSAztecRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Aztec Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesAztecRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.AztecRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSAztecRevivalist < 0) {
@@ -1312,7 +1310,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSEgyptianRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Egyptian Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesEgyptianRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.EgyptianRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSEgyptianRevivalist < 0) {
@@ -1335,7 +1333,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSEdoRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Edo Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesEdoRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.EdoRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSEdoRevivalist < 0) {
@@ -1358,7 +1356,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSArabianRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Arabian Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesArabianRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.ArabianRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSArabianRevivalist < 0) {
@@ -1385,7 +1383,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.FSChineseRevivalist >= V.FSLockinLevel) {
 					if ((arc.name.indexOf("Arcology") !== -1) && (random(0, 2) === 0)) {
 						r.push(`Chinese Revivalism has reached stability and acceptance there. The arcology has been renamed`);
-						arc.name = App.Neighbor.getUnusedName(setup.ArcologyNamesChineseRevivalist);
+						arc.name = App.Neighbor.getUnusedName(App.Data.ArcologyNames.ChineseRevivalist);
 						r.push(`<span class="bold">${arc.name}</span> to mark the occasion.`);
 					}
 				} else if (arc.FSChineseRevivalist < 0) {
@@ -1435,483 +1433,54 @@ App.EndWeek.neighborsDevelopment = function() {
 					if (arc.ownership >= 100) {
 						appliedInfluenceBonus /= 2;
 					}
-					desc = [];
-					let alignment = 0;
 
-					if (arc2.FSSubjugationist !== "unset" && arc2.FSSubjugationist > 60) {
-						if (arc.FSSubjugationist !== "unset") {
-							if (arc2.FSSubjugationistRace === arc.FSSubjugationistRace) {
-								arc.FSSubjugationist += Math.trunc((arc2.FSSubjugationist - 60) / 4) + appliedInfluenceBonus;
-								if (arc.FSSubjugationist > V.FSLockinLevel) {
-									alignment += 1;
-								}
-								desc.push("helping to advance its racially aligned Subjugationism");
-							} else {
-								arc.FSSubjugationist -= Math.trunc((arc2.FSSubjugationist - 60) / 4) + appliedInfluenceBonus;
-								desc.push("attacking its incompatible Subjugationism");
-							}
-						} else if ((arc.FSSupremacist !== "unset") && (arc2.FSSubjugationistRace === arc.FSSupremacistRace)) {
-							arc.FSSupremacist -= Math.trunc((arc2.FSSubjugationist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its opposing Supremacism");
-						}
-					}
-					if (arc2.FSSupremacist !== "unset" && arc2.FSSupremacist > 60) {
-						if (arc.FSSupremacist !== "unset") {
-							if (arc2.FSSupremacistRace === arc.FSSupremacistRace) {
-								arc.FSSupremacist += Math.trunc((arc2.FSSupremacist - 60) / 4) + appliedInfluenceBonus;
-								if (arc.FSSupremacist > V.FSLockinLevel) {
-									alignment += 1;
-								}
-								desc.push("helping to advance its racially aligned Supremacism");
-							} else {
-								arc.FSSupremacist -= Math.trunc((arc2.FSSupremacist - 60) / 4) + appliedInfluenceBonus;
-								desc.push("attacking its incompatible Supremacism");
-							}
-						} else if ((arc.FSSubjugationist !== "unset") && (arc2.FSSupremacistRace === arc.FSSubjugationistRace)) {
-							arc.FSSubjugationist -= Math.trunc((arc2.FSSupremacist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its opposing Subjugationism");
-						}
-					}
-					if (arc2.FSRepopulationFocus !== "unset" && arc2.FSRepopulationFocus > 60) {
-						if (arc.FSRepopulationFocus !== "unset") {
-							arc.FSRepopulationFocus += Math.trunc((arc2.FSRepopulationFocus - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSRepopulationFocus > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Repopulationism");
-						} else if (arc.FSRestart !== "unset") {
-							arc.FSRestart -= Math.trunc((arc2.FSRepopulationFocus - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Eugenics");
-						}
-					} else if (arc2.FSRestart !== "unset" && arc2.FSRestart > 60) {
-						if (arc.FSRestart !== "unset") {
-							arc.FSRestart += Math.trunc((arc2.FSRestart - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSRestart > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Eugenics");
-						} else if (arc.FSRepopulationFocus !== "unset") {
-							arc.FSRepopulationFocus -= Math.trunc((arc2.FSRestart - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Repopulation Efforts");
-						}
-					}
-					if (arc2.FSGenderRadicalist !== "unset" && arc2.FSGenderRadicalist > 60) {
-						if (arc.FSGenderRadicalist !== "unset") {
-							arc.FSGenderRadicalist += Math.trunc((arc2.FSGenderRadicalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSGenderRadicalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Gender Radicalism");
-						} else if (arc.FSGenderFundamentalist !== "unset") {
-							arc.FSGenderFundamentalist -= Math.trunc((arc2.FSGenderRadicalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Gender Fundamentalism");
-						}
-					} else if (arc2.FSGenderFundamentalist !== "unset" && arc2.FSGenderFundamentalist > 60) {
-						if (arc.FSGenderFundamentalist !== "unset") {
-							arc.FSGenderFundamentalist += Math.trunc((arc2.FSGenderFundamentalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSGenderFundamentalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Gender Fundamentalism");
-						} else if (arc.FSGenderRadicalist !== "unset") {
-							arc.FSGenderRadicalist -= Math.trunc((arc2.FSGenderFundamentalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Gender Radicalism");
-						}
-					}
-					if (arc2.FSPaternalist !== "unset" && arc2.FSPaternalist > 60) {
-						if (arc.FSPaternalist !== "unset") {
-							arc.FSPaternalist += Math.trunc((arc2.FSPaternalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSPaternalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Paternalism");
-						} else if (arc.FSDegradationist !== "unset") {
-							arc.FSDegradationist -= Math.trunc((arc2.FSPaternalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Degradationism");
-						}
-					} else if (arc2.FSDegradationist !== "unset" && arc2.FSDegradationist > 60) {
-						if (arc.FSDegradationist !== "unset") {
-							arc.FSDegradationist += Math.trunc((arc2.FSDegradationist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSDegradationist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Degradationism");
-						} else if (arc.FSPaternalist !== "unset") {
-							arc.FSPaternalist -= Math.trunc((arc2.FSDegradationist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Paternalism");
-						}
-					}
-					if (arc2.FSIntellectualDependency !== "unset" && arc2.FSIntellectualDependency > 60) {
-						if (arc.FSIntellectualDependency !== "unset") {
-							arc.FSIntellectualDependency += Math.trunc((arc2.FSIntellectualDependency - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSIntellectualDependency > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Intellectual Dependency");
-						} else if (arc.FSSlaveProfessionalism !== "unset") {
-							arc.FSSlaveProfessionalism -= Math.trunc((arc2.FSIntellectualDependency - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Slave Professionalism");
-						}
-					} else if (arc2.FSSlaveProfessionalism !== "unset" && arc2.FSSlaveProfessionalism > 60) {
-						if (arc.FSSlaveProfessionalism !== "unset") {
-							arc.FSSlaveProfessionalism += Math.trunc((arc2.FSSlaveProfessionalism - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSSlaveProfessionalism > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Slave Professionalism");
-						} else if (arc.FSIntellectualDependency !== "unset") {
-							arc.FSIntellectualDependency -= Math.trunc((arc2.FSSlaveProfessionalism - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Intellectual Dependency");
-						}
-					}
-					if (arc2.FSBodyPurist !== "unset" && arc2.FSBodyPurist > 60) {
-						if (arc.FSBodyPurist !== "unset") {
-							arc.FSBodyPurist += Math.trunc((arc2.FSBodyPurist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSBodyPurist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Body Purism");
-						} else if (arc.FSTransformationFetishist !== "unset") {
-							arc.FSTransformationFetishist -= Math.trunc((arc2.FSBodyPurist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Transformation Fetishism");
-						}
-					} else if (arc2.FSTransformationFetishist !== "unset" && arc2.FSTransformationFetishist > 60) {
-						if (arc.FSTransformationFetishist !== "unset") {
-							arc.FSTransformationFetishist += Math.trunc((arc2.FSTransformationFetishist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSTransformationFetishist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Transformation Fetishism");
-						} else if (arc.FSBodyPurist !== "unset") {
-							arc.FSBodyPurist -= Math.trunc((arc2.FSTransformationFetishist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Body Purism");
-						}
-					}
-					if (arc2.FSYouthPreferentialist !== "unset" && arc2.FSYouthPreferentialist > 60) {
-						if (arc.FSYouthPreferentialist !== "unset") {
-							arc.FSYouthPreferentialist += Math.trunc((arc2.FSYouthPreferentialist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSYouthPreferentialist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Youth Preferentialism");
-						} else if (arc.FSMaturityPreferentialist !== "unset") {
-							arc.FSMaturityPreferentialist -= Math.trunc((arc2.FSYouthPreferentialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Maturity Preferentialism");
-						}
-					} else if (arc2.FSMaturityPreferentialist !== "unset" && arc2.FSMaturityPreferentialist > 60) {
-						if (arc.FSMaturityPreferentialist !== "unset") {
-							arc.FSMaturityPreferentialist += Math.trunc((arc2.FSMaturityPreferentialist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSMaturityPreferentialist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Maturity Preferentialism");
-						} else if (arc.FSYouthPreferentialist !== "unset") {
-							arc.FSYouthPreferentialist -= Math.trunc((arc2.FSMaturityPreferentialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Youth Preferentialism");
-						}
-					}
-					if (arc2.FSPetiteAdmiration !== "unset" && arc2.FSPetiteAdmiration > 60) {
-						if (arc.FSPetiteAdmiration !== "unset") {
-							arc.FSPetiteAdmiration += Math.trunc((arc2.FSPetiteAdmiration - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSPetiteAdmiration > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Petite Admiration");
-						} else if (arc.FSStatuesqueGlorification !== "unset") {
-							arc.FSStatuesqueGlorification -= Math.trunc((arc2.FSPetiteAdmiration - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Statuesque Glorification");
-						}
-					} else if (arc2.FSStatuesqueGlorification !== "unset" && arc2.FSStatuesqueGlorification > 60) {
-						if (arc.FSStatuesqueGlorification !== "unset") {
-							arc.FSStatuesqueGlorification += Math.trunc((arc2.FSStatuesqueGlorification - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSStatuesqueGlorification > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Statuesque Glorification");
-						} else if (arc.FSPetiteAdmiration !== "unset") {
-							arc.FSPetiteAdmiration -= Math.trunc((arc2.FSStatuesqueGlorification - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Petite Admiration");
-						}
-					}
-					if (arc2.FSSlimnessEnthusiast !== "unset" && arc2.FSSlimnessEnthusiast > 60) {
-						if (arc.FSSlimnessEnthusiast !== "unset") {
-							arc.FSSlimnessEnthusiast += Math.trunc((arc2.FSSlimnessEnthusiast - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSSlimnessEnthusiast > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Slimness Enthusiasm");
-						} else if (arc.FSAssetExpansionist !== "unset") {
-							arc.FSAssetExpansionist -= Math.trunc((arc2.FSSlimnessEnthusiast - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Asset Expansionism");
-						}
-					} else if (arc2.FSAssetExpansionist !== "unset" && arc2.FSAssetExpansionist > 60) {
-						if (arc.FSAssetExpansionist !== "unset") {
-							arc.FSAssetExpansionist += Math.trunc((arc2.FSAssetExpansionist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSAssetExpansionist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Asset Expansionism");
-						} else if (arc.FSSlimnessEnthusiast !== "unset") {
-							arc.FSSlimnessEnthusiast -= Math.trunc((arc2.FSAssetExpansionist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Slimness Enthusiasm");
-						}
-					}
-					if (arc2.FSPastoralist !== "unset" && arc2.FSPastoralist > 60) {
-						if (arc.FSPastoralist !== "unset") {
-							arc.FSPastoralist += Math.trunc((arc2.FSPastoralist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSPastoralist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Pastoralism");
-						} else if (arc.FSCummunism !== "unset") {
-							arc.FSCummunism -= Math.trunc((arc2.FSPastoralist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Cummunism");
-						}
-					} else if (arc2.FSCummunism !== "unset" && arc2.FSCummunism > 60) {
-						if (arc.FSCummunism !== "unset") {
-							arc.FSCummunism += Math.trunc((arc2.FSCummunism - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSCummunism > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Cummunism");
-						} else if (arc.FSPastoralist !== "unset") {
-							arc.FSPastoralist -= Math.trunc((arc2.FSCummunism - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Pastoralism");
-						}
-					}
-					if (arc2.FSPhysicalIdealist !== "unset" && arc2.FSPhysicalIdealist > 60) {
-						if (arc.FSPhysicalIdealist !== "unset") {
-							arc.FSPhysicalIdealist += Math.trunc((arc2.FSPhysicalIdealist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSPhysicalIdealist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Physical Idealism");
-						} else if (arc.FSHedonisticDecadence !== "unset") {
-							arc.FSHedonisticDecadence -= Math.trunc((arc2.FSPhysicalIdealist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Hedonism");
-						}
-					} else if (arc2.FSHedonisticDecadence !== "unset" && arc2.FSHedonisticDecadence > 60) {
-						if (arc.FSHedonisticDecadence !== "unset") {
-							arc.FSHedonisticDecadence += Math.trunc((arc2.FSHedonisticDecadence - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSHedonisticDecadence > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Hedonism");
-						} else if (arc.FSPhysicalIdealist !== "unset") {
-							arc.FSPhysicalIdealist -= Math.trunc((arc2.FSHedonisticDecadence - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its Physical Idealism");
-						}
-					}
-					if (arc2.FSIncestFetishist !== "unset" && arc2.FSIncestFetishist > 60) {
-						if (arc.FSIncestFetishist !== "unset") {
-							arc.FSIncestFetishist += Math.trunc((arc2.FSIncestFetishist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSIncestFetishist > V.FSLockinLevel) {
+					let alignment = 0;
+					let helping = [], attacking = [];
+					const dipFSes = FutureSocieties.diplomaticFSes(arc, arc2);
+					for (const sharedFS of dipFSes.shared) {
+						if (arc2[sharedFS] > 60) {
+							arc[sharedFS] += Math.trunc((arc2[sharedFS] - 60) / 4) + appliedInfluenceBonus;
+							if (arc[sharedFS] > V.FSLockinLevel) {
 								alignment += 1;
 							}
-							desc.push("helping to advance its Incest Fetishism");
-						}
-					}
-					if (arc2.FSChattelReligionist !== "unset" && arc2.FSChattelReligionist > 60) {
-						if (arc.FSChattelReligionist !== "unset") {
-							arc.FSChattelReligionist += Math.trunc((arc2.FSChattelReligionist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSChattelReligionist > V.FSLockinLevel) {
-								alignment += 1;
+							if (sharedFS === "FSSubjugationist") {
+								helping.push("racially aligned Subjugationism");
+							} else if (sharedFS === "FSSupremacist") {
+								helping.push("racially aligned Supremacism");
+							} else {
+								helping.push(FutureSocieties.displayName(sharedFS));
 							}
-							desc.push("helping to advance its Chattel Religionism");
 						}
 					}
-					if (arc2.FSRomanRevivalist !== "unset" && arc2.FSRomanRevivalist > 60) {
-						if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist += Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSRomanRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Roman Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSRomanRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSAztecRevivalist !== "unset" && arc2.FSAztecRevivalist > 60) {
-						if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist += Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSAztecRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Aztec Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSAztecRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSEgyptianRevivalist !== "unset" && arc2.FSEgyptianRevivalist > 60) {
-						if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist += Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSEgyptianRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Egyptian Revivalism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSEgyptianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSEdoRevivalist !== "unset" && arc2.FSEdoRevivalist > 60) {
-						if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist += Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSEdoRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Edo Revivalism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSEdoRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSArabianRevivalist !== "unset" && arc2.FSArabianRevivalist > 60) {
-						if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist += Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSArabianRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Arabian Revivalism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSArabianRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSChineseRevivalist !== "unset" && arc2.FSChineseRevivalist > 60) {
-						if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist += Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSChineseRevivalist > V.FSLockinLevel) {
-								alignment += 1;
-							}
-							desc.push("helping to advance its Chinese Revivalism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Imperialism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSChineseRevivalist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						}
-					} else if (arc2.FSNeoImperialist !== "unset" && arc2.FSNeoImperialist > 60) {
-						if (arc.FSNeoImperialist !== "unset") {
-							arc.FSNeoImperialist += Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							if (arc.FSNeoImperialist > V.FSLockinLevel) {
-								alignment += 1;
+					for (const [arcFS, arc2FS] of dipFSes.conflicting) {
+						if (arc2[arc2FS] > 60) {
+							arc[arcFS] -= Math.trunc((arc2[arc2FS] - 60) / 4) + appliedInfluenceBonus;
+							if (arcFS === "FSSubjugationist" && arc2FS === "FSSupremacist") {
+								attacking.push("opposing Subjugationism");
+							} else if (arcFS === "FSSupremacist" && arc2FS === "FSSubjugationist") {
+								attacking.push("opposing Supremacism");
+							} else if (arcFS === "FSSubjugationist" && arc2FS === "FSSubjugationist") {
+								attacking.push("incompatible Subjugationism");
+							} else if (arcFS === "FSSupremacist" && arc2FS === "FSSupremacist") {
+								attacking.push("incompatible Supremacism");
+							} else if (arcFS.includes("Revivalism")) {
+								attacking.push("incompatible Revivalism");
+							} else if (arcFS.includes("Imperialism")) {
+								attacking.push("incompatible Imperialism");
+							} else {
+								attacking.push(FutureSocieties.displayName(arcFS));
 							}
-							desc.push("helping to advance its Neo-Imperialism");
-						} else if (arc.FSEgyptianRevivalist !== "unset") {
-							arc.FSEgyptianRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSChineseRevivalist !== "unset") {
-							arc.FSChineseRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSEdoRevivalist !== "unset") {
-							arc.FSEdoRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSArabianRevivalist !== "unset") {
-							arc.FSArabianRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSRomanRevivalist !== "unset") {
-							arc.FSRomanRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
-						} else if (arc.FSAztecRevivalist !== "unset") {
-							arc.FSAztecRevivalist -= Math.trunc((arc2.FSNeoImperialist - 60) / 4) + appliedInfluenceBonus;
-							desc.push("attacking its incompatible Revivalism");
 						}
 					}
 
-					if (desc.length === 0) {
+					if (helping.length === 0 && attacking.length === 0) {
 						r.push(`<span class="bold">${arc2.name}</span> attempts to influence it, but has no significant impacts.`);
-					} else if (desc.length > 2) {
-						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, ${desc.slice(0, desc.length - 1).join(', ') + ", and " + desc.slice(-1)}.`);
-					} else if (desc.length === 2) {
-						r.push(`<span class="bold">${arc2.name}</span>'s culture influences ${arc.name}'s ${desc[0]} and ${desc[1]}.`);
+					} else if (helping.length === 0) {
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, attacking its ${arrayToSentence(attacking)}.`);
+					} else if (attacking.length === 0) {
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${arrayToSentence(helping)}.`);
 					} else {
-						r.push(`<span class="bold">${arc2.name}</span>'s culture is beginning to influence ${arc.name}'s ${desc[0]}.`);
+						r.push(`<span class="bold">${arc2.name}</span>'s mature culture influences ${arc.name}, helping to advance its ${arrayToSentence(helping)}, while attacking its ${arrayToSentence(attacking)}.`);
 					}
 
 					if (appliedInfluenceBonus > 0) {
@@ -1929,7 +1498,7 @@ App.EndWeek.neighborsDevelopment = function() {
 					}
 
 					if (arc2.direction !== 0) {
-						if (desc.length === 0) {
+						if (helping.length === 0 && attacking.length === 0) {
 							r.push(`<span class="bold">${arc2.name}</span> is not satisfied with the impact its directed influence is having, and withdraws it with the intention of targeting it elsewhere.`);
 							arc2.influenceTarget = -1;
 						} else if (alignment >= 4) {
@@ -2067,7 +1636,7 @@ App.EndWeek.neighborsDevelopment = function() {
 				if (arc.government === "an individual") {
 					if (V.rivalryFSAdopted === 0) {
 						V.rivalryFSAdopted = 1;
-						desc = "Its owner is";
+						let desc = "Its owner is";
 						switch (V.rivalryFS) {
 							case "Racial Subjugationism":
 								if (arc.FSSubjugationist !== "unset") {
@@ -2376,7 +1945,7 @@ App.EndWeek.neighborsDevelopment = function() {
 								V.rivalryFSAdopted = 0;
 						}
 					} else { // RIVAL ADOPTION
-						desc = "Its owner is";
+						let desc = "Its owner is";
 						if (V.arcologies[0].FSSubjugationist > random(5, 60)) {
 							if (validFSes.includes("FSSupremacist") && (arc.FSSubjugationist === "unset") || (arc.FSSubjugationistRace !== V.arcologies[0].FSSubjugationistRace)) {
 								r.push(`${desc} preoccupied by belief in the superiority of the ${V.arcologies[0].FSSubjugationistRace} race, leading the arcology to <span class="yellow">adopt ${V.arcologies[0].FSSubjugationistRace} Supremacy.</span>`);
@@ -2567,41 +2136,41 @@ App.EndWeek.neighborsDevelopment = function() {
 			/* AGENT ADOPTION*/
 			if (arc.government === "your agent") {
 				const leader = App.currentAgent(i);
-				const {he, himself, woman, him, hers} = getPronouns(leader);
+				const {he, his, himself, woman, him, hers} = getPronouns(leader);
 
 				if (validFSes.includes("FSRepopulationFocus") && leader.fetish === "pregnancy" && V.seePreg === 1) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Repopulationism,</span> since as a pregnancy fetishist, ${he} can't wait to see the female population's bellies swell with life.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Repopulationism,</span> since as a pregnancy fetishist, ${he} can't wait to see the female population's bellies swell with life.`);
 					arc.FSRepopulationFocus = 5;
 					return;
 				} else if (validFSes.includes("FSRestart") && (leader.preg < -1 || (leader.ovaries === 0 && leader.mpreg !== 1)) && leader.genes === "XX") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Eugenics,</span> since if no-one can get pregnant, ${he} won't be alone.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Eugenics,</span> since if no-one can get pregnant, ${he} won't be alone.`);
 					arc.FSRestart = 5;
 					return;
 				}
 				if (validFSes.includes("FSGenderRadicalist") && leader.dick > 0) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Gender Radicalism,</span> since ${he}'s a walking, swinging argument for dickgirls.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Gender Radicalism,</span> since ${he}'s a walking, swinging argument for dickgirls.`);
 					arc.FSGenderRadicalist = 5;
 					return;
-				} else if (validFSes.includes("FSGenderFundamentalist") && leader.pregKnown === 1 || leader.bellyPreg > 1500) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Gender Fundamentalism,</span> since its citizens find leadership by a pregnant ${woman} fascinating.`);
+				} else if (validFSes.includes("FSGenderFundamentalist") && (leader.pregKnown === 1 || leader.bellyPreg > 1500)) {
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Gender Fundamentalism,</span> since its citizens find leadership by a pregnant ${woman} fascinating.`);
 					arc.FSGenderFundamentalist = 5;
 					return;
 				}
 				if (validFSes.includes("FSPaternalist") && leader.behavioralQuirk === "advocate") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Paternalism,</span> since as an advocate for slavery, ${he} believes in its benefits.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Paternalism,</span> since as an advocate for slavery, ${he} believes in its benefits.`);
 					arc.FSPaternalist = 5;
 					return;
 				} else if (validFSes.includes("FSDegradationist") && leader.fetish === "sadist") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Degradationism,</span> since as a sexual sadist, ${he}'s excited by the idea of leading a society that applauds ${his} cruelest impulses.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Degradationism,</span> since as a sexual sadist, ${he}'s excited by the idea of leading a society that applauds ${his} cruelest impulses.`);
 					arc.FSDegradationist = 5;
 					return;
 				}
 				if (validFSes.includes("FSSlaveProfessionalism") && (leader.intelligence + leader.intelligenceImplant >= 120) && (leader.skill.vaginal + leader.skill.oral + leader.skill.anal + leader.skill.whoring + leader.skill.entertainment >= 400)) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Slave Professionalism,</span> since ${he} wishes to produce slaves you can be proud of.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Slave Professionalism,</span> since ${he} wishes to produce slaves you can be proud of.`);
 					arc.FSSlaveProfessionalism = 5;
 					return;
 				} else if (validFSes.includes("FSIntellectualDependency") && (leader.intelligence + leader.intelligenceImplant >= 120) && (leader.behavioralFlaw === "arrogant" || leader.behavioralQuirk === "insecure")) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Intellectual Dependency,</span>`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Intellectual Dependency,</span>`);
 					if (leader.behavioralQuirk === "insecure") {
 						r.push(`since, due to ${his} own insecurities, needs to be frequently reassured that ${he} is smarter than the masses.`);
 					} else {
@@ -2611,125 +2180,125 @@ App.EndWeek.neighborsDevelopment = function() {
 					return;
 				}
 				if (validFSes.includes("FSBodyPurist") && leader.chem > 50) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Body Purism,</span> since ${he} knows what long term drug damage feels like, and doesn't want any slave to ever experience it again.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Body Purism,</span> since ${he} knows what long term drug damage feels like, and doesn't want any slave to ever experience it again.`);
 					arc.FSBodyPurist = 5;
 					return;
 				} else if (validFSes.includes("FSTransformationFetishist") && leader.boobsImplant > 1000) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Transformation Fetishism,</span> out of a perverse desire to subject all slaves to massive implants like ${hers}.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Transformation Fetishism,</span> out of a perverse desire to subject all slaves to massive implants like ${hers}.`);
 					arc.FSTransformationFetishist = 5;
 					return;
 				}
 				if (validFSes.includes("FSYouthPreferentialist") && leader.actualAge <= 25) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Youth Preferentialism,</span> to buttress acceptance of ${his} own young age.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Youth Preferentialism,</span> to buttress acceptance of ${his} own young age.`);
 					arc.FSYouthPreferentialist = 5;
 					return;
 				} else if (validFSes.includes("FSMaturityPreferentialist") && leader.actualAge > 35) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Maturity Preferentialism,</span> since ${he} has a certain personal interest in promoting the idea that MILFs are sexy.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Maturity Preferentialism,</span> since ${he} has a certain personal interest in promoting the idea that MILFs are sexy.`);
 					arc.FSMaturityPreferentialist = 5;
 					return;
 				}
 				if (validFSes.includes("FSSlimnessEnthusiast") && leader.behavioralQuirk === "insecure") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Slimness Enthusiasm,</span> since ${his} history of anorexia has deeply impacted ${his} idea of beauty.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Slimness Enthusiasm,</span> since ${his} history of anorexia has deeply impacted ${his} idea of beauty.`);
 					arc.FSSlimnessEnthusiast = 5;
 					return;
 				} else if (validFSes.includes("FSAssetExpansionist") && leader.fetish === "boobs") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Asset Expansionism,</span> since ${he}'s a breast expansion fetishist in addition to being a mere breast fetishist.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Asset Expansionism,</span> since ${he}'s a breast expansion fetishist in addition to being a mere breast fetishist.`);
 					arc.FSAssetExpansionist = 5;
 					return;
 				} else if (validFSes.includes("FSAssetExpansionist") && leader.sexualQuirk === "size queen" && leader.vagina > 3) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Asset Expansionism,</span> since ${he}'s a stickler for big dicks and seeks to find one large enough to push ${him} to ${his} very limit.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Asset Expansionism,</span> since ${he}'s a stickler for big dicks and seeks to find one large enough to push ${him} to ${his} very limit.`);
 					arc.FSAssetExpansionist = 5;
 					return;
 				}
 				if (validFSes.includes("FSCummunism") && leader.fetish === "cumslut") { // this will become the cum focused condition, being replaced with breast focus for milk
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Cummunism,</span> since ${he} already loves sucking down huge loads of cum.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Cummunism,</span> since ${he} already loves sucking down huge loads of cum.`);
 					arc.FSCummunism = 5;
 					return;
 				} else if (validFSes.includes("FSPastoralist") && leader.fetish === "boobs") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Pastoralism,</span> since ${he} loves boobs and adores suckling them.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Pastoralism,</span> since ${he} loves boobs and adores suckling them.`);
 					arc.FSPastoralist = 5;
 					return;
 				}
 				if (validFSes.includes("FSHedonisticDecadence") && leader.behavioralFlaw === "gluttonous") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Decadent Hedonism,</span> since ${he} already loves over-eating.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Decadent Hedonism,</span> since ${he} already loves over-eating.`);
 					arc.FSHedonisticDecadence = 5;
 					return;
 				} else if (validFSes.includes("FSPhysicalIdealist") && leader.behavioralQuirk === "fitness") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Physical Idealism,</span> since ${he}'s a fitness fanatic ${himself}.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Physical Idealism,</span> since ${he}'s a fitness fanatic ${himself}.`);
 					arc.FSPhysicalIdealist = 5;
 					return;
 				} else if (validFSes.includes("FSHedonisticDecadence") && leader.fetish !== "none" && leader.fetishStrength >= 100) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Decadent Hedonism,</span> since ${he} seeks to satisfy ${his} powerful fetish.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Decadent Hedonism,</span> since ${he} seeks to satisfy ${his} powerful fetish.`);
 					arc.FSHedonisticDecadence = 5;
 					return;
 				}
 				if (validFSes.includes("FSStatuesqueGlorification") && leader.height >= 200) {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Statuesque Glorification,</span> since ${he} is tired of being one of the tallest in arcology.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Statuesque Glorification,</span> since ${he} is tired of being one of the tallest in arcology.`);
 					arc.FSStatuesqueGlorification = 5;
 					return;
 				} else if (validFSes.includes("FSPetiteAdmiration") && leader.height >= 170 && leader.fetish === "dom") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Petite Admiration,</span> since it is far easier to dominate someone much smaller than oneself.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Petite Admiration,</span> since it is far easier to dominate someone much smaller than oneself.`);
 					arc.FSPetiteAdmiration = 5;
 					return;
 				}
 				if (validFSes.includes("FSIncestFetishist")) {
 					const lover = V.slaves.find(s => (s.ID === leader.relationshipTarget && areRelated(s, leader) && s.assignment === "live with your agent"));
 					if ((leader.behavioralQuirk === "sinful" || leader.sexualQuirk === "perverted") && lover && V.seeIncest === 1) {
-						r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Incest Festishism,</span> to share the love and joy ${he} holds with ${his} ${relativeTerm(leader, lover)}.`);
+						r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Incest Festishism,</span> to share the love and joy ${he} holds with ${his} ${relativeTerm(leader, lover)}.`);
 						arc.FSIncestFetishist = 5;
 						return;
 					}
 				}
 				if (validFSes.includes("FSChattelReligionist") && leader.behavioralFlaw === "devout") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Chattel Religionism,</span> to share and spread ${his} deeply held beliefs about the holiness of sexual service.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Chattel Religionism,</span> to share and spread ${his} deeply held beliefs about the holiness of sexual service.`);
 					arc.FSChattelReligionist = 5;
 					return;
 				} else if (validFSes.includes("FSChattelReligionist") && leader.behavioralQuirk === "sinful") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Chattel Religionism,</span> since ${he}'s excited by the prospect of getting away with horrible sins against old religions in public.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Chattel Religionism,</span> since ${he}'s excited by the prospect of getting away with horrible sins against old religions in public.`);
 					arc.FSChattelReligionist = 5;
 					return;
 				}
 				if (validFSes.includes("FSEgyptianRevivalist") && leader.relationshipTarget !== 0) {
 					const lover = getSlave(leader.relationshipTarget);
 					if (lover && areRelated(leader, lover)) {
-						r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Egyptian Revivalism,</span> since ${he}'s already part of a gloriously incestuous relationship.`);
+						r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Egyptian Revivalism,</span> since ${he}'s already part of a gloriously incestuous relationship.`);
 						arc.FSEgyptianRevivalist = 5;
 						return;
 					}
 				} else if (validFSes.includes("FSChineseRevivalist") && leader.nationality === "Chinese") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Chinese Revivalism,</span> since ${he}'s Chinese ${himself} and can claim high honor in such a society.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Chinese Revivalism,</span> since ${he}'s Chinese ${himself} and can claim high honor in such a society.`);
 					arc.FSChineseRevivalist = 5;
 					return;
 				} else if (validFSes.includes("FSEdoRevivalist") && leader.nationality === "Japanese") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Edo Revivalism,</span> since ${he}'s Japanese ${himself} and can claim high honor in such a society.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Edo Revivalism,</span> since ${he}'s Japanese ${himself} and can claim high honor in such a society.`);
 					arc.FSEdoRevivalist = 5;
 					return;
 				} else if (validFSes.includes("FSAztecRevivalist") && leader.nationality === "Mexican") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Aztec Revivalism,</span> since ${he}'s Mexican ${himself} and can claim high honor in such a society.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Aztec Revivalism,</span> since ${he}'s Mexican ${himself} and can claim high honor in such a society.`);
 					arc.FSAztecRevivalist = 5;
 					return;
 				} else if (validFSes.includes("FSNeoImperialist") && leader.nationality === "German") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s German ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s German ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
 					arc.FSNeoImperialist = 5;
 					return;
 				} else if (validFSes.includes("FSNeoImperialist") && leader.nationality === "French") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s French ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s French ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
 					arc.FSNeoImperialist = 5;
 					return;
 				} else if (validFSes.includes("FSNeoImperialist") && leader.nationality === "Spanish") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s Spanish ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s Spanish ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
 					arc.FSNeoImperialist = 5;
 					return;
 				} else if (validFSes.includes("FSNeoImperialist") && leader.nationality === "English") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s English ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Neo-Imperialism,</span> since ${he}'s English ${himself} and can easily cement ${his} rule with Imperial directives in your name.`);
 					arc.FSNeoImperialist = 5;
 					return;
 				} else if (validFSes.includes("FSRomanRevivalist") && leader.behavioralQuirk === "confident") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Roman Revivalism,</span> since it appeals to ${his} confident, patrician nature.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Roman Revivalism,</span> since it appeals to ${his} confident, patrician nature.`);
 					arc.FSRomanRevivalist = 5;
 					return;
 				} else if (validFSes.includes("FSArabianRevivalist") && leader.fetish === "dom") {
-					r.push(`Your agent <span class="pink">leader.slaveName</span> successfully pushes it to <span class="yellow">adopt Arabian Revivalism,</span> since ${he}'s sexually dominant and quite likes the idea of overseeing slave bazaars.`);
+					r.push(`Your agent <span class="pink">${leader.slaveName}</span> successfully pushes it to <span class="yellow">adopt Arabian Revivalism,</span> since ${he}'s sexually dominant and quite likes the idea of overseeing slave bazaars.`);
 					arc.FSArabianRevivalist = 5;
 					return;
 				}
@@ -3141,10 +2710,8 @@ App.EndWeek.neighborsDevelopment = function() {
 				}
 			}
 
-
 			/* NEIGHBOR ADOPTION*/
-			for (let j = 0; j < V.arcologies.length; j++) {
-				const arc2 = V.arcologies[j];
+			for (const arc2 of V.arcologies) {
 				if (arc.direction !== arc2.direction) {
 					let influenceBonus = 0;
 					if (arc.direction === arc2.influenceTarget) {
@@ -3152,7 +2719,7 @@ App.EndWeek.neighborsDevelopment = function() {
 						influenceBonus = 20;
 					}
 
-					const opinion = App.Neighbor.opinion(i, j);
+					const opinion = App.Neighbor.opinion(arc, arc2);
 					if (opinion >= 50) {
 						r.push(`${arc.name} is aligned with ${arc2.name} socially, encouraging it to consider adopting all its cultural values.`);
 						influenceBonus += opinion - 50;
@@ -3193,6 +2760,7 @@ App.EndWeek.neighborsDevelopment = function() {
 
 			/* RANDOM ADOPTION*/
 			if (random(0, 4) === 1) {
+				let desc;
 				switch (arc.government) {
 					case "elected officials":
 						desc = "Its elected leaders are";
@@ -3216,11 +2784,9 @@ App.EndWeek.neighborsDevelopment = function() {
 					default:
 						desc = "Its citizens are";
 				}
-				let subjugationRace;
-				let supremacistRace;
 				switch (validFSes.random()) {
-					case "FSSubjugationist":
-						subjugationRace = setup.filterRacesLowercase.random();
+					case "FSSubjugationist": {
+						const subjugationRace = setup.filterRacesLowercase.random();
 						if ((arc.FSSupremacist === "unset") || (subjugationRace !== arc.FSSupremacistRace)) {
 							r.push(`${desc} preoccupied by a racial animus towards ${subjugationRace} people, leading the arcology to <span class="yellow">adopt ${subjugationRace} Subjugation.</span>`);
 							arc.FSSubjugationist = 5;
@@ -3228,8 +2794,9 @@ App.EndWeek.neighborsDevelopment = function() {
 							return;
 						}
 						break;
-					case "FSSupremacist":
-						supremacistRace = setup.filterRacesLowercase.random();
+					}
+					case "FSSupremacist": {
+						const supremacistRace = setup.filterRacesLowercase.random();
 						if ((arc.FSSubjugationist === "unset") || (supremacistRace !== arc.FSSubjugationistRace)) {
 							r.push(`${desc} preoccupied by belief in the superiority of the ${supremacistRace} race, leading the arcology to <span class="yellow">adopt ${supremacistRace} Supremacy.</span>`);
 							arc.FSSupremacist = 5;
@@ -3237,6 +2804,7 @@ App.EndWeek.neighborsDevelopment = function() {
 							return;
 						}
 						break;
+					}
 					case "FSGenderRadicalist":
 						r.push(`${desc} enthusiastic about fucking slaves in the butt, leading the arcology to <span class="yellow">adopt Gender Radicalism.</span>`);
 						arc.FSGenderRadicalist = 5;
diff --git a/src/endWeek/economics/persBusiness.js b/src/endWeek/economics/persBusiness.js
index e5c9d3876afd7cbd9d67cc461c41ae6aaa809056..84bddcd21596872ce25ae034383c3074a85933d1 100644
--- a/src/endWeek/economics/persBusiness.js
+++ b/src/endWeek/economics/persBusiness.js
@@ -22,7 +22,7 @@ App.EndWeek.personalBusiness = function() {
 		r.push(`<span class="red">You are in debt.</span> This week, interest came to ${cashFormat(interest)}.`);
 		if (V.arcologies[0].FSRomanRevivalist !== "unset") {
 			r.push(`Society <span class="red">very strongly disapproves</span> of your being in debt; this damages the idea that you model yourself on what a Roman leader should be.`);
-			FutureSocieties.Change("RomanRevivalist", -10);
+			FutureSocieties.Change("Roman Revivalist", -10);
 		}
 		if (V.cash < 0 && V.cash > -25000 && V.arcologies[0].FSRestartDecoration === 100) {
 			if (V.eugenicsFullControl !== 1) {
@@ -485,7 +485,7 @@ App.EndWeek.personalBusiness = function() {
 			} else {
 				r.push(`responsible Roman lady.`);
 			}
-			FutureSocieties.Change("RomanRevivalist", 2);
+			FutureSocieties.Change("Roman Revivalist", 2);
 		}
 	} else if (V.cash > 1000) {
 		income = Math.trunc(Math.min(3000 * Math.log(V.cash), V.cash * 0.07));
@@ -838,10 +838,14 @@ App.EndWeek.personalBusiness = function() {
 	if (V.rep <= 18000) {
 		if (V.policies.regularParties === 0) {
 			if (V.rep > 3000) {
-				r.push(`Your <span class="red">reputation is damaged</span> by your not hosting regular social events for your leading citizens.`);
+				r.push(`Your <span class="red">reputation is damaged</span> by your not `);
+				r.push(App.UI.DOM.disabledLink("hosting regular social events", ["Regular Social Events is a domestic policy costing ¤5000 a week that you can adopt."]));
+				r.push(`for your leading citizens.`);
 				repX(-50, "personalBusiness");
 			} else {
-				r.push(`Though you are not hosting regular social events for your leading citizens, your lack of renown prevents this from damaging your reputation; they don't expect someone so relatively unknown to be throwing parties.`);
+				r.push(`Though you are not `);
+				r.push(App.UI.DOM.disabledLink("hosting regular social events", ["Regular Social Events is a domestic policy costing ¤5000 a week that you can adopt."]));
+				r.push(` for your leading citizens, your lack of renown prevents this from damaging your reputation; they don't expect someone so relatively unknown to be throwing parties.`);
 			}
 		}
 	}
@@ -878,14 +882,9 @@ App.EndWeek.personalBusiness = function() {
 			cashX(blackMarket, "personalBusiness");
 		}
 
-		if (V.arcRepairTime > 0) {
-			r.push(`The recent rebellion left the arcology wounded and it falls to its owner to fix it. It will still take`);
-			if (V.arcRepairTime > 1) {
-				r.push(`${V.arcRepairTime} weeks`);
-			} else {
-				r.push(`a week`);
-			}
-			r.push(`to finish repair works.`);
+		const arcDamaged = V.arcRepairTime;
+		if (arcDamaged > 0) {
+			r.push(`The recent rebellion left the arcology wounded and it falls to its owner to fix it. It will still take ${numberWithPluralOne(arcDamaged, 'week')} to finish repair works.`);
 			cashX(-5000, "personalBusiness");
 			V.arcRepairTime--;
 			IncreasePCSkills('engineering', 0.1);
diff --git a/src/endWeek/economics/personalNotes.js b/src/endWeek/economics/personalNotes.js
index 0618688c401df95e1ac7c7032b285846399db795..3a9cfbff16870944e742b4750e00cde2675145c9 100644
--- a/src/endWeek/economics/personalNotes.js
+++ b/src/endWeek/economics/personalNotes.js
@@ -259,7 +259,7 @@ App.EndWeek.personalNotes = function() {
 						}
 					}
 					if (V.PC.boobShape !== "saggy" && V.PC.preg > random(V.PC.pregData.normalBirth / 1.25, V.PC.pregData.normalBirth * 2.5) && (V.PC.breastMesh !== 1) && (V.PC.drugs !== "sag-B-gone")) {
-						r.push(`Your <span class="orange">breasts become saggy</span> in the last stages of  pregnancy as your body undergoes changes in anticipation of the forthcoming birth.`);
+						r.push(`Your <span class="orange">breasts become saggy</span> in the last stages of pregnancy as your body undergoes changes in anticipation of the forthcoming birth.`);
 						V.PC.boobShape = "saggy";
 					}
 				}
diff --git a/src/endWeek/economics/reputation.js b/src/endWeek/economics/reputation.js
index c7c614a887cce4e82f93e8b2fac989fa7cb60a22..1a31805abc4d44f9416b54ea7e5e075f539bb94a 100644
--- a/src/endWeek/economics/reputation.js
+++ b/src/endWeek/economics/reputation.js
@@ -16,10 +16,10 @@ App.EndWeek.reputation = function() {
 			if (V.nicaea.achievement === "slaves") {
 				if (V.slaves.length > 50) {
 					r.push(`<span class="green">strongly approve</span> of the large`);
-					FutureSocieties.Change("ChattelReligionist", 5);
+					FutureSocieties.Change("Chattel Religionist", 5);
 				} else if (V.slaves.length > 20) {
 					r.push(`<span class="green">approve</span> of the good`);
-					FutureSocieties.Change("ChattelReligionist", 2);
+					FutureSocieties.Change("Chattel Religionist", 2);
 				} else {
 					r.push(`are not impressed by the`);
 				}
@@ -27,10 +27,10 @@ App.EndWeek.reputation = function() {
 			} else if (V.nicaea.achievement === "devotion") {
 				if (V.averageDevotion > 80) {
 					r.push(`<span class="green">strongly approve</span> of the worshipfulness`);
-					FutureSocieties.Change("ChattelReligionist", 5);
+					FutureSocieties.Change("Chattel Religionist", 5);
 				} else if (V.averageDevotion > 50) {
 					r.push(`<span class="green">approve</span> of the devotion`);
-					FutureSocieties.Change("ChattelReligionist", 2);
+					FutureSocieties.Change("Chattel Religionist", 2);
 				} else {
 					r.push(`are not impressed by the devotion`);
 				}
@@ -38,10 +38,10 @@ App.EndWeek.reputation = function() {
 			} else {
 				if (V.averageTrust > 50) {
 					r.push(`<span class="green">strongly approve</span> of the great trust your slaves place in you.`);
-					FutureSocieties.Change("ChattelReligionist", 5);
+					FutureSocieties.Change("Chattel Religionist", 5);
 				} else if (V.averageTrust > 20) {
 					r.push(`<span class="green">approve</span> of the trust your slaves place in you.`);
-					FutureSocieties.Change("ChattelReligionist", 2);
+					FutureSocieties.Change("Chattel Religionist", 2);
 				} else {
 					r.push(`are not impressed by the fear many of your slaves feel towards you.`);
 				}
@@ -163,15 +163,16 @@ App.EndWeek.reputation = function() {
 		repX(-100, "architecture");
 	}
 
+	_enduringRep = V.enduringRep;
 	if (V.arcologies[0].FSRestartDecoration === 100) {
 		r.push(`As a member of the Societal Elite, your appearance has no bearing on your reputation.`);
 	} else {
 		if (V.PC.dick === 0 && V.PC.boobs >= 300 && V.PC.title === 0) {
-			if (V.rep > 18000) {
+			if (V.rep > 18000 - _enduringRep) {
 				r.push(`Your reputation is so well-established that society has accepted your notoriously feminine appearance despite how unusual it is for a prominent slaveowner to look like you do.`);
 				if (V.arcologies[0].FSGenderRadicalist > 30) {
 					r.push(`Indeed, society sees you as entirely male, since you are powerful, and <span class="green">strongly approves</span> of your nonconformity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				} else if (V.arcologies[0].FSGenderFundamentalist > 30) {
 					r.push(`Indeed, society has been reconciled to female leadership, preferring to see you as a mother figure.`);
 				}
@@ -179,22 +180,22 @@ App.EndWeek.reputation = function() {
 				r.push(`Society accepts you as an arcology owner, since it has become open-minded about power and gender.`);
 				if (V.arcologies[0].FSGenderRadicalist > 50) {
 					r.push(`Indeed, society sees you as fundamentally male, since you are powerful, and <span class="green">strongly approves</span> of your audacity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				}
 			} else {
 				r.push(`Most prominent slaveowners are male, and your obviously feminine appearance makes it <span class="red">harder for you to maintain your reputation.</span>`);
 				repX(forceNeg(Math.min((V.rep * 0.05), 500)), "PCappearance");
 				if (V.arcologies[0].FSGenderFundamentalist > 10) {
 					r.push(`Society <span class="red">strongly resents</span> your being an arcology owner; this damages the idea that women should not be in positions of responsibility.`);
-					FutureSocieties.Change("GenderFundamentalist", -5);
+					FutureSocieties.Change("Gender Fundamentalist", -5);
 				}
 			}
 		} else if ((V.PC.boobs >= 300) || V.PC.title === 0) {
-			if (V.rep > 15000) {
+			if (V.rep > 15000 - _enduringRep) {
 				r.push(`Your reputation is so strong that society has accepted your feminine appearance despite how unusual it is for a prominent slaveowner to look like you do.`);
 				if (V.arcologies[0].FSGenderRadicalist > 30) {
 					r.push(`Indeed, society sees you as entirely male, since you are powerful, and <span class="green">strongly approves</span> of your nonconformity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				} else if (V.arcologies[0].FSGenderFundamentalist > 30) {
 					r.push(`Indeed, society has been reconciled to your feminine appearance, seeing you as a person apart.`);
 				}
@@ -202,22 +203,22 @@ App.EndWeek.reputation = function() {
 				r.push(`Society accepts you as an arcology owner, since it has become open-minded anyone who has a cock and fucks.`);
 				if (V.arcologies[0].FSGenderRadicalist > 30) {
 					r.push(`Indeed, society sees you as dominant, since you fuck bitches, and <span class="green">strongly approves</span> of your nonconformity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				}
 			} else {
 				r.push(`Most prominent slaveowners are very masculine, and your feminine appearance makes it <span class="red">harder for you to maintain your reputation.</span>`);
 				repX(forceNeg(Math.min((V.rep * 0.025), 250)), "PCappearance");
 				if (V.arcologies[0].FSGenderFundamentalist > 30) {
 					r.push(`Society <span class="red">strongly resents</span> your being an arcology owner; this damages the idea that feminine people should not be in positions of responsibility.`);
-					FutureSocieties.Change("GenderFundamentalist", -5);
+					FutureSocieties.Change("Gender Fundamentalist", -5);
 				}
 			}
 		} else if ((V.PC.dick === 0) || (V.PC.vagina !== -1)) {
-			if (V.rep > 15000) {
+			if (V.rep > 15000 - _enduringRep) {
 				r.push(`Your reputation is so strong that society has accepted your unorthodox arrangement downstairs, for an arcology owner.`);
 				if (V.arcologies[0].FSGenderRadicalist > 30) {
 					r.push(`Indeed, society sees you as entirely male, since you are powerful, and <span class="green">strongly approves</span> of your nonconformity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				} else if (V.arcologies[0].FSGenderFundamentalist > 30) {
 					r.push(`Indeed, society has been reconciled to your strangeness, seeing you as a person apart.`);
 				}
@@ -225,14 +226,14 @@ App.EndWeek.reputation = function() {
 				r.push(`Society accepts you as an arcology owner, since it has become open-minded about the exact genital layout of powerful people.`);
 				if (V.arcologies[0].FSGenderRadicalist > 30) {
 					r.push(`Indeed, society sees you as dominant, since you are powerful, and <span class="green">strongly approves</span> of your nonconformity; this advances the redefinition of gender around power.`);
-					FutureSocieties.Change("GenderRadicalist", 5);
+					FutureSocieties.Change("Gender Radicalist", 5);
 				}
 			} else {
 				r.push(`Most prominent slaveowners are very masculine, and though your unorthodox arrangement downstairs isn't obvious when you're clothed, the rumors are unavoidable and it's <span class="red">harder for you to maintain your reputation.</span>`);
 				repX(forceNeg(Math.min((V.rep * 0.025), 250)), "PCappearance");
 				if (V.arcologies[0].FSGenderFundamentalist > 30) {
 					r.push(`Society <span class="red">strongly resents</span> your being an arcology owner; this damages the idea that people who are not men should not be in positions of responsibility.`);
-					FutureSocieties.Change("GenderFundamentalist", -5);
+					FutureSocieties.Change("Gender Fundamentalist", -5);
 				}
 			}
 		}
@@ -244,7 +245,7 @@ App.EndWeek.reputation = function() {
 		/* already handled above */
 	} else if (V.arcologies[0].FSIntellectualDependency !== "unset") {
 		if (V.PC.intelligence + V.PC.intelligenceImplant < -10) {
-			if (V.rep > 18000) {
+			if (V.rep > 18000 - _enduringRep) {
 				r.push(`You've somehow built such a reputation for yourself that your lack of a brain is no longer a societal concern.`);
 			} else {
 				repX(forceNeg(Math.min((V.rep * 0.025), 100)), "PCappearance");
@@ -253,23 +254,23 @@ App.EndWeek.reputation = function() {
 		}
 	} else if (V.arcologies[0].FSSlaveProfessionalism !== "unset") {
 		if (V.PC.intelligence + V.PC.intelligenceImplant < 100) {
-			if (V.rep > 18000) {
+			if (V.rep > 18000 - _enduringRep) {
 				r.push(`You've built such a reputation for yourself that you not being a genius is no longer a societal concern.`);
 			} else {
 				repX(forceNeg(Math.min((V.rep * 0.05), 750)), "PCappearance");
 				r.push(`Society <span class="red">strongly despises</span> being led by someone so easily outsmarted by even the slave population.`);
-				FutureSocieties.Change("SlaveProfessionalism", -10);
+				FutureSocieties.Change("Slave Professionalism", -10);
 			}
 		}
 	} else if (V.PC.intelligence + V.PC.intelligenceImplant <= 10) {
-		if (V.rep > 18000) {
+		if (V.rep > 18000 - _enduringRep) {
 			r.push(`You've managed to build such a reputation for yourself that your lack of intelligence is no longer a societal concern.`);
 		} else {
 			repX(forceNeg(Math.min((V.rep * 0.05), 750)), "PCappearance");
 			r.push(`Society <span class="red">is uncomfortable</span> being led by someone not smart. Your lack of intelligence brings your every action under scrutiny.`);
 		}
 	} else if (V.PC.intelligence + V.PC.intelligenceImplant <= 50) {
-		if (V.rep > 12000) {
+		if (V.rep > 12000 - _enduringRep) {
 			r.push(`You've built such a reputation for yourself that your lack of intelligence is no longer a societal concern.`);
 		} else {
 			repX(forceNeg(Math.min((V.rep * 0.05), 500)), "PCappearance");
@@ -282,24 +283,24 @@ App.EndWeek.reputation = function() {
 			/* already handled above */
 		} else {
 			if (V.arcologies[0].FSGenderRadicalist !== "unset") {
-				if (V.rep > 18000) {
+				if (V.rep > 18000 - _enduringRep) {
 					r.push(`You are so well regarded that society has acquiesced that getting penetrated is not a sure sign of femininity.`);
 				} else {
 					r.push(`Society views getting fucked as sign of femininity and is <span class="red">strongly against your sexual preferences.</span>`);
-					FutureSocieties.Change("GenderRadicalist", -1);
+					FutureSocieties.Change("Gender Radicalist", -1);
 					repX(-1000, "PCactions");
 				}
 			} else if (V.arcologies[0].FSGenderFundamentalist !== "unset" && V.PC.vagina !== -1 && V.PC.title === 0) {
-				if (V.rep > 10000) {
+				if (V.rep > 10000 - _enduringRep) {
 					r.push(`Society has grown accustomed to your efforts enough to not care that you enjoy slave dick. In fact, it even <span class="green">strengthens</span> traditional gender roles, even though you insist on breaking them.`);
-					FutureSocieties.Change("GenderFundamentalist", 1);
+					FutureSocieties.Change("Gender Fundamentalist", 1);
 				} else {
 					r.push(`Society wonders if you would be happier in a whore house getting fucked all day instead of trying to lead an arcology. Your efforts <span class="red">strongly support</span> the idea that women should not be in positions of responsibility.`);
-					FutureSocieties.Change("GenderFundamentalist", -3);
+					FutureSocieties.Change("Gender Fundamentalist", -3);
 					repX(-1000, "PCactions");
 				}
 			} else {
-				if (V.rep > 15000) {
+				if (V.rep > 15000 - _enduringRep) {
 					r.push(`You are so well liked that society has accepted that you enjoy taking everything a slave has to offer.`);
 				} else {
 					r.push(`Society finds your penchant for taking slave dick <span class="red">very distasteful</span> for a slaveowner.`);
@@ -350,7 +351,7 @@ App.EndWeek.reputation = function() {
 		}
 	} else if (V.arcologies[0].FSSubjugationist !== "unset") {
 		if (V.PC.race === V.arcologies[0].FSSubjugationistRace) {
-			if (V.rep > 15000) {
+			if (V.rep > 15000 - _enduringRep) {
 				r.push(`Your reputation is so strong that society has accepted your ${V.PC.race}ness despite you being an inferior race.`);
 			} else {
 				r.push(`Society <span class="red">loathes;</span> being lead by an inferior ${V.PC.race}, believing that any other race would make a far better leader than you.`);
@@ -508,6 +509,8 @@ App.EndWeek.reputation = function() {
 				r.push(`penetration.`);
 			}
 			repX(10, "PCappearance");
+		} else if (V.rep < 20000 - _enduringRep) {
+			r.push(`You have carved out such a name for yourself that society has come to terms with being led by a woman with child.`);
 		} else {
 			r.push(`Most prominent female owners avoid being penetrated on`);
 			if (V.policies.sexualOpenness === 1) {
@@ -520,13 +523,13 @@ App.EndWeek.reputation = function() {
 		}
 	}
 
-	if (V.PC.career === "escort" && V.rep < 16000) {
+	if (V.PC.career === "escort" && V.rep < 16000 - _enduringRep) {
 		r.push(`Society <span class="red">frowns</span> over being run by an ex-whore. The presence of porn of you on the net doesn't aid your reputation either.`);
 		repX(forceNeg(Math.min((V.rep * 0.05), 500)), "PCactions");
 	} else if (V.PC.career === "escort") {
 		r.push(`Your reputation is so strong that society has accepted your previous endeavors despite how unusual it is for a prominent slaveowner to have once nearly been a slave.`);
 	}
-	if (V.PC.career === "servant" && V.rep < 12000) {
+	if (V.PC.career === "servant" && V.rep < 12000 - _enduringRep) {
 		r.push(`Society <span class="red">frowns</span> over being run by an ex-`);
 		if (V.PC.title === 1) {
 			r.push(`butler,`);
@@ -538,10 +541,10 @@ App.EndWeek.reputation = function() {
 	} else if (V.PC.career === "servant") {
 		r.push(`Your reputation is so strong that society has accepted your previous vocation despite how unusual it is for a prominent slaveowner to have once been nothing more than a lowly servant.`);
 	}
-	if (V.PC.career === "gang" && V.rep < 15000) {
+	if (V.PC.career === "gang" && V.rep < 15000 - _enduringRep) {
 		r.push(`Society <span class="red">frowns</span> over being run by an ex-gang leader, no matter how strong they might have been.`);
 		repX(forceNeg(Math.min((V.rep * 0.05), 500)), "PCactions");
-	} else if (V.PC.career === "BlackHat" && V.rep < 15000) {
+	} else if (V.PC.career === "BlackHat" && V.rep < 15000 - _enduringRep) {
 		r.push(`Society <span class="red">dislikes</span> being run by someone so capable of dredging up secrets, especially when they used to do it for the highest bidder.`);
 		repX(forceNeg(Math.min((V.rep * 0.05), 500)), "PCactions");
 	} else if (V.PC.career === "gang" || V.PC.career === "BlackHat") {
@@ -560,7 +563,7 @@ App.EndWeek.reputation = function() {
 	if (V.arcologies[0].FSRomanRevivalist !== "unset") {
 		if (V.mercenaries > 0) {
 			r.push(`Society <span class="green">approves</span> of how you are providing for the defense of the state, as should all citizens of the new Rome.`);
-			FutureSocieties.Change("RomanRevivalist", V.mercenaries);
+			FutureSocieties.Change("Roman Revivalist", V.mercenaries);
 		}
 		if (V.slaves.length > 20 && V.cash > 50000) {
 			r.push(`Society <span class="green">strongly approves</span> of your wealth and prosperity, fit goals for the`);
@@ -571,42 +574,42 @@ App.EndWeek.reputation = function() {
 			} else {
 				r.push(`rising Roman lady.`);
 			}
-			FutureSocieties.Change("RomanRevivalist", 5);
+			FutureSocieties.Change("Roman Revivalist", 5);
 		}
 		if (V.language !== "Latin") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than the storied Latin <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("RomanRevivalist", -2);
+			FutureSocieties.Change("Roman Revivalist", -2);
 		}
 	} else if (V.arcologies[0].FSAztecRevivalist !== "unset") {
 		if (V.PC.visualAge >= 35) {
 			r.push(`Society <span class="green">approves</span> of your advancing age, which advances the ancient Aztec ideal of an experienced leader of the people.`);
-			FutureSocieties.Change("AztecRevivalist", 1);
+			FutureSocieties.Change("Aztec Revivalist", 1);
 		}
 		if (V.HeadGirlID === 0) {
 			r.push(`Society <span class="red">disapproves</span> of you not having a Head Girl as an advisor and assistant.`);
-			FutureSocieties.Change("AztecRevivalist", -2);
+			FutureSocieties.Change("Aztec Revivalist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of your reliance on a Head Girl as an advisor and assistant.`);
-			FutureSocieties.Change("AztecRevivalist", 2);
+			FutureSocieties.Change("Aztec Revivalist", 2);
 		}
 		if (V.PC.skill.warfare < 0) {
 			r.push(`Society <span class="red">greatly disapproves</span> of your feebleness in the arts of war.`);
-			FutureSocieties.Change("AztecRevivalist", -4);
+			FutureSocieties.Change("Aztec Revivalist", -4);
 		} else if (V.PC.skill.warfare < 50) {
 			r.push(`Society <span class="red">disapproves</span> of you not being properly trained in the arts of war.`);
-			FutureSocieties.Change("AztecRevivalist", -2);
+			FutureSocieties.Change("Aztec Revivalist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of having a leader that is trained in the arts of war.`);
-			FutureSocieties.Change("AztecRevivalist", 2);
+			FutureSocieties.Change("Aztec Revivalist", 2);
 		}
 		if (V.language !== "Nahuatl") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than the revived Nahuatl <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("AztecRevivalist", -3);
+			FutureSocieties.Change("Aztec Revivalist", -3);
 		}
 	} else if (V.arcologies[0].FSNeoImperialist !== "unset") {
 		if (V.mercenaries > 0) {
 			r.push(`Society <span class="green">approves</span> of your strong militarism and elite mercenaries, as your tradition of Imperial conquest glorifies military success above all else.`);
-			FutureSocieties.Change("NeoImperialist", V.mercenaries);
+			FutureSocieties.Change("Neo-Imperialist", V.mercenaries);
 		}
 		if (V.slaves.length > 20 && V.cash > 50000) {
 			r.push(`Society <span class="green">strongly approves</span> of your great wealth and prosperity, as is only fitting for an`);
@@ -617,82 +620,82 @@ App.EndWeek.reputation = function() {
 			} else {
 				r.push(`graceful Imperial noble.`);
 			}
-			FutureSocieties.Change("NeoImperialist", 5);
+			FutureSocieties.Change("Neo-Imperialist", 5);
 		}
 		if (V.cash < 1000) {
 			r.push(`Society <span class="red">disapproves</span> of your poverty; it is viewed as completely unbefitting for an Imperial ruler to have so little cash on hand, and indicative of weakness in your rule.`);
-			FutureSocieties.Change("NeoImperialist", -2);
+			FutureSocieties.Change("Neo-Imperialist", -2);
 		}
 		if (V.PC.skill.warfare < 0) {
 			r.push(`Society <span class="red">greatly disapproves</span> of your weakness in combat. The core duty of any Imperial noble is to fight, and your failure to understand the art of war is an unacceptable weakness.`);
-			FutureSocieties.Change("NeoImperialist", -4);
+			FutureSocieties.Change("Neo-Imperialist", -4);
 		} else if (V.PC.skill.warfare < 50) {
 			r.push(`Society <span class="red">disapproves</span> of you lacking training in the art of warfare, as fighting is a core duty of any Imperial noble.`);
-			FutureSocieties.Change("NeoImperialist", -2);
+			FutureSocieties.Change("Neo-Imperialist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of having a leader who is a capable warrior. Your strength in battle is seen proof of your indisputable right to rule.`);
-			FutureSocieties.Change("NeoImperialist", 2);
+			FutureSocieties.Change("Neo-Imperialist", 2);
 		}
 	} else if (V.arcologies[0].FSEgyptianRevivalist !== "unset") {
 		const _racialVarieties = new Set(V.slaves.map((s) => s.race));
 		if (_racialVarieties.size > 4) {
 			r.push(`Society <span class="green">strongly approves</span> of how you own a cornucopia of different races, which advances the ancient Egyptian ideal of cosmopolitan sex slavery.`);
-			FutureSocieties.Change("EgyptianRevivalist", 5);
+			FutureSocieties.Change("Egyptian Revivalist", 5);
 		}
 		if (V.language !== "Ancient Egyptian") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than revived Ancient Egyptian <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("EgyptianRevivalist", -2);
+			FutureSocieties.Change("Egyptian Revivalist", -2);
 		}
 	} else if (V.arcologies[0].FSEdoRevivalist !== "unset") {
 		const _threshold = Math.trunc(V.rep / 2000);
 		if (V.publicServants <= _threshold) {
 			r.push(`Society <span class="red">disapproves</span> of your failure to provide for cultural development by offering public servants or club slaves in a number that befits your reputation.`);
-			FutureSocieties.Change("EdoRevivalist", -2);
+			FutureSocieties.Change("Edo Revivalist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of your provision for cultural development by offering public servants and club slaves in a number that befits your reputation.`);
-			FutureSocieties.Change("EdoRevivalist", 2);
+			FutureSocieties.Change("Edo Revivalist", 2);
 		}
 		if (V.language !== "Japanese") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than pure Japanese <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("EdoRevivalist", -2);
+			FutureSocieties.Change("Edo Revivalist", -2);
 		}
 	} else if (V.arcologies[0].FSArabianRevivalist !== "unset") {
 		if (V.fuckSlaves < V.rep / 3500) {
 			r.push(`Society <span class="red">disapproves</span> of the small size of your harem, feeling that you do not have enough fucktoys or slaves in your master suite for your reputation.`);
-			FutureSocieties.Change("ArabianRevivalist", -2);
+			FutureSocieties.Change("Arabian Revivalist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of the size of your harem, feeling that you have a good number of fucktoys and slaves in your master suite for your reputation.`);
-			FutureSocieties.Change("ArabianRevivalist", 2);
+			FutureSocieties.Change("Arabian Revivalist", 2);
 		}
 		if (V.language !== "Arabic") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than the Arabic in which the word of God was passed to Muhammad <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("ArabianRevivalist", -2);
+			FutureSocieties.Change("Arabian Revivalist", -2);
 		}
 	} else if (V.arcologies[0].FSChineseRevivalist !== "unset") {
 		if (V.HeadGirlID === 0) {
 			r.push(`Society <span class="red">disapproves</span> of your failure to rely on a Head Girl, as proper imperial administration requires,`);
-			FutureSocieties.Change("ChineseRevivalist", -2);
+			FutureSocieties.Change("Chinese Revivalist", -2);
 		} else {
 			r.push(`Society <span class="green">approves</span> of your reliance on a Head Girl, as proper imperial administration requires,`);
-			FutureSocieties.Change("ChineseRevivalist", 2);
+			FutureSocieties.Change("Chinese Revivalist", 2);
 		}
 		if (V.RecruiterID === 0) {
 			r.push(`<span class="red">disapproves</span> of your failure to maintain a Recruiter to expand the Middle Kingdom,`);
-			FutureSocieties.Change("ChineseRevivalist", -2);
+			FutureSocieties.Change("Chinese Revivalist", -2);
 		} else {
 			r.push(`<span class="green">approves</span> of your maintaining a Recruiter to expand the Middle Kingdom,`);
-			FutureSocieties.Change("ChineseRevivalist", 2);
+			FutureSocieties.Change("Chinese Revivalist", 2);
 		}
 		if (V.BodyguardID === 0) {
 			r.push(`and <span class="red">disapproves</span> of your failure to keep a Bodyguard as befits a proper imperial palace.`);
-			FutureSocieties.Change("ChineseRevivalist", -2);
+			FutureSocieties.Change("Chinese Revivalist", -2);
 		} else {
 			r.push(`and <span class="green">approves</span> of your keeping a Bodyguard, as befits a proper imperial palace.`);
-			FutureSocieties.Change("ChineseRevivalist", 2);
+			FutureSocieties.Change("Chinese Revivalist", 2);
 		}
 		if (V.language !== "Chinese") {
 			r.push(`Continuing to use ${V.language} as the lingua franca of ${V.arcologies[0].name} rather than the Chinese of the Middle Kingdom <span class="red">disappoints</span> society and causes doubt about your revivalist project.`);
-			FutureSocieties.Change("ChineseRevivalist", -2);
+			FutureSocieties.Change("Chinese Revivalist", -2);
 		}
 	}
 
@@ -702,7 +705,7 @@ App.EndWeek.reputation = function() {
 		if (policies.countEugenicsSMRs() > 0) {
 			r.push(`Society <span class="red">disapproves</span> of your policies sterilizing potential mothers. Your insistence on eugenics hinders adoption of your new society.`);
 			_noEugenics = -1 * policies.countEugenicsSMRs();
-			FutureSocieties.Change("RepopulationFocus", _noEugenics);
+			FutureSocieties.Change("Repopulationist", _noEugenics);
 		}
 	} else if (V.arcologies[0].FSPaternalist !== "unset") {
 		if (policies.countEugenicsSMRs() > 0) {
@@ -820,7 +823,7 @@ App.EndWeek.reputation = function() {
 	if (V.breederOrphanageTotal > 0 && V.arcologies[0].FSRepopulationFocus !== "unset") {
 		r.push(`The public <span class="green">approves</span> of the way you've dedicated ${num(V.breederOrphanageTotal)} of your slaves' children to be raised into future breeders.`);
 		const _futureBreeders = Math.round(((V.breederOrphanageTotal / 100) + 1));
-		FutureSocieties.Change("RepopulationFocus", _futureBreeders);
+		FutureSocieties.Change("Repopulationist", _futureBreeders);
 	}
 
 	if (V.arcologies[0].FSNull !== "unset") {
diff --git a/src/endWeek/healthFunctions.js b/src/endWeek/healthFunctions.js
index a23337026450cb4e5142f1e84334322c2398e682..64b4202b316231e918e4c4fd08fbb6e45e24b7fb 100644
--- a/src/endWeek/healthFunctions.js
+++ b/src/endWeek/healthFunctions.js
@@ -85,22 +85,22 @@ globalThis.illness = function(slave) {
 		assignBonus += 20;
 	} else if ((slave.assignment === Job.MASTERSUITE || slave.assignment === Job.FUCKTOY) && V.PC.skill.medicine >= 40) {
 		assignBonus += 10;
-	} else if (setup.nurseCareers.includes(slave.career) || slave.skill.nurse >= 100 || slave.intelligence + slave.intelligenceImplant > 95) { // Let slaves with experience or brains use it
+	} else if (App.Data.Careers.Leader.nurse.includes(slave.career) || slave.skill.nurse >= 100 || slave.intelligence + slave.intelligenceImplant > 95) { // Let slaves with experience or brains use it
 		assignBonus += 10;
 	}
 	if (random < 6) { // There is always a 5% chance of a slave feeling worse or coming in contact with an illness
 		if (H.illness > 0) {
 			H.illness += 1 + Math.trunc((slave.chem / 10 + jsRandom(1, 50) + 15) / 100); // Illness progresses with 1, unless chem > 350, then there's a chance for 2
-			r += ` <span class="red">${His} sickness has progressed.</span>`;
+			r += ` <span class="health dec">${His} sickness has progressed.</span>`;
 			if (H.illness > 5) {
 				healthDamage(slave, 20 * (H.illness - 5)); // Condition penalty for going over maximum illness, very dangerous
 				H.illness = 5;
-				r += ` ${His} illness <span class="red">makes an attempt to claim ${his} life.</span>`;
+				r += ` ${His} illness <span class="health dec">makes an attempt to claim ${his} life.</span>`;
 			}
 		} else {
 			getIll(slave);
 			if (H.illness > 0) {
-				r += ` ${He} has come down with <span class="red">${addA(sicknessDegree[H.illness])}.</span>`;
+				r += ` ${He} has come down with <span class="health dec">${addA(sicknessDegree[H.illness])}.</span>`;
 			}
 		}
 	} else if (random > 95 && H.illness > 0) { // There is always a 5% chance of a slave getting better
@@ -108,9 +108,9 @@ globalThis.illness = function(slave) {
 		if (H.illness < 0) {
 			H.illness = 0;
 			improveCondition(slave, 5);
-			r += ` <span class="green">${His} sickness has waned</span> and ${he} now feels better.`;
+			r += ` <span class="health inc">${His} sickness has waned</span> and ${he} now feels better.`;
 		} else {
-			r += ` ${His} immune system <span class="green">fights back</span> against ${his} illness.`;
+			r += ` ${His} immune system <span class="health inc">fights back</span> against ${his} illness.`;
 		}
 	} else {
 		/* eslint-disable camelcase */
@@ -125,24 +125,24 @@ globalThis.illness = function(slave) {
 		if (H.illness > 0 && ((health_adjusted + age_modifier + industrialDairyImmunoBoost) / 3) + bonus_modifiers > (30 + (H.illness * 10)) / curativesBonus) {
 			if (nurse_effectiveness > 30 && (jsRandom(1, 2) === 2 || slave.assignment === Job.CLINIC) && H.illness > 1) { // A particularly effective nurse can improve illness faster
 				H.illness -= 2;
-				r += ` ${S.Nurse.slaveName} <span class="green">successfully treats</span> ${his} illness.`;
+				r += ` ${S.Nurse.slaveName} <span class="health inc">successfully treats</span> ${his} illness.`;
 			} else {
 				H.illness -= 1;
-				r += ` ${His} body <span class="green">fights back</span> against ${his} illness.`;
+				r += ` ${His} body <span class="health inc">fights back</span> against ${his} illness.`;
 			}
 			if (H.illness === 0) {
-				r += ` ${He} no longer <span class="green">shows any signs</span> of ${his} previous sickness.`;
+				r += ` ${He} no longer <span class="health inc">shows any signs</span> of ${his} previous sickness.`;
 			}
 		} else if (H.illness === 0) {
 			if ([Job.BODYGUARD, Job.DJ, Job.MADAM, Job.ARCADE, Job.GLORYHOLE, Job.CLUB, Job.PUBLIC, Job.WHORE, Job.BROTHEL].includes(slave.assignment)) { // Limit to outside jobs only
 				if (((Math.min(health_adjusted, 50) + age_modifier) / 3) + bonus_modifiers < 30 / Math.min(curativesBonus + 1, 2)) { // Chance of getting ill 30% at complete default, 20% with a favorable assignment, 15% with curatives or preventatives, 10% with both measures active and a small benefit from effective Nurse screening
 					getIll(slave);
-					r += ` ${He} has come down with <span class="red">${addA(sicknessDegree[H.illness])}.</span>`;
+					r += ` ${He} has come down with <span class="health dec">${addA(sicknessDegree[H.illness])}.</span>`;
 				}
 			}
 		} else {
 			healthDamage(slave, Math.pow(H.illness, 2));
-			r += ` ${He} <span class="red">suffers under ${his} ${sicknessDegree[H.illness]}.</span>`;
+			r += ` ${He} <span class="health dec">suffers under ${his} ${sicknessDegree[H.illness]}.</span>`;
 		}
 		/* eslint-enable camelcase */
 	}
@@ -215,7 +215,7 @@ globalThis.nurseEffectiveness = function(slave) {
 	const clinicUpgrade = 1; // Creating a purchasable upgrade to increase the amount of slaves the nurse can handle -- TODO
 	const clinicScreening = 1; // Assumes the clinic is set to screening all slaves to improve their chances of staying healthy. Turning it off would allow the nurse to focus on just her patients in the clinic -- TODO
 	if (S.Nurse) {
-		const nurseSkill = setup.nurseCareers.includes(S.Nurse.career) ? 200 : S.Nurse.skill.nurse;
+		const nurseSkill = App.Data.Careers.Leader.nurse.includes(S.Nurse.career) ? 200 : S.Nurse.skill.nurse;
 		let nurseEffectiveness = Math.trunc((nurseSkill * clinicUpgrade / Math.max((App.Entity.facilities.clinic.employeesIDs().size * 10 + (V.slaves.length * 2) * clinicScreening), 1)) * 20);
 		if (H.illness > 1 && slave.assignment === Job.CLINIC) {
 			if (nurseEffectiveness < 20) {
@@ -389,7 +389,7 @@ globalThis.tired = function(slave) {
 			assignment -= 40 * (V.spaUpgrade + 1); // Reduces tired by an average of 40 points while in the spa, double with the upgraded spa
 			if (S.Attendant) {
 				let skillBonus;
-				if (setup.attendantCareers.includes(S.Attendant.career)) {
+				if (App.Data.Careers.Leader.attendant.includes(S.Attendant.career)) {
 					skillBonus = 200;
 				} else {
 					skillBonus = S.Attendant.skill.attendant;
diff --git a/src/endWeek/nextWeek/nextWeek.js b/src/endWeek/nextWeek/nextWeek.js
new file mode 100644
index 0000000000000000000000000000000000000000..a6add17ab0d2ff3fcc6780dc864333278a16134f
--- /dev/null
+++ b/src/endWeek/nextWeek/nextWeek.js
@@ -0,0 +1,449 @@
+App.EndWeek.nextWeek = function() {
+	V.HackingSkillMultiplier = upgradeMultiplier('hacking');
+	V.upgradeMultiplierArcology = upgradeMultiplier('engineering');
+	V.upgradeMultiplierMedicine = upgradeMultiplier('medicine');
+	V.upgradeMultiplierTrade = upgradeMultiplier('trading');
+
+	if (V.rivalOwner !== 0) {
+		const rival = V.arcologies.find(function(s) { return s.rival === 1; });
+		if (rival) {
+			V.rivalOwner = rival.prosperity;
+		}
+	}
+
+	if (V.playerAging !== 0) {
+		V.PC.birthWeek += 1;
+		if (V.PC.ovaryAge >= 47 && V.PC.ovaries === 1 && (V.PC.preg === -1 || V.PC.preg === 0)) {
+			V.PC.preg = -2;
+		}
+		if (V.PC.birthWeek >= 52) {
+			V.PC.birthWeek = 0;
+			if (V.playerAging === 2) {
+				V.PC.physicalAge++;
+				V.PC.actualAge++;
+				V.PC.visualAge++;
+				V.PC.ovaryAge++;
+				agePCEffects();
+			}
+		}
+	}
+	if (V.menstruation === 1) {
+	} else if (V.PC.geneticQuirks.superfetation === 2 && V.PC.womb.length > 0) {
+		if (V.PC.fertPeak === 0) {
+			V.PC.fertPeak = 1;
+		}
+		V.PC.fertPeak--;
+	} else if (V.PC.fertPeak !== 0) {
+		V.PC.fertPeak = 0;
+	}
+
+	// Adding random changes to the economy
+	if (V.difficultySwitch === 1) {
+		const globalEconSeed = random(1, 100);
+		if (globalEconSeed > 98) {
+			V.economy += 2;
+		} else if (globalEconSeed > 85) {
+			V.economy += 1;
+		} else if (globalEconSeed <= 2) {
+			V.economy -= 2;
+		} else if (globalEconSeed <= 25 + V.econRate * 10) {
+			V.economy -= 1;
+		}
+		if (V.economy < 20) {
+			V.economy = 20;
+		}
+		const localEconSeed = random(1, 100);
+		if (V.localEcon <= (V.economy + V.econAdvantage)) {
+			if (localEconSeed > 95) {
+				V.localEcon += 2;
+			} else if (localEconSeed > 50) {
+				V.localEcon += 1;
+			} else if (localEconSeed <= 1) {
+				V.localEcon -= 2;
+			} else if (localEconSeed <= 10) {
+				V.localEcon -= 1;
+			}
+		} else if (V.localEcon <= (V.economy + V.econAdvantage + 5)) {
+			if (localEconSeed > 98) {
+				V.localEcon += 2;
+			} else if (localEconSeed > 66) {
+				V.localEcon += 1;
+			} else if (localEconSeed <= 2) {
+				V.localEcon -= 2;
+			} else if (localEconSeed <= 33) {
+				V.localEcon -= 1;
+			}
+		} else if (localEconSeed > 99) {
+			V.localEcon += 2;
+		} else if (localEconSeed > 90) {
+			V.localEcon += 1;
+		} else if (localEconSeed <= 5) {
+			V.localEcon -= 2;
+		} else if (localEconSeed <= 50) {
+			V.localEcon -= 1;
+		}
+		if (V.localEcon < 20) {
+			V.localEcon = 20;
+		}
+
+		if (V.experimental.food === 1) {
+			if (V.localEcon > 100) {
+				V.farmyardFoodCost = Math.max(5 / (1 + (Math.trunc(1000 - 100000 / V.localEcon) / 10) / 100), 3.125);
+			} else if (V.localEcon === 100) {
+				V.farmyardFoodCost = 5;
+			} else {
+				V.farmyardFoodCost = Math.min(5 * (1 + 1.5 * Math.sqrt(Math.trunc(100000 / V.localEcon - 1000) / 10) / 100), 6.5);
+			}
+		}
+		V.foodCost = Math.trunc(2500 / V.localEcon);
+		V.drugsCost = Math.trunc(10000 / V.localEcon);
+		V.rulesCost = Math.trunc(10000 / V.localEcon);
+		V.modCost = Math.trunc(5000 / V.localEcon);
+		V.surgeryCost = Math.trunc(30000 / (V.localEcon * (V.PC.career === "medicine" ? 2 : 1)));
+	}
+
+	V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity, 1, V.AProsperityCap);
+
+	V.averageTrust = 0;
+	V.averageDevotion = 0;
+	let slavesContributing = 0;
+	let oldHG = -1;
+	let newHG = -1;
+	if (V.studio === 1) {
+		for (const genre of App.Porn.getAllGenres()) {
+			V.pornStars[genre.fameVar].p1count = 0;
+		}
+	}
+	for (const slave of V.slaves) {
+		ageSlaveWeeks(slave);
+		if (slave.indenture > 0) {
+			slave.indenture -= 1;
+		}
+		if (slave.induceLactation > 0) {
+			slave.induceLactation--;
+		}
+		if (slave.lactation === 1) {
+			if (slave.lactationDuration === 1) {
+				slave.boobsMilk = 10 * slave.lactationAdaptation;
+				slave.boobs += slave.boobsMilk;
+			}
+		}
+		if (V.menstruation === 1) {
+		} else if (slave.geneticQuirks.superfetation === 2 && slave.womb.length > 0) {
+			if (slave.fertPeak === 0) {
+				slave.fertPeak = 1;
+			}
+			slave.fertPeak--;
+		} else if (slave.fertPeak !== 0) {
+			slave.fertPeak = 0;
+		}
+		slave.trust = Number(slave.trust.toFixed(1));
+		slave.devotion = Number(slave.devotion.toFixed(1));
+		slave.oldDevotion = slave.devotion;
+		slave.oldTrust = slave.trust;
+		slave.minorInjury = 0;
+		if (slave.sentence > 1) {
+			slave.sentence -= 1;
+		} else if (slave.sentence === 1) {
+			removeJob(slave, slave.assignment);
+		}
+		if (slave.weekAcquired < 0) {
+			slave.weekAcquired = 0;
+		}
+		if (slave.relationship === 0) {
+			slave.relationshipTarget = 0;
+		}
+		if (slave.rivalry === 0) {
+			slave.rivalryTarget = 0;
+		} else if (slave.rivalry < 0) {
+			slave.rivalryTarget = 0;
+			slave.rivalry = 0;
+		}
+		if (slave.vagina < 0) {
+			slave.vaginalAccessory = "none";
+			slave.chastityVagina = 0;
+			slave.vaginaPiercing = 0;
+		}
+		if (slave.dick === 0) {
+			slave.dickAccessory = "none";
+			slave.chastityPenis = 0;
+			slave.dickTat = 0;
+			slave.dickPiercing = 0;
+		}
+		/* I don't trust these */
+		if (!hasAnyArms(slave)) {
+			slave.armsTat = 0;
+			slave.nails = 0;
+			slave.armAccessory = "none";
+		}
+		if (!hasAnyLegs(slave)) {
+			slave.heels = 0;
+			slave.shoes = "none";
+			slave.legAccessory = "none";
+			slave.legsTat = 0;
+		}
+		/* irregular leptin production weight gain/loss setter */
+		if (slave.geneticQuirks.wGain === 2 && slave.geneticQuirks.wLoss === 2) {
+			slave.weightDirection = either(-1, 1);
+		} else if (slave.geneticQuirks.wGain === 2) {
+			slave.weightDirection = 1;
+		} else if (slave.geneticQuirks.wLoss === 2) {
+			slave.weightDirection = -1;
+		} else {
+			slave.weightDirection = 0;
+		}
+		/* Fix some possible floating point rounding errors, and bring precision to one decimal place. */
+		SlaveStatClamp(slave);
+		slave.energy = Math.clamp(slave.energy.toFixed(1), 0, 100);
+		slave.attrXY = Math.clamp(slave.attrXY.toFixed(1), 0, 100);
+		slave.attrXX = Math.clamp(slave.attrXX.toFixed(1), 0, 100);
+		if (slave.fetishStrength > 95) {
+			slave.fetishStrength = 100;
+		} else {
+			slave.fetishStrength = Math.clamp(slave.fetishStrength.toFixed(1), 0, 100);
+		}
+		slave.weight = Math.clamp(slave.weight.toFixed(1), -100, 200);
+		slave.butt = Number(slave.butt.toFixed(1));
+		slave.muscles = Math.clamp(slave.muscles.toFixed(1), -100, 100);
+		slave.face = Math.clamp(slave.face.toFixed(1), -100, 100);
+		slave.lips = Math.clamp(slave.lips.toFixed(1), 0, 100);
+		slave.skill.oral = Math.clamp(slave.skill.oral.toFixed(1), 0, 100);
+		slave.skill.vaginal = Math.clamp(slave.skill.vaginal.toFixed(1), 0, 100);
+		slave.skill.anal = Math.clamp(slave.skill.anal.toFixed(1), 0, 100);
+		slave.skill.whoring = Math.clamp(slave.skill.whoring.toFixed(1), 0, 100);
+		slave.skill.entertainment = Math.clamp(slave.skill.entertainment.toFixed(1), 0, 100);
+		slave.lactationAdaptation = Math.clamp(slave.lactationAdaptation.toFixed(1), 0, 100);
+		slave.intelligenceImplant = Math.clamp(slave.intelligenceImplant.toFixed(1), 0, 30);
+		slave.prematureBirth = 0;
+		if (V.HGSuiteEquality === 1 && V.HeadGirlID !== 0 && slave.devotion > 50) {
+			if (slave.assignment === "live with your Head Girl") {
+				newHG = slave.ID;
+			} else if (slave.ID === V.HeadGirlID) {
+				oldHG = slave.ID;
+			}
+		}
+		/* AVERAGE VALUES UPDATE */
+		if (assignmentVisible(slave)) {
+			V.averageTrust += slave.trust;
+			V.averageDevotion += slave.devotion;
+			slavesContributing++;
+		} else if (
+			slave.assignment !== "be confined in the cellblock" &&
+			slave.assignment !== "be confined in the arcade" &&
+			(slave.assignment !== "work in the dairy" || V.dairyRestraintsSetting < 2) &&
+			slave.assignment !== "labor in the production line"
+		) {
+			V.averageTrust += slave.trust * 0.5;
+			V.averageDevotion += slave.devotion * 0.5;
+			slavesContributing += 0.5;
+		}
+		if (V.studio === 1) {
+			const activeGenres = App.Porn.getAllGenres().filter((g) => slave.porn.fame[g.fameVar] > 0);
+			for (const genre of activeGenres) {
+				V.pornStars[genre.fameVar].p1count++;
+			}
+		}
+		if (slave.choosesOwnAssignment > 0) {
+			assignJob(slave, "choose her own job");
+		}
+	}
+	if (slavesContributing !== 0) {
+		V.averageTrust = V.averageTrust / slavesContributing;
+		V.averageDevotion = V.averageDevotion / slavesContributing;
+	}
+	V.enduringTrust = (V.averageTrust + (V.enduringTrust * 3)) / 4;
+	V.enduringDevotion = (V.averageDevotion + (V.enduringDevotion * 3)) / 4;
+
+	if (oldHG !== -1 && newHG !== -1) {
+		const oldTimeInGrade = V.HGTimeInGrade;
+		// preserve time in grade during HG swaps
+		const keepHelpingHG = (V.personalAttention === "HG"); // keep removeJob from clearing PC HG supporting.
+		removeJob(getSlave(newHG), "live with your Head Girl");
+		assignJob(getSlave(oldHG), "live with your Head Girl");
+		assignJob(getSlave(newHG), "be your Head Girl");
+		getSlave(newHG).diet = "healthy";
+		V.HGTimeInGrade = oldTimeInGrade;
+		if (keepHelpingHG) {
+			V.personalAttention = "HG";
+		}
+	}
+
+	const toSearch = V.PC.refreshment.toLowerCase();
+	if (toSearch.indexOf("fertility") !== -1) {
+		V.PC.forcedFertDrugs = 1;
+	} else if (V.PC.forcedFertDrugs > 0) {
+		V.PC.forcedFertDrugs--;
+	}
+
+	if (V.FCTV.receiver > 0) {
+		if (V.FCTV.pcViewership.frequency !== -1) {
+			V.FCTV.pcViewership.count++;
+			if (V.FCTV.pcViewership.count >= V.FCTV.pcViewership.frequency) {
+				V.FCTV.pcViewership.count = 0;
+			}
+		}
+	}
+
+	V.week++;
+
+	if (V.playerSurgery > 0) {
+		V.playerSurgery--;
+	}
+
+	if (V.secExpEnabled > 0) {
+		if (V.foughtThisWeek === 1) {
+			V.foughtThisWeek = 0;
+		}
+
+		if (V.SecExp.buildings.riotCenter) {
+			V.SecExp.buildings.riotCenter.sentUnitCooldown = Math.max(V.SecExp.buildings.riotCenter.sentUnitCooldown - 1, 0);
+		}
+		V.SecExp.proclamation.cooldown = Math.max(V.SecExp.proclamation.cooldown - 1, 0);
+		delete V.SecExp.war;
+	}
+
+	App.EndWeek.weather();
+
+	if (V.boomerangWeeks) {
+		V.boomerangWeeks++;
+	} else {
+		V.boomerangSlave = 0;
+	}
+	if (V.traitorWeeks) {
+		V.traitorWeeks++;
+	}
+
+	V.thisWeeksFSWares = V.merchantFSWares.randomMany(2);
+	V.thisWeeksIllegalWares = V.merchantIllegalWares.randomMany(1);
+	V.prisonCircuitIndex++;
+	if (V.prisonCircuitIndex >= V.prisonCircuit.length) {
+		V.prisonCircuitIndex = 0;
+	}
+
+	V.coursed = 0;
+	V.prestigeAuctioned = 0;
+	V.eliteAuctioned = 0;
+	V.shelterSlave = 0;
+	V.shelterSlaveBought = 0;
+	V.slaveMarketLimit = 10 + (V.rep / 1000);
+	V.slavesSeen = 0;
+	V.slavesSacrificedThisWeek = 0;
+
+	if (V.pit) {
+		V.pit.fought = false;
+	}
+
+	App.EndWeek.resetGlobals();
+
+	if (V.autosave !== 0) {
+		Save.autosave.save("Week Start Autosave");
+	}
+
+	if (V.SF.Toggle && V.SF.FS.Tension > 100) {
+		if (V.rep > 17500) {
+			V.rep = 17500;
+		}
+	}
+	V.NaNArray = findNaN();
+
+	function agePCEffects() {
+		switch (V.PC.actualAge) {
+			case 3:
+				V.AgeTrainingLowerBoundPC = 18;
+				V.AgeTrainingUpperBoundPC = 20;
+				V.AgeEffectOnTrainerPricingPC = .1;
+				V.AgeEffectOnTrainerEffectivenessPC = .1;
+				break;
+			case 4:
+				V.AgeTrainingLowerBoundPC = 17;
+				V.AgeTrainingUpperBoundPC = 19;
+				V.AgeEffectOnTrainerPricingPC = .15;
+				V.AgeEffectOnTrainerEffectivenessPC = .15;
+				break;
+			case 5:
+				V.AgeTrainingLowerBoundPC = 16;
+				V.AgeTrainingUpperBoundPC = 18;
+				V.AgeEffectOnTrainerPricingPC = .35;
+				V.AgeEffectOnTrainerEffectivenessPC = .35;
+				break;
+			case 6:
+				V.AgeTrainingLowerBoundPC = 15;
+				V.AgeTrainingUpperBoundPC = 17;
+				V.AgeEffectOnTrainerPricingPC = .55;
+				V.AgeEffectOnTrainerEffectivenessPC = .55;
+				break;
+			case 7:
+				V.AgeTrainingLowerBoundPC = 14;
+				V.AgeTrainingUpperBoundPC = 16;
+				V.AgeEffectOnTrainerPricingPC = .75;
+				V.AgeEffectOnTrainerEffectivenessPC = .75;
+				break;
+			case 8:
+				V.AgeTrainingLowerBoundPC = 13;
+				V.AgeTrainingUpperBoundPC = 15;
+				V.AgeEffectOnTrainerPricingPC = .85;
+				V.AgeEffectOnTrainerEffectivenessPC = .85;
+				break;
+			case 9:
+				V.AgeTrainingLowerBoundPC = 12;
+				V.AgeTrainingUpperBoundPC = 14;
+				V.AgeEffectOnTrainerPricingPC = 1.00;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.00;
+				break;
+			case 10:
+				V.AgeTrainingLowerBoundPC = 11;
+				V.AgeTrainingUpperBoundPC = 13;
+				V.AgeEffectOnTrainerPricingPC = 1.0005;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.0005;
+				break;
+			case 11:
+				V.AgeTrainingLowerBoundPC = 10;
+				V.AgeTrainingUpperBoundPC = 12;
+				V.AgeEffectOnTrainerPricingPC = 1.01;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.01;
+				break;
+			case 12:
+				V.AgeTrainingLowerBoundPC = 9;
+				V.AgeTrainingUpperBoundPC = 11;
+				V.AgeEffectOnTrainerPricingPC = 1.02;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.02;
+				break;
+			case 13:
+				V.AgeTrainingLowerBoundPC = 8;
+				V.AgeTrainingUpperBoundPC = 10;
+				V.AgeEffectOnTrainerPricingPC = 1.03;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.03;
+				break;
+			case 14:
+				V.AgeTrainingLowerBoundPC = 7;
+				V.AgeTrainingUpperBoundPC = 9;
+				V.AgeEffectOnTrainerPricingPC = 1.04;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.04;
+				break;
+			case 15:
+				V.AgeTrainingLowerBoundPC = 6;
+				V.AgeTrainingUpperBoundPC = 8;
+				V.AgeEffectOnTrainerPricingPC = 1.05;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.05;
+				break;
+			case 16:
+				V.AgeTrainingLowerBoundPC = 5;
+				V.AgeTrainingUpperBoundPC = 7;
+				V.AgeEffectOnTrainerPricingPC = 1.06;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.06;
+				break;
+			case 17:
+				V.AgeTrainingLowerBoundPC = 4;
+				V.AgeTrainingUpperBoundPC = 6;
+				V.AgeEffectOnTrainerPricingPC = 1.07;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.07;
+				break;
+			case 18:
+				V.AgeTrainingLowerBoundPC = 3;
+				V.AgeTrainingUpperBoundPC = 5;
+				V.AgeEffectOnTrainerPricingPC = 1.08;
+				V.AgeEffectOnTrainerEffectivenessPC = 1.08;
+				break;
+		}
+	}
+};
diff --git a/src/endWeek/nextWeek/nextWeek.tw b/src/endWeek/nextWeek/nextWeek.tw
new file mode 100644
index 0000000000000000000000000000000000000000..0502b990dafac6a88e36ae42cd9bdc901abcf735
--- /dev/null
+++ b/src/endWeek/nextWeek/nextWeek.tw
@@ -0,0 +1,5 @@
+:: Next Week [nobr]
+
+<<run App.EndWeek.nextWeek()>>
+
+<<goto "Main">>
diff --git a/src/endWeek/nextWeek/resetGlobals.js b/src/endWeek/nextWeek/resetGlobals.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1a63cefc0315c418792ac747d849368bbb0dac8
--- /dev/null
+++ b/src/endWeek/nextWeek/resetGlobals.js
@@ -0,0 +1,79 @@
+/**
+ * These are variables that either should be made into temp vars or should be Zeroed out once done with them instead of here. This can also interfere with debugging or hide NaN's as zeroing things out would clear a NaN. Also could stop from NaN's getting worse?
+ */
+App.EndWeek.resetGlobals = function() {
+	// Integer and float variables. No real need to zero them out but doesn't hurt to have them in a known state, though this might mask variables NaN'ing out. Takes up the least amount of Memory besides a "" string.
+	V.i = 0;
+	V.j = 0;
+	V.averageProsperity = 0;
+	V.motherSlave = -1;
+	V.daughterSlave = -1;
+	V.devMother = -1;
+	V.devDaughter = -1;
+	V.alphaTwin = -1;
+	V.betaTwin = -1;
+	V.youngerSister = -1;
+	V.olderSister = -1;
+	V.boobsID = -1;
+	V.boobsInterestTargetID = -1;
+	V.buttslutID = -1;
+	V.buttslutInterestTargetID = -1;
+	V.cumslutID = -1;
+	V.cumslutInterestTargetID = -1;
+	V.humiliationID = -1;
+	V.humiliationInterestTargetID = -1;
+	V.sadistID = -1;
+	V.sadistInterestTargetID = -1;
+	V.masochistID = -1;
+	V.masochistInterestTargetID = -1;
+	V.domID = -1;
+	V.dominantInterestTargetID = -1;
+	V.subID = -1;
+	V.submissiveInterestTargetID = -1;
+	V.shelterGirlID = -1;
+
+	// Other arrays
+	V.events = [];
+	V.RESSevent = [];
+	V.RESSTRevent = [];
+	V.RETSevent = [];
+	V.RECIevent = [];
+	V.RecETSevent = [];
+	V.REFIevent = [];
+	V.REFSevent = [];
+	V.PESSevent = [];
+	V.PETSevent = [];
+	V.FSAcquisitionEvents = [];
+	V.FSNonconformistEvents = [];
+	V.REAnalCowgirlSubIDs = [];
+	V.REButtholeCheckinIDs = [];
+	V.recruit = [];
+	V.RETasteTestSubIDs = [];
+	V.rebelSlaves = [];
+	V.REBoobCollisionSubIDs = [];
+	V.REIfYouEnjoyItSubIDs = [];
+	V.RESadisticDescriptionSubIDs = [];
+	V.REShowerForceSubIDs = [];
+	V.RECockmilkInterceptionIDs = [];
+	V.REInterslaveBeggingIDs = [];
+	V.eligibleSlaves = [];
+
+	// Slave Objects using 0 instead of null. Second most memory eaten up.
+	V.activeSlave = 0;
+	V.eventSlave = 0;
+	V.subSlave = 0;
+	V.relation = 0;
+	V.relative = 0;
+	V.relative2 = 0;
+
+	// Slave Objects that never get zeroed so null them here. Second most memory eaten up.
+	V.beforeGingering = null;
+
+	// Strings Memory varies.
+	V.buyer = "";
+	V.desc = "";
+	V.event = "";
+	V.goto = "";
+	V.malefactor = "";
+	// Done with zeroing out, what should be for the most part Temps
+};
diff --git a/src/endWeek/nextWeek/weather.js b/src/endWeek/nextWeek/weather.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8f7bec32ee76f34dbca9d3abfd75f44eae2a74c
--- /dev/null
+++ b/src/endWeek/nextWeek/weather.js
@@ -0,0 +1,653 @@
+App.EndWeek.weather = function() {
+	if (V.weatherRemaining === 0) {
+		if (V.continent === "Africa") {
+			V.weatherType = either(1, 1, 1, 1, 1, 2, 3, 4, 4, 5, 6, 6);
+		} else if (V.continent === "Asia") {
+			V.weatherType = either(1, 2, 2, 2, 2, 3, 4, 4, 5, 6);
+		} else if (V.continent === "Australia") {
+			V.weatherType = either(1, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6);
+		} else if (V.continent === "Western Europe") {
+			V.weatherType = either(1, 2, 2, 3, 4, 5, 5, 6);
+		} else if (V.continent === "Central Europe") {
+			V.weatherType = either(1, 2, 3, 4, 5, 5, 5, 6);
+		} else if (V.continent === "Scandinavia") {
+			V.weatherType = either(1, 2, 3, 4, 5, 5, 5, 5, 5, 6);
+		} else if (V.continent === "Eastern Europe") {
+			V.weatherType = either(1, 2, 3, 4, 4, 5, 5, 5, 6);
+		} else if (V.continent === "Southern Europe") {
+			V.weatherType = either(1, 1, 1, 2, 2, 3, 4, 4, 5, 6);
+		} else if (V.continent === "Brazil") {
+			V.weatherType = either(1, 1, 2, 3, 3, 3, 4, 4, 5, 6);
+		} else if (V.continent === "Japan") {
+			V.weatherType = either(1, 2, 2, 2, 3, 4, 4, 5, 6, 6);
+		} else if (V.continent === "Middle East") {
+			V.weatherType = either(1, 1, 1, 1, 1, 2, 3, 4, 4, 5, 6);
+		} else if (V.continent === "North America") {
+			V.weatherType = either(1, 2, 3, 3, 4, 5, 6, 6, 6);
+		} else if (V.continent === "South America") {
+			V.weatherType = either(1, 1, 2, 3, 3, 3, 4, 4, 5, 6);
+		} else if (V.terrain === "oceanic") {
+			V.weatherType = either(1, 2, 2, 2, 2, 2, 3, 4, 5, 6);
+		}
+		V.weatherRemaining = random(3, 6);
+	}
+
+	if (V.weatherLastWeek === 0) {
+		V.weatherLastWeek = 1;
+	}
+
+	const _seed = random(1, 10);
+	if (V.weatherType === 1) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.hotNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.hotLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.hotExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.hotHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.hotExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	} else if (V.weatherType === 2) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.windyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.windyLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.windyExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.windyHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.windyExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	} else if (V.weatherType === 3) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.smokyNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.smokyLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.smokyHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.smokyExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	} else if (V.weatherType === 4) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.toxicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.toxicLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.toxicHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.toxicExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	} else if (V.weatherType === 5) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.coldNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.coldLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.coldExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.coldHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.coldExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	} else if (V.weatherType === 6) {
+		if (V.week < 25) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 1) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 2) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 50 && V.weatherLastWeek === 3) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 1) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 2) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 3) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week < 75 && V.weatherLastWeek === 3) {
+			if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 1) {
+			if (_seed > 8) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 2) {
+			if (_seed > 7) {
+				V.weatherToday = App.Data.Weather.tectonicNice.random();
+				V.weatherLastWeek = 1;
+			} else if (_seed > 5) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 3) {
+			if (_seed > 6) {
+				V.weatherToday = App.Data.Weather.tectonicLight.random();
+				V.weatherLastWeek = 2;
+			} else if (_seed > 2) {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		} else if (V.week > 75 && V.weatherLastWeek === 4) {
+			if (_seed > 4) {
+				V.weatherToday = App.Data.Weather.tectonicHeavy.random();
+				V.weatherLastWeek = 3;
+			} else {
+				V.weatherToday = App.Data.Weather.tectonicExtreme.random();
+				V.weatherLastWeek = 4;
+			}
+		}
+	}
+};
diff --git a/src/endWeek/reports/arcadeReport.js b/src/endWeek/reports/arcadeReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..40aa29586d623197261fcb91ab70bfaa8b2cf843
--- /dev/null
+++ b/src/endWeek/reports/arcadeReport.js
@@ -0,0 +1,304 @@
+/**
+ * @returns {DocumentFragment}
+ */
+App.EndWeek.arcadeReport = function() {
+	const el = new DocumentFragment();
+	let r;
+
+	const arcadeStats = document.createElement("span");
+	el.append(arcadeStats);
+
+	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.arcade);
+	const slavesLength = slaves.length;
+	let cockmilked = 0, milked = 0, milkProfits = 0, profits = 0, boobsImplanted = 0, prostatesImplanted = 0, vasectomiesUndone = 0, mSlave, bSlave, pSlave, cSlave, milkResults, growth;
+
+	// Statistics gathering
+	V.facility = V.facility || {};
+	V.facility.arcade = initFacilityStatistics(V.facility.arcade);
+	const arcadeNameCaps = capFirstChar(V.arcadeName);
+
+	if (slavesLength > 1) {
+		App.UI.DOM.appendNewElement("p", el, `There are ${slavesLength} inmates confined in ${V.arcadeName}.`, ["bold", "indent"]);
+	} else {
+		App.UI.DOM.appendNewElement("p", el, `There is one inmate confined in ${V.arcadeName}.`, ["bold", "indent"]);
+	}
+	r = [];
+	if (V.arcologies[0].FSDegradationist > 20) {
+		if (V.arcologies[0].FSDegradationistLaw === 1) {
+			r.push(`The tenants located near the arcade consider the facility a normal part of their surroundings, including the crowd of menial slaves waiting their turn spills out into the hallway, 24 hours a day.`);
+		} else {
+			r.push(`The tenants located near the arcade consider the facility a normal part of their surroundings.`);
+		}
+	} else if (V.arcologies[0].FSPaternalist > 20) {
+		r.push(`The tenants located near the arcade consider it an <span class="red">intolerable</span> establishment.`);
+	}
+	App.Events.addNode(el, r, "div", "indent");
+	if (V.arcadeDecoration !== "standard") {
+		App.Events.addNode(el, [`${arcadeNameCaps}'s customers enjoy <span class="green">getting off in ${V.arcadeDecoration} surroundings.</span>`], "p", "indent");
+	}
+
+	for (const slave of slaves) {
+		const {He} = getPronouns(slave);
+		r = [];
+		/* for the included passages */
+
+		/* Perform facility based rule changes */
+		slave.clothes = "no clothing";
+		/* Health */
+		if (V.arcadeUpgradeHealth === 2 && slave.health.condition < 40) {
+			improveCondition(slave, 2);
+		} else if (slave.health.condition > 20) {
+			healthDamage(slave, 5);
+		} else if (V.arcadeUpgradeHealth === 1 && slave.health.condition < -30) {
+			improveCondition(slave, 2);
+		} else if (slave.health.condition > -50) {
+			healthDamage(slave, 3);
+		} else if (slave.health.condition > -90) {
+			healthDamage(slave, 1);
+		}
+		/* Curatives */
+		if (V.arcadeUpgradeHealth === 2) {
+			slave.curatives = 2;
+		} else if (V.arcadeUpgradeHealth === 1) {
+			slave.curatives = 1;
+		}
+		/* Stimulation or lack thereof trust/devotion effect */
+		if (V.arcadeUpgradeInjectors > 0) {
+			if (V.arcadeUpgradeInjectors === 2) {
+				slave.aphrodisiacs = 2;
+			}
+			slave.devotion -= 5;
+			slave.trust -= 10;
+		} else {
+			slave.trust -= 5;
+		}
+		/* Muscles */
+		if (slave.muscles > -100) {
+			slave.muscles--;
+		}
+		const oldCash = V.cash;
+		if (V.showEWD !== 0) {
+			const {He} = getPronouns(slave);
+			const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
+			r = [];
+			r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
+			if (slave.choosesOwnAssignment === 2) {
+				r.push(App.SlaveAssignment.choosesOwnJob(slave));
+			} else {
+				r.push(`is confined in ${V.arcadeName}.`);
+			}
+			App.Events.addNode(slaveEntry, r);
+
+			App.Events.addNode(
+				slaveEntry,
+				[
+					He,
+					App.SlaveAssignment.workAGloryHole(slave),
+				],
+				"div",
+				"indent"
+			);
+		} else {
+			// discard return values silently
+			App.SlaveAssignment.choosesOwnJob(slave);
+			App.SlaveAssignment.workAGloryHole(slave);
+		}
+		profits += V.cash - oldCash;
+
+		if (V.arcadeUpgradeCollectors > 0) {
+			if (slave.vasectomy === 1) {
+				slave.vasectomy = 0;
+				vasectomiesUndone++;
+				cashX(forceNeg(V.surgeryCost), "slaveSurgery", slave);
+				surgeryDamage(slave, 10);
+			} else if (slave.lactation < 2) {
+				slave.lactation = 2;
+				boobsImplanted++;
+				bSlave = slave;
+				cashX(forceNeg(V.surgeryCost), "slaveSurgery", slave);
+				surgeryDamage(slave, 10);
+			} else if (slave.prostate === 1) {
+				slave.prostate = 2;
+				prostatesImplanted++;
+				pSlave = slave;
+				cashX(forceNeg(V.surgeryCost), "slaveSurgery", slave);
+				surgeryDamage(slave, 10);
+			} else if (slave.lactation > 0 || slave.balls > 0) {
+				milkResults = App.SlaveAssignment.getMilked(slave, 1.0);
+				if (V.showEWD !== 0) {
+					r = [];
+					r.push(App.UI.DOM.makeElement("div", `${He} ${milkResults.text}`, "indent"));
+					App.Events.addNode(el, r, "div", "indent");
+				}
+				milkProfits += milkResults.cash;
+				growth = 0;
+				const gigantomastiaMod = slave.geneticQuirks.gigantomastia === 2 ? (slave.geneticQuirks.macromastia === 2 ? 3 : 2) : 1;
+				if (slave.boobs < 2000) {
+					growth = 100;
+				} else if (slave.boobs < 5000 * gigantomastiaMod) {
+					growth = 50;
+				} else if (slave.boobs < 10000 * gigantomastiaMod) {
+					growth = 25;
+				}
+				if (slave.geneMods.NCS === 1) {
+					/*
+					** NCS will allow some growth for Arcade milking, but not as much as the Dairy.
+					*/
+					growth = Math.trunc(growth / 3.5);
+				}
+				slave.boobs += growth;
+				if (
+					(slave.balls > 0) &&
+					(slave.balls < 10) &&
+					(random(1, 100) > (40 + (10 * (slave.balls + (2 * slave.geneMods.NCS)))))
+				) {
+					slave.balls++;
+				}
+				if (
+					(slave.dick > 0) &&
+					(slave.dick < 10) &&
+					(random(1, 100) > (40 + (10 * slave.dick + (2 * slave.geneMods.NCS))))
+				) {
+					slave.dick++;
+				}
+				if (slave.lactation > 0) {
+					milked++;
+					mSlave = slave;
+				}
+				if (slave.balls > 0) {
+					cockmilked++;
+					cSlave = slave;
+				}
+			}
+		}
+		if (slave.inflation > 0) {
+			deflate(slave);
+		}
+		if (V.showEWD !== 0) {
+			el.append(App.SlaveAssignment.standardSlaveReport(slave, false));
+		} else {
+			App.SlaveAssignment.standardSlaveReport(slave, true);
+		}
+	}
+
+	if (slavesLength + V.fuckdolls > 0) {
+		r = [];
+		if (milked === 1) {
+			const {his} = getPronouns(mSlave);
+			r.push(`One of them is lactating and spends ${his} time in ${V.arcadeName} being simultaneously milked and fucked.`);
+		} else if (milked > 1) {
+			r.push(`${capFirstChar(num(milked))} of them are lactating and spend their time in ${V.arcadeName} being simultaneously milked and fucked.`);
+		}
+
+		if (vasectomiesUndone) {
+			if (vasectomiesUndone === 1) {
+				r.push(`One`);
+			} else {
+				r.push(capFirstChar(num(vasectomiesUndone)));
+			}
+			r.push(`of them had severed vas deferens, so they were reattached to allow sperm through, costing <span class="red">${cashFormat(V.surgeryCost * vasectomiesUndone)}.</span>`);
+		}
+		if (boobsImplanted) {
+			if (boobsImplanted === 1) {
+				const {he} = getPronouns(bSlave);
+				r.push(`One of them was not lactating, so ${he} was`);
+			} else {
+				r.push(`${capFirstChar(num(boobsImplanted))} of them were not lactating, so they were`);
+			}
+			r.push(`implanted with long-acting lactation inducing drugs, costing <span class="red">${cashFormat(V.surgeryCost * boobsImplanted)}.</span>`);
+		}
+		if (prostatesImplanted) {
+			if (prostatesImplanted === 1) {
+				const {he} = getPronouns(pSlave);
+				r.push(`One of them was not producing the maximum possible amount of precum, so ${he} was`);
+			} else {
+				r.push(`${capFirstChar(num(prostatesImplanted))} of them were not producing the maximum possible amount of precum, so they were`);
+			}
+			r.push(`implanted with long-acting prostate stimulation drugs, costing <span class="red">${cashFormat(V.surgeryCost * prostatesImplanted)}.</span>`);
+		}
+		if (cockmilked === 1) {
+			const {he} = getPronouns(cSlave);
+			r.push(`One of them retains testicles and is brutally cockmilked as ${he} is used.`);
+		} else if (cockmilked > 1) {
+			r.push(`${capFirstChar(num(cockmilked))} of them retain testicles and are brutally cockmilked as they are used.`);
+		}
+
+		r.push(`The arcade makes you`);
+		if (V.policies.publicFuckdolls === 0) {
+			r.push(`<span class="yellowgreen">${cashFormat(profits)}</span> from selling the inmates' holes`);
+		} else {
+			r.push(`<span class="green">more reputable</span> from freely providing the inmates' holes`);
+		}
+		if (V.arcadeUpgradeCollectors > 0 && V.policies.publicFuckdolls === 0) {
+			r.push(`and`);
+		}
+		if (V.arcadeUpgradeCollectors > 0) {
+			r.push(`<span class="yellowgreen">${cashFormat(milkProfits)}</span> from selling the fluids they produced`);
+		}
+		r.push(`this week.`);
+		if (V.arcologies[0].FSPaternalist > 20) {
+			repX(forceNeg(Math.trunc(profits / 20)), "arcade");
+		}
+		App.Events.addNode(el, r, "div", "indent");
+	}
+
+	if (V.arcadeUpgradeFuckdolls > 1) {
+		const fuckdolls = slaves.filter(s => fuckdollQualifier(s));
+		if (fuckdolls.length > 0) {
+			// ascending sort by sexAmount...least popular slaves are converted first
+			fuckdolls.sort((a, b) => b.sexAmount - a.sexAmount);
+			for (const fuckdoll of fuckdolls) {
+				const {he} = getPronouns(fuckdoll);
+				App.UI.DOM.appendNewElement("div", el, `${fuckdoll.slaveName} is low-quality merchandise, so ${he} has been converted into a Fuckdoll.`, "indent");
+				removeSlave(fuckdoll);
+				V.fuckdolls++;
+				if (V.arcadeUpgradeFuckdolls === 2) {
+					break; // convert at most one per week on this setting
+				}
+			}
+		} else {
+			App.UI.DOM.appendNewElement("div", el, `No slaves have failed quality inspection for Fuckdoll conversion. ${arcadeNameCaps} will remain overcrowded this week.`, "indent");
+		}
+	}
+
+	if (slavesLength > 0) {
+		// Record statistics gathering
+		let b = V.facility.arcade;
+		b.whoreIncome = 0;
+		b.customers = 0;
+		b.whoreCosts = 0;
+		b.rep = 0;
+		for (let si of b.income.values()) {
+			b.whoreIncome += si.income;
+			b.customers += si.customers;
+			b.whoreCosts += si.cost;
+			b.rep += si.rep;
+		}
+		b.maintenance = V.arcade * V.facilityCost * (0.05 + 0.02 * V.arcadeUpgradeInjectors + 0.05 * V.arcadeUpgradeCollectors);
+		b.totalIncome = b.whoreIncome;
+		b.totalExpenses = b.whoreCosts + b.maintenance;
+		b.profit = b.totalIncome - b.totalExpenses;
+
+
+		// Arcade stats
+		el.append(App.Facilities.Arcade.Stats(false));
+		arcadeStats.append(App.Facilities.Arcade.Stats(true));
+	}
+	return el;
+
+	/**
+	 *
+	 * @param {App.Entity.SlaveState} slave
+	 */
+	function fuckdollQualifier(slave) {
+		if (slave.sentence === 0 && slave.fuckdoll === 0 && slave.fetish === "mindbroken") {
+			if (slave.physicalAge > 35) {
+				return true;
+			} else if (slave.vagina >= 4 || slave.anus >= 4) {
+				return true;
+			}
+		}
+	}
+};
diff --git a/src/endWeek/brothelReport.js b/src/endWeek/reports/brothelReport.js
similarity index 93%
rename from src/endWeek/brothelReport.js
rename to src/endWeek/reports/brothelReport.js
index a7df34a613a6ae9de947f566c4052414ed32a003..cf79c57d336cd6dae081c13879a78750954e9229 100644
--- a/src/endWeek/brothelReport.js
+++ b/src/endWeek/reports/brothelReport.js
@@ -1,4 +1,4 @@
-globalThis.brothelReport = function() {
+App.EndWeek.brothelReport = function() {
 	const el = document.createElement("p");
 	let r;
 
@@ -90,7 +90,7 @@ globalThis.brothelReport = function() {
 			if (S.Madam.actualAge > 35) {
 				r.push(`${His} age and experience also contribute.`);
 			}
-			if (setup.madamCareers.includes(S.Madam.career)) {
+			if (App.Data.Careers.Leader.madam.includes(S.Madam.career)) {
 				r.push(`${He} has experience from ${his} life before ${he} was a slave that helps ${him} in the seedy business of selling other people's bodies for sex.`);
 			} else if (S.Madam.skill.madam >= V.masteredXP) {
 				r.push(`${He} has experience from working for you that helps ${him} in the seedy business of selling other people's bodies for sex.`);
@@ -103,11 +103,11 @@ globalThis.brothelReport = function() {
 			}
 			App.Events.addParagraph(el, r);
 
+			r = [];
 			for (const slave of slaves) {
 				const {
 					he2, him2, his2
 				} = getPronouns(slave).appendSuffix('2');
-				r = [];
 
 				if (S.Madam.rivalryTarget === slave.ID) {
 					r.push(`${He} forces ${his} ${rivalryTerm(S.Madam)}, to service all the men in the brothel.`);
@@ -208,11 +208,11 @@ globalThis.brothelReport = function() {
 						}
 					}
 				}
-				App.Events.addNode(el, r);
 			}
+			App.Events.addNode(el, r);
 
 			if ((SL + V.brothelSlavesGettingHelp < 10) && V.MadamNoSex !== 1 && !slaveResting(S.Madam)) {
-				let oldCash = V.cash;
+				const oldCash = V.cash;
 				if (V.showEWD !== 0) {
 					App.Events.addParagraph(
 						el,
@@ -233,14 +233,11 @@ globalThis.brothelReport = function() {
 					]
 				);
 				profits += V.cash - oldCash;
-				oldCash = V.cash;
 			}
 		}
-		return r.join(" ");
 	}
 
-	const madamEffects = App.UI.DOM.appendNewElement("p", el, '', "indent");
-	$(madamEffects).append(madamText());
+	madamText();
 
 	if (SL > 0) {
 		const whoreNumber = document.createElement("p");
@@ -256,20 +253,19 @@ globalThis.brothelReport = function() {
 
 	if (S.Madam) {
 		const slave = S.Madam;
+		tired(slave);
 		if (V.showEWD !== 0) {
 			const madamEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", madamEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			madamEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(madamEntry, slave);
+			madamEntry.append(App.UI.favoriteToggle(slave), " ");
 			App.Events.addNode(
 				madamEntry,
 				[
 					App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"),
-					`is serving as the Madam.`,
-					App.SlaveAssignment.standardSlaveReport(slave, false),
+					`is serving as the Madam.`
 				]
 			);
+			madamEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -332,10 +328,8 @@ globalThis.brothelReport = function() {
 			if (V.showEWD !== 0) {
 				const {He} = getPronouns(slave);
 				const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-				if (V.seeImages && V.seeReportImages) {
-					App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-				}
-				slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+				App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+				slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 				r = [];
 				r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
 				if (slave.choosesOwnAssignment === 2) {
@@ -343,18 +337,18 @@ globalThis.brothelReport = function() {
 				} else {
 					r.push(`is working out of ${V.brothelName}.`);
 				}
-				App.Events.addNode(slaveEntry, r, "div");
+				App.Events.addNode(slaveEntry, r);
 
 				App.Events.addNode(
 					slaveEntry,
 					[
 						He,
 						App.SlaveAssignment.whore(slave),
-						App.SlaveAssignment.standardSlaveReport(slave, false)
 					],
 					"div",
-					"indented"
+					"indent"
 				);
+				slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 			} else {
 				// discard return values silently
 				App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/endWeek/reports/cellblockReport.js b/src/endWeek/reports/cellblockReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..59cb643ca983ff9bd89c214f37f78c67ad1b7bc6
--- /dev/null
+++ b/src/endWeek/reports/cellblockReport.js
@@ -0,0 +1,344 @@
+/**
+ * @returns {DocumentFragment}
+ */
+App.EndWeek.cellblockReport = function() {
+	const el = new DocumentFragment();
+	let r;
+
+	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.cellblock);
+	let brokenSlaves = 0, idleBonus = 0, softenedQuirks = 0, trustMalus = 0, FLsFetish = 0;
+	let devBonus = (V.cellblockDecoration !== "standard") ? 1 : 0;
+	let confinedResults;
+
+	const cellblockNameCaps = capFirstChar(V.cellblockName);
+	V.flSex = App.EndWeek.getFLSex(App.Entity.facilities.cellblock);/* FIXME: should be local, passed as a parameter to saRules */
+
+	if (S.Wardeness) {
+		const {
+			He, His,
+			he, his, him, wife
+		} = getPronouns(S.Wardeness);
+		r = [];
+
+		if (S.Wardeness.health.condition < -80) {
+			improveCondition(S.Wardeness, 20);
+		} else if (S.Wardeness.health.condition < -40) {
+			improveCondition(S.Wardeness, 15);
+		} else if (S.Wardeness.health.condition < 0) {
+			improveCondition(S.Wardeness, 10);
+		} else if (S.Wardeness.health.condition < 90) {
+			improveCondition(S.Wardeness, 7);
+		}
+		if (S.Wardeness.devotion <= 60) {
+			S.Wardeness.devotion += 2;
+		}
+		S.Wardeness.devotion += devBonus;
+		if (S.Wardeness.trust < 60) {
+			S.Wardeness.trust += 3;
+		}
+		if (S.Wardeness.rules.living !== "luxurious") {
+			S.Wardeness.rules.living = "luxurious";
+		}
+		if (S.Wardeness.rules.rest !== "restrictive") {
+			S.Wardeness.rules.rest = "restrictive";
+		}
+		if (S.Wardeness.fetishStrength <= 95) {
+			if (S.Wardeness.fetish !== "sadist") {
+				if (fetishChangeChance(S.Wardeness) > random(0, 100)) {
+					FLsFetish = 1;
+					S.Wardeness.fetishKnown = 1;
+					S.Wardeness.fetish = "sadist";
+				}
+			} else if (S.Wardeness.fetishKnown === 0) {
+				FLsFetish = 1;
+				S.Wardeness.fetishKnown = 1;
+			} else {
+				FLsFetish = 2;
+				S.Wardeness.fetishStrength += 4;
+			}
+		}
+		if (S.Wardeness.energy > 95 || S.Wardeness.fetish === "sadist") {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+		}
+		r.push(`${SlaveFullName(S.Wardeness)} is serving as the Wardeness.`);
+		if (S.Wardeness.relationship === -3 && S.Wardeness.devotion > 50) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`As your ${wife}, ${he} tries ${his} best to break the disobedient slaves to your will.`);
+		}
+		if (FLsFetish === 1) {
+			r.push(`One day ${he} demands obedience. The next day ${he} strikes a slave when it isn't given. The next, ${he} seems more excited than embarrassed when beating a prisoner. Soon, ${he}'s looking for an excuse to punish. ${He}'s <span class="pink">become more of a sadist.</span>`);
+		} else if (FLsFetish === 2) {
+			r.push(`Being not only allowed but encouraged to get off while punishing prisoners <span class="lightsalmon">deepens ${his} sadism.</span>`);
+		}
+		if (App.Data.Careers.Leader.wardeness.includes(S.Wardeness.career)) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`${He} has experience with detecting security issues and grinding down potential miscreants from ${his} life before ${he} was a slave, making ${him} more effective.`);
+		} else if (S.Wardeness.skill.wardeness >= V.masteredXP) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`${He} has experience with detecting security issues and grinding down potential miscreants from working for you, making ${him} more effective.`);
+		} else {
+			S.Wardeness.skill.wardeness += random(1, Math.ceil((S.Wardeness.intelligence + S.Wardeness.intelligenceImplant) / 15) + 8);
+		}
+		if (S.Wardeness.fetish === "sadist") {
+			r.push(`${He} uses the prisoners to gratify ${his} sadism, terrifying them and quickly breaking their resistance.`);
+		} else if (S.Wardeness.energy > 95) {
+			r.push(`${He} is so sexually depraved that ${he} constantly molests the prisoners, breaking them to a life of servitude.`);
+		}
+		if (S.Wardeness.sexualFlaw === "malicious" || S.Wardeness.sexualFlaw === "abusive") {
+			devBonus++;
+			trustMalus += 2;
+			idleBonus += 2;
+			r.push(`Sexual abuse of prisoners has become an end for ${him}, not a means. ${He} inspires ${his} prisoners to do everything possible to qualify to get away from ${him}, even as ${he} slowly grinds away their interest in sex to do it.`);
+		} else if (S.Wardeness.sexualQuirk === "caring") {
+			devBonus--;
+			trustMalus--;
+			idleBonus--;
+			r.push(`${His} kindness sometimes shows through ${his} tough facade, letting prisoners get off easier than they should.`);
+		}
+		if (S.Wardeness.dick > 2 && canPenetrate(S.Wardeness)) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`${His} molestation of the prisoners is more varied and effective because ${he} has a dick to fuck them with.`);
+		}
+		if (S.Wardeness.muscles > 35) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`${He} is strong enough to restrain anyone that tries anything with ${his} own hands.`);
+		}
+		if (S.Wardeness.devotion > 95) {
+			devBonus++;
+			trustMalus++;
+			idleBonus++;
+			r.push(`${His} devotion to you is so absolute that ${he} sees breaking bitches for your service as a noble calling.`);
+		}
+		App.Events.addNode(el, r, "div", "indent");
+		for (const slave of slaves) {
+			r = [];
+			if (S.Wardeness.rivalryTarget === slave.ID) {
+				r.push(`${He} greatly enjoys breaking ${his} ${rivalryTerm(S.Wardeness)}, ${slave.slaveName}'s will.`);
+				slave.devotion++;
+				slave.trust -= 3;
+				if (random(1, 100) > 30) {
+					S.Wardeness.rivalry++;
+					slave.rivalry++;
+				}
+			} else if (S.Wardeness.relationshipTarget === slave.ID) {
+				r.push(`${He} hates having to break ${his} ${relationshipTerm(S.Wardeness)}, ${slave.slaveName}, but ${his} devotion to you wins out in the end.`);
+				slave.devotion++;
+				slave.trust -= 3;
+				if (random(1, 100) >= 50) {
+					r.push(`${His} and ${slave.slaveName}'s relationship has been shattered by these events.`);
+					S.Wardeness.relationship = 0;
+					S.Wardeness.relationshipTarget = 0;
+					slave.relationship = 0;
+					slave.relationshipTarget = 0;
+				}
+			} else if (areRelated(S.Wardeness, slave)) {
+				const {he2, his2} = getPronouns(slave).appendSuffix("2");
+				r.push(`${He} shows ${his} ${relativeTerm(S.Wardeness, slave)} ${slave.slaveName} no mercy, making sure ${he2} understands ${his2} place.`);
+				slave.devotion++;
+				slave.trust--;
+			}
+			App.Events.addNode(el, r, "div", "indent");
+		}
+		if (slaves.length < V.cellblock && !slaveResting(S.Wardeness)) {
+			const seed = random(1, 10) + ((V.cellblock - slaves.length) * (random(150, 170) + (idleBonus * 10)));
+			cashX(seed, "cellblock", S.Wardeness);
+			App.Events.addNode(el, [`Since ${he} doesn't have enough prisoners to manage to keep ${him} busy, ${he} works on citizens' slaves, earning <span class="yellowgreen"> ${cashFormat(seed)}.</span>`], "div", "indent");
+		}
+	}
+
+	if (slaves.length > 0) {
+		if (slaves.length === 1) {
+			App.UI.DOM.appendNewElement("div", el, `One slave is being confined in ${V.cellblockName} until they are willing to obey.`, "indent");
+		} else {
+			App.UI.DOM.appendNewElement("div", el, `${slaves.length} slaves are being confined in ${V.cellblockName} until they are willing to obey.`, "indent");
+		}
+	}
+
+
+	if (S.Wardeness) {
+		const slave = S.Wardeness;
+		tired(slave);
+		/* apply following SA passages to facility leader */
+		if (V.showEWD !== 0) {
+			const wardenessEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
+			App.SlaveAssignment.appendSlaveArt(wardenessEntry, slave);
+			wardenessEntry.append(App.UI.favoriteToggle(slave), " ");
+			App.Events.addNode(
+				wardenessEntry,
+				[
+					App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"),
+					`is serving as the Wardeness in ${V.cellblockName}`,
+				]
+			);
+			wardenessEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
+		} else {
+			App.SlaveAssignment.standardSlaveReport(slave, true);
+		}
+	}
+
+	for (const slave of slaves) {
+		const {He} = getPronouns(slave);
+		if (slave.devotion <= 20) {
+			if (slave.trust >= -20) {
+				if (
+					(slave.hears === -1 && slave.earwear !== "hearing aids") ||
+					(slave.hears === 0 && slave.earwear === "muffling ear plugs") ||
+					(slave.hears === -2)
+				) {
+					slave.devotion -= 2;
+					slave.trust -= 2;
+				} else {
+					slave.devotion -= 4;
+					slave.trust -= 4;
+				}
+			} else {
+				slave.devotion++;
+			}
+		}
+		switch (V.cellblockDecoration) {
+			case "Paternalist":
+				slave.rules.living = "normal";
+				if (slave.inflation > 0) {
+					deflate(slave);
+				}
+				break;
+			case "Pastoralist":
+				slave.rules.living = "spare";
+				if (slave.inflation === 0) {
+					if (slave.pregKnown === 0 && slave.bellyImplant < 1500) {
+						slave.inflation = 2;
+						slave.inflationType = "milk";
+						slave.inflationMethod = 1;
+					} else {
+						slave.inflation = 1;
+						slave.inflationType = "milk";
+						slave.inflationMethod = 1;
+					}
+					SetBellySize(slave);
+				}
+				break;
+			case "Hedonistic":
+				slave.rules.living = "spare";
+				if (slave.weight < 200) {
+					if (slave.weightDirection === 1) {
+						slave.weight += 5;
+					} else if (slave.weightDirection === -1) {
+						slave.weight += 1;
+					} else {
+						slave.weight += 3;
+					}
+				}
+				if (slave.muscles > -100) {
+					slave.muscles -= 2;
+				}
+				if (slave.inflation === 0) {
+					if (slave.pregKnown === 0 && slave.bellyImplant < 1500) {
+						slave.inflation = 3;
+						slave.inflationType = "food";
+						slave.inflationMethod = 1;
+					} else {
+						slave.inflation = 1;
+						slave.inflationType = "food";
+						slave.inflationMethod = 1;
+					}
+					SetBellySize(slave);
+				}
+				break;
+			default:
+				slave.rules.living = "spare";
+				if (slave.inflation > 0) {
+					deflate(slave);
+				}
+		}
+		if (V.cellblockUpgrade === 1) {
+			if (slave.behavioralFlaw !== "none" && slave.behavioralQuirk === "none") {
+				SoftenBehavioralFlaw(slave);
+				slave.devotion -= 10;
+				softenedQuirks++;
+			} else if (slave.sexualFlaw !== "none" && slave.sexualQuirk === "none") {
+				SoftenSexualFlaw(slave);
+				slave.devotion -= 10;
+				softenedQuirks++;
+			}
+		}
+		slave.devotion += devBonus;
+		slave.trust -= trustMalus;
+		if (S.Wardeness && S.Wardeness.sexualFlaw === "malicious" && slave.energy >= 2) {
+			slave.energy -= 2;
+		}
+		if (slave.health.condition < -80) {
+			improveCondition(slave, 20);
+		} else if (slave.health.condition < -40) {
+			improveCondition(slave, 15);
+		} else if (slave.health.condition < 0) {
+			improveCondition(slave, 10);
+		} else if (slave.health.condition < 40) {
+			improveCondition(slave, 7);
+		} else if (slave.health.condition < 100) {
+			improveCondition(slave, 3);
+		}
+
+		if (V.showEWD !== 0) {
+			const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
+			r = [];
+			r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
+			if (slave.choosesOwnAssignment === 2) {
+				r.push(`${App.SlaveAssignment.choosesOwnJob(slave)}`);
+			} else {
+				r.push(`is confined in ${V.cellblockName}.`);
+			}
+			App.Events.addNode(slaveEntry, r);
+
+			confinedResults = App.SlaveAssignment.stayConfined(slave);
+			App.Events.addNode(slaveEntry, [He, confinedResults.text], "div", "indent");
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
+		} else {
+			// discard return values silently
+			App.SlaveAssignment.choosesOwnJob(slave);
+			confinedResults = App.SlaveAssignment.stayConfined(slave);
+			App.SlaveAssignment.standardSlaveReport(slave, true);
+		}
+		if (confinedResults.broken) {
+			brokenSlaves++;
+		}
+	}
+	if (softenedQuirks || brokenSlaves) {
+		r = [];
+		if (softenedQuirks > 0) {
+			r.push(`${cellblockNameCaps}'s advanced compliance systems successfully softened`);
+			if (softenedQuirks === 1) {
+				r.push(`one slave's mental flaw into an <span class="green">appealing quirk,</span> though the constant correction caused them <span class="mediumorchid">considerable anguish.</span>`);
+			} else {
+				r.push(`${softenedQuirks} slaves' mental flaws into <span class="green">appealing quirks,</span> though the constant correction caused them <span class="mediumorchid">considerable anguish.</span>`);
+			}
+		}
+		if (brokenSlaves > 0) {
+			if (brokenSlaves === 1) {
+				r.push(`One slave is now willing to <span class="hotpink">do as they're told</span> and has been released.`);
+			} else {
+				r.push(`${brokenSlaves} slaves are now willing to <span class="hotpink">do as they're told</span> and have been released.`);
+			}
+			if (V.cellblockDecoration !== "standard") {
+				App.Events.addNode(el, r, "p", "indent");
+				r = [];
+				r.push(`${cellblockNameCaps}'s ${V.cellblockDecoration} atmosphere <span class="hotpink">had an impact on them while they were</span> imprisoned.`);
+			}
+		}
+		App.Events.addNode(el, r, "p", "indent");
+	}
+	return el;
+};
diff --git a/src/endWeek/clinicReport.js b/src/endWeek/reports/clinicReport.js
similarity index 96%
rename from src/endWeek/clinicReport.js
rename to src/endWeek/reports/clinicReport.js
index b741896901b99e518f6093c35ed09ae52080da62..1073e82bd91a11b3d3d83b25b25235fdbf49a94e 100644
--- a/src/endWeek/clinicReport.js
+++ b/src/endWeek/reports/clinicReport.js
@@ -56,7 +56,7 @@ App.EndWeek.clinicReport = function() {
 			} else if (FLsFetish === 2) {
 				r.push(`Every new patient in the clinic is a new target for ${his} authority. <span class="fetish inc">${He} becomes more dominant.</span>`);
 			}
-			if (setup.nurseCareers.includes(S.Nurse.career)) {
+			if (App.Data.Careers.Leader.nurse.includes(S.Nurse.career)) {
 				r.push(`${He} has experience with medicine from ${his} life before ${he} was a slave, and can often recognize conditions before even the medical scanners can.`);
 				idleBonus++;
 				healthBonus++;
@@ -240,15 +240,14 @@ App.EndWeek.clinicReport = function() {
 
 	if (S.Nurse) {
 		const slave = S.Nurse;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const nurseEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", nurseEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			nurseEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(nurseEntry, slave);
+			nurseEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(nurseEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> is serving as the clinical nurse.`);
-			$(nurseEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			nurseEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -375,10 +374,8 @@ App.EndWeek.clinicReport = function() {
 
 		if (V.showEWD !== 0) {
 			const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(slaveEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> `);
 			if (slave.choosesOwnAssignment === 2) {
 				$(slaveEntry).append(App.SlaveAssignment.choosesOwnJob(slave));
@@ -388,7 +385,7 @@ App.EndWeek.clinicReport = function() {
 			const patientContent = App.UI.DOM.appendNewElement("div", slaveEntry, '', "indent");
 			$(patientContent).append(`${He} ${App.SlaveAssignment.rest(slave)} `);
 			$(patientContent).append(remainReason);
-			$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/endWeek/reports/clubReport.js b/src/endWeek/reports/clubReport.js
index de98b3b10aa2a49b8c96c8edccb3b48456b25b6d..65f83a2e2a94bd4fbbdd018cb73b9b0e7f861364 100644
--- a/src/endWeek/reports/clubReport.js
+++ b/src/endWeek/reports/clubReport.js
@@ -4,12 +4,9 @@
 App.EndWeek.clubReport = function() {
 	const el = new DocumentFragment();
 	let r;
-	let he, him, his, He, His, wife, himself;
 
 	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.club);
-	const DL = slaves.length;
 	V.legendaryEntertainerID = 0;
-	let FLsFetish = 0;
 	V.legendaryWombID = 0;
 
 	// Statistics gathering; income is rep boosts in numbers, and profit will be rep per cash unit, or cash unit per rep
@@ -19,6 +16,7 @@ App.EndWeek.clubReport = function() {
 	el.append(clubStats);
 
 	if (S.DJ) {
+		let FLsFetish = 0;
 		r = [];
 		if (S.DJ.health.condition < -80) {
 			improveCondition(S.DJ, 20);
@@ -58,16 +56,16 @@ App.EndWeek.clubReport = function() {
 		}
 		/* Make sure we have registered living expenses as for any other slave */
 		getSlaveStatisticData(S.DJ, V.facility.club);
-		({
-			he, him, his, He, His, wife
-		} = getPronouns(S.DJ));
+		const {
+			he, him, his, He, His, wife, himself
+		} = getPronouns(S.DJ);
 		r.push(`${SlaveFullName(S.DJ)} is performing as the DJ.`);
 		if (S.DJ.relationship === -3 && S.DJ.devotion > 50) {
 			r.push(`${He} tries ${his} best to be your energetic, cheerful ${wife}.`);
 		}
 		if (FLsFetish === 1) {
 			r.push(`${He}'s expected to be the innovative, beautiful DJ spinning beats one minute, and come out of ${his} booth to grind on the floor the next; ${he} enjoys the interplay, and finds greater <span class="lightcoral">pleasure in exhibitionism.</span>`);
-		} else if ((FLsFetish === 2)) {
+		} else if (FLsFetish === 2) {
 			r.push(`Every day ${he} gets to enjoy hundreds of stares on ${his} skin, and <span class="lightsalmon">becomes more of an exhibitionist.</span>`);
 		}
 		if (getBestVision(S.DJ) === 0) {
@@ -93,7 +91,7 @@ App.EndWeek.clubReport = function() {
 		if (S.DJ.face > 95) {
 			r.push(`${His} great beauty is a further draw, even when ${he}'s in ${his} DJ booth, but especially when ${he} comes out to dance.`);
 		}
-		if (setup.DJCareers.includes(S.DJ.career)) {
+		if (App.Data.Careers.Leader.DJ.includes(S.DJ.career)) {
 			r.push(`${He} has musical experience from ${his} life before ${he} was a slave, a grounding that gives ${his} tracks actual depth.`);
 		} else if (S.DJ.skill.DJ >= V.masteredXP) {
 			r.push(`${He} has musical experience from working for you, giving ${his} tracks actual depth.`);
@@ -101,7 +99,7 @@ App.EndWeek.clubReport = function() {
 			S.DJ.skill.DJ += random(1, Math.ceil((S.DJ.intelligence + S.DJ.intelligenceImplant) / 15) + 8);
 		}
 		App.Events.addNode(el, r, "p", "indent");
-		if (DL + V.clubSlavesGettingHelp < 10 && V.DJnoSex !== 1 && !slaveResting(S.DJ)) {
+		if (slaves.length + V.clubSlavesGettingHelp < 10 && V.DJnoSex !== 1 && !slaveResting(S.DJ)) {
 			if (V.legendaryEntertainerID === 0 && S.DJ.prestige === 0 && S.DJ.skill.entertainment >= 100 && S.DJ.devotion > 50) {
 				V.legendaryEntertainerID = S.DJ.ID;
 			}
@@ -127,10 +125,10 @@ App.EndWeek.clubReport = function() {
 		}
 	}
 
-	if (DL > 0) {
+	if (slaves.length > 0) {
 		r = [];
-		if (DL !== 1) {
-			r.push(App.UI.DOM.makeElement("span", `The ${DL} slaves pleasing citizens in ${V.clubName}`, "bold"));
+		if (slaves.length !== 1) {
+			r.push(App.UI.DOM.makeElement("span", `The ${slaves.length} slaves pleasing citizens in ${V.clubName}`, "bold"));
 		} else {
 			r.push(App.UI.DOM.makeElement("span", `The one slave pleasing citizens in ${V.clubName}`, "bold"));
 		}
@@ -140,31 +138,27 @@ App.EndWeek.clubReport = function() {
 
 	if (S.DJ) {
 		const slave = S.DJ;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const DJEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", DJEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			DJEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(DJEntry, slave);
+			DJEntry.append(App.UI.favoriteToggle(slave), " ");
 			App.Events.addNode(
 				DJEntry,
 				[
 					App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"),
 					`is performing as the DJ in ${V.clubName}.`,
-					App.SlaveAssignment.standardSlaveReport(slave, false),
 				]
 			);
+			DJEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
 	}
 
-	if (DL > 0) {
+	if (slaves.length > 0) {
 		for (const slave of slaves) {
-			({
-				he, him, his, He, His, wife
-			} = getPronouns(slave));
 			if (V.legendaryEntertainerID === 0 && slave.prestige === 0 && slave.skill.entertainment >= 100 && slave.devotion > 50) {
 				V.legendaryEntertainerID = slave.ID;
 			}
@@ -200,10 +194,8 @@ App.EndWeek.clubReport = function() {
 
 			if (V.showEWD !== 0) {
 				const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-				if (V.seeImages && V.seeReportImages) {
-					App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-				}
-				slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+				App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+				slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 				r = [];
 				r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
 				if (slave.choosesOwnAssignment === 2) {
@@ -211,18 +203,19 @@ App.EndWeek.clubReport = function() {
 				} else {
 					r.push(`is serving in ${V.clubName}.`);
 				}
-				App.Events.addNode(slaveEntry, r, "div");
+				App.Events.addNode(slaveEntry, r);
 
+				const {He} = getPronouns(slave);
 				App.Events.addNode(
 					slaveEntry,
 					[
 						He,
 						App.SlaveAssignment.serveThePublic(slave),
-						App.SlaveAssignment.standardSlaveReport(slave, false)
 					],
 					"div",
-					"indented"
+					"indent"
 				);
+				slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 			} else {
 				// discard return values silently
 				App.SlaveAssignment.choosesOwnJob(slave);
@@ -231,10 +224,10 @@ App.EndWeek.clubReport = function() {
 			}
 		}
 
-		App.Events.addNode(el, [App.Ads.report("club")], "p", "indented");
+		App.Events.addNode(el, [App.Ads.report("club")], "p", "indent");
 
 		// Record statistics gathering
-		const b = State.variables.facility.club;
+		const b = V.facility.club;
 		b.whoreIncome = 0;
 		b.customers = 0;
 		b.whoreCosts = 0;
@@ -245,8 +238,8 @@ App.EndWeek.clubReport = function() {
 			b.whoreCosts += si.cost;
 			b.rep += si.rep;
 		}
-		b.adsCosts = State.variables.clubAdsSpending;
-		b.maintenance = State.variables.club * State.variables.facilityCost * (1.0 + 0.2 * State.variables.clubUpgradePDAs);
+		b.adsCosts = V.clubAdsSpending;
+		b.maintenance = V.club * V.facilityCost * (1.0 + 0.2 * V.clubUpgradePDAs);
 		b.totalIncome = b.whoreIncome + b.adsIncome;
 		b.totalExpenses = b.whoreCosts + b.adsCosts + b.maintenance;
 		b.profit = b.totalIncome / b.totalExpenses;
@@ -257,7 +250,7 @@ App.EndWeek.clubReport = function() {
 					`${capFirstChar(V.clubName)}'s customers enjoy <span class="green">having sex in ${V.clubDecoration} surroundings.</span>`
 				],
 				"p",
-				"indented"
+				"indent"
 			);
 		}
 
diff --git a/src/endWeek/reports/dairyReport.js b/src/endWeek/reports/dairyReport.js
index 78b75d9d63353f74f24a0ee70f0da9298894f467..c6a0bbb2c4d51483ea5c318c66a7707770a27b71 100644
--- a/src/endWeek/reports/dairyReport.js
+++ b/src/endWeek/reports/dairyReport.js
@@ -77,53 +77,26 @@ App.EndWeek.dairyReport = function() {
 			if (slave.muscles >= 30 || slave.muscles < -30) {
 				MMWorkout++;
 			}
+			let workoutEffect = 1;
+			if (!canMove(slave)) { // big bonus if they can't move themselves and are fat as cows
+				workoutEffect += 2;
+			} else if (!canWalk(slave)) { // smaller bonus if they are fat as cows and need assistance with moving
+				workoutEffect += 1;
+			}
 			if (slave.weight >= 70) {
-				MMWorkout++;
+				MMWorkout += workoutEffect;
 			}
 			if (slave.weight >= 160) {
-				MMWorkout++;
+				MMWorkout += workoutEffect;
 			}
 			if (slave.boobs >= 20000) {
-				MMWorkout++;
+				MMWorkout += workoutEffect;
 			}
 			if (slave.balls >= 30) {
-				MMWorkout++;
+				MMWorkout += workoutEffect;
 			}
 			if (slave.belly >= 5000) {
-				MMWorkout++;
-			}
-			if (!canMove(slave)) { // big bonus if they can't move themselves and are fat as cows
-				if (slave.weight >= 70) {
-					MMWorkout += 2;
-				}
-				if (slave.weight >= 160) {
-					MMWorkout += 2;
-				}
-				if (slave.boobs >= 20000) {
-					MMWorkout += 2;
-				}
-				if (slave.balls >= 30) {
-					MMWorkout += 2;
-				}
-				if (slave.belly >= 5000) {
-					MMWorkout += 2;
-				}
-			} else if (!canWalk(slave)) { // smaller bonus if they are fat as cows and need assistance with moving
-				if (slave.weight >= 70) {
-					MMWorkout += 1;
-				}
-				if (slave.weight >= 160) {
-					MMWorkout += 1;
-				}
-				if (slave.boobs >= 20000) {
-					MMWorkout += 1;
-				}
-				if (slave.balls >= 30) {
-					MMWorkout += 1;
-				}
-				if (slave.belly >= 5000) {
-					MMWorkout += 1;
-				}
+				MMWorkout += workoutEffect;
 			}
 			if (V.dairyStimulatorsSetting < 2 && S.Milkmaid.dick > 4 && canPenetrate(S.Milkmaid) && prostateStim !== 1) {
 				if (slave.balls > 0 && slave.prostate > 0) {
@@ -273,7 +246,7 @@ App.EndWeek.dairyReport = function() {
 			V.milkmaidTrustBonus++;
 			r.push(`${He}'s funny, and does ${his} best to get the cows to trust ${him} by keeping them laughing.`);
 		}
-		if (setup.milkmaidCareers.includes(S.Milkmaid.career)) {
+		if (App.Data.Careers.Leader.milkmaid.includes(S.Milkmaid.career)) {
 			V.milkmaidHealthBonus++;
 			r.push(`${He} has career experience dealing with milk animals.`);
 		} else if (S.Milkmaid.skill.milkmaid >= V.masteredXP) {
@@ -362,21 +335,20 @@ App.EndWeek.dairyReport = function() {
 
 	if (V.MilkmaidID !== 0) {
 		const slave = S.Milkmaid;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const milkMaidEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", milkMaidEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			milkMaidEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(milkMaidEntry, slave);
+			milkMaidEntry.append(App.UI.favoriteToggle(slave), " ");
 			App.Events.addNode(
 				milkMaidEntry,
 				[
 					App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"),
 					`is serving as your Milkmaid.`,
-					App.SlaveAssignment.standardSlaveReport(slave, false),
 				]
 			);
+			milkMaidEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -490,10 +462,8 @@ App.EndWeek.dairyReport = function() {
 		cumWeek += milkResults.cum;
 		if (V.showEWD !== 0) {
 			const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			r = [];
 			r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
 			if (slave.choosesOwnAssignment === 2) {
@@ -501,7 +471,7 @@ App.EndWeek.dairyReport = function() {
 			} else {
 				r.push(`is serving as a cow in ${V.dairyName}.`);
 			}
-			App.Events.addNode(slaveEntry, r, "div");
+			App.Events.addNode(slaveEntry, r);
 
 			const {He} = getPronouns(slave);
 			App.Events.addNode(
@@ -509,11 +479,11 @@ App.EndWeek.dairyReport = function() {
 				[
 					He,
 					milkResults.text,
-					App.SlaveAssignment.standardSlaveReport(slave, false)
 				],
 				"div",
 				"indent"
 			);
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/facilities/farmyard/reports/farmyardReport.js b/src/endWeek/reports/farmyardReport.js
similarity index 95%
rename from src/facilities/farmyard/reports/farmyardReport.js
rename to src/endWeek/reports/farmyardReport.js
index 7d646bf9f79726cfb9e0ee169169e07ea67e87b7..10b7d475f9df6da16eb4d6e515ab7acfcab0321a 100644
--- a/src/facilities/farmyard/reports/farmyardReport.js
+++ b/src/endWeek/reports/farmyardReport.js
@@ -64,7 +64,7 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 			FarmerCashBonus += 0.05;
 		}
 
-		if (setup.farmerCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
 			FarmerCashBonus += 0.05;
 			if (slave.skill.farmer >= V.masteredXP) {
 				FarmerCashBonus += 0.05;
@@ -157,7 +157,7 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 	function farmerExperience(slave) {
 		const {he, his, him, He} = getPronouns(slave);
 
-		if (setup.farmerCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
 			return `${He} has experience from ${his} life before ${he} was a slave that helps ${him} in the difficult life of managing animals and property.`;
 		} else if (slave.skill.farmer >= V.masteredXP) {
 			return `${He} has experience from working for you that helps ${him} in the difficult life of managing animals and property.`;
@@ -254,14 +254,10 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 
 		if (V.showEWD) {
 			const farmerEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", farmerEntry, App.Art.SlaveArtElement(Farmer, 0, 0), ["imageRef", "tinyImg"]);
-			}
-
-			farmerEntry.append(App.EndWeek.favoriteIcon(Farmer), " ");
+			App.SlaveAssignment.appendSlaveArt(farmerEntry, Farmer);
+			farmerEntry.append(App.UI.favoriteToggle(Farmer), " ");
 			$(farmerEntry).append(`<span class="slave-name">${SlaveFullName(Farmer)}</span> is serving as the Farmer.`);
-			$(farmerEntry).append(App.SlaveAssignment.standardSlaveReport(Farmer, false));
+			farmerEntry.append(App.SlaveAssignment.standardSlaveReport(Farmer, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(Farmer, true);
 		}
@@ -353,12 +349,8 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 
 			if (V.showEWD) {
 				const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-
-				if (V.seeImages && V.seeReportImages) {
-					App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-				}
-
-				slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+				App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+				slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 				$(slaveEntry).append(`<span class="slave-name">${SlaveFullName(slave)}</span> `);
 
 				if (slave.choosesOwnAssignment === 2) {
@@ -379,7 +371,7 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 				const farmhandContent = App.UI.DOM.appendNewElement("div", slaveEntry, '', "indent");
 
 				$(farmhandContent).append(App.SlaveAssignment.workTheFarm(slave));
-				$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+				slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 			} else {	// silently discard return values
 				App.SlaveAssignment.workTheFarm(slave);
 				App.SlaveAssignment.standardSlaveReport(slave, true);
diff --git a/src/endWeek/reports/incubatorReport.js b/src/endWeek/reports/incubatorReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b61119b2ad0ecdff0889b7dd1901a6a5936dbf8
--- /dev/null
+++ b/src/endWeek/reports/incubatorReport.js
@@ -0,0 +1,835 @@
+App.EndWeek.incubatorReport = function() {
+	const frag = document.createDocumentFragment();
+	V.readySlaves = 0;
+
+	for (const tank of V.tanks) {
+		const entry = App.UI.DOM.appendNewElement('p', frag);
+		let r = [];
+		const {He, he, His, his, him} = getPronouns(tank);
+		tank.birthWeek += 1;
+		if (tank.birthWeek >= 52) {
+			tank.birthWeek = 0;
+			if (V.seeAge === 1) {
+				tank.actualAge++;
+				tank.ovaryAge++;
+			}
+		}
+		if (tank.growTime > 0) {
+			tank.growTime -= V.incubatorUpgradeSpeed;
+			r.push(`<span class="pink">${tank.slaveName}'s</span> growth is currently being accelerated. ${He}`);
+			if (Math.round(tank.growTime/V.incubatorUpgradeSpeed) <= 0) {
+				r.push(`is <span class="lime">ready for release.</span> ${He} will be ejected from ${his} tank upon your approach.`);
+			} else {
+				r.push(`will be ready for release in about ${Math.round(tank.growTime/V.incubatorUpgradeSpeed)} weeks.`);
+			}
+		} else {
+			r.push(`<span class="pink">${tank.slaveName}</span> is <span class="lime">ready for release.</span> ${He} will be ejected from ${his} tank upon your approach.`);
+			V.readySlaves = 1;
+		}
+		App.Events.addNode(entry, r, "div");
+
+		r = [];
+		if (V.incubatorUpgradeWeight === 1) {
+			if (V.incubatorWeightSetting === 1) {
+				if (tank.weight < 200) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.weight += 70;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.weight += 40;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.weight += 20;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.weight += 10;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.weight += 5;
+					}
+				}
+				r.push(`The weight monitoring systems are overloading ${his} intake causing <span class="red">rapid weight gain.</span>`);
+			} else if (V.incubatorWeightSetting === 2) {
+				if (tank.weight > 10) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.weight -= 30;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.weight -= 10;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.weight -= 5;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.weight -= 2;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.weight -= 1;
+					}
+					r.push(`The weight monitoring systems detect ${he} is overweight and <span class="green">decrease ${his} caloric intake.</span>`);
+				} else if (tank.weight < -10) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.weight += 30;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.weight += 10;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.weight += 5;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.weight += 2;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.weight += 1;
+					}
+					r.push(`The weight monitoring systems detect ${he} is underweight and <span class="green">increase ${his} caloric intake.</span>`);
+				} else {
+					r.push(`${He} is <span class="lime">currently at a healthy weight;</span> efforts will be made to maintain it.`);
+				}
+			} else if (V.incubatorWeightSetting === 0) {
+				if (tank.weight > -100) {
+					r.push(`${His} developing body <span class="red">quickly sheds its gained weight.</span>`);
+					tank.weight -= 40;
+				}
+			}
+		} else {
+			if (tank.weight > -100) {
+				r.push(`${His} developing body <span class="red">quickly sheds its gained weight.</span>`);
+				tank.weight -= 40;
+			}
+		}
+		App.Events.addNode(entry, r, "div");
+
+		r = [];
+		if (V.incubatorUpgradeMuscles === 1) {
+			if (V.incubatorMusclesSetting === 2) {
+				if (tank.muscles < 100) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.muscles += 70;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.muscles += 40;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.muscles += 20;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.muscles += 10;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.muscles += 5;
+					}
+				}
+				r.push(`The strength monitoring systems are overloading ${him} with steroids causing <span class="green">rapid muscle development.</span>`);
+			} else if (V.incubatorMusclesSetting === 1) {
+				if (tank.muscles > 10) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.muscles -= 30;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.muscles -= 10;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.muscles -= 5;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.muscles -= 2;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.muscles--;
+					}
+					r.push(`The strength monitoring systems detect ${he} is overly muscular and <span class="green">decrease ${his} steroid dosage.</span>`);
+				} else if (tank.muscles < -10) {
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.muscles += 30;
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.muscles += 10;
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.muscles += 5;
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.muscles += 2;
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.muscles++;
+					}
+					r.push(`The strength monitoring systems detect ${he} is weak and <span class="green">increase ${his} steroid dosage.</span>`);
+				} else {
+					r.push(`${He} has <span class="lime">a healthy musculature;</span> efforts will be made to maintain it.`);
+				}
+			} else if (V.incubatorMusclesSetting === 0) {
+				if (tank.muscles > -100) {
+					r.push(`${His} developing body <span class="red">quickly loses its gained muscle.</span>`);
+					tank.muscles -= 40;
+				}
+			}
+		} else {
+			if (tank.muscles > -100) {
+				r.push(`${His} developing body <span class="red">quickly loses its gained muscle.</span>`);
+				tank.muscles -= 40;
+			}
+		}
+		App.Events.addNode(entry, r, "div");
+
+		r = [];
+		if (V.incubatorUpgradeGrowthStims === 1 && V.incubatorGrowthStimsSetting !== 0) {
+			let heightLimit = Math.trunc(Math.clamp((Height.mean(tank) * 1.25), 0, 274));
+			let heightLimitAge = Height.forAge(tank.height, tank);
+			if (tank.geneticQuirks.dwarfism === 2 && tank.geneticQuirks.gigantism !== 2) {
+				heightLimit = Math.min(heightLimit, 160);
+			}
+			if (tank.geneMods.NCS === 1) {
+				/* NCS should block physical growth beyond that of a toddler, but some players might like
+				 * a little more or less. So using V.minimumSlaveAge or 8, whichever is lesser.	*/
+				const _limitAge = Math.min(8, V.minimumSlaveAge);
+				heightLimitAge = Height.forAge(tank.height, _limitAge, tank.genes);
+				heightLimit = heightLimitAge;
+			} else if (tank.geneticQuirks.neoteny === 2 && tank.actualAge > 12) {
+				heightLimitAge = Height.forAge(tank.height, 12, tank.genes);
+				heightLimit = heightLimitAge;
+			}
+			if (tank.height >= heightLimit) {
+				r.push(`The monitoring system detects ${his} body is not able to support further increases in height, so it carefully regulates stimulant injections to <span class="yellow">maintain ${his} current stature.</span>`);
+				tank.height = heightLimit;
+			} else if (V.incubatorGrowthStimsSetting === 2) {
+				if (tank.geneMods.NCS === 1) {
+					r.push(`The monitoring system floods ${his} body with growth stimulants, but ${his} <span class="orange">NCS prevents an increase in ${his} growth rate.</span>`);
+					tank.height = heightLimitAge;
+				} else {
+					r.push(`The monitoring system floods ${his} body with growth stimulants, causing <span class="green">a sharp increase in growth rate.</span>`);
+					if (V.incubatorWeightSetting >= 1 && V.incubatorMusclesSetting <= 1 && V.incubatorReproductionSetting <= 1) {
+						if (V.incubatorUpgradeSpeed === 52) {
+							tank.height += random(3, 6);
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							tank.height += random(2, 5);
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							tank.height += random(1, 4);
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							tank.height += random(1, 3);
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							tank.height += random(1, 2);
+						}
+					} else {
+						if (V.incubatorUpgradeSpeed === 52) {
+							tank.height += random(2, 5);
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							tank.height += random(1, 4);
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							tank.height += random(1, 3);
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							tank.height += random(1, 2);
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							tank.height += random(0, 1);
+						}
+					}
+				}
+			} else if (V.incubatorGrowthStimsSetting === 1) {
+				if (tank.geneMods.NCS === 1) {
+					r.push(`The monitoring system detects ${he} is near the expected height for ${his} <span class="orange">NCS</span> condition, so it carefully regulates stimulant injections to <span class="yellow">maintain ${his} current stature.</span>`);
+					tank.height = heightLimitAge;
+				} else if (tank.height > heightLimitAge) {
+					r.push(`The monitoring systems detect ${he} is near the expected height, so it carefully regulates stimulant injections to <span class="yellow">maintain ${his} current stature.</span>`);
+					if (random(1, 10) === 10) {
+						if (V.incubatorUpgradeSpeed === 52) {
+							tank.height += random(1, 4);
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							tank.height += random(1, 3);
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							tank.height += random(1, 2);
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							tank.height += random(0, 1);
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							tank.height += random(0, 1);
+						}
+					}
+				} else {
+					r.push(`The monitoring systems detect ${his} body is capable of developing more rapidly and <span class="green">increase ${his} growth stimulant dosage.</span>`);
+					if (V.incubatorUpgradeSpeed === 52) {
+						tank.height += random(1, 4);
+					} else if (V.incubatorUpgradeSpeed === 18) {
+						tank.height += random(1, 3);
+					} else if (V.incubatorUpgradeSpeed === 9) {
+						tank.height += random(1, 2);
+					} else if (V.incubatorUpgradeSpeed === 6) {
+						tank.height += random(0, 1);
+					} else if (V.incubatorUpgradeSpeed === 5) {
+						tank.height += random(0, 1);
+					}
+				}
+			}
+			tank.height = Math.clamp(tank.height, 0, heightLimit);
+		} else {
+			r.push(`With the growth stimulant injections offline, ${his} body is left to develop naturally.`);
+		}
+		App.Events.addNode(entry, r, "div");
+
+		r = [];
+		if (V.incubatorUpgradeReproduction === 1) {
+			const rearQuirk = tank.geneticQuirks.rearLipedema === 2 ? 2 : 1;
+			if (V.incubatorReproductionSetting === 2) {
+				r.push(`${His} developing body is being flooded with hormones.`);
+				if (V.incubatorWeightSetting === 1) {
+					r.push(`Combined with the abundant food provided to ${him}, ${his} body grows rapidly.`);
+					if (tank.ovaries === 1) {
+						tank.pubertyXX = 1;
+						if (tank.hormoneBalance < 500) {
+							tank.hormoneBalance += 100;
+						}
+						tank.readyOva = V.seeHyperPreg === 1 ? random(25, 45) : random(3, 8);
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess estrogen-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.boobs < 8000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 2000;
+							}
+							if (tank.hips < 2 && random(1, 100) > 50) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips += 2;
+							}
+							if (tank.butt < 12*rearQuirk && random(1, 100) > 30/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt += 4;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.boobs < 8000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 500;
+							}
+							if (tank.hips < 2 && random(1, 100) > 50) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 12*rearQuirk && random(1, 100) > 50/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt += 3;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.boobs < 8000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 200;
+							}
+							if (tank.hips < 2 && random(1, 100) > 60) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">causes ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 12*rearQuirk && random(1, 100) > 50/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear grow fatter.</span>`);
+								tank.butt += 2;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.boobs < 8000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 100;
+							}
+							if (tank.hips < 2 && random(1, 100) > 70) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 12*rearQuirk && random(1, 100) > 60/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.boobs < 8000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 100;
+							}
+							if (tank.hips < 2 && random(1, 100) > 80) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 12*rearQuirk && random(1, 100) > 70/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						}
+					} else if (tank.balls > 0) {
+						tank.pubertyXY = 1;
+						if (tank.hormoneBalance > -500) {
+							tank.hormoneBalance -= 100;
+						}
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess testosterone-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.balls < 40) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 16;
+							}
+							if (tank.dick < 10 && random(1, 100) > 20) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick += 4;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.balls < 40 && random(1, 100) > 10) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 9;
+							}
+							if (tank.dick < 10 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick += 3;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.balls < 40 && random(1, 100) > 20) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 4;
+							}
+							if (tank.dick < 10 && random(1, 100) > 50) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick += 2;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.balls < 40 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 2;
+							}
+							if (tank.dick < 10 && random(1, 100) > 70) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.balls < 40 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 10 && random(1, 100) > 80) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						}
+					}
+				} else if (V.incubatorWeightSetting === 2) {
+					r.push(`Combined with the healthy food provided to ${him}, ${his} body grows readily.`);
+					if (tank.ovaries === 1) {
+						tank.pubertyXX = 1;
+						if (tank.hormoneBalance < 500) {
+							tank.hormoneBalance += 100;
+						}
+						tank.readyOva = V.seeHyperPreg === 1 ? random(15, 25) : random(2, 6);
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess estrogen-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.boobs < 4000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 1000;
+							}
+							if (tank.hips < 2 && random(1, 100) > 70) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 8*rearQuirk && random(1, 100) > 50/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt += 3;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.boobs < 4000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 500;
+							}
+							if (tank.hips < 2 && random(1, 100) > 80) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 8*rearQuirk && random(1, 100) > 50/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.boobs < 4000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 200;
+							}
+							if (tank.hips < 2 && random(1, 100) > 90) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 8*rearQuirk && random(1, 100) > 60/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.boobs < 4000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 100;
+							}
+							if (tank.hips < 2 && random(1, 100) > 95) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 8*rearQuirk && random(1, 100) > 70/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.boobs < 4000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 100;
+							}
+							if (tank.hips < 2 && random(1, 100) > 95) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 8*rearQuirk && random(1, 100) > 80/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						}
+					} else if (tank.balls > 0) {
+						tank.pubertyXY = 1;
+						if (tank.hormoneBalance > -500) {
+							tank.hormoneBalance -= 100;
+						}
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess testosterone-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.balls < 10) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 3;
+							}
+							if (tank.dick < 7 && random(1, 100) > 20) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick += 2;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.balls < 10 && random(1, 100) > 10) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls += 2;
+							}
+							if (tank.dick < 7 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.balls < 10 && random(1, 100) > 20) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 7 && random(1, 100) > 50) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.balls < 10 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 7 && random(1, 100) > 70) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.balls < 10 && random(1, 100) > 30) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 7 && random(1, 100) > 80) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						}
+					}
+				} else {
+					r.push(`Since ${his} body has little to work with, ${his} growth is fairly minor.`);
+					if (tank.ovaries === 1) {
+						tank.pubertyXX = 1;
+						if (tank.hormoneBalance < 500) {
+							tank.hormoneBalance += 100;
+						}
+						tank.readyOva = V.seeHyperPreg === 1 ? random(10, 15) : random(2, 4);
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess estrogen-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.boobs < 2000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 700;
+							}
+							if (tank.hips < 2 && random(1, 100) > 90) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips += 2;
+							}
+							if (tank.butt < 6*rearQuirk && random(1, 100) > 70/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt += 2;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.boobs < 2000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 200;
+							}
+							if (tank.hips < 2 && random(1, 100) > 80) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 6*rearQuirk && random(1, 100) > 70/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.boobs < 2000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly balloon ${his} breasts.</span>`);
+								tank.boobs += 50;
+							}
+							if (tank.hips < 2 && random(1, 100) > 80) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 6*rearQuirk && random(1, 100) > 90/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.boobs < 2000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly grow ${his} breasts.</span>`);
+								tank.boobs += 20;
+							}
+							if (tank.hips < 2 && random(1, 100) > 90) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 6*rearQuirk && random(1, 100) > 90/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.boobs < 2000) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">rapidly grow ${his} breasts.</span>`);
+								tank.boobs += 10;
+							}
+							if (tank.hips < 2 && random(1, 100) > 95) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} hips to widen for childbirth.</span>`);
+								tank.hips++;
+							}
+							if (tank.butt < 6*rearQuirk && random(1, 100) > 90/rearQuirk) {
+								r.push(`The excess estrogen-laced growth hormones <span class="green">cause ${his} rear to grow fatter.</span>`);
+								tank.butt++;
+							}
+						}
+					} else if (tank.balls > 0) {
+						tank.pubertyXY = 1;
+						if (tank.hormoneBalance > -500) {
+							tank.hormoneBalance -= 100;
+						}
+						if (tank.geneMods.NCS === 1) {
+							/* NCS blocks hormonal growth of all secondary sexual characteristics */
+							r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the excess testosterone-laced growth hormones flooding ${his} body.`);
+						} else if (V.incubatorUpgradeSpeed === 52) {
+							if (tank.balls < 6) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to grow for extra cum production.</span>`);
+								tank.balls += 2;
+							}
+							if (tank.dick < 4 && random(1, 100) > 60) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 18) {
+							if (tank.balls < 6 && random(1, 100) > 50) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to grow for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 4 && random(1, 100) > 60) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 9) {
+							if (tank.balls < 6 && random(1, 100) > 60) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to balloon for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 4 && random(1, 100) > 70) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick += 2;
+							}
+						} else if (V.incubatorUpgradeSpeed === 6) {
+							if (tank.balls < 6 && random(1, 100) > 70) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to grow for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 4 && random(1, 100) > 80) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						} else if (V.incubatorUpgradeSpeed === 5) {
+							if (tank.balls < 6 && random(1, 100) > 80) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} balls to grow for extra cum production.</span>`);
+								tank.balls++;
+							}
+							if (tank.dick < 4 && random(1, 100) > 90) {
+								r.push(`The excess testosterone-laced growth hormones <span class="green">cause ${his} penis to swell.</span>`);
+								tank.dick++;
+							}
+						}
+					}
+				}
+			} else if (V.incubatorReproductionSetting === 1) {
+				r.push(`${His} hormone levels are being carefully managed, <span class="green">encouraging early puberty.</span>`);
+				if (tank.ovaries === 1) {
+					tank.pubertyXX = 1;
+					tank.hormoneBalance = 250;
+					if (tank.geneMods.NCS === 1) {
+						/* NCS blocks hormonal growth of all secondary sexual characteristics */
+						r.push(`${His} <span class="orange">NCS blocks growth</span> despite the added estrogen.`);
+					} else {
+						if (tank.boobs < 400 && random(1, 100) > 60) {
+							r.push(`The added estrogen <span class="green">causes ${his} breasts to swell.</span>`);
+							tank.boobs += 50;
+						}
+						if (tank.hips < 2 && random(1, 100) > 90) {
+							r.push(`The added estrogen <span class="green">causes ${his} hips to widen.</span>`);
+							tank.hips++;
+						}
+						if (tank.butt < 5*rearQuirk && random(1, 100) > 80/rearQuirk) {
+							r.push(`The added estrogen <span class="green">causes ${his} butt to grow.</span>`);
+							tank.butt++;
+						}
+					}
+				} else if (tank.balls > 0) {
+					tank.pubertyXY = 1;
+					tank.hormoneBalance = -250;
+					if (tank.geneMods.NCS === 1) {
+						/* NCS blocks hormonal growth of all secondary sexual characteristics */
+						r.push(`${His} <span class="orange">NCS blocks all growth</span> despite the added testosterone.`);
+					} else {
+						if (tank.balls < 3 && random(1, 100) > 80) {
+							r.push(`The added testosterone <span class="green">causes ${his} balls to swell.</span>`);
+							tank.balls++;
+						}
+						if (tank.dick < 3 && random(1, 100) > 60) {
+							r.push(`The added testosterone <span class="green">causes ${his} penis to grow.</span>`);
+							tank.dick++;
+						}
+					}
+				}
+			} else {
+				if (tank.hormoneBalance > 100) {
+					tank.hormoneBalance -= 50;
+				} else if (tank.hormoneBalance < -100) {
+					tank.hormoneBalance += 50;
+				}
+				if (tank.balls > 0) {
+					if (tank.balls > 1) {
+						tank.balls -= 5;
+					}
+					if (tank.dick > 1) {
+						tank.dick -= 5;
+					}
+					if (tank.balls <= 0) {
+						tank.balls = 1;
+					}
+					if (tank.dick <= 0) {
+						tank.dick = 1;
+					}
+				}
+				if (tank.boobs > 0) {
+					tank.boobs -= 500;
+				}
+				if (tank.butt > 0) {
+					tank.butt -= 4;
+				}
+			}
+		} else {
+			if (tank.hormoneBalance > 100) {
+				tank.hormoneBalance -= 50;
+			} else if (tank.hormoneBalance < -100) {
+				tank.hormoneBalance += 50;
+			}
+			if (tank.balls > 0) {
+				if (tank.balls > 1) {
+					tank.balls -= 5;
+				}
+				if (tank.dick > 1) {
+					tank.dick -= 5;
+				}
+				if (tank.balls <= 0) {
+					tank.balls = 1;
+				}
+				if (tank.dick <= 0) {
+					tank.dick = 1;
+				}
+			}
+			if (tank.boobs > 0) {
+				tank.boobs -= 500;
+			}
+			if (tank.butt > 0) {
+				tank.butt -= 4;
+			}
+		}
+		App.Events.addNode(entry, r, "div");
+
+		if (V.incubatorReproductionSetting === 2) {
+			tank.energy = 80;
+			tank.need = 100;
+		} else if (V.incubatorReproductionSetting === 1) {
+			tank.energy = 50;
+			tank.need = 20;
+		} else {
+			tank.energy = 0;
+			tank.need = 0;
+		}
+
+		r = [];
+		if (((V.incubatorPregAdaptationSetting === 1 && tank.genes === "XX") || (V.incubatorPregAdaptationSetting === 2 && tank.genes === "XY") || V.incubatorPregAdaptationSetting === 3) && tank.growTime > 0) {
+			r.push(`The incubator is working on adapting ${his} abdomen and reproductive organs for future pregnancies.`);
+
+			let _weekAdapt = tank.incubatorPregAdaptationInWeek * V.incubatorUpgradeSpeed;
+			if (isNaN(_weekAdapt)) {
+				/* NaN check AFTER multiply operation, against it result is critical here. Need to be absolutely sure about this operation, not about just tank property itself. This give me two very unpleasant hours to catch this */
+				tank.incubatorPregAdaptationInWeek = (15000 / 2000 - tank.pregAdaptation) / tank.growTime;
+			}
+			_weekAdapt = tank.incubatorPregAdaptationInWeek * V.incubatorUpgradeSpeed;
+			/* Now it should be fine */
+			_weekAdapt *= 1 + (V.incubatorReproductionSetting / 5);
+			_weekAdapt *= 1 + (tank.hormoneBalance / 1500);
+			tank.pregAdaptation += _weekAdapt;
+			/* here goes side effect from intense and extreme settings: */
+			if (random(0, 100) <= (tank.incubatorPregAdaptationPower - 1) * (V.incubatorUpgradeSpeed / 2 + 1)) {
+				switch (random(1, 9)) { /* side effect selection*/
+					case 1:
+						tank.preg = -2;
+						r.push(`It caused <span class="red">reproductive damage</span> when excessive meddling damaged an organ.`);
+						break;
+					case 2:
+						if (tank.ovaries === 1 || tank.mpreg === 1) {
+							tank.preg = -3;
+						}
+						if (tank.balls > 0) {
+							tank.ballType = "sterile";
+						}
+						r.push(`It caused <span class="red">severe reproductive damage</span> when excessive hormones shut down the associated organs.`);
+						break;
+					case 3:
+						tank.lactation = 1;
+						r.push(`It has <span class="orange">triggered a hormonal disorder,</span> causing ${his} breast glands begin producing milk.`);
+						break;
+					case 4:
+						tank.bellySag = 100;
+						tank.bellySagPreg = 100;
+						r.push(`It activated <span class="red">emergency protocols</span> when overpressure to ${his} abdominal tissues and skin reached critical levels. ${His} belly has lost muscle tone and has begun to strongly sag.`);
+						break;
+					case 5:
+						tank.health.condition -= 15;
+						r.push(`Overzealous prodding caused some <span class="red">internal bleeding.</span>`);
+						break;
+					case 6:
+						tank.weight += 50;
+						r.push(`An unexpected shift in hormones spurred <span class="red">massive weight gain</span> before it could be corrected.`);
+						break;
+					case 7:
+						tank.weight -= 50;
+						r.push(`An unexpected shift in hormones spurred <span class="red">massive weight loss</span> before it could be corrected.`);
+						break;
+					case 8:
+						tank.boobs += 5000;
+						tank.boobShape = "saggy";
+						r.push(`An unexpected shift in hormones encouraged <span class="green">explosive breast growth</span> before it could be corrected.`);
+						break;
+					case 9:
+						tank.hips = 3;
+						r.push(`A malfunction in the skeletal reconstruction software caused it to <span class="green">overwiden ${his} hips.</span>`);
+						break;
+				}
+			}
+		}
+		App.Events.addNode(entry, r, "div");
+
+		if (tank.preg > 0) {
+			App.UI.DOM.appendNewElement("span", entry, `The incubator is displaying an alert that ${he} may be pregnant.`, 'red');
+		}
+
+		tank.weight = Math.clamp(tank.weight, -100, 200);
+		tank.muscles = Math.clamp(tank.muscles, -100, 100);
+		tank.dick = Math.clamp(tank.dick, 0, 10);
+		tank.hips = Math.clamp(tank.hips, -2, 2);
+		tank.balls = Math.clamp(tank.balls, 0, 40);
+		tank.boobs = Math.clamp(tank.boobs, 25, 30000);
+		tank.height = Math.clamp(tank.height, 0, 274);
+		tank.hormoneBalance = Math.clamp(tank.hormoneBalance, -500, 500);
+		tank.foreskin = tank.dick;	/* simple, clean way of making sure foreskins and scrotums grow appropriately */
+		tank.scrotum = tank.balls;	/* if we want dicks/balls to outgrow foreskins/scrotums, this will have to be removed */
+	}
+
+	return frag;
+};
diff --git a/src/endWeek/masterSuiteReport.js b/src/endWeek/reports/masterSuiteReport.js
similarity index 96%
rename from src/endWeek/masterSuiteReport.js
rename to src/endWeek/reports/masterSuiteReport.js
index e038db401dc6a3150eeec16cd35ced52f24c1e04..1239707027840bce36ec0eaea1048b5f83253bc5 100644
--- a/src/endWeek/masterSuiteReport.js
+++ b/src/endWeek/reports/masterSuiteReport.js
@@ -341,8 +341,7 @@ App.EndWeek.masterSuiteReport = function() {
 				const milkDiv = App.UI.DOM.appendNewElement("div", smallFrag, `When ${his} breasts begin to feel full and you aren't around, ${he} avails ${himself} to the penthouse milkers and gives ${milkingResults.milk} liters of milk over the week, which is sold for `, "indent");
 				App.UI.DOM.appendNewElement("span", milkDiv, `${cashFormat(milkingResults.milkSale)}.`, ["cash", "inc"]);
 			}
-			const commonContent = App.UI.DOM.appendNewElement("div", smallFrag, '', "indent");
-			commonContent.append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			smallFrag.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
@@ -375,7 +374,7 @@ App.EndWeek.masterSuiteReport = function() {
 
 		if ((V.arcologies[0].FSEgyptianRevivalist > 0) && (slaves.length >= 5)) {
 			r.push(`Society <span class="green">approves</span> of your keeping a large number of women. This advances the Egyptian revivalist ideal of multiple concubinage.`);
-			FutureSocieties.Change("EgyptianRevivalist", 2);
+			FutureSocieties.Change("Egyptian Revivalist", 2);
 		}
 		if (pregnantSlaves >= 1) {
 			r.push(`The suite is supporting the pregnancies of the slaves`);
@@ -426,21 +425,18 @@ App.EndWeek.masterSuiteReport = function() {
 	}
 
 	if (S.Concubine) {
+		tired(S.Concubine);
 		const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-		if (V.seeImages && V.seeReportImages) {
-			App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(S.Concubine, 0, 0), ["imageRef", "tinyImg"]);
-		}
-		slaveEntry.append(App.EndWeek.favoriteIcon(S.Concubine), " ");
-		$(slaveEntry).append(concubineText(), commonText(S.Concubine));
+		App.SlaveAssignment.appendSlaveArt(slaveEntry, S.Concubine);
+		slaveEntry.append(App.UI.favoriteToggle(S.Concubine), " ");
+		App.Events.addNode(slaveEntry, [concubineText(), commonText(S.Concubine)]);
 	}
 
 	for (const slave of slaves) {
 		const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-		if (V.seeImages && V.seeReportImages) {
-			App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-		}
-		slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
-		$(slaveEntry).append(nonConcubineText(slave), commonText(slave));
+		App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+		slaveEntry.append(App.UI.favoriteToggle(slave), " ");
+		App.Events.addNode(slaveEntry, [nonConcubineText(slave), commonText(slave)]);
 	}
 
 	if (pregnantSlaves > 0 && V.arcologies[0].FSRestart !== "unset" && V.propOutcome !== 1 && V.eugenicsFullControl !== 1) {
diff --git a/src/endWeek/reports/nurseryReport.js b/src/endWeek/reports/nurseryReport.js
index 13d315fe65c1d9ec2e980f3b7ec9c2e782fc4410..4a4663bf304061ad4b4487f11712b8ab7d87e7b5 100644
--- a/src/endWeek/reports/nurseryReport.js
+++ b/src/endWeek/reports/nurseryReport.js
@@ -70,7 +70,7 @@ App.Facilities.Nursery.nurseryReport = function nurseryReport() {
 				matronBonus += V.nurseryNannies ? 75 : 0;
 			}
 
-			if (setup.matronCareers.includes(S.Matron.career)) {
+			if (App.Data.Careers.Leader.matron.includes(S.Matron.career)) {
 				r.push(`${He} has experience with looking after children from ${his} life before ${he} was a slave.`);
 
 				matronBonus += 25;
@@ -135,19 +135,14 @@ App.Facilities.Nursery.nurseryReport = function nurseryReport() {
 	}
 
 	if (S.Matron) {
-		/** @type {App.Entity.SlaveState} */
 		const slave = S.Matron;
 
 		if (V.showEWD !== 0) {
 			const matronEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", matronEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-
-			matronEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(matronEntry, slave);
+			matronEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(matronEntry).append(`<span class="slave-name">${SlaveFullName(slave)}</span> is serving as your Matron.`);
-			$(matronEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			matronEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -200,12 +195,8 @@ App.Facilities.Nursery.nurseryReport = function nurseryReport() {
 		if (V.showEWD) {
 			const {He} = getPronouns(slave);
 			const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(slaveEntry).append(`<span class="slave-name">${SlaveFullName(slave)}</span> `);
 
 			if (slave.choosesOwnAssignment === 2) {
@@ -218,7 +209,7 @@ App.Facilities.Nursery.nurseryReport = function nurseryReport() {
 
 			$(nannyContent).append(`${He} ${App.SlaveAssignment.nanny(slave, matronBonus)}`);	// FIXME: nanny() takes one argument
 
-			$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {	// silently discard return values
 			App.SlaveAssignment.nanny(slave, matronBonus);	// FIXME: nanny() takes one argument
 			App.SlaveAssignment.standardSlaveReport(slave, true);
diff --git a/src/endWeek/reports/penthouseReport.js b/src/endWeek/reports/penthouseReport.js
new file mode 100644
index 0000000000000000000000000000000000000000..450dc08f44276e483d26f71a8aaab87268ce9dbe
--- /dev/null
+++ b/src/endWeek/reports/penthouseReport.js
@@ -0,0 +1,702 @@
+App.EndWeek.penthouseReport = function() {
+	const el = document.createElement("p");
+
+	const HGSuiteSlaves = App.Utils.jobForAssignment(Job.HEADGIRLSUITE).employees();
+	const hgSlave = HGSuiteSlaves.length > 0 ? HGSuiteSlaves[0] : null;
+	const HGTrainSlavesIDs = slavesToTrain();
+
+	// penthouse images are larger and on the right (for some reason), so we need to initialize a second batch renderer instead of using the global SA Report one
+	let penthouseArtRenderer = null;
+	if (V.seeImages && V.seeReportImages) {
+		const penthouseArtSlaveIDs = V.slaves.filter(s => assignmentVisible(s)).map(s => s.ID);
+		if (hgSlave) {
+			penthouseArtSlaveIDs.push(hgSlave.ID);
+		}
+		penthouseArtRenderer = new App.Art.SlaveArtBatch(penthouseArtSlaveIDs, 2);
+		el.append(penthouseArtRenderer.writePreamble());
+	}
+
+	for (const slave of V.slaves) {
+		if (assignmentVisible(slave)) {
+			const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
+			if (penthouseArtRenderer) {
+				App.UI.DOM.appendNewElement("div", slaveEntry, penthouseArtRenderer.render(slave), ["imageRef", "medImg"]);
+			}
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
+			slaveEntry.append(fullReport(slave));
+
+			if (slave.ID === V.HeadGirlID && hgSlave) {
+				/* Output the HG's slave immediately after the hg */
+				const {He2, he2} = getPronouns(hgSlave).appendSuffix("2");
+				const r = [];
+				if (hgSlave.assignment !== Job.HEADGIRLSUITE) {
+					r.push(`<span class="red">${hgSlave.slaveName} had been assigned to live with your Head Girl, but this week ${he2} was assigned to ${hgSlave.assignment}. ${He2} has been released to your penthouse for reassignment.</span>`);
+					removeJob(hgSlave, Job.HEADGIRLSUITE);
+				} else {
+					r.push(App.UI.DOM.makeElement("span", SlaveFullName(hgSlave), "slave-name"));
+					if (hgSlave.choosesOwnAssignment === 2) {
+						r.push(App.SlaveAssignment.choosesOwnJob(hgSlave));
+						r.push(He2);
+					}
+					if (penthouseArtRenderer) {
+						r.push(App.UI.DOM.makeElement("div", penthouseArtRenderer.render(hgSlave), ["imageRef", "medImg"]));
+					}
+					r.push(App.SlaveAssignment.liveWithHG(hgSlave));
+				}
+				App.Events.addNode(el, r, "div", "slave-report");
+			}
+		}
+	}
+
+	/* count open spots in facilities after all assignments have been decided for the week */
+	V.brothelSpots = App.Entity.facilities.brothel.freeSpace;
+	V.clubSpots = App.Entity.facilities.club.freeSpace;
+	V.dairySpots = App.Entity.facilities.dairy.freeSpace;
+	V.servantsQuartersSpots = App.Entity.facilities.servantsQuarters.freeSpace;
+
+	return el;
+
+	/**
+	 * @param {App.Entity.SlaveState} slave
+	 */
+	function fullReport(slave) {
+		const el = new DocumentFragment();
+		const {
+			He, His,
+			he, him
+		} = getPronouns(slave);
+		let r = [];
+		let milkResults;
+
+		r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
+		if (slave.choosesOwnAssignment === 2) {
+			r.push(App.SlaveAssignment.choosesOwnJob(slave));
+			r.push(He);
+		}
+
+		switch (slave.assignment) {
+			case "rest":
+				r.push(App.SlaveAssignment.rest(slave));
+				break;
+			case "whore":
+				r.push(App.SlaveAssignment.whore(slave));
+				break;
+			case "serve the public":
+				r.push(App.SlaveAssignment.serveThePublic(slave));
+				break;
+			case "work a glory hole":
+				r.push(App.SlaveAssignment.workAGloryHole(slave));
+				break;
+			case "get milked":
+				milkResults = App.SlaveAssignment.getMilked(slave);
+				r.push(milkResults.text);
+				break;
+			case "take classes":
+				r.push(App.SlaveAssignment.takeClasses(slave));
+				break;
+			case "please you":
+				r.push(App.SlaveAssignment.pleaseYou(slave));
+				break;
+			case "be a subordinate slave":
+				r.push(App.SlaveAssignment.serveYourOtherSlaves(slave));
+				break;
+			case "be a servant":
+				r.push(App.SlaveAssignment.servant(slave));
+				break;
+			case "stay confined":
+				r.push(App.SlaveAssignment.stayConfined(slave).text);
+				break;
+			case "guard you":
+				r.push(App.SlaveAssignment.guardYou(slave));
+				break;
+			case "be your Head Girl":
+				r.push(App.SlaveAssignment.beYourHeadGirl(slave));
+				break;
+			case "recruit girls":
+				r.push(App.SlaveAssignment.recruitGirls(slave));
+				break;
+			default:
+				removeJob(slave, slave.assignment);
+				r.push(App.SlaveAssignment.rest(slave));
+		}
+
+		if (V.servantMilkers === 1 && slave.lactation > 0 && slave.assignment !== "get milked") {
+			r.push(`${His} assignment`);
+			if (App.Data.misc.servantMilkersJobs.includes(slave.assignment)) {
+				r.push(`is not strenuous, so ${he}`);
+				if (slave.devotion > 20) {
+					if (slave.fetish === "boobs") {
+						r.push(`eagerly`);
+					}
+					r.push(`uses`);
+				} else if (slave.devotion >= -20) {
+					r.push(`is required to use`);
+				} else {
+					r.push(`is forced to use`);
+				}
+				r.push(`the penthouse milkers frequently,`);
+				milkResults = App.SlaveAssignment.getMilked(slave, 0.5);
+			} else {
+				r.push(`keeps ${him} busy, but ${he}`);
+				if (slave.devotion > 20) {
+					if (slave.fetish === "boobs") {
+						r.push(`eagerly`);
+					}
+					r.push(`uses`);
+				} else if (slave.devotion >= -20) {
+					r.push(`is required to use`);
+				} else {
+					r.push(`is forced to use`);
+				}
+				r.push(`the penthouse milkers whenever ${he} can,`);
+				milkResults = App.SlaveAssignment.getMilked(slave, 0.25);
+			}
+			r.push(`and ${he} gives ${milkResults.milk} liters of milk over the week, which is sold for <span class="yellowgreen">${cashFormat(milkResults.milkSale)}.</span>`);
+		}
+
+		App.Events.addNode(el, r);
+
+		if (V.showEWD !== 0) {
+			r = [];
+			if (slave.minorInjury !== 0) {
+				r.push(`${His} ${slave.minorInjury} will heal by the end of the week.`);
+			}
+			r.push(App.SlaveAssignment.choosesOwnClothes(slave));
+			r.push(...App.SlaveAssignment.individualSlaveReport(slave));
+			App.Events.addNode(el, r, "div", "indent");
+		} else {
+			// discard return values silently
+			App.SlaveAssignment.choosesOwnClothes(slave);
+			App.SlaveAssignment.individualSlaveReport(slave);
+		}
+
+		r = [];
+		if (V.PC.health.shortDamage < 30 && Array.isArray(V.personalAttention) && V.personalAttention.some(s => s.ID === slave.ID)) {
+			r.push(personalAttention(slave));
+		}
+
+		if (HGTrainSlavesIDs.length > 0) {
+			const trainee = HGTrainSlavesIDs.find(trainee => slave.ID === trainee.ID);
+			if (trainee) {
+				r.push(HGApplication(slave, trainee.training));
+			}
+		}
+
+		r.push(`<div class="indent">${App.SlaveAssignment.devotion(slave)}</div>`);
+		App.Events.addNode(el, r);
+		return el;
+	}
+
+	/**
+	 * @param {App.Entity.SlaveState} slave
+	 * @param {string} headGirlsTraining
+	 */
+	function HGApplication(slave, headGirlsTraining) {
+		const el = document.createElement("span");
+		const {
+			He, His,
+			he, his, him, himself, girl
+		} = getPronouns(S.HeadGirl);
+		const {he2, his2, him2, himself2, girl2} = getPronouns(slave).appendSuffix("2");
+		let r = [];
+
+		slave.training = Math.clamp(slave.training, 0, 150);
+		let effectiveness = S.HeadGirl.actualAge + ((S.HeadGirl.intelligence + S.HeadGirl.intelligenceImplant) / 3) - (S.HeadGirl.accent * 5) + (V.HGSeverity * 10) + ((slave.intelligence + slave.intelligenceImplant) / 4) - (slave.accent * 5);
+		if (V.AgePenalty === 0) {
+			effectiveness += Math.max(0, (30 - S.HeadGirl.actualAge));
+		}
+		if (S.HeadGirl.sexualFlaw === "abusive" && V.HGSeverity >= 0) {
+			effectiveness += 10;
+		}
+		if (headGirlsTraining !== "health") {
+			slave.health.tired += 25;
+		}
+
+		r.push(`<span style="font-weight: bold">Your Head Girl</span> <span class='slave-name'>${S.HeadGirl.slaveName}</span> notices that <span class='slave-name'>${slave.slaveName}</span>`);
+		switch (headGirlsTraining) {
+			case "health":
+				r.push(`is unwell.`);
+				break;
+			case "obedience":
+				r.push(`is disobedient.`);
+				break;
+			case "paraphilia":
+				r.push(`has a paraphilia.`);
+				break;
+			case "flaw":
+				r.push(`has an unpleasant flaw worth fixing.`);
+				break;
+			case "soften":
+				r.push(`has an unpleasant flaw that ${he} can try to soften into a unique quirk.`);
+				break;
+			case "oral skill":
+				r.push(`isn't as orally skilled as ${he} is.`);
+				break;
+			case "anal skill":
+				r.push(`isn't as skilled at taking anal as ${he} is.`);
+				break;
+			case "fuck skill":
+				r.push(`isn't as skilled at vaginal intercourse as ${he} is.`);
+				break;
+			case "whore skill":
+				r.push(`isn't as skilled at the fine art of sex for money as ${he} is.`);
+				break;
+			case "entertain skill":
+				r.push(`isn't as skilled at entertainment as ${he} is.`);
+		}
+
+		if (headGirlsTraining === "health") {
+			r.push(`${He} helps ${slave.slaveName} however ${he} can. The tender care has <span class="health inc">improved ${slave.slaveName}'s health.</span>`);
+			improveCondition(slave, 10);
+			slave.health.tired = Math.clamp(slave.health.tired - 10, 0, 1000);
+			slave.training = 0;
+		} else {
+			if (S.HeadGirl.career === "an arcology owner") {
+				r.push(`${He} was once an arcology owner ${himself}, making ${him} truly peerless as a Head Girl.`);
+				effectiveness += 50;
+			} else if (App.Data.Careers.Leader.HG.includes(S.HeadGirl.career)) {
+				r.push(`${He} was used to giving commands and being obeyed in ${his} life before ${he} was a slave, experience ${he} can call on now.`);
+				effectiveness += 5;
+			} else if (S.HeadGirl.skill.headGirl >= V.masteredXP) {
+				r.push(`${He} is used to giving commands and being obeyed through experience, rendering ${him} more effective.`);
+				effectiveness += 5;
+			}
+
+			if (S.HeadGirl.actualAge > 30) {
+				r.push(`${His} age and experience help ${him} as ${he} works with ${slave.slaveName}.`);
+			} else if (S.HeadGirl.actualAge < 25 && V.AgePenalty > 0) {
+				r.push(`${His} youth and inexperience impede ${him} as ${he} works with ${slave.slaveName}.`);
+			}
+
+			if (S.HeadGirl.accent >= 3) {
+				r.push(`${He} does ${his} best to give orders in ${V.language}, but has to resort to a lot of unseemly gesticulation and shouting, making ${him} much less effective.`);
+			} else if (S.HeadGirl.accent === 2) {
+				r.push(`${His} heavy ${aNational(S.HeadGirl.nationality)} accent impedes ${his} clarity in ${V.language}, making ${him} somewhat less effective.`);
+			}
+
+			if (headGirlsTraining === "obedience" || headGirlsTraining === "flaw" || headGirlsTraining === "soften") {
+				if (S.HeadGirl.intelligence + S.HeadGirl.intelligenceImplant > 50) {
+					if (slave.intelligence + slave.intelligenceImplant > 50) {
+						r.push(`${He} needs ${his} wits about ${him} to mold ${slave.slaveName}, who's quite intelligent ${himself2}.`);
+					} else if (slave.intelligence + slave.intelligenceImplant >= -50) {
+						r.push(`${His} intelligence helps ${him} mold ${slave.slaveName}.`);
+					} else {
+						r.push(`Molding ${slave.slaveName} is very easy, especially for a smart Head Girl like ${him}.`);
+					}
+				}
+			} else {
+				if (S.HeadGirl.intelligence + S.HeadGirl.intelligenceImplant > 50) {
+					if (slave.intelligence + slave.intelligenceImplant > 50) {
+						r.push(`Both slaves are quite intelligent, making ${his} job much easier.`);
+					} else if (slave.intelligence + slave.intelligenceImplant >= -50) {
+						r.push(`${He}'s quite intelligent and can teach ${slave.slaveName} well.`);
+					} else {
+						r.push(`${He} needs all ${his} considerable intelligence to get through to the idiot ${he} has to teach.`);
+					}
+				}
+			}
+
+			if (V.HGSeverity > 0) {
+				r.push(`Your directives encourage ${him} to rape any slave that fails in the slightest way, a task ${he} approaches`);
+				if (S.HeadGirl.sexualFlaw === "abusive") {
+					r.push(`eagerly, since ${he} prefers to take ${his} pleasure without consent.`);
+				} else if (S.HeadGirl.fetish === "sadist") {
+					r.push(`with sadistic enthusiasm.`);
+				} else if (S.HeadGirl.fetish === "dom") {
+					r.push(`with confidence.`);
+				} else if (S.HeadGirl.energy > 95) {
+					r.push(`as a convenient way to`);
+					if (S.HeadGirl.balls > 0) {
+						r.push(`drain ${his} perpetually overfilled balls into a series of spasming holes.`);
+					} else {
+						r.push(`sate ${his} excess sexual appetite.`);
+					}
+				} else {
+					r.push(`dutifully.`);
+				}
+				if (slave.vagina === 0) {
+					r.push(`In ${slave.slaveName}'s case, ${he}'s careful to limit ${his2} sexual abuse to things that won't take the ${girl2}'s virginity. The slave`);
+				} else if (slave.anus === 0) {
+					r.push(`${He}'s careful to molest ${slave.slaveName} in ways that don't involve penetrating ${his2} virgin ass. The slave`);
+				} else {
+					r.push(`${slave.slaveName}`);
+				}
+				if (slave.devotion > 50) {
+					r.push(`accepts the abuse, believing ${he2} deserves to be raped.`);
+				} else if (slave.devotion >= -20) {
+					r.push(`is <span class="gold">frightened</span> by the unceasing abuse.`);
+					slave.trust -= 1;
+				} else {
+					r.push(`<span class="mediumorchid">hates</span> and <span class="gold">fears</span> the constant abuse.`);
+					slave.trust -= 1;
+					slave.devotion -= 1;
+				}
+				r.push(`${S.HeadGirl.slaveName} makes sure ${his} victim hates it, every time, and ${slave.slaveName} can't help but do ${his2} very best to avoid this treatment.`);
+			} else if (V.HGSeverity === 0) {
+				r.push(`You allow ${him} to use ${his} own judgment when punishing failures,`);
+				if (S.HeadGirl.sexualFlaw === "abusive") {
+					r.push(`and ${he} usually decides in favor of forcing misbehaving slaves to get ${him} off.`);
+				} else if (S.HeadGirl.fetish === "sadist") {
+					r.push(`permission the sadist uses for ${his} own gratification.`);
+				} else if (S.HeadGirl.fetish === "dom") {
+					r.push(`which the dominant ${girl} uses confidently.`);
+				} else if (S.HeadGirl.energy > 95) {
+					r.push(`which ${he} uses to`);
+					if (S.HeadGirl.balls > 0) {
+						r.push(`force ${his} subordinates to keep ${his} balls drained.`);
+					} else {
+						r.push(`extract the constant sexual favors ${his} libido demands.`);
+					}
+				} else {
+					r.push(`and ${he} does ${his} best to be fair.`);
+				}
+				r.push(`${slave.slaveName}`);
+				if (slave.devotion > 50) {
+					r.push(`is <span class="mediumaquamarine">grateful</span> that ${he2} is punished justly when ${he2} makes mistakes.`);
+					slave.trust += 1;
+				} else if (slave.devotion >= -20) {
+					r.push(`does ${his2} best to learn quickly and avoid punishment.`);
+				} else {
+					r.push(`is <span class="gold">scared</span> by ${his2} inability to get away with the smallest disobedience.`);
+					slave.trust -= 1;
+				}
+			} else {
+				r.push(`You require ${him} to use respect when punishing; ${he} does ${his} best to motivate, but is unable to use real fear as a tool. ${slave.slaveName} understands this, and <span class="mediumaquamarine">trusts</span> that if ${he2} fails, the consequences won't be too awful.`);
+				slave.trust += 1;
+			}
+
+			if (headGirlsTraining === "obedience") {
+				slave.training = 0;
+				effectiveness -= (slave.intelligence + slave.intelligenceImplant) / 3;
+				if (effectiveness <= 0) {
+					r.push(`${slave.slaveName} is smart enough to complicate things; ${he2} manages to outwit ${him} this week and makes no progress.`);
+				} else {
+					slave.devotion += Math.ceil(effectiveness / 4);
+					if (slave.devotion < -50) {
+						r.push(`${He} helps watch and restrain the rebellious ${slave.slaveName}, helping wear ${him2} down, <span class="gold">breaking ${slave.slaveName}'s rebelliousness.</span>`);
+					} else if (slave.devotion < -20) {
+						r.push(`${He} keeps ${slave.slaveName} constantly aware of ${his2} slavery, <span class="gold">breaking ${slave.slaveName}'s resistance.</span>`);
+					} else {
+						r.push(`${He} acts as another pair of eyes watching ${slave.slaveName} and metes out punishments, <span class="gold">improving ${slave.slaveName}'s servitude.</span>`);
+					}
+				}
+			} else if (headGirlsTraining === "paraphilia") {
+				effectiveness -= (slave.intelligence + slave.intelligenceImplant) / 3;
+				slave.training += effectiveness;
+				r.push(`${S.HeadGirl.slaveName} does ${his} best to get ${slave.slaveName} past it with punishments and rewards,`);
+				if (slave.training > 100) {
+					r.push(`and <span class="green">resolves ${slave.slaveName}'s paraphilia.</span>`);
+					slave.training = 0;
+					slave.sexualFlaw = "none";
+				} else {
+					r.push(`and makes partial progress.`);
+				}
+			} else if (headGirlsTraining === "flaw") {
+				effectiveness -= (slave.intelligence + slave.intelligenceImplant) / 3;
+				slave.training += effectiveness;
+				r.push(`${S.HeadGirl.slaveName} punishes ${slave.slaveName} whenever ${he} catches ${him2} indulging in ${his2} bad habits,`);
+				if (slave.training > 100) {
+					r.push(`and <span class="green">fixes ${slave.slaveName}'s flaw.</span>`);
+					slave.training = 0;
+					if (slave.behavioralFlaw !== "none") {
+						slave.behavioralFlaw = "none";
+					} else if (slave.sexualFlaw !== "none") {
+						slave.sexualFlaw = "none";
+					}
+				} else {
+					r.push(`and makes partial progress.`);
+				}
+			} else if (headGirlsTraining === "soften") {
+				effectiveness -= (slave.intelligence + slave.intelligenceImplant) / 3;
+				slave.training += effectiveness;
+				r.push(`${He} punishes ${slave.slaveName} whenever ${he} sees ${him2} breaking the rules yet does ${his} best to retain what makes the slave special,`);
+				if (slave.training > 150) {
+					slave.training = 0;
+					r.push(`and successfully <span class="green">softens ${slave.slaveName}'s flaw.</span>`);
+					if (slave.behavioralFlaw !== "none") {
+						SoftenBehavioralFlaw(slave);
+					} else if (slave.sexualFlaw !== "none") {
+						SoftenSexualFlaw(slave);
+					}
+				} else {
+					r.push(`and makes partial progress.`);
+				}
+			} else if (headGirlsTraining === "oral skill") {
+				slave.training = 0;
+				if (S.HeadGirl.fetish === "cumslut" && S.HeadGirl.fetishStrength > 60) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to suck cocks, cunts, and assholes. ${His} enthusiasm for oral sex is infectious. ${slave.slaveName}'s <span class="green">oral skills have improved.</span>`);
+					slaveSkillIncrease('oral', slave, random(5, 10));
+				} else if ((S.HeadGirl.dick > 0) && canPenetrate(S.HeadGirl)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to suck cocks, cunts, and assholes. Your Head Girl uses ${his} penis as an effective teaching tool. ${slave.slaveName}'s <span class="green">oral skills have improved.</span>`);
+					slaveSkillIncrease('oral', slave, random(5, 10));
+				} else if ((S.HeadGirl.clit > 2)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to suck cocks, cunts, and assholes. Your Head Girl uses ${his} pseudophallus-sized clit as an effective teaching tool. ${slave.slaveName}'s <span class="green">oral skills have improved.</span>`);
+					slaveSkillIncrease('oral', slave, random(5, 10));
+				} else {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to suck cocks, cunts, and assholes. ${slave.slaveName}'s <span class="green">oral skills have improved.</span>`);
+				}
+				slaveSkillIncrease('oral', slave, Math.ceil(effectiveness / 10));
+			} else if (headGirlsTraining === "anal skill") {
+				slave.training = 0;
+				if (S.HeadGirl.fetish === "buttslut" && S.HeadGirl.fetishStrength > 60) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take it up the ass. Your Head Girl's enthusiasm for backdoor loving is infectious. ${slave.slaveName}'s <span class="green">anal skills have improved.</span>`);
+					slaveSkillIncrease('anal', slave, random(5, 10));
+				} else if ((S.HeadGirl.dick > 0) && canPenetrate(S.HeadGirl)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a dick up the butt. Your Head Girl uses ${his} penis as an effective teaching tool. ${slave.slaveName}'s <span class="green">anal skills have improved.</span>`);
+					slaveSkillIncrease('anal', slave, random(5, 10));
+				} else if ((S.HeadGirl.clit > 2)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a phallus up the butt. Your Head Girl uses ${his} pseudophallus-sized clit as an effective teaching tool. ${slave.slaveName}'s <span class="green">anal skills have improved.</span>`);
+					slaveSkillIncrease('anal', slave, random(5, 10));
+				} else {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a dick up the butt. ${slave.slaveName}'s <span class="green">anal skills have improved.</span>`);
+				}
+				slaveSkillIncrease('anal', slave, Math.ceil(effectiveness / 10));
+			} else if (headGirlsTraining === "fuck skill") {
+				slave.training = 0;
+				if (S.HeadGirl.energy > 95) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a dick. Your Head Girl's enthusiasm for sex is infectious. ${slave.slaveName}'s <span class="green">vanilla sex skills have improved.</span>`);
+					slaveSkillIncrease('vaginal', slave, random(5, 10));
+				} else if ((S.HeadGirl.dick > 0) && canPenetrate(S.HeadGirl)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a dick. Your Head Girl uses ${his} penis as an effective teaching tool. ${slave.slaveName}'s <span class="green">vanilla sex skills have improved.</span>`);
+					slaveSkillIncrease('vaginal', slave, random(5, 10));
+				} else if ((S.HeadGirl.clit > 2)) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a phallus. Your Head Girl uses ${his} pseudophallus-sized clit as an effective teaching tool. ${slave.slaveName}'s <span class="green">vanilla sex skills have improved.</span>`);
+					slaveSkillIncrease('vaginal', slave, random(5, 10));
+				} else {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to take a dick. ${slave.slaveName}'s <span class="green">vanilla sex skills have improved.</span>`);
+				}
+				slaveSkillIncrease('vaginal', slave, Math.ceil(effectiveness / 10));
+			} else if (headGirlsTraining === "whore skill") {
+				slave.training = 0;
+				r.push(`In spare moments ${he} teaches ${slave.slaveName} how to prostitute ${himself2}. ${slave.slaveName}'s <span class="green">whoring skills have improved.</span>`);
+				slaveSkillIncrease('whoring', slave, Math.ceil(effectiveness / 10));
+			} else if (headGirlsTraining === "entertain skill") {
+				slave.training = 0;
+				if (S.HeadGirl.fetish === "humiliation" && S.HeadGirl.fetishStrength > 60) {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to entertain. Your Head Girl's enthusiasm for public display is infectious. ${slave.slaveName}'s <span class="green">entertainment skills have improved.</span>`);
+					slaveSkillIncrease('entertainment', slave, random(5, 10));
+				} else {
+					r.push(`In spare moments ${he} teaches ${slave.slaveName} how to entertain. ${slave.slaveName}'s <span class="green">entertainment skills have improved.</span>`);
+				}
+				slaveSkillIncrease('entertainment', slave, Math.ceil(effectiveness / 10));
+			}
+		}
+
+		if (slave.devotion >= -20) {
+			if (slave.attrKnown === 0) {
+				slave.attrKnown = 1;
+				r.push(`${S.HeadGirl.slaveName} also investigates the slave's basic sexual orientation, finding that ${he2} is`);
+				if (slave.attrXY <= 40) {
+					r.push(`<span class="red">disgusted by men</span>`);
+				} else if ((slave.attrXY <= 60)) {
+					r.push(`indifferent to men`);
+				} else if ((slave.attrXY <= 80)) {
+					r.push(`<span class="green">attracted to men</span>`);
+				} else {
+					r.push(`<span class="green">intensely attracted to men</span>`);
+				}
+				r.push(`and`);
+				if (slave.attrXX <= 40) {
+					r.push(`<span class="red">disgusted by women.</span>`);
+				} else if ((slave.attrXX <= 60)) {
+					r.push(`indifferent to women.`);
+				} else if ((slave.attrXX <= 80)) {
+					r.push(`<span class="green">attracted to women.</span>`);
+				} else {
+					r.push(`<span class="green">intensely attracted to women.</span>`);
+				}
+			}
+		}
+
+		if (V.HGPiercings) {
+			if (slave.devotion >= -20 && slave.genes === "XY" && slave.attrXY <= 35 && V.arcologies[0].FSBodyPurist === "unset" && (V.arcologies[0].FSPaternalist === "unset" || SlaveStatsChecker.modScore(slave).total < 13)) {
+				piercingCheck();
+			}
+		}
+
+		App.Events.addNode(el, r, "div", "indent");
+		return el;
+
+		function piercingCheck() {
+			let piercingForbidden = 0;
+			if (slave.earPiercing === 0 && slave.earShape !== "none") {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					slave.earPiercing = 2;
+				} else {
+					slave.earPiercing = 1;
+				}
+				RulesDeconfliction(slave);
+				if (slave.earPiercing !== V.slaveAfterRA.earPiercing) {
+					piercingForbidden = 1;
+					slave.earPiercing = 0;
+				} else {
+					if (V.arcologies[0].FSDegradationist !== "unset") {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs to adjust to life as a slave${girl2}, so ${he} has the slave's ears pierced. This will be a constant reminder that ${he2} is simply a sexual object here.`);
+					} else {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} has the slave's ears pierced. A little feminine touch can make a big difference.`);
+					}
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					return;
+				}
+			}
+			if (slave.nosePiercing === 0) {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					slave.nosePiercing = 2;
+				} else {
+					slave.nosePiercing = 1;
+				}
+				RulesDeconfliction(slave);
+				if (slave.nosePiercing !== V.slaveAfterRA.nosePiercing) {
+					piercingForbidden = 1;
+					slave.nosePiercing = 0;
+				} else {
+					if (V.arcologies[0].FSDegradationist !== "unset") {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs to adjust to life as a slave${girl2}, so ${he} gives the slave nasal studs and a large septum ring. It should push ${slave.slaveName} to see ${himself2} as a sexual object for others to use.`);
+					} else {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} gives the slave a cute little nose piercing. It should help ${slave.slaveName} see ${himself2} as a bit more feminine.`);
+					}
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					return;
+				}
+			}
+			if (slave.eyebrowPiercing === 0) {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					slave.eyebrowPiercing = 2;
+				} else {
+					slave.eyebrowPiercing = 1;
+				}
+				RulesDeconfliction(slave);
+				if (slave.eyebrowPiercing !== V.slaveAfterRA.eyebrowPiercing) {
+					piercingForbidden = 1;
+					slave.eyebrowPiercing = 0;
+				} else {
+					if (V.arcologies[0].FSDegradationist !== "unset") {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs to adjust to life as a slave${girl2}, so ${he} gives the slave multiple eyebrow piercings. A slutty touch for a slave${girl2} should help ${him2} feel a little hungrier for cock.`);
+					} else {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} gives the slave a cute little eyebrow piercing. A slutty touch for a slave${girl2} should help ${him2} feel a little hungrier for cock.`);
+					}
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					return;
+				}
+			}
+			if (slave.lipsPiercing === 0) {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					slave.lipsPiercing = 2;
+				} else {
+					slave.lipsPiercing = 1;
+				}
+				RulesDeconfliction(slave);
+				if (slave.lipsPiercing !== V.slaveAfterRA.lipsPiercing) {
+					piercingForbidden = 1;
+					slave.lipsPiercing = 0;
+				} else {
+					if (V.arcologies[0].FSDegradationist !== "unset") {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs to adjust to life as a slave${girl2}, so ${he} has the slave's lower lip pierced. ${his2} mouth is for pleasing penises now, so it'll help ${him2} if it looks like it.`);
+					} else {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} has the slave's lower lip pierced. ${his2} mouth is for pleasing penises now, so it'll help ${him2} if it looks like it.`);
+					}
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					return;
+				}
+			}
+			if (slave.navelPiercing === 0) {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					slave.navelPiercing = 2;
+				} else {
+					slave.navelPiercing = 1;
+				}
+				RulesDeconfliction(slave);
+				if (slave.navelPiercing !== V.slaveAfterRA.navelPiercing) {
+					piercingForbidden = 1;
+					slave.navelPiercing = 0;
+				} else {
+					if (V.arcologies[0].FSDegradationist !== "unset") {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} has the slave's navel pierced with a big ring. Whatever ${he2} thinks in ${his2} mind, S.HeadGirl.slaveName makes clear to ${him2} that ${his2} body belongs to you.`);
+					} else {
+						r.push(`${S.HeadGirl.slaveName} knows that ${slave.slaveName} needs help adjusting to life as a slave${girl2}, so ${he} has the slave's navel pierced. The prettier ${his2} lower half looks, the less reluctant ${he2} should feel to take it up the butt.`);
+					}
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					return;
+				}
+			}
+			if (piercingForbidden) {
+				if (V.arcologies[0].FSDegradationist !== "unset") {
+					r.push(`${S.HeadGirl.slaveName} thinks some piercings might push ${slave.slaveName} to adjust to life as a slave${girl2}, but ${he} also knows you have rules applied to this slave that forbid it.`);
+				} else {
+					r.push(`${S.HeadGirl.slaveName} thinks some cute piercings might help ${slave.slaveName} adjust to life as a slave${girl2}, but ${he} also knows you have rules applied to this slave that forbid it.`);
+				}
+			}
+		}
+	}
+
+	/**
+	 * @returns {FC.HeadGirlTrainee[]}
+	 */
+	function slavesToTrain() {
+		if (S.HeadGirl) {
+			/** @type {FC.HeadGirlTrainee[][]} */
+			const HGPossibleSlaves = [[], [], [], [], [], []];
+			for (const slave of V.slaves) {
+				if (!assignmentVisible(slave) || slave.fuckdoll === 1 || slave.ID === V.BodyguardID || slave.ID === V.HeadGirlID || slave.fetish === "mindbroken") {
+					continue;
+				} else if (Array.isArray(V.personalAttention) && V.personalAttention.some(p => p.ID === slave.ID)) {
+					continue;
+				}
+
+				if (V.headGirlTrainsHealth && slave.health.condition < -20) {
+					HGPossibleSlaves[0].push({ID: slave.ID, training: "health"});
+					continue;
+				}
+
+				if (slave.health.tired < 50) {
+					const hasParaphilia = (App.Data.misc.paraphiliaList.includes(slave.sexualFlaw));
+					if (V.headGirlTrainsParaphilias && hasParaphilia) {
+						HGPossibleSlaves[1].push({ID: slave.ID, training: "paraphilia"});
+						continue;
+					}
+
+					if (V.headGirlTrainsFlaws || V.headGirlSoftensFlaws) {
+						if (slave.behavioralFlaw !== "none" || (slave.sexualFlaw !== "none" && !hasParaphilia)) {
+							if (V.headGirlSoftensFlaws) {
+								if (slave.devotion > 20) {
+									if ((slave.behavioralFlaw !== "none" && slave.behavioralQuirk === "none") || (slave.sexualFlaw !== "none" && slave.sexualQuirk === "none" && !hasParaphilia)) {
+										HGPossibleSlaves[3].push({ID: slave.ID, training: "soften"});
+									} else {
+										HGPossibleSlaves[3].push({ID: slave.ID, training: "flaw"});
+									}
+									continue;
+								}
+							} else if (V.headGirlTrainsFlaws) {
+								HGPossibleSlaves[2].push({ID: slave.ID, training: "flaw"});
+								continue;
+							}
+						}
+					}
+
+					if (V.headGirlTrainsObedience && slave.devotion <= 20 && slave.trust >= -20) {
+						HGPossibleSlaves[4].push({ID: slave.ID, training: "obedience"});
+						continue;
+					}
+
+					if (V.headGirlTrainsSkills) {
+						if (slave.skill.oral < S.HeadGirl.skill.oral) {
+							HGPossibleSlaves[5].push({ID: slave.ID, training: "oral skill"});
+						} else if ((slave.skill.vaginal < S.HeadGirl.skill.vaginal) && (slave.vagina > 0) && (canDoVaginal(slave))) {
+							HGPossibleSlaves[5].push({ID: slave.ID, training: "fuck skill"});
+						} else if ((slave.skill.anal < S.HeadGirl.skill.anal) && (slave.anus > 0) && (canDoAnal(slave))) {
+							HGPossibleSlaves[5].push({ID: slave.ID, training: "anal skill"});
+						} else if (slave.skill.whoring < S.HeadGirl.skill.whoring) {
+							HGPossibleSlaves[5].push({ID: slave.ID, training: "whore skill"});
+						} else if ((slave.skill.entertainment < S.HeadGirl.skill.entertainment) && !isAmputee(slave)) {
+							HGPossibleSlaves[5].push({ID: slave.ID, training: "entertain skill"});
+						}
+					}
+				}
+			}
+			// @ts-ignore - SC's flatten() is not typed correctly
+			return HGPossibleSlaves.flatten().slice(0, App.EndWeek.saVars.HGEnergy);
+		} else {
+			return [];
+		}
+	}
+};
diff --git a/src/endWeek/reports/personalAttention.js b/src/endWeek/reports/personalAttention.js
index bbe8dcb0aae1eb2d99400a44db52ddc7c2e7af2c..ecd47ff39e3c3587669db54aff7513b5a0d9b12d 100644
--- a/src/endWeek/reports/personalAttention.js
+++ b/src/endWeek/reports/personalAttention.js
@@ -30,7 +30,7 @@ globalThis.personalAttention = (function() {
 		if (slave.energy > 10) {
 			slave.energy -= 2;
 			el.append(`${His} `);
-			App.UI.DOM.appendNewElement("span", el, `appetite for sex is also reduced. `, "red");
+			App.UI.DOM.appendNewElement("span", el, `appetite for sex is also reduced. `, ["libido", "dec"]);
 		}
 		return el;
 	}
@@ -62,7 +62,7 @@ globalThis.personalAttention = (function() {
 			el.append(`Since ${he}'s obedient, `);
 			App.UI.DOM.appendNewElement("span", el, `${his} training assignment has defaulted to softening ${his} behavioral flaw. `, "yellow");
 			pa.trainingRegimen = "soften her behavioral flaw";
-		} else if ((slave.devotion > 20) && (slave.sexualQuirk === "none") && !["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "none", "self hating"].includes(slave.sexualFlaw)) {
+		} else if ((slave.devotion > 20) && (slave.sexualQuirk === "none") && !App.Data.misc.paraphiliaList.includes(slave.sexualFlaw) && slave.sexualFlaw !== "none") {
 			el.append(`Since ${he}'s obedient, `);
 			App.UI.DOM.appendNewElement("span", el, `${his} training assignment has defaulted to softening ${his} sexual flaw. `, "yellow");
 			pa.trainingRegimen = "soften her sexual flaw";
@@ -70,7 +70,7 @@ globalThis.personalAttention = (function() {
 			el.append(`Since ${he}'s obedient and already has a behavioral quirk, `);
 			App.UI.DOM.appendNewElement("span", el, `${his} training assignment has defaulted to removing ${his} behavioral flaw. `, "yellow");
 			pa.trainingRegimen = "fix her behavioral flaw";
-		} else if ((slave.devotion > 20) && !["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "none", "self hating"].includes(slave.sexualFlaw)) {
+		} else if ((slave.devotion > 20) && !App.Data.misc.paraphiliaList.includes(slave.sexualFlaw) && slave.sexualFlaw !== "none") {
 			el.append(`Since ${he}'s obedient and already has a sexual quirk, `);
 			App.UI.DOM.appendNewElement("span", el, `${his} training assignment has defaulted to removing ${his} sexual flaw. `, "yellow");
 			pa.trainingRegimen = "fix her sexual flaw";
@@ -88,7 +88,7 @@ globalThis.personalAttention = (function() {
 
 	/**
 	 * @param {App.Entity.SlaveState} slave
-	 * @returns {Node}
+	 * @returns {HTMLElement}
 	 */
 	function personalAttention(slave) {
 		const el = document.createElement("p");
@@ -295,12 +295,12 @@ globalThis.personalAttention = (function() {
 				}
 				if (slave.health.condition < 100) {
 					r.push(`Your close and expert attention improves ${his} health in a way drug treatment or mere medical intervention cannot.`);
-					r.push(App.UI.DOM.makeElement("span", `${His} health has improved.`, "green"));
+					r.push(App.UI.DOM.makeElement("span", `${His} health has improved.`, ["health", "inc"]));
 					improveCondition(slave, 10);
 				}
 				if (slave.health.tired > 10) {
 					r.push(`You watch over ${him} as ${he} sleeps, assuring`);
-					r.push(App.UI.DOM.makeElement("span", `a proper night's rest.`, "green"));
+					r.push(App.UI.DOM.makeElement("span", `a proper night's rest.`, ["health", "inc"]));
 					slave.health.tired = Math.clamp(slave.health.tired - 10, 0, 1000);
 				}
 				if (((slave.anus >= 3) || (slave.vagina >= 3)) && slave.geneMods.rapidCellGrowth !== 1) {
@@ -326,7 +326,7 @@ globalThis.personalAttention = (function() {
 					r.push(`${slave.slaveName} got over ${his} behavioral flaw without you;`);
 					coloredText = [];
 					coloredText.push(`${his} training assignment has defaulted to`);
-					if (["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "none", "self hating"].includes(slave.sexualFlaw)) {
+					if (App.Data.misc.paraphiliaList.includes(slave.sexualFlaw) && slave.sexualFlaw !== "none") {
 						if ((slave.devotion <= 20) && (slave.trust >= -20)) {
 							coloredText.push(`breaking ${his} will.`);
 							pa.trainingRegimen = "break her will";
@@ -942,11 +942,11 @@ globalThis.personalAttention = (function() {
 				if (slave.fetish === "mindbroken") {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
 					r.push(`${slave.slaveName}'s mind is broken. ${He} is a boring slave to torture, though ${his} body will still occasionally react to intense pain. No matter what you try, nothing really reaches ${his} destroyed soul. The agonies do`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 				} else if ((slave.devotion < -90)) {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
 					r.push(`Old traditions should not be forgotten. The scourge is the oldest slavebreaking tool known to man, and to slave${girl}s who do not properly obey ${womenP}. For the whole week, whenever ${slave.slaveName} disobeys you or whenever the whim strikes, you bind ${him} securely and flog ${him} without mercy. You use a soft leather appliance and apply medical care afterward, so there will be no permanent scarring, but`);
-					r.push(App.UI.DOM.makeElement("span", `${his} health is affected and the beatings leave ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `${his} health is affected and the beatings leave ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 					r.push(`${He} is subjected to`);
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience.`, "gold"));
@@ -962,7 +962,7 @@ globalThis.personalAttention = (function() {
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the extreme stress`);
-					r.push(App.UI.DOM.makeElement("span", `affects ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affects ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 					r.push(`${VCheck.Anal(slave, 6)}`);
 				} else if ((slave.scrotum > 0)) {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
@@ -978,28 +978,28 @@ globalThis.personalAttention = (function() {
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the beatings`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 				} else if ((slave.dick > 0)) {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
 					r.push(`${slave.slaveName} has an indefensible, obvious target for harsh breaking. Whenever ${he} falls short in the smallest way, you bind ${him} in such a way that ${his} cock is dangling defenseless, and ${he} cannot move to avoid blows. You then indulge your inventiveness, applying clips, weights, and simple beatings to ${his} member, while beating the rest of ${him} thoroughly. ${He} is subjected to`);
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the beatings`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 				} else if ((slave.clit > 0)) {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
 					r.push(`${slave.slaveName} has an indefensible, obvious target for harsh breaking. Whenever ${he} falls short in the smallest way, you bind ${him} in such a way that ${his} unusually large clit is visible and defenseless, and ${he} cannot move to avoid blows. You then indulge your inventiveness, applying clips, weights, and simple slaps to ${his} womanhood, while beating the rest of ${him} thoroughly. ${He} is subjected to`);
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the beatings`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 				} else if ((slave.nipples === "huge")) {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
 					r.push(`${slave.slaveName}'s nipples beg for punishment. Whenever ${he} falls short in the smallest way, you bind ${him} in such a way that breasts dangle, ${his} nipples are free and at your mercy, and ${he} can only move enough to cause ${his} boobs to sway erotically when ${he} flinches with pain. You then indulge your inventiveness, applying clips, weights, and simple abuse to ${his} nipples, while beating the rest of ${him} thoroughly. ${He} is subjected to`);
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the beatings`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health, leaving ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 				} else if ((slave.anus > 0) && canDoAnal(slave)) {
 					r.push(`You bind ${slave.slaveName} with the head of an uncomfortably large dildo just inside ${his} anus. The setup offers ${him} a choice: ${he} can either stand and have only tip up ${his} butt, or ${he} can take ${his} weight off ${his} legs, and take a massive phallus up the ass. You keep ${him} like this for hours on end. At the start ${he} tries to stand all the time. Then, ${he} tries to rest on it for short periods, but realizes that this up and down motion really just leads to ${him} assraping ${himself}. Finally, ${he} becomes so`);
 					r.push(App.UI.DOM.makeElement("span", `tired and apathetic`, "red"));
@@ -1007,7 +1007,7 @@ globalThis.personalAttention = (function() {
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the extreme stress`);
-					r.push(App.UI.DOM.makeElement("span", `affects ${his} health.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affects ${his} health.`, ["health", "dec"]));
 					slave.health.tired = Math.clamp(slave.health.tired + 20, 0, 1000);
 				} else {
 					slave.minorInjury = either("black eye", "bruise", "split lip");
@@ -1017,7 +1017,7 @@ globalThis.personalAttention = (function() {
 					r.push(App.UI.DOM.makeElement("span", `immense mental pressure`, "mediumorchid"));
 					r.push(App.UI.DOM.makeElement("span", `in favor of obedience,`, "gold"));
 					r.push(`but the extreme stress and rough treatment`);
-					r.push(App.UI.DOM.makeElement("span", `affect ${his} health and leave ${him} with a ${slave.minorInjury}.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `affect ${his} health and leave ${him} with a ${slave.minorInjury}.`, ["health", "dec"]));
 					if (slave.health.tired < 120) {
 						slave.health.tired = 120;
 					}
@@ -1452,8 +1452,6 @@ globalThis.personalAttention = (function() {
 				}
 				if (slave.fetishKnown !== 1) {
 					slave.fetishKnown = 1;
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`You then give ${him} a good exploratory fondle. You play with ${his} nipples and each of ${his} holes and gauge ${his} reaction.`);
 					if (slave.fetish === "boobs") {
 						r.push(`You've barely touched ${his} nipples before ${he} moans. After some experimentation, it becomes clear that ${his} nipples might as well be a pair of slightly less sensitive`);
@@ -1479,8 +1477,6 @@ globalThis.personalAttention = (function() {
 					} else {
 						r.push(`Nothing unusual happens.`);
 					}
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`Next, you demand extreme submission from ${him}. You make ${him} change into bondage gear that blinds ${him}, restricts ${his} movement, forces ${him} to present ${his} breasts uncomfortably, and holds vibrators against ${him}. Thus attired, ${he} is forced to serve you in whatever petty ways occur to you.`);
 					if (slave.fetish === "submissive") {
 						r.push(`During the first hour of this treatment, ${he} cums hard against the vibrators. ${He}'s a natural submissive! Discovering this about ${himself} under your hands has`);
@@ -1491,8 +1487,6 @@ globalThis.personalAttention = (function() {
 					} else {
 						r.push(`${He} complies, but ${he}'s not a natural submissive.`);
 					}
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`Before you let ${him} out of the extreme bondage, you rain a series of light blows across ${his} nipples and buttocks.`);
 					if (slave.fetish === "masochist") {
 						r.push(`${He} almost orgasms at the stinging pain. ${He}'s a masochist! This discovery has`);
@@ -1516,8 +1510,6 @@ globalThis.personalAttention = (function() {
 						r.push(`If ${he} had any special regard for cum, you'd know it, and ${he} doesn't.`);
 					}
 					const {himU, hisU, girlU} = getNonlocalPronouns(0).appendSuffix('U');
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`You carefully watch ${his} reaction as you let ${him} spend a short time relaxing with a pregnant slave.`);
 					if (slave.fetish === "pregnancy") {
 						r.push(`${He}'s fascinated. ${He} fetishizes fertility! Discovering this with you has`);
@@ -1528,8 +1520,6 @@ globalThis.personalAttention = (function() {
 					} else {
 						r.push(`${He} simply enjoys the rest.`);
 					}
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`You restrain the pregnant slave and administer a brief beating across ${hisU} bare buttocks, ensuring that you cause enough pain to produce a few tears, a bit of begging, and some struggling.`);
 					if (slave.fetish === "sadist") {
 						r.push(`${He}'s almost painfully aroused. ${He}'s titillated by the idea of causing pain! Discovering this about ${himself} under your direction has`);
@@ -1548,8 +1538,6 @@ globalThis.personalAttention = (function() {
 						}
 						r.push(`of you punishing the pregnant ${girlU}.`);
 					}
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`Before letting the poor pregnant slave go, you require ${slave.slaveName} to add a blindfold to the restraints.`);
 					if (slave.fetish === "dom") {
 						r.push(`${He} seems to really enjoy blindfolding the poor ${girlU}, reassuring ${himU} as ${he} does. ${He}'s a natural sexual top! Discovering this about ${himself} under your direction has`);
@@ -1560,8 +1548,6 @@ globalThis.personalAttention = (function() {
 					} else {
 						r.push(`${He} just follows orders.`);
 					}
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`Lastly, you place ${him} in a special room in your penthouse filled with live video equipment. They get to see ${him} groped, deepthroated, facialed, teased, and tortured.`);
 					if (slave.fetish === "humiliation") {
 						r.push(`The more viewers ${he} gets, the harder ${he} comes. ${He}'s a slut for humiliation! Discovering this about ${himself} under your hands has`);
@@ -1582,8 +1568,6 @@ globalThis.personalAttention = (function() {
 						}
 					}
 				} else {
-					App.Events.addParagraph(el, r);
-					r = [];
 					r.push(`You already know that ${he}`);
 					switch (slave.fetish) {
 						case "buttslut":
diff --git a/src/endWeek/rulesAssistantReport.js b/src/endWeek/reports/rulesAssistantReport.js
similarity index 100%
rename from src/endWeek/rulesAssistantReport.js
rename to src/endWeek/reports/rulesAssistantReport.js
diff --git a/src/endWeek/schoolroomReport.js b/src/endWeek/reports/schoolroomReport.js
similarity index 94%
rename from src/endWeek/schoolroomReport.js
rename to src/endWeek/reports/schoolroomReport.js
index 9e675864fcd23d341a6c07bfe82b079bf6ec48ef..e6854c231a8798b2399dfb0a54523d9c40e24ab6 100644
--- a/src/endWeek/schoolroomReport.js
+++ b/src/endWeek/reports/schoolroomReport.js
@@ -59,7 +59,7 @@ App.EndWeek.schoolroomReport = function() {
 			} else if (FLsFetish === 2) {
 				r.push(`Every new student in class is a new target for ${his} personal educational attention. <span class="lightsalmon">${He} becomes more dominant.</span>`);
 			}
-			if (setup.schoolteacherCareers.includes(S.Schoolteacher.career)) {
+			if (App.Data.Careers.Leader.schoolteacher.includes(S.Schoolteacher.career)) {
 				r.push(`${He} has experience with students and learning from ${his} life before ${he} was a slave, making ${him} more effective.`);
 				idleBonus++;
 			} else if (S.Schoolteacher.skill.teacher >= V.masteredXP) {
@@ -146,15 +146,14 @@ App.EndWeek.schoolroomReport = function() {
 
 	if (S.Schoolteacher) {
 		const slave = S.Schoolteacher;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const schoolteacherEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", schoolteacherEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			schoolteacherEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(schoolteacherEntry, slave);
+			schoolteacherEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(schoolteacherEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> is serving as your Schoolteacher.`);
-			$(schoolteacherEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			schoolteacherEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -224,10 +223,8 @@ App.EndWeek.schoolroomReport = function() {
 		if (V.showEWD !== 0) {
 			const {He} = getPronouns(slave);
 			const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(slaveEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> `);
 			if (slave.choosesOwnAssignment === 2) {
 				$(slaveEntry).append(App.SlaveAssignment.choosesOwnJob(slave));
@@ -242,7 +239,7 @@ App.EndWeek.schoolroomReport = function() {
 			}
 			const studentContent = App.UI.DOM.appendNewElement("div", slaveEntry, '', "indent");
 			$(studentContent).append(`${He} ${App.SlaveAssignment.takeClasses(slave)}`);
-			$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/endWeek/servantsQuartersReport.js b/src/endWeek/reports/servantsQuartersReport.js
similarity index 95%
rename from src/endWeek/servantsQuartersReport.js
rename to src/endWeek/reports/servantsQuartersReport.js
index 75a67b0b63ebbe5938e3eb7b0f912a9201302f96..3e09cb8b1a2139fb4d3e355003cbc49cd8185f7b 100644
--- a/src/endWeek/servantsQuartersReport.js
+++ b/src/endWeek/reports/servantsQuartersReport.js
@@ -94,7 +94,7 @@ App.EndWeek.servantsQuartersReport = function() {
 				stewardessBonus += 75;
 				r.push(`${His} perfect health allows ${him} to work exhaustive hours and <span class="yellowgreen">drive</span> the servants very hard.`);
 			}
-			if (setup.stewardessCareers.includes(S.Stewardess.career)) {
+			if (App.Data.Careers.Leader.stewardess.includes(S.Stewardess.career)) {
 				stewardessBonus += 25;
 				r.push(`${He} has applicable experience with daily sums and organizational trifles from ${his} life before ${he} was a slave.`);
 			} else if (S.Stewardess.skill.stewardess >= V.masteredXP) {
@@ -204,17 +204,15 @@ App.EndWeek.servantsQuartersReport = function() {
 	}
 
 	if (S.Stewardess) {
-		/** @type {App.Entity.SlaveState} */
 		const slave = S.Stewardess;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const stewardessEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", stewardessEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			stewardessEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(stewardessEntry, slave);
+			stewardessEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(stewardessEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> is serving as your Stewardess.`);
-			$(stewardessEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			stewardessEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			App.SlaveAssignment.standardSlaveReport(slave, true);
 		}
@@ -284,10 +282,8 @@ App.EndWeek.servantsQuartersReport = function() {
 		if (V.showEWD !== 0) {
 			const {He} = getPronouns(slave);
 			const slaveEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			$(slaveEntry).append(`<span class='slave-name'>${SlaveFullName(slave)}</span> `);
 			if (slave.choosesOwnAssignment === 2) {
 				$(slaveEntry).append(App.SlaveAssignment.choosesOwnJob(slave));
@@ -303,7 +299,7 @@ App.EndWeek.servantsQuartersReport = function() {
 				SQMilk += milkResults.milk;
 				SQMilkSale += milkResults.milkSale;
 			}
-			$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/endWeek/reports/spaReport.js b/src/endWeek/reports/spaReport.js
index f2057ae42464bce93b6889fd4233a9bc113ca749..2e70f4714f460f9e08203710701e3c58f457cb3d 100644
--- a/src/endWeek/reports/spaReport.js
+++ b/src/endWeek/reports/spaReport.js
@@ -6,18 +6,17 @@ App.EndWeek.spaReport = function() {
 	let r;
 
 	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.spa);
-	let DL = slaves.length;
-	let bonusToggle = 0, healthBonus = 0, idleBonus = 0, restedSlaves = 0, restedSlave, devBonus;
+	let healthBonus = 0, restedSlaves = 0, restedSlave;
+	let devBonus = (V.spaDecoration !== "standard") ? 1 : 0;
 
-	if (V.spaDecoration !== "standard") {
-		devBonus = 1;
-	} else {
-		devBonus = 0;
-	}
 	V.flSex = App.EndWeek.getFLSex(App.Entity.facilities.spa); /* FIXME: should be local, passed as a parameter to saRules */
 
-	if (V.AttendantID !== 0) {
+	if (S.Attendant) {
+		let idleBonus = 0;
+		let bonusToggle = 0;
+		let FLsFetish = 0;
 		r = [];
+
 		if (S.Attendant.health.condition < 100) {
 			improveCondition(S.Attendant, normalRandInt(20));
 		}
@@ -30,7 +29,6 @@ App.EndWeek.spaReport = function() {
 		if (S.Attendant.rules.living !== "luxurious") {
 			S.Attendant.rules.living = "luxurious";
 		}
-		let FLsFetish = 0;
 		if (S.Attendant.fetishStrength <= 95) {
 			if (S.Attendant.fetish !== "submissive") {
 				if (fetishChangeChance(S.Attendant) > random(0, 100)) {
@@ -63,7 +61,7 @@ App.EndWeek.spaReport = function() {
 		} else if ((FLsFetish === 2)) {
 			r.push(`Every new slave in the spa is a new person ${he} gets to connect with and serve. Sexually. <span class="lightsalmon">${He} becomes more submissive.</span>`);
 		}
-		if (setup.attendantCareers.includes(S.Attendant.career)) {
+		if (App.Data.Careers.Leader.attendant.includes(S.Attendant.career)) {
 			r.push(`${He} has experience with counseling from ${his} life before ${he} was a slave, making ${him} better at building rapport with troubled slaves, and giving ${him} a better chance of softening flaws into beneficial quirks.`);
 			bonusToggle = 1;
 			idleBonus++;
@@ -101,13 +99,14 @@ App.EndWeek.spaReport = function() {
 			idleBonus++;
 			healthBonus++;
 		}
-		let attendantUsedCure = 0;
+		let attendantUsedCure = false;
 		App.Events.addNode(el, r, "div", "indent");
+		const softenFlawBonus = bonusToggle ? 10 : 0;
 		for (const slave of slaves) {
 			const {he2, his2, him2} = getPronouns(slave).appendSuffix("2");
 			r = [];
-			if (slave.fetish === "mindbroken" && slave.health.condition > 20 && attendantUsedCure === 0 && V.spaFix !== 2) {
-				attendantUsedCure = 1;
+			if (slave.fetish === "mindbroken" && slave.health.condition > 20 && !attendantUsedCure && V.spaFix !== 2) {
+				attendantUsedCure = true;
 				if (random(1, 100) > 90 - S.Attendant.devotion) {
 					const curedSlave = App.UI.DOM.makeElement("div", null, "indent");
 					const curedArray = [`<span class="green">Something almost miraculous has happened.</span> ${S.Attendant.slaveName} has always refused to believe that ${slave.slaveName} could not be reached, and has lavished patient tenderness on ${him2} in ${V.spaName}. ${slave.slaveName} has begun to respond, and is stirring from ${his2} mental torpor.`];
@@ -129,7 +128,6 @@ App.EndWeek.spaReport = function() {
 					r.push(curedSlave);
 				}
 			}
-			const seed = bonusToggle * 10;
 			if (bonusToggle === 1 && slave.trust < 60) {
 				slave.trust++;
 			}
@@ -231,20 +229,20 @@ App.EndWeek.spaReport = function() {
 					}
 			}
 			if (
+				!attendantUsedCure &&
 				(S.Attendant.intelligence + S.Attendant.intelligenceImplant > 15) &&
-				(attendantUsedCure === 0) &&
-				(S.Attendant.intelligence + S.Attendant.intelligenceImplant + seed) > random(1, 200) &&
+				(S.Attendant.intelligence + S.Attendant.intelligenceImplant + softenFlawBonus) > random(1, 200) &&
 				(V.spaFix === 0)
 			) {
 				if (slave.behavioralFlaw !== "none") {
 					SoftenBehavioralFlaw(slave);
-					attendantUsedCure += 1;
+					attendantUsedCure = true;
 					r.push(`${S.Attendant.slaveName} works carefully with ${slave.slaveName}, and successfully`);
 					r.push(App.UI.DOM.makeElement("span", `softens ${his2} behavioral flaw`, "green"));
 					r.push(`into an appealing quirk.`);
 				} else if (slave.sexualFlaw !== "none") {
 					SoftenSexualFlaw(slave);
-					attendantUsedCure += 1;
+					attendantUsedCure = true;
 					r.push(`${S.Attendant.slaveName} works carefully with ${slave.slaveName}, and successfully`);
 					r.push(App.UI.DOM.makeElement("span", `softens ${his2} sexual flaw`, "green"));
 					r.push(`into an appealing quirk.`);
@@ -259,12 +257,12 @@ App.EndWeek.spaReport = function() {
 			App.Events.addNode(el, r, "div", "indent");
 		}
 
-		if (DL < V.spa) {
-			const seed = random(1, 10) + ((V.spa - DL) * (random(150, 170) + (idleBonus * 10)));
+		if (slaves.length < V.spa) {
+			const seed = random(1, 10) + ((V.spa - slaves.length) * (random(150, 170) + (idleBonus * 10)));
 			cashX(seed, "spa", S.Attendant);
 			r = [];
 			r.push(`Since ${he} doesn't have enough slaves to occupy all ${his} time, the spa takes in citizens' slaves on a contract basis and ${he} helps them too, earning <span class="yellowgreen"> ${cashFormat(seed)}.</span>`);
-			if (V.arcologies[0].FSHedonisticDecadence > 0 && DL === 0) {
+			if (V.arcologies[0].FSHedonisticDecadence > 0 && slaves.length === 0) {
 				r.push(`Society <span class="green">loves</span> being allowed to lounge in your spa, greatly advancing your laid back culture.`);
 				FutureSocieties.Change("Hedonistic", 2);
 			}
@@ -272,30 +270,25 @@ App.EndWeek.spaReport = function() {
 		}
 	}
 
-	if (DL > 0) {
+	if (slaves.length > 0) {
 		r = [];
-		if (DL > 1) {
-			r.push(`There are ${DL} slaves`);
+		if (slaves.length > 1) {
+			r.push(`There are ${slaves.length} slaves`);
 		} else {
 			r.push(`There is one slave`);
 		}
 		r.push(`resting and recuperating in the spa.`);
-		if (V.arcologies[0].FSHedonisticDecadence > 0 && DL === 0) {
-			r.push(`Society <span class="green">approves</span> of your slaves being pampered this way, greatly advancing your laid back culture.`);
-			FutureSocieties.Change("Hedonistic", 1);
-		}
 		App.Events.addNode(el, r, "p", ["indent", "bold"]);
 	}
 
 	if (S.Attendant) {
 		const slave = S.Attendant;
+		tired(slave);
 		/* apply following SA passages to facility leader */
 		if (V.showEWD !== 0) {
 			const attendantEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", attendantEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			attendantEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(attendantEntry, slave);
+			attendantEntry.append(App.UI.favoriteToggle(slave), " ");
 			App.Events.addNode(
 				attendantEntry,
 				[
@@ -374,16 +367,13 @@ App.EndWeek.spaReport = function() {
 			el.append(slaveFixed);
 			restedSlaves++;
 			restedSlave = slave;
-			DL--;
 			continue;
 		}
 
 		if (V.showEWD !== 0) {
 			const slaveEntry = App.UI.DOM.appendNewElement("div", el, '', "slave-report");
-			if (V.seeImages && V.seeReportImages) {
-				App.UI.DOM.appendNewElement("div", slaveEntry, App.Art.SlaveArtElement(slave, 0, 0), ["imageRef", "tinyImg"]);
-			}
-			slaveEntry.append(App.EndWeek.favoriteIcon(slave), " ");
+			App.SlaveAssignment.appendSlaveArt(slaveEntry, slave);
+			slaveEntry.append(App.UI.favoriteToggle(slave), " ");
 			r = [];
 			r.push(App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name"));
 			if (slave.choosesOwnAssignment === 2) {
@@ -391,7 +381,7 @@ App.EndWeek.spaReport = function() {
 			} else {
 				r.push(`is resting in ${V.spaName}.`);
 			}
-			App.Events.addNode(slaveEntry, r, "div");
+			App.Events.addNode(slaveEntry, r);
 
 			r = [];
 			r.push(He);
@@ -408,8 +398,8 @@ App.EndWeek.spaReport = function() {
 			} else if (slave.health.tired > 30) {
 				r.push(`${He} remains in the Spa, continuing to soak away ${his} fatigue.`);
 			}
-			r.push(App.SlaveAssignment.standardSlaveReport(slave, false));
 			App.Events.addNode(slaveEntry, r, "div", "indent");
+			slaveEntry.append(App.SlaveAssignment.standardSlaveReport(slave, false));
 		} else {
 			// discard return values silently
 			App.SlaveAssignment.choosesOwnJob(slave);
diff --git a/src/endWeek/reportsTW/brothelReport.tw b/src/endWeek/reportsTW/brothelReport.tw
deleted file mode 100644
index 933e96f16f578fec98c6942a03cf9cd7f0223036..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/brothelReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Brothel Report [nobr]
-
-<<includeDOM brothelReport()>>
diff --git a/src/endWeek/reportsTW/childrenReport.tw b/src/endWeek/reportsTW/childrenReport.tw
deleted file mode 100644
index 25e61aba4a5364839ca02d639891c2e218bf7350..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/childrenReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Children Report [nobr]
-
-<<includeDOM App.Facilities.Nursery.childrenReport()>>
diff --git a/src/endWeek/reportsTW/clinicReport.tw b/src/endWeek/reportsTW/clinicReport.tw
deleted file mode 100644
index 8478322d5f928bfaa5782fe62866fe04b2d7989d..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/clinicReport.tw
+++ /dev/null
@@ -1,5 +0,0 @@
-:: Clinic Report [nobr]
-
-/* This passage probably isn't really necessary but it helps organize the profiler output.
- * TODO: Once all the facility reports look like this, we should probably get rid of these intermediate passages. */
-<<includeDOM App.EndWeek.clinicReport()>>
diff --git a/src/endWeek/reportsTW/clubReport.tw b/src/endWeek/reportsTW/clubReport.tw
deleted file mode 100644
index c345a8117e0533450cebafdeec7c0338106ce2b7..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/clubReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Club Report [nobr]
-
-<<includeDOM App.EndWeek.clubReport()>>
diff --git a/src/endWeek/reportsTW/dairyReport.tw b/src/endWeek/reportsTW/dairyReport.tw
deleted file mode 100644
index c299f843e0c2d1a866783854c32efe328bb221be..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/dairyReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Dairy Report [nobr]
-
-<<includeDOM App.EndWeek.dairyReport()>>
diff --git a/src/endWeek/reportsTW/farmyardReport.tw b/src/endWeek/reportsTW/farmyardReport.tw
deleted file mode 100644
index f1f3443c0b0576a383498b8498646cc3c34cac52..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/farmyardReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Farmyard Report [nobr]
-
-<<includeDOM App.Facilities.Farmyard.farmyardReport()>>
diff --git a/src/endWeek/reportsTW/masterSuiteReport.tw b/src/endWeek/reportsTW/masterSuiteReport.tw
deleted file mode 100644
index de0391e8f8020139f88db5f4421f60af6b01f701..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/masterSuiteReport.tw
+++ /dev/null
@@ -1,5 +0,0 @@
-:: Master Suite Report [nobr]
-
-/* This passage probably isn't really necessary but it helps organize the profiler output.
- * TODO: Once all the facility reports look like this, we should probably get rid of these intermediate passages. */
-<<includeDOM App.EndWeek.masterSuiteReport()>>
diff --git a/src/endWeek/reportsTW/nurseryReport.tw b/src/endWeek/reportsTW/nurseryReport.tw
deleted file mode 100644
index 4f318e0e1c9fdfb0c96f0fbea11a23034881c267..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/nurseryReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Nursery Report [nobr]
-
-<<includeDOM App.Facilities.Nursery.nurseryReport()>>
diff --git a/src/endWeek/reportsTW/schoolroomReport.tw b/src/endWeek/reportsTW/schoolroomReport.tw
deleted file mode 100644
index 2d651a8e6642991fa209ba41e731453c85bff245..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/schoolroomReport.tw
+++ /dev/null
@@ -1,5 +0,0 @@
-:: Schoolroom Report [nobr]
-
-/* This passage probably isn't really necessary but it helps organize the profiler output.
- * TODO: Once all the facility reports look like this, we should probably get rid of these intermediate passages. */
-<<includeDOM App.EndWeek.schoolroomReport()>>
diff --git a/src/endWeek/reportsTW/servantsQuartersReport.tw b/src/endWeek/reportsTW/servantsQuartersReport.tw
deleted file mode 100644
index 6abf6fc4e9d59aec7819e2b6c0eaa63e013633f6..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/servantsQuartersReport.tw
+++ /dev/null
@@ -1,5 +0,0 @@
-:: Servants' Quarters Report [nobr]
-
-/* This passage probably isn't really necessary but it helps organize the profiler output.
- * TODO: Once all the facility reports look like this, we should probably get rid of these intermediate passages. */
-<<includeDOM App.EndWeek.servantsQuartersReport()>>
diff --git a/src/endWeek/reportsTW/spaReport.tw b/src/endWeek/reportsTW/spaReport.tw
deleted file mode 100644
index 66addcfb79267c42028517494fb0128cb299fc59..0000000000000000000000000000000000000000
--- a/src/endWeek/reportsTW/spaReport.tw
+++ /dev/null
@@ -1,3 +0,0 @@
-:: Spa Report [nobr]
-
-<<includeDOM App.EndWeek.spaReport()>>
diff --git a/src/endWeek/saBeYourHeadGirl.js b/src/endWeek/saBeYourHeadGirl.js
index 752a166b71265bfa8a570c4ddde56656beaf0b5d..eef75b7e0cb342a73048f04712185514c1379f58 100644
--- a/src/endWeek/saBeYourHeadGirl.js
+++ b/src/endWeek/saBeYourHeadGirl.js
@@ -135,7 +135,7 @@ App.SlaveAssignment.beYourHeadGirl = (function() {
 			}
 		}
 		if (App.EndWeek.saVars.HGEnergy === 0) {
-			r.push(`${He} notices ${his} <span class="red">fatigue getting in the way</span> of training your slaves, so ${he} focuses ${his} attention on ${himself} this week to <span class="green">rectify this.</span>`);
+			r.push(`${He} notices ${his} <span class="health dec">fatigue getting in the way</span> of training your slaves, so ${he} focuses ${his} attention on ${himself} this week to <span class="health inc">rectify this.</span>`);
 		}
 	}
 
@@ -277,7 +277,7 @@ App.SlaveAssignment.beYourHeadGirl = (function() {
 	 *
 	 */
 	function cleanupVars(slave) {
-		if (!setup.HGCareers.includes(slave.career) && slave.skill.headGirl < V.masteredXP) {
+		if (!App.Data.Careers.Leader.HG.includes(slave.career) && slave.skill.headGirl < V.masteredXP) {
 			slave.skill.headGirl += jsRandom(1, Math.ceil((slave.intelligence + slave.intelligenceImplant) / 15) + 8);
 		}
 		slave.health.tired = Math.clamp(slave.health.tired, 0, 1000);
diff --git a/src/endWeek/saChoosesOwnJob.js b/src/endWeek/saChoosesOwnJob.js
index f59353abde47fc5268fa7546b00439183a53685a..f6676c62fda3af1860f92ce9c18fd2daad155a86 100644
--- a/src/endWeek/saChoosesOwnJob.js
+++ b/src/endWeek/saChoosesOwnJob.js
@@ -75,89 +75,89 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 			 if (slave.relationship === -3 && slave.devotion < -20) {
 				choice.push(`is reluctantly married to you, and ${he} thinks of all the ways ${he} <span class="devotion dec">can take advantage of this,</span>`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.spa > spaL) {
-					choice.push(`so ${he} heads straight to ${V.spaName} to relax.`);
+					choice.push(`so ${he} <span class="job change">heads straight to ${V.spaName}</span> to relax.`);
 					choice.push(assignJob(slave, "rest in the spa"));
 				} else {
-					choice.push(`so ${he} cheerfully decides to lounge about the penthouse.`);
+					choice.push(`so ${he} cheerfully <span class="job change">decides to lounge about the penthouse.</span>`);
 					choice.push(removeJob(slave, slave.assignment));
 				}
 			 } else {
-				choice.push(`decides to rest, taking advantage of your permission to <span class="devotion dec">remain indolent.</span>`);
+				choice.push(`<span class="job change">decides to rest,</span> taking advantage of your permission to <span class="devotion dec">remain indolent.</span>`);
 				choice.push(removeJob(slave, slave.assignment));
 			 }
 			 slave.devotion -= 5;
 		} else if (slave.health.illness > 1) {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.clinic > clinicL) {
-				choice.push(`is ill, so ${he} decides to get treatment at ${V.clinicName}.`);
+				choice.push(`is ill, so ${he} <span class="job change">decides to get treatment at ${V.clinicName}.</span>`);
 				choice.push(assignJob(slave, "get treatment in the clinic"));
 			} else {
-				choice.push(`is ill, so ${he} decides to rest.`);
+				choice.push(`is ill, so ${he} <span class="job change">decides to rest.</span>`);
 				choice.push(removeJob(slave, slave.assignment));
 			}
 		} else if (slave.health.condition < 20) {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.spa > spaL) {
-				choice.push(`is unhealthy, so ${he} decides to get recover at ${V.spaName}.`);
+				choice.push(`is unhealthy, so ${he} <span class="job change">decides to recover at ${V.spaName}.</span>`);
 				choice.push(assignJob(slave, "rest in the spa"));
 			} else {
-				choice.push(`is unhealthy, so ${he} decides to rest.`);
+				choice.push(`is unhealthy, so ${he} <span class="job change">decides to rest.</span>`);
 				choice.push(removeJob(slave, slave.assignment));
 			}
 		} else if (slave.health.tired > 45) {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.spa > spaL) {
-				choice.push(`is tired, so ${he} decides to recover at ${V.spaName}.`);
+				choice.push(`is tired, so ${he} <span class="job change">decides to recover at ${V.spaName}.</span>`);
 				choice.push(assignJob(slave, "rest in the spa"));
 			} else {
-				choice.push(`is tired, so ${he} decides to rest.`);
+				choice.push(`is tired, so ${he} <span class="job change">decides to rest.</span>`);
 				choice.push(removeJob(slave, slave.assignment));
 			}
 		} else if (slave.boobsMilk > 0) {
-			choice.push(`rushes to get milked since ${his} breasts have become painfully engorged.`);
+			choice.push(`<span class="job change">rushes to get milked</span> since ${his} breasts have become painfully engorged.`);
 			choice.push(assignJob(slave, "get milked"));
 		} else if (slave.intelligenceImplant < 15 && slave.intelligence > 10 && (arcology.FSPaternalist > 20 || arcology.FSSlaveProfessionalism > 20) && arcology.FSGenderFundamentalistLawBimbo !== 1 ) {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.schoolroom > schoolL) {
-				choice.push(`is uneducated, so ${he} sits ${himself} down in ${V.schoolroomName}.`);
+				choice.push(`is uneducated, so ${he} <span class="job change">sits ${himself} down in ${V.schoolroomName}.</span>`);
 				choice.push(assignJob(slave, "learn in the schoolroom"));
 			} else {
-				choice.push(`is uneducated, so ${he} sits ${himself} down to learn.`);
+				choice.push(`is uneducated, so ${he} <span class="job change">sits ${himself} down to learn.</span>`);
 				choice.push(assignJob(slave, "take classes"));
 			}
 		} else if ((S.Attendant && V.universalRulesAssignsSelfFacility === 1 && V.spa > spaL) && (slave.devotion < 45 || slave.trust < 45 || (slave.sexualFlaw !== "none" && !setup.paraphiliaList.includes(slave.sexualFlaw)) || slave.behavioralFlaw !== "none")) {
-			choice.push(`could use some counseling, so ${he} decides to visit ${V.spaName}.`);
+			choice.push(`could use some counseling, so ${he} <span class="job change">decides to visit ${V.spaName}.</span>`);
 			choice.push(assignJob(slave, "rest in the spa"));
 		} else if (slave.devotion <= 50 && canWalk(slave) && canSee(slave)) {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.servantsQuarters > servQL) {
-				choice.push(`is obedient but not devoted, so ${he} decides to work from ${V.servantsQuartersName} since it's the least sexually demanding job available.`);
+				choice.push(`is obedient but not devoted, so ${he} <span class="job change">decides to work from ${V.servantsQuartersName}</span> since it's the least sexually demanding job available.`);
 				choice.push(assignJob(slave, "work as a servant"));
 			} else {
-				choice.push(`is obedient but not devoted, so ${he} decides to work as a servant since it's the least sexually demanding job available.`);
+				choice.push(`is obedient but not devoted, so ${he} <span class="job change">decides to work as a servant</span> since it's the least sexually demanding job available.`);
 				choice.push(assignJob(slave, "be a servant"));
 			}
 		} else if (V.universalRulesAssignsSelfFacility === 1 && slave.devotion > 50 && canWalk(slave) && canSee(slave) && V.nurseryNannies > nurseryL && (V.cribs.findIndex((c) => (c.mother === slave.ID || c.father === slave.ID)))) {
 			 if (V.cribs.findIndex((c) => (c.mother === slave.ID || c.father === slave.ID))) {
-				choice.push(`wants to look after ${his} child, so ${he} decides to work in ${V.nurseryName}.`);
+				choice.push(`wants to look after ${his} child, so ${he} <span class="job change">decides to work in ${V.nurseryName}.</span>`);
 				choice.push(assignJob(slave, "work as a nanny"));
 			} else { // motherly sexualQuirk
-				choice.push(`enjoys taking care of children, so ${he} decides to work in ${V.nurseryName}.`);
+				choice.push(`enjoys taking care of children, so ${he} <span class="job change">decides to work in ${V.nurseryName}.</span>`);
 				choice.push(assignJob(slave, "work as a nanny"));
 			}
 		} else if (slave.relationship === -1) {
 			choice.push(`relies on promiscuity to fulfill ${his} emotional needs,`);
 			if (V.cash < 10000) {
-				choice.push(`and doesn't mind being a whore, so ${he} prostitutes ${himself}`);
+				choice.push(`and doesn't mind being a whore, <span class="job change">so ${he} prostitutes ${himself}`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`in ${V.brothelName}.`);
+					choice.push(`in ${V.brothelName}.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`on the streets.`);
+					choice.push(`on the streets.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 			} else {
-				choice.push(`so ${he} eagerly decides to slut around`);
+				choice.push(`so ${he} eagerly <span class="job change">decides to slut around`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`in ${V.clubName}.`);
+					choice.push(`in ${V.clubName}.</span>`);
 					choice.push(assignJob(slave, "serve in the club"));
 				} else {
-					choice.push(`on the streets.`);
+					choice.push(`on the streets.</span>`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
@@ -165,50 +165,50 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 		} else if (slave.relationship === -2) {
 			choice.push(`is emotionally bonded to you,`);
 			if (slave.behavioralQuirk === "insecure") {
-				choice.push(`but insecure, so ${he} decides to make you money by prostituting ${himself}`);
+				choice.push(`but insecure, so ${he} <span class="job change">decides to make you money by prostituting ${himself}`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`in ${V.brothelName}.`);
+					choice.push(`in ${V.brothelName}.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`on the streets.`);
+					choice.push(`on the streets.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.behavioralQuirk === "advocate") {
-				choice.push(`and an advocate for slavery, so ${he} decides to burnish your reputation by slutting it up`);
+				choice.push(`and an advocate for slavery, so ${he} <span class="job change">decides to burnish your reputation by slutting it up`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`in ${V.clubName}.`);
+					choice.push(`in ${V.clubName}.</span>`);
 					choice.push(assignJob(slave, "serve in the club"));
 				} else {
-					choice.push(`on the streets.`);
+					choice.push(`on the streets.</span>`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.energy > 60) {
 				choice.push(`and ${he} thinks of little but sex with you,`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-					choice.push(`so ${he} heads straight to ${V.masterSuiteName}.`);
+					choice.push(`so ${he} <span class="job change">heads straight to ${V.masterSuiteName}.</span>`);
 					choice.push(assignJob(slave, "serve in the master suite"));
 				} else {
-					choice.push(`so ${he} cheerfully designates ${himself} one of your fucktoys.`);
+					choice.push(`so ${he} cheerfully <span class="job change">designates ${himself} one of your fucktoys.</span>`);
 					choice.push(assignJob(slave, "please you"));
 				}
 			} else if (canSee(slave) && canWalk(slave)) {
-				choice.push(`so ${he} decides to work`);
+				choice.push(`so ${he} <span class="job change">decides to work`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.servantsQuarters > servQL) {
-					choice.push(`from ${V.servantsQuartersName} to make your penthouse as clean and homelike as possible.`);
+					choice.push(`from ${V.servantsQuartersName}</span> to make your penthouse as clean and homelike as possible.`);
 					choice.push(assignJob(slave, "work as a servant"));
 				} else {
-					choice.push(`as a servant to make your penthouse as clean and homelike as possible.`);
+					choice.push(`as a servant</span> to make your penthouse as clean and homelike as possible.`);
 					choice.push(assignJob(slave, "be a servant"));
 				}
 			} else {
 				choice.push(`but unable to do much on ${his} own,`);
 				if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-					choice.push(`so ${he} heads straight to ${V.masterSuiteName} to await your caress.`);
+					choice.push(`so ${he} <span class="job change">heads straight to ${V.masterSuiteName} to await your caress.</span>`);
 					choice.push(assignJob(slave, "serve in the master suite"));
 				} else {
-					choice.push(`so ${he} cheerfully designates ${himself} one of your fucktoys to be close to you.`);
+					choice.push(`so ${he} cheerfully <span class="job change">designates ${himself} one of your fucktoys</span> to be close to you.`);
 					choice.push(assignJob(slave, "please you"));
 				}
 			}
@@ -224,122 +224,122 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 				if (slave.energy > 60) {
 					choice.push(`and ${he} thinks of little but sex with you,`);
 					if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-						choice.push(`so ${he} heads straight to ${V.masterSuiteName}.`);
+						choice.push(`so ${he} <span class="job change">heads straight to ${V.masterSuiteName}.</span>`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`so ${he} cheerfully designates ${himself} one of your fucktoys.`);
+						choice.push(`so ${he} cheerfully <span class="job change">designates ${himself} one of your fucktoys.</span>`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				} else if (canSee(slave) && canWalk(slave)) {
-					choice.push(`so ${he} decides to work`);
+					choice.push(`so ${he} <span class="job change">decides to work`);
 					if (V.universalRulesAssignsSelfFacility === 1 && V.servantsQuarters > servQL) {
-						choice.push(`from ${V.servantsQuartersName} to make your penthouse as clean and homelike as possible.`);
+						choice.push(`from ${V.servantsQuartersName}</span> to make your penthouse as clean and homelike as possible.`);
 						choice.push(assignJob(slave, "work as a servant"));
 					} else {
-						choice.push(`as a servant to make your penthouse as clean and homelike as possible.`);
+						choice.push(`as a servant</span> to make your penthouse as clean and homelike as possible.`);
 						choice.push(assignJob(slave, "be a servant"));
 					}
 				} else {
 					choice.push(`but unable to do much on ${his} own`);
 					if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-						choice.push(`so ${he} heads straight to ${V.masterSuiteName} to await your caress.`);
+						choice.push(`so ${he} <span class="job change">heads straight to ${V.masterSuiteName}</span> to await your caress.`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`so ${he} cheerfully designates ${himself} one of your fucktoys to be close to you.`);
+						choice.push(`so ${he} cheerfully <span class="job change">designates ${himself} one of your fucktoys</span> to be close to you.`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				}
 			} else if (slave.devotion < -20) {
-				choice.push(`and ${he} is scared of you, so ${he} chooses to work as a servant so that ${he} may serve you without "serving" you.`);
+				choice.push(`and ${he} is scared of you, so ${he} <span class="job change">chooses to work as a servant</span> so that ${he} may serve you without "serving" you.`);
 				choice.push(assignJob(slave, "be a servant"));
 			} else {
 				if (slave.energy > 60) {
 					choice.push(`and ${he} thinks of little but sex,`);
 					if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-						choice.push(`so ${he} heads straight to ${V.masterSuiteName}.`);
+						choice.push(`so ${he} <span class="job change">heads straight to ${V.masterSuiteName}.</span>`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`so ${he} cheerfully designates ${himself} one of your fucktoys.`);
+						choice.push(`so ${he} cheerfully <span class="job change">designates ${himself} one of your fucktoys.</span>`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				} else if (canSee(slave) && canWalk(slave)) {
-					choice.push(`so ${he} decides to work`);
+					choice.push(`so ${he} <span class="job change">decides to work`);
 					if (V.universalRulesAssignsSelfFacility === 1 && V.servantsQuarters > servQL) {
-						choice.push(`from ${V.servantsQuartersName} to make your penthouse as clean and homelike as possible.`);
+						choice.push(`from ${V.servantsQuartersName}</span> to make your penthouse as clean and homelike as possible.`);
 						choice.push(assignJob(slave, "work as a servant"));
 					} else {
-						choice.push(`as a servant to make your penthouse as clean and homelike as possible.`);
+						choice.push(`as a servant</span> to make your penthouse as clean and homelike as possible.`);
 						choice.push(assignJob(slave, "be a servant"));
 					}
 				} else {
-					choice.push(`but unable to do much on ${his} own, so ${he} designates ${himself} one of your fucktoys to get more intimate with you.`);
+					choice.push(`but unable to do much on ${his} own, so ${he} <span class="job change">designates ${himself} one of your fucktoys</span> to get more intimate with you.`);
 					choice.push(assignJob(slave, "please you"));
 				}
 			}
 		} else if (slave.fetishKnown === 1 || jsRandom(1, 100) > 5) { // Yes, this segways into other things than fetish. PM - I added a 5% chance for her to not think of something just for flavor.
 			if (slave.fetish === "submissive" && canWalk(slave) && canSee(slave)) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.servantsQuarters > servQL) {
-					choice.push(`thinks ${he} belongs at the bottom of the penthouse hierarchy, so ${he} goes to live in ${V.servantsQuartersName}.`);
+					choice.push(`thinks ${he} belongs at the bottom of the penthouse hierarchy, so ${he} <span class="job change">goes to live in ${V.servantsQuartersName}.</span>`);
 					choice.push(assignJob(slave, "work as a servant"));
 				} else {
-					choice.push(`thinks ${he} belongs at the bottom of the penthouse hierarchy, so ${he} decides ${he} should be a servant.`);
+					choice.push(`thinks ${he} belongs at the bottom of the penthouse hierarchy, so ${he} <span class="job change">decides ${he} should be a servant.</span>`);
 					choice.push(assignJob(slave, "be a servant"));
 				}
 			} else if (slave.fetish === "dom" || slave.fetish === "sadist") {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`is self-confident, so ${he} decides to work in ${V.clubName}.`);
+					choice.push(`is self-confident, so ${he} <span class="job change">decides to work in ${V.clubName}.</span>`);
 					choice.push(assignJob(slave, "serve in the club"));
 				} else {
-					choice.push(`is self-confident, so ${he} decides to work as a public servant.`);
+					choice.push(`is self-confident, so ${he} <span class="job change">decides to work as a public servant.</span>`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.fetish === "masochist") {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`enjoys abuse, so ${he} hurries down to ${V.brothelName}.`);
+					choice.push(`enjoys abuse, so ${he} <span class="job change">hurries down to ${V.brothelName}.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`enjoys abuse, so ${he} decides to become a whore.`);
+					choice.push(`enjoys abuse, so ${he} <span class="job change">decides to become a whore.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.fetish === "cumslut") {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`hurries down to ${V.brothelName} to suck cocks.`);
+					choice.push(`<span class="job change">hurries down to ${V.brothelName}</span> to suck cocks.`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`decides to become a whore, mostly to suck cock.`);
+					choice.push(`<span class="job change">decides to become a whore,</span> mostly to suck cock.`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.fetish === "humiliation") {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`decides to work in ${V.brothelName}, since it's even more embarrassing to be a whore than a club slut.`);
+					choice.push(`<span class="job change">decides to work in ${V.brothelName},</span> since it's even more embarrassing to be a whore than a club slut.`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`decides to whore, since it's even more embarrassing to be a whore than to be a public servant.`);
+					choice.push(`<span class="job change">decides to whore,</span> since it's even more embarrassing to be a whore than to be a public servant.`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.fetish === "buttslut") {
 				if (slave.balls > 0 && slave.prostate > 0 && V.universalRulesAssignsSelfFacility === 1 && V.dairyRestraintsSetting < 2 && V.dairyStimulatorsSetting > 0 && V.dairy > dairyL) {
-					choice.push(`chooses confinement in ${V.dairyName}, since all ${he} will be expected to do is produce cum by orgasming to buttsex.`);
+					choice.push(`<span class="job change">chooses confinement in ${V.dairyName},</span> since all ${he} will be expected to do is produce cum by orgasming to buttsex.`);
 					choice.push(assignJob(slave, "work in the dairy"));
 				} else if (canDoAnal(slave)){
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`decides to work in ${V.brothelName}, since whores get buttfucked more than anyone else.`);
+						choice.push(`<span class="job change">decides to work in ${V.brothelName},</span> since whores get buttfucked more than anyone else.`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`decides to whore, since whores get buttfucked more than anyone else.`);
+						choice.push(`<span class="job change">decides to whore,</span> since whores get buttfucked more than anyone else.`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
 				} else {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-						choice.push(`decides to shake ${his} money maker in ${V.clubName}.`);
+						choice.push(`<span class="job change">decides to shake ${his} money maker in ${V.clubName}.</span>`);
 						choice.push(assignJob(slave, "serve in the club"));
 					} else {
-						choice.push(`decides to shake ${his} money maker on the streets.`);
+						choice.push(`<span class="job change">decides to shake ${his} money maker on the streets.</span>`);
 						choice.push(assignJob(slave, "serve the public"));
 					}
 					slave.sexAmount = 10;
@@ -347,78 +347,78 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 			} else if (slave.fetish === "pregnancy") {
 				if (V.PC.dick > 0 && isFertile(slave)) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-						choice.push(`decides to serve you in ${V.masterSuiteName}, hoping that you'll get ${him} pregnant.`);
+						choice.push(`<span class="job change">decides to serve you in ${V.masterSuiteName},</span> hoping that you'll get ${him} pregnant.`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`decides to be your fucktoy, hoping that you'll get ${him} pregnant.`);
+						choice.push(`<span class="job change">decides to be your fucktoy,</span> hoping that you'll get ${him} pregnant.`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				} else if (V.universalRulesAssignsSelfFacility === 1 && V.dairyPregSetting > 0 && V.dairy > dairyL && isFertile(slave)) {
 					 if (V.dairyPregSetting > 1) {
-						choice.push(`eagerly rushes to ${V.dairyName} in the hopes that ${his} fertile womb will be packed full of children.`);
+						choice.push(`eagerly <span class="job change">rushes to ${V.dairyName}</span> in the hopes that ${his} fertile womb will be packed full of children.`);
 						choice.push(assignJob(slave, "work in the dairy"));
 					} else {
-						choice.push(`rushes to ${V.dairyName} in the hopes that ${his} fertile womb will be rented out.`);
+						choice.push(`<span class="job change">rushes to ${V.dairyName}</span> in the hopes that ${his} fertile womb will be rented out.`);
 						choice.push(assignJob(slave, "work in the dairy"));
 					}
 				} else if (slave.bellyPreg >= 1500) {
 					 if (arcology.FSRepopulationFocus > 20) {
 						if (V.cash < 10000) {
 							 if (V.brothel > brothelL && V.universalRulesAssignsSelfFacility === 1) {
-								choice.push(`heads to ${V.brothelName} since ${he} wants to set an example for any unimpregnated girls.`);
+								choice.push(`<span class="job change">heads to ${V.brothelName}</span> since ${he} wants to set an example for any unimpregnated girls.`);
 								choice.push(assignJob(slave, "work in the brothel"));
 							} else {
-								choice.push(`heads to the streets to lavish in the attention given to pregnant prostitutes.`);
+								choice.push(`<span class="job change">heads to the streets</span> to lavish in the attention given to pregnant prostitutes.`);
 								choice.push(assignJob(slave, "whore"));
 							}
 						} else {
 							 if (V.club > clubL && V.universalRulesAssignsSelfFacility === 1) {
-								choice.push(`heads to ${V.clubName} to show off ${his} growing middle and lavish in the public's attention.`);
+								choice.push(`<span class="job change">heads to ${V.clubName}</span> to show off ${his} growing middle and lavish in the public's attention.`);
 								choice.push(assignJob(slave, "serve in the club"));
 							} else {
-								choice.push(`heads to the streets to contribute to the number of visibly pregnant women around.`);
+								choice.push(`<span class="job change">heads to the streets</span> to contribute to the number of visibly pregnant women around.`);
 								choice.push(assignJob(slave, "serve the public"));
 							}
 						}
 						slave.sexAmount = 10;
 					} else if (V.masterSuite > masterSL && V.universalRulesAssignsSelfFacility === 1) {
-						choice.push(`heads straight to ${V.masterSuiteName} to share the intimacy of ${his} pregnant body with you.`);
+						choice.push(`<span class="job change">heads straight to ${V.masterSuiteName}</span> to share the intimacy of ${his} pregnant body with you.`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`decides to be your fucktoy to share the intimacy of ${his} pregnant body with you.`);
+						choice.push(`<span class="job change">decides to be your fucktoy</span> to share the intimacy of ${his} pregnant body with you.`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				} else if (slave.pregKnown === 1) {
 					 if (V.masterSuite > masterSL && V.universalRulesAssignsSelfFacility === 1) {
-						choice.push(`heads straight to ${V.masterSuiteName} so you may watch for the day that ${he} finally starts to show.`);
+						choice.push(`<span class="job change">heads straight to ${V.masterSuiteName}</span> so you may watch for the day that ${he} finally starts to show.`);
 						choice.push(assignJob(slave, "serve in the master suite"));
 					} else {
-						choice.push(`decides to be your fucktoy so you may enjoy watching ${him} begin showing.`);
+						choice.push(`<span class="job change">decides to be your fucktoy</span> so you may enjoy watching ${him} begin showing.`);
 						choice.push(assignJob(slave, "please you"));
 					}
 				} else if (canGetPregnant(slave)) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`can't indulge ${his} pregnancy fetish without getting dicked, so ${he} heads down to ${V.brothelName} to do just that.`);
+						choice.push(`can't indulge ${his} pregnancy fetish without getting dicked, so ${he} <span class="job change">heads down to ${V.brothelName}</span> to do just that.`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`can't indulge ${his} pregnancy fetish without getting dicked, so ${he} decides to be a whore to get seeded.`);
+						choice.push(`can't indulge ${his} pregnancy fetish without getting dicked, so ${he} <span class="job change">decides to be a whore</span> to get seeded.`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
 				} else if (slave.lactation > 0) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.dairyRestraintsSetting < 2 && V.dairy > dairyL) {
-						choice.push(`heads down to ${V.dairyName} to be around other lactating girls.`);
+						choice.push(`<span class="job change">heads down to ${V.dairyName}</span> to be around other lactating girls.`);
 						choice.push(assignJob(slave, "work in the dairy"));
 					} else {
-						choice.push(`decides to get milked, since ${he} is already lactating.`);
+						choice.push(`<span class="job change">decides to get milked,</span> since ${he} is already lactating.`);
 						choice.push(assignJob(slave, "get milked"));
 					}
 				} else {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`can't indulge ${his} fetish by getting pregnant ${himself}, so ${he} just heads down to ${V.brothelName}.`);
+						choice.push(`can't indulge ${his} fetish by getting pregnant ${himself}, so ${he} just <span class="job change">heads down to ${V.brothelName}.</span>`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`can't indulge ${his} fetish by getting pregnant ${himself}, so ${he} glumly decides to be a whore.`);
+						choice.push(`can't indulge ${his} fetish by getting pregnant ${himself}, so ${he} glumly <span class="job change">decides to be a whore.</span>`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
@@ -426,144 +426,144 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 			} else if (slave.fetish === "boobs") {
 				if (slave.lactation > 0) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.dairyRestraintsSetting < 2 && V.dairy > dairyL) {
-						choice.push(`heads down to ${V.dairyName} for all the attention that'll be lavished on ${his} nipples.`);
+						choice.push(`<span class="job change">heads down to ${V.dairyName}</span> for all the attention that'll be lavished on ${his} nipples.`);
 						choice.push(assignJob(slave, "work in the dairy"));
 					} else {
-						choice.push(`decides to get milked, since ${he} loves getting off to it.`);
+						choice.push(`<span class="job change">decides to get milked,</span> since ${he} loves getting off to it.`);
 						choice.push(assignJob(slave, "get milked"));
 					}
 				} else if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`decides to work in ${V.clubName} so ${he} can show off ${his} bare breasts.`);
+					choice.push(`<span class="job change">decides to work in ${V.clubName}</span> so ${he} can show off ${his} bare breasts.`);
 					choice.push(assignJob(slave, "serve in the club"));
 					slave.sexAmount = 10;
 				} else {
-					choice.push(`decides to work as a public servant so ${he} can show off ${his} bare breasts.`);
+					choice.push(`<span class="job change">decides to work as a public servant</span> so ${he} can show off ${his} bare breasts.`);
 					choice.push(assignJob(slave, "serve the public"));
 					slave.sexAmount = 10;
 				}
 			} else if (slave.attrXX > 85) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`decides to work in ${V.clubName} so ${he} can hit on hot girls.`);
+					choice.push(`<span class="job change">decides to work in ${V.clubName}</span> so ${he} can hit on hot girls.`);
 					choice.push(assignJob(slave, "serve in the club"));
 				} else {
-					choice.push(`decides to work as a public servant so ${he} can hit on hot girls.`);
+					choice.push(`<span class="job change">decides to work as a public servant</span> so ${he} can hit on hot girls.`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.attrXY > 85) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-					choice.push(`decides to work in ${V.clubName} so ${he} can hit on cute boys.`);
+					choice.push(`<span class="job change">decides to work in ${V.clubName}</span> so ${he} can hit on cute boys.`);
 					choice.push(assignJob(slave, "serve in the club"));
 				} else {
-					choice.push(`decides to work as a public servant so ${he} can hit on cute boys.`);
+					choice.push(`<span class="job change">decides to work as a public servant</span> so ${he} can hit on cute boys.`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
 			} else if (slave.energy > 95) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`decides to help those of your slaves who mind taking dick all day by working in ${V.brothelName}.`);
+					choice.push(`<span class="job change">decides to help those of your slaves who mind taking dick all day by working in ${V.brothelName}.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`decides to help those of your slaves who mind taking dick all day by working as a whore.`);
+					choice.push(`<span class="job change">decides to help those of your slaves who mind taking dick all day by working as a whore.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (arcology.FSChattelReligionist > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`is a pure and faithful slave, so ${he} sells ${his} body in the holy brothel.`);
+					choice.push(`is a pure and faithful slave, <span class="job change">so ${he} sells ${his} body in the holy brothel.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`is a pure and faithful slave, so ${he} sells ${his} body as a holy prostitute.`);
+					choice.push(`is a pure and faithful slave, <span class="job change">so ${he} sells ${his} body as a holy prostitute.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (arcology.FSEgyptianRevivalist > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.masterSuite > masterSL) {
-					choice.push(`is a devoted slave, so ${he} immediately joins your harem.`);
+					choice.push(`is a devoted slave, <span class="job change">so ${he} immediately joins your harem.</span>`);
 					choice.push(assignJob(slave, "serve in the master suite"));
 				} else {
-					choice.push(`is a devoted slave, so ${he} immediately joins your harem.`);
+					choice.push(`is a devoted slave, <span class="job change">so ${he} immediately joins your harem.</span>`);
 					choice.push(assignJob(slave, "please you"));
 				}
 			} else if (arcology.FSRomanRevivalist > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`is a devoted slave, so ${he} decides ${he} can best serve the state by raising money for the treasury, by selling ${his} body in ${V.brothelName}.`);
+					choice.push(`is a devoted slave, so ${he} <span class="job change">decides ${he} can best serve the state by raising money for the treasury, by selling ${his} body in ${V.brothelName}.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`is a devoted slave, so ${he} decides ${he} can best serve the state by raising money for the treasury, by selling ${his} body as a prostitute.`);
+					choice.push(`is a devoted slave, so ${he} <span class="job change">decides ${he} can best serve the state by raising money for the treasury, by selling ${his} body as a prostitute.</span>`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (arcology.FSAztecRevivalist > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`is a devoted slave, so ${he} decides ${he} can best serve the empire by serving at the fertility temple.`);
+					choice.push(`is a devoted slave, so ${he} <span class="job change">decides ${he} can best serve the empire by serving at the fertility temple.</span>`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`is a devoted slave, so ${he} decides ${he} can best serve the empire by offering ${himself} to the public.`);
+					choice.push(`is a devoted slave, so ${he} <span class="job change">decides ${he} can best serve the empire by offering ${himself} to the public.</span>`);
 					choice.push(assignJob(slave, "serve the public"));
 				}
 				slave.sexAmount = 10;
 			} else if (arcology.FSPaternalist > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-					choice.push(`decides to work in ${V.brothelName}, a respectable position for a slave in this enlightened arcology.`);
+					choice.push(`<span class="job change">decides to work in ${V.brothelName},</span> a respectable position for a slave in this enlightened arcology.`);
 					choice.push(assignJob(slave, "work in the brothel"));
 				} else {
-					choice.push(`decides to be a whore, a respectable position for a slave in this enlightened arcology.`);
+					choice.push(`<span class="job change">decides to be a whore,</span> a respectable position for a slave in this enlightened arcology.`);
 					choice.push(assignJob(slave, "whore"));
 				}
 				slave.sexAmount = 10;
 			} else if (arcology.FSPastoralist > 0) {
 				if (slave.lactation > 0) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.dairyRestraintsSetting < 2 && V.dairy > dairyL) {
-						choice.push(`hurries to join your herd of dairy cows.`);
+						choice.push(`<span class="job change">hurries to join your herd of dairy cows.</span>`);
 						choice.push(assignJob(slave, "work in the dairy"));
 					} else {
-						choice.push(`hurries to join your herd of cows.`);
+						choice.push(`<span class="job change">hurries to join your herd of cows.</span>`);
 						choice.push(assignJob(slave, "get milked"));
 					}
 				} else {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`decides to work in ${V.brothelName} to help raise money to get more girls lactating properly.`);
+						choice.push(`<span class="job change">decides to work in ${V.brothelName}</span> to help raise money to get more girls lactating properly.`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`decides to prostitute ${himself} to help raise money to get more girls lactating properly.`);
+						choice.push(`<span class="job change">decides to prostitute ${himself}</span> to help raise money to get more girls lactating properly.`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
 				}
 			} else if (arcology.FSHedonisticDecadence > 0) {
 				if (V.universalRulesAssignsSelfFacility === 1 && V.spa > spaL && (slave.trust < 60 || slave.devotion <= 60)) {
-					choice.push(`could use a break, so ${he} heads to take a dip in the spa.`);
+					choice.push(`could use a break, so ${he} <span class="job change">heads to take a dip in the spa.</span>`);
 					choice.push(assignJob(slave, "rest in the spa"));
 				} else {
-					choice.push(`could use a meal and a nap, so ${he} grabs a cup of food and heads to bed.`);
+					choice.push(`could use a meal and a nap, so ${he} grabs a cup of food and <span class="job change">heads to bed.</span>`);
 					choice.push(removeJob(slave, slave.assignment));
 				}
 			} else {
 				if (slave.skill.whoring > slave.skill.entertainment) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`decides to work in ${V.brothelName}, since ${he} thinks ${himself} a better whore than a public slut.`);
+						choice.push(`<span class="job change">decides to work in ${V.brothelName},</span> since ${he} thinks ${himself} a better whore than a public slut.`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`decides to whore, since ${he} thinks ${himself} a better whore than a public slut.`);
+						choice.push(`<span class="job change">decides to whore,</span> since ${he} thinks ${himself} a better whore than a public slut.`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
 				} else if (slave.skill.entertainment > slave.skill.whoring) {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-						choice.push(`decides to be a club ${girl}, since ${he} thinks ${himself} a better public slut than a whore.`);
+						choice.push(`<span class="job change">decides to be a club ${girl},</span> since ${he} thinks ${himself} a better public slut than a whore.`);
 						choice.push(assignJob(slave, "serve in the club"));
 					} else {
-						choice.push(`decides to serve the public, since ${he} thinks ${himself} a better public slut than a whore.`);
+						choice.push(`<span class="job change">decides to serve the public,</span> since ${he} thinks ${himself} a better public slut than a whore.`);
 						choice.push(assignJob(slave, "serve the public"));
 					}
 					slave.sexAmount = 10;
 				} else {
 					if (V.universalRulesAssignsSelfFacility === 1 && V.brothel > brothelL) {
-						choice.push(`decides to join ${his} sisters and work in ${V.brothelName}.`);
+						choice.push(`<span class="job change">decides to join ${his} sisters and work in ${V.brothelName}.</span>`);
 						choice.push(assignJob(slave, "work in the brothel"));
 					} else {
-						choice.push(`decides to prostitute ${himself} to help you upgrade ${arcology.name} and improve everyone's life.`);
+						choice.push(`<span class="job change">decides to prostitute ${himself}</span> to help you upgrade ${arcology.name} and improve everyone's life.`);
 						choice.push(assignJob(slave, "whore"));
 					}
 					slave.sexAmount = 10;
@@ -571,14 +571,14 @@ App.SlaveAssignment.choosesOwnJob = (function() {
 			}
 		} else {
 			if (V.universalRulesAssignsSelfFacility === 1 && V.club > clubL) {
-				choice.push(`decides to be a club ${girl}, since partying is better than sitting around and failing to think of a job to do.`);
+				choice.push(`<span class="job change">decides to be a club ${girl},</span> since partying is better than sitting around and failing to think of a job to do.`);
 				choice.push(assignJob(slave, "serve in the club"));
 				slave.sexAmount = 10;
 			} else if (canWalk(slave) && canSee(slave)) {
-				choice.push(`decides to tidy up the penthouse a little while ${he} thinks.`);
+				choice.push(`<span class="job change">decides to tidy up the penthouse</span> a little while ${he} thinks.`);
 				choice.push(assignJob(slave, "be a servant"));
 			} else {
-				choice.push(`stays in bed, unable to come up with anything.`);
+				choice.push(`<span class="job change">stays in bed,</span> unable to come up with anything.`);
 				choice.push(removeJob(slave, slave.assignment));
 			}
 		}
diff --git a/src/endWeek/saClothes.js b/src/endWeek/saClothes.js
index 2c32a0f8e58afa5311d199f5b095f747386c599f..bf925dfc8df15fe35d7c4b68fc87a66406214d2f 100644
--- a/src/endWeek/saClothes.js
+++ b/src/endWeek/saClothes.js
@@ -104,7 +104,7 @@ App.SlaveAssignment.clothes = (function() {
 				}
 				break;
 			case "a penitent nuns habit":
-				r.push(`The mortification of the flesh ${he} endures from wearing ${his} sackcloth habit slowly and painfully <span class="hotpink">purifies ${his} mind</span> of any but devoted, <span class="gold">fearful</span> thoughts. It's also <span class="red">unhealthy.</span>`);
+				r.push(`The mortification of the flesh ${he} endures from wearing ${his} sackcloth habit slowly and painfully <span class="hotpink">purifies ${his} mind</span> of any but devoted, <span class="gold">fearful</span> thoughts. It's also <span class="health dec">unhealthy.</span>`);
 				slave.devotion += 2;
 				slave.trust -= 2;
 				healthDamage(slave, 3);
@@ -555,7 +555,7 @@ App.SlaveAssignment.clothes = (function() {
 						} else if (slave.energy > 95) {
 							r.push(`${He}'s such a nympho that having ${his} butthole do double duty for ${his} pussy doesn't bother ${him}.`);
 						} else {
-							r.push(`${He} has a healthy sexuality, and misses vaginal sex, <span class="red">reducing ${his} sex drive slightly.</span>`);
+							r.push(`${He} has a healthy sexuality, and misses vaginal sex, <span class="libido dec">reducing ${his} sex drive slightly.</span>`);
 							slave.energy -= 1;
 						}
 					}
@@ -582,7 +582,7 @@ App.SlaveAssignment.clothes = (function() {
 							r.push(`it's almost always dripping a stream of precum.`);
 						} else if (slave.fetish === "masochist" && slave.fetishStrength > 60 && canAchieveErection(slave)) {
 							if (slave.fetishKnown === 1) {
-								r.push(`${He}'s such a masochist that the pain of even getting semi-hard in ${his} chastity cage turns ${him} on even more, <span class="green">slightly increasing ${his} sexual appetite.</span>`);
+								r.push(`${He}'s such a masochist that the pain of even getting semi-hard in ${his} chastity cage turns ${him} on even more, <span class="libido inc">slightly increasing ${his} sexual appetite.</span>`);
 								slave.energy++;
 							} else {
 								r.push(`${His} chastity cage is always painfully tight around ${his} semi-hard dick; something that seems to be arousing to ${him}, only to make it even more painful.`);
@@ -591,13 +591,13 @@ App.SlaveAssignment.clothes = (function() {
 						} else if (slave.sexualFlaw === "neglectful") {
 							r.push(`${His} soft, constrained dick is of little concern to ${him}. ${He} doesn't need release during sex.`);
 						} else if (slave.balls > 0 && slave.ballType === "sterile") {
-							r.push(`Though ${he}'s a soft bitch with worthless balls, having ${his} dick guarded against all stimulation by a chastity cage reduces ${his} enjoyment of intercourse a bit, <span class="red">slightly reducing ${his} sexual appetite.</span>`);
+							r.push(`Though ${he}'s a soft bitch with worthless balls, having ${his} dick guarded against all stimulation by a chastity cage reduces ${his} enjoyment of intercourse a bit, <span class="libido dec">slightly reducing ${his} sexual appetite.</span>`);
 							slave.energy -= 1;
 						} else if (slave.balls === 0) {
-							r.push(`Though ${he}'s a soft, ballsless bitch, having ${his} dick guarded against all stimulation by a chastity cage reduces ${his} enjoyment of intercourse a bit, <span class="red">slightly reducing ${his} sexual appetite.</span>`);
+							r.push(`Though ${he}'s a soft, ballsless bitch, having ${his} dick guarded against all stimulation by a chastity cage reduces ${his} enjoyment of intercourse a bit, <span class="libido dec">slightly reducing ${his} sexual appetite.</span>`);
 							slave.energy -= 1;
 						} else {
-							r.push(`${He} has a healthy sexuality, but ${his} chastity cage punishes it by making it very uncomfortable to achieve erection. ${He} often goes without release rather than pushing through, <span class="red">reducing ${his} sex drive.</span>`);
+							r.push(`${He} has a healthy sexuality, but ${his} chastity cage punishes it by making it very uncomfortable to achieve erection. ${He} often goes without release rather than pushing through, <span class="libido dec">reducing ${his} sex drive.</span>`);
 							slave.energy -= 2;
 							if (slave.devotion > 95) {
 								r.push(`${He}'s so devoted to you that ${he} doesn't let this affect ${his} opinion of you.`);
@@ -623,7 +623,7 @@ App.SlaveAssignment.clothes = (function() {
 				r.push(`${His} straining corset finally gives in to ${his} giant stomach and bursts, freeing ${his} belly.`);
 				slave.bellyAccessory = "none";
 			} else if (slave.bellyPreg >= 1500) {
-				r.push(`The tight corseting has <span class="orange">caused ${him} to miscarry,</span> which <span class="red">damages ${his} health.</span>`);
+				r.push(`The tight corseting has <span class="orange">caused ${him} to miscarry,</span> which <span class="health dec">damages ${his} health.</span>`);
 				healthDamage(slave, 20);
 				if (lastPregRule(slave, V.defaultRules)) {
 					slave.preg = -1;
@@ -668,7 +668,7 @@ App.SlaveAssignment.clothes = (function() {
 						slave.waist = -95;
 					}
 					if (slave.waist >= -40) {
-						r.push(`It's so tight that it's <span class="red">unhealthy.</span>`);
+						r.push(`It's so tight that it's <span class="health dec">unhealthy.</span>`);
 						healthDamage(slave, 5);
 						if (slave.fuckdoll === 0 && slave.fetish !== "mindbroken") {
 							if (slave.devotion < -20) {
@@ -841,7 +841,7 @@ App.SlaveAssignment.clothes = (function() {
 						slave.devotion += 1;
 						slave.trust -= 1;
 					}
-					r.push(`They're so high they're a bit <span class="red">unhealthy</span> for ${his} legs.`);
+					r.push(`They're so high they're a bit <span class="health dec">unhealthy</span> for ${his} legs.`);
 					healthDamage(slave, 2);
 				}
 			} else {
@@ -1033,7 +1033,7 @@ App.SlaveAssignment.clothes = (function() {
 			if (slave.vaginalAccessory === "long, huge dildo" || slave.vaginalAccessory === "long, large dildo" || slave.vaginalAccessory === "long dildo") {
 				if ((slave.preg > slave.pregData.normalBirth / 10) && slave.pregKnown === 1) {
 					if (jsRandom(1, 100) > 50) {
-						r.push(`The dildo penetrating ${his} womb <span class="orange">caused ${him} to miscarry,</span> which <span class="red">damages ${his} health.</span>`);
+						r.push(`The dildo penetrating ${his} womb <span class="orange">caused ${him} to miscarry,</span> which <span class="health dec">damages ${his} health.</span>`);
 						healthDamage(slave, 20);
 						if (lastPregRule(slave, V.defaultRules)) {
 							slave.preg = -1;
@@ -1148,7 +1148,7 @@ App.SlaveAssignment.clothes = (function() {
 				} else {
 					r.push(`${His} anus accommodates the large plug ${he}'s required to wear.`);
 					if (slave.fetish === "buttslut" && slave.fetishKnown === 1 && slave.fetishStrength > 60) {
-						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="green">constantly stimulating</span> ${his} rear-end.`);
+						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="libido inc">constantly stimulating</span> ${his} rear-end.`);
 						slave.devotion += 1;
 						slave.energy += 1;
 					}
@@ -1180,7 +1180,7 @@ App.SlaveAssignment.clothes = (function() {
 				} else {
 					r.push(`${His} anus accommodates the large plug ${he}'s required to wear.`);
 					if (slave.fetish === "buttslut" && slave.fetishKnown === 1 && slave.fetishStrength > 60) {
-						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="green">constantly stimulating</span> ${his} rear-end.`);
+						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="libido inc">constantly stimulating</span> ${his} rear-end.`);
 						slave.devotion += 1;
 						slave.energy += 1;
 					}
@@ -1212,7 +1212,7 @@ App.SlaveAssignment.clothes = (function() {
 				} else {
 					r.push(`${His} gaping anus accommodates the huge tailed plug ${he}'s required to wear, serving little purpose other than to remind ${him} of ${his} <span class="gold">humiliation.</span>`);
 					if (slave.fetish === "buttslut" && slave.fetishKnown === 1 && slave.fetishStrength > 60) {
-						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="green">constantly stimulating</span> ${his} rear-end.`);
+						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="libido inc">constantly stimulating</span> ${his} rear-end.`);
 						slave.devotion += 1;
 						slave.energy += 1;
 					}
@@ -1241,7 +1241,7 @@ App.SlaveAssignment.clothes = (function() {
 				} else {
 					r.push(`${His} gaping anus accommodates the huge plug ${he}'s required to wear.`);
 					if (slave.fetish === "buttslut" && slave.fetishKnown === 1 && slave.fetishStrength > 60) {
-						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="green">constantly stimulating</span> ${his} rear-end.`);
+						r.push(`In fact, ${he} <span class="hotpink">regularly orgasms</span> even in non-sexual situations as the plug is <span class="libido inc">constantly stimulating</span> ${his} rear-end.`);
 						slave.devotion += 1;
 						slave.energy += 1;
 					}
diff --git a/src/endWeek/saDevotion.js b/src/endWeek/saDevotion.js
index 2ee9f12fc2c7132114375a396bdc3c1d16500e43..abd02ed4ab34eaf9bdfeeb0ae775a9cb1c48ef94 100644
--- a/src/endWeek/saDevotion.js
+++ b/src/endWeek/saDevotion.js
@@ -989,7 +989,7 @@ App.SlaveAssignment.devotion = (function() {
 			}
 		}
 		if (energyPlus > 0) {
-			r.push(`${His} confidence in ${his} sexuality <span class="improvement">improved ${his} low sex drive.</span>`);
+			r.push(`${His} confidence in ${his} sexuality <span class="libido inc">improved ${his} low sex drive.</span>`);
 			slave.energy += energyPlus;
 		}
 
diff --git a/src/endWeek/saDiet.js b/src/endWeek/saDiet.js
index 0b176106b4b98e1bfd9cf4b4f1b19699ebf6792e..cd181294e04ea7a9b008e17bedc5dd8c7cb8d62d 100644
--- a/src/endWeek/saDiet.js
+++ b/src/endWeek/saDiet.js
@@ -299,7 +299,7 @@ App.SlaveAssignment.diet = (function() {
 							} else {
 								r.push(`${he} accepts being overfed.`);
 							}
-						} else if (setup.gratefulCareers.includes(slave.career) && slave.weight < 100) {
+						} else if (App.Data.Careers.General.grateful.includes(slave.career) && slave.weight < 100) {
 							r.push(`<span class="trust inc">${He} appreciates having a belly full of food.</span>`);
 							slave.trust += 1;
 						} else if (slave.devotion <= 20) {
@@ -881,7 +881,7 @@ App.SlaveAssignment.diet = (function() {
 							r.push(`${He} approaches endurance work with real enthusiasm, quickly slimming ${him} down.`);
 							slave.muscles -= 2 + slave.geneticQuirks.mLoss;
 						}
-						if (((slave.geneMods.NCS === 0 && random(1, 100) > 90)) || (slave.geneMods.NCS === 1 && random(1, 100) > 45)) {
+						if ((slave.geneMods.NCS === 0 && random(1, 100) > 90) || (slave.geneMods.NCS === 1 && random(1, 100) > 45)) {
 							if ((slave.geneMods.NCS === 0 && boobSize >= 200) || (slave.geneMods.NCS === 1 && boobSize > 100) && gigantomastiaMod !== 3) {
 								if (slave.geneMods.NCS === 0) {
 									r.push(`<span class="change negative">${His} breasts get a little smaller.</span>`);
@@ -976,7 +976,7 @@ App.SlaveAssignment.diet = (function() {
 								r.push(`${His} thoughts frequently drift towards <span class="fetish inc">bellies swelling with ${his} children</span> whenever ${he} has sex or pleasures ${himself}.`);
 								slave.fetishStrength += 1;
 							} else if (slave.energy < 90 && slave.fetish === "pregnancy") {
-								r.push(`${His} eagerness for sex <span class="improvement">grows stronger</span> the more ${his} aching nuts yearn to inseminate a fertile womb.`);
+								r.push(`${His} eagerness for sex <span class="libido inc">grows stronger</span> the more ${his} aching nuts yearn to inseminate a fertile womb.`);
 								slave.energy += 1;
 							}
 						}
@@ -1351,7 +1351,7 @@ App.SlaveAssignment.diet = (function() {
 						}
 						r.push(`</span> by ${his} diet.`);
 						if (slave.energy < 45 && slave.energy > 20) {
-							r.push(`${He} begins craving <span class="improvement">sex for the sole purpose of reproduction,</span> even if ${he} doesn't comprehend it.`);
+							r.push(`${He} begins craving <span class="libido inc">sex for the sole purpose of reproduction,</span> even if ${he} doesn't comprehend it.`);
 							slave.energy++;
 						}
 					} else if (slave.sexualFlaw === "breeder") {
@@ -1373,7 +1373,7 @@ App.SlaveAssignment.diet = (function() {
 							slave.attrXY += 2;
 						}
 						if (slave.energy < 45 && slave.energy > 20) {
-							r.push(`${He} begins craving <span class="improvement">penetrative sex and hot loads left inside ${him}</span> as well.`);
+							r.push(`${He} begins craving <span class="libido inc">penetrative sex and hot loads left inside ${him}</span> as well.`);
 							slave.energy++;
 						}
 					} else {
@@ -1394,7 +1394,7 @@ App.SlaveAssignment.diet = (function() {
 							slave.attrXY += 2;
 						}
 						if (slave.energy < 45 && slave.energy > 20) {
-							r.push(`${He} begins craving <span class="improvement">penetrative sex and hot loads left inside ${him}</span> as well.`);
+							r.push(`${He} begins craving <span class="libido inc">penetrative sex and hot loads left inside ${him}</span> as well.`);
 							slave.energy++;
 						}
 					}
@@ -1558,7 +1558,7 @@ App.SlaveAssignment.diet = (function() {
 						} else {
 							if (slave.dietCum === 2) {
 								if (slave.energy > 80) {
-									r.push(`${He} has become so sex-driven that ${he} <span class="improvement">appreciates the perversity</span> of ${his} extreme cum diet, despite <span class="devotion dec">it sometimes being a bit too much for ${him}.</span>`);
+									r.push(`${He} has become so sex-driven that ${he} <span class="libido inc">appreciates the perversity</span> of ${his} extreme cum diet, despite <span class="devotion dec">it sometimes being a bit too much for ${him}.</span>`);
 									slave.devotion -= 1;
 									slave.energy += 1;
 								} else {
@@ -1591,7 +1591,7 @@ App.SlaveAssignment.diet = (function() {
 								}
 							} else if (slave.dietCum === 1) {
 								if (slave.energy > 80) {
-									r.push(`${He} has become so sex-driven that ${he} appreciates the perversity of ${his} cum diet, making ${him} <span class="improvement">feel hot</span> just eating it.`);
+									r.push(`${He} has become so sex-driven that ${he} appreciates the perversity of ${his} cum diet, making ${him} <span class="libido inc">feel hot</span> just eating it.`);
 									slave.energy += 2;
 								} else {
 									r.push(`${His} high sex drive helps ${him} pretend ${his} cum-supplemented diet is the `);
diff --git a/src/endWeek/saDrugs.js b/src/endWeek/saDrugs.js
index 8fe4927f1f8c826c1b895df2a7a1ec6ffe67267f..5a0580f1e90b69beff68356a963817b7eead1a94 100644
--- a/src/endWeek/saDrugs.js
+++ b/src/endWeek/saDrugs.js
@@ -76,10 +76,10 @@ App.SlaveAssignment.drugs = (function() {
 					r += `${His} dick is so enormous that attempts to force an erection would kill ${him}. <span class="yellow">${His} drug regimen has been mercifully ended.</span>`;
 					slave.drugs = "no drugs";
 				} else {
-					r += `${His} drug regime keeps ${his} dick <span class="red">painfully erect.</span>`;
+					r += `${His} drug regime keeps ${his} dick <span class="health dec">painfully erect.</span>`;
 					healthDamage(slave, 5);
 					if (slave.dick >= 7) {
-						r += ` The amount of blood needed to keep ${his} oversized cock hard has <span class="red">severe effects on ${his} health!</span>`;
+						r += ` The amount of blood needed to keep ${his} oversized cock hard has <span class="health dec">severe effects on ${his} health!</span>`;
 						healthDamage(slave, slave.dick*5);
 					}
 				}
@@ -118,7 +118,7 @@ App.SlaveAssignment.drugs = (function() {
 					}
 					slave.intelligence += 1;
 					if (slave.energy > 60) {
-						r += ` ${He} spends <span class="red">less time thinking about sex,</span> as well.`;
+						r += ` ${He} spends <span class="libido dec">less time thinking about sex,</span> as well.`;
 						if (slave.energy > 95) {
 							slave.energy -= 3;
 						} else if (slave.energy > 80) {
@@ -991,17 +991,17 @@ App.SlaveAssignment.drugs = (function() {
 					} else if (growth === 4) {
 						r += ` <span class="lime">${His} height dramatically increased this week.</span>`;
 					} else if (growth === 5) {
-						r += ` <span class="lime">${His} body experienced explosive growth this week,</span> so extreme that ${his} cardiovascular system can barely keep up, <span class="red">severely damaging ${his} health.</span>`;
+						r += ` <span class="lime">${His} body experienced explosive growth this week,</span> so extreme that ${his} cardiovascular system can barely keep up, <span class="health dec">severely damaging ${his} health.</span>`;
 						healthDamage(slave, 20);
 					}
 					// health issues
 					if (jsRandom(1, 10) === 1 && growth !== 5) {
-						r += ` The stimulants stressed ${slave.slaveName}'s body more than expected, <span class="red">damaging ${his} health.</span>`;
+						r += ` The stimulants stressed ${slave.slaveName}'s body more than expected, <span class="health dec">damaging ${his} health.</span>`;
 						healthDamage(slave, 10);
 					}
 					if (slave.physicalAge > V.maxGrowthAge) {
 						if (jsRandom(1, 6) === 1) {
-							r += ` Since ${his} body already concluded ${his} natural growth processes, the treatment <span class="red">weakens ${him} considerably.</span>`;
+							r += ` Since ${his} body already concluded ${his} natural growth processes, the treatment <span class="health dec">weakens ${him} considerably.</span>`;
 							healthDamage(slave, 15);
 						}
 					}
@@ -1017,7 +1017,7 @@ App.SlaveAssignment.drugs = (function() {
 					r += ` ${He} has potent sperm. <span class="yellow">${His} drug regimen has been ended.</span>`;
 					slave.drugs = "no drugs";
 				} else {
-					r += ` The intense hormonal injections leave ${him} <span class="red">sickened and weak</span> as ${his} body struggles to adapt to the overwhelming chemicals flooding ${his} system.`;
+					r += ` The intense hormonal injections leave ${him} <span class="health dec">sickened and weak</span> as ${his} body struggles to adapt to the overwhelming chemicals flooding ${his} system.`;
 					slave.chem += 20;
 					healthDamage(slave, 10);
 					if (slave.energy > 5) {
@@ -1030,7 +1030,7 @@ App.SlaveAssignment.drugs = (function() {
 					r += ` ${He} has begun ovulating. <span class="yellow">${His} drug regimen has been ended.</span>`;
 					slave.drugs = "no drugs";
 				} else {
-					r += ` The intense hormonal injections leave ${him} <span class="red">sickened and weak</span> as ${his} body struggles to adapt to the overwhelming chemicals flooding ${his} system.`;
+					r += ` The intense hormonal injections leave ${him} <span class="health dec">sickened and weak</span> as ${his} body struggles to adapt to the overwhelming chemicals flooding ${his} system.`;
 					slave.chem += 20;
 					healthDamage(slave, 10);
 					if (slave.energy > 5) {
@@ -1254,11 +1254,11 @@ App.SlaveAssignment.drugs = (function() {
 					slave.lactationDuration = 2;
 				}
 				if (slave.attrXY < 100 && jsRandom(0, 10) < slave.health.condition) {
-					r += ` The hormonal changes produced by the fertility drugs cause ${him} to begin <span class="green">finding men more attractive.</span>`;
+					r += ` The hormonal changes produced by the fertility drugs cause ${him} to begin <span class="improvement">finding men more attractive.</span>`;
 					slave.attrXY += jsRandom(5, 10);
 				}
 				if (slave.energy < 85 && jsRandom(0, 10) < slave.health.condition) {
-					r += ` The hormonal changes produced by the fertility drugs cause ${him} to <span class="green">begin craving sex more.</span>`;
+					r += ` The hormonal changes produced by the fertility drugs cause ${him} to <span class="libido inc">begin craving sex more.</span>`;
 					slave.energy += 2;
 				}
 				break;
@@ -1589,7 +1589,7 @@ App.SlaveAssignment.drugs = (function() {
 			}
 		}
 		if (intensive) {
-			r += ` Such reckless doping is dangerous and <span class="red">unhealthy.</span>`;
+			r += ` Such reckless doping is dangerous and <span class="health dec">unhealthy.</span>`;
 			healthDamage(slave, jsRandom(3, 5));
 		}
 	}
@@ -1603,7 +1603,7 @@ App.SlaveAssignment.drugs = (function() {
 			case "slow gestation":
 				slave.chem += 1;
 				if (slave.preg >= slave.pregData.normalBirth) {
-					r += ` ${He} is full term; gestation restriction agents <span class="red">can't suppress labor</span> and ${his} regimen is ended to prevent health damage.`;
+					r += ` ${He} is full term; gestation restriction agents <span class="health dec">can't suppress labor</span> and ${his} regimen is ended to prevent health damage.`;
 					slave.pregControl = "none";
 				} else if ((slave.preg >= slave.pregData.normalBirth / 2.5) || (slave.bellyPreg >= 1500 && slave.weight <= 130)) {
 					r += ` ${His} belly feels very docile, ${his} gestation rate is <span class="coral">greatly reduced.</span>`;
@@ -1623,9 +1623,9 @@ App.SlaveAssignment.drugs = (function() {
 					} else {
 						r += `child is`;
 					}
-					r += ` growing rapidly within ${his} womb, far faster than ${his} <span class="red">poor body can handle.</span>`;
+					r += ` growing rapidly within ${his} womb, far faster than ${his} <span class="health dec">poor body can handle.</span>`;
 					if (slave.pregType >= 10 && slave.bellyPreg >= 100000) {
-						r += ` ${His} rate of growth is straining ${his} womb; ${he} is <span class="red">at risk of bursting!</span>`;
+						r += ` ${His} rate of growth is straining ${his} womb; ${he} is <span class="health dec">at risk of bursting!</span>`;
 					}
 					healthDamage(slave, (slave.preg + slave.pregType - slave.bellySag));
 					if (slave.health.condition < -90) {
@@ -1672,13 +1672,13 @@ App.SlaveAssignment.drugs = (function() {
 					healthDamage(slave, 20);
 					induce(slave);
 					V.birthee = 1;
-					r += ` ${He} has been ready to give birth for some time now. Suppressing birth for so long <span class="red">greatly affects ${his} health.</span> ${He} may <span class="red">have trouble</span> giving birth to ${his} oversized ${(slave.pregType === 1) ? `child`:`children`}. ${He} seems to be in distress, ${his} body is <span class="red">forcing ${his} ${(slave.pregType === 1) ? `child`:`children`} out!</span>`;
+					r += ` ${He} has been ready to give birth for some time now. Suppressing birth for so long <span class="health dec">greatly affects ${his} health.</span> ${He} may <span class="health dec">have trouble</span> giving birth to ${his} oversized ${(slave.pregType === 1) ? `child`:`children`}. ${He} seems to be in distress, ${his} body is <span class="health dec">forcing ${his} ${(slave.pregType === 1) ? `child`:`children`} out!</span>`;
 				} else if (WombBirthReady(slave, slave.pregData.normalBirth * 1.25) > 0) {
 					healthDamage(slave, 20);
-					r += ` ${He} has been ready to give birth for some time now. Suppressing birth for so long <span class="red">greatly affects ${his} health.</span> ${He} may <span class="red">have trouble</span> giving birth to ${his} oversized ${(slave.pregType === 1) ? `child` : `children`}. `;
+					r += ` ${He} has been ready to give birth for some time now. Suppressing birth for so long <span class="health dec">greatly affects ${his} health.</span> ${He} may <span class="health dec">have trouble</span> giving birth to ${his} oversized ${(slave.pregType === 1) ? `child` : `children`}. `;
 				} else if (WombBirthReady(slave, slave.pregData.normalBirth) > 0) {
 					healthDamage(slave, 10);
-					r += ` Labor suppressing agents <span class="red">negatively affect ${his} health.</span> `;
+					r += ` Labor suppressing agents <span class="health dec">negatively affect ${his} health.</span> `;
 				}
 				break;
 		}
@@ -1694,16 +1694,16 @@ App.SlaveAssignment.drugs = (function() {
 			slave.curatives = 1;
 		} else if ((slave.assignment === Job.REST || slave.assignment === Job.SPA) && (slave.fetish !== "mindbroken")) {
 			if (slave.inflationType !== "curative" || slave.inflation === 0) {
-				r += ` The curatives ${he}'s taking synergize with rest, keeping ${him} asleep most of the time. This is an <span class="green">extremely effective health treatment.</span>`;
+				r += ` The curatives ${he}'s taking synergize with rest, keeping ${him} asleep most of the time. This is an <span class="health inc">extremely effective health treatment.</span>`;
 			} else {
-				r += ` Since ${he} is usually slumbering soundly, the curatives are able to <span class="green">work more efficiently.</span>`;
+				r += ` Since ${he} is usually slumbering soundly, the curatives are able to <span class="health inc">work more efficiently.</span>`;
 			}
 			improveCondition(slave, 4);
 		} else if (slave.health.condition < -20) {
-			r += ` <span class="green">${His} poor health rapidly improves</span> under drug treatment.`;
+			r += ` <span class="health inc">${His} poor health rapidly improves</span> under drug treatment.`;
 			improveCondition(slave, 2);
 		} else {
-			r += ` <span class="green">${His} health improves</span> under curative treatment.`;
+			r += ` <span class="health inc">${His} health improves</span> under curative treatment.`;
 		}
 		if (slave.fuckdoll === 0 && slave.fetish !== "mindbroken") {
 			r += ` ${He} <span class="mediumaquamarine">trusts you more</span> for giving ${him} access to expensive modern medicine.`;
@@ -1724,7 +1724,7 @@ App.SlaveAssignment.drugs = (function() {
 		if (slave.aphrodisiacs > 1) {
 			r += ` The aphrodisiacs ${he}'s on `;
 			if (slave.energy <= 95) {
-				r += `<span class="green">rapidly boost ${his} sex drive.</span>`;
+				r += `<span class="libido inc">rapidly boost ${his} sex drive.</span>`;
 				slave.energy += 5;
 			} else {
 				r += `can't improve ${his} absurd sex drive.`;
@@ -1734,14 +1734,14 @@ App.SlaveAssignment.drugs = (function() {
 				slave.devotion += 5;
 			}
 			if (V.arcologies[0].FSBodyPuristLaw === 0 && V.healthyDrugsUpgrade === 0) {
-				r += ` Such a high dosage <span class="red">isn't healthy.</span>`;
+				r += ` Such a high dosage <span class="health dec">isn't healthy.</span>`;
 				healthDamage(slave, 5);
 			}
 			slave.chem += 1;
 		} else {
 			r += ` The aphrodisiacs ${he}'s on `;
 			if (slave.energy <= 95) {
-				r += `<span class="green">boost ${his} sex drive.</span>`;
+				r += `<span class="libido inc">boost ${his} sex drive.</span>`;
 				slave.energy += 3;
 			} else {
 				r += `can't improve ${his} absurd sex drive.`;
diff --git a/src/endWeek/saGetMilked.js b/src/endWeek/saGetMilked.js
index 49fe61b36850fbc1085742fd749597795ce665d8..96240a88cdcab11e1b8ecc4b3d7e42511d70fcf8 100644
--- a/src/endWeek/saGetMilked.js
+++ b/src/endWeek/saGetMilked.js
@@ -61,7 +61,7 @@ App.SlaveAssignment.getMilked = (function() {
 		}
 
 		if (slave.lactation > 0) {
-			harvestMilk(slave, multiplier);
+			harvestMilk(slave, multiplier, preview);
 			if (!preview) {
 				milkingEffects(slave);
 			}
@@ -156,8 +156,9 @@ App.SlaveAssignment.getMilked = (function() {
 	/**
 	 * @param {App.Entity.SlaveState} slave
 	 * @param {number} multiplier
+	 * @param {boolean} preview
 	 */
-	function harvestMilk(slave, multiplier) {
+	function harvestMilk(slave, multiplier, preview) {
 		r.milk = milkAmount(slave);
 
 		r.text += ` ${He} produces from ${his} ${jsEither(["boobs", "breasts", "mammaries", "tits", "udders"])}, which have a combined volume of ${(slave.boobs * 2)} CCs; `;
@@ -180,13 +181,15 @@ App.SlaveAssignment.getMilked = (function() {
 			} else {
 				r.text += `a river of milk.`;
 			}
-			if (slave.lactationAdaptation < 100) {
-				r.text += ` ${His} udders are forced to adapt to this unnatural productivity.`;
-				slave.lactationAdaptation += 1;
-			}
-			if (slave.curatives === 0 && slave.inflationType !== "curative") {
-				r.text += ` The stress of extreme milk production <span class="health dec">damages ${his} health.</span>`;
-				healthDamage(slave, 3);
+			if (!preview) {
+				if (slave.lactationAdaptation < 100) {
+					r.text += ` ${His} udders are forced to adapt to this unnatural productivity.`;
+					slave.lactationAdaptation += 1;
+				}
+				if (slave.curatives === 0 && slave.inflationType !== "curative") {
+					r.text += ` The stress of extreme milk production <span class="health dec">damages ${his} health.</span>`;
+					healthDamage(slave, 3);
+				}
 			}
 		}
 
@@ -244,14 +247,12 @@ App.SlaveAssignment.getMilked = (function() {
 			r.text += ` ${His} internal chemistry is poorly suited to milk production.`;
 		}
 
-		if (slave.lactationAdaptation < 100) {
-			if (slave.assignment === Job.MILKED || slave.assignment === Job.DAIRY) {
-				r.text += ` Living as a cow helps ${his} body and mind adapt to producing milk.`;
-				slave.lactationAdaptation += 1;
-			}
+		if (!preview && slave.lactationAdaptation < 100 && slave.assignment === Job.MILKED || slave.assignment === Job.DAIRY) {
+			r.text += ` Living as a cow helps ${his} body and mind adapt to producing milk.`;
+			slave.lactationAdaptation += 1;
 		}
 
-		if (slave.preg > (slave.pregData.normalBirth / 8) && slave.pregKnown === 1) {
+		if (!preview && slave.preg > (slave.pregData.normalBirth / 8) && slave.pregKnown === 1) {
 			r.text += ` ${His} pregnancy helps ${his} body produce more milk naturally`;
 			if (slave.lactationAdaptation < 100) {
 				r.text += `, and also helps it adapt to milk production`;
@@ -688,7 +689,7 @@ App.SlaveAssignment.getMilked = (function() {
 					r.text += ` The milking machine is merciless in its extraction of fluids from ${him}, but ${his} body is supplied with chemical stimulants to keep fatigue from setting in.`;
 				} else if (V.dairyRestraintsSetting > 0) {
 					if (slaveResting(slave)) {
-						r.text += ` Resting doesn't stop ${him} from being thoroughly milked, but it does free ${him} from some of the associated chores, allowing ${him} time <span class="green">to snooze</span> in ${his} harness post harvesting.`;
+						r.text += ` Resting doesn't stop ${him} from being thoroughly milked, but it does free ${him} from some of the associated chores, allowing ${him} time <span class="health inc">to snooze</span> in ${his} harness post harvesting.`;
 					} else if (slave.health.tired + 9 >= 90 && !willWorkToDeath(slave)) {
 						r.text += ` ${He} attempts to skip out on milkings due to ${his} exhaustion, but can do little to avoid being dragged and strapped in to the milkers by `;
 						if (V.MilkmaidID !== 0) {
@@ -715,7 +716,7 @@ App.SlaveAssignment.getMilked = (function() {
 					}
 				} else {
 					if (slaveResting(slave)) {
-						r.text += ` Resting doesn't stop ${him} from being thoroughly milked, but it does free ${him} from some of the associated chores, allowing ${him} time <span class="green">to catch some extra sleep</span> in ${his} stall.`;
+						r.text += ` Resting doesn't stop ${him} from being thoroughly milked, but it does free ${him} from some of the associated chores, allowing ${him} time <span class="health inc">to catch some extra sleep</span> in ${his} stall.`;
 					} else if (slave.health.tired + 9 >= 90 && !willWorkToDeath(slave)) {
 						r.text += ` ${He} attempts to skip out on milkings due to ${his} exhaustion, but can do little to avoid being dragged and hooked up to the milkers by `;
 						if (V.MilkmaidID !== 0) {
@@ -727,7 +728,7 @@ App.SlaveAssignment.getMilked = (function() {
 						slave.devotion += 2;
 					} else {
 						if (slave.devotion > 20) {
-							r.text += ` Being a free range cow is one of <span class="green">the most laid-back assignments</span> available. All that is required of ${him} is that ${he} lie back and get milked.`;
+							r.text += ` Being a free range cow is one of <span class="health inc">the most laid-back assignments</span> available. All that is required of ${him} is that ${he} lie back and get milked.`;
 						} else {
 							r.text += ` Being a free range cow can be one of the most laid-back assignments available, but ${he} fails to realize that and instead chooses to <span class="red">waste energy</span> struggling against the inevitable.`;
 						}
@@ -736,7 +737,7 @@ App.SlaveAssignment.getMilked = (function() {
 				tired(slave);
 			} else if (slave.assignment === Job.MILKED) {
 				if (slaveResting(slave)) {
-					r.text += ` While less is required of ${him} during ${his} <span class="green">mandatory rest periods,</span> ${he} still needs to frequently visit the milkers, reducing the overall effectiveness of ${his} breaks.`;
+					r.text += ` While less is required of ${him} during ${his} <span class="health inc">mandatory rest periods,</span> ${he} still needs to frequently visit the milkers, reducing the overall effectiveness of ${his} breaks.`;
 				} else if (slave.health.tired + 8 >= 90 && !willWorkToDeath(slave)) {
 					r.text += ` ${He} attempts to skip out on milkings due to ${his} exhaustion, but can do little to avoid being dragged and hooked up to the milkers by `;
 					if (V.dairy > 0 && V.universalRulesFacilityWork === 1 && V.dairySpots > 0 && V.MilkmaidID !== 0 && V.dairyRestraintsSetting < 2) {
@@ -751,7 +752,7 @@ App.SlaveAssignment.getMilked = (function() {
 					if (slave.devotion > 20) {
 						r.text += `giving ${him} plenty of time to relax throughout the day.`;
 					} else {
-						r.text += `but ${he} complicates things, <span class="red">wasting energy</span> ${he} should be conserving for ${his} other responsibilities.`;
+						r.text += `but ${he} complicates things, <span Class="red">wasting energy</span> ${he} should be conserving for ${his} other responsibilities.`;
 					}
 				}
 				tired(slave);
diff --git a/src/endWeek/saGuardYou.js b/src/endWeek/saGuardYou.js
index 5f3e14c0c6b288890cdc35b640a5e58d17c70096..d2115412a92a6c2cab9fd5885c1b4f97e5691459 100644
--- a/src/endWeek/saGuardYou.js
+++ b/src/endWeek/saGuardYou.js
@@ -68,7 +68,7 @@ App.SlaveAssignment.guardYou = (function() {
 			r.push(`${His} combat skills greatly increase ${his} deadliness.`);
 		}
 
-		if (setup.bodyguardCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.bodyguard.includes(slave.career)) {
 			r.push(`${He} has experience in personal defense from before ${he} was a slave.`);
 		} else if (slave.skill.bodyguard >= V.masteredXP) {
 			r.push(`${He} has experience in personal defense from working for you.`);
@@ -254,10 +254,11 @@ App.SlaveAssignment.guardYou = (function() {
 		slave.trust += 4;
 
 		if (V.dojo > 1) {
-			r.push(`${He} <span class="hotpink">appreciates</span> how special it is that ${he} has a nice room off the armory to rest in. ${He} can finally <span class="green">rest easy</span> while still keeping tabs on your safety.`);
+			r.push(`${He} <span class="hotpink">appreciates</span> how special it is that ${he} has a nice room off the armory to rest in. ${He} can finally <span class="health inc">rest easy</span> while still keeping tabs on your safety.`);
 			slave.devotion += 1;
 		} else {
-			r.push(`Between protecting you, training, and keeping vigil while you sleep, ${he} lives a <span class="red">very tiring</span> life.`);
+			r.push(`Between protecting you, training, and keeping vigil while you sleep, ${he} lives a <span class="health dec">very tiring</span> life.`);
+			/** ##Note I assume these two are health effects **/
 		}
 		tired(slave);
 	}
@@ -325,7 +326,7 @@ App.SlaveAssignment.guardYou = (function() {
 						candidate.skill.combat = 1;
 					}
 				} else {
-					r.push(`${He} finds no suitable candidates to serve as ${his} replacement, leaving ${him} stressed over your future safety. The worry is <span class="red">exhausting</span> and <span class="red">bad for ${his} health.</span>`);
+					r.push(`${He} finds no suitable candidates to serve as ${his} replacement, leaving ${him} stressed over your future safety. The worry is <span class="health dec">exhausting</span> and <span class="health dec">bad for ${his} health.</span>`);
 					healthDamage(slave, 3);
 					slave.health.tired += 15;
 				}
diff --git a/src/endWeek/saHormonesEffects.js b/src/endWeek/saHormonesEffects.js
index a9ab33b502e5e8c744458f7e142e2323d77ff03d..9332be379b8aabc6aeb89947b6720dd5f1c6bcc5 100644
--- a/src/endWeek/saHormonesEffects.js
+++ b/src/endWeek/saHormonesEffects.js
@@ -96,7 +96,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 				slave.devotion -= 1;
 			}
 			if (slave.energy > 10) {
-				r.push(`It also has the unfortunate consequence of <span class="red">damaging ${his} libido.</span>`);
+				r.push(`It also has the unfortunate consequence of <span class="libido dec">damaging ${his} libido.</span>`);
 				slave.energy--;
 			}
 			if (slave.attrXX > 50 || slave.attrXY > 50) {
@@ -118,7 +118,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 				slave.devotion -= 1;
 			}
 			if (slave.energy > 10) {
-				r.push(`It also has the unfortunate consequence of <span class="red">damaging ${his} libido.</span>`);
+				r.push(`It also has the unfortunate consequence of <span class="libido dec">damaging ${his} libido.</span>`);
 				slave.energy--;
 			}
 			if (slave.attrXX > 50 || slave.attrXY > 50) {
diff --git a/src/endWeek/saInflation.js b/src/endWeek/saInflation.js
index 037fbec4308b0c43c4a14e88828bac47811bf11c..2ce8a41b70dd120c2b5beb27bcdfd74d74f9158e 100644
--- a/src/endWeek/saInflation.js
+++ b/src/endWeek/saInflation.js
@@ -132,7 +132,7 @@ App.SlaveAssignment.inflation = (function() {
 			case "water":
 				r.push(`${He} makes sure to fill ${his} rear with nearly`);
 				if (slave.inflation === 3) {
-					r.push(`two gallons of water, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+					r.push(`two gallons of water, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 					healthDamage(slave, 10);
 				} else if (slave.inflation === 2) {
 					r.push(`four liters of water, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} is full enough to be distended but not enough to grow taut.`);
@@ -144,7 +144,7 @@ App.SlaveAssignment.inflation = (function() {
 			case "urine":
 				r.push(`${He} makes sure to fill ${his} rear with nearly`);
 				if (slave.inflation === 3) {
-					r.push(`two gallons of urine, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+					r.push(`two gallons of urine, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 					healthDamage(slave, 10);
 				} else if (slave.inflation === 2) {
 					r.push(`four liters of urine, ${distensionTerm}, whenever ${he} leaks or needs to release ${his} load. ${He} is full enough to be distended but not enough to grow taut.`);
@@ -156,7 +156,7 @@ App.SlaveAssignment.inflation = (function() {
 			case "aphrodisiac":
 				r.push(`${He} makes sure to fill ${his} rear with nearly`);
 				if (slave.inflation === 3) {
-					r.push(`two gallons of an aphrodisiac solution, leaving ${him} looking ready to burst, whenever ${he} leaks or ${his} body absorbs too much. ${He} keeps ${himself} <span class="red">painfully full</span> for you, though ${he} barely notices it over ${his} horniness. While having ${his} body packed full of aphrodisiacs doesn't make ${him} additionally submissive, it does amplify the effects of them. ${His} aphrodisiac bursting belly`);
+					r.push(`two gallons of an aphrodisiac solution, leaving ${him} looking ready to burst, whenever ${he} leaks or ${his} body absorbs too much. ${He} keeps ${himself} <span class="health dec">painfully full</span> for you, though ${he} barely notices it over ${his} horniness. While having ${his} body packed full of aphrodisiacs doesn't make ${him} additionally submissive, it does amplify the effects of them. ${His} aphrodisiac bursting belly`);
 					healthDamage(slave, 10);
 				} else if (slave.inflation === 2) {
 					r.push(`four liters of an aphrodisiac solution, leaving ${him} looking pregnant, whenever ${he} leaks or ${his} body absorbs too much. ${He} is full enough to be distended but not enough to grow taut. While having ${his} body bloated with aphrodisiacs doesn't make ${him} additionally submissive, it does amplify the effects of them. ${His} overfilled aphrodisiac belly`);
@@ -164,7 +164,7 @@ App.SlaveAssignment.inflation = (function() {
 					r.push(`two liters of an aphrodisiac solution, leaving ${his} belly noticeably distended, whenever ${he} leaks or ${his} body absorbs too much. ${He} is full enough to be swollen but not enough to visibly jiggle. ${His} aphrodisiac filled belly`);
 				}
 				if (slave.energy <= 95) {
-					r.push(`<span class="green">rapidly boosts ${his} sex drive.</span>`);
+					r.push(`<span class="libido inc">rapidly boosts ${his} sex drive.</span>`);
 					slave.energy += 5 * slave.inflation;
 				} else {
 					r.push(`can't improve ${his} absurd sex drive.`);
@@ -174,7 +174,7 @@ App.SlaveAssignment.inflation = (function() {
 					slave.devotion += 5;
 				}
 				if (V.arcologies[0].FSBodyPuristLaw === 0 && V.healthyDrugsUpgrade === 0) {
-					r.push(`Such a high dosage held for so long <span class="red">isn't healthy.</span>`);
+					r.push(`Such a high dosage held for so long <span class="health dec">isn't healthy.</span>`);
 					healthDamage(slave, 5);
 				}
 				slave.chem += 2 * slave.inflation;
@@ -191,37 +191,37 @@ App.SlaveAssignment.inflation = (function() {
 				if (slave.inflation === 3) {
 					r.push(`two gallons of a curative solution, leaving ${him} looking ready to burst, whenever ${he} leaks or ${his} body absorbs too much. ${He} keeps ${himself} full for you; the curatives in ${his} gut keeping ${him} healthy despite the pain.`);
 					if ((slave.assignment === Job.REST || slave.assignment === Job.SPA) && slave.fetish !== "mindbroken") {
-						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="green">extremely effective health treatment,</span> though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="health inc">extremely effective health treatment,</span> though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
 						improveCondition(slave, 4);
 					} else if (slave.health.condition < -20) {
-						r.push(`<span class="green">${His} poor health rapidly improves</span> under the curative enema, though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`<span class="health inc">${His} poor health rapidly improves</span> under the curative enema, though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
 						improveCondition(slave, 2);
 					} else {
-						r.push(`<span class="green">${His} health improves</span> under the curative enema, though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`<span class="health inc">${His} health improves</span> under the curative enema, though being filled to ${his} limit doesn't hasten ${his} recovery at all; it's just perverted.`);
 					}
 					improveCondition(slave, 1);
 				} else if (slave.inflation === 2) {
 					r.push(`four liters of a curative solution, leaving ${him} looking pregnant, whenever ${he} leaks or ${his} body absorbs too much. ${He} is full enough to be distended but not enough to grow taut.`);
 					if ((slave.assignment === Job.REST || slave.assignment === Job.SPA) && slave.fetish !== "mindbroken") {
-						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="green">extremely effective health treatment,</span> though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="health inc">extremely effective health treatment,</span> though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
 						improveCondition(slave, 4);
 					} else if (slave.health.condition < -20) {
-						r.push(`<span class="green">${His} poor health rapidly improves</span> under the curative enema, though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`<span class="health inc">${His} poor health rapidly improves</span> under the curative enema, though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
 						improveCondition(slave, 2);
 					} else {
-						r.push(`<span class="green">${His} health improves</span> under curative enema, though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
+						r.push(`<span class="health inc">${His} health improves</span> under curative enema, though being overfilled doesn't hasten ${his} recovery at all; it's just perverted.`);
 					}
 					improveCondition(slave, 6);
 				} else if (slave.inflation === 1) {
 					r.push(`two liters of a curative solution, leaving ${his} belly noticeably distended, whenever ${he} leaks or ${his} body absorbs too much. ${He} is full enough to be swollen but not enough to visibly jiggle.`);
 					if ((slave.assignment === Job.REST || slave.assignment === Job.SPA) && slave.fetish !== "mindbroken") {
-						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="green">extremely effective health treatment.</span>`);
+						r.push(`The curative enema ${he}'s on synergize with rest, keeping ${him} asleep most of the time. This is an <span class="health inc">extremely effective health treatment.</span>`);
 						improveCondition(slave, 4);
 					} else if (slave.health.condition < -20) {
-						r.push(`<span class="green">${His} poor health rapidly improves</span> under the curative enema.`);
+						r.push(`<span class="health inc">${His} poor health rapidly improves</span> under the curative enema.`);
 						improveCondition(slave, 2);
 					} else {
-						r.push(`<span class="green">${His} health improves</span> under curative enema.`);
+						r.push(`<span class="health inc">${His} health improves</span> under curative enema.`);
 					}
 					improveCondition(slave, 6);
 				}
@@ -239,7 +239,7 @@ App.SlaveAssignment.inflation = (function() {
 			case "tightener":
 				r.push(`${He} makes sure to fill ${his} rear with nearly`);
 				if (slave.inflation === 3) {
-					r.push(`two gallons of tightening solution, leaving ${him} looking ready to burst, whenever ${he} leaks or ${his} body absorbs too much. ${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+					r.push(`two gallons of tightening solution, leaving ${him} looking ready to burst, whenever ${he} leaks or ${his} body absorbs too much. ${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 					healthDamage(slave, 10);
 				} else if (slave.inflation === 2) {
 					r.push(`four liters of tightening solution, leaving ${him} looking pregnant, whenever ${he} leaks or ${his} body absorbs too much. ${He} is full enough to be distended but not enough to grow taut.`);
@@ -332,7 +332,7 @@ App.SlaveAssignment.inflation = (function() {
 						cow.boobsMilk = 0;
 					}
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons`);
 						healthDamage(slave, 10);
 						slave.devotion -= 8;
 						slave.trust -= 8;
@@ -359,7 +359,7 @@ App.SlaveAssignment.inflation = (function() {
 						cow.boobsMilk = 0;
 					}
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons`);
 						healthDamage(slave, 10);
 					} else if (slave.inflation === 2) {
 						r.push(`bloated with nearly four liters`);
@@ -422,7 +422,7 @@ App.SlaveAssignment.inflation = (function() {
 						r.push(`${distensionTerm}.`);
 					}
 					if (slave.inflation === 3) {
-						r.push(`${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+						r.push(`${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 						healthDamage(slave, 10);
 					} else if (slave.inflation === 2) {
 						r.push(`${He} is full enough to be distended but not enough to grow taut.`);
@@ -444,7 +444,7 @@ App.SlaveAssignment.inflation = (function() {
 						r.push(`sucks ${cow.slaveName}'s ${(cow.dick > 0) ? `cock` : `cum hole`} until ${his} stomach is`);
 					}
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons`);
 						healthDamage(slave, 10);
 						slave.devotion -= 8;
 						slave.trust -= 8;
@@ -473,7 +473,7 @@ App.SlaveAssignment.inflation = (function() {
 						r.push(`sucks ${cow.slaveName}'s ${(cow.dick > 0) ? `cock` : `cum hole`} until ${his} stomach is`);
 					}
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons`);
 						healthDamage(slave, 10);
 					} else if (slave.inflation === 2) {
 						r.push(`bloated with nearly four liters`);
@@ -533,7 +533,7 @@ App.SlaveAssignment.inflation = (function() {
 						r.push(`${distensionTerm}.`);
 					}
 					if (slave.inflation === 3) {
-						r.push(`${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+						r.push(`${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 						healthDamage(slave, 10);
 					} else if (slave.inflation === 2) {
 						r.push(`${He} is full enough to be distended but not enough to grow taut.`);
@@ -548,7 +548,7 @@ App.SlaveAssignment.inflation = (function() {
 				if (slave.behavioralFlaw === "anorexic") {
 					r.push(`focuses ${his} <span class="mediumorchid">loathing</span> on you as ${he} forces down servings of slave food until ${his} stomach is`);
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons of the paste, ${distensionTerm}.`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons of the paste, ${distensionTerm}.`);
 						healthDamage(slave, 10);
 						slave.devotion -= 16;
 						slave.trust -= 16;
@@ -565,7 +565,7 @@ App.SlaveAssignment.inflation = (function() {
 				} else if (slave.behavioralFlaw === "gluttonous") {
 					r.push(`<span class="hotpink">eagerly</span> stuffs ${his} face with servings of slave food until ${his} stomach is`);
 					if (slave.inflation === 3) {
-						r.push(`<span class="red">painfully bloated</span> with nearly two gallons of the paste, ${distensionTerm}. ${He} rubs ${his} stuffed belly <span class="mediumaquamarine">contently,</span> anticipating ${his} next gorging.`);
+						r.push(`<span class="health dec">painfully bloated</span> with nearly two gallons of the paste, ${distensionTerm}. ${He} rubs ${his} stuffed belly <span class="mediumaquamarine">contently,</span> anticipating ${his} next gorging.`);
 						healthDamage(slave, 10);
 						slave.devotion += 10;
 						slave.trust += 10;
@@ -581,7 +581,7 @@ App.SlaveAssignment.inflation = (function() {
 				} else {
 					r.push(`makes sure to binge eat until`);
 					if (slave.inflation === 3) {
-						r.push(`${his} gut is stuffed with nearly two gallons of slave food, ${distensionTerm}. ${He} keeps ${himself} <span class="red">painfully full</span> for you.`);
+						r.push(`${his} gut is stuffed with nearly two gallons of slave food, ${distensionTerm}. ${He} keeps ${himself} <span class="health dec">painfully full</span> for you.`);
 						healthDamage(slave, 10);
 					} else if (slave.inflation === 2) {
 						r.push(`${his} gut is filled with nearly four liters of slave food, giving ${him} quite the food baby. ${He} is full enough to be distended but not enough to grow taut.`);
diff --git a/src/endWeek/saLiveWithHG.js b/src/endWeek/saLiveWithHG.js
index bd6c5eb51b69c7149e92987115ce40d674ab77f3..0f91df75a3bee70e0a16c8ecce969622a2879f06 100644
--- a/src/endWeek/saLiveWithHG.js
+++ b/src/endWeek/saLiveWithHG.js
@@ -49,10 +49,6 @@ App.SlaveAssignment.liveWithHG = (function() {
 		penetrativeUse = 0;
 		cervixPump = 0;
 
-		if (V.seeImages && V.seeReportImages) {
-			App.UI.DOM.appendNewElement("div", el, App.Art.SlaveArtElement(slave, 2, 0), ["imageRef", "medImg"]);
-		}
-
 		HGSetsDiet(slave, S.HeadGirl);
 		if (V.HGSuiteHormones !== 0) {
 			HGSetsHormones(slave, S.HeadGirl);
@@ -415,7 +411,7 @@ App.SlaveAssignment.liveWithHG = (function() {
 							if (HG.fetishKnown === 1) {
 								r.push(`Since their relationship is very abusive, only ${HG.slaveName} <span class="devotion inc">enjoys</span> living together with ${slave.slaveName}.`);
 							} else {
-								r.push(`It turns out their relationship is <span class="fetish gain">rather abusive</span>, so only ${HG.slaveName} gets any <span class="devotion inc">enjoyment</span> out of living together with ${slave.slaveName}.`);
+								r.push(`It turns out their relationship is <span class="fetish gain">rather abusive,</span> so only ${HG.slaveName} gets any <span class="devotion inc">enjoyment</span> out of living together with ${slave.slaveName}.`);
 							}
 							HG.devotion += 3;
 							slave.devotion -= 5;
@@ -719,7 +715,7 @@ App.SlaveAssignment.liveWithHG = (function() {
 				slave.attrKnown = 1;
 			}
 			if (HG.energy > 95 && slave.energy <= 95) {
-				r.push(`${slave.slaveName} has so much fun sex with ${HG.slaveName} that <span class="improvement">${his} sex drive is slowly enhanced.</span>`);
+				r.push(`${slave.slaveName} has so much fun sex with ${HG.slaveName} that <span class="libido inc">${his} sex drive is slowly enhanced.</span>`);
 				slave.energy += 2;
 			}
 		}
@@ -1625,24 +1621,14 @@ App.SlaveAssignment.liveWithHG = (function() {
 	function slaveReport(slave) {
 		if (V.showEWD === 0) {
 			/* App.SlaveAssignment.choosesOwnClothes(slave) */
-			App.SlaveAssignment.rules(slave);
-			App.SlaveAssignment.diet(slave);
-			App.SlaveAssignment.longTermEffects(slave);
-			App.SlaveAssignment.drugs(slave);
-			App.SlaveAssignment.relationships(slave);
-			App.SlaveAssignment.rivalries(slave);
+			App.SlaveAssignment.individualSlaveReport(slave);
 			App.SlaveAssignment.devotion(slave);
 		} else {
 			/* App.SlaveAssignment.choosesOwnClothes(slave) */
 			const content = App.UI.DOM.makeElement("div", '', "indent");
 			$(content).append(
-				App.SlaveAssignment.rules(slave),
-				App.SlaveAssignment.diet(slave),
-				App.SlaveAssignment.longTermEffects(slave),
-				App.SlaveAssignment.drugs(slave),
-				App.SlaveAssignment.relationships(slave),
-				App.SlaveAssignment.rivalries(slave),
-				`<div class="indent">${App.SlaveAssignment.devotion(slave)}</span>`);
+				...App.Events.spaceSentences(App.SlaveAssignment.individualSlaveReport(slave)),
+				`<div class="indent">${App.SlaveAssignment.devotion(slave)}</div>`);
 			r.push(content);
 		}
 	}
diff --git a/src/endWeek/saLongTermEffects.js b/src/endWeek/saLongTermEffects.js
index 1dfd4167a7356c9af00202b01fd0479d897600c2..02643bb3432fcdaada267a12e3bd00c276ed8488 100644
--- a/src/endWeek/saLongTermEffects.js
+++ b/src/endWeek/saLongTermEffects.js
@@ -366,22 +366,22 @@ App.SlaveAssignment.longTermEffects = (function() {
 					masochistic = 2;
 				}
 			}
-			if (masochistic === 1) {
-				if (slave.fetish === "masochist" && slave.fetishKnown === 1) {
-					r.push(`This is <span class="devotion inc">a long and extremely uncomfortable experience, which ${he} gets off on.</span>`);
-					slave.devotion += 4;
-				} else {
-					r.push(`This is <span class="devotion dec">a long and extremely uncomfortable experience.</span>`);
-					slave.devotion -= 4;
-				}
-			} else if (masochistic === 2) {
-				if (slave.fetish === "masochist" && slave.fetishKnown === 1) {
-					r.push(`<span class="devotion inc">gets off on.</span>`);
-					slave.devotion += 2;
-				} else {
-					r.push(`<span class="devotion dec">hates.</span>`);
-					slave.devotion -= 2;
-				}
+		}
+		if (masochistic === 1) {
+			if (slave.fetish === "masochist" && slave.fetishKnown === 1) {
+				r.push(`This is <span class="devotion inc">a long and extremely uncomfortable experience, which ${he} gets off on.</span>`);
+				slave.devotion += 4;
+			} else {
+				r.push(`This is <span class="devotion dec">a long and extremely uncomfortable experience.</span>`);
+				slave.devotion -= 4;
+			}
+		} else if (masochistic === 2) {
+			if (slave.fetish === "masochist" && slave.fetishKnown === 1) {
+				r.push(`<span class="devotion inc">gets off on.</span>`);
+				slave.devotion += 2;
+			} else {
+				r.push(`<span class="devotion dec">hates.</span>`);
+				slave.devotion -= 2;
 			}
 		}
 	}
@@ -736,6 +736,9 @@ App.SlaveAssignment.longTermEffects = (function() {
 				slave.hormoneBalance -= 4;
 			} else if (slave.hormones === -2) {
 				slave.hormoneBalance -= 12;
+			} else {
+				slave.drugs = "no drugs";
+				r.push(`${He} is not on hormones, so ${he} has been taken off hormone enhancers.`);
 			}
 			if (V.hormoneUpgradePower === 1) {
 				if (slave.hormones === 1) {
@@ -2537,16 +2540,16 @@ App.SlaveAssignment.longTermEffects = (function() {
 					deathSeed += 200;
 				}
 				if (random(1, 1000) > (400 + deathSeed)) {
-					planDeath(slave, "health");
+					planDeath(slave, "lowHealth");
 				}
 				if ((slave.aphrodisiacs > 0 || slave.inflationType === "aphrodisiac") && random(1, 1000) > (200 + deathSeed)) {
-					planDeath(slave, "OD");
+					planDeath(slave, "overdosed");
 				}
 			}
 			if (V.seeAge === 1) {
 				deathSeed = ((slave.health.health * 2) - (slave.physicalAge * 2) - (slave.chem * 4) - (slave.addict * 3));
 				if (slave.physicalAge >= Math.max((70 + (slave.health.health / 5) - (slave.addict) - (slave.chem / 20)), 50) && random(1, 1000) > 800 + deathSeed) {
-					planDeath(slave, "old");
+					planDeath(slave, "oldAge");
 				}
 			}
 		}
@@ -2569,7 +2572,7 @@ App.SlaveAssignment.longTermEffects = (function() {
 	function anaphrodisiacEffects(slave, oldEnergy) {
 		const maxEnergyGain = Math.round((75 - oldEnergy) / 9.3);
 		if (slave.aphrodisiacs === -1 && slave.energy - oldEnergy > maxEnergyGain) {
-			r.push(`Anaphrodisiacs <span class="stat drop">limit ${his} sex drive.</span>`);
+			r.push(`Anaphrodisiacs <span class="libido dec">limit ${his} sex drive.</span>`);
 			slave.energy = oldEnergy + maxEnergyGain;
 		}
 	}
diff --git a/src/endWeek/saLongTermMentalEffects.js b/src/endWeek/saLongTermMentalEffects.js
index fafd7a77f55c5b2dc8eccb0a8418731f141d943f..82ae98860059f5fcbddb59fba3e515f5098f6fff 100644
--- a/src/endWeek/saLongTermMentalEffects.js
+++ b/src/endWeek/saLongTermMentalEffects.js
@@ -96,12 +96,12 @@ App.SlaveAssignment.longTermMentalEffects = (function() {
 		if (slave.ovaImplant === "asexual" && isFertile(slave) && (slave.preg === 0 || (slave.preg >= 0 && slave.geneticQuirks.superfetation === 2))) {
 			r.push(`The frequent internal ejaculations and accompanying climaxes brought about by ${his} ovarian modifications keeps ${him} sexually sated.`);
 			if (slave.energy >= 10 && (slave.attrXY >= 10 || slave.attrXX >= 10)) {
-				r.push(`However, the constant self-gratification <span class="stat drop">both damages what ${he} finds attractive and leaves sex less satisfying.</span>`);
+				r.push(`However, the constant self-gratification both <span class="stat drop">damages what ${he} finds attractive</span> and <span class="libido dec">leaves sex less satisfying.</span>`);
 				slave.energy -= 10;
 				slave.attrXY = Math.clamp(slave.attrXY - 10, 0, 100);
 				slave.attrXX = Math.clamp(slave.attrXX - 10, 0, 100);
 			} else if (slave.energy >= 10) {
-				r.push(`However, the constant self-gratification <span class="stat drop">leaves sex less satisfying.</span>`);
+				r.push(`However, the constant self-gratification <span class="libido dec">leaves sex less satisfying.</span>`);
 				slave.energy -= 10;
 			} else if (slave.attrXY >= 10 || slave.attrXX >= 10) {
 				r.push(`However, the constant self-gratification <span class="stat drop">twists what ${he} finds attractive.</span>`);
@@ -777,7 +777,7 @@ App.SlaveAssignment.longTermMentalEffects = (function() {
 						slave.fetishStrength += 4;
 					}
 					if (slave.energy < 94) {
-						r.push(`${He}'s such a pervert that the depravity all around ${him} <span class="green">improves ${his} sex drive.</span>`);
+						r.push(`${He}'s such a pervert that the depravity all around ${him} <span class="libido inc">improves ${his} sex drive.</span>`);
 						slave.energy += 1;
 					}
 					break;
@@ -1789,7 +1789,7 @@ App.SlaveAssignment.longTermMentalEffects = (function() {
 				break;
 			default:
 				if (slave.trust >= -50) {
-					if (setup.gratefulCareers.includes(slave.career)) {
+					if (App.Data.Careers.General.grateful.includes(slave.career)) {
 						slave.trust += 1;
 						r.push(`${He} remembers how hard ${his} life was before ${he} was a slave, and`);
 						if (slave.trust > 50) {
@@ -1803,7 +1803,7 @@ App.SlaveAssignment.longTermMentalEffects = (function() {
 					}
 				}
 				if (slave.devotion >= -50) {
-					if (setup.menialCareers.includes(slave.career)) {
+					if (App.Data.Careers.General.menial.includes(slave.career)) {
 						slave.devotion += 1;
 						r.push(`${He} took orders a lot before ${he} was a slave, and is subconsciously`);
 						if (slave.trust > 50) {
diff --git a/src/endWeek/saLongTermPhysicalEffects.js b/src/endWeek/saLongTermPhysicalEffects.js
index 619d5fd8c0f7aca3126ccb3dafa224eaad7e827d..e17fd0dc15aa2ce4dcb0cb2c9d9d58275b6397ed 100644
--- a/src/endWeek/saLongTermPhysicalEffects.js
+++ b/src/endWeek/saLongTermPhysicalEffects.js
@@ -275,7 +275,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 			if (slave.foreskin > 0) {
 				if (slave.foreskin - slave.dick < -1) {
 					if (canAchieveErection(slave)) {
-						r.push(`${His} cockhead has grown to the point where it is much too large for ${his} foreskin, making ${his} erections so uncomfortable to achieve that ${his} <span class="stat drop">appetite for sex is reduced</span> by ${his} reluctance to undergo the discomfort.`);
+						r.push(`${His} cockhead has grown to the point where it is much too large for ${his} foreskin, making ${his} erections so uncomfortable to achieve that ${his} <span class="libido dec">appetite for sex is reduced</span> by ${his} reluctance to undergo the discomfort.`);
 						slave.energy -= 2;
 						if (random(1, 300) < slave.energy || slave.geneMods.rapidCellGrowth === 1) {
 							r.push(`Despite this, ${he} spends enough of ${his} time hard that ${his} foreskin <span class="change positive">stretches out naturally.</span>`);
@@ -284,7 +284,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 					}
 				} else if (slave.foreskin - slave.dick < 0) {
 					if (canAchieveErection(slave)) {
-						r.push(`${His} cockhead has grown to the point where it is too large for ${his} foreskin, making ${his} erections uncomfortable to achieve; ${his} <span class="stat drop">arousal is slightly impeded</span> by ${his} reluctance to undergo the discomfort.`);
+						r.push(`${His} cockhead has grown to the point where it is too large for ${his} foreskin, making ${his} erections uncomfortable to achieve; ${his} <span class="libido dec">arousal is slightly impeded</span> by ${his} reluctance to undergo the discomfort.`);
 						slave.energy -= 1;
 						if (random(1, 500) < slave.energy || slave.geneMods.rapidCellGrowth === 1) {
 							r.push(`Despite this, ${he} spends enough of ${his} time hard that ${his} foreskin <span class="change positive">stretches out naturally.</span>`);
@@ -315,7 +315,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 			if (slave.scrotum > 0) {
 				const extraScrotum = slave.scrotum - slave.balls;
 				if (extraScrotum < -1) {
-					r.push(`${His} nuts are much too big for ${his} ballsack, giving ${him} constant discomfort. ${His} <span class="stat drop">appetite for sex is reduced</span> by how much ${his} junk hurts.`);
+					r.push(`${His} nuts are much too big for ${his} ballsack, giving ${him} constant discomfort. ${His} <span class="libido dec">appetite for sex is reduced</span> by how much ${his} junk hurts.`);
 					slave.energy -= 2;
 					if (random(0, 2) === 0 || slave.geneMods.rapidCellGrowth === 1) {
 						r.push(`${His} scrotum <span class="change positive">stretches out naturally</span> as ${his} balls force it to accept their size.`);
@@ -462,7 +462,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 		if (slave.fetish !== "mindbroken") { // future proofing for mindbreak-pleasure
 			if (slave.devotion > 95) {
 				if (slave.energy <= 50) {
-					r.push(`${He}'s so worshipful of you that ${he} derives erotic satisfaction simply from being your slave, giving ${him} <span class="improvement">more of an appetite for sex.</span>`);
+					r.push(`${He}'s so worshipful of you that ${he} derives erotic satisfaction simply from being your slave, giving ${him} <span class="libido inc">more of an appetite for sex.</span>`);
 					slave.energy += 1;
 				}
 			} else if (slave.devotion > 50) {
@@ -473,30 +473,30 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 				const sexJobs = [Job.PUBLIC, Job.WHORE, Job.BROTHEL, Job.CLUB, Job.GLORYHOLE, Job.ARCADE, Job.FUCKTOY, Job.MASTERSUITE, Job.SUBORDINATE].includes(slave.assignment);
 				if (slave.devotion > 20) {
 					if (sexJobs) {
-						r.push(`${His} assignment constantly requires ${him} to fuck. ${He} obeys, but <span class="stat drop">${his} appetite for sex is reduced.</span>`);
+						r.push(`${His} assignment constantly requires ${him} to fuck. ${He} obeys, but <span class="libido dec">${his} appetite for sex is reduced.</span>`);
 						slave.energy -= 2;
 					} else {
-						r.push(`Sometimes, ${he} wishes ${he} weren't a sex slave, <span class="stat drop">reducing ${his} appetite for sex,</span> though less than if ${his} assignment required ${him} to fuck constantly.`);
+						r.push(`Sometimes, ${he} wishes ${he} weren't a sex slave, <span class="libido dec">reducing ${his} appetite for sex,</span> though less than if ${his} assignment required ${him} to fuck constantly.`);
 						slave.energy -= 1;
 					}
 				} else if (slave.devotion >= -20) {
 					if (sexJobs) {
-						r.push(`${His} assignment forces ${him} to let ${himself} get fucked constantly, <span class="stat drop">reducing ${his} appetite for sex.</span>`);
+						r.push(`${His} assignment forces ${him} to let ${himself} get fucked constantly, <span class="libido dec">reducing ${his} appetite for sex.</span>`);
 						slave.energy -= 3;
 					} else {
-						r.push(`${He} isn't used to life as a sex slave, and the constant sexual anxiety <span class="stat drop">reduces ${his} appetite for sex,</span> though less than if ${his} assignment forced ${him} to let ${himself} get fucked constantly.`);
+						r.push(`${He} isn't used to life as a sex slave, and the constant sexual anxiety <span class="libido dec">reduces ${his} appetite for sex,</span> though less than if ${his} assignment forced ${him} to let ${himself} get fucked constantly.`);
 						slave.energy -= 2;
 					}
 				} else {
 					if (sexJobs) {
-						r.push(`${His} assignment subjects ${him} to constant rape, <span class="stat drop">rapidly reducing ${his} appetite for sex.</span>`);
+						r.push(`${His} assignment subjects ${him} to constant rape, <span class="libido dec">rapidly reducing ${his} appetite for sex.</span>`);
 						slave.energy -= 5;
 					} else {
-						r.push(`${He} hates being a sex slave, <span class="stat drop">reducing ${his} appetite for sex,</span> though less than if ${his} assignment subjected ${him} to constant rape.`);
+						r.push(`${He} hates being a sex slave, <span class="libido dec">reducing ${his} appetite for sex,</span> though less than if ${his} assignment subjected ${him} to constant rape.`);
 						slave.energy -= 3;
 					}
 				}
-				Math.max(slave.energy, 0);
+				slave.energy = Math.max(slave.energy, 0);
 			}
 		}
 	}
@@ -570,10 +570,10 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 						slave.devotion -= decay;
 						slave.trust -= decay;
 					}
-					r.push(`Unable to achieve the release ${he} needs, ${his} <span class="stat drop">runaway libido is damaged.</span>`);
+					r.push(`Unable to achieve the release ${he} needs, ${his} <span class="libido dec">runaway libido is damaged.</span>`);
 					slave.energy -= decay;
 				} else {
-					r.push(`${He} is not allowed to get off as frequently as ${his} healthy sex drive demands, and the constant frustration <span class="stat drop">wears away at ${his} libido.</span>`);
+					r.push(`${He} is not allowed to get off as frequently as ${his} healthy sex drive demands, and the constant frustration <span class="libido dec">wears away at ${his} libido.</span>`);
 					slave.energy -= decay;
 				}
 			}
@@ -588,7 +588,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 		if (slave.health.condition > 90) {
 			if (slave.need <= slave.energy / 2) {
 				if (slave.energy <= 70) {
-					r.push(`${His} outstanding health produces a <span class="improvement">slow improvement in libido.</span>`);
+					r.push(`${His} outstanding health produces a <span class="libido inc">slow improvement in libido.</span>`);
 					slave.energy += 1;
 				}
 			}
@@ -617,7 +617,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 		} else if (slave.health.condition > 60) {
 			if (slave.need <= slave.energy / 2) {
 				if (slave.energy <= 50) {
-					r.push(`${His} good health produces a <span class="improvement">slow improvement in libido.</span>`);
+					r.push(`${His} good health produces a <span class="libido inc">slow improvement in libido.</span>`);
 					slave.energy += 1;
 				}
 			}
@@ -941,7 +941,7 @@ App.SlaveAssignment.longTermPhysicalEffects = (function() {
 			if ((countNCS < 2) && (slave.vagina > 1) && (random(1, 100) < (slave.vagina * 10))) {
 				r.push(`${His} <span class="ncs">NCS</span> manages to <span class="change positive">reverse the stretching in ${his} vagina, ${his} pussy now looks more childlike.</span>`);
 				slave.vagina -= 1;
-				countNCS++;
+				countNCS++; // These are in case anything is ever added after.
 			} else if ((countNCS < 2) && (slave.anus > 1) && (random(1, 100) < (slave.anus * 10))) {
 				r.push(`${His} <span class="ncs">NCS</span> effectively <span class="change positive">reverses the stretching in ${his} anus, ${his} ass-pussy now looks more childlike.</span>`);
 				slave.anus -= 1;
diff --git a/src/endWeek/saNanny.js b/src/endWeek/saNanny.js
index e2a6c8f7c62b16648a0c1ca4a647d406ea64f4af..9908fd1643332cf4955f0a4aa6ae2fe78f206167 100644
--- a/src/endWeek/saNanny.js
+++ b/src/endWeek/saNanny.js
@@ -61,7 +61,7 @@ App.SlaveAssignment.nanny = function(slave) {
 		let t = '';
 
 		// TODO:
-		if (setup.servantCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.servant.includes(slave.career)) {
 			t += ` ${He} has experience with nannying from ${his} life before ${he} was a slave, making ${him} more effective.`;
 		} else if (slave.skill.servant >= V.masteredXP) {
 			t += ` ${He} has experience with nannying from working for you, making ${him} more effective.`;
diff --git a/src/endWeek/saPleaseYou.js b/src/endWeek/saPleaseYou.js
index 429356747b7a560d642da9d018f2300a710d280f..6f8dfd270b91428fcc3dad8f73d77ef6f612745f 100644
--- a/src/endWeek/saPleaseYou.js
+++ b/src/endWeek/saPleaseYou.js
@@ -1452,7 +1452,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 	 */
 	function physicalEffects(slave) {
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			r.push(`${He} is<span class="red">`);
+			r.push(`${He} is<span class="health dec">`);
 			if (slave.health.tired > 60) {
 				if (slave.health.illness === 1) {
 					r.push(`feeling under the weather and`);
@@ -1490,7 +1490,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 			}
 		}
 		if (slave.health.condition < 0 && jsRandom(1, 100) > 50) {
-			r.push(`Under your personal supervision, <span class="green">${his} health improves.</span>`);
+			r.push(`Under your personal supervision, <span class="health inc">${his} health improves.</span>`);
 			improveCondition(slave, 10);
 		}
 		if (slave.assignment === Job.MASTERSUITE) {
@@ -1498,7 +1498,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 			tired(slave);
 		} else if (slave.assignment === Job.FUCKTOY) {
 			if (slaveResting(slave)) {
-				r.push(`${He} spends reduced hours serving you in order to <span class="green">offset ${his} lack of rest.</span>`);
+				r.push(`${He} spends reduced hours serving you in order to <span class="health inc">offset ${his} lack of rest.</span>`);
 			} else if (slave.health.tired + 11 >= 90 && !willWorkToDeath(slave)) {
 				r.push(`${He} attempts to rebuke your advances due to ${his} exhaustion, but can do little to avoid them or the resulting <span class="trust dec">severe punishment.</span> It still feels like <span class="devotion dec">fucking a dead fish,</span> since it's obvious that ${he} has chosen ${his} overall well-being over angering you.`);
 				slave.devotion -= 10;
@@ -1513,7 +1513,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 				if (slave.devotion > 20) {
 					r.push(`so despite ending ${his} days out of breath and soaked with sweat, ${he} doesn't find ${himself} that tired in the morning.`);
 				} else {
-					r.push(`but just doesn't understand that <span class="red">${he}'d be less tired</span> if ${he} simply gave you what you want.`);
+					r.push(`but just doesn't understand that <span class="health dec">${he}'d be less tired</span> if ${he} simply gave you what you want.`);
 				}
 			}
 			tired(slave);
diff --git a/src/endWeek/saPregnancy.js b/src/endWeek/saPregnancy.js
index e2c6a82a14a7f902e1b193901fb2fcf3c044c3da..7411e739ccf258926f74c1f3618b767d01c62f1f 100644
--- a/src/endWeek/saPregnancy.js
+++ b/src/endWeek/saPregnancy.js
@@ -110,48 +110,48 @@ App.SlaveAssignment.pregnancy = (function() {
 	function pregnancyLibido(slave) {
 		if (slave.geneticQuirks.uterineHypersensitivity === 2 && V.geneticMappingUpgrade >= 1) {
 			if (slave.preg >= slave.pregData.normalBirth) {
-				r.push(`${He}'s full-term and has never been hornier. ${His} uterine hypersensitivity combined with ${his} full womb and upcoming birth confers a <span class="improvement">huge improvement in ${his} sexual appetite.</span>`);
+				r.push(`${He}'s full-term and has never been hornier. ${His} uterine hypersensitivity combined with ${his} full womb and upcoming birth confers a <span class="libido inc">huge improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 7;
 			} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-				r.push(`Being hugely pregnant with uterine hypersensitivity confers an <span class="improvement">improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being hugely pregnant with uterine hypersensitivity confers an <span class="libido inc">improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 5;
 			} else if (slave.preg > slave.pregData.normalBirth / 2) {
-				r.push(`Being pregnant with uterine hypersensitivity confers a <span class="improvement">slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being pregnant with uterine hypersensitivity confers a <span class="libido inc">slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 3;
 			} else if (slave.preg > slave.pregData.normalBirth / 4) {
-				r.push(`${His} new pregnancy excites ${him} and produces <span class="improvement">very slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`${His} new pregnancy excites ${him} and produces <span class="libido inc">very slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 1;
 			} else if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
 				r.push(`The rigors of early pregnancy do not seem to decrease ${his} sex drive. If anything, it seems to be exciting ${him}.`);
 			}
 		} else if (slave.fetish === "pregnancy" && slave.fetishKnown === 1) {
 			if (slave.preg >= slave.pregData.normalBirth) {
-				r.push(`${He}'s full-term and has never been hornier. ${His} pregnancy fetish combined with ${his} ripe belly confers a <span class="improvement">huge improvement in ${his} sexual appetite.</span>`);
+				r.push(`${He}'s full-term and has never been hornier. ${His} pregnancy fetish combined with ${his} ripe belly confers a <span class="libido inc">huge improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 5;
 			} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-				r.push(`Being a pregnancy fetishist and hugely pregnant confers an <span class="improvement">improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being a pregnancy fetishist and hugely pregnant confers an <span class="libido inc">improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 3;
 			} else if (slave.preg > slave.pregData.normalBirth / 2) {
-				r.push(`Being a pregnancy fetishist and pregnant confers a <span class="improvement">slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being a pregnancy fetishist and pregnant confers a <span class="libido inc">slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 2;
 			} else if (slave.preg > slave.pregData.normalBirth / 4) {
-				r.push(`${His} new pregnancy excites ${him} and produces <span class="improvement">very slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`${His} new pregnancy excites ${him} and produces <span class="libido inc">very slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 1;
 			} else if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
 				r.push(`The rigors of early pregnancy do not seem to decrease ${his} sex drive. If anything, it seems to be exciting ${him}.`);
 			}
 		} else if (slave.fetish === "pregnancy") {
 			if (slave.preg >= slave.pregData.normalBirth) {
-				r.push(`${He}'s full-term and has never been hornier. ${His} advanced pregnancy confers a <span class="improvement">huge improvement in ${his} sexual appetite.</span>`);
+				r.push(`${He}'s full-term and has never been hornier. ${His} advanced pregnancy confers a <span class="libido inc">huge improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 5;
 			} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-				r.push(`Being hugely pregnant confers an <span class="improvement">improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being hugely pregnant confers an <span class="libido inc">improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 3;
 			} else if (slave.preg > slave.pregData.normalBirth / 2) {
-				r.push(`Being pregnant confers a <span class="improvement">slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being pregnant confers a <span class="libido inc">slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 2;
 			} else if (slave.preg > slave.pregData.normalBirth / 4) {
-				r.push(`${His} new pregnancy excites ${him} and produces <span class="improvement">very slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`${His} new pregnancy excites ${him} and produces <span class="libido inc">very slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 1;
 			} else if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
 				r.push(`The rigors of early pregnancy do not seem to decrease ${his} sex drive. If anything, it seems to be exciting ${him}.`);
@@ -164,16 +164,16 @@ App.SlaveAssignment.pregnancy = (function() {
 			}
 		} else if (slave.geneticQuirks.uterineHypersensitivity === 2) {
 			if (slave.preg >= slave.pregData.normalBirth) {
-				r.push(`${He}'s full-term and has never been hornier, conferring a <span class="improvement">huge improvement in ${his} sexual appetite.</span>`);
+				r.push(`${He}'s full-term and has never been hornier, conferring a <span class="libido inc">huge improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 7;
 			} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-				r.push(`Being hugely pregnant confers an <span class="improvement">improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being hugely pregnant confers an <span class="libido inc">improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 5;
 			} else if (slave.preg > slave.pregData.normalBirth / 2) {
-				r.push(`Being pregnant confers a <span class="improvement">slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`Being pregnant confers a <span class="libido inc">slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 3;
 			} else if (slave.preg > slave.pregData.normalBirth / 4) {
-				r.push(`${His} new pregnancy excites ${him} and produces <span class="improvement">very slow improvement in ${his} sexual appetite.</span>`);
+				r.push(`${His} new pregnancy excites ${him} and produces <span class="libido inc">very slow improvement in ${his} sexual appetite.</span>`);
 				slave.energy += 1;
 			} else if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
 				r.push(`The rigors of early pregnancy do not seem to decrease ${his} sex drive. If anything, it seems to be exciting ${him}.`);
@@ -181,52 +181,52 @@ App.SlaveAssignment.pregnancy = (function() {
 		} else { // not pregnancy fetish
 			if (slave.energy < 41) {
 				if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
-					r.push(`The rigors of early pregnancy <span class="stat drop">reduce ${his} sexual appetite.</span>`);
+					r.push(`The rigors of early pregnancy <span class="libido dec">reduce ${his} sexual appetite.</span>`);
 					slave.energy -= 3;
 				} else if (slave.preg >= slave.pregData.normalBirth) {
-					r.push(`${He} is full-term and feels massively unattractive, <span class="stat drop">greatly suppressing ${his} sexual appetite.</span>`);
+					r.push(`${He} is full-term and feels massively unattractive, <span class="libido dec">greatly suppressing ${his} sexual appetite.</span>`);
 					slave.energy -= 4;
 				} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-					r.push(`${His} advanced pregnancy <span class="stat drop">greatly suppresses ${his} sexual appetite.</span>`);
+					r.push(`${His} advanced pregnancy <span class="libido dec">greatly suppresses ${his} sexual appetite.</span>`);
 					slave.energy -= 3;
 				} else if (slave.preg > slave.pregData.normalBirth / 2) {
-					r.push(`${His} growing pregnancy <span class="stat drop">suppresses ${his} sexual appetite.</span>`);
+					r.push(`${His} growing pregnancy <span class="libido dec">suppresses ${his} sexual appetite.</span>`);
 					slave.energy -= 2;
 				} else if (slave.bellyPreg >= 1500) {
-					r.push(`${His} visible pregnancy causes ${him} to feel unattractive, <span class="stat drop">reducing ${his} sex drive.</span>`);
+					r.push(`${His} visible pregnancy causes ${him} to feel unattractive, <span class="libido dec">reducing ${his} sex drive.</span>`);
 					slave.energy -= 1;
 				}
 			} else if (slave.energy < 61) {
 				if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
-					r.push(`The rigors of early pregnancy <span class="stat drop">slightly reduce ${his} sexual appetite.</span>`);
+					r.push(`The rigors of early pregnancy <span class="libido dec">slightly reduce ${his} sexual appetite.</span>`);
 					slave.energy -= 1;
 				} else if (slave.preg >= slave.pregData.normalBirth) {
-					r.push(`${He} is full-term and <span class="improvement">hornier than ever.</span>`);
+					r.push(`${He} is full-term and <span class="libido inc">hornier than ever.</span>`);
 					slave.energy += 2;
 				} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-					r.push(`${His} advanced pregnancy <span class="improvement">increases ${his} libido.</span>`);
+					r.push(`${His} advanced pregnancy <span class="libido inc">increases ${his} libido.</span>`);
 					slave.energy += 1;
 				}
 			} else if (slave.energy < 90) {
 				if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
-					r.push(`The rigors of early pregnancy <span class="stat drop">reduce ${his} sexual appetite.</span>`);
+					r.push(`The rigors of early pregnancy <span class="libido dec">reduce ${his} sexual appetite.</span>`);
 					slave.energy -= 3;
 				} else if (slave.preg >= slave.pregData.normalBirth) {
-					r.push(`${He} is full-term and suffering from an <span class="improvement">unquenchable need for sex.</span>`);
+					r.push(`${He} is full-term and suffering from an <span class="libido inc">unquenchable need for sex.</span>`);
 					slave.energy += 4;
 				} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
-					r.push(`${His} advanced pregnancy comes with a hugely increased libido, <span class="improvement">greatly increasing ${his} sexual drive.</span>`);
+					r.push(`${His} advanced pregnancy comes with a hugely increased libido, <span class="libido inc">greatly increasing ${his} sexual drive.</span>`);
 					slave.energy += 3;
 				} else if (slave.preg > slave.pregData.normalBirth / 2) {
-					r.push(`${His} growing pregnancy comes with an increased libido, <span class="improvement">spurring ${his} sexual appetite.</span>`);
+					r.push(`${His} growing pregnancy comes with an increased libido, <span class="libido inc">spurring ${his} sexual appetite.</span>`);
 					slave.energy += 2;
 				}
 			} else {
 				if (slave.preg <= slave.pregData.normalBirth / 4 && slave.preg > slave.pregData.normalBirth / 13.33) {
-					r.push(`The rigors of early pregnancy <span class="stat drop">reduce ${his} sexual appetite.</span>`);
+					r.push(`The rigors of early pregnancy <span class="libido dec">reduce ${his} sexual appetite.</span>`);
 					slave.energy -= 3;
 				} else if (slave.preg >= slave.pregData.normalBirth) {
-					r.push(`${He} is full-term and <span class="improvement">horny as hell.</span> ${His} hormones and already high libido have ${him} acting like ${he} hasn't had a good fuck in nine months.`);
+					r.push(`${He} is full-term and <span class="libido inc">horny as hell.</span> ${His} hormones and already high libido have ${him} acting like ${he} hasn't had a good fuck in nine months.`);
 					slave.energy += 2;
 				} else if (slave.preg > slave.pregData.normalBirth / 1.33) {
 					r.push(`${His} advanced pregnancy, combined with ${his} already high libido, has ${him} practically begging for sex whenever ${he} has a spare moment.`);
@@ -442,7 +442,7 @@ App.SlaveAssignment.pregnancy = (function() {
 							r.push(`${He} is <span class="devotion inc">delirious with joy</span> over ${his} straining womb. Every week ${he} gets bigger, fuller and tighter; in ${his} mind, it won't be long until ${he} bursts, bringing ${his} children into the world.`);
 							slave.devotion += 10;
 						} else if (slave.geneticQuirks.uterineHypersensitivity === 2) {
-							r.push(`${He} is <span class="improvement">extremely aroused</span> over ${his} straining womb.`);
+							r.push(`${He} is <span class="libido inc">extremely aroused</span> over ${his} straining womb.`);
 							if (V.geneticMappingUpgrade >= 1) {
 								r.push(`${His} hypersensitive uterus is overstimulated by the pressure, clouding both pain and worry from ${his} mind.`);
 							} else {
@@ -465,7 +465,7 @@ App.SlaveAssignment.pregnancy = (function() {
 							slave.trust -= 10;
 						}
 					} else if (slave.geneticQuirks.uterineHypersensitivity === 2) {
-						r.push(`${He} is <span class="improvement">extremely aroused</span> over ${his} straining womb.`);
+						r.push(`${He} is <span class="libido inc">extremely aroused</span> over ${his} straining womb.`);
 						if (V.geneticMappingUpgrade >= 1) {
 							r.push(`${His} hypersensitive uterus is overstimulated by the pressure, clouding both pain and worry from ${his} mind.`);
 						} else {
diff --git a/src/endWeek/saRecruitGirls.js b/src/endWeek/saRecruitGirls.js
index 2815560f3a779593e31196a055cf500cda8f0ee2..b477133b47db7a84ea4877a4a9d0b43176312ef9 100644
--- a/src/endWeek/saRecruitGirls.js
+++ b/src/endWeek/saRecruitGirls.js
@@ -597,14 +597,14 @@ App.SlaveAssignment.recruitGirls = (function() {
 			} else if (arcology.FSHedonisticDecadence !== "unset") {
 				if (targetArcology.FSHedonisticDecadence !== "unset") {
 					r.push(`${He} advances Hedonistic Decadence there by stuffing ${his} face at all times, having one hand down ${his} pants, and inviting anyone near ${him} for a quick fuck whenever the mood strikes ${him}.`);
-					if (["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "self hating"].includes(slave.sexualFlaw)) {
+					if (App.Data.misc.paraphiliaList.includes(slave.sexualFlaw)) {
 						r.push(`${He} wears ${his} sexual paraphilia proudly and makes sure everyone knows what ${he} likes and how ${he} likes it.`);
 						targetArcology.FSHedonisticDecadence++;
 					}
 					targetArcology.FSHedonisticDecadence += influence;
 				} else if (targetArcology.FSPhysicalIdealist !== "unset") {
 					r.push(`${He} challenges Physical Idealism there by stuffing ${his} face at all times, having one hand down ${his} pants, and inviting anyone near ${him} for a quick fuck whenever the mood strikes ${him}.`);
-					if (["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "self hating"].includes(slave.sexualFlaw)) {
+					if (App.Data.misc.paraphiliaList.includes(slave.sexualFlaw)) {
 						r.push(`${He} wears ${his} sexual paraphilia proudly and make sure everyone knows what ${he} likes and how ${he} likes it.`);
 						targetArcology.FSPhysicalIdealist--;
 					}
@@ -800,7 +800,7 @@ App.SlaveAssignment.recruitGirls = (function() {
 			r.push(`${His} imperfect feelings toward you give ${him} little conviction.`);
 		}
 
-		if (setup.recruiterCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.recruiter.includes(slave.career)) {
 			r.push(`${He} has experience in recruitment from before ${he} was a slave.`);
 			V.recruiterProgress += 2;
 		} else if (slave.skill.recruiter >= V.masteredXP) {
diff --git a/src/endWeek/saRelationships.js b/src/endWeek/saRelationships.js
index 38cf71f30eebc29217eb33b9a90439f20a4c727c..ed45109b57e076010e0767e6109d44058c11aa2a 100644
--- a/src/endWeek/saRelationships.js
+++ b/src/endWeek/saRelationships.js
@@ -5,8 +5,6 @@ App.SlaveAssignment.relationships = (function() {
 
 	// eslint-disable-next-line no-unused-vars
 	let he, him, his, himself, He, His, wife, woman;
-	// eslint-disable-next-line no-unused-vars
-	let he2, him2, his2, girl2, wife2, He2;
 	let playerPronouns;
 
 	let PC;
@@ -154,10 +152,8 @@ App.SlaveAssignment.relationships = (function() {
 				if (randomSeed > 75) {
 					let resentment = 0;
 					for (const potentialFriend of V.slaves) {
-						({
-							he2, him2, his2, girl2, He2
-						} = getPronouns(potentialFriend).appendSuffix("2"));
 						if (canStartFriendship(slave, potentialFriend) && potentialFriend.assignment !== Job.CONFINEMENT) {
+							const {him2, He2} = getPronouns(potentialFriend).appendSuffix("2");
 							if (potentialFriend.ID === V.MadamID && slave.assignment === Job.BROTHEL) {
 								if (potentialFriend.rules.relationship !== "restrictive" && silverTongue(potentialFriend, manipulationSkill)) {
 									r.push(`${slave.slaveName} manages to ingratiate ${himself} with the Madam, ${potentialFriend.slaveName}. The two slaves have <span class="relationship">struck up a friendship.</span>`);
@@ -285,10 +281,10 @@ App.SlaveAssignment.relationships = (function() {
 									}
 								}
 							} else if (mutualChildren(slave, potentialFriend, V.slaves) > 0) {
-								 if (App.Utils.sexAllowed(slave, potentialFriend)) {
-									 if (potentialFriend.rules.relationship === "permissive") {
+								if (App.Utils.sexAllowed(slave, potentialFriend)) {
+									if (potentialFriend.rules.relationship === "permissive") {
 										r.push(`${slave.slaveName} and ${potentialFriend.slaveName} have`);
-										if (mutualChildren(slave, potentialFriend, V.slaves) === 0) {
+										if (mutualChildren(slave, potentialFriend, V.slaves) === 1) {
 											r.push(`a child`);
 										} else {
 											r.push(`children`);
@@ -350,10 +346,11 @@ App.SlaveAssignment.relationships = (function() {
 	 *
 	 */
 	function existingRelationship(slave) {
+		let him2, his2, wife2;
 		if (slave.relationship > 0) {
 			friend = getSlave(slave.relationshipTarget);
 			({
-				he2, him2, his2, girl2, He2
+				him2, his2, wife2
 			} = getPronouns(friend).appendSuffix("2"));
 		}
 		if (slave.fetish === "mindbroken" && slave.relationship !== -3) {
@@ -972,9 +969,9 @@ App.SlaveAssignment.relationships = (function() {
 	 */
 	function consummateRelationship(slave) {
 		let lover = getSlave(slave.relationshipTarget);
-		({
-			he2, him2, his2, girl2, He2, wife2
-		} = getPronouns(lover).appendSuffix("2"));
+		const {
+			he2, him2, his2, girl2, wife2
+		} = getPronouns(lover).appendSuffix("2");
 
 		incestReactions(slave, lover);
 		if (App.Utils.sexAllowed(slave, lover)) {
@@ -1013,16 +1010,16 @@ App.SlaveAssignment.relationships = (function() {
 			}
 			if (slave.energy <= 90) {
 				if (lover.energy > 95) {
-					r.push(`${lover.slaveName} is such a sex addict that ${he2} drags ${slave.slaveName} along with ${him2} in their enthusiastic sex life, <span class="improvement">slowly improving ${his} sex drive.</span>`);
+					r.push(`${lover.slaveName} is such a sex addict that ${he2} drags ${slave.slaveName} along with ${him2} in their enthusiastic sex life, <span class="libido inc">slowly improving ${his} sex drive.</span>`);
 					slave.energy += 3;
 				} else if (lover.fetish === slave.fetish && lover.fetish !== "none") {
-					r.push(`${lover.slaveName} and ${slave.slaveName} enjoy sharing their sexual fetishes so much it <span class="improvement">improves their sex drives</span> in proportion to the strength of their kinks.`);
+					r.push(`${lover.slaveName} and ${slave.slaveName} enjoy sharing their sexual fetishes so much it <span class="libido inc">improves their sex drives</span> in proportion to the strength of their kinks.`);
 					slave.energy += 1 + Math.trunc(slave.fetishStrength / 30);
 				} else if (slave.attrXX > 65 && (lover.vagina > -1 || (lover.face > 0 && lover.faceShape !== "masculine"))) {
-					r.push(`${slave.slaveName} is very much attracted to ${lover.slaveName}, and their fulfilling sexual relationship <span class="improvement">gradually improves ${slave.slaveName}'s sex drive.</span>`);
+					r.push(`${slave.slaveName} is very much attracted to ${lover.slaveName}, and their fulfilling sexual relationship <span class="libido inc">gradually improves ${slave.slaveName}'s sex drive.</span>`);
 					slave.energy++;
 				} else if (slave.attrXY > 65 && lover.dick > 0) {
-					r.push(`${slave.slaveName} enjoys ${lover.slaveName} and ${his2} cock, and their fulfilling sexual relationship <span class="improvement">gradually improves ${slave.slaveName}'s sex drive.</span>`);
+					r.push(`${slave.slaveName} enjoys ${lover.slaveName} and ${his2} cock, and their fulfilling sexual relationship <span class="libido inc">gradually improves ${slave.slaveName}'s sex drive.</span>`);
 					slave.energy++;
 				}
 			}
@@ -1180,36 +1177,154 @@ App.SlaveAssignment.relationships = (function() {
 	 *
 	 */
 	function familyFeelings(slave) {
+		/** @param {Map<string, Array<App.Entity.SlaveState>>} map
+		 *  @param {App.Entity.SlaveState} relative */
+		function addToRelativeMap(map, relative) {
+			const term = relativeTerm(slave, relative);
+			if (!map.has(term)) {
+				map.set(term, [relative]);
+			} else {
+				map.get(term).push(relative);
+			}
+		}
+
+		/** @param {Map<string, Array<App.Entity.SlaveState>>} map
+		 *  @returns {Array<string>} */
+		function relativeMapToGroupArray(map) {
+			let groups = [];
+			for (const [type, people] of map) {
+				if (people.length > 1) {
+					groups.push(`${his} ${type}s ${arrayToSentence(people.map(s => s.slaveName))}`);
+				} else {
+					groups.push(`${his} ${type} ${people[0].slaveName}`);
+				}
+			}
+			return groups;
+		}
+
+		/** @param {Map<string, Array<App.Entity.SlaveState>>} map
+		 *  @returns {App.Entity.SlaveState} */
+		function singleRelativeInMap(map) {
+			if (map.size !== 1) {
+				return null;
+			}
+			/** @type {App.Entity.SlaveState[]} */
+			const slavesOfType = map.values().next().value;
+			if (slavesOfType.length !== 1) {
+				return null;
+			}
+			return slavesOfType[0];
+		}
+
+		/** @param {Map<string, Array<App.Entity.SlaveState>>} map
+		 *  @returns {number} */
+		function relativeMapTotalSize(map) {
+			let size = 0;
+			for (const people of map.values()) {
+				size += people.length;
+			}
+			return size;
+		}
+
+		const overwhelmed = 5;
 		if (slave.trust <= 95) {
-			let relatives = V.slaves.filter((s) => areRelated(slave, s)); // Is it possible to move this into the loop?
-			for (const relative of relatives) {
-				({
-					he2, him2
-				} = getPronouns(relative).appendSuffix("2"));
-				if (slave.trust < -20) {
+			let relatives = V.slaves.filter((s) => areRelated(slave, s));
+			if (slave.trust < -20) {
+				/** @type {Array<App.Entity.SlaveState>} */
+				const worriedAboutChildren = [];
+				/** @type {Map<string, Array<App.Entity.SlaveState>>} */
+				const worriedAboutRelatives = new Map();
+				for (const relative of relatives) {
 					if (slave.rivalryTarget !== relative.ID) {
 						if (isParentP(relative, slave)) {
-							r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} child ${relative.slaveName} is also your slave and might suffer if either of them angers you, and <span class="devotion inc">does ${his} best</span> to protect ${him2}.`);
-							slave.trust -= 2;
-							slave.devotion += 6;
+							worriedAboutChildren.push(relative);
+							if (worriedAboutChildren.length <= overwhelmed) {
+								slave.trust -= 2;
+								slave.devotion += 6;
+							}
 						} else {
-							r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${his} ${relativeTerm(slave, relative)} ${relative.slaveName} is also your slave and might suffer if either of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
-							slave.trust -= 1;
-							slave.devotion += 3;
+							addToRelativeMap(worriedAboutRelatives, relative);
+							if (relativeMapTotalSize(worriedAboutRelatives) <= overwhelmed) {
+								slave.trust -= 1;
+								slave.devotion += 3;
+							}
 						}
 					}
-				} else {
-					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, relative)} ${relative.slaveName}`);
+				}
+				if (worriedAboutChildren.length > 1) {
+					r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} children ${arrayToSentence(worriedAboutChildren.map(s => s.slaveName))} are also your slaves and might suffer if any of them angers you, and <span class="devotion inc">does ${his} best</span> to protect them.`);
+				} else if (worriedAboutChildren.length > 0) {
+					const {him2} = getPronouns(worriedAboutChildren[0]).appendSuffix("2");
+					r.push(`${slave.slaveName} is <span class="trust dec">agonizingly aware</span> that ${his} child ${worriedAboutChildren[0].slaveName} is also your slave and might suffer if either of them angers you, and <span class="devotion inc">does ${his} best</span> to protect ${him2}.`);
+				}
+				if (worriedAboutChildren.length > overwhelmed) {
+					r.push(`${He} has so many children to worry about that ${he} is overwhelmed with fear and <span class="trust inc">forced to trust you.</span>`);
+				}
+				let singleRelative = singleRelativeInMap(worriedAboutRelatives);
+				if (singleRelative) {
+					r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} is also your slave and might suffer if either of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
+				} else if (worriedAboutRelatives.size > 0) {
+					const groups = relativeMapToGroupArray(worriedAboutRelatives);
+					r.push(`${slave.slaveName} is <span class="trust dec">painfully conscious</span> that ${arrayToSentence(groups)} are also your slaves and might suffer if any of them displeases you, and <span class="devotion inc">tries to obey</span> as best ${he} can.`);
+				}
+				if (relativeMapTotalSize(worriedAboutRelatives) > overwhelmed) {
+					r.push(`${He} has so many relatives to worry about that ${he} is overwhelmed with fear and <span class="trust inc">forced to trust you.</span>`);
+				}
+			} else {
+				/** @type {Map<string, Array<App.Entity.SlaveState>>} */
+				const devotedRelatives = new Map();
+				/** @type {Map<string, Array<App.Entity.SlaveState>>} */
+				const obedientRelatives = new Map();
+				/** @type {Map<string, Array<App.Entity.SlaveState>>} */
+				const hatefulRelatives = new Map();
+				for (const relative of relatives) {
 					if (relative.devotion > 50) {
-						r.push(`loves being your sex slave, and is <span class="devotion inc">happy</span> for ${him2}.`);
-						slave.devotion += 4;
+						addToRelativeMap(devotedRelatives, relative);
+						if (relativeMapTotalSize(devotedRelatives) <= overwhelmed) {
+							slave.devotion += 4;
+						}
 					} else if (relative.devotion > 20 || relative.trust < -20) {
-						r.push(`is an obedient sex slave, and hopes ${he2}'ll avoid punishment.`);
+						addToRelativeMap(obedientRelatives, relative);
 					} else {
-						r.push(`hates being a sex slave, and is <span class="trust dec">afraid</span> for ${him2}.`);
-						slave.trust -= 1;
+						addToRelativeMap(hatefulRelatives, relative);
+						if (relativeMapTotalSize(hatefulRelatives) <= overwhelmed) {
+							slave.trust -= 1;
+						}
 					}
 				}
+				let singleRelative = singleRelativeInMap(devotedRelatives);
+				if (singleRelative) {
+					const {him2} = getPronouns(singleRelative).appendSuffix('2');
+					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} loves being your sex slave, and is <span class="devotion inc">happy</span> for ${him2}.`);
+				} else if (devotedRelatives.size > 0) {
+					const groups = relativeMapToGroupArray(devotedRelatives);
+					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} all love being your sex slaves, and is <span class="devotion inc">happy</span> for them.`);
+				}
+				if (relativeMapTotalSize(devotedRelatives) > overwhelmed) {
+					r.push(`${He} has so many relatives that love being your slaves that ${he} is sometimes overwhelmed with joy and <span class="devotion dec">neglects ${his} duties.</span>`);
+				}
+				singleRelative = singleRelativeInMap(obedientRelatives);
+				if (singleRelative) {
+					const {he2} = getPronouns(singleRelative).appendSuffix('2');
+					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} is an obedient sex slave, and hopes ${he2}'ll avoid punishment.`);
+				} else if (obedientRelatives.size > 0) {
+					const groups = relativeMapToGroupArray(obedientRelatives);
+					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} are obedient sex slaves, and hopes they'll avoid punishment.`);
+				}
+				if (relativeMapTotalSize(obedientRelatives) > overwhelmed) {
+					r.push(`${He} has so many obedient relatives that ${he} sometimes forgets about some of them.`);
+				}
+				singleRelative = singleRelativeInMap(hatefulRelatives);
+				if (singleRelative) {
+					const {him2} = getPronouns(singleRelative).appendSuffix('2');
+					r.push(`${slave.slaveName} knows that ${his} ${relativeTerm(slave, singleRelative)} ${singleRelative.slaveName} hates being a sex slave, and is <span class="trust dec">afraid</span> for ${him2}.`);
+				} else if (hatefulRelatives.size > 0) {
+					const groups = relativeMapToGroupArray(hatefulRelatives);
+					r.push(`${slave.slaveName} knows that ${arrayToSentence(groups)} all hate being sex slaves, and is <span class="trust dec">afraid</span> for them.`);
+				}
+				if (relativeMapTotalSize(hatefulRelatives) > overwhelmed) {
+					r.push(`${He} has so many relatives that hate being your sex slaves that ${he} is overwhelmed with fear and <span class="trust inc">just has to trust you to take care of them.</span>`);
+				}
 			}
 		}
 	}
diff --git a/src/endWeek/saRest.js b/src/endWeek/saRest.js
index 06f0b9ac8b9ec40c58a7525c2e4f1b01caabd3a7..9b7dcbd1e93b3f4be7e6753b35e6ad6711adcb40 100644
--- a/src/endWeek/saRest.js
+++ b/src/endWeek/saRest.js
@@ -57,7 +57,7 @@ App.SlaveAssignment.rest = function(slave) {
 	}
 
 	if (slave.health.illness > 0 || slave.health.tired > 60) {
-		t += ` Since ${he} is<span class="red">`;
+		t += ` Since ${he} is<span class="health dec">`;
 		if (slave.health.illness === 1) {
 			t += ` feeling under the weather`;
 		} else if (slave.health.illness === 2) {
diff --git a/src/endWeek/saRewardAndPunishment.js b/src/endWeek/saRewardAndPunishment.js
index 66d37095b1592b9fb975654d42bd57a3c1b46d7f..79978e8ffe7c475501847fbb6949c173dbd0d7f2 100644
--- a/src/endWeek/saRewardAndPunishment.js
+++ b/src/endWeek/saRewardAndPunishment.js
@@ -60,14 +60,14 @@ App.SlaveAssignment.rewardAndPunishment = (function() {
 			if (slave.relationship > 0) {
 				r.push(`${He} often asks to save these breaks so ${he} can spend them with ${his} ${relationshipTerm(slave)}.`);
 			}
-			r.push(`These breaks are <span class="green">good for ${him}.</span>`);
+			r.push(`These breaks are <span class="health inc">good for ${him}.</span>`);
 			improveCondition(slave, rewards);
 			return r;
 		},
 
 		drugs: function() {
 			let r = [];
-			r.push(`${He}'s <span class="hotpink">rewarded</span> with hits of mild recreational drugs, which <span class="red">isn't healthy,</span> but helps bind ${him} to you strongly.`);
+			r.push(`${He}'s <span class="hotpink">rewarded</span> with hits of mild recreational drugs, which <span class="health dec">isn't healthy,</span> but helps bind ${him} to you strongly.`);
 			if ([Job.CLUB, Job.PUBLIC].includes(slave.assignment)) {
 				r.push(`${His} patrons don't complain either.`);
 			}
@@ -97,7 +97,7 @@ App.SlaveAssignment.rewardAndPunishment = (function() {
 			} else {
 				r.push(`a quick climax from a vibrator,`);
 			}
-			r.push(`<span class="green">boosting ${his} libido.</span>`);
+			r.push(`<span class="libido inc">boosting ${his} libido.</span>`);
 			if (slave.energy < 98) {
 				slave.energy += rewards;
 			}
@@ -149,7 +149,7 @@ App.SlaveAssignment.rewardAndPunishment = (function() {
 			if (slave.assignment === Job.SCHOOL) {
 				r.push(`dragged to the front of the class and`);
 			}
-			r.push(`<span class="gold">whipped,</span> not hard enough to mark ${him}${slave.assignment === Job.CLINIC ? `or complicate ${his} stay` : ``}, but hard enough to <span class="red">hurt,</span> breaking ${him} quickly.`);
+			r.push(`<span class="gold">whipped,</span> not hard enough to mark ${him}${slave.assignment === Job.CLINIC ? `or complicate ${his} stay` : ``}, but hard enough to <span class="health dec">hurt,</span> breaking ${him} quickly.`);
 			healthDamage(slave, punishments);
 			slave.trust -= 2*punishments;
 			return r;
@@ -173,7 +173,7 @@ App.SlaveAssignment.rewardAndPunishment = (function() {
 			} else {
 				r.push(`${he}'s <span class="gold">denied</span> ${his} next orgasm,`);
 			}
-			r.push(`<span class="red">reducing ${his} libido</span> but breaking ${him} to <span class="hotpink">sexual obedience.</span>`);
+			r.push(`<span class="libido dec">reducing ${his} libido</span> but breaking ${him} to <span class="hotpink">sexual obedience.</span>`);
 			if (slave.energy > 2) {
 				slave.energy -= 2*punishments;
 			}
diff --git a/src/endWeek/saRules.js b/src/endWeek/saRules.js
index 0b325131f894578811cd9dab3baee08ced124b1b..2e1b33261e3abef2b8feef70a38fe666c0d345c9 100644
--- a/src/endWeek/saRules.js
+++ b/src/endWeek/saRules.js
@@ -196,7 +196,7 @@ App.SlaveAssignment.rules = function(slave) {
 						}
 					}
 					if (slave.rules.living === "luxurious") {
-						r.push(`They provide <span class="green">satisfying rest</span> every time ${he} drifts off to sleep.`);
+						r.push(`They provide <span class="health inc">satisfying rest</span> every time ${he} drifts off to sleep.`);
 					} else if (slave.rules.living === "spare") {
 						if (slave.devotion > 20 && slave.trust <= 10) {
 							r.push(`They don't provide much rest, however.`);
@@ -206,9 +206,9 @@ App.SlaveAssignment.rules = function(slave) {
 					} else {
 						r.push(`They provide`);
 						if (slave.devotion > 20) {
-							r.push(`<span class="green">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
+							r.push(`<span class="health inc">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
 						} else {
-							r.push(`<span class="green">adequate rest,</span> but not enough for a slave lacking time management.`);
+							r.push(`<span class="health inc">adequate rest,</span> but not enough for a slave lacking time management.`);
 						}
 					}
 
@@ -402,9 +402,9 @@ App.SlaveAssignment.rules = function(slave) {
 					}
 					r.push(`It provides`);
 					if (slave.devotion > 20) {
-						r.push(`<span class="green">adequate rest</span> for a slut that knows how to manage ${his} time.`);
+						r.push(`<span class="health inc">adequate rest</span> for a slut that knows how to manage ${his} time.`);
 					} else {
-						r.push(`<span class="green">adequate rest,</span> but not enough for a slut lacking time management.`);
+						r.push(`<span class="health inc">adequate rest,</span> but not enough for a slut lacking time management.`);
 					}
 
 					if (slave.rules.rest === "mandatory") {
@@ -1748,7 +1748,7 @@ App.SlaveAssignment.rules = function(slave) {
 						}
 					}
 					if (slave.rules.living === "luxurious") {
-						r.push(`They provide <span class="green">satisfying rest</span> every time ${he} drifts off to sleep.`);
+						r.push(`They provide <span class="health inc">satisfying rest</span> every time ${he} drifts off to sleep.`);
 					} else if (slave.rules.living === "spare") {
 						if (slave.devotion > 20 && slave.trust <= 10) {
 							r.push(`They don't provide much rest, however.`);
@@ -1758,9 +1758,9 @@ App.SlaveAssignment.rules = function(slave) {
 					} else {
 						r.push(`They provide`);
 						if (slave.devotion > 20) {
-							r.push(`<span class="green">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
+							r.push(`<span class="health inc">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
 						} else {
-							r.push(`<span class="green">adequate rest,</span> but not enough for a slave lacking time management.`);
+							r.push(`<span class="health inc">adequate rest,</span> but not enough for a slave lacking time management.`);
 						}
 					}
 
@@ -2051,11 +2051,11 @@ App.SlaveAssignment.rules = function(slave) {
 							}
 						}
 						if (slave.rules.living === "luxurious") {
-							r.push(`It provides a <span class="green">satisfying rest</span> every time ${he} drifts off to sleep.`);
+							r.push(`It provides a <span class="health inc">satisfying rest</span> every time ${he} drifts off to sleep.`);
 						} else if (slave.rules.living === "spare") {
 							if (slave.devotion > 20) {
 								if (adequateConditions) {
-									r.push(`They are <span class="green">quite relaxing</span>`);
+									r.push(`They are <span class="health inc">quite relaxing</span>`);
 								} else {
 									r.push(`They suffice`);
 								}
@@ -2070,9 +2070,9 @@ App.SlaveAssignment.rules = function(slave) {
 						} else {
 							r.push(`It provides`);
 							if (slave.devotion > 20) {
-								r.push(`<span class="green">more than enough rest</span> for a happy cow looking to unwind.`);
+								r.push(`<span class="health inc">more than enough rest</span> for a happy cow looking to unwind.`);
 							} else {
-								r.push(`<span class="green">adequate rest,</span> but only to cows capable of appreciating what they've got.`);
+								r.push(`<span class="health inc">adequate rest,</span> but only to cows capable of appreciating what they've got.`);
 							}
 						}
 
@@ -2639,7 +2639,7 @@ App.SlaveAssignment.rules = function(slave) {
 							} else {
 								r.push(`It provides a`);
 							}
-							r.push(`<span class="green">satisfying rest</span> every time ${he} drifts off to sleep.`);
+							r.push(`<span class="health inc">satisfying rest</span> every time ${he} drifts off to sleep.`);
 						} else if (slave.rules.living === "spare") {
 							if (slave.devotion > 20 && slave.trust <= 10) {
 								r.push(`They don't provide much rest, however.`);
@@ -2653,9 +2653,9 @@ App.SlaveAssignment.rules = function(slave) {
 								r.push(`It provides`);
 							}
 							if (slave.devotion > 20) {
-								r.push(`<span class="green">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
+								r.push(`<span class="health inc">adequate rest</span> for a ${girl} that knows how to manage ${his} time.`);
 							} else {
-								r.push(`<span class="green">adequate rest,</span> but not enough for a slave lacking time management.`);
+								r.push(`<span class="health inc">adequate rest,</span> but not enough for a slave lacking time management.`);
 							}
 						}
 					}
@@ -2682,7 +2682,7 @@ App.SlaveAssignment.rules = function(slave) {
 									r.push(`<span class="mediumorchid">lowers you</span> in ${his} opinion`);
 									slave.devotion -= 3;
 								}
-								r.push(`and is <span class="red">not good for ${him},</span> since it's difficult to rest there.`);
+								r.push(`and is <span class="health dec">not good for ${him},</span> since it's difficult to rest there.`);
 								healthDamage(slave, 2);
 							} else {
 								r.push(`<span class="yellow">extremely overcrowded.</span> The unpleasant situation`);
@@ -2693,7 +2693,7 @@ App.SlaveAssignment.rules = function(slave) {
 									r.push(`seriously <span class="mediumorchid">lowers you</span> in ${his} opinion`);
 									slave.devotion -= 5;
 								}
-								r.push(`and is <span class="red">bad for ${his} health.</span>`);
+								r.push(`and is <span class="health dec">bad for ${his} health.</span>`);
 								healthDamage(slave, 4);
 							}
 						}
diff --git a/src/endWeek/saRulesFunctions.js b/src/endWeek/saRulesFunctions.js
index 4d890dd0c007ae5b958ac0186f475297e1224cb2..9669e3a08174895ef75d9a4b45ff6bb765ccd8f9 100644
--- a/src/endWeek/saRulesFunctions.js
+++ b/src/endWeek/saRulesFunctions.js
@@ -560,7 +560,7 @@ App.EndWeek.Rules.masturbationDrugEffects = function(slave) {
 			}
 			if (slave.energy > 40) {
 				el.append(`The constant orgasms steadily lose their impact, `);
-				App.UI.DOM.appendNewElement("span", el, `weakening ${his} sex drive.`, "red");
+				App.UI.DOM.appendNewElement("span", el, `weakening ${his} sex drive.`, ["libido", "dec"]);
 				slave.energy -= 2;
 			}
 		}
@@ -600,7 +600,7 @@ App.EndWeek.Rules.masturbationDrugEffects = function(slave) {
 	}
 	if (slave.energy > 60) {
 		el.append(`${His} chronic masturbation `);
-		App.UI.DOM.appendNewElement("span", el, `steadily dulls`, "red");
+		App.UI.DOM.appendNewElement("span", el, `steadily dulls`, ["libido", "dec"]);
 		el.append(` ${his} sexual pleasure. `);
 		slave.energy--;
 	}
@@ -1362,13 +1362,13 @@ App.EndWeek.Rules.noReleaseDrugEffects = function(slave) {
 				el.append(`Forbidden to masturbate or seek relief through sex, he `);
 				App.UI.DOM.appendNewElement("span", el, `can't find relief`, "mediumorchid");
 				el.append(` and `);
-				App.UI.DOM.appendNewElement("span", el, `the situation harms ${his} health.`, "red");
+				App.UI.DOM.appendNewElement("span", el, `the situation harms ${his} health.`, ["health", "dec"]);
 				slave.devotion -= 2;
 			} else {
 				el.append(`${His} terribly swollen balls ache, cultivating a need for sex that `);
 				App.UI.DOM.appendNewElement("span", el, `${he} can't fulfill`, "mediumorchid");
 				el.append(` and `);
-				App.UI.DOM.appendNewElement("span", el, `harming ${his} health.`, "red");
+				App.UI.DOM.appendNewElement("span", el, `harming ${his} health.`, ["health", "dec"]);
 				slave.devotion -= 2;
 			}
 			healthDamage(slave, 3);
@@ -1383,13 +1383,13 @@ App.EndWeek.Rules.noReleaseDrugEffects = function(slave) {
 				el.append(`Forbidden to masturbate or seek relief through sex, he `);
 				App.UI.DOM.appendNewElement("span", el, `can't find relief`, "mediumorchid");
 				el.append(` and `);
-				App.UI.DOM.appendNewElement("span", el, `the situation harms ${his} health.`, "red");
+				App.UI.DOM.appendNewElement("span", el, `the situation harms ${his} health.`, ["health", "dec"]);
 				slave.devotion -= 4;
 			} else {
 				el.append(`${His} grotesquely swollen balls ache, cultivating a need for sex that `);
 				App.UI.DOM.appendNewElement("span", el, `${he} can't fulfill`, "mediumorchid");
 				el.append(` and `);
-				App.UI.DOM.appendNewElement("span", el, `harming ${his} health.`, "red");
+				App.UI.DOM.appendNewElement("span", el, `harming ${his} health.`, ["health", "dec"]);
 				slave.devotion -= 4;
 			}
 			healthDamage(slave, 6);
@@ -1412,7 +1412,7 @@ App.EndWeek.Rules.noReleaseDrugEffects = function(slave) {
 	}
 	if (slave.energy > 50) {
 		el.append(`${His} enforced chastity `);
-		App.UI.DOM.appendNewElement("span", el, `habituates ${him} to a lack of release.`, "red");
+		App.UI.DOM.appendNewElement("span", el, `habituates ${him} to a lack of release.`, ["libido", "dec"]);
 		slave.energy -= 2;
 	}
 	return el;
diff --git a/src/endWeek/saServant.js b/src/endWeek/saServant.js
index 73d85623825d2be10b0957c9707275bd140b187e..9b4d6a1ecd02bb545797895927f4480e8ab8000a 100644
--- a/src/endWeek/saServant.js
+++ b/src/endWeek/saServant.js
@@ -145,7 +145,7 @@ App.SlaveAssignment.servant = (function() {
 			r.push(`${He} does ${his} very best to be the perfect house${wife}, making ${him} an outstanding servant.`);
 		}
 
-		if (setup.servantCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.servant.includes(slave.career)) {
 			r.push(`${He} has experience with house keeping from ${his} life before ${he} was a slave, making ${him} more effective.`);
 		} else if (slave.skill.servant >= V.masteredXP) {
 			r.push(`${He} has experience with house keeping from working for you, making ${him} more effective.`);
@@ -193,7 +193,7 @@ App.SlaveAssignment.servant = (function() {
 	 */
 	function physicalEffects(slave) {
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			r.push(`${He} is<span class="red">`);
+			r.push(`${He} is<span class="health dec">`);
 			if (slave.health.tired > 60) {
 				if (slave.health.illness === 1) {
 					r.push(`feeling under the weather and`);
@@ -228,7 +228,7 @@ App.SlaveAssignment.servant = (function() {
 		}
 		if (slave.assignment === window.Job.QUARTER) {
 			if (slaveResting(slave)) {
-				r.push(`${He} is assigned easy tasks <span class="green">so ${he} may rest</span> while still being productive.`);
+				r.push(`${He} is assigned easy tasks <span class="health inc">so ${he} may rest</span> while still being productive.`);
 			} else if (slave.health.tired + 11 >= 90 && !willWorkToDeath(slave)) {
 				r.push(`${He} attempts to refuse to work due to ${his} exhaustion, but can do little to avoid it or the resulting`);
 				if (S.Stewardess) {
@@ -243,13 +243,13 @@ App.SlaveAssignment.servant = (function() {
 				if (slave.devotion > 20) {
 					r.push(`there's plenty of opportunities for ${him} to relax, be it drifting into a meditative trance while cleaning or even just taking a seat while folding sheets.`);
 				} else {
-					r.push(`<span class="red">${he}'d find ${himself} less tired</span> if ${he} simply could only learn to lose ${himself} in ${his} work.`);
+					r.push(`<span class="health dec">${he}'d find ${himself} less tired</span> if ${he} simply could only learn to lose ${himself} in ${his} work.`);
 				}
 			}
 			tired(slave);
 		} else if (slave.assignment === window.Job.HOUSE) {
 			if (slaveResting(slave)) {
-				r.push(`${He} is assigned easy tasks <span class="green">so ${he} may rest</span> while still being productive.`);
+				r.push(`${He} is assigned easy tasks <span class="health inc">so ${he} may rest</span> while still being productive.`);
 			} else if (slave.health.tired + 11 >= 90 && !willWorkToDeath(slave)) {
 				r.push(`${He} attempts to refuse to work due to ${his} exhaustion, but can do little to avoid it or the resulting`);
 				if (V.servantsQuarters > 0 && V.universalRulesFacilityWork === 1 && V.servantsQuartersSpots > 0 && S.Stewardess) {
@@ -264,7 +264,7 @@ App.SlaveAssignment.servant = (function() {
 				if (slave.devotion > 20) {
 					r.push(`there's plenty of opportunities for ${him} to relax, be it drifting into a meditative trance while cleaning or even just taking a seat while folding sheets.`);
 				} else {
-					r.push(`<span class="red">${he}'d find ${himself} less tired</span> if ${he} simply could only learn to lose ${himself} in ${his} work.`);
+					r.push(`<span class="health dec">${he}'d find ${himself} less tired</span> if ${he} simply could only learn to lose ${himself} in ${his} work.`);
 				}
 			}
 			tired(slave);
diff --git a/src/endWeek/saServeThePublic.js b/src/endWeek/saServeThePublic.js
index 261705930f3d4eba2e857d065b91fc87a3441e22..149be8fd13ee257dd1e1da5eba062758a30ab11c 100644
--- a/src/endWeek/saServeThePublic.js
+++ b/src/endWeek/saServeThePublic.js
@@ -167,7 +167,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 							}
 						}
 						if (V.DJignoresFlaws !== 1) {
-							if (!["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "none", "self hating"].includes(slave.sexualFlaw) && jsRandom(1, 100) > 90) {
+							if (!App.Data.misc.paraphiliaList.includes(slave.sexualFlaw) && slave.sexualFlaw !== "none" && jsRandom(1, 100) > 90) {
 								r += ` ${SlaveFullName(S.DJ)} manages to <span class="flaw break">break</span> ${slave.slaveName} of ${his} sexual flaws.`;
 								slave.sexualFlaw = "none";
 							} else if (slave.behavioralFlaw !== "none" && jsRandom(1, 100) > 90) {
@@ -284,7 +284,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 	 */
 	function physicalEffects(slave) {
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			r += ` ${He} is<span class="red">`;
+			r += ` ${He} is<span class="health dec">`;
 			if (slave.health.illness === 1) {
 				r += ` feeling under the weather`;
 			} else if (slave.health.illness === 2) {
@@ -308,7 +308,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 		}
 		if (slave.assignment === Job.CLUB) {
 			if (slaveResting(slave)) {
-				r += ` ${He} spends reduced hours working the floor in ${V.clubName} in order to <span class="green">offset ${his} lack of rest.</span>`;
+				r += ` ${He} spends reduced hours working the floor in ${V.clubName} in order to <span class="health inc">offset ${his} lack of rest.</span>`;
 			} else if (slave.health.tired + 15 >= 90 && !willWorkToDeath(slave)) {
 				r += ` ${He} attempts to turn down citizens due to ${his} exhaustion, but can do little to stop it or the resulting `;
 				if (S.DJ) {
@@ -326,12 +326,12 @@ App.SlaveAssignment.serveThePublic = (function() {
 				} else {
 					r += `being a popular slut, `;
 				}
-				r += `so ${he} ends ${his} shifts <span class="red">eager for sleep.</span>`;
+				r += `so ${he} ends ${his} shifts <span class="health dec">eager for sleep.</span>`;
 			}
 			tired(slave);
 		} else if (slave.assignment === Job.PUBLIC) {
 			if (slaveResting(slave)) {
-				r += ` ${He} spends reduced hours serving in order to <span class="green">offset ${his} lack of rest.</span>`;
+				r += ` ${He} spends reduced hours serving in order to <span class="health inc">offset ${his} lack of rest.</span>`;
 			} else if (slave.health.tired + 15 >= 90 && !willWorkToDeath(slave)) {
 				r += ` ${He} attempts to turn down citizens due to ${his} exhaustion, but can do little to avoid it or the resulting `;
 				if (V.club > 0 && V.universalRulesFacilityWork === 1 && V.clubSpots > 0 && S.DJ) {
@@ -343,7 +343,7 @@ App.SlaveAssignment.serveThePublic = (function() {
 				slave.devotion -= 10;
 				slave.trust -= 5;
 			} else {
-				r += ` It gets tiring being a popular slut and ${he} ends ${his} shifts <span class="red">eager for sleep.</span>`;
+				r += ` It gets tiring being a popular slut and ${he} ends ${his} shifts <span class="health dec">eager for sleep.</span>`;
 			}
 			tired(slave);
 		}
@@ -358,45 +358,45 @@ App.SlaveAssignment.serveThePublic = (function() {
 				}
 				r += ` protect ${him} from the wear of being a slut.`;
 			} else if (slave.health.condition < -50) {
-				r += ` The stress of being a slut while in terrible condition is <span class="red">very hard on ${him}.</span>`;
+				r += ` The stress of being a slut while in terrible condition is <span class="health dec">very hard on ${him}.</span>`;
 			} else if (slave.health.condition < -20 && jsRandom(1, 100) > 50) {
-				r += ` The stress of being a slut while in poor condition is <span class="red">hard on ${him}.</span>`;
+				r += ` The stress of being a slut while in poor condition is <span class="health dec">hard on ${him}.</span>`;
 			} else if (slave.vagina < 0) {
 				if (slave.minorInjury === 0) {
 					r += ` ${He}'s such an expert public servant that ${he} copes with the stress of being a ${SlaveTitle(slave)} slut.`;
 				} else {
-					r += ` The stress of being a ${SlaveTitle(slave)} slut is <span class="red">hard on ${him}.</span>`;
+					r += ` The stress of being a ${SlaveTitle(slave)} slut is <span class="health dec">hard on ${him}.</span>`;
 				}
 			} else if (slave.vagina > 0 && !canDoVaginal(slave)) {
 				if (canDoAnal(slave)) {
 					if (slave.minorInjury === 0) {
 						r += ` ${He}'s such an expert slut that ${he} copes with the stress of being limited to buttsex and oral.`;
 					} else {
-						r += ` The stress of being limited to buttsex and oral is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being limited to buttsex and oral is <span class="health dec">hard on ${him}.</span>`;
 					}
 				} else {
 					if (slave.minorInjury === 0) {
 						r += ` ${He}'s such an expert slut that ${he} copes with the stress of being limited to oral.`;
 					} else {
-						r += ` The stress of being limited to oral sex is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being limited to oral sex is <span class="health dec">hard on ${him}.</span>`;
 					}
 				}
 			}
 			if (slave.minorInjury !== 0) {
 				if (slave.minorInjury === "sore ass") {
-					r += ` Rough anal with a careless citizen left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` Rough anal with a careless citizen left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (slave.minorInjury === "black eye") {
-					r += ` A violent citizen left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` A violent citizen left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (slave.minorInjury === "split lip") {
-					r += ` An abusive citizen left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` An abusive citizen left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (slave.minorInjury === "bad bruise") {
-					r += ` A rough citizen left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` A rough citizen left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else {
-					r += ` The hard labor of constant sex left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` The hard labor of constant sex left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 				}
 			}
 		}
@@ -468,10 +468,10 @@ App.SlaveAssignment.serveThePublic = (function() {
 	 */
 	function slaveSkills(slave) {
 		let skillIncrease;
-		if (!setup.entertainmentCareers.includes(slave.career) && slave.skill.entertainer < V.masteredXP) {
+		if (!App.Data.Careers.General.entertainment.includes(slave.career) && slave.skill.entertainer < V.masteredXP) {
 			slave.skill.entertainer += jsRandom(1, Math.ceil((slave.intelligence + slave.intelligenceImplant) / 15) + 8);
 		}
-		if (setup.entertainmentCareers.includes(slave.career)) {
+		if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 			r += ` ${He} has experience with entertainment from ${his} life before ${he} was a slave, making ${him} more effective.`;
 		} else if (slave.skill.entertainer >= V.masteredXP) {
 			r += ` ${He} has learned to be entertaining from working for you, making ${him} more effective.`;
diff --git a/src/endWeek/saServeYourOtherSlaves.js b/src/endWeek/saServeYourOtherSlaves.js
index edac378504491aeea46d2c40ef881a5ee8f134b8..fc24c16e7cb24a87c13c4415c2c77aeb367e6dcf 100644
--- a/src/endWeek/saServeYourOtherSlaves.js
+++ b/src/endWeek/saServeYourOtherSlaves.js
@@ -19,8 +19,10 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 	let fuckCount;
 	let cervixPump;
 
+	/** @type {App.Entity.SlaveState} */
 	let domSlave;
 	let domName;
+	/** @type {FC.Race} */
 	let domRace;
 
 	let domFetishKnown;
@@ -29,6 +31,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 	let subHatesDom;
 
 	let subName;
+	/** @type {FC.Race} */
 	let subRace;
 	let hands;
 
@@ -244,7 +247,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 			} else if (load > 0.5) {
 				r.push(`With ${his} servicing sisters, ${his} workload is reasonable and ${he} isn't overworked.`);
 			} else if (load > 0.2) {
-				r.push(`While ${he} may have support in servicing your stock, ${he} is <span class="red">overwhelmed by their collective need.</span>`);
+				r.push(`While ${he} may have support in servicing your stock, ${he} is <span class="health dec">overwhelmed by their collective need.</span>`);
 				if (slave.sexualFlaw === "self hating") {
 					r.push(`With so many other slaves taking advantage of ${his} body, ${his} life's purpose of <span class="hotpink">being nothing more than a piece of meat</span> has come true.`);
 					slave.devotion += 5;
@@ -269,7 +272,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 				} else {
 					r.push(`there are so few other slaves`);
 				}
-				r.push(`servicing your stock, ${he} is used to the <span class="red">point of exhaustion.</span>`);
+				r.push(`servicing your stock, ${he} is used to the <span class="health dec">point of exhaustion.</span>`);
 				healthDamage(slave, 10);
 				if (App.EndWeek.saVars.averageDick > 5) {
 					if (canDoVaginal(slave) && slave.vagina > 0 && slave.vagina < 4) {
@@ -682,7 +685,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 			} else {
 				r.push(`${domName} carefully uses gestures to communicate to ${him} that ${he} might as well scream.`);
 			}
-			r.push(`${subName}'s struggles to escape become desperate at this, and more desperate still when the week's first <span class="red">beating</span> starts. Later, ${he} tries going limp and unresistant, but that just makes the annoyed ${domName} beat ${his2} pain slave all the harder until ${he2} gets a reaction again. It's a long and <span class="hotpink">will breaking</span> week for ${subName}, and ${domName} certainly <span class="hotpink">enjoys torturing ${his2} toy.</span>`);
+			r.push(`${subName}'s struggles to escape become desperate at this, and more desperate still when the week's first <span class="health dec">beating</span> starts. Later, ${he} tries going limp and unresistant, but that just makes the annoyed ${domName} beat ${his2} pain slave all the harder until ${he2} gets a reaction again. It's a long and <span class="hotpink">will breaking</span> week for ${subName}, and ${domName} certainly <span class="hotpink">enjoys torturing ${his2} toy.</span>`);
 			slave.devotion += 1;
 			healthDamage(slave, 3);
 			if (slave.need && slave.fetish === "masochist") {
@@ -712,7 +715,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 			} else {
 				r.push(`a`);
 			}
-			r.push(`crop and implores ${subName} to punish ${him2}; ${domName} has been bad and ${subName} needs to teach ${him2} a lesson. ${subName} spends a long and <span class="trust inc">empowering</span> week learning new ways to <span class="red">inflict pain</span> on someone, not that the <span class="hotpink">thoroughly bruised ${domName} will complain.</span>`);
+			r.push(`crop and implores ${subName} to punish ${him2}; ${domName} has been bad and ${subName} needs to teach ${him2} a lesson. ${subName} spends a long and <span class="trust inc">empowering</span> week learning new ways to <span class="health dec">inflict pain</span> on someone, not that the <span class="hotpink">thoroughly bruised ${domName} will complain.</span>`);
 			slave.trust += 1;
 			healthDamage(domSlave, 3);
 			if (slave.need && slave.fetish === "sadist") {
@@ -939,7 +942,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 						r.push(`dick`);
 					}
 					r.push(`is licked clean of any slave food clinging to it.`);
-					if (domSlave.skills.oral - ((slave.dick * 15) - 20) >= 0) {
+					if (domSlave.skill.oral - ((slave.dick * 15) - 20) >= 0) {
 						r.push(`${subName} practically throws ${his} cup when ${domName} sucks the entire length of ${his} dick into ${his2} mouth and down ${his2} throat, and`);
 					} else {
 						r.push(`${subName} shudders as ${domName} wraps ${his2} lips around ${his} cockhead and`);
@@ -1643,7 +1646,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 			} else {
 				r.push(`${He}`);
 			}
-			r.push(`is<span class="red">`);
+			r.push(`is<span class="health dec">`);
 			if (slave.health.tired > 60) {
 				if (slave.health.illness === 1) {
 					r.push(`feeling under the weather and`);
@@ -1682,7 +1685,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 		}
 		if (jobType === "stud") {
 			if (slaveResting(slave)) {
-				r.push(`${He} is only made available during certain hours to maximize ${his} potency while <span class="green">keeping ${him} rested.</span>`);
+				r.push(`${He} is only made available during certain hours to maximize ${his} potency while <span class="health inc">keeping ${him} rested.</span>`);
 				if (slave.fetish === "mindbroken" && slave.fuckdoll > 0) {
 					r.push(`This doesn't stop recreational breeding, should ${he} be in the mood, but gives ${him} a chance to recover as needed.`);
 				}
@@ -1700,7 +1703,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 			}
 		} else if (jobType === "cumdump") {
 			if (slaveResting(slave)) {
-				r.push(`${He} is only available during certain hours in <span class="green">an effort to keep ${him} rested.</span>`);
+				r.push(`${He} is only available during certain hours in <span class="health inc">an effort to keep ${him} rested.</span>`);
 			} else if (slave.health.tired >= 60 && !willWorkToDeath(slave)) {
 				r.push(`As much as ${he} <span class="devotion dec">wishes to resist being used</span> in ${his} tired state, ${he} <span class="trust dec">can do little to avoid it.</span>`);
 				slave.devotion -= 5;
@@ -1709,13 +1712,13 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 				if (slave.devotion > 20) {
 					r.push(`${He} is accustomed enough to slave life to properly manage ${his} time.`);
 				} else {
-					r.push(`${He} <span class="red">wastes time and energy resisting</span> where a properly broken slave would accept what is happening and take it.`);
+					r.push(`${He} <span class="health dec">wastes time and energy resisting</span> where a properly broken slave would accept what is happening and take it.`);
 				}
 			}
 			tiredFucks(slave);
 		} else if (jobType === "sub") {
 			if (slaveResting(slave)) {
-				r.push(`${domName} only uses ${subName} sexually <span class="green">out of respect for ${his} rest rules.</span>`);
+				r.push(`${domName} only uses ${subName} sexually <span class="health inc">out of respect for ${his} rest rules.</span>`);
 			} else if (slave.relationshipTarget === domSlave.ID && slave.health.tired > 60) {
 				r.push(`${domName} goes easy on ${his2} ${relationshipTerm(domSlave)} ${subName} outside of the bedroom.`);
 			} else if (slave.health.tired + 10 >= 90 && !willWorkToDeath(slave)) {
@@ -1735,13 +1738,13 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 				} else if (slave.devotion > 20) {
 					r.push(`${subName} understands that working with ${domName} makes things easier.`);
 				} else {
-					r.push(`${subName} fails to realize that working with, not against, ${domName} <span class="red">would make things go smoother.</span>`);
+					r.push(`${subName} fails to realize that working with, not against, ${domName} <span class="health dec">would make things go smoother.</span>`);
 				}
 				if (domSlave.health.condition < 40) {
-					r.push(`<span class="green">${domName}'s health improves</span> with ${subName} to serve ${him2} and`);
+					r.push(`<span class="health inc">${domName}'s health improves</span> with ${subName} to serve ${him2} and`);
 					improveCondition(domSlave, 10);
 				} else {
-					r.push(`<span class="green">${domName} spends more time at ease</span> with ${subName} to`);
+					r.push(`<span class="health inc">${domName} spends more time at ease</span> with ${subName} to`);
 				}
 				r.push(`help with some of ${his2} harder duties.`);
 				domSlave.health.tired -= 10;
diff --git a/src/endWeek/saSharedVariables.js b/src/endWeek/saSharedVariables.js
index 8fc356b65590ba174514ca70dc37f606a8d39e32..81608508ffef26ba0ac0ef0768900c84b5ac28bf 100644
--- a/src/endWeek/saSharedVariables.js
+++ b/src/endWeek/saSharedVariables.js
@@ -34,6 +34,8 @@ App.EndWeek.SASharedVariables = class {
 		 * @type {Map<number, Array<number>>} - key is sub target, value is list of sub slaves (by ID) assigned to that target
 		 */
 		this.subSlaveMap = new Map();
+		/** Slave art manager */
+		this.slaveArt = null;
 	}
 
 	/**
diff --git a/src/endWeek/saSmartPiercingEffects.js b/src/endWeek/saSmartPiercingEffects.js
index f7b70c7c900030e36e2ebca11cb21e295c1369cb..7b13bbba49b4293714d45b25fa80c3eee48c523c 100644
--- a/src/endWeek/saSmartPiercingEffects.js
+++ b/src/endWeek/saSmartPiercingEffects.js
@@ -18,7 +18,7 @@ App.SlaveAssignment.SmartPiercing.BASE = class {
 
 	/** Return text for slave effect for this setting.
 	 * @param {boolean} plural was more than one device used.
-	 * @returns {string} predicate phrase for a sentence describing the results.  note that the subject is provided.
+	 * @returns {string} predicate phrase for a sentence describing the results. note that the subject is provided.
 	 */
 	text(plural) {
 		return `<span class="error">ABSTRACT</span>`;
@@ -31,10 +31,10 @@ App.SlaveAssignment.SmartPiercing.BASE = class {
 		return true;
 	}
 
-	/** Activate effect and return text for a slave.  Typically should be inherited without changes.
+	/** Activate effect and return text for a slave. Typically should be inherited without changes.
 	 * @param {number} magnitude 1: vibe/piercing, 2: smart vibe, or vibe+piercing, 3: smart vibe+piercing
 	 * @param {boolean} plural was more than one device used.
-	 * @returns {string} predicate phrase for a sentence describing the results.  note that the subject is provided.
+	 * @returns {string} predicate phrase for a sentence describing the results. note that the subject is provided.
 	 */
 	trigger(magnitude, plural) {
 		if (this.valid()) {
@@ -54,7 +54,7 @@ App.SlaveAssignment.SmartPiercing.none = class extends App.SlaveAssignment.Smart
 
 	text(plural) {
 		const {his, him} = getPronouns(this.slave);
-		return `${plural ? "disrupt" : "disrupts"} arousal, <span class="red">reducing ${his} sex drive</span> and <span class="devotion dec">infuriating ${him}.</span>`;
+		return `${plural ? "disrupt" : "disrupts"} arousal, <span class="libido dec">reducing ${his} sex drive</span> and <span class="devotion dec">infuriating ${him}.</span>`;
 	}
 };
 
@@ -69,7 +69,7 @@ App.SlaveAssignment.SmartPiercing.all = class extends App.SlaveAssignment.SmartP
 
 	text(plural) {
 		const {his} = getPronouns(this.slave);
-		return `${plural ? "encourage" : "encourages"} sex of all kinds, <span class="improvement">increasing ${his} sex drive.</span>`;
+		return `${plural ? "encourage" : "encourages"} sex of all kinds, <span class="libido inc">increasing ${his} sex drive.</span>`;
 	}
 };
 
@@ -111,12 +111,12 @@ App.SlaveAssignment.SmartPiercing.GENDERBASE = class extends App.SlaveAssignment
 			const {his} = getPronouns(this.slave);
 			if (this.positive) {
 				if (this.slave.energy < 80) {
-					ret += ` This has the secondary effect of slightly <span class="improvement">enhancing ${his} libido.</span>`;
+					ret += ` This has the secondary effect of slightly <span class="libido inc">enhancing ${his} libido.</span>`;
 					this.slave.energy += (magnitude === 1 ? 1 : 2);
 				}
 			} else {
 				if (this.slave.energy > 0) {
-					ret += ` This has the secondary effect of slightly <span class="red">reducing ${his} libido.</span>`;
+					ret += ` This has the secondary effect of slightly <span class="libido dec">reducing ${his} libido.</span>`;
 					this.slave.energy -= (magnitude === 1 ? 1 : 2);
 				}
 			}
@@ -186,7 +186,7 @@ App.SlaveAssignment.SmartPiercing.FETISHBASE = class extends App.SlaveAssignment
 	/** Return text for slave effect for this fetish setting.
 	 * @param {boolean} plural was more than one device used.
 	 * @param {string} which text type to return - "decrease" (of opposing fetish), "change" (to this fetish), or "increase" (of this fetish)
-	 * @returns {string} predicate phrase for a sentence describing the results.  note that the subject is provided.
+	 * @returns {string} predicate phrase for a sentence describing the results. note that the subject is provided.
 	 */
 	fetishText(plural, which) {
 		return `<span class="error">ABSTRACT</span>`;
@@ -331,7 +331,7 @@ App.SlaveAssignment.SmartPiercing.pregnancy = class extends App.SlaveAssignment.
 		if (which === "decrease") {
 			return `${plural ? "act" : "acts"} to <span class="fetish loss">suppress ${his} current fetish,</span> encouraging ${him} to orgasm when ${he} feels like ${he}'s being bred.`;
 		} else if (which === "change") {
-			let activities = ''; // FIXME: no text for null PCs.  Is that a thing?
+			let activities = ''; // FIXME: no text for null PCs. Is that a thing?
 			if (V.PC.dick !== 0) {
 				activities = "unprotected sex";
 				if (V.PC.vagina !== -1) {
@@ -491,10 +491,10 @@ App.SlaveAssignment.saSmartPiercingEffects = function(slave) {
 			slave.fetishStrength -= Math.min(15, slave.fetishStrength);
 		}
 		if (slave.energy <= 40) {
-			effects.push(`<span class="improvement">increases</span> ${his} below-average sex drive`);
+			effects.push(`<span class="libido inc">increases</span> ${his} below-average sex drive`);
 			slave.energy += 3;
 		} else if (slave.energy >= 60) {
-			effects.push(`<span class="stat drop">reduces</span> ${his} overcharged libido`);
+			effects.push(`<span class="libido dec">reduces</span> ${his} overcharged libido`);
 			slave.energy -= 3;
 		}
 		return effects.length > 0 ? intro + arrayToSentence(effects) + "." : "";
diff --git a/src/endWeek/saSocialEffects.js b/src/endWeek/saSocialEffects.js
index 443086463102615886df3ea4f254d2531ea9ee24..47fbf492b0595681891f8d8c10279b22b3e1a2a1 100644
--- a/src/endWeek/saSocialEffects.js
+++ b/src/endWeek/saSocialEffects.js
@@ -6,7 +6,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 	const {His, his, him, he, girl, wife} = getPronouns(slave);
 
 	/** Build a social effect object
-	 * @param {string} FS
+	 * @param {FC.FutureSocietyDeco} FS
 	 * @param {number} magnitude positive or negative value (a small integer, or a fraction between -1 and 1)
 	 * @param {string} shortDesc for compact/list mode (text string)
 	 * @param {string} longDesc for expanded mode (HTML string)
@@ -111,84 +111,84 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 
 		if (V.arcologies[0].FSYouthPreferentialist !== "unset") {
 			if ((slave.geneMods.NCS > 0) && (slave.visualAge <= 18)) {
-				t.push(new SocialEffect("YouthPreferentialist", 2, "NCS Youthening",
+				t.push(new SocialEffect("Youth Preferentialist", 2, "NCS Youthening",
 					`Society <span class="green">strongly approves</span> of you keeping ${slave.slaveName} forever young and youthening; this furthers the fashion for young slaves.`));
 			} else if (slave.visualAge < 30) {
 				const youthRepChange = ((30-slave.visualAge)/5);
-				t.push(new SocialEffect("YouthPreferentialist", youthRepChange, "Young-looking slave",
+				t.push(new SocialEffect("Youth Preferentialist", youthRepChange, "Young-looking slave",
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s youthful body; ${he} furthers the fashion for young slaves.`));
 			}
 		} else if (V.arcologies[0].FSMaturityPreferentialist !== "unset") {
 			if (slave.visualAge >= 30) {
 				const matureRepChange = ((slave.visualAge-25)/5);
-				t.push(new SocialEffect("MaturityPreferentialist", matureRepChange, "Mature-looking slave",
+				t.push(new SocialEffect("Maturity Preferentialist", matureRepChange, "Mature-looking slave",
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s mature body; ${he} furthers the fashion for older ladies.`));
 			}
 		}
 
 		if (V.arcologies[0].FSPetiteAdmiration !== "unset") {
 			if (heightPass(slave)) {
-				t.push(new SocialEffect("PetiteAdmiration", 1, "Short slave",
+				t.push(new SocialEffect("Petite Admiration", 1, "Short slave",
 					`Society <span class="green">approves</span> of keeping a slave as short as ${slave.slaveName} is; ${he} furthers the fashion for shorter slaves.`));
 			} else if (slave.height >= 170) {
-				t.push(new SocialEffect("PetiteAdmiration", -1, "Tall slave",
+				t.push(new SocialEffect("Petite Admiration", -1, "Tall slave",
 					`Society <span class="red">frowns</span> at keeping a slave as tall as ${slave.slaveName}; ${he} hinders the fashion for shorter slaves.`));
 			}
 		} else if (V.arcologies[0].FSStatuesqueGlorification !== "unset") {
 			if (heightPass(slave)) {
-				t.push(new SocialEffect("StatuesqueGlorification", 1, "Tall slave",
+				t.push(new SocialEffect("Statuesque Glorification", 1, "Tall slave",
 					`Society <span class="green">approves</span> of keeping a slave as tall as ${slave.slaveName} is; ${he} furthers the fashion for taller slaves.`));
 			} else {
-				t.push(new SocialEffect("StatuesqueGlorification", -1, "Short slave",
+				t.push(new SocialEffect("Statuesque Glorification", -1, "Short slave",
 					`Society <span class="red">frowns</span> at keeping someone as embarrassingly short as ${slave.slaveName}; ${he} hinders the fashion for taller slaves.`));
 			}
 		}
 
 		if (V.arcologies[0].FSGenderRadicalist !== "unset") {
 			 if ((slave.balls > 0) && (slave.pubertyXY === 0) && (slave.physicalAge >= V.potencyAge)) {
-				t.push(new SocialEffect("GenderRadicalist", 2, "Avoiding male puberty",
+				t.push(new SocialEffect("Gender Radicalist", 2, "Avoiding male puberty",
 					`Society <span class="green">approves</span> of you keeping ${slave.slaveName} from going through puberty; this advances public interest in ${girl}s with soft little dicks.`));
 			} else if ((slave.balls > 0) && (slave.dick > 0) && (slave.hormoneBalance >= 100)) {
-				t.push(new SocialEffect("GenderRadicalist", 1, "Feminization",
+				t.push(new SocialEffect("Gender Radicalist", 1, "Feminization",
 					`Society <span class="green">approves</span> of your heavy hormonal feminization of ${slave.slaveName}; this advances public interest in ${girl}s with soft little dicks.`));
 			} else if ((slave.dick > 0) && (slave.balls === 0)) {
-				t.push(new SocialEffect("GenderRadicalist", 1, "Gelded slave",
+				t.push(new SocialEffect("Gender Radicalist", 1, "Gelded slave",
 					`Society <span class="green">approves</span> of your keeping a gelded slave; this advances public interest in ${girl}s with soft dickclits.`));
 			} else if ((slave.dick > 0) && (slave.anus > 0) && (slave.devotion > 20) && (slave.trust >= -20)) {
-				t.push(new SocialEffect("GenderRadicalist", 1, "Contented dickgirl bottom",
+				t.push(new SocialEffect("Gender Radicalist", 1, "Contented dickgirl bottom",
 					`Society <span class="green">approves</span> of your keeping a contented dickgirl bottom; this advances public interest in ${girl}s who get hard when assfucked.`));
 			}
 		} else if (V.arcologies[0].FSGenderFundamentalist !== "unset") {
 			if (V.arcologies[0].FSRestart === "unset") {
 				if (slave.bellyPreg >= 1500) {
-					t.push(new SocialEffect("GenderFundamentalist", 1, "Pregnant",
+					t.push(new SocialEffect("Gender Fundamentalist", 1, "Pregnant",
 						`Society <span class="green">${V.arcologies[0].FSGenderFundamentalist >= V.FSLockinLevel*0.5 ? "strongly " : ""}approves</span> of your keeping a pregnant slave; this also supports the idea that slave women should bear babies.`));
 				} else if ((slave.preg === 0) && isFertile(slave)) {
-					t.push(new SocialEffect("GenderFundamentalist", 1, "Fertile",
+					t.push(new SocialEffect("Gender Fundamentalist", 1, "Fertile",
 						`Society <span class="green">${V.arcologies[0].FSGenderFundamentalist >= V.FSLockinLevel*0.5 ? "strongly " : ""}approves</span> of your keeping a slave fertile; this also supports the idea that slave women should bear babies.`));
 				}
 			} else {
 				if (slave.hips > slave.shoulders) {
-					t.push(new SocialEffect("GenderFundamentalist", 1, "Feminine figure",
+					t.push(new SocialEffect("Gender Fundamentalist", 1, "Feminine figure",
 						`Society <span class="green">approves</span> of keeping a slave with a feminine figure.`));
 				}
 			}
 			if ((slave.intelligenceImplant > 15 || slave.intelligenceImplant < 0) && V.arcologies[0].FSGenderFundamentalistLawBimbo === 1) {
-				t.push(new SocialEffect("GenderFundamentalist", -1, "Educated woman",
+				t.push(new SocialEffect("Gender Fundamentalist", -1, "Educated woman",
 					`Society <span class="red">disapproves</span> of encouraging a woman's education.`));
 			}
 			if ((slave.devotion <= 95) && canPenetrate(slave)) {
-				t.push(new SocialEffect("GenderFundamentalist", -1, "Can penetrate, not worshipful",
+				t.push(new SocialEffect("Gender Fundamentalist", -1, "Can penetrate, not worshipful",
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s stiff, unrestrained dick, since ${he} isn't even worshipful of you.`));
 			}
 		}
 
 		function repopRacialPregnancy() {
 			if (V.arcologies[0].FSSubjugationist !== "unset" && (slave.race === V.arcologies[0].FSSubjugationistRace)) {
-				t.push(new SocialEffect("RepopulationFocus", 0, "Undesirable race (Subjugationist)",
+				t.push(new SocialEffect("Repopulationist", 0, "Undesirable race (Subjugationist)",
 					`They just wish it wasn't ${V.arcologies[0].FSSubjugationistRace}, of course.`));
 			} else if (V.arcologies[0].FSSupremacist !== "unset" && (slave.race === V.arcologies[0].FSSupremacistRace)) {
-				t.push(new SocialEffect("RepopulationFocus", 0, "Desirable race (Supremacist)",
+				t.push(new SocialEffect("Repopulationist", 0, "Desirable race (Supremacist)",
 					`The fact that ${he} is ${V.arcologies[0].FSSupremacistRace} only makes it better.`));
 			}
 		}
@@ -196,13 +196,13 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 		if (V.arcologies[0].FSRepopulationFocus !== "unset") {
 			if (slave.preg > slave.pregData.normalBirth/1.33) {
 				if (slave.pregType >= 20) {
-					t.push(new SocialEffect("RepopulationFocus", 5, "Broodmother",
+					t.push(new SocialEffect("Repopulationist", 5, "Broodmother",
 						`Society is <span class="green">very pleased</span> at ${slave.slaveName}'s dedication to pregnancy.`));
 				} else if (slave.pregType >= 10) {
-					t.push(new SocialEffect("RepopulationFocus", 3, "Large multiple pregnancy",
+					t.push(new SocialEffect("Repopulationist", 3, "Large multiple pregnancy",
 						`Society is <span class="green">pleased</span> by ${slave.slaveName}'s abnormally large pregnancy.`));
 				} else {
-					t.push(new SocialEffect("RepopulationFocus", 2, "Advanced pregnancy",
+					t.push(new SocialEffect("Repopulationist", 2, "Advanced pregnancy",
 						`Society is <span class="green">pleased</span> by ${slave.slaveName}'s advanced pregnancy.`));
 				}
 				repopRacialPregnancy();
@@ -211,19 +211,19 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 				r.push(`${slave.slaveName} is so fat, society just assumes there is a baby somewhere in there, though they wish it was more obvious.`);
 				if (slave.pregWeek < 0) {
 					r.push(`But fortunately for ${him}, word of ${his} recent birth has gotten around <span class="green">reassuring the masses</span> that ${he} can still bear children.`);
-					t.push(new SocialEffect("RepopulationFocus", 2, "Fat recent mother", r.join(" ")));
+					t.push(new SocialEffect("Repopulationist", 2, "Fat recent mother", r.join(" ")));
 				} else if (slave.collar === "preg biometrics") {
 					if (slave.preg > 0) {
 						r.push(`<span class="green">Their wish is granted</span> by ${slave.slaveName}'s collar revealing ${his} womb's secret${slave.pregType > 1 ? 's' : ''} even when ${his} body is trying its best to keep ${slave.pregType > 1 ? "them" : "it"} hidden.`);
-						t.push(new SocialEffect("RepopulationFocus", 2, "Fat pregnancy", r.join(" ")));
+						t.push(new SocialEffect("Repopulationist", 2, "Fat pregnancy", r.join(" ")));
 						repopRacialPregnancy();
 					} else if (slave.preg <= 0) {
 						r.push(`<span class="red">The illusion is shattered</span> by ${slave.slaveName}'s collar revealing ${his} vacant womb.`);
-						t.push(new SocialEffect("RepopulationFocus", -2, "Fat but not pregnant", r.join(" ")));
+						t.push(new SocialEffect("Repopulationist", -2, "Fat but not pregnant", r.join(" ")));
 					}
 				}
 			} else if (slave.bellyPreg >= 1500) {
-				t.push(new SocialEffect("RepopulationFocus", 1, "Obviously pregnant",
+				t.push(new SocialEffect("Repopulationist", 1, "Obviously pregnant",
 					`Society is <span class="green">pleased</span> by ${slave.slaveName}'s pregnancy.`));
 				repopRacialPregnancy();
 			} else if (V.arcologies[0].FSSubjugationist !== "unset" && (slave.race === V.arcologies[0].FSSubjugationistRace) && slave.bellyImplant >= 1500) {
@@ -233,34 +233,34 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 				t.push(new SocialEffect("", 0.1, "Belly implant for infertile slave (Repopulation Focus)",
 					`Society <span class="green">accepts</span> ${slave.slaveName}'s implant filled belly due to ${his} infertility.`));
 			} else if (slave.collar === "preg biometrics" && slave.preg > 0) {
-				t.push(new SocialEffect("RepopulationFocus", 1, "Pregnancy biometrics",
+				t.push(new SocialEffect("Repopulationist", 1, "Pregnancy biometrics",
 					`Society is <span class="green">pleased</span> by ${slave.slaveName}'s collar revealing ${his} womb's secret${slave.pregType > 1 ? 's' : ''} even when ${his} body is trying its best to keep ${slave.pregType > 1 ? "them" : "it"} hidden.`));
 				repopRacialPregnancy();
 			} else if (slave.pregWeek < 0) {
-				t.push(new SocialEffect("RepopulationFocus", 2, "Recently gave birth",
+				t.push(new SocialEffect("Repopulationist", 2, "Recently gave birth",
 					`Society is <span class="green">pleased</span> by ${slave.slaveName}'s recent birth.`));
 			} else if (setup.fakeBellies.includes(slave.bellyAccessory)) {
 				t.push(new SocialEffect("", 0.01, "Fake belly (Repopulation Focus)",
 					`Society is <span class="green">placated</span> by ${slave.slaveName}'s fake belly.`));
 			} else if (V.arcologies[0].FSSubjugationist !== "unset" && (slave.race === V.arcologies[0].FSSubjugationistRace)) {
-				t.push(new SocialEffect("RepopulationFocus", 0, "Not pregnant but inferior race (Subjugationist)",
+				t.push(new SocialEffect("Repopulationist", 0, "Not pregnant but inferior race (Subjugationist)",
 					`Society is perfectly fine with ${slave.slaveName} not reproducing. ${His} belly is still unattractively small, however.`));
 			} else if ((slave.ovaries === 0 && slave.mpreg === 0) || (slave.preg < -1) || (slave.pubertyXX === 0)) {
-				t.push(new SocialEffect("RepopulationFocus", -1, "Unable to become pregnant",
+				t.push(new SocialEffect("Repopulationist", -1, "Unable to become pregnant",
 					`Society is <span class="red">mildly disappointed</span> that ${slave.slaveName} is unable to become pregnant.`));
 			} else if (slave.preg === -1) {
-				t.push(new SocialEffect("RepopulationFocus", -2, "On contraceptives",
+				t.push(new SocialEffect("Repopulationist", -2, "On contraceptives",
 					`Society is <span class="red">disapproving</span> of ${slave.slaveName}'s contraceptive regimen.`));
 			} else {
-				t.push(new SocialEffect("RepopulationFocus", -2, "Not visibly pregnant",
+				t.push(new SocialEffect("Repopulationist", -2, "Not visibly pregnant",
 					`Society is <span class="red">disapproving</span> of ${slave.slaveName}'s lack of a baby bump.`));
 			}
 			if (slave.abortionTat > 0) {
-				t.push(new SocialEffect("RepopulationFocus", -1, "Abortion tattoos",
+				t.push(new SocialEffect("Repopulationist", -1, "Abortion tattoos",
 					`Society <span class="red">is disgusted</span> by the tally of aborted children adorning ${his} skin.`));
 			}
 			if (slave.birthsTat > 0) {
-				t.push(new SocialEffect("RepopulationFocus", 1, "Birth tattoos",
+				t.push(new SocialEffect("Repopulationist", 1, "Birth tattoos",
 					`Society <span class="green">is pleased</span> by the tally of successful births adorning ${his} skin.`));
 			}
 		} else if (V.arcologies[0].FSRepopulationFocusPregPolicy === 1) {
@@ -422,7 +422,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 			}
 		} else if (V.arcologies[0].FSDegradationist !== "unset") {
 			if (slave.fetish === "mindbroken") {
-				t.push(new SocialEffect("Degradationist", -1, `Mindbroken`,
+				t.push(new SocialEffect("Degradationist", 1, `Mindbroken`,
 					`Society <span class="green">approves</span> of ${his} broken spirit; ${he} serves as an example of a soulless fuckpuppet.`));
 			} else {
 				if (slave.trust <= 4) {
@@ -506,123 +506,123 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 		if (V.arcologies[0].FSIntellectualDependency !== "unset") {
 			if (V.arcologies[0].FSIntellectualDependencyLawBeauty === 1) {
 				if (bimboScore(slave) === 6) {
-					t.push(new SocialEffect("IntellectualDependency", 1, `Perfect bimbo`,
+					t.push(new SocialEffect("Intellectual Dependency", 1, `Perfect bimbo`,
 						`Society <span class="green">is delighted</span> by ${slave.slaveName}'s perfect bimbo appearance.`));
 				}
 			}
 			if (slave.intelligence+slave.intelligenceImplant < -10) {
-				t.push(new SocialEffect("IntellectualDependency", 1, `Dimwitted`,
+				t.push(new SocialEffect("Intellectual Dependency", 1, `Dimwitted`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s dimwitted mind; this supports the idea that slaves should be entirely dependent on their owner.`));
 			} else if ([Job.HEADGIRL, Job.ATTENDANT, Job.FARMER, Job.MADAM, Job.MATRON, Job.NURSE, Job.TEACHER, Job.STEWARD, Job.BODYGUARD].includes(slave.assignment)) {
-				t.push(new SocialEffect("IntellectualDependency", 0, `Intelligence required by job`,
+				t.push(new SocialEffect("Intellectual Dependency", 0, `Intelligence required by job`,
 					`Society understands the value of intelligence in ${his} appointed position and is willing to overlook it.`));
 			} else if ((slave.intelligence+slave.intelligenceImplant > 10)) {
-				t.push(new SocialEffect("IntellectualDependency", -1, `Too smart`,
+				t.push(new SocialEffect("Intellectual Dependency", -1, `Too smart`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s sharp mind; this holds back acceptance of the idea that slaves should be dumb and dependent.`));
 			}
 			if (slave.energy > 95) {
-				t.push(new SocialEffect("IntellectualDependency", 1, `Nymphomania`,
+				t.push(new SocialEffect("Intellectual Dependency", 1, `Nymphomania`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s bottomless lust, showing the public one more way a slave may be reliant on ${his} owner.`));
 			} else if ((slave.energy <= 60)) {
-				t.push(new SocialEffect("IntellectualDependency", -1, `Low libido`,
+				t.push(new SocialEffect("Intellectual Dependency", -1, `Low libido`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s restrained libido; to the public, this gives ${him} too much freedom to focus on things other than sex.`));
 			}
 		} else if (V.arcologies[0].FSSlaveProfessionalism !== "unset") {
 			if (slave.intelligence+slave.intelligenceImplant > 95) {
-				t.push(new SocialEffect("SlaveProfessionalism", 1, `Brilliant`,
+				t.push(new SocialEffect("Slave Professionalism", 1, `Brilliant`,
 					`Society <span class="green">strongly approves</span> of ${slave.slaveName}'s brilliance; ${his} sharp wit is the foundation of slave perfectionism.`));
 			} else if (slave.intelligenceImplant >= 30) {
-				t.push(new SocialEffect("SlaveProfessionalism", 1, `Highly educated`,
+				t.push(new SocialEffect("Slave Professionalism", 1, `Highly educated`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s advanced education; this advances ideas about crafting the perfect slave.`));
 			} else if (slave.intelligenceImplant <= -15) {
-				t.push(new SocialEffect("SlaveProfessionalism", -3, `Miseducated`,
+				t.push(new SocialEffect("Slave Professionalism", -3, `Miseducated`,
 					`Society <span class="red">is appalled</span> by ${slave.slaveName}'s twisted education; why someone would do this is beyond them.`));
 			} else if (slave.intelligence < -10 && slave.intelligenceImplant < 15) {
-				t.push(new SocialEffect("SlaveProfessionalism", -2, `Slow and uneducated`,
+				t.push(new SocialEffect("Slave Professionalism", -2, `Slow and uneducated`,
 					`Society <span class="red">frowns</span> upon keeping a slave as slow as ${slave.slaveName}; the lack of an attempt to correct this sets a bad example for other owners.`));
 			}
 			if (slave.accent > 1 && canTalk(slave) && (slave.rules.speech !== "restrictive" || (slave.rules.speech === "restrictive" && slave.devotion < 20 && slave.trust >= -20))) {
-				t.push(new SocialEffect("SlaveProfessionalism", -2, `Can't speak ${V.language} or be silent`,
+				t.push(new SocialEffect("Slave Professionalism", -2, `Can't speak ${V.language} or be silent`,
 					`Society <span class="red">dislikes</span> ${slave.slaveName}'s inability to properly speak ${V.language} or hold ${his} tongue; allowing such a flaw hinders the notion of professional slavery.`));
 			}
 			if (slave.skill.entertainment + slave.skill.whoring + slave.skill.oral + slave.skill.anal + slave.skill.vaginal >= 400) {
-				t.push(new SocialEffect("SlaveProfessionalism", 1, `Highly skilled`,
+				t.push(new SocialEffect("Slave Professionalism", 1, `Highly skilled`,
 					`Society <span class="green">appreciates</span> a slave with skills of ${slave.slaveName}'s caliber.`));
 			}
 			if (slave.energy <= 40 && slave.devotion > 50) {
-				t.push(new SocialEffect("SlaveProfessionalism", 1, `Clearminded (low libido or devoted)`,
+				t.push(new SocialEffect("Slave Professionalism", 1, `Clearminded (low libido or devoted)`,
 					`Society <span class="green">approves</span> of a ${girl} with a clear mind like ${slave.slaveName}; ${he} can pour all ${his} efforts into ${his} lover's pleasure without being lost in ${his} own.`));
 			}
 		}
 
 		if (V.arcologies[0].FSBodyPurist !== "unset") {
 			if ((slave.boobsImplant === 0) && (slave.buttImplant === 0) && (slave.lipsImplant === 0) && (slave.bellyImplant === -1)) {
-				t.push(new SocialEffect("BodyPurist", 1, `No implants`,
+				t.push(new SocialEffect("Body Purist", 1, `No implants`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s natural body; this supports the fashion for surgically untouched slaves.`));
 			} else {
-				t.push(new SocialEffect("BodyPurist", -1, `Implants`,
+				t.push(new SocialEffect("Body Purist", -1, `Implants`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s implants; this holds back acceptance of the idea that slaves should be all-natural.`));
 			}
 			if (slave.corsetPiercing === 0 && modScore.piercing < 3 && modScore.tat < 2) {
-				t.push(new SocialEffect("BodyPurist", 1, `Minimal piercings/tats`,
+				t.push(new SocialEffect("Body Purist", 1, `Minimal piercings/tats`,
 					`Society <span class="green">approves</span> of ${his} unmarked, unblemished skin, advancing the fashion for unspoiled slaves.`));
 			}
 			if ((slave.earShape !== "normal" && slave.earShape !== "damaged") || slave.earT !== "none") {
-				t.push(new SocialEffect("BodyPurist", -1, `Inhuman ears`,
+				t.push(new SocialEffect("Body Purist", -1, `Inhuman ears`,
 					`Society finds ${his} inhuman ears <span class="red">appalling.</span>`));
 			}
 			if (slave.horn !== "none" || slave.tail !== "none") {
-				t.push(new SocialEffect("BodyPurist", -1, `Inhuman tail or horns`,
+				t.push(new SocialEffect("Body Purist", -1, `Inhuman tail or horns`,
 					`Society <span class="red">is disgusted</span> by ${his} inhuman attributes.`));
 			}
 			if (slave.faceImplant <= 5 && slave.race === slave.origRace) {
-				t.push(new SocialEffect("BodyPurist", 1, `Unaltered`,
+				t.push(new SocialEffect("Body Purist", 1, `Unaltered`,
 					`Society <span class="green">approves</span> of ${his} natural, untouched appearance, advancing the fashion for unaltered slaves.`));
 			} else {
-				t.push(new SocialEffect("BodyPurist", -2, `Surgically altered`,
+				t.push(new SocialEffect("Body Purist", -2, `Surgically altered`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s surgical beautification; this holds back acceptance of the idea that a natural slave is a beautiful slave.`));
 			}
 			if (slave.fuckdoll === 0) {
 				if (slave.vagina === 0 && slave.anus === 0 && slave.counter.vaginal === 0 && slave.counter.anal === 0) {
-					t.push(new SocialEffect("BodyPurist", 3, `Total virgin`,
+					t.push(new SocialEffect("Body Purist", 3, `Total virgin`,
 						`Society <span class="green">strongly approves</span> of ${his} intact holes, idolizing ${his} pristine body.`));
 				} else if (slave.vagina === 0 && slave.counter.vaginal === 0) {
-					t.push(new SocialEffect("BodyPurist", 1, `Vaginal virgin`,
+					t.push(new SocialEffect("Body Purist", 1, `Vaginal virgin`,
 						`Society <span class="green">approves</span> of ${his} intact hymen, advancing ideals of pure, fresh slaves.`));
 				} else if (slave.anus === 0 && slave.counter.anal === 0) {
-					t.push(new SocialEffect("BodyPurist", 1, `Anal virgin`,
+					t.push(new SocialEffect("Body Purist", 1, `Anal virgin`,
 						`Society <span class="green">approves</span> of ${his} intact anus, advancing ideals of pure, fresh slaves.`));
 				}
 			}
 		} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
 			let transformed = 0;
 			if ((slave.boobsImplant > 0) && (slave.buttImplant > 0) && (slave.lipsImplant > 0)) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Implants`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Implants`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s thoroughly modified body; this supports the fashion for surgically upgraded slaves.`));
 				transformed++;
 			}
 			if ((slave.lipsImplant/slave.lips >= .50) || (slave.buttImplant/slave.butt >= .50 && slave.butt >= 6) || (slave.buttImplant/slave.butt >= .25 && slave.butt >= 3) || (slave.boobsImplant/slave.boobs >= .75 && slave.boobs >= 10000) || (slave.boobsImplant/slave.boobs >= .50 && slave.boobs >= 2000) || (slave.boobsImplant/slave.boobs >= .25 && slave.boobs >= 1000) || (slave.boobsImplant/slave.boobs >= .10 && slave.boobs >= 400)) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Big implants`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Big implants`,
 					`Society <span class="green">approves</span> of ${his} obvious implants.`));
 				transformed++;
 			}
 			if (slave.bellyImplant >= 1500) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Big belly implant`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Big belly implant`,
 					`Society <span class="green">mildly approves</span> of ${slave.slaveName}'s belly bulging implant; this supports interest in more unusual implantations.`));
 				transformed++;
 			}
 			if (isAmputee(slave) || (slave.waist < -95) || (slave.teeth === "pointy") || (slave.teeth === "fangs") || (slave.teeth === "removable") || (slave.hips === 3 && slave.hipsImplant > 0)) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Extreme surgery`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Extreme surgery`,
 					`Society <span class="green">approves</span> of ${his} extreme surgeries; interest in ${him} stirs interest in transformations of all kinds.`));
 				transformed++;
 			}
 			if (slave.faceImplant > 30 || slave.race !== slave.origRace) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Surgically improved`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Surgically improved`,
 					`Society <span class="green">approves</span> of ${his} surgically improved appearance; this supports the fashion for surgical corrections.`));
 				transformed++;
 			}
 			if (slave.faceImplant > 95 && slave.face > 40) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Uncannily beautiful face`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Uncannily beautiful face`,
 					`Society <span class="green">approves</span> of ${his} beautiful face, considering it's uncanny nature a boon rather than a fault; this supports the belief that there is no such thing as too much surgery.`));
 				transformed++;
 			}
@@ -640,42 +640,42 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 				addons.push("tail");
 			}
 			if (addons.length > 0) {
-				t.push(new SocialEffect("TransformationFetishist", Math.min(addons.length, 2), `Transhuman addons`,
+				t.push(new SocialEffect("Transformation Fetishist", Math.min(addons.length, 2), `Transhuman addons`,
 					`Society <span class="green">strongly approves</span> of ${his} transhuman ${arrayToSentence(addons)}.`));
 				transformed += addons.length;
 			}
 			if (slave.dick > 8) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Giant dick`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Giant dick`,
 					`Society <span class="green">approves</span> of ${his} monolithic dick, since it's such an obvious transformation masterpiece.`));
 				transformed++;
 			}
 			if (slave.lips > 95) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Facepussy`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Facepussy`,
 					`Society <span class="green">approves</span> of ${his} absurd facepussy as a transformation of ${his} mouth into nothing more than another fuckhole.`));
 				transformed++;
 			}
 			if (slave.nipples === "fuckable") {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Fuckable nipples`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Fuckable nipples`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s fuckable nipples; this supports interest in more unusual body modifications.`));
 				transformed++;
 			}
 			if (slave.fuckdoll > 0) {
-				t.push(new SocialEffect("TransformationFetishist", 1, `Fuckdoll`,
+				t.push(new SocialEffect("Transformation Fetishist", 1, `Fuckdoll`,
 					`Society <span class="green">approves</span> of your owning a Fuckdoll.`));
 				transformed += 5; // total transformation
 			}
 			if (transformed === 0) {
-				t.push(new SocialEffect("TransformationFetishist", -2, `Totally unmodified`,
+				t.push(new SocialEffect("Transformation Fetishist", -2, `Totally unmodified`,
 					`Society <span class="red">strongly disapproves</span> of ${slave.slaveName}'s complete lack of any obvious transformations; ${he} does not advance the ideal of body modification.`));
 			} else if (transformed === 1) {
-				t.push(new SocialEffect("TransformationFetishist", -1, `Too few modifications`,
+				t.push(new SocialEffect("Transformation Fetishist", -1, `Too few modifications`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s mostly-natural appearance; more transformations would help advance the ideal of body modification.`));
 			}
 		}
 
 		if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
 			if (isSlim(slave)) {
-				t.push(new SocialEffect("SlimnessEnthusiast", 1, `Slim`,
+				t.push(new SocialEffect("Slimness Enthusiast", 1, `Slim`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s graceful, girlish form; ${he} furthers the fashion for slaves like ${him}.`));
 			}
 			let unslim = [];
@@ -686,47 +686,47 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 				unslim.push("ass");
 			}
 			if (unslim.length > 0) {
-				t.push(new SocialEffect("SlimnessEnthusiast", -1, `Big tits/ass`,
+				t.push(new SocialEffect("Slimness Enthusiast", -1, `Big tits/ass`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s boorishly large ${arrayToSentence(unslim)}; ${he} damages the fashion for slender slaves.`));
 			}
 		} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
 			let assets = 0;
 			if (slave.geneMods.NCS > 0) {
-				t.push(new SocialEffect("AssetExpansionist", -2, `NCS Youthening`,
+				t.push(new SocialEffect("Asset Expansionist", -2, `NCS Youthening`,
 					`Society <span class="green">strongly disapproves </span> of ${slave.slaveName} who can't get bigger; ${his} shrinking body hurts the fashion for Asset expansion.`));
 				assets++;
 			}
 			if (slave.boobs > 2000) {
-				t.push(new SocialEffect("AssetExpansionist", 1, `Titanic tits`,
+				t.push(new SocialEffect("Asset Expansionist", 1, `Titanic tits`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s huge tits; ${his} breasts further the fashion for bouncing boobs on slaves.`));
 				assets++;
 			}
 			if (slave.butt > 7) {
-				t.push(new SocialEffect("AssetExpansionist", 1, `Big butt`,
+				t.push(new SocialEffect("Asset Expansionist", 1, `Big butt`,
 					`Society <span class="green">approves</span> of ${his} massive ass; ${his} butt furthers the fashion for big behinds on slaves.`));
 				assets++;
 			}
 			if (slave.dick > 8) {
-				t.push(new SocialEffect("AssetExpansionist", Math.ceil(slave.dick/10), `Massive member`,
+				t.push(new SocialEffect("Asset Expansionist", Math.ceil(slave.dick/10), `Massive member`,
 					`Society <span class="green">approves</span> of ${his} massive member, which might be nonfunctional, but is a wonder of expansionism.`));
 				assets++;
 			} else if (slave.dick >= 6) {
-				t.push(new SocialEffect("AssetExpansionist", 1, `Prodigious penis`,
+				t.push(new SocialEffect("Asset Expansionist", 1, `Prodigious penis`,
 					`Society <span class="green">approves</span> of ${his} enormous penis; ${his} cock furthers the fashion for dangling dicks on slaves.`));
 				assets++;
 			}
 			if (slave.balls > 6) {
-				t.push(new SocialEffect("AssetExpansionist", 1, `Tremendous testicles`,
+				t.push(new SocialEffect("Asset Expansionist", 1, `Tremendous testicles`,
 					`Society <span class="green">approves</span> of ${his} swinging balls; ${his} nuts further the fashion for tremendous testicles on slaves.`));
 				assets++;
 			}
 			if (slave.lips > 95) {
-				t.push(new SocialEffect("AssetExpansionist", 1, `Luscious lips`,
+				t.push(new SocialEffect("Asset Expansionist", 1, `Luscious lips`,
 					`Society <span class="green">approves</span> of ${his} expanded lips.`));
 				assets++;
 			}
 			if (assets === 0) {
-				t.push(new SocialEffect("AssetExpansionist", -1, `Unexceptional assets`,
+				t.push(new SocialEffect("Asset Expansionist", -1, `Unexceptional assets`,
 					`Society <span class="red">is disappointed</span> by ${his} normal-sized (or even petite, by expansionist standards) assets.`));
 			}
 		}
@@ -745,37 +745,37 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 		if (V.arcologies[0].FSPhysicalIdealist !== "unset") {
 			if (V.arcologies[0].FSPhysicalIdealistLaw === 1) {
 				if (slave.muscles > 50) {
-					t.push(new SocialEffect("PhysicalIdealist", 1, `Overmuscled (Fit Feminine)`,
+					t.push(new SocialEffect("Physical Idealist", 1, `Overmuscled (Fit Feminine)`,
 						`Society <span class="green">approves</span> of ${slave.slaveName}'s commitment to fitness; but thinks ${his} muscles are too big and vascular.`));
 				} else if ((slave.muscles > 20)) {
-					t.push(new SocialEffect("PhysicalIdealist", 2, `Fit (Fit Feminine)`,
+					t.push(new SocialEffect("Physical Idealist", 2, `Fit (Fit Feminine)`,
 						`Society <span class="green">strongly approves</span> of ${slave.slaveName}'s fit body; physical enthusiasts see ${him} as the optimal example of feminine fitness.`));
 				} else if ((slave.muscles > 5)) {
-					t.push(new SocialEffect("PhysicalIdealist", 1, `Toned (Fit Feminine)`,
+					t.push(new SocialEffect("Physical Idealist", 1, `Toned (Fit Feminine)`,
 						`Society <span class="green">approves</span> of ${slave.slaveName}'s toned form; the public sees potential in those guns.`));
 				}
 			} else {
 				if (slave.muscles > 95) {
-					t.push(new SocialEffect("PhysicalIdealist", 2, `Bodybuilder`,
+					t.push(new SocialEffect("Physical Idealist", 2, `Bodybuilder`,
 						`Society <span class="green">strongly approves</span> of ${slave.slaveName}'s glorious muscles; everyone wants to train a slave to look as swole as ${him}.`));
 				} else if ((slave.muscles > 30)) {
-					t.push(new SocialEffect("PhysicalIdealist", 1, `Fit`,
+					t.push(new SocialEffect("Physical Idealist", 1, `Fit`,
 						`Society <span class="green">approves</span> of ${slave.slaveName}'s fit body; physical enthusiasts see ${him} as on ${his} way to something great.`));
 				} else if ((slave.muscles > 5)) {
-					t.push(new SocialEffect("PhysicalIdealist", 1, `Toned`,
+					t.push(new SocialEffect("Physical Idealist", 1, `Toned`,
 						`Society <span class="green">approves</span> of ${slave.slaveName}'s toned form; the public sees potential in those guns.`));
 				}
 			}
 			if (slave.muscles < -30) {
-				t.push(new SocialEffect("PhysicalIdealist", -1, `Frail`,
+				t.push(new SocialEffect("Physical Idealist", -1, `Frail`,
 					`Society <span class="red">disapproves</span> of ${slave.slaveName}'s frail state; ${he} needs to build some muscles if ${he}'s going to help the public idolize fitness.`));
 			}
 			if (slave.height >= 185) {
-				t.push(new SocialEffect("PhysicalIdealist", 1, `Tall`,
+				t.push(new SocialEffect("Physical Idealist", 1, `Tall`,
 					`Society <span class="green">approves</span> of how tall ${he} is; the sexual advantages of ${his} height are impressed on the public mind.`));
 			}
 			if (slave.health.condition > 80) {
-				t.push(new SocialEffect("PhysicalIdealist", 1, `Healthy`,
+				t.push(new SocialEffect("Physical Idealist", 1, `Healthy`,
 					`Society <span class="green">approves</span> of ${his} health; the expectation that slaves should be kept perfectly healthy grows.`));
 			}
 		} else if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
@@ -812,36 +812,36 @@ App.SlaveAssignment.saSocialEffects = function(slave) {
 
 		if (V.arcologies[0].FSChattelReligionist !== "unset") {
 			if (["cruel retirement counter", "tight steel", "uncomfortable leather"].includes(slave.collar)) {
-				t.push(new SocialEffect("ChattelReligionist", 1, `Punishing collar`,
+				t.push(new SocialEffect("Chattel Religionist", 1, `Punishing collar`,
 					`Society <span class="green">approves</span> of ${slave.slaveName}'s collar as an expression of the old ideal of mortification of the flesh, advancing the combination of religious originalism and modern slavery.`));
 			}
 			if (["a burqa", "a chattel habit", "a fallen nuns habit", "a hijab and abaya", "a niqab and abaya", "a penitent nuns habit", "a succubus outfit"].includes(slave.clothes)) {
-				t.push(new SocialEffect("ChattelReligionist", 1, `Religious clothing`,
+				t.push(new SocialEffect("Chattel Religionist", 1, `Religious clothing`,
 					`Society <span class="green">approves</span> of ${his} religiously themed clothing, strengthening the connection between sexual servitude and faith.`));
-			} else if (["a bunny outfit", "a hijab and blouse", "a huipil", "a kimono", "a military uniform", "a nice maid outfit", "a nice nurse outfit", "a skimpy loincloth", "a slutty qipao", "a toga", "body oil", "chains", "conservative clothing", "nice business attire", "no clothing", "shibari ropes", "uncomfortable straps"].includes(slave.clothes)) {
-				t.push(new SocialEffect("ChattelReligionist", 0, `Spartan clothing`,
+			} else if (["a bunny outfit", "a hijab and blouse", "a huipil", "a kimono", "a military uniform", "a nice maid outfit", "a nice nurse outfit", "a skimpy loincloth", "a slutty qipao", "a toga", "Imperial Plate", "a tight Imperial bodysuit", "body oil", "chains", "conservative clothing", "nice business attire", "no clothing", "shibari ropes", "uncomfortable straps"].includes(slave.clothes)) {
+				t.push(new SocialEffect("Chattel Religionist", 0, `Spartan clothing`,
 					`Society accepts ${his} spartan clothing, seeing it as permissible under the new religious mores.`));
 			} else if (slave.fuckdoll === 0) {
-				t.push(new SocialEffect("ChattelReligionist", -1, `Extravagant clothing`,
+				t.push(new SocialEffect("Chattel Religionist", -1, `Extravagant clothing`,
 					`Society <span class="red">disapproves</span> of ${his} unnecessarily gaudy attire; this slows acceptance of a link between faith and sexual servitude.`));
 			}
 			if (slave.devotion > 95) {
-				t.push(new SocialEffect("ChattelReligionist", 1, `Worshipful`,
+				t.push(new SocialEffect("Chattel Religionist", 1, `Worshipful`,
 					`Society <span class="green">approves</span> of ${his} devotion to you as a companion to religious devotion, seeing both as the model for holy slaves.`));
 			}
 			if (slave.trust > 95) {
-				t.push(new SocialEffect("ChattelReligionist", 2, `Completely trusting`,
+				t.push(new SocialEffect("Chattel Religionist", 2, `Completely trusting`,
 					`Society <span class="green">strongly approves</span> of ${his} faith in you as a companion to faith in God, seeing both as the model for righteous slaves of the future.`));
 			}
 			if (slave.behavioralFlaw === "devout") {
-				t.push(new SocialEffect("ChattelReligionist", 0, `Devout`,
+				t.push(new SocialEffect("Chattel Religionist", 0, `Devout`,
 					`Society does not disapprove of ${his} devout adherence to an old world faith, having confidence that you'll bring ${him} around, and looking forward to seeing ${him} converted into a holy sex object.`));
 			} else if ((slave.behavioralQuirk === "sinful")) {
-				t.push(new SocialEffect("ChattelReligionist", 2, `Sinful`,
+				t.push(new SocialEffect("Chattel Religionist", 2, `Sinful`,
 					`Society <span class="green">strongly approves</span> of ${his} eagerness to transgress against the old world religious mores ${he} once held dear.`));
 			}
 			if (slave.shouldersTat === "sacrilege" && slave.lipsTat === "sacrilege" && slave.boobsTat === "sacrilege" && slave.armsTat === "sacrilege" && slave.backTat === "sacrilege" && slave.stampTat === "sacrilege" && slave.buttTat === "sacrilege" && slave.vaginaTat === "sacrilege" && slave.dickTat === "sacrilege" && slave.anusTat === "sacrilege" && slave.legsTat === "sacrilege" && slave.fuckdoll === 0) {
-				t.push(new SocialEffect("ChattelReligionist", 1, `Sacrilegious tattoos`,
+				t.push(new SocialEffect("Chattel Religionist", 1, `Sacrilegious tattoos`,
 					`Society <span class="green">enjoys the sight</span> of the religious sacrilege scrawled across ${his} skin.`));
 			}
 		}
diff --git a/src/endWeek/saStayConfined.js b/src/endWeek/saStayConfined.js
index 5583751bbe840c6a8e8ed93addc7bef23423c0f7..eda744d2db9db9247912625bf90bd19cf06011c0 100644
--- a/src/endWeek/saStayConfined.js
+++ b/src/endWeek/saStayConfined.js
@@ -1,6 +1,12 @@
+/**
+ * @typedef {Object} confinedResults
+ * @property {string} text
+ * @property {Boolean} broken
+ */
+
 /**
  * @param {App.Entity.SlaveState} slave
- * @returns {string}
+ * @returns {confinedResults}
  */
 App.SlaveAssignment.stayConfined = function(slave) {
 	/* eslint-disable no-unused-vars*/
@@ -11,6 +17,7 @@ App.SlaveAssignment.stayConfined = function(slave) {
 	/* eslint-enable */
 
 	let t = "";
+	let brokenSlaves = false;
 
 	if (slave.fetish !== "mindbroken") {
 		if (slave.devotion < -50) {
@@ -46,7 +53,7 @@ App.SlaveAssignment.stayConfined = function(slave) {
 		}
 
 		if (slave.health.illness > 0) {
-			t += ` ${He} is<span class="red">`;
+			t += ` ${He} is<span class="health dec">`;
 			if (slave.health.illness === 1) {
 				t += ` feeling under the weather`;
 			} else if (slave.health.illness === 2) {
@@ -61,32 +68,32 @@ App.SlaveAssignment.stayConfined = function(slave) {
 			t += `,</span> so ${his} misery only grows.`;
 		}
 		if (slave.assignment === Job.CELLBLOCK && V.WardenessID !== 0) {
-			t += ` The stress of confinement <span class="red">damages ${his} health.</span> ${S.Wardeness.slaveName}`;
+			t += ` The stress of confinement <span class="health dec">damages ${his} health.</span> ${S.Wardeness.slaveName}`;
 			if (S.Wardeness.fetish === "mindbroken") {
 				if (slave.health.tired > 80) {
-					t += `'s empty mind often overlooks ${him} when ${he} falls inert from exhaustion, giving ${him} <span class="green">a much needed chance to rest.</span>`;
+					t += `'s empty mind often overlooks ${him} when ${he} falls inert from exhaustion, giving ${him} <span class="health inc">a much needed chance to rest.</span>`;
 				} else {
-					t += `'s empty mind is void of mercy; This broken Wardeness sees <span class="red">no need for breaks nor rest.</span>`;
+					t += `'s empty mind is void of mercy; This broken Wardeness sees <span class="health dec">no need for breaks nor rest.</span>`;
 				}
 			} else if (S.Wardeness.sexualQuirk === "caring") {
 				t += ` is too caring and has to focus on not coddling ${him}.`;
 			} else if (S.Wardeness.sexualFlaw === "malicious" || S.Wardeness.sexualFlaw === "abusive") {
 				if (slave.health.condition > 20) {
-					t += ` enjoys <span class="red">depriving ${him} of sleep and watching ${him} succumb to fatigue.</span>`;
+					t += ` enjoys <span class="health dec">depriving ${him} of sleep and watching ${him} succumb to fatigue.</span>`;
 				} else if (slave.health.tired > 80) {
 					t += ` knows better than to let ${him} become dangerously exhausted in ${his} condition.`;
 				} else {
-					t += ` enjoys <span class="red">depriving ${him} of sleep,</span> leaving ${him} vulnerable.`;
+					t += ` enjoys <span class="health dec">depriving ${him} of sleep,</span> leaving ${him} vulnerable.`;
 				}
 			} else if (slave.health.tired > 50) {
-				t += ` allots ${him} <span class="green">time to sleep</span> in an effort to prevent exhaustion from interrupting ${his} efforts.`;
+				t += ` allots ${him} <span class="health inc">time to sleep</span> in an effort to prevent exhaustion from interrupting ${his} efforts.`;
 			} else if (slave.health.tired <= 30) {
 				t += ` welcomes ${him} to ${V.cellblockName} by <span class="yellow">denying ${him} sleep</span> until ${he} is nice and pliable.`;
 			} else {
 				t += ` makes sure to <span class="yellow">deny ${him} sleep</span> to keep ${him} pliable.`;
 			}
 		} else {
-			t += ` The stress of confinement <span class="red">damages ${his} health`;
+			t += ` The stress of confinement <span class="health dec">damages ${his} health`;
 			if (slave.health.tired > 30) {
 				t += `,</span> but the isolation gives ${his} tired body a chance to rest.`;
 			} else {
@@ -128,7 +135,7 @@ App.SlaveAssignment.stayConfined = function(slave) {
 
 		t += ` <span class="noteworthy">`;
 		if (slave.assignment === Job.CELLBLOCK) {
-			State.temporary.brokenSlaves++;
+			brokenSlaves = true;
 		}
 		if (V.assignmentRecords[slave.ID]) {
 			let oldJob = V.assignmentRecords[slave.ID];
@@ -151,5 +158,5 @@ App.SlaveAssignment.stayConfined = function(slave) {
 
 		t += `</span>`;
 	}
-	return t;
+	return {text: t, broken: brokenSlaves};
 };
diff --git a/src/endWeek/saTakeClasses.js b/src/endWeek/saTakeClasses.js
index 5acfdff2aa255bbdb15f3a3e3cbbd37fcb44b161..22495e4211661c649b670f1e2588822547c4b593 100644
--- a/src/endWeek/saTakeClasses.js
+++ b/src/endWeek/saTakeClasses.js
@@ -67,7 +67,7 @@ App.SlaveAssignment.takeClasses = (function() {
 				if (S.Schoolteacher.visualAge > 35) {
 					teaching += 10;
 				}
-				if (setup.schoolteacherCareers.includes(S.Schoolteacher.career)) {
+				if (App.Data.Careers.Leader.schoolteacher.includes(S.Schoolteacher.career)) {
 					teaching += 10;
 				} else if (S.Schoolteacher.skill.teacher >= V.masteredXP) {
 					teaching += 10;
@@ -127,7 +127,7 @@ App.SlaveAssignment.takeClasses = (function() {
 	 */
 	function jobHealthImpact(slave) {
 		if (slave.health.illness > 0 || slave.health.tired > 90) {
-			r += ` ${He} performed worse this week due to<span class="red">`;
+			r += ` ${He} performed worse this week due to<span class="health dec">`;
 			if (slave.health.illness === 1) {
 				r += ` feeling under the weather`;
 				learning--;
diff --git a/src/endWeek/saWhore.js b/src/endWeek/saWhore.js
index 67f32fa4e960a8a0bf19419d577ab89ab7ed6e31..92a3aedbb6aed830c71ab0fa58d0256004b38667 100644
--- a/src/endWeek/saWhore.js
+++ b/src/endWeek/saWhore.js
@@ -206,7 +206,7 @@ App.SlaveAssignment.whore = (function() {
 							r += ` Unfortunately, ${his} inability to hear wastes most of ${madam.slaveName}'s advice.`;
 						}
 						if (V.MadamIgnoresFlaws !== 1) {
-							if (!["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "none", "self hating"].includes(slave.sexualFlaw) && jsRandom(1, 100) > 90) {
+							if (!App.Data.misc.paraphiliaList.includes(slave.sexualFlaw) && slave.sexualFlaw !== "none" && jsRandom(1, 100) > 90) {
 								r += ` ${SlaveFullName(madam)} manages to <span class="flaw break">break</span> ${slave.slaveName} of ${his} sexual flaws.`;
 								slave.sexualFlaw = "none";
 							} else if (slave.behavioralFlaw !== "none" && jsRandom(1, 100) > 90) {
@@ -323,7 +323,7 @@ App.SlaveAssignment.whore = (function() {
 		let injury = 0;
 
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			r += ` ${He} is<span class="red">`;
+			r += ` ${He} is<span class="health dec">`;
 			if (slave.health.illness === 1) {
 				r += ` feeling under the weather`;
 			} else if (slave.health.illness === 2) {
@@ -347,7 +347,7 @@ App.SlaveAssignment.whore = (function() {
 		}
 		if (slave.assignment === Job.BROTHEL) {
 			if (slaveResting(slave)) {
-				r += ` ${He} spends reduced hours working ${V.brothelName} in order to <span class="green">offset ${his} lack of rest.</span>`;
+				r += ` ${He} spends reduced hours working ${V.brothelName} in order to <span class="health inc">offset ${his} lack of rest.</span>`;
 			} else if (slave.health.tired + 15 >= 90 && !willWorkToDeath(slave)) {
 				r += ` ${He} attempts to refuse customers due to ${his} exhaustion, but can do little to stop it or the resulting `;
 				if (V.MadamID !== 0) {
@@ -359,12 +359,12 @@ App.SlaveAssignment.whore = (function() {
 				slave.devotion -= 10;
 				slave.trust -= 5;
 			} else {
-				r += ` It takes effort to be a productive whore and ${he} ends ${his} shifts <span class="red">eager for sleep.</span>`;
+				r += ` It takes effort to be a productive whore and ${he} ends ${his} shifts <span class="health dec">eager for sleep.</span>`;
 			}
 			tired(slave);
 		} else if (slave.assignment === Job.WHORE) {
 			if (slaveResting(slave)) {
-				r += ` ${He} spends reduced hours plying ${his} trade in order to <span class="green">offset ${his} lack of rest.</span>`;
+				r += ` ${He} spends reduced hours plying ${his} trade in order to <span class="health inc">offset ${his} lack of rest.</span>`;
 			} else if (slave.health.tired + 15 >= 90 && !willWorkToDeath(slave)) {
 				r += ` ${He} attempts to refuse to whore due to ${his} exhaustion, but can do little to avoid it or the resulting `;
 				if (V.brothel > 0 && V.universalRulesFacilityWork === 1 && V.brothelSpots > 0 && V.MadamID !== 0) {
@@ -376,7 +376,7 @@ App.SlaveAssignment.whore = (function() {
 				slave.devotion -= 10;
 				slave.trust -= 5;
 			} else {
-				r += ` It takes effort to be an effective prostitute and ${he} ends ${his} shifts <span class="red">eager for sleep.</span>`;
+				r += ` It takes effort to be an effective prostitute and ${he} ends ${his} shifts <span class="health dec">eager for sleep.</span>`;
 			}
 			tired(slave);
 		}
@@ -391,11 +391,11 @@ App.SlaveAssignment.whore = (function() {
 				}
 				r += ` protect ${him} from the wear of whoring.`;
 			} else if (slave.health.condition < -50) {
-				r += ` The stress of whoring while in terrible condition is <span class="red">very hard on ${him}.</span>`;
+				r += ` The stress of whoring while in terrible condition is <span class="health dec">very hard on ${him}.</span>`;
 				healthDamage(slave, 13);
 				injury = 1;
 			} else if (slave.health.condition < -20 && jsRandom(1, 100) > 50) {
-				r += ` The stress of whoring while in poor condition is <span class="red">hard on ${him}.</span>`;
+				r += ` The stress of whoring while in poor condition is <span class="health dec">hard on ${him}.</span>`;
 				healthDamage(slave, 10);
 				injury = 1;
 			} else {
@@ -413,25 +413,25 @@ App.SlaveAssignment.whore = (function() {
 					if (!injury) {
 						r += ` ${He}'s such an expert whore that ${he} copes with the stress of being a ${SlaveTitle(slave)} slut.`;
 					} else {
-						r += ` The stress of being a ${SlaveTitle(slave)} prostitute is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being a ${SlaveTitle(slave)} prostitute is <span class="health dec">hard on ${him}.</span>`;
 					}
 				} else if (!canV && canA) {
 					if (!injury) {
 						r += ` ${He}'s such an expert whore that ${he} copes with the stress of being limited to buttsex and oral.`;
 					} else {
-						r += ` The stress of being limited to buttsex and oral is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being limited to buttsex and oral is <span class="health dec">hard on ${him}.</span>`;
 					}
 				} else if (!canA && canV) {
 					if (!injury) {
 						r += ` ${He}'s such an expert whore that ${he} copes with the stress of being limited to vaginal and oral.`;
 					} else {
-						r += ` The stress of being limited to vaginal and oral is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being limited to vaginal and oral is <span class="health dec">hard on ${him}.</span>`;
 					}
 				} else if (!canA && !canV) {
 					if (!injury) {
 						r += ` ${He}'s such an expert whore that ${he} copes with the stress of being limited to oral.`;
 					} else {
-						r += ` The stress of being limited to oral sex is <span class="red">hard on ${him}.</span>`;
+						r += ` The stress of being limited to oral sex is <span class="health dec">hard on ${him}.</span>`;
 					}
 				}
 			}
@@ -444,23 +444,23 @@ App.SlaveAssignment.whore = (function() {
 				}
 				if (injuryChance > 80) {
 					slave.minorInjury = "sore ass";
-					r += ` Rough anal with a careless customer left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` Rough anal with a careless customer left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (injuryChance > 60) {
 					slave.minorInjury = "black eye";
-					r += ` A violent customer left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` A violent customer left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (injuryChance > 40) {
 					slave.minorInjury = "split lip";
-					r += ` An abusive customer left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` An abusive customer left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else if (injuryChance > 20) {
 					slave.minorInjury = "bad bruise";
-					r += ` A rough customer left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` A rough customer left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 					r += minorInjuryResponse(slave);
 				} else {
 					slave.minorInjury = "sore muscle";
-					r += ` The hard labor of constant sex left ${him} with a <span class="red">${slave.minorInjury}.</span>`;
+					r += ` The hard labor of constant sex left ${him} with a <span class="health dec">${slave.minorInjury}.</span>`;
 				}
 			}
 		}
@@ -531,10 +531,10 @@ App.SlaveAssignment.whore = (function() {
 	 */
 	function slaveSkills(slave) {
 		let skillIncrease;
-		if (!setup.whoreCareers.includes(slave.career) && slave.skill.whore < V.masteredXP) {
+		if (!App.Data.Careers.General.whore.includes(slave.career) && slave.skill.whore < V.masteredXP) {
 			slave.skill.whore += jsRandom(1, Math.ceil((slave.intelligence + slave.intelligenceImplant) / 15) + 8);
 		}
-		if (setup.whoreCareers.includes(slave.career)) {
+		if (App.Data.Careers.General.whore.includes(slave.career)) {
 			r += ` ${He} has sex work experience from ${his} life before ${he} was a slave, making ${him} more effective.`;
 		} else if (slave.skill.whore >= V.masteredXP) {
 			r += ` ${He} has experience as a prostitute from working for you, making ${him} more effective.`;
diff --git a/src/endWeek/saWorkAGloryHole.js b/src/endWeek/saWorkAGloryHole.js
index 114d5994c392ad6065778be8b0db9258271abc29..56e2731edf69ec034659ac8a013fabb25f01db42 100644
--- a/src/endWeek/saWorkAGloryHole.js
+++ b/src/endWeek/saWorkAGloryHole.js
@@ -106,30 +106,30 @@ App.SlaveAssignment.workAGloryHole = (function() {
 			}
 			r += ` protect ${his} health from the stress of being used as a sexual appliance.`;
 		} else if (slave.health.condition < -50) {
-			r += ` The stress of being used while very in terrible condition <span class="red">damaged ${his} health.</span>`;
+			r += ` The stress of being used while very in terrible condition <span class="health dec">damaged ${his} health.</span>`;
 			healthDamage(slave, Math.max(Math.trunc((slave.sexAmount - 400) / 25) + jsRandom(-2, 2), 1));
 		} else if (slave.health.condition < -10) {
 			if (jsRandom(1, 100) < 10) {
-				r += ` The stress of being used while weak has <span class="red">damaged ${his} health.</span>`;
+				r += ` The stress of being used while weak has <span class="health dec">damaged ${his} health.</span>`;
 				healthDamage(slave, Math.max(Math.trunc((slave.sexAmount - 400) / 25) + jsRandom(-2, 2), 1));
 			}
 		} else if (!canDoVaginal(slave) && !canDoAnal(slave)) {
 			if (jsRandom(1, 100) < 75) {
-				r += ` The stress of being used repeatedly in only one hole has <span class="red">damaged ${his} health.</span>`;
+				r += ` The stress of being used repeatedly in only one hole has <span class="health dec">damaged ${his} health.</span>`;
 				healthDamage(slave, Math.max(Math.trunc((slave.sexAmount - 400) / 25) + jsRandom(-2, 2), 1));
 			}
 		} else if (!canDoVaginal(slave)) {
 			if (jsRandom(1, 100) < 50) {
-				r += ` The stress of being used in only two holes has <span class="red">damaged ${his} health.</span>`;
+				r += ` The stress of being used in only two holes has <span class="health dec">damaged ${his} health.</span>`;
 				healthDamage(slave, Math.max(Math.trunc((slave.sexAmount - 400) / 25) + jsRandom(-2, 2), 1));
 			}
 		} else if (jsRandom(1, 100) < 25) {
-			r += ` The stress of being used has <span class="red">damaged ${his} health.</span>`;
+			r += ` The stress of being used has <span class="health dec">damaged ${his} health.</span>`;
 			healthDamage(slave, Math.max(Math.trunc((slave.sexAmount - 400) / 25) + jsRandom(-2, 2), 1));
 		}
 
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			r += ` ${He} is<span class="red">`;
+			r += ` ${He} is<span class="health dec">`;
 			if (slave.health.illness === 1) {
 				r += ` feeling under the weather`;
 			} else if (slave.health.illness === 2) {
@@ -153,13 +153,13 @@ App.SlaveAssignment.workAGloryHole = (function() {
 		}
 		if (slave.assignment === Job.ARCADE) {
 			if (!slave.fuckdoll) {
-				r += ` ${He} spends ${his} working hours constrained to a box, and ${his} time outside preparing for ${his} next shift; <span class="red">an exhausting life for even the healthiest of slaves.</span>`;
+				r += ` ${He} spends ${his} working hours constrained to a box, and ${his} time outside preparing for ${his} next shift; <span class="health dec">an exhausting life for even the healthiest of slaves.</span>`;
 				if (V.arcadeUpgradeHealth > 0) {
 					r += ` The curative cocktail pumped into ${him} is laced with `;
 					if (V.arcadeUpgradeHealth === 2) {
-						r += `sedatives and painkillers to keep ${him} in a <span class="green">state of forced rest.</span>`;
+						r += `sedatives and painkillers to keep ${him} in a <span class="health inc">state of forced rest.</span>`;
 					} else {
-						r += `painkillers and mild muscle relaxants; Not enough to ruin a customer's fun, but enough to keep ${him} in a <span class="green">state of pseudo-rest.</span>`;
+						r += `painkillers and mild muscle relaxants; Not enough to ruin a customer's fun, but enough to keep ${him} in a <span class="health inc">state of pseudo-rest.</span>`;
 					}
 				} else {
 					r += ` If ${he} needs to rest, however, ${he} can always just pass out in ${his} restraints.`;
@@ -169,14 +169,14 @@ App.SlaveAssignment.workAGloryHole = (function() {
 		} else if (slave.assignment === Job.GLORYHOLE) {
 			if (!slave.fuckdoll) {
 				if (slaveResting(slave)) {
-					r += ` ${He} spends reduced hours serving ${his} glory hole in order to <span class="green">offset ${his} lack of rest.</span>`;
+					r += ` ${He} spends reduced hours serving ${his} glory hole in order to <span class="health inc">offset ${his} lack of rest.</span>`;
 				} else if (slave.health.tired + 20 >= 90 && !willWorkToDeath(slave)) {
 					r += ` ${He} attempts to refuse work due to ${his} exhaustion, but can do little to stop it or the resulting <span class="trust dec">severe punishment.</span> ${He} <span class="devotion dec">purposefully underperforms,</span> choosing ${his} overall well-being over the consequences, <span class="reputation dec">aggravating customers and damaging the establishment's image.</span>`;
 					slave.devotion -= 10;
 					slave.trust -= 5;
 					repX(forceNeg(50), "disobedience", slave);
 				} else {
-					r += ` ${He} spends long hours cooped up in a small box with nothing to rest on. Even if ${he} were to try to lean against its side, it won't be long before another dick pokes through the hole demanding service. By the time ${he} is released, ${he} is <span class="red">utterly exhausted.</span>`;
+					r += ` ${He} spends long hours cooped up in a small box with nothing to rest on. Even if ${he} were to try to lean against its side, it won't be long before another dick pokes through the hole demanding service. By the time ${he} is released, ${he} is <span class="health dec">utterly exhausted.</span>`;
 				}
 			}
 			tired(slave);
@@ -440,7 +440,6 @@ App.SlaveAssignment.workAGloryHole = (function() {
 			cashX(cash, "income for working a gloryhole in an unregistered building", slave);
 		}
 
-		State.temporary.profits += cash;
 		incomeStats.income += cash;
 	}
 })();
diff --git a/src/endWeek/sexualServices.js b/src/endWeek/sexualServices.js
index 84edc494ec9c8e904c72bac34b290cdd94952f22..8b23262f427fe1bdd586b8a2116a5879047048c9 100644
--- a/src/endWeek/sexualServices.js
+++ b/src/endWeek/sexualServices.js
@@ -88,8 +88,6 @@ App.EndWeek.computeSexualServicesModel = function(renderContainer) {
 			arcadeSupply.upperClass += Math.trunc(1.15 * upperClassArcadeSexDemand);
 			upperClassSexDemand -= arcadeSupply.upperClass;
 		}
-	} else {
-		arcadeDemandDeg = 0;
 	}
 
 	// Public slut sex supply. Top and upper class won't partake
diff --git a/src/endWeek/slaveAssignmentReport.js b/src/endWeek/slaveAssignmentReport.js
index 6e2c5ba860109411a735b2d8b68d9feb4e9a7e2f..e51c6c9832e072a8245d48e63de851c1663dd193 100644
--- a/src/endWeek/slaveAssignmentReport.js
+++ b/src/endWeek/slaveAssignmentReport.js
@@ -232,69 +232,14 @@ App.EndWeek.slaveAssignmentReport = function() {
 		}
 	}
 
-	if (S.HeadGirl) {
-		/** @type {FC.HeadGirlTrainee[][]} */
-		const HGPossibleSlaves = [[], [], [], [], [], []];
-		for (const slave of V.slaves) {
-			if (!assignmentVisible(slave) || slave.fuckdoll === 1 || slave.ID === V.BodyguardID || slave.ID === V.HeadGirlID || slave.fetish === "mindbroken") {
-				continue;
-			} else if (Array.isArray(V.personalAttention) && V.personalAttention.some(p => p.ID === slave.ID)) {
-				continue;
-			}
-
-			if (V.headGirlTrainsHealth && slave.health.condition < -20) {
-				HGPossibleSlaves[0].push({ID: slave.ID, training: "health"});
-				continue;
-			}
-
-			if (slave.health.tired < 50) {
-				const hasParaphilia = (["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "self hating"].includes(slave.sexualFlaw));
-				if (V.headGirlTrainsParaphilias && hasParaphilia) {
-					HGPossibleSlaves[1].push({ID: slave.ID, training: "paraphilia"});
-					continue;
-				}
-
-				if (V.headGirlTrainsFlaws || V.headGirlSoftensFlaws) {
-					if (slave.behavioralFlaw !== "none" || (slave.sexualFlaw !== "none" && !hasParaphilia)) {
-						if (V.headGirlSoftensFlaws) {
-							if (slave.devotion > 20) {
-								if ((slave.behavioralFlaw !== "none" && slave.behavioralQuirk === "none") || (slave.sexualFlaw !== "none" && slave.sexualQuirk === "none" && !hasParaphilia)) {
-									HGPossibleSlaves[3].push({ID: slave.ID, training: "soften"});
-								} else {
-									HGPossibleSlaves[3].push({ID: slave.ID, training: "flaw"});
-								}
-								continue;
-							}
-						} else if (V.headGirlTrainsFlaws) {
-							HGPossibleSlaves[2].push({ID: slave.ID, training: "flaw"});
-							continue;
-						}
-					}
-				}
-
-				if (V.headGirlTrainsObedience && slave.devotion <= 20 && slave.trust >= -20) {
-					HGPossibleSlaves[4].push({ID: slave.ID, training: "obedience"});
-					continue;
-				}
-
-				if (V.headGirlTrainsSkills) {
-					if (slave.skill.oral < S.HeadGirl.skill.oral) {
-						HGPossibleSlaves[5].push({ID: slave.ID, training: "oral skill"});
-					} else if ((slave.skill.vaginal < S.HeadGirl.skill.vaginal) && (slave.vagina > 0) && (canDoVaginal(slave))) {
-						HGPossibleSlaves[5].push({ID: slave.ID, training: "fuck skill"});
-					} else if ((slave.skill.anal < S.HeadGirl.skill.anal) && (slave.anus > 0) && (canDoAnal(slave))) {
-						HGPossibleSlaves[5].push({ID: slave.ID, training: "anal skill"});
-					} else if (slave.skill.whoring < S.HeadGirl.skill.whoring) {
-						HGPossibleSlaves[5].push({ID: slave.ID, training: "whore skill"});
-					} else if ((slave.skill.entertainment < S.HeadGirl.skill.entertainment) && !isAmputee(slave)) {
-						HGPossibleSlaves[5].push({ID: slave.ID, training: "entertain skill"});
-					}
-				}
-			}
-		}
-		V.HGTrainSlavesIDs = HGPossibleSlaves.flatten().slice(0, App.EndWeek.saVars.HGEnergy);
-	} else {
-		V.HGTrainSlavesIDs = [];
+	// initialize slave art
+	if (V.seeImages && V.seeReportImages) {
+		// agents and partners are not drawn; penthouse partners and the head girl's slave will be drawn via a different mechanism (since they are larger and right-aligned)
+		const undrawnJobs = [Job.AGENT, Job.AGENTPARTNER, ...App.Entity.facilities.penthouse.jobsNames, Job.HEADGIRLSUITE];
+		const drawnSlaveIDs = V.slaves.filter(s => !undrawnJobs.includes(s.assignment)).map(s => s.ID);
+		// this batch renderer object will be accessible to all the facility reports
+		App.EndWeek.saVars.slaveArt = new App.Art.SlaveArtBatch(drawnSlaveIDs, 0);
+		res.append(App.EndWeek.saVars.slaveArt.writePreamble());
 	}
 
 	/**
@@ -304,7 +249,7 @@ App.EndWeek.slaveAssignmentReport = function() {
 	 *
 	 * @param array _facListArr
 	 *	Multidimensional temporary array
-	 *	0: The SC passage name or DOM function for the facility's report
+	 *	0: The DOM function for the facility's report
 	 *	1: A facility object, or the title of the report if there is no facility object
 	 *	2: If there is no facility object, a truthy value indicating whether the facility exists
 	 *	3: If there is no facility object, the maximum capacity of the facility
@@ -316,21 +261,21 @@ App.EndWeek.slaveAssignmentReport = function() {
 	 */
 
 	const facListArr = [
-		["Arcade Report", App.Entity.facilities.arcade],
-		["Brothel Report", App.Entity.facilities.brothel],
-		["Cellblock Report", App.Entity.facilities.cellblock],
-		["Clinic Report", App.Entity.facilities.clinic],
-		["Club Report", App.Entity.facilities.club],
-		["Dairy Report", App.Entity.facilities.dairy],
-		["Farmyard Report", App.Entity.facilities.farmyard],
-		["Schoolroom Report", App.Entity.facilities.schoolroom],
-		["Spa Report", App.Entity.facilities.spa],
-		["Servants' Quarters Report", App.Entity.facilities.servantsQuarters],
-		["Nursery Report", App.Entity.facilities.nursery],
-		["Children Report", "Nursery Children", V.nursery, V.nurseryChildren],
-		["Incubator Report", App.Entity.facilities.incubator],
-		["Master Suite Report", App.Entity.facilities.masterSuite],
-		["Penthouse Report", "The Penthouse"],
+		[App.EndWeek.arcadeReport, App.Entity.facilities.arcade],
+		[App.EndWeek.brothelReport, App.Entity.facilities.brothel],
+		[App.EndWeek.cellblockReport, App.Entity.facilities.cellblock],
+		[App.EndWeek.clinicReport, App.Entity.facilities.clinic],
+		[App.EndWeek.clubReport, App.Entity.facilities.club],
+		[App.EndWeek.dairyReport, App.Entity.facilities.dairy],
+		[App.Facilities.Farmyard.farmyardReport, App.Entity.facilities.farmyard],
+		[App.EndWeek.schoolroomReport, App.Entity.facilities.schoolroom],
+		[App.EndWeek.spaReport, App.Entity.facilities.spa],
+		[App.EndWeek.servantsQuartersReport, App.Entity.facilities.servantsQuarters],
+		[App.Facilities.Nursery.nurseryReport, App.Entity.facilities.nursery],
+		[App.Facilities.Nursery.childrenReport, "Nursery Children", V.nursery, V.nurseryChildren],
+		[App.EndWeek.incubatorReport, App.Entity.facilities.incubator],
+		[App.EndWeek.masterSuiteReport, App.Entity.facilities.masterSuite],
+		[App.EndWeek.penthouseReport, "The Penthouse"],
 		[App.EndWeek.rulesAssistantReport, "Rules Assistant", V.rulesAssistantAuto], /** should be last — may reassign slaves **/
 		[App.EndWeek.labReport, "Lab", V.researchLab.level]
 	];
@@ -372,12 +317,8 @@ App.EndWeek.slaveAssignmentReport = function() {
 		// TODO: this call should be in the individual reports' slave loops so slaves don't leak data to their coworkers if someone fucks up
 		App.EndWeek.saVars.nextSlave();
 
-		const reportContent = document.createElement("div");
-		if (typeof facSubArr[0] === "function") {
-			reportContent.append(facSubArr[0]());
-		} else if (typeof facSubArr[0] === "string") {
-			App.UI.DOM.includePassage(reportContent, facSubArr[0]);
-		}
+		// @ts-ignore - the first element of the subarray is always callable but TS isn't smart enough to figure that out
+		const reportContent = App.UI.DOM.makeElement("div", facSubArr[0]());
 
 		// needs to be inside the loop after the report passage to get the employees number after re-assignments
 		const stats = _getReportElementStats(facSubArr);
@@ -839,14 +780,3 @@ App.EndWeek.slaveAssignmentReport = function() {
 		warningLine.appendChild(document.createTextNode("."));
 	}
 };
-
-/**
- * @param {App.Entity.SlaveState} slave
- * @returns {Node}
- */
-App.EndWeek.favoriteIcon = function(slave) {
-	if (V.favorites.includes(slave.ID)) {
-		return App.UI.DOM.makeElement("span", String.fromCharCode(0xe800), ["icons", "favorite"]);
-	}
-	return document.createDocumentFragment();
-};
diff --git a/src/endWeek/standardSlaveReport.js b/src/endWeek/standardSlaveReport.js
index 5ed4a6d9b43f2f8129093671ce28c516ed8e1fe3..a5d6800111c57daf442b648f47cbfb3b07f6d0a3 100644
--- a/src/endWeek/standardSlaveReport.js
+++ b/src/endWeek/standardSlaveReport.js
@@ -1,27 +1,47 @@
 /**
  * Generates (and returns if not silent) a standard slave report
+ * This is the part after the slave's job in most facilities.
  * @param {App.Entity.SlaveState} slave
  * @param {boolean} silent
- * @returns {HTMLElement|null}
+ * @returns {DocumentFragment|null}
  */
 App.SlaveAssignment.standardSlaveReport = function(slave, silent=false) {
 	const clothes = App.SlaveAssignment.choosesOwnClothes(slave);
-
-	tired(slave);
-
-	const rules = App.SlaveAssignment.rules(slave);
-	const diet = App.SlaveAssignment.diet(slave);
-	const ltEffects = App.SlaveAssignment.longTermEffects(slave);
-	const drugs = App.SlaveAssignment.drugs(slave);
-	const relationships = App.SlaveAssignment.relationships(slave);
-	const rivalries = App.SlaveAssignment.rivalries(slave);
+	const individualReport = App.SlaveAssignment.individualSlaveReport(slave);
 	const devotion = App.SlaveAssignment.devotion(slave);
 
 	if (!silent) {
-		const content = App.UI.DOM.makeElement("div", '', "indent");
+		const container = document.createDocumentFragment();
+		App.Events.addNode(container, [clothes, ...individualReport], "div", "indent");
+		App.Events.addNode(container, [devotion], "div", "indent");
+		return container;
+	}
+};
 
-		$(content).append(clothes, rules, diet, ltEffects, drugs, relationships, rivalries, `<div class="indent">${devotion}</div>`);
+/**
+ * Generates the main part of the standard slave report for an individual slave.
+ * This is the section that's identical for all slaves regardless of facility.
+ * @param {App.Entity.SlaveState} slave
+ * @returns {Array<DocumentFragment|string>}
+ */
+App.SlaveAssignment.individualSlaveReport = function(slave) {
+	return [
+		App.SlaveAssignment.rules(slave),
+		App.SlaveAssignment.diet(slave),
+		App.SlaveAssignment.longTermEffects(slave),
+		App.SlaveAssignment.drugs(slave),
+		App.SlaveAssignment.relationships(slave),
+		App.SlaveAssignment.rivalries(slave),
+	];
+};
 
-		return content;
+/**
+ * Render slave assignment report art
+ * @param {ParentNode} node
+ * @param {App.Entity.SlaveState} slave
+ */
+App.SlaveAssignment.appendSlaveArt = function(node, slave) {
+	if (V.seeImages && V.seeReportImages) {
+		App.UI.DOM.appendNewElement("div", node, App.EndWeek.saVars.slaveArt.render(slave), ["imageRef", "tinyImg"]);
 	}
 };
diff --git a/src/events/PE/concubineInterview.js b/src/events/PE/concubineInterview.js
index 9d64498a8df7c4bd0deebc08769dd33166690d4e..40b5db7e7cb649db4aa2a75d07d4846c269e5b8d 100644
--- a/src/events/PE/concubineInterview.js
+++ b/src/events/PE/concubineInterview.js
@@ -31,7 +31,7 @@ App.Events.PEConcubineInterview = class PEConcubineInterview extends App.Events.
 		const rep = val => repX(val, "concubine", eventSlave);
 		const speak = string => Spoken(eventSlave, string);
 
-		V.nextLink = "Next Week";
+		V.nextLink = "RIE Eligibility Check";
 
 		App.Events.drawEventArt(node, eventSlave);
 
@@ -529,13 +529,14 @@ App.Events.PEConcubineInterview = class PEConcubineInterview extends App.Events.
 
 				App.Events.addParagraph(node, t);
 
-				t = [];
-
-				t.push(`${eventSlave.slaveName} even manages to <span class="green">respond well</span> to a probing question about your gender. ${He} ${lisps ? `lisps` : `explains`}, ${speak(`"You have to understand that all that nonsense about men and women means less to us in the Free Cities. My ${Master} is a successful and powerful ${womanP}.`)}`);
-				if (!PC.dick) {
-					t.push(`${speak(`We just pay the right amount of attention to the success and the power.`)} ${He} quirks a corner of ${his} mouth. "I know <em>I</em> do."`);
-				} else {
-					t.push(`${He} quirks a corner of ${his} mouth. ${speak(`"And ${heP} has a <em>wonderful</em> cock."`)}`);
+				if (PC.title === 0) {
+					t = [];
+					t.push(`${eventSlave.slaveName} even manages to <span class="green">respond well</span> to a probing question about your gender. ${He} ${lisps ? `lisps` : `explains`}, ${speak(`"You have to understand that all that nonsense about men and women means less to us in the Free Cities. My ${Master} is a successful and powerful ${womanP}.`)}`);
+					if (!PC.dick) {
+						t.push(`${speak(`We just pay the right amount of attention to the success and the power.`)} ${He} quirks a corner of ${his} mouth. "I know <em>I</em> do."`);
+					} else {
+						t.push(`${He} quirks a corner of ${his} mouth. ${speak(`"And ${heP} has a <em>wonderful</em> cock."`)}`);
+					}
 				}
 
 				rep(500);
diff --git a/src/events/RECI/feminization.js b/src/events/RECI/feminization.js
index e75a58fffaa469be78bf745898307b86f805d517..2d278430d847708279eb9ea0af409ababb847f99 100644
--- a/src/events/RECI/feminization.js
+++ b/src/events/RECI/feminization.js
@@ -360,8 +360,8 @@ App.Events.RECIFeminization = class RECIFeminization extends App.Events.BaseEven
 			}
 			t.push(`and the bottom of ${his} dress hiked up so a guest can fuck ${his} ass. ${He}'s obviously enjoying ${himself}, happy to be getting some at last. ${His} fee for the night is <span class="cash inc">substantial,</span> and the host attaches a note to the payment, complimenting ${his} eagerness to get assfucked and ${his} willingness to suck cock, and asking when ${he}'ll be available next.`);
 			cashX(500, "event", eventSlave);
-			seX(eventSlave, "anal", "publicUse", "penetrative", 5);
-			seX(eventSlave, "oral", "publicUse", "penetrative", 5);
+			seX(eventSlave, "anal", "public", "penetrative", 5);
+			seX(eventSlave, "oral", "public", "penetrative", 5);
 			if (canGetPregnant(eventSlave) && eventSlave.eggType === "human") {
 				knockMeUp(eventSlave, 10, 1, -2);
 			}
diff --git a/src/events/RECI/milf.js b/src/events/RECI/milf.js
index 38e205e5ca98fb115ea4511dd9019549761b1864..aceebf314c23b555277b492991ba270c1a7a4c31 100644
--- a/src/events/RECI/milf.js
+++ b/src/events/RECI/milf.js
@@ -207,7 +207,7 @@ App.Events.RECIMilf = class RECIMilf extends App.Events.BaseEvent {
 		} else {
 			t.push(`${He} arches ${his} back prettily and then scoots ${himself} down the couch cushions, taking ${his} ${hands} away from ${his} crotch to spread ${his} ${legs} even wider, showing off everything ${he} has.`);
 		}
-		t.push(Spoken(eventSlave, `"I didn't really appreciate being fucked by ${V.PC.title !== 0 ? `gorgeous` : `hot`}  ${womenP} then, either. Not like I do now..."`));
+		t.push(Spoken(eventSlave, `"I didn't really appreciate being fucked by ${V.PC.title !== 0 ? `gorgeous` : `hot`} ${womenP} then, either. Not like I do now..."`));
 		App.Events.addParagraph(node, t);
 
 
@@ -769,7 +769,7 @@ App.Events.RECIMilf = class RECIMilf extends App.Events.BaseEvent {
 			t.push(`lips until another cock finds its way into ${his} mouth. ${His} fee for the night is <span class="cash inc">substantial,</span> and the student's father attaches a note to the payment. Apparently ${he} got him too, and he's interested in hiring ${him} for a business meeting next month.`);
 			cashX(500, "event", eventSlave);
 			if (canDoVaginal(eventSlave)) {
-				seX(eventSlave, "vaginal", "publicUse", "penetrative", 5);
+				seX(eventSlave, "vaginal", "public", "penetrative", 5);
 				if (eventSlave.vagina === 0) {
 					eventSlave.vagina++;
 				}
@@ -778,7 +778,7 @@ App.Events.RECIMilf = class RECIMilf extends App.Events.BaseEvent {
 				}
 			}
 			if (canDoAnal(eventSlave)) {
-				seX(eventSlave, "anal", "publicUse", "penetrative", 5);
+				seX(eventSlave, "anal", "public", "penetrative", 5);
 				if (eventSlave.anus === 0) {
 					eventSlave.anus++;
 				}
@@ -786,7 +786,7 @@ App.Events.RECIMilf = class RECIMilf extends App.Events.BaseEvent {
 					knockMeUp(eventSlave, 10, 1, -2);
 				}
 			}
-			seX(eventSlave, "oral", "publicUse", "penetrative", 5);
+			seX(eventSlave, "oral", "public", "penetrative", 5);
 			return t;
 		}
 
diff --git a/src/events/RESS/devotedEducated.js b/src/events/RESS/devotedEducated.js
index 6a3793665b65b34eb547cabd7f5658f30f8062a5..e2d9733a717c57890c7c3f47ce4b5f743ea3d83c 100644
--- a/src/events/RESS/devotedEducated.js
+++ b/src/events/RESS/devotedEducated.js
@@ -113,7 +113,7 @@ App.Events.RESSDevotedEducated = class RESSDevotedEducated extends App.Events.Ba
 			App.Events.addParagraph(node, t);
 			t = [];
 
-			t.push(`When you return later in the evening, it becomes abundantly clear that your citizenry taught ${eventSlave.slaveName} a harsh lesson about a slave's place in ${V.arcologies[0].name}. ${eventSlave.slaveName} has certainly <span class="green">learned to keep any pretentious thoughts about ${his} education in ${his} head.</span> ${He} did, however, <span class="red">have quite a rough time</span> sucking all those dicks${eventSlave.skill.oral <= 30 ? `, though ${he} did learn about sucking dick, so ${he} can't claim enslavement isn't educational. ${slaveSkillIncrease('oral', eventSlave, 10)}` : "."} And last of all, you and ${eventSlave.slaveName} did make <span class="green">quite a good impression</span> today, though for widely differing reasons.`);
+			t.push(`When you return later in the evening, it becomes abundantly clear that your citizenry taught ${eventSlave.slaveName} a harsh lesson about a slave's place in ${V.arcologies[0].name}. ${eventSlave.slaveName} has certainly <span class="green">learned to keep any pretentious thoughts about ${his} education in ${his} head.</span> ${He} did, however, <span class="health dec">have quite a rough time</span> sucking all those dicks${eventSlave.skill.oral <= 30 ? `, though ${he} did learn about sucking dick, so ${he} can't claim enslavement isn't educational. ${slaveSkillIncrease('oral', eventSlave, 10)}` : "."} And last of all, you and ${eventSlave.slaveName} did make <span class="green">quite a good impression</span> today, though for widely differing reasons.`);
 			App.Events.addParagraph(node, t);
 			t = [];
 
diff --git a/src/events/RESS/devotedShortstack.js b/src/events/RESS/devotedShortstack.js
index 5749585f9ba75f04f6830f485872fd2413303cfa..b51b5ca4d0e06f1ab283b9a4fd4486c01d8e7f06 100644
--- a/src/events/RESS/devotedShortstack.js
+++ b/src/events/RESS/devotedShortstack.js
@@ -302,7 +302,7 @@ App.Events.RESSDevotedShortstack = class RESSDevotedShortstack extends App.Event
 			eventSlave.trust -= 5;
 			seX(eventSlave, "oral", "public", "penetrative", 55);
 			healthDamage(eventSlave, 5);
-			return `You inform ${eventSlave.slaveName} that short ${girl}s like ${him} are delightfully amusing when immured in the arcade. Magnanimous as you are, you have two other slaves drag ${him} off to be installed in the arcade for a day, so that ${he} too may see the humor in having short ${girl}s serve in the arcade. Though ${V.arcadeName} has arcade pens to match any height of slave, you have ${eventSlave.slaveName} confined in a pen built for a much taller slave. Although ${his} head and neck protrude from one side of the pen without issue, ${he} is too short for ${his} ass to fill the other opening. As a result, ${he} must use the tips of ${his} toes maintain an unsteady grip on the rear opening, forcing ${him} to maintain an extremely taxing stretch just to keep ${his} body held aloft within the pen. Customers are unable to fuck ${his} holes but readily delight in watching ${him} squirm to keep ${his} body extended and horizontal, even with hard cocks brutally fucking ${his} face. Somewhere in the grueling, 18-hour marathon of relentless throat fucking, ${his} precarious position slips and ${his} lower half tumbles into the interior of the pen proper. Until an attendant rescues ${him}, ${his} neck is held crooked at an unnatural angle by ${his} restraints, as the rest of ${his} body dangles beneath it. ${His} ordeal forces ${him} to accept that a short ${girl}'s place is as an <span class="hotpink">amusing arcade hole,</span> though ${he} can't find the humor <span class="gold">in such a terrible plight.</span> Furthermore, ${his} intense exertions during ${his} stay <span class="red">negatively effects ${his} health.</span> Your other slaves take note of what you do to short ${girl}s who ask questions about their place in your penthouse.`;
+			return `You inform ${eventSlave.slaveName} that short ${girl}s like ${him} are delightfully amusing when immured in the arcade. Magnanimous as you are, you have two other slaves drag ${him} off to be installed in the arcade for a day, so that ${he} too may see the humor in having short ${girl}s serve in the arcade. Though ${V.arcadeName} has arcade pens to match any height of slave, you have ${eventSlave.slaveName} confined in a pen built for a much taller slave. Although ${his} head and neck protrude from one side of the pen without issue, ${he} is too short for ${his} ass to fill the other opening. As a result, ${he} must use the tips of ${his} toes maintain an unsteady grip on the rear opening, forcing ${him} to maintain an extremely taxing stretch just to keep ${his} body held aloft within the pen. Customers are unable to fuck ${his} holes but readily delight in watching ${him} squirm to keep ${his} body extended and horizontal, even with hard cocks brutally fucking ${his} face. Somewhere in the grueling, 18-hour marathon of relentless throat fucking, ${his} precarious position slips and ${his} lower half tumbles into the interior of the pen proper. Until an attendant rescues ${him}, ${his} neck is held crooked at an unnatural angle by ${his} restraints, as the rest of ${his} body dangles beneath it. ${His} ordeal forces ${him} to accept that a short ${girl}'s place is as an <span class="hotpink">amusing arcade hole,</span> though ${he} can't find the humor <span class="gold">in such a terrible plight.</span> Furthermore, ${his} intense exertions during ${his} stay <span class="health dec">negatively effects ${his} health.</span> Your other slaves take note of what you do to short ${girl}s who ask questions about their place in your penthouse.`;
 		}
 	}
 };
diff --git a/src/events/RESS/escapee.js b/src/events/RESS/escapee.js
index 87a53cb7dfa5799dc796ade6226f35c061b8615a..0d7d337cccedef63b1bdbe121b69423de61008f0 100644
--- a/src/events/RESS/escapee.js
+++ b/src/events/RESS/escapee.js
@@ -106,7 +106,7 @@ App.Events.RESSEscapee = class RESSEscapee extends App.Events.BaseEvent {
 		function flog() {
 			t = [];
 
-			t.push(`Lesser whippings are usually performed by binding a slave's hands and then securing them to the ceiling so that ${he} will dance titillatingly when struck. This is not such a beating. You tie ${eventSlave.slaveName} to a wall by ${his} wrists and ankles and flog ${him} with workmanlike thoroughness${eventSlave.pregKnown === 1 ? `, making sure to avoid accidentally ending ${his} pregnancy` : ""}. ${He} passes from angry struggles to agonized sobbing and finally to bloody, exhausted weeping before you untie ${his} now-limp form and apply first aid. ${eventSlave.slaveName}'s rebelliousness is <span class="gold">dulled by the experience,</span> and ${his} <span class="red">health is damaged.</span> Furthermore, every single one of your other slaves not already obedient to you is <span class="gold">frightened</span> by the example set.`);
+			t.push(`Lesser whippings are usually performed by binding a slave's hands and then securing them to the ceiling so that ${he} will dance titillatingly when struck. This is not such a beating. You tie ${eventSlave.slaveName} to a wall by ${his} wrists and ankles and flog ${him} with workmanlike thoroughness${eventSlave.pregKnown === 1 ? `, making sure to avoid accidentally ending ${his} pregnancy` : ""}. ${He} passes from angry struggles to agonized sobbing and finally to bloody, exhausted weeping before you untie ${his} now-limp form and apply first aid. ${eventSlave.slaveName}'s rebelliousness is <span class="gold">dulled by the experience,</span> and ${his} <span class="health dec">health is damaged.</span> Furthermore, every single one of your other slaves not already obedient to you is <span class="gold">frightened</span> by the example set.`);
 
 			eventSlave.trust -= 5;
 			healthDamage(eventSlave, 40);
@@ -118,7 +118,7 @@ App.Events.RESSEscapee = class RESSEscapee extends App.Events.BaseEvent {
 		function clip() {
 			t = [];
 
-			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. ${eventSlave.slaveName} is <span class="mediumorchid">saddened</span> and <span class="gold">frightened</span> by this punishment, and ${his} <span class="red">health is damaged</span> by the surgery. Every single one of your other slaves with any resistance left in them is <span class="gold">sensibly restrained</span> from trying such rash acts.`);
+			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. ${eventSlave.slaveName} is <span class="mediumorchid">saddened</span> and <span class="gold">frightened</span> by this punishment, and ${his} <span class="health dec">health is damaged</span> by the surgery. Every single one of your other slaves with any resistance left in them is <span class="gold">sensibly restrained</span> from trying such rash acts.`);
 
 			eventSlave.devotion -= 5;
 			eventSlave.trust -= 5;
@@ -132,7 +132,7 @@ App.Events.RESSEscapee = class RESSEscapee extends App.Events.BaseEvent {
 		function amputate() {
 			t = [];
 
-			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. ${eventSlave.slaveName} is <span class="mediumorchid">filled with implacable hatred</span> by this terrible punishment, and ${his} <span class="red">health is damaged</span> by the major surgery. However, every single one of your other slaves not already obedient to you is <span class="gold">utterly terrified</span> by the example set.`);
+			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. ${eventSlave.slaveName} is <span class="mediumorchid">filled with implacable hatred</span> by this terrible punishment, and ${his} <span class="health dec">health is damaged</span> by the major surgery. However, every single one of your other slaves not already obedient to you is <span class="gold">utterly terrified</span> by the example set.`);
 
 			eventSlave.devotion -= 50;
 			eventSlave.trust -= 50;
@@ -147,7 +147,7 @@ App.Events.RESSEscapee = class RESSEscapee extends App.Events.BaseEvent {
 		function blind() {
 			t = [];
 
-			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. You make sure to point out all the things ${he}'ll never get to see again. ${eventSlave.slaveName} is <span class="mediumorchid">filled with implacable hatred</span> by this terrible punishment, and ${his} <span class="red">health is slightly damaged</span> by the surgery. However, every single one of your other slaves not already obedient to you is <span class="gold">utterly terrified</span> by the example set.`);
+			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, weeping form through the penthouse to the surgery. You make sure to point out all the things ${he}'ll never get to see again. ${eventSlave.slaveName} is <span class="mediumorchid">filled with implacable hatred</span> by this terrible punishment, and ${his} <span class="health dec">health is slightly damaged</span> by the surgery. However, every single one of your other slaves not already obedient to you is <span class="gold">utterly terrified</span> by the example set.`);
 
 			eventSlave.devotion -= 50;
 			eventSlave.trust -= 50;
@@ -161,7 +161,7 @@ App.Events.RESSEscapee = class RESSEscapee extends App.Events.BaseEvent {
 		function mute() {
 			t = [];
 
-			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, protesting form through the penthouse to the surgery. ${He} is allowed one last chance to verbally resist you, plead with you, and finally to beg with you, all in vain. ${eventSlave.slaveName} is <span class="mediumorchid">filled with hatred</span> and <span class="gold">terror</span> by this harsh punishment, and ${his} <span class="red">health is slightly damaged</span> by the surgery. However, every single one of your other slaves not already obedient to you is <span class="hotpink">terrified</span> by the example set.`);
+			t.push(`The simple thing to do would be to sedate ${eventSlave.slaveName} and haul ${him} to the remote surgery for ${his} punishment. That would deny ${him} the added weight of terror, however, and would stop your other slaves from learning by the sight of it. So, you restrain ${him}, pronounce judgment, and drag ${his} struggling, protesting form through the penthouse to the surgery. ${He} is allowed one last chance to verbally resist you, plead with you, and finally to beg with you, all in vain. ${eventSlave.slaveName} is <span class="mediumorchid">filled with hatred</span> and <span class="gold">terror</span> by this harsh punishment, and ${his} <span class="health dec">health is slightly damaged</span> by the surgery. However, every single one of your other slaves not already obedient to you is <span class="hotpink">terrified</span> by the example set.`);
 
 			eventSlave.devotion -= 5;
 			eventSlave.trust -= 25;
diff --git a/src/events/RESS/frighteningDick.js b/src/events/RESS/frighteningDick.js
index 0c3cc2a31bf680aaaaf8d227125ad40e21717645..d08c34bbdd287cef1d8149aaf9485750b7bf927a 100644
--- a/src/events/RESS/frighteningDick.js
+++ b/src/events/RESS/frighteningDick.js
@@ -140,7 +140,7 @@ App.Events.RESSFrighteningDick = class RESSFrighteningDick extends App.Events.Ba
 
 		function virginityWarning() {
 			if (eventSlave.vagina === 0 && canDoVaginal(eventSlave)) {
-				return `This option will take ${his}  virginity`;
+				return `This option will take ${his} virginity`;
 			} else if (eventSlave.anus === 0 && !canDoVaginal(eventSlave)) {
 				return `This option will take ${his} anal virginity`;
 			}
diff --git a/src/events/RESS/retchingCum.js b/src/events/RESS/retchingCum.js
index f49ce0e31a0c9ae5d89dbb98cbe8d20b3e1aaec0..aa25dd3abd5c2c8c06b10c364a793bfc23a3060d 100644
--- a/src/events/RESS/retchingCum.js
+++ b/src/events/RESS/retchingCum.js
@@ -119,7 +119,7 @@ App.Events.RESSRetchingCum = class RESSRetchingCum extends App.Events.BaseEvent
 			} else {
 				t.push(`${His} hatred of oral sex makes ${his} ordeal that much more horrific,`);
 			}
-			t.push(`but it <span class="hotpink">breaks down ${his} resistance.</span> ${He} now <span class="gold">better understands the terrifying power you have over ${him},</span> and the sheer amount of cum ${he} is forced to ingest <span class="red">negatively effects ${his} health.</span> Your other cum-fed slaves take note of what you do to those who can't hold down their assigned diet.`);
+			t.push(`but it <span class="hotpink">breaks down ${his} resistance.</span> ${He} now <span class="gold">better understands the terrifying power you have over ${him},</span> and the sheer amount of cum ${he} is forced to ingest <span class="health dec">negatively effects ${his} health.</span> Your other cum-fed slaves take note of what you do to those who can't hold down their assigned diet.`);
 			eventSlave.devotion += 5;
 			eventSlave.trust -= 5;
 			eventSlave.sexualFlaw = "hates oral";
diff --git a/src/events/RESS/tooThinForCumDiet.js b/src/events/RESS/tooThinForCumDiet.js
index 129a2447d2e2d2e8c3a7ce626612571e78e84a09..2a0dd087e7072def727f8efde9585977db733264 100644
--- a/src/events/RESS/tooThinForCumDiet.js
+++ b/src/events/RESS/tooThinForCumDiet.js
@@ -343,7 +343,7 @@ App.Events.RESSTooThinForCumDiet = class RESSTooThinForCumDiet extends App.Event
 			t.push(`You then book ${him} to be the main attraction at a corporate office party that afternoon, where ${he} will be the target of a blow-bang and bukkake. You see that ${he}'s dosed heavily with the drugs every day this week, and then book the rest of ${his} afternoons for similar duties, making sure ${he} still tends to ${his} regular assignments as well. In the meantime, you also instruct the kitchen that ${he} is to eat as much cum-based food from the dispensers as ${he} can suck down in order to fuel up for these exhausting escapades.`);
 			App.Events.addParagraph(frag, t);
 			t = [];
-			t.push(`By the end of the week, the aphrodisiacs and ${his} slide into an inescapable routine of cum immersion have done their job, and ${he} has begun to <span class="lightcoral">view cum as an inevitable component of ${his} daily life.</span> ${He} also manages to <span class="green">gain a little weight.</span> Thanks to your manipulation of ${his} Pavlovian responses through extreme drug therapy, ${his} <span class="red">health has suffered a bit,</span> but ${he} is also <span class="hotpink">more dependent on you</span> thanks to ${his}`);
+			t.push(`By the end of the week, the aphrodisiacs and ${his} slide into an inescapable routine of cum immersion have done their job, and ${he} has begun to <span class="lightcoral">view cum as an inevitable component of ${his} daily life.</span> ${He} also manages to <span class="green">gain a little weight.</span> Thanks to your manipulation of ${his} Pavlovian responses through extreme drug therapy, ${his} <span class="health dec">health has suffered a bit,</span> but ${he} is also <span class="hotpink">more dependent on you</span> thanks to ${his}`);
 			if (eventSlave.addict === 0) {
 				t.push(`<span class="cyan">new</span>`);
 			} else {
diff --git a/src/events/RETS/reFucktoyPrefersRelative.js b/src/events/RETS/reFucktoyPrefersRelative.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a2c620e2fcb3162852a4e0127031f21cefd867c
--- /dev/null
+++ b/src/events/RETS/reFucktoyPrefersRelative.js
@@ -0,0 +1,345 @@
+App.Events.RETSFucktoyPrefersRelative = class RETSFucktoyPrefersRelative extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [
+			() => V.seeIncest > 0,
+		];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // event slave
+				s => s.fetish !== "mindbroken",
+				s => s.devotion > 20,
+				s => s.relationship > -2, // ebonded and lower always prefer PC
+				s => s.devotion < 95, // perfectly devoted prefer PC
+				s => [Job.CONCUBINE, Job.MASTERSUITE, Job.FUCKTOY].includes(s.assignment), // is assigned to fuck you
+				s => !["either", V.PC.genes].includes(RETSFucktoyPrefersRelative.preferredSex(s)), // but swings the other way
+				s => s.energy > 40, // scene assumes horniness...professionals might feel the same way, but you'd never know
+				canSee,
+				canHear
+			],
+			[ // and a relative that they prefer over you
+				s => s.fetish !== "mindbroken",
+				s => s.genes === RETSFucktoyPrefersRelative.preferredSex(getSlave(this.actors[0])),
+				s => areRelated(s, getSlave(this.actors[0])),
+				s => this.actors[0].rivalTarget !== s.ID,
+				s => (canWalk(s) || (canMove(s) && s.rules.mobility === "permissive")),
+				isSlaveAvailable
+			]
+		];
+	}
+
+	/** Return the preferred sex for this slave's partners
+	 * @param {App.Entity.SlaveState} slave
+	 * @returns {string}
+	 */
+	static preferredSex(slave) {
+		if (slave.attrXX > (slave.attrXY + 5)) {
+			return "XX";
+		} else if (slave.attrXY > (slave.attrXX + 5)) {
+			return "XY";
+		}
+		return "either";
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		const [fucktoy, relative] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, him
+		} = getPronouns(fucktoy);
+		const {
+			He2, he2, His2, his2, him2, girl2, women2
+		} = getPronouns(relative).appendSuffix("2");
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, [fucktoy, relative], "no clothing");
+
+		let fuckhole = fucktoy.toyHole;
+		if (fuckhole === "all her holes") {
+			if (canDoVaginal(fucktoy)) {
+				fuckhole = "pussy";
+			} else if (canDoAnal(fucktoy)) {
+				fuckhole = "ass";
+			} else {
+				fuckhole = "mouth";
+			}
+		}
+
+		let t = [];
+		t.push(`You're enjoying`, contextualIntro(V.PC, fucktoy, "DOM"), `one evening in your office when`, contextualIntro(fucktoy, relative, "DOM"), `passes by stark naked (probably on ${his2} way to or from the shower). The glass walls of your office are designed to let you, and by extension everyone else inside, clearly see everything that happens in the rest of the Penthouse, as well as the comings and goings of all your slaves.`);
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`In this case, that means ${fucktoy.slaveName} gets an eyeful of ${relative.slaveName} as ${he2} passes by. ${He} is a well-disciplined slave and ${his} reaction might have passed unnoticed if you weren't`);
+		switch (fuckhole) {
+			case "pussy":
+				if (V.PC.dick > 0) {
+					t.push(`deep inside ${his} pussy. You feel ${his} walls pulse and contract around you as ${he} cums hard.`);
+				} else {
+					t.push(`thrusting a strap-on deep into ${his} pussy. You pause your thrusts as ${he} gasps and twitches, cumming hard.`);
+				}
+				break;
+			case "ass":
+				if (V.PC.dick > 0) {
+					t.push(`deep inside ${his} ass. You feel ${his} sphincter contract around you as ${he} cums hard.`);
+				} else {
+					t.push(`thrusting a strap-on deep into ${his} ass. You pause your thrusts as ${he} gasps and twitches, cumming hard.`);
+				}
+				break;
+			case "mouth":
+				if (V.PC.dick > 0) {
+					t.push(`buried deep in ${his} throat. ${His} motions waver and you feel ${his} throat contract around you as ${he} cums hard.`);
+				} else {
+					t.push(`thrusting a strap-on deep into ${his} throat. ${His} motions waver as ${he} twitches, cumming hard.`);
+				}
+				break;
+			case "boobs":
+				if (V.PC.dick > 0) {
+					t.push(`receiving a ${fucktoy.boobs > 300 ? "boobjob" : "naizuri"} from ${him}. ${His} motions waver and ${he} gasps aloud as ${he} cums hard.`);
+				} else if (fucktoy.boobs > 300) {
+					t.push(`groping ${his} boobs. ${He} gasps and twitches, cumming hard.`);
+				} else {
+					t.push(`teasing ${his} flat chest. ${He} gasps and twitches, cumming hard.`);
+				}
+				break;
+			case "dick":
+				t.push(`riding ${him}. ${He} groans as ${he} unexpectedly cums hard, filling you up.`);
+				if (V.PC.vagina > 0) {
+					if (canImpreg(V.PC, fucktoy)) {
+						t.push(`You wonder briefly if ${he} might have just gotten you pregnant.`);
+						knockMeUp(V.PC, 5, 0, fucktoy.ID);
+					}
+				}
+				break;
+			default:
+				throw `Unexpected toyhole ${fuckhole}`;
+		}
+		t.push(`You have a lot of experience with slaves getting off, and you're pretty sure that reaction wasn't just from your use of ${him}, but from ogling the nude form of ${relative.slaveName} at the same time.`);
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`You decide to test your fucktoy, and ask ${him} directly whether ${he} was turned on by ${his} ${relativeTerm(fucktoy, relative)} ${relative.slaveName}.`);
+		if (fucktoy.trust <= 20) { // illusion of choice...he'll always tell you, just for a different reason
+			t.push(`${He} knows better than to lie to you, and replies, terrified, that ${he} was.`);
+		} else if (fucktoy.devotion < -20) {
+			t.push(`${He} defiantly replies that ${he} was.`);
+		} else {
+			t.push(`${He} trusts you enough to tell you the truth without reservation, and replies that ${he} was.`);
+		}
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`Pushing further, you ask ${him} what turns ${him} on so much about ${relative.slaveName} that ${he}'d cum from looking at ${him2} but not from looking at you.`);
+		if (fucktoy.devotion < -20 && fucktoy.trust > 20) {
+			t.push(`${He} starts with some drivel about how much ${he} loves ${his} ${relativeTerm(fucktoy, relative)}, but ends with a snide remark about how`);
+		} else {
+			t.push(`Knowing that it's a potentially dangerous answer, but unwilling to lie to you, ${he} responds`);
+			if (!canTalk(fucktoy)) {
+				t.push(`with nervous gestures, indicating that`);
+			} else {
+				t.push(`nervously that`);
+			}
+			t.push(`${he} loves ${his} ${relativeTerm(fucktoy, relative)}, and besides,`);
+		}
+		if (RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XX") { // likes women
+			if (relative.vagina > -1 && V.PC.vagina === -1) {
+				t.push(`${he2} has a pretty pussy, and you don't.`);
+			} else if (relative.boobs > 300 && V.PC.boobs < 300) {
+				t.push(`${he2} has great tits, and you don't.`);
+			} else if (relative.boobs > V.PC.boobs * 1.2) {
+				t.push(`${he2} has way bigger tits than you do.`);
+			} else if (V.PC.title === 0) { // gender overhaul
+				t.push(`${he2}'s a woman, while you just look like one.`);
+			} else {
+				t.push(`${he2}'s a woman, and you aren't.`); // meh
+			}
+		} else { // likes men
+			if (relative.dick > V.PC.dick) {
+				if (V.PC.dick === 0) {
+					t.push(`${he2} has a cock, and ${he} loves cocks.`);
+				} else {
+					t.push(`${he2} has a bigger cock than you do.`);
+				}
+			} else if (relative.balls > 0 && V.PC.balls === 0) {
+				t.push(`${he2} has balls, and you don't.`);
+			} else if (relative.boobs === 0 && (V.PC.boobs >= 300 || V.PC.title === 0)) {
+				t.push(`${he2} has a manly, muscular chest.`);
+			} else if (V.PC.title === 1) { // gender overhaul
+				t.push(`${he2}'s a man, while you just look like one.`);
+			} else {
+				t.push(`${he2}'s a man, and you aren't.`); // meh
+			}
+		}
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`You consider this for a moment... it's objectively a true difference between you and ${relative.slaveName}, and that reason lines up with what you ${fucktoy.attrKnown ? "know" : "have guessed"} about ${his} sexuality already.`);
+		if (App.Utils.sexAllowed(fucktoy, relative)) {
+			t.push(`In fact, you're pretty sure you've seen the two of them specifically going at it fairly vigorously before.`);
+		}
+		t.push(`${His} higher attraction to ${women2} isn't necessarily a problem... ${he}'s been plenty attentive to your sexual needs even if ${he}'s not necessarily turned on by your body... but at the same time, you could probably do something about it if you wanted.`);
+		fucktoy.attrKnown = 1; // now you know
+		App.Events.addParagraph(node, t);
+
+		App.Events.addResponses(node, [
+			new App.Events.Result(`Punish ${him}`, punish),
+			new App.Events.Result(`Condition ${him} to appreciate ${RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XX" ? "men" : "women"}`, condition),
+			new App.Events.Result(`Call ${relative.slaveName} back and have a threesome`, threesome),
+		]);
+
+		function punish() {
+			const frag = document.createDocumentFragment();
+			t = [];
+			t.push(`You push ${fucktoy.slaveName} face-down over your desk and sternly inform that ${he} has only one objective when serving as your fucktoy: bringing you pleasure. To punctuate each phrase, you strike ${his} buttocks with your hand, leaving a red mark. ${He} is not to be distracted when a ${girl2} ${he} likes walks by, or when you get a call, or for any other reason unless you specifically tell ${him} to stop. And ${he} is certainly not allowed to cum before you without permission.`);
+			App.Events.addParagraph(frag, t);
+
+			t = [];
+			t.push(`${He} meekly acknowledges your instructions and <span class="devotion inc">vows to do better</span> for you, and you can tell that ${he} <span class="trust dec">fears</span> a repeat of this lesson.`);
+			fucktoy.devotion += 4;
+			fucktoy.trust -= 4;
+			if (fucktoy.fetish === "submissive" && fucktoy.fetishKnown === 1) {
+				t.push(`Still, when you glance down, you catch the faintest hint of a smile; it seems your punishment appealed to him, <span class="fetish inc">reinforcing ${his} submission</span> to your will.`);
+				fucktoy.fetishStrength += 10;
+			} else {
+				t.push(`${His} suppression of ${his} attraction naturally leads to a <span class="libido dec">decrease in ${his} libido</span> and <span class="stat drop">attraction to ${women2}.</span>`);
+				fucktoy.energy -= 3;
+				if (RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XX") {
+					fucktoy.attrXX -= 5;
+				} else {
+					fucktoy.attrXY -= 5;
+				}
+			}
+			App.Events.addParagraph(frag, t);
+
+			if (V.arcologies[0].FSSlaveProfessionalism !== "unset") {
+				t = [];
+				t.push(`Whispers spread through your arcology about how seriously you take your slaves' <span class="reputation inc">duty to professional detachment.</span>`);
+				FutureSocieties.Change("Slave Professionalism", 3);
+				App.Events.addParagraph(frag, t);
+			}
+
+			return [frag];
+		}
+
+		function condition() {
+			const {himA} = getPronouns(assistant.pronouns().main).appendSuffix('A');
+			t = [];
+			t.push(`While ${fucktoy.slaveName} kneels before you, ready to resume ${his} service to you, you inform ${him} that ${he}'ll get some help to see things the right way. You summon ${V.assistant.name} and instruct ${himA} to begin a conditioning regimen to encourage ${fucktoy.slaveName} to engage sexually with`);
+			if (RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XX") {
+				t.push(`<span class="improvement">men rather than women.</span>`);
+				fucktoy.attrXX -= 10;
+				fucktoy.attrXY += 15;
+			} else {
+				t.push(`<span class="improvement">women rather than men.</span>`);
+				fucktoy.attrXX += 15;
+				fucktoy.attrXY -= 10;
+			}
+			t.push(`${fucktoy.slaveName} is <span class="trust dec">apprehensive</span> of conditioning`);
+			if (fucktoy.devotion < -20) {
+				t.push(`and <span class="devotion dec">none too thrilled</span> that you're willing to twist ${his} tastes this way.`);
+				fucktoy.devotion -= 1;
+			} else {
+				t.push(`but <span class="devotion inc">thankful</span> that you're willing to help ${him} this way.`);
+				fucktoy.devotion += 1;
+			}
+			fucktoy.trust -= 1;
+			return t;
+		}
+
+		function threesome() {
+			const frag = document.createDocumentFragment();
+			const enjoysIncest = s => s.sexualQuirk === "perverted" || s.sexualQuirk === "sinful";
+
+			t = [];
+			t.push(`You're not done with ${fucktoy.slaveName} yet, but what good is maintaining a stable of sex slaves if you don't have a threesome every now and then? You have your assistant call ${relative.slaveName} back to your office. ${He2} arrives in just a couple of seconds, finding ${fucktoy.slaveName} blushing with embarrassment. You inform ${him2} that you'll be using ${him2} with ${fucktoy.slaveName}.`);
+			if (enjoysIncest(relative) || relative.relationshipTarget === fucktoy.ID) {
+				t.push(`${He2} grins, clearly aroused at the thought, and comes around the desk towards the two of you.`);
+			} else if (relative.devotion > 50) {
+				t.push(`${His2} eyes widen slightly, but ${he2} is devoted enough to commit fully to whatever you ask of ${him2}.`);
+			} else {
+				t.push(`${He2} stops in ${his2} tracks, clearly disturbed by what's about to happen, until you repeat your order.`);
+			}
+			App.Events.addParagraph(frag, t);
+
+			/* Double pen the fucktoy if she prefers dudes and can take it */
+			const doublePenFucktoy = RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XY" && (canDoAnal(fucktoy) || canDoVaginal(fucktoy)) && canPenetrate(relative);
+			/* Double pen the relative if the fucktoy prefers chicks and the relative can take it */
+			const doublePenRelative = RETSFucktoyPrefersRelative.preferredSex(fucktoy) === "XX" && (canDoAnal(relative) || canDoVaginal(relative)) && canPenetrate(fucktoy);
+			/* If double pen is not possible or desirable, and EITHER slave can ride you, double cowgirl on the player, so the slaves face each other */
+			const doubleCowgirl = canDoAnal(fucktoy) || canDoVaginal(fucktoy) || canDoAnal(relative) || canDoVaginal(relative);
+			if (doublePenFucktoy || doublePenRelative) {
+				const recipient = doublePenFucktoy ? fucktoy : relative;
+				const copenetrator = doublePenFucktoy ? relative : fucktoy;
+				const {heR, himR, hisR} = getPronouns(recipient).appendSuffix('R');
+				t = [];
+				t.push(`You lay back on your couch while ${recipient.slaveName} mounts you, with ${copenetrator.slaveName} getting ready behind ${himR}.`);
+				if (canDoAnal(recipient) && canDoVaginal(recipient)) {
+					t.push(`While ${heR} slides your ${V.PC.dick > 0 ? "dick" : "strap-on"} into ${hisR} waiting pussy, ${copenetrator.slaveName} pushes into ${hisR} ass. You continue like this for a bit, before swapping positions with ${copenetrator.slaveName}.`);
+					t.push(VCheck.Both(recipient, 2, 2));
+				} else {
+					const anal = canDoAnal(recipient);
+					t.push(`It's a bit tight in ${recipient.slaveName}'s ${anal ? 'ass' : 'pussy'}, with both ${copenetrator.slaveName} and your ${V.PC.dick > 0 ? "dick" : "strap-on"} sharing the space, but you make it work.`);
+					if (anal && recipient.anus < 3) {
+						t.push(VCheck.Anal(recipient, 2));
+						t.push(`Having both of you in ${hisR} tight ass <span class="lime">stretches it out</span> a bit.`);
+						recipient.anus++;
+					} else if (!anal && recipient.vagina < 3) {
+						t.push(VCheck.Vaginal(recipient, 2));
+						t.push(`Having both of you in ${hisR} tight pussy <span class="lime">stretches it out</span> a bit.`);
+						recipient.vagina++;
+					}
+				}
+				t.push(`It takes a few moments to find a rhythm, but ${fucktoy.slaveName} is excited that ${he} gets to do this with ${his} ${getWrittenTitle(fucktoy)} and ${his} ${relativeTerm(fucktoy, relative)}. It's easy to enjoy the change of pace, especially with ${recipient.slaveName} writhing on your ${V.PC.dick > 0 ? `dick` : `strap-on`}.`);
+				App.Events.addParagraph(frag, t);
+			} else if (doubleCowgirl) {
+				const onDick = canDoVaginal(fucktoy) ? fucktoy : canDoVaginal(relative) ? relative : canDoAnal(fucktoy) ? fucktoy : relative; // whee
+				const onMouth = onDick === fucktoy ? relative : fucktoy;
+				const {heD} = getPronouns(onDick).appendSuffix('D');
+				const {himM, heM, hisM} = getPronouns(onMouth).appendSuffix('M');
+				t = [];
+				t.push(`You lay back on your couch ${V.PC.dick < 1 ? `with your strap-on attached ` : ``}and have ${onDick.slaveName} climb onto you, facing you while ${heD} rides.`);
+				t.push(VCheck.Simple(onDick, 1));
+				t.push(`Meanwhile, you signal to ${onMouth.slaveName} that you want to taste ${himM}, and ${heM} straddles your face, facing ${hisM} ${relativeTerm(onMouth, onDick)}.`);
+				t.push(`Naturally, ${fucktoy.slaveName} starts a makeout session while ${he} and ${relative.slaveName} ride you. It doesn't take long for moans to turn to screams.`);
+				App.Events.addParagraph(frag, t);
+			} else {
+				/* FFS, really? You have two sex slaves here, one of whom is your *personal fucktoy*, and neither can do anything but oral? Fine, have an oral triangle. */
+				t = [];
+				t.push(`The three of you lie down together, as you order ${fucktoy.slaveName} to continue servicing you orally. You tell ${relative.slaveName} to`);
+				if (fucktoy.dick > 0) {
+					t.push(`give ${fucktoy.slaveName} a blowjob`);
+				} else if (fucktoy.vagina > 0) {
+					t.push(`eat ${fucktoy.slaveName} out`);
+				} else {
+					t.push(`give ${fucktoy.slaveName} a rimjob`); // I guess?
+				}
+				t.push(`while you get a taste of ${him2}. It takes a few moments to find a rhythm, but ${fucktoy.slaveName} is excited that ${he} gets to do this with ${his} ${getWrittenTitle(fucktoy)} and ${his} ${relativeTerm(fucktoy, relative)}, and it's not long before all of you have reached orgasm.`);
+				seX(fucktoy, "oral", relative, "oral", 1);
+				App.Events.addParagraph(frag, t);
+			}
+
+			t = [];
+			t.push(`The three of you collapse into a warm, satisfied pile of bodies. ${fucktoy.slaveName} is <span class="devotion inc">grateful</span> that you indulged ${his} fantasy, and <span class="trust inc">trusts</span> that you'll continue to keep ${his} satisfaction in mind.`);
+			fucktoy.devotion += 2;
+			fucktoy.trust += 2;
+
+			/* did one or both of them *really* have fun? */
+			if (enjoysIncest(fucktoy) && enjoysIncest(relative)) {
+				t.push(`On top of it all, it turns out that ${fucktoy.slaveName} and ${relative.slaveName} both really enjoy breaking the taboo of incest, and they've <span class="devotion inc">grown closer to you</span> from this encounter.`);
+				fucktoy.devotion += 4;
+				relative.devotion += 4;
+			} else if (enjoysIncest(fucktoy)) {
+				t.push(`On top of it all, it turns out that ${he} really enjoys breaking the taboo of incest, and ${he}'s <span class="devotion inc">grown closer to you</span> from this encounter.`);
+				fucktoy.devotion += 3;
+			} else if (enjoysIncest(relative)) {
+				t.push(`On top of it all, it turns out that ${relative.slaveName} really enjoys breaking the taboo of incest, and ${he2}'s <span class="devotion inc">grown closer to you</span> from this encounter.`);
+				relative.devotion += 3;
+			}
+			App.Events.addParagraph(frag, t);
+
+			return [frag];
+		}
+	}
+};
diff --git a/src/events/RETS/reSiblingTussle.js b/src/events/RETS/reSiblingTussle.js
index fef2421134b32538a517395ead0fb8babc6081bc..bf386b5a3b5e74c47ef4d7983627d588b629b15e 100644
--- a/src/events/RETS/reSiblingTussle.js
+++ b/src/events/RETS/reSiblingTussle.js
@@ -162,7 +162,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv
 			if (Math.abs(dead1 - dead2) <= 1) {
 				// fight between equals
 				t = [];
-				t.push(`The two siblings are fairly closely matched in size and skill, and the nude brawl carries on for a few minutes as the other slaves finish preparing for their day.  Eventually, ${sib1.slaveName} notices that they're going to be late to work, and they agree to a truce.`);
+				t.push(`The two siblings are fairly closely matched in size and skill, and the nude brawl carries on for a few minutes as the other slaves finish preparing for their day. Eventually, ${sib1.slaveName} notices that they're going to be late to work, and they agree to a truce.`);
 				t.push(`As they dress quickly, they see you standing at the door, and a quick flash of <span class="trust dec">fear</span> passes over their faces as they realize you've probably just seen the whole thing.`);
 				t.push(`The fear is replaced with relief when they realize that if you'd wanted to punish them, you probably would have done so already, but they'll leave with the lasting impression that you're always watching.`);
 
@@ -185,6 +185,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv
 				t.push(`${winner.slaveName} has a clear advantage over ${hisW} ${relativeTerm(winner, loser)} and quickly subdues ${himL}.`);
 				if (V.seeIncest && App.Utils.sexAllowed(winner, loser) && (winner.energy > 40 || winner.fetish === "dom")) {
 					t.push(`Excited by ${hisW} dominance of ${hisW} weaker ${relativeTerm(winner, loser)}, ${heW} quickly begins to take advantage of ${himL}.`);
+					/** @type {FC.SlaveActs} */
 					let sexType = "oral";
 					if (canPenetrate(winner)) {
 						// no taking of virginities here
@@ -221,7 +222,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv
 						t.push(`${winner.slaveName} encourages ${loser.slaveName} to ${winner.dick > 0 ? `suck ${himW} off` : `eat ${himW} out`} quickly, knowing that they both still have to get to work.`);
 					}
 					if (loser.fetish !== "submissive") {
-						t.push(`It's not long before ${heW} comes, and the two slaves separate to finish getting ready for work. ${winner.slaveName} had fun this morning, but is still clearly <span class="improvement">up for more</span>.`);
+						t.push(`It's not long before ${heW} comes, and the two slaves separate to finish getting ready for work. ${winner.slaveName} had fun this morning, but is still clearly <span class="libido inc">up for more</span>.`);
 						if (fetishChangeChance(loser) > jsRandom(0, 100)) {
 							loser.fetishKnown = 1;
 							loser.fetish = "submissive";
@@ -230,7 +231,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv
 						}
 						winner.energy += 4;
 					} else {
-						t.push(`${HisW} ${relativeTerm(winner, loser)} really gets off on ${hisL} submissive position and comes before ${heW} does, and when they're finished they both leave satisfied, <span class="improvement">heightening their sex drives</span>.`);
+						t.push(`${HisW} ${relativeTerm(winner, loser)} really gets off on ${hisL} submissive position and comes before ${heW} does, and when they're finished they both leave satisfied, <span class="libido inc">heightening their sex drives</span>.`);
 						sib1.energy += 4;
 						sib2.energy += 4;
 					}
diff --git a/src/events/gameover.tw b/src/events/gameover.tw
index 70975838ca93e6bb3184caf2ba74b874984c3431..144b9b464eb0b40e859473d40a79bea5801f69ab 100644
--- a/src/events/gameover.tw
+++ b/src/events/gameover.tw
@@ -32,16 +32,16 @@
 		Your appointed successor arrives in your old office to find $assistant.name ready to help them take control of the arcology. Most of your assets have been liquidated to create a huge endowment for $arcologies[0].name's Sisters. They'll never have to sell one of their own again, and will be able to afford all the advanced drugs and surgeries they desire. From the most matronly futa down to their newest Sister, none of them need concern themselves with anything other than sex.
 
 	<<case "major battle defeat">>
-		<<if $attackType == "raiders">>
+		<<if $SecExp.war.attacker.type === "raiders">>
 			As the horde of raiders breaks the battle lines and enters the arcology, all hell breaks loose. You citizens are raped, your slaves captured and abused, your penthouse burned.
 			As for you, you are quickly captured and brought in front of the warlord himself. With a satisfied smile he aims his pistol to your forehead and squeezes the trigger.
-		<<elseif $attackType == "old world">>
+		<<elseif $SecExp.war.attacker.type === "old world">>
 			As the interminable column of old world puppets breaks the battle lines and enters the arcology, all hell breaks loose. Properties are seized and wealth stolen and distributed between the victorious soldiers.
 			You are stripped of everything you possessed and left to rot in a corner of your once glorious arcology.
-		<<elseif $attackType == "freedom fighters">>
+		<<elseif $SecExp.war.attacker.type === "freedom fighters">>
 			As the army of freedom fighters invades the arcology, all hell breaks loose. Their righteous fury torches everything you held dear, while the streets of the arcology run red with the blood of the masters.
 			You are reserved a special death: crucified in front of the arcology's entrance, your corpse a grim reminder of your legacy.
-		<<elseif $attackType == "free city">>
+		<<elseif $SecExp.war.attacker.type === "free city">>
 			As the mercenaries break the battle lines and enter the arcology all hell breaks loose. The sectors are pillaged one by one, systematically and thoroughly. When they break in the penthouse they quickly capture and send you to their employers as proof of their success.
 			Your personal story may continue, but that part of it worthy of retelling has now ended.
 		<</if>>
diff --git a/src/events/intro/acquisition.js b/src/events/intro/acquisition.js
new file mode 100644
index 0000000000000000000000000000000000000000..4cf8481e4361563ae52dbb9ec9c1853a5304ed2c
--- /dev/null
+++ b/src/events/intro/acquisition.js
@@ -0,0 +1,1088 @@
+App.Intro.acquisition = function() {
+	const el = new DocumentFragment();
+
+	const valueOwed = (V.saveImported === 1) ? 5000 : 50000;
+
+	const r = [];
+
+	if (V.freshPC === 1 || V.saveImported === 0) {
+		PCSetup();
+	}
+
+	parentSetup();
+	inbreedingCalc();
+	if (V.plot === 1 && V.neighboringArcologies > 0) {
+		V.arcologies.reduce((acc, val) => val.prosperity > acc.prosperity ? val : acc).rival = 1;
+		V.rivalSet = 1;
+	}
+	V.targetAge = V.minimumSlaveAge;
+	V.targetAgeNursery = V.minimumSlaveAge;
+	resetFamilyCounters();
+
+	App.UI.DOM.appendNewElement("p", el, "You've done it.");
+	App.UI.DOM.appendNewElement("p", el, `You arrive at your new arcology, ${V.arcologies[0].name}, and head straight to the penthouse to enter the access codes that will tell the ${V.arcologies[0].name} systems to recognize you as their owner. The penthouse office is ready to receive the codes, and they authenticate. A voice activates in your earpiece.`);
+	App.UI.DOM.appendNewElement("p", el, `Congratulations. I am a personal assistant program, and it is my pleasure to assist you, ${PlayerName()} the new owner of ${V.arcologies[0].name}. I will offer useful information whenever possible in italics. Your new arcology has some unusual equipment. The previous owner kept a small stable of sex slaves. The penthouse therefore has a body modification studio for tattooing, bleaching and piercing, and an auto salon for more prosaic things like hair care. It also has a remote surgery, a small surgical theater that can be operated remotely by a qualified surgeon if you can pay the fee. Finally, it has a slave nutrition system connected to the arcology's hydroponics bays. This system produces a tasty protein-rich drink that provides the physically active female body all its necessary nutrients while leaving the lower digestive tract extremely clean. It even causes a mild increase in sex drive.`, "note");
+
+	r.push(`The previous owner seems to have left in something of a hurry.`);
+	let valueGiven = 0;
+	const heroSlaves = App.Utils.buildHeroArray().shuffle();
+	if (V.cheatMode === 1) {
+		r.push(`Since you've elected to take over an arcology with special advantages, you've acquired a very special group of slaves.`);
+		r.push(App.Intro.cheatModeSlaves());
+	} else if (V.saveImported === 1) {
+		r.push(`Since it took some time for you to ensure that your existing stable of slaves were safely moved to ${V.arcologies[0].name}, the previous owner had the time to get most of their things away.`);
+	} else if ((V.targetArcology.fs !== "New") && (V.targetArcology.fs !== "Multiculturalist")) {
+		for (let j = 0; j < 5; j++) {
+			if (valueOwed - valueGiven <= 5000) {
+				break;
+			}
+			const slave = generateFSSlave(V.targetArcology.fs);
+			const slaveCostValue = slaveCost(slave);
+			if (["AztecRevivalist", "ChineseRevivalist", "NeoImperialist", "Eugenics", "SlaveProfessionalism"].includes(V.targetArcology.fs)) {
+				valueGiven += slaveCostValue * 4;
+			} else {
+				valueGiven += slaveCostValue;
+			}
+			newSlave(slave);
+		}
+		const {He, his, girl} = getPronouns(V.slaves.random());
+		switch (V.targetArcology.fs) {
+			case "Supremacist":
+				r.push(`They kept a personal stable of fearful ${V.arcologies[0].FSSupremacistRace} sex slaves, but their sexual training is incomplete. Several of them are still here.`);
+				break;
+			case "Subjugationist":
+				r.push(`They made it a special goal to enslave and whore out as many ${V.arcologies[0].FSSubjugationistRace} people as possible. Several of them are still here.`);
+				break;
+			case "GenderRadicalist":
+				r.push(`They were in the process of building a stable of pretty young shemale whores. Several of them are still here. They're are all very attracted to men, and skilled at pleasing them.`);
+				break;
+			case "GenderFundamentalist":
+				r.push(`They kept a personal stable of slaves for breeding purposes. Several of them are still here. They've been kept pregnant, and work as servants when they aren't being bred.`);
+				break;
+			case "Paternalist":
+				r.push(`Their slaves were all very well treated. Several of them are still here. They were allowed to work as maids, and weren't even forced to have sex.`);
+				break;
+			case "Degradationist":
+				r.push(`Their personal slaves were all Fuckdolls, slaves who have been permanently encased in advanced latex suits and converted into living sex toys. Several of them are still here.`);
+				break;
+			case "AssetExpansionist":
+				r.push(`They kept a collection of bejeweled boobs for company, but they focused on breast expansion to the exclusion the slaves' emotional training. Several of them are still here.`);
+				break;
+			case "SlimnessEnthusiast":
+				r.push(`They kept a harem of slim, pretty girls, and treated them very well. Several of them are still here. They should be very trusting of a new owner.`);
+				break;
+			case "TransformationFetishist":
+				r.push(`They were just putting the finishing touches on a planned brothel's worth of surgically enhanced whores. Several of them are still here. They are already used to prostitution.`);
+				break;
+			case "BodyPurist":
+				r.push(`Their slaves were trained for sexual use, but their health, fitness, and natural appearance were the main priorities. Several of them are still here.`);
+				break;
+			case "MaturityPreferentialist":
+				r.push(`They preferred to keep their MILFs as scantily clad servants. Several of them are still here. They aren't all happy to be sex objects, but they're used to it.`);
+				break;
+			case "YouthPreferentialist":
+				r.push(`They treated their young slaves very well. Several of them are still here. Virgins have been carefully preserved, but have learned to use their mouths through experience.`);
+				break;
+			case "Pastoralist":
+				r.push(`Their herd of cow girls was progressing nicely, though more progress had been made on milk production than on training. Several of them are still here.`);
+				break;
+			case "PhysicalIdealist":
+				r.push(`Their slaves worked as prostitutes, but mostly to fund a program of muscle building for all of them, which was nearing completion. Several of them are still here.`);
+				break;
+			case "ChattelReligionist":
+				r.push(`They were recruiting a stable of slave whores by targeting people with religious complexes that made them particularly vulnerable to recruitment. Several of them are still here.`);
+				break;
+			case "RomanRevivalist":
+				r.push(`The only one of their slaves left is the bodyguard. ${He} should be very willing to transfer ${his} loyalty to you, as ${his} new owner.`);
+				break;
+			case "NeoImperialist":
+				r.push(`This arcology kept their Knights as actively enslaved and docile servants; you find one of them remaining here. ${He} immediately pledges ${his} loyalty over to you.`);
+				break;
+			case "AztecRevivalist":
+				r.push(`They maintained a combination of disobedient slaves, deemed incorrigible and blood priestesses. Since they offer themselves as slaves willingly, one has remained loyal to the owner of the arcology.`);
+				break;
+			case "EgyptianRevivalist":
+				r.push(`They kept a harem of beautiful slave girls, who have been well-treated and well-trained. They should be very willing to serve you in turn.`);
+				break;
+			case "EdoRevivalist":
+				r.push(`They put considerable effort into creating modern slave geisha, elegant Japanese slaves who were assigned to serve the public. Several of them are still here.`);
+				break;
+			case "ArabianRevivalist":
+				r.push(`They trained slaves personally, and never kept slaves for very long. The slaves they left are all partway through being trained.`);
+				break;
+			case "ChineseRevivalist":
+				r.push(`The only one of their slaves left is the Head Girl. ${He} should be willing to transfer ${his} loyalty to you, as ${his} proper superior.`);
+				break;
+			case "Eugenics":
+				r.push(`The only one of their slaves left is an absolute beauty of a ${girl}. ${He} is too smart for ${his} own good and will likely not trust you.`);
+				break;
+			case "Repopulationist":
+				r.push(`They kept a personal stable of slaves for breeding purposes. Several of them are still here. They've been kept heavily pregnant, and are used to being used whilst so.`);
+				break;
+			case "HedonisticDecadence":
+				r.push(`Their slaves were heavily pampered; free to lie about, fuck, and eat as much as they wanted. Several of them are still here, too lazy and spoiled to leave. They eagerly paw at you upon your arrival, begging for their bellies to be filled with food and for a good, hard fucking.`);
+				break;
+			case "IntellectualDependency":
+				r.push(`They kept several vapid sluts, who are now desperately begging you for sex. It seems they've drained the life out of every toy left behind and have no idea how to recharge them.`);
+				break;
+			case "SlaveProfessionalism":
+				r.push(`The only one of their slaves left has made it ${his} mission to have the penthouse ready for whomever should come to own it. ${He} greets you cordially, hands you a detailed summary of ${his} skills and information on your new arcology, and calmly awaits further orders.`);
+				break;
+			case "PetiteAdmiration":
+				r.push(`They had quite the impish little harem. Several of them are still here and immediately swarm you as a welcome, eager to see someone taller again.`);
+				break;
+			case "StatuesqueGlorification":
+				r.push(`They had quite the collection of towering slaves. Several of them are still here. They gaze down on you, evaluating how best to serve their new owner.`);
+				break;
+			default:
+				r.push(`ERROR: bad arcology type`);
+		}
+	} else {
+		r.push(`They could not get all of their personal effects away. Since they `);
+		if (V.targetArcology.fs === "Multiculturalist") {
+			r.push(`tried to sample different kinds of sexual slavery,`);
+		} else {
+			r.push(`did not have the time in control of the arcology to develop a specific stable of sex slaves,`);
+		}
+		r.push(`their slaves were quite varied.`);
+		for (let j = 0; j < heroSlaves.length; j++) {
+			if (valueOwed - valueGiven <= 5000) {
+				break;
+			}
+			const slave = App.Utils.getHeroSlave(heroSlaves[j]);
+			heroSlaves.splice(j, 1);
+			let slaveCostValue = slaveCost(slave);
+			if (valueGiven + slaveCostValue < valueOwed * 2) {
+				const {him, he, his, himself} = getPronouns(slave);
+				nationalityToAccent(slave);
+				slave.oldDevotion = slave.devotion;
+				slave.oldTrust = slave.trust;
+				slave.health.tired = 0;
+				valueGiven += slaveCostValue;
+				newSlave(slave);
+				App.Utils.setLocalPronouns(slave);
+
+				r.push (slave.slaveName);
+				if (slave.fetish === "mindbroken") {
+					r.push(`is, sadly, not mentally competent, and is wandering through the penthouse at the moment.`);
+				} else if (isAmputee(slave)) {
+					r.push(`is a quadruple amputee and is quite helpless, so you can attend to ${him} at your leisure.`);
+				} else if (slave.devotion < -50) {
+					r.push(`is quite rebellious and was attempting to escape, so I have locked ${him} in the slave quarters.`);
+				} else if (slave.devotion < -20) {
+					r.push(`resists my orders and was considering escape, so I have locked ${him} in the slave quarters.`);
+				} else if (slave.devotion <= 20) {
+					r.push(`is reasonably obedient, and is waiting for you in the dormitory, I believe in the hope of making a good impression.`);
+				} else if (slave.energy > 95) {
+					r.push(`is a remarkable sexual addict, and I believe ${he} will be very happy to meet you.`);
+				} else if (slave.fetish === "pregnancy") {
+					if (slave.bellyPreg >= 500) {
+						r.push(`is currently in the dormitory masturbating over ${his} growing pregnancy, and `);
+						if (V.PC.belly >= 5000) {
+							r.push(`will certainly be eager to meet you.`);
+						} else {
+							r.push(`I believe ${he} will be happy to show it to you.`);
+						}
+					} else {
+						r.push(`is currently in the dormitory examining ${himself} to try to discern ${his} fertility, and I believe ${he} will be `);
+						if (V.PC.belly >= 5000) {
+							r.push(`eager to get acquainted with you.`);
+						} else {
+							r.push(`happy to meet you.`);
+						}
+					}
+				} else if (slave.belly >= 5000) { // had to be placed after pregnancy or it would intercept
+					r.push(`is currently in the dormitory massaging ${his} greatly distended belly.`);
+				} else if (slave.fetish === "buttslut") {
+					r.push(`is currently in the dormitory masturbating anally, and I believe ${he} will be happy to meet you.`);
+				} else if (slave.fetish === "cumslut") {
+					r.push(`is currently in the dormitory exhibiting oral fixation, and I believe ${he} will be happy to meet you.`);
+				} else if (slave.fetish === "boobs") {
+					r.push(`is currently in the dormitory playing with ${his} nipples, and I believe ${he} will be happy to meet you.`);
+				} else if (slave.fetish === "humiliation") {
+					r.push(`is currently in the entryway flashing passersby, and I believe ${he} will be happy to meet you.`);
+				} else if (slave.fetish === "submissive") {
+					r.push(`is currently in the dormitory, experimenting with self-bondage using the sheets; I believe ${he} will be happy to meet you.`);
+				} else if (slave.fetish === "dom") {
+					r.push(`is currently in the exercise area keeping fit; ${he} likes to take an active role sexually and is using this down time to work out.`);
+				} else if (slave.fetish === "sadist") {
+					r.push(`is currently outside your office; ${he} enjoys being superior to other slaves and I believe ${he} means to ingratiate ${himself} to you.`);
+				} else if (slave.fetish === "masochist") {
+					r.push(`is a sexual masochist; ${he} is currently in the bathroom, experimenting with auto-flagellation with a wet towel.`);
+				} else {
+					r.push(`is currently outside your office, and I believe ${he} is attempting to maintain sexual arousal to make a good first impression on you.`);
+				}
+			}
+		}
+	}
+	if (valueOwed - valueGiven > 0) {
+		r.push(`There are some valuables present, worth ${cashFormat(valueOwed - valueGiven)}.`);
+		cashX((valueOwed - valueGiven), "event");
+	}
+	App.Events.addNode(el, r, "p", "note");
+
+	V.averageTrust = 0;
+	V.averageDevotion = 0;
+	let slavesContributing = 0;
+	for (const slave of V.slaves) {
+		updateHealth(slave);
+		slave.oldDevotion = slave.devotion;
+		slave.oldTrust = slave.trust;
+		// AVERAGE VALUES UPDATE
+		if (assignmentVisible(slave)) {
+			V.averageTrust += slave.trust;
+			V.averageDevotion += slave.devotion;
+			slavesContributing += 1;
+		} else {
+			if (slave.assignment !== "be confined in the cellblock") {
+				if (slave.assignment !== "be confined in the arcade") {
+					if (slave.assignment !== "work in the dairy" || V.dairyRestraintsSetting < 2) {
+						V.averageTrust += slave.trust * 0.5;
+						V.averageDevotion += slave.devotion * 0.5;
+						slavesContributing += 0.5;
+					}
+				}
+			}
+		}
+	}
+	if (slavesContributing !== 0) {
+		V.averageTrust = V.averageTrust / slavesContributing;
+		V.averageDevotion = V.averageDevotion / slavesContributing;
+	}
+	V.enduringTrust = V.averageTrust;
+	V.enduringDevotion = V.averageDevotion;
+	App.UI.SlaveSummary.settingsChanged();
+
+	el.append(
+		App.UI.DOM.link(
+			"Continue",
+			() => {
+				V.ui = "main";
+				if (V.terrain === "urban") {
+					V.slaveCostFactor = 0.85;
+					V.menialSupplyFactor = 30000;
+					V.menialDemandFactor = -30000;
+				} else if (V.terrain === "marine") {
+					V.slaveCostFactor = 1;
+				} else {
+					V.slaveCostFactor = 1.15;
+					V.menialDemandFactor = 30000;
+					V.menialSupplyFactor = -30000;
+				}
+				Save.autosave.save("Week Start Autosave");
+			},
+			[],
+			"Main"
+		)
+	);
+	return el;
+
+	function PCSetup() {
+		V.PC.birthName = V.PC.slaveName;
+		if (V.PC.slaveSurname) {
+			V.PC.birthSurname = V.PC.slaveSurname;
+		}
+		if (V.PC.title === 1) {
+			V.PC.muscles = 50;
+		} else {
+			V.PC.hLength = 15;
+			V.PC.waist = -20;
+			V.PC.voice = 2;
+			V.PC.shoulders = -1;
+			V.PC.hips = 1;
+			V.PC.muscles = 30;
+		}
+		if (V.PC.career === "escort") {
+			V.PC.anus = 1;
+			V.PC.clothes = "a slutty outfit";
+			V.PC.intelligenceImplant = 15;
+		} else if (V.PC.career === "servant") {
+			V.PC.geneticQuirks.fertility = 1;
+			V.PC.clothes = "a nice maid outfit";
+			V.PC.intelligenceImplant = 0;
+		}
+		if (V.PC.vagina === -1) {
+			V.PC.ovaries = 0;
+		} else if (V.PC.vagina > 0) {
+			V.PC.vaginaLube = 1;
+			V.PC.counter.birthsTotal = 0;
+			if (V.PC.career === "servant") {
+				if (V.PC.pregType !== 8) {
+					if (V.PC.actualAge >= 50) {
+						V.PC.counter.birthsTotal = 9;
+						V.PC.counter.birthMaster = 9;
+					} else if (V.PC.actualAge >= 35) {
+						V.PC.counter.birthsTotal = 6;
+						V.PC.counter.birthMaster = 6;
+					} else {
+						V.PC.counter.birthsTotal = 3;
+						V.PC.counter.birthMaster = 3;
+					}
+				} else { // Master kept you pregged up
+					if (V.PC.actualAge >= 50) {
+						V.PC.counter.birthsTotal = 70;
+						V.PC.counter.birthMaster = 70;
+					} else if (V.PC.actualAge >= 35) {
+						V.PC.counter.birthsTotal = 40;
+						V.PC.counter.birthMaster = 40;
+					} else {
+						V.PC.counter.birthsTotal = 16;
+						V.PC.counter.birthMaster = 16;
+					}
+				}
+				V.PC.geneticQuirks.fertility = 2;
+				for (const slave of V.slaves) {
+					if (slave.mother === -1) {
+						V.PC.counter.birthsTotal += 1;
+						if (slave.father === -1) {
+							V.PC.counter.birthSelf += 1;
+						} else {
+							slave.father = -3;
+							V.PC.counter.birthMaster += 1;
+						}
+					}
+				}
+			} else if (V.PC.career === "escort") {
+				for (const slave of V.slaves) {
+					if (slave.mother === -1) {
+						V.PC.counter.birthsTotal += 1;
+						if (slave.father === -1) {
+							V.PC.counter.birthSelf += 1;
+						} else {
+							slave.father = -5;
+							V.PC.counter.birthClient += 1;
+						}
+					} else if (slave.father === -1) {
+						slave.mother = -5;
+					}
+				}
+			} else {
+				for (const slave of V.slaves) {
+					if (slave.mother === -1) {
+						V.PC.counter.birthsTotal += 1;
+						if (slave.father === -1) {
+							V.PC.counter.birthSelf += 1;
+						} else {
+							V.PC.counter.birthOther += 1;
+						}
+					}
+				}
+			}
+			if (V.PC.preg > 0) {
+				V.PC.pregWeek = V.PC.preg;
+				if (V.PC.pregType !== 8) {
+					V.PC.pregType = 1;
+				} else {
+					V.PC.geneticQuirks.hyperFertility = 2;
+				}
+				if (V.PC.career === "servant") {
+					V.PC.pregSource = -3;
+					if (V.PC.pregType !== 8) {
+						V.PC.pregType += either(0, 0, 1);
+					}
+				} else if (V.PC.career === "escort") {
+					V.PC.pregSource = -5;
+				}
+				V.PC.pregKnown = 1;
+				V.PC.belly = getPregBellySize(V.PC);
+				WombImpregnate(V.PC, V.PC.pregType, V.PC.pregSource, V.PC.preg);
+			}
+		}
+		if (V.PC.dick !== 0) {
+			V.PC.geneticQuirks.wellHung = 2;
+		} else {
+			V.PC.balls = 0;
+			V.PC.scrotum = 0;
+			V.PC.prostate = 0;
+		}
+		V.PC.ovaryAge = V.PC.physicalAge;
+	}
+
+
+	function parentSetup() {
+		/** @type {Map<number, number>} */
+		const missingMap = new Map();
+
+		/** @param {FC.HumanState} slave */
+		function checkMissingParents(slave) {
+			const missingMom = missingMap.get(slave.mother);
+			if (missingMom) {
+				slave.mother = missingMom;
+			} else if (slave.mother > 0 && !getSlave(slave.mother)) {
+				missingMap.set(slave.mother, V.missingParentID);
+				V.missingParentID--;
+			}
+
+			const missingDad = missingMap.get(slave.father);
+			if (missingDad) {
+				slave.father = missingDad;
+			} else if (slave.father > 0 && !getSlave(slave.father)) {
+				missingMap.set(slave.father, V.missingParentID);
+				V.missingParentID--;
+			}
+		}
+		checkMissingParents(V.PC);
+		V.slaves.forEach(checkMissingParents);
+	}
+
+
+	function inbreedingCalc() {
+		const coeffSlaves = [];
+		const genePoolMap = {};
+		for (const slave of V.genePool) {
+			genePoolMap[slave.ID] = slave;
+		}
+		for (const slave of V.slaves) {
+			if (slave.newGamePlus === 0) {
+				slave.inbreedingCoeff = -1;
+				genePoolMap[slave.ID].inbreedingCoeff = -1;
+				coeffSlaves.push(slave);
+			}
+		}
+		const ibcoeffs = ibc.coeff_slaves(coeffSlaves);
+		for (const slave of coeffSlaves) {
+			slave.inbreedingCoeff = ibcoeffs[slave.ID];
+			genePoolMap[slave.ID].inbreedingCoeff = ibcoeffs[slave.ID];
+		}
+	}
+
+	function generateFSSlave(FS) {
+		let slave;
+		switch (FS) {
+			case "Supremacist":
+				V.oneTimeDisableDisability = 1;
+				slave = GenerateNewSlave("XX", {race: V.arcologies[0].FSSupremacistRace});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(0, 20), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "uncomfortable straps";
+				slave.collar = "uncomfortable leather";
+				slave.shoes = "flats";
+				slave.assignment = "please you";
+				break;
+			case "Subjugationist":
+				V.oneTimeDisableDisability = 1;
+				slave = GenerateNewSlave("XX", {race: V.arcologies[0].FSSubjugationistRace});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(0, 20), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.whoring = random(15, 35);
+				slave.clothes = "uncomfortable straps";
+				slave.collar = "uncomfortable leather";
+				slave.shoes = "flats";
+				slave.assignment = "whore";
+				break;
+			case "GenderRadicalist":
+				slave = GenerateNewSlave("XY", {disableDisability: 1, maxAge: 25});
+				slave.devotion = random(25, 45);
+				slave.trust = random(15, -15);
+				setHealth(slave, 100, 0, 0, 0, jsRandom(0, 20));
+				slave.face = random(0, 55);
+				slave.boobs += 100 * random(2, 4);
+				slave.butt += random(1, 2);
+				slave.attrXY = random(70, 90);
+				slave.attrXX = 0;
+				slave.skill.oral = random(35, 65);
+				slave.skill.anal = random(35, 65);
+				slave.skill.whoring = random(35, 65);
+				SoftenSexualFlaw(slave);
+				slave.clothes = "uncomfortable straps";
+				slave.collar = "stylish leather";
+				slave.shoes = "heels";
+				slave.assignment = "whore";
+				break;
+			case "GenderFundamentalist":
+				slave = GenerateNewSlave("XX", {minAge: V.fertilityAge, maxAge: 25, disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(55, 65), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.preg = random(1, 40);
+				slave.lactation = 1;
+				slave.lactationDuration = 2;
+				SetBellySize(slave);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "a nice maid outfit";
+				slave.collar = "tight steel";
+				slave.shoes = "flats";
+				slave.assignment = "be a servant";
+				break;
+			case "Paternalist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(55, 65);
+				setHealth(slave, jsRandom(55, 65), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.intelligence = random(0, 100);
+				slave.intelligenceImplant = 30;
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "conservative clothing";
+				slave.collar = "none";
+				slave.shoes = "flats";
+				slave.assignment = "be a servant";
+				break;
+			case "Degradationist":
+				slave = GenerateNewSlave("XX");
+				slave.devotion = 25;
+				slave.trust = -25;
+				setHealth(slave, jsRandom(0, 15), 0, 0, 0, 0);
+				slave.fuckdoll = 100;
+				slave.career = "a Fuckdoll";
+				slave.fetish = "mindbroken";
+				slave.boobs += 100 * random(10, 20);
+				slave.butt += random(2, 3);
+				slave.lips = random(2, 4);
+				slave.weight = random(-15, 15);
+				slave.skill.oral = 0;
+				slave.skill.anal = 0;
+				slave.skill.vaginal = 0;
+				slave.skill.entertainment = 0;
+				slave.skill.whoring = 0;
+				slave.behavioralFlaw = "none";
+				slave.sexualFlaw = "none";
+				slave.clothes = "a Fuckdoll suit";
+				slave.assignment = "please you";
+				break;
+			case "AssetExpansionist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.chem = 50;
+				slave.face = random(15, 100);
+				slave.boobs += 100 * random(10, 20);
+				slave.butt += random(2, 3);
+				slave.lips += random(0, 1);
+				if (slave.balls > 0) {
+					slave.balls++;
+				}
+				if (slave.dick > 0) {
+					slave.dick++;
+				}
+				slave.weight = random(15, 90);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = 0;
+				slave.anus = 0;
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "slutty jewelry";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "heels";
+				slave.assignment = "please you";
+				break;
+			case "SlimnessEnthusiast":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(55, 65);
+				setHealth(slave, jsRandom(55, 65), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.boobs = 100 * random(1, 4);
+				slave.butt = random(1, 2);
+				slave.weight = random(-25, -15);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = 0;
+				slave.anus = 0;
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "a leotard";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "flats";
+				slave.assignment = "please you";
+				break;
+			case "TransformationFetishist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(-15, 0), Math.max(normalRandInt(5, 3), 0), Math.max(normalRandInt(5, 3), 0), 0, 0);
+				slave.faceImplant = random(40, 70);
+				slave.face = Math.clamp(Math.trunc(slave.face + slave.faceImplant / 2), -100, 100);
+				slave.boobsImplant = 200 * random(4, 8);
+				slave.boobs += slave.boobsImplant;
+				slave.boobsImplantType = "normal";
+				slave.buttImplant = random(2, 4);
+				slave.butt += slave.buttImplant;
+				slave.buttImplantType = "normal";
+				slave.lipsImplant = random(1, 2);
+				slave.lips = Math.clamp(Math.trunc(slave.lipsImplant + 2), -3, 3);
+				slave.weight = random(-25, -15);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "a string bikini";
+				slave.collar = "shock punishment";
+				slave.shoes = "extreme heels";
+				slave.assignment = "whore";
+				break;
+			case "BodyPurist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(55, 65);
+				slave.trust = random(25, 45);
+				setHealth(slave, 100, 0, 0, 0, jsRandom(0, 20));
+				slave.face = random(15, 100);
+				slave.weight = random(-5, 5);
+				slave.muscles = random(10, 25);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "a nice maid outfit";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "heels";
+				slave.assignment = "be a servant";
+				break;
+			case "MaturityPreferentialist":
+				slave = GenerateNewSlave("XX", {
+					minAge: 36, maxAge: 39, ageOverridesPedoMode: 1, disableDisability: 1
+				});
+				slave.devotion = random(55, 65);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.boobs += 100 * random(1, 4);
+				slave.butt += random(1, 2);
+				slave.weight = random(-5, 90);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				SoftenBehavioralFlaw(slave);
+				slave.clothes = "a slutty maid outfit";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "heels";
+				slave.assignment = "be a servant";
+				break;
+			case "YouthPreferentialist":
+				slave = GenerateNewSlave("XX", {maxAge: 19, disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(55, 65);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.boobs = 100 * random(1, 4);
+				slave.butt = random(1, 3);
+				slave.weight = random(-25, 25);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = 0;
+				slave.anus = 0;
+				if (slave.vagina > -1) {
+					slave.skill.vaginal = 0;
+					slave.vagina = 0;
+				}
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "a schoolgirl outfit";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "heels";
+				slave.assignment = "be a servant";
+				break;
+			case "Pastoralist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.boobs += 100 * random(10, 20);
+				if (slave.balls > 0) {
+					slave.balls++;
+				}
+				slave.lactation = 2;
+				slave.lactationDuration = 2;
+				slave.clothes = "Western clothing";
+				slave.collar = "leather with cowbell";
+				slave.shoes = "flats";
+				slave.assignment = "get milked";
+				break;
+			case "PhysicalIdealist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(25, 45);
+				setHealth(slave, 100, 0, 0, 0, jsRandom(10, 40));
+				slave.muscles = random(50, 100);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.whoring = random(15, 35);
+				slave.clothes = "body oil";
+				slave.collar = "none";
+				slave.shoes = "none";
+				slave.assignment = "whore";
+				break;
+			case "ChattelReligionist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(55, 65);
+				slave.trust = random(55, 65);
+				setHealth(slave, jsRandom(0, 15), 0, 0, 0, 0);
+				if (slave.vagina === 0) {
+					slave.vagina++;
+				}
+				slave.skill.whoring = random(10, 20);
+				slave.behavioralFlaw = "none";
+				slave.behavioralQuirk = "sinful";
+				slave.clothes = "a chattel habit";
+				slave.collar = "heavy gold";
+				slave.shoes = "flats";
+				slave.assignment = "whore";
+				break;
+			case "RomanRevivalist":
+				slave = GenerateNewSlave("XX", {maxAge: 19, disableDisability: 1});
+				slave.devotion = 100;
+				slave.trust = random(55, 65);
+				setHealth(slave, 100, 0, 0, 0, jsRandom(10, 30));
+				slave.face = random(0, 55);
+				slave.muscles = random(25, 50);
+				slave.skill.combat = 1;
+				slave.behavioralFlaw = "none";
+				slave.behavioralQuirk = "fitness";
+				slave.behavioralFlaw = "none";
+				slave.clothes = "a toga";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "flats";
+				slave.assignment = "guard you";
+				V.BodyguardID = slave.ID;
+				break;
+			case "NeoImperialist":
+				slave = GenerateNewSlave("XX", {
+					minAge: 16, maxAge: 28, race: "white", disableDisability: 1
+				});
+				slave.devotion = 100;
+				slave.trust = random(55, 65);
+				setHealth(slave, 100, 0, 0, 0, jsRandom(10, 30));
+				slave.face = random(20, 75);
+				slave.muscles = random(25, 60);
+				slave.skill.combat = 1;
+				slave.behavioralFlaw = "none";
+				slave.behavioralQuirk = "fitness";
+				slave.behavioralFlaw = "none";
+				slave.skill.entertainment = random(15, 35);
+				slave.clothes = "a tight Imperial bodysuit";
+				slave.shoes = "flats";
+				slave.assignment = "guard you";
+				V.BodyguardID = slave.ID;
+				break;
+			case "AztecRevivalist":
+				slave = GenerateNewSlave(null, {race: "latina", nationality: "Mexican", disableDisability: 1});
+				slave.accent = 0;
+				slave.devotion = 75;
+				slave.trust = 75;
+				setHealth(slave, jsRandom(-20, 20), 0, 0, 0, 0);
+				slave.muscles = random(50, 75);
+				slave.skill.combat = 1;
+				slave.sexualFlaw = "malicious";
+				slave.behavioralQuirk = "none";
+				slave.clothes = "a huipil";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "none";
+				slave.assignment = "be your Head Girl";
+				V.HeadGirlID = slave.ID;
+				break;
+			case "EgyptianRevivalist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(25, 45);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.skill.entertainment = random(15, 35);
+				SoftenSexualFlaw(slave);
+				slave.clothes = "slutty jewelry";
+				slave.collar = "ancient Egyptian";
+				slave.shoes = "flats";
+				slave.assignment = "please you";
+				break;
+			case "EdoRevivalist":
+				slave = GenerateNewSlave(null, {race: "asian", nationality: "Japanese", disableDisability: 1});
+				slave.accent = 0;
+				slave.devotion = random(25, 45);
+				slave.trust = random(25, 45);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.intelligence = random(0, 100);
+				slave.intelligenceImplant = 30;
+				slave.skill.entertainment = 100;
+				slave.clothes = "a kimono";
+				slave.collar = "satin choker";
+				slave.shoes = "heels";
+				slave.assignment = "serve the public";
+				break;
+			case "ArabianRevivalist":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.intelligence = random(-15, 80);
+				slave.intelligenceImplant = 0;
+				slave.clothes = "harem gauze";
+				slave.collar = "uncomfortable leather";
+				slave.shoes = "flats";
+				slave.assignment = "take classes";
+				break;
+			case "ChineseRevivalist":
+				slave = GenerateNewSlave(null, {
+					race: "asian", nationality: "Chinese", minAge: 36, maxAge: 38, ageOverridesPedoMode: 1, disableDisability: 1
+				});
+				slave.devotion = random(55, 65);
+				slave.trust = random(25, 45);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(0, 55);
+				slave.accent = 0;
+				slave.intelligence = 100;
+				slave.intelligenceImplant = 30;
+				slave.skill.oral = 100;
+				slave.skill.anal = 100;
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = 100;
+				}
+				slave.skill.entertainment = 100;
+				slave.skill.whoring = 100;
+				SoftenBehavioralFlaw(slave);
+				SoftenSexualFlaw(slave);
+				slave.clothes = "a slutty qipao";
+				slave.collar = "pretty jewelry";
+				slave.shoes = "heels";
+				slave.assignment = "be your Head Girl";
+				V.HeadGirlID = slave.ID;
+				break;
+			case "Eugenics":
+				slave = GenerateNewSlave("XX", {disableDisability: 1});
+				slave.devotion = -100;
+				slave.trust = -100;
+				setHealth(slave, jsRandom(80, 90), 0, 0, 0, 0);
+				slave.intelligence = 100;
+				slave.intelligenceImplant = 30;
+				slave.face = 100;
+				slave.faceShape = "sensual";
+				slave.skill.oral = random(35, 75);
+				slave.skill.anal = random(35, 75);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(35, 75);
+				}
+				slave.skill.entertainment = random(15, 35);
+				slave.skill.whoring = 0;
+				SoftenSexualFlaw(slave);
+				slave.clothes = "a ball gown";
+				slave.shoes = "flats";
+				slave.assignment = "rest";
+				break;
+			case "Repopulationist":
+				slave = GenerateNewSlave("XX", {
+					minAge: V.fertilityAge + 3, maxAge: 25, ageOverridesPedoMode: 1, disableDisability: 1
+				});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(55, 65), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.preg = random(10, 40);
+				slave.pregType = random(3, 8);
+				slave.lactation = 1;
+				slave.lactationDuration = 2;
+				SetBellySize(slave);
+				slave.counter.birthsTotal = 5;
+				slave.bellySag = 20;
+				slave.bellySagPreg = 20;
+				if (slave.vagina > -1) {
+					slave.vagina = 4;
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "a nice maid outfit";
+				slave.shoes = "flats";
+				slave.assignment = "please you";
+				break;
+			case "HedonisticDecadence":
+				slave = GenerateNewSlave("XX", {maxAge: 25, disableDisability: 1});
+				slave.devotion = random(25, 45);
+				slave.trust = random(-25, -45);
+				setHealth(slave, jsRandom(-20, 20), 0, 0, 0, 0);
+				slave.face = random(15, 40);
+				slave.boobs += 100 * random(3, 6);
+				slave.butt += random(2, 5);
+				slave.weight = random(100, 200);
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				slave.anus = 2;
+				if (slave.vagina > -1) {
+					slave.skill.vaginal = random(15, 35);
+					slave.vagina = 3;
+				}
+				slave.skill.entertainment = 0;
+				slave.energy = random(60, 80);
+				slave.behavioralFlaw = "gluttonous";
+				slave.clothes = "attractive lingerie";
+				slave.shoes = "flats";
+				slave.diet = "fattening";
+				slave.rules.living = "luxurious";
+				slave.assignment = "rest";
+				break;
+			case "IntellectualDependency":
+				slave = GenerateNewSlave("XX", {minAge: 14, maxAge: 18, disableDisability: 1});
+				setHealth(slave, jsRandom(55, 65), 0, 0, 0, 0);
+				slave.devotion = random(45, 65);
+				slave.trust = random(-15, 45);
+				slave.face = random(30, 100);
+				slave.energy = 100;
+				slave.weight = random(-25, -15);
+				slave.skill.oral = 0;
+				slave.skill.anal = 0;
+				slave.skill.vaginal = 0;
+				slave.skill.entertainment = 0;
+				slave.skill.whoring = 0;
+				slave.intelligence = -100;
+				slave.intelligenceImplant = 0;
+				if (slave.vagina === 0) {
+					slave.vagina++;
+				}
+				slave.anus++;
+				break;
+			case "SlaveProfessionalism":
+				slave = GenerateNewSlave("XX", {minAge: 18, maxAge: 25, disableDisability: 1});
+				slave.devotion = 100;
+				slave.trust = 20;
+				setHealth(slave, 80, 0, 0, 0, jsRandom(10, 30));
+				slave.face = random(30, 100);
+				slave.energy = 10;
+				slave.weight = random(-15, 5);
+				slave.skill.oral = 100;
+				slave.skill.anal = 100;
+				slave.skill.vaginal = 100;
+				slave.skill.entertainment = 100;
+				slave.skill.whoring = 100;
+				slave.intelligence = 100;
+				slave.intelligenceImplant = 30;
+				slave.accent = 0;
+				SoftenBehavioralFlaw(slave);
+				SoftenSexualFlaw(slave);
+				slave.clothes = "a nice maid outfit";
+				slave.collar = "pretty jewelry";
+				slave.assignment = "be your Head Girl";
+				V.HeadGirlID = slave.ID;
+				break;
+			case "PetiteAdmiration":
+				slave = GenerateNewSlave("XX", {minAge: 14, maxAge: 18, disableDisability: 1});
+				slave.devotion = random(55, 65);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				if (slave.height >= 150) {
+					slave.height = Math.trunc(Height.random(slave, {limitMult: [-3, -1]}));
+					if (slave.height >= 150) {
+						slave.height = Math.trunc(Height.random(slave, {limitMult: [-4, -2]}));
+						if (slave.height >= 150) {
+							slave.height = random(90, 130);
+							slave.geneticQuirks.dwarfism = 2;
+						}
+					}
+				}
+				slave.skill.oral = random(35, 65);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "a succubus outfit";
+				slave.legAccessory = "long stockings";
+				slave.assignment = "please you";
+				break;
+			case "StatuesqueGlorification":
+				slave = GenerateNewSlave("XX", {
+					minAge: 18, maxAge: 30, ageOverridesPedoMode: 1, disableDisability: 1
+				});
+				slave.devotion = random(55, 65);
+				slave.trust = random(-15, 15);
+				setHealth(slave, jsRandom(25, 45), 0, 0, 0, 0);
+				slave.face = random(15, 100);
+				slave.weight = random(-5, 5);
+				slave.muscles = random(20, 40);
+				if (slave.height < 185) {
+					slave.height = Math.trunc(Height.random(slave, {limitMult: [1, 3]}));
+					if (slave.height < 185) {
+						slave.height = Math.trunc(Height.random(slave, {limitMult: [2, 4]}));
+						if (slave.height < 185) {
+							slave.height = random(200, 264);
+							slave.geneticQuirks.gigantism = 2;
+						}
+					}
+				}
+				slave.skill.oral = random(15, 35);
+				slave.skill.anal = random(15, 35);
+				if (slave.vagina > -1) {
+					if (slave.vagina === 0) {
+						slave.vagina++;
+					}
+					slave.skill.vaginal = random(15, 35);
+				}
+				slave.clothes = "slutty business attire";
+				slave.shoes = "heels";
+				slave.assignment = "please you";
+				break;
+			default:
+				r.push(`ERROR: bad arcology type`);
+		}
+		slave.origin = "You acquired $him along with the arcology.";
+		slave.career = "a slave";
+		return slave;
+	}
+};
diff --git a/src/events/intro/acquisition.tw b/src/events/intro/acquisition.tw
new file mode 100644
index 0000000000000000000000000000000000000000..15af72727f501a40741b9b601aae933abce6594e
--- /dev/null
+++ b/src/events/intro/acquisition.tw
@@ -0,0 +1,5 @@
+:: Acquisition [nobr]
+
+<<set $encyclopedia = "How to Play">>
+
+<<includeDOM App.Intro.acquisition()>>
\ No newline at end of file
diff --git a/src/events/intro/arcologySelection.js b/src/events/intro/arcologySelection.js
index c3d5327a51b05781a53286948cc610faf6e73cbf..150b3d9946e29bdad7c20e4b6bbc3f691cf3116e 100644
--- a/src/events/intro/arcologySelection.js
+++ b/src/events/intro/arcologySelection.js
@@ -217,117 +217,117 @@ App.Intro.generateEstablishedArcologies = function() {
 				case "Supremacist":
 					switch (arcology.race) {
 						case "white":
-							return setup.ArcologyNamesSupremacistWhite.random();
+							return App.Data.ArcologyNames.SupremacistWhite.random();
 						case "asian":
-							return setup.ArcologyNamesSupremacistAsian.random();
+							return App.Data.ArcologyNames.SupremacistAsian.random();
 						case "latina":
-							return setup.ArcologyNamesSupremacistLatina.random();
+							return App.Data.ArcologyNames.SupremacistLatina.random();
 						case "middle eastern":
-							return setup.ArcologyNamesSupremacistMiddleEastern.random();
+							return App.Data.ArcologyNames.SupremacistMiddleEastern.random();
 						case "black":
-							return setup.ArcologyNamesSupremacistBlack.random();
+							return App.Data.ArcologyNames.SupremacistBlack.random();
 						case "indo-aryan":
-							return setup.ArcologyNamesSupremacistIndoAryan.random();
+							return App.Data.ArcologyNames.SupremacistIndoAryan.random();
 						case "pacific islander":
-							return setup.ArcologyNamesSupremacistPacificIslander.random();
+							return App.Data.ArcologyNames.SupremacistPacificIslander.random();
 						case "malay":
-							return setup.ArcologyNamesSupremacistMalay.random();
+							return App.Data.ArcologyNames.SupremacistMalay.random();
 						case "amerindian":
-							return setup.ArcologyNamesSupremacistAmerindian.random();
+							return App.Data.ArcologyNames.SupremacistAmerindian.random();
 						case "southern european":
-							return setup.ArcologyNamesSupremacistSouthernEuropean.random();
+							return App.Data.ArcologyNames.SupremacistSouthernEuropean.random();
 						case "semitic":
-							return setup.ArcologyNamesSupremacistSemitic.random();
+							return App.Data.ArcologyNames.SupremacistSemitic.random();
 						default:
-							return setup.ArcologyNamesSupremacistMixedRace.random();
+							return App.Data.ArcologyNames.SupremacistMixedRace.random();
 					}
 				case "Subjugationist":
 					switch (arcology.race) {
 						case "white":
-							return setup.ArcologyNamesSubjugationistWhite.random();
+							return App.Data.ArcologyNames.SubjugationistWhite.random();
 						case "asian":
-							return setup.ArcologyNamesSubjugationistAsian.random();
+							return App.Data.ArcologyNames.SubjugationistAsian.random();
 						case "latina":
-							return setup.ArcologyNamesSubjugationistLatina.random();
+							return App.Data.ArcologyNames.SubjugationistLatina.random();
 						case "middle eastern":
-							return setup.ArcologyNamesSubjugationistMiddleEastern.random();
+							return App.Data.ArcologyNames.SubjugationistMiddleEastern.random();
 						case "black":
-							return setup.ArcologyNamesSubjugationistBlack.random();
+							return App.Data.ArcologyNames.SubjugationistBlack.random();
 						case "indo-aryan":
-							return setup.ArcologyNamesSubjugationistIndoAryan.random();
+							return App.Data.ArcologyNames.SubjugationistIndoAryan.random();
 						case "pacific islander":
-							return setup.ArcologyNamesSubjugationistPacificIslander.random();
+							return App.Data.ArcologyNames.SubjugationistPacificIslander.random();
 						case "malay":
-							return setup.ArcologyNamesSubjugationistMalay.random();
+							return App.Data.ArcologyNames.SubjugationistMalay.random();
 						case "amerindian":
-							return setup.ArcologyNamesSubjugationistAmerindian.random();
+							return App.Data.ArcologyNames.SubjugationistAmerindian.random();
 						case "southern european":
-							return setup.ArcologyNamesSubjugationistSouthernEuropean.random();
+							return App.Data.ArcologyNames.SubjugationistSouthernEuropean.random();
 						case "semitic":
-							return setup.ArcologyNamesSubjugationistSemitic.random();
+							return App.Data.ArcologyNames.SubjugationistSemitic.random();
 						default:
-							return setup.ArcologyNamesSubjugationistMixedRace.random();
+							return App.Data.ArcologyNames.SubjugationistMixedRace.random();
 					}
 				case "GenderRadicalist":
-					return setup.ArcologyNamesGenderRadicalist.random();
+					return App.Data.ArcologyNames.GenderRadicalist.random();
 				case "GenderFundamentalist":
-					return setup.ArcologyNamesGenderFundamentalist.random();
+					return App.Data.ArcologyNames.GenderFundamentalist.random();
 				case "Paternalist":
-					return setup.ArcologyNamesPaternalist.random();
+					return App.Data.ArcologyNames.Paternalist.random();
 				case "Degradationist":
-					return setup.ArcologyNamesDegradationist.random();
+					return App.Data.ArcologyNames.Degradationist.random();
 				case "AssetExpansionist":
-					return setup.ArcologyNamesAssetExpansionist.random();
+					return App.Data.ArcologyNames.AssetExpansionist.random();
 				case "SlimnessEnthusiast":
-					return setup.ArcologyNamesSlimnessEnthusiast.random();
+					return App.Data.ArcologyNames.SlimnessEnthusiast.random();
 				case "TransformationFetishist":
-					return setup.ArcologyNamesTransformationFetishist.random();
+					return App.Data.ArcologyNames.TransformationFetishist.random();
 				case "BodyPurist":
-					return setup.ArcologyNamesBodyPurist.random();
+					return App.Data.ArcologyNames.BodyPurist.random();
 				case "MaturityPreferentialist":
-					return setup.ArcologyNamesMaturityPreferentialist.random();
+					return App.Data.ArcologyNames.MaturityPreferentialist.random();
 				case "YouthPreferentialist":
 					if (V.pedo_mode === 1 || V.minimumSlaveAge < 6) {
-						return setup.ArcologyNamesYouthPreferentialistLow.random();
+						return App.Data.ArcologyNames.YouthPreferentialistLow.random();
 					} else if (V.minimumSlaveAge < 14) {
-						return setup.ArcologyNamesYouthPreferentialist.concat(setup.ArcologyNamesYouthPreferentialistLow).random();
+						return App.Data.ArcologyNames.YouthPreferentialist.concat(App.Data.ArcologyNames.YouthPreferentialistLow).random();
 					} else {
-						return setup.ArcologyNamesYouthPreferentialist.random();
+						return App.Data.ArcologyNames.YouthPreferentialist.random();
 					}
 				case "Pastoralist":
-					return setup.ArcologyNamesPastoralist.random();
+					return App.Data.ArcologyNames.Pastoralist.random();
 				case "PhysicalIdealist":
-					return setup.ArcologyNamesPhysicalIdealist.random();
+					return App.Data.ArcologyNames.PhysicalIdealist.random();
 				case "ChattelReligionist":
-					return setup.ArcologyNamesChattelReligionist.random();
+					return App.Data.ArcologyNames.ChattelReligionist.random();
 				case "RomanRevivalist":
-					return setup.ArcologyNamesRomanRevivalist.random();
+					return App.Data.ArcologyNames.RomanRevivalist.random();
 				case "NeoImperialist":
-					return setup.ArcologyNamesNeoImperialist.random();
+					return App.Data.ArcologyNames.NeoImperialist.random();
 				case "AztecRevivalist":
-					return setup.ArcologyNamesAztecRevivalist.random();
+					return App.Data.ArcologyNames.AztecRevivalist.random();
 				case "EgyptianRevivalist":
-					return setup.ArcologyNamesEgyptianRevivalist.random();
+					return App.Data.ArcologyNames.EgyptianRevivalist.random();
 				case "EdoRevivalist":
-					return setup.ArcologyNamesEdoRevivalist.random();
+					return App.Data.ArcologyNames.EdoRevivalist.random();
 				case "ArabianRevivalist":
-					return setup.ArcologyNamesArabianRevivalist.random();
+					return App.Data.ArcologyNames.ArabianRevivalist.random();
 				case "ChineseRevivalist":
-					return setup.ArcologyNamesChineseRevivalist.random();
+					return App.Data.ArcologyNames.ChineseRevivalist.random();
 				case "Repopulationist":
-					return setup.ArcologyNamesRepopulationist.random();
+					return App.Data.ArcologyNames.Repopulationist.random();
 				case "Eugenics":
-					return setup.ArcologyNamesEugenics.random();
+					return App.Data.ArcologyNames.Eugenics.random();
 				case "HedonisticDecadence":
-					return setup.ArcologyNamesHedonisticDecadence.random();
+					return App.Data.ArcologyNames.HedonisticDecadence.random();
 				case "IntellectualDependency":
-					return setup.ArcologyNamesIntellectualDependency.random();
+					return App.Data.ArcologyNames.IntellectualDependency.random();
 				case "SlaveProfessionalism":
-					return setup.ArcologyNamesSlaveProfessionalism.random();
+					return App.Data.ArcologyNames.SlaveProfessionalism.random();
 				case "PetiteAdmiration":
-					return setup.ArcologyNamesPetiteAdmiration.random();
+					return App.Data.ArcologyNames.PetiteAdmiration.random();
 				case "StatuesqueGlorification":
-					return setup.ArcologyNamesStatuesqueGlorification.random();
+					return App.Data.ArcologyNames.StatuesqueGlorification.random();
 				default:
 					return "Arcology X-4";
 			}
diff --git a/src/events/intro/customizeSlaveTrade/customizeSlaveTrade.js b/src/events/intro/customizeSlaveTrade/customizeSlaveTrade.js
index 0967be7a4b4b1133c861aacc63f97111be11fee2..c17a83331fa9aa8f4e1bb8dfea4d67f39680c0a0 100644
--- a/src/events/intro/customizeSlaveTrade/customizeSlaveTrade.js
+++ b/src/events/intro/customizeSlaveTrade/customizeSlaveTrade.js
@@ -382,6 +382,9 @@ App.UI.CustomSlaveTrade = function() {
 	}
 };
 
+/**
+ * @returns {HTMLElement}
+ */
 App.UI.nationalitiesDisplay = function() {
 	const p = document.createElement("p");
 
diff --git a/src/events/intro/initNationalities.js b/src/events/intro/initNationalities.js
index e870dbb31e606661d19f6f53d2302d66c7db3841..e484c6a4aa1b9bc18d5afc3c780f0caaf003f219 100644
--- a/src/events/intro/initNationalities.js
+++ b/src/events/intro/initNationalities.js
@@ -16,13 +16,6 @@ App.Intro.initNationalities = function() {
 			assistantTime: 0,
 			waterwayTime: 0
 		};
-		if (V.wasToggledBefore === 0) {
-			if (V.mercenaries === 1) {
-				V.mercFreeManpower = jsRandom(5, 20);
-			} else if (V.mercenaries > 1) {
-				V.mercFreeManpower = jsRandom(10, 30);
-			}
-		}
 	}
 
 	function applyPCQualities() {
diff --git a/src/events/intro/introSummary.js b/src/events/intro/introSummary.js
new file mode 100644
index 0000000000000000000000000000000000000000..1a2d27cbf306ef455dd495f79658350b9d6505c5
--- /dev/null
+++ b/src/events/intro/introSummary.js
@@ -0,0 +1,621 @@
+App.Intro.summary = function() {
+	const el = new DocumentFragment();
+
+	V.neighboringArcologies = variableAsNumber(V.neighboringArcologies, 0, 8, 3);
+	V.FSCreditCount = variableAsNumber(V.FSCreditCount, 4, 7, 5);
+	V.PC.actualAge = variableAsNumber(V.PC.actualAge, 14, 80, 35);
+	V.PC.birthWeek = variableAsNumber(V.PC.birthWeek, 0, 51, 0);
+
+	el.append(introContent());
+
+	V.minimumSlaveAge = variableAsNumber(V.minimumSlaveAge, 3, 18, 18);
+	V.retirementAge = variableAsNumber(V.retirementAge, 25, 120, 45);
+	V.fertilityAge = variableAsNumber(V.fertilityAge, 3, 18, 13);
+	V.potencyAge = variableAsNumber(V.potencyAge, 3, 18, 13);
+	V.PC.mother = Number(V.PC.mother);
+	V.PC.father = Number(V.PC.father);
+	if (V.freshPC === 1 || V.saveImported === 0) {
+		V.PC.origRace = V.PC.race;
+		V.PC.origSkin = V.PC.skin;
+		V.PC.origEye = V.PC.eye.right.iris; // needed for compatibility
+		V.PC.origHColor = V.PC.hColor;
+	}
+
+	App.UI.tabBar.handlePreSelectedTab(V.tabChoice.IntroSummary);
+	/**
+	 * @typedef {Object} siCategory
+	 * @property {string} title
+	 * @property {string} id
+	 * @property {DocumentFragment|HTMLElement} node
+	 * @property {Function} [onClick]
+	 */
+
+	/** @type {Array<siCategory>} */
+	const buttons = [
+		{
+			title: "World",
+			id: "world",
+			get node() { return worldContent(); }
+		},
+		{
+			title: "Slaves",
+			id: "slaves",
+			get node() { return slavesContent(); }
+		},
+		{
+			title: "Player Character",
+			id: "player",
+			get node() { return App.UI.Player.design(); }
+		},
+		{
+			title: "UI",
+			id: "interface",
+			get node() { return interfaceContent(); }
+		}
+	];
+
+	el.append(displayWithTabs());
+
+	return el;
+
+	function displayWithTabs() {
+		const el = new DocumentFragment();
+
+		const tabBar = document.createElement("div");
+		tabBar.classList.add("tab-bar");
+		const tabContents = new DocumentFragment();
+
+		for (const item of buttons) {
+			tabBar.append(makeTabButton(item));
+			tabContents.append(makeTabContents(item));
+		}
+
+		el.append(tabBar);
+
+		el.append(tabContents);
+
+		return el;
+
+		/**
+		 * @param {siCategory} item
+		 * @returns {HTMLElement}
+		 */
+		function makeTabButton(item) {
+			const btn = document.createElement("button");
+			btn.className = "tab-links";
+			btn.id = `tab ${item.id}`;
+			btn.innerHTML = item.title;
+			btn.onclick = (event) => {
+				App.UI.tabBar.openTab(event, item.id);
+				jQuery(`#content-${item.id}`).empty().append(item.node);
+				if (item.hasOwnProperty("onClick")){
+					item.onClick();
+				}
+			};
+
+			return btn;
+		}
+	}
+
+	/**
+	 * @param {siCategory} item
+	 * @returns {HTMLElement}
+	 */
+	function makeTabContents(item) {
+		const wrapperEl = document.createElement("div");
+		wrapperEl.id = item.id;
+		wrapperEl.classList.add("tab-content");
+
+		const classEl = document.createElement("div");
+		classEl.classList.add("content");
+
+		classEl.append(makeContentSpan(item));
+		wrapperEl.append(classEl);
+
+		return wrapperEl;
+	}
+
+	/**
+	 * @param {siCategory} item
+	 * @returns {HTMLElement}
+	 */
+	function makeContentSpan(item) {
+		const uniqueContent = document.createElement("span");
+		uniqueContent.id = `content-${item.id}`;
+
+		uniqueContent.append(item.node);
+		return uniqueContent;
+	}
+
+	function introContent() {
+		const el = new DocumentFragment();
+		App.UI.DOM.appendNewElement("div", el, `You may review your settings before clicking "Continue" to begin.`);
+
+		const linkArray = [];
+		linkArray.push(
+			App.UI.DOM.link(
+				"Continue",
+				() => {
+					if (V.freshPC === 1 || V.saveImported === 0) {
+						switch (V.PC.career) {
+							case "wealth":
+							case "celebrity":
+								if (V.PC.vagina === 1) {
+									V.PC.vagina = 2;
+								}
+								break;
+							case "capitalist":
+								V.PC.skill.trading = 100;
+								break;
+							case "mercenary":
+								V.PC.skill.warfare = 100;
+								break;
+							case "slaver":
+								V.PC.skill.slaving = 100;
+								break;
+							case "engineer":
+								V.PC.skill.engineering = 100;
+								break;
+							case "medicine":
+								V.PC.skill.medicine = 100;
+								break;
+							case "BlackHat":
+								V.PC.skill.hacking = 100;
+								break;
+							case "arcology owner":
+								V.PC.skill.trading = 100;
+								V.PC.skill.warfare = 100;
+								V.PC.skill.hacking = 100;
+								V.PC.skill.slaving = 100;
+								V.PC.skill.engineering = 100;
+								V.PC.skill.medicine = 100;
+								break;
+							case "escort":
+								if (V.PC.vagina === 1) {
+									V.PC.vagina = 4;
+								}
+								V.PC.anus = 1;
+								V.PC.clothes = "a slutty outfit";
+								V.PC.education = 15;
+								V.PC.skill.trading = 50;
+								V.PC.skill.warfare = -100;
+								V.PC.skill.slaving = -100;
+								V.PC.skill.engineering = -100;
+								V.PC.skill.medicine = 10;
+								V.PC.skill.hacking = 10;
+								break;
+							case "servant":
+								V.PC.clothes = "a nice maid outfit";
+								V.PC.education = 0;
+								if (V.PC.vagina === 1) {
+									V.PC.vagina = 4;
+								}
+								V.PC.skill.trading = -100;
+								V.PC.skill.warfare = -100;
+								V.PC.skill.slaving = -100;
+								V.PC.skill.engineering = -100;
+								V.PC.skill.medicine = -100;
+								V.PC.skill.hacking = -100;
+								break;
+							case "gang":
+								if (V.PC.vagina === 1) {
+									V.PC.vagina = 2;
+								}
+								V.PC.skill.trading = 50;
+								V.PC.skill.warfare = 50;
+								V.PC.skill.slaving = 50;
+								V.PC.skill.engineering = -100;
+								V.PC.skill.medicine = 0;
+								V.PC.skill.hacking = 50;
+						}
+					}
+					if (V.saveImported === 1 && V.freshPC === 0 && V.PC.rules.living !== "luxurious") {
+						if (V.PC.rules.living === "spare") {
+							V.PC.rules.living = "normal";
+						} else {
+							V.PC.rules.living = "luxurious";
+						}
+					} else if (V.PC.career === "wealth" || V.PC.career === "celebrity") {
+						V.PC.rules.living = "normal";
+					} else {
+						V.PC.rules.living = "spare";
+					}
+					App.Intro.initNationalities();
+					SectorCounts(); // Update AProsperityCap
+				},
+				[],
+				"Starting Girls"
+			)
+		);
+
+		if ((V.economy !== 100) || (V.seeDicks !== 25) || (V.continent !== "North America") || (V.internationalTrade !== 1) || (V.internationalVariety !== 1) || (V.seeRace !== 1) || (V.seeNationality !== 1) || (V.seeExtreme !== 0) || (V.seeCircumcision !== 1) || (V.seeAge !== 1) || (V.plot !== 1)) {
+			linkArray.push(
+				App.UI.DOM.link(
+					"restore defaults",
+					() => {
+						V.seeDicks = 25;
+						V.economy = 100;
+						V.continent = "North America";
+						V.internationalTrade = 1;
+						V.internationalVariety = 1;
+						V.seeRace = 1;
+						V.seeNationality = 1;
+						V.seeExtreme = 0;
+						V.seeCircumcision = 1;
+						V.seeAge = 1;
+						V.plot = 1;
+					},
+					[],
+					"Intro Summary"
+				)
+			);
+		}
+		linkArray.push(
+			App.UI.DOM.link(
+				"Cheat Start",
+				() => {
+					cashX(1000000, "cheating");
+					V.PC.rules.living = "luxurious";
+					repX(20000, "cheating");
+					V.dojo += 1;
+					V.cheatMode = 1;
+					V.seeDesk = 0;
+					V.seeFCNN = 0;
+					V.sortSlavesBy = "devotion";
+					V.sortSlavesOrder = "descending";
+					V.sortSlavesMain = 0;
+					V.rulesAssistantMain = 1;
+					Object.assign(
+						V.UI.slaveSummary.abbreviation,
+						{
+							devotion: 1,
+							rules: 1,
+							clothes: 2,
+							health: 1,
+							diet: 1,
+							drugs: 1,
+							race: 1,
+							nationality: 1,
+							genitalia: 1,
+							physicals: 1,
+							skills: 1,
+							mental: 2
+						}
+					);
+					V.PC.skill.trading = 100;
+					V.PC.skill.warfare = 100;
+					V.PC.skill.slaving = 100;
+					V.PC.skill.engineering = 100;
+					V.PC.skill.medicine = 100;
+					V.PC.skill.hacking = 100;
+					App.Intro.initNationalities();
+				},
+				[],
+				"Starting Girls",
+				"Intended for debugging: may have unexpected effects"
+			)
+		);
+		App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(linkArray));
+
+		return el;
+	}
+
+	function worldContent() {
+		const el = new DocumentFragment();
+		App.UI.DOM.appendNewElement("h2", el, "Economy");
+
+		let options = new App.UI.OptionsGroup();
+
+		V.localEcon = V.economy;
+		options.addOption("Economic climate", "baseDifficulty")
+			.addValue("Very Easy", 1, () => V.economy = 200)
+			.addValue("Easy", 2, () => V.economy = 125)
+			.addValue("Default Difficulty", 3, () => V.economy = 100)
+			.addValue("Hard", 4, () => V.economy = 80)
+			.addValue("Very Hard", 5, () => V.economy = 67);
+
+		if (V.difficultySwitch === 1) {
+			V.econAdvantage = -2;
+		}
+		if (V.difficultySwitch === 0) {
+			V.econRate = 0;
+		}
+		options.addOption("Economic forecast", "econRate").addComment("Some economic content requires this to be set to harder than vanilla")
+			.addValue("Vanilla", 0, () => V.difficultySwitch = 0)
+			.addValue("Easy", 1, () => V.difficultySwitch = 1)
+			.addValue("Default", 2, () => V.difficultySwitch = 1)
+			.addValue("Hard", 3, () => V.difficultySwitch = 1);
+
+		/* Not functional yet
+		All the things you need to run your arcology are getting more expensive
+		if (V.incomeMod === 0) {
+			while all forms of income <strong>remain static</strong>.
+				[[Easier|Intro Summary][V.incomeMod = 1]]
+		} else if (V.incomeMod === 1) {
+			while all forms of income <strong>rise but cannot keep pace</strong>.
+			[[Harder|Intro Summary][V.incomeMod = 0]] | [[Easier|Intro Summary][V.incomeMod = 2]]
+		} else {
+			but luckily all forms of income <strong>rise in lockstep</strong>.
+			[[Harder|Intro Summary][V.incomeMod = 1]]
+		}
+		*/
+
+		V.foodCost = Math.trunc(2500 / V.economy);
+		V.drugsCost = Math.trunc(10000 / V.economy);
+		V.rulesCost = Math.trunc(10000 / V.economy);
+		V.modCost = Math.trunc(5000 / V.economy);
+		V.surgeryCost = Math.trunc(30000 / V.economy);
+
+		if (!V.customVariety) {
+			options.addOption("You are using standardized slave trading channels.")
+				.customButton("Customize the slave trade", () => { V.customVariety = 1; V.customWA = 0; }, "Customize Slave Trade");
+
+			options.addOption("", "internationalTrade")
+				.addValue("Allow intercontinental trade", 1).on().customDescription("The slave trade is <strong>international,</strong> so a wider variety of slaves will be available.")
+				.addValue("Restrict the trade to continental", 0).off().customDescription("The slave trade is <strong>continental,</strong> so a narrower variety of slaves will be available.");
+
+			if (V.internationalTrade === 1) {
+				options.addOption("International slave variety is", "internationalVariety")
+					.addValue("Normalized national variety", 1).customDescription("<strong>normalized,</strong> so small nations will appear nearly as much as large ones.")
+					.addValue("Semi-realistic national variety", 0).customDescription("<strong>semi-realistic,</strong> so more populous nations will be more common.");
+			}
+		} else {
+			options.addOption("Nationality distributions are customized.")
+				.customButton("Adjust the slave trade", () => { V.customWA = 0; V.customVariety = 1; }, "Customize Slave Trade")
+				.customButton("Stop customizing", () => { delete V.customVariety; });
+		}
+		/* closes V.customVariety is defined */
+
+		if (V.customVariety) {
+			options.addCustom(App.UI.nationalitiesDisplay());
+		}
+
+		options.addOption("", "plot")
+			.addValue("Enable non-erotic events", 1).on().customDescription("Game mode: <strong>two-handed</strong>. Includes non-erotic events concerning the changing world.")
+			.addValue("Disable non-erotic events", 0).off().customDescription("Game mode: <strong>one-handed</strong>. No non-erotic events concerning the changing world.");
+
+		el.append(options.render());
+
+
+		App.UI.DOM.appendNewElement("h2", el, "The Free City");
+		options = new App.UI.OptionsGroup();
+
+		options.addOption(`The Free City features <strong>${V.neighboringArcologies}</strong> arcologies in addition to your own.`, "neighboringArcologies")
+			.showTextBox().addComment("Setting this to 0 will disable most content involving the rest of the Free City.");
+
+		if (V.targetArcology.fs === "New") {
+			options.addOption(`The Free City is located on <strong>${V.terrain}</strong> terrain.`, "terrain")
+				.addValueList([
+					["Urban", "urban"],
+					["Rural", "rural"],
+					["Ravine", "ravine"],
+					["Marine", "marine"],
+					["Oceanic", "oceanic"]
+				]);
+
+			if (V.terrain !== "oceanic") {
+				// TODO: this is from original, but seems unused?
+				options.addOption(`The Free City is located in <strong>${V.continent}</strong>.`, "continent")
+					.addValue("North America").addCallback(() => V.language = "English")
+					.addValue("South America").addCallback(() => V.language = "Spanish")
+					.addValue("Brazil").addCallback(() => V.language = "Portuguese")
+					.addValue("Western Europe").addCallback(() => V.language = "English")
+					.addValue("Central Europe").addCallback(() => V.language = "German")
+					.addValue("Eastern Europe").addCallback(() => V.language = "Russian")
+					.addValue("Southern Europe").addCallback(() => V.language = "Italian")
+					.addValue("Scandinavia").addCallback(() => V.language = "Norwegian")
+					.addValue("the Middle East").addCallback(() => V.language = "Arabic")
+					.addValue("Africa").addCallback(() => V.language = "Arabic")
+					.addValue("Asia").addCallback(() => V.language = "Chinese")
+					.addValue("Australia").addCallback(() => V.language = "English")
+					.addValue("Japan").addCallback(() => V.language = "Japanese");
+			}
+
+			options.addOption("The lingua franca of your arcology is", "language")
+				.showTextBox();
+		} else if (!["ArabianRevivalist", "AztecRevivalist", "ChineseRevivalist", "EdoRevivalist", "EgyptianRevivalist", "RomanRevivalist"].includes(V.targetArcology.fs)) {
+			options.addOption("The lingua franca of your arcology is", "language")
+				.addValueList(["English", "Spanish", "Arabic"]).showTextBox();
+		}
+
+		options.addOption(`The Free City could develop as many as <strong>${V.FSCreditCount}</strong> future societies.`, "FSCreditCount")
+			.showTextBox().addComment("5 is default, 4 behaves the same as pre-patch 0.9.9.0, max is 7. This option cannot be changed during the game.");
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, "Content");
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Proportion of slave girls with dicks", "seeDicks")
+			.addValueList([
+				["None (0%)", 0],
+				["Nearly none (1%)", 1],
+				["A few (10%)", 10],
+				["Some (25%)", 25],
+				["Half (50%)", 50],
+				["Lots (75%)", 75],
+				["Most (90%)", 90],
+				["Almost all (99%)", 99],
+				["All (100%)", 100]
+			]);
+
+		if (V.seeDicks === 0) {
+			options.addOption("Should you be able to surgically attach a penis to your female slaves and starting girls?", "makeDicks")
+				.addValue("Yes", 1).on().addValue("No", 0).off();
+		}
+
+		options.addOption("Slaves getting sick is", "seeIllness")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Pregnancy related content is", "seePreg")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Children born in game strictly adhering to dick content settings is", "seeDicksAffectsPregnancy")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		if (V.seeDicksAffectsPregnancy === 0) {
+			options.addOption("XX slaves only fathering daughters is", "adamPrinciple")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		}
+
+		options.addOption("Extreme pregnancy content like broodmothers is", "seeHyperPreg")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Advanced pregnancy complications such as miscarriage and premature birth are", "dangerousPregnancy")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Extreme content like amputation is", "seeExtreme")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Bestiality content is", "seeBestiality")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Watersports content is", "seePee")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Incest is", "seeIncest")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+
+		if (V.seeDicks !== 0 || V.makeDicks !== 0) {
+			options.addOption("Circumcision is", "seeCircumcision")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		}
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, "Mods");
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("The Special Force Mod is", "Toggle", V.SF)
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		options.addComment(`This mod is initially from anon1888 but expanded by SFanon offers a lategame special (started out as security but changed to special in order to try and reduce confusion with CrimeAnon's separate Security Expansion (SecExp) mod) force, that is triggered after week 72.
+		It is non-canon where it conflicts with canonical updates to the base game.`);
+
+		options.addOption("The Security Expansion Mod is", "secExpEnabled")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		options.addComment(`This mod introduces security and crime in the arcology, as well as attacks and battles.
+		The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.`);
+
+		el.append(options.render());
+		return el;
+	}
+
+	function slavesContent() {
+		const el = new DocumentFragment();
+		let options = new App.UI.OptionsGroup();
+
+		options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slaves can have alternate titles", "newDescriptions")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Mention ethnicity", "seeRace")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Mention nationality", "seeNationality")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Dynasties of enslaved royalties are", "realRoyalties")
+			.addValueList([
+				["Historical", 1],
+				["Random", 0]
+			]);
+
+		options.addOption("New slaves may have male names", "allowMaleSlaveNames").addComment("This only affects slave generation and not your ability to name your slaves.")
+			.addValue("Enabled", true).on().addValue("Disabled", false).off();
+
+		options.addOption("Schema for ordering slave names is", "surnameOrder")
+			.addValueList([
+				["Country of origin", 0],
+				["Name Surname", 1],
+				["Surname Name", 2]
+			]);
+
+		options.addOption("Family size", "limitFamilies").addComment("Controls acquisition of additional relatives, by means other than birth, for slaves with families.")
+			.addValue("Allow extended families", 0).on().addValue("Restrict family size (Vanilla Emulation)", 1).off();
+
+		options.addOption("Tracking distant relatives is", "showDistantRelatives")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Successive breeding resulting in sub-average slaves is", "inbreeding")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Family titles for relatives", "allowFamilyTitles")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slave assets affected by weight is", "weightAffectsAssets")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Diet will still affect asset size.");
+
+		options.addOption("Curative side effects are", "curativeSideEffects")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping")
+			.addValue("Yes", 0).on().addValue("No", 1).off();
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, "Age");
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Slave aging", "seeAge")
+			.addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off();
+
+		options.addOption("Slave age distribution", "pedo_mode").addComment("In loli mode most new slaves are under the age of 18. May not apply to custom slaves and slaves from specific events.")
+			.addValue("Loli mode", 1, () => V.minimumSlaveAge = 5).addValue("Normal mode", 0);
+
+		V.minimumSlaveAge = Math.clamp(V.minimumSlaveAge, 3, 18);
+		options.addOption("Girls appearing in the game will be no younger than", "minimumSlaveAge").showTextBox();
+
+		options.addOption(`Molestation of slaves younger than ${V.minimumSlaveAge} is`, "extremeUnderage")
+			.addValue("Permitted", 1).on().addValue("Forbidden", 0).off();
+
+		V.retirementAge = Math.clamp(V.retirementAge, V.minimumSlaveAge + 1, 120);
+		options.addOption("Initial retirement age will be at", "retirementAge")
+			.addComment("May cause issues with New Game and initial slaves if set below 45.").showTextBox();
+
+		V.fertilityAge = Math.clamp(V.fertilityAge, 3, 18);
+		options.addOption("Girls will not be able to become pregnant if their age is under", "fertilityAge").showTextBox();
+
+		V.potencyAge = Math.clamp(V.potencyAge, 3, 18);
+		options.addOption("Girls will not be able to impregnate others if their age is under", "potencyAge").showTextBox();
+
+		options.addOption("Precocious puberty is", "precociousPuberty").addComment("Under certain conditions they can become pregnant or inseminate others younger then normal age - V.fertilityAge, though they may also experience delayed puberty.")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Age penalties are", "AgePenalty").addComment("Job and career penalties due to age.")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Children growing as they age is", "loliGrow")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		el.append(options.render());
+
+		return el;
+	}
+
+	function interfaceContent() {
+		const el = new DocumentFragment();
+		let options = new App.UI.OptionsGroup();
+
+		options.addOption("Help tooltips are", "tooltipsEnabled")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment(`This is mostly for new players. <span class="exampleTooltip.noteworthy">Colored text</span> can have tooltips.`);
+
+		options.addOption("Accordion on week end defaults to", "useAccordion")
+			.addValue("Open", 0).on().addValue("Collapsed", 1).off();
+
+		options.addOption("Economic Tabs on weekly reports are", "useTabs")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, "Images");
+		el.append(App.UI.artOptions());
+
+		return el;
+	}
+};
diff --git a/src/events/intro/introSummary.tw b/src/events/intro/introSummary.tw
index bde3a0f223bd8aee69dc007426b13f5cb4e583e1..27d3c57173f853f6fb5737a6721f6d46862d7f44 100644
--- a/src/events/intro/introSummary.tw
+++ b/src/events/intro/introSummary.tw
@@ -1,839 +1,3 @@
 :: Intro Summary [nobr]
 
-<style>
-.active {
-	background-color: grey;
-}
-</style>
-
-<<set $neighboringArcologies = variableAsNumber($neighboringArcologies, 0, 8, 3)>>
-<<set $FSCreditCount = variableAsNumber($FSCreditCount, 4, 7, 5)>>
-<<set $PC.actualAge = variableAsNumber($PC.actualAge, 14, 80, 35)>>
-<<set $PC.birthWeek = variableAsNumber($PC.birthWeek, 0, 51, 0)>>
-
-You may review your settings before clicking "Continue" to begin.<br>
-<<link "Continue" "Starting Girls">>
-	<<if $freshPC == 1 || $saveImported == 0>>
-		<<switch $PC.career>>
-		<<case "wealth">>
-			<<if $PC.vagina == 1>>
-				<<set $PC.vagina = 2>>
-			<</if>>
-		<<case "capitalist">>
-			<<set $PC.skill.trading = 100>>
-		<<case "mercenary">>
-			<<set $PC.skill.warfare = 100>>
-		<<case "slaver">>
-			<<set $PC.skill.slaving = 100>>
-		<<case "engineer">>
-			<<set $PC.skill.engineering = 100>>
-		<<case "medicine">>
-			<<set $PC.skill.medicine = 100>>
-		<<case "celebrity">>
-			<<if $PC.vagina == 1>>
-				<<set $PC.vagina = 2>>
-			<</if>>
-		<<case "BlackHat">>
-			<<set $PC.skill.hacking = 100>>
-		<<case "arcology owner">>
-			<<set $PC.skill.trading = 100, $PC.skill.warfare = 100, $PC.skill.hacking = 100, $PC.skill.slaving = 100, $PC.skill.engineering = 100, $PC.skill.medicine = 100>>
-		<<case "escort">>
-			<<if $PC.vagina == 1>>
-				<<set $PC.vagina = 4>>
-			<</if>>
-			<<set $PC.anus = 1>>
-			<<set $PC.clothes = "a slutty outfit">>
-			<<set $PC.education = 15>>
-			<<set $PC.skill.trading = 50, $PC.skill.warfare = -100, $PC.skill.slaving = -100, $PC.skill.engineering = -100, $PC.skill.medicine = 10, $PC.skill.hacking = 10>>
-		<<case "servant">>
-			<<set $PC.clothes = "a nice maid outfit">>
-			<<set $PC.education = 0>>
-			<<if $PC.vagina == 1>>
-				<<set $PC.vagina = 4>>
-			<</if>>
-			<<set $PC.skill.trading = -100, $PC.skill.warfare = -100, $PC.skill.slaving = -100, $PC.skill.engineering = -100, $PC.skill.medicine = -100, $PC.skill.hacking = -100>>
-		<<case "gang">>
-			<<if $PC.vagina == 1>>
-				<<set $PC.vagina = 2>>
-			<</if>>
-			<<set $PC.skill.trading = 50, $PC.skill.warfare = 50, $PC.skill.slaving = 50, $PC.skill.engineering = -100, $PC.skill.medicine = 0, $PC.skill.hacking = 50>>
-		<</switch>>
-	<</if>>
-	<<if $saveImported == 1 && $freshPC == 0 && $PC.rules.living != "luxurious">>
-		<<if $PC.rules.living == "spare">>
-			<<set $PC.rules.living = "normal">>
-		<<else>>
-			<<set $PC.rules.living = "luxurious">>
-		<</if>>
-	<<elseif $PC.career == "wealth" || $PC.career == "celebrity">>
-		<<set $PC.rules.living = "normal">>
-	<<else>>
-		<<set $PC.rules.living = "spare">>
-	<</if>>
-	<<run App.Intro.initNationalities()>>
-	<<run SectorCounts()>> /* Update AProsperityCap */
-<</link>>
-<<if ($economy != 100) || ($seeDicks != 25) || ($continent != "North America") || ($internationalTrade != 1) || ($internationalVariety != 1) || ($seeRace != 1) || ($seeNationality != 1) || ($seeExtreme != 0) || ($seeCircumcision != 1) || ($seeAge != 1) || ($plot != 1)>>
-	| [[restore defaults|Intro Summary][$seeDicks = 25, $economy = 100, $continent = "North America", $internationalTrade = 1, $internationalVariety = 1, $seeRace = 1, $seeNationality = 1, $seeExtreme = 0, $seeCircumcision = 1, $seeAge = 1, $plot = 1]]
-<</if>>
-	| [[Cheat Start|Starting Girls][cashX(1000000, "cheating"), $PC.rules.living = "luxurious",repX(20000, "cheating"), $dojo += 1, $cheatMode = 1, $seeDesk = 0, $seeFCNN = 0, $sortSlavesBy = "devotion", $sortSlavesOrder = "descending", $sortSlavesMain = 0, $rulesAssistantMain = 1, $UI.slaveSummary.abbreviation = {devotion: 1, rules: 1, clothes: 2, health: 1, diet: 1, drugs: 1, race: 1, nationality: 1, genitalia: 1, physicals: 1, skills: 1, mental: 2}, $PC.skill.trading = 100, $PC.skill.warfare = 100, $PC.skill.slaving = 100, $PC.skill.engineering = 100, $PC.skill.medicine = 100, $PC.skill.hacking = 100, App.Intro.initNationalities()]] //Intended for debugging: may have unexpected effects//
-
-<<set $minimumSlaveAge = variableAsNumber($minimumSlaveAge, 3, 18, 18)>>
-<<set $retirementAge = variableAsNumber($retirementAge, 25, 120, 45)>>
-<<set $fertilityAge = variableAsNumber($fertilityAge, 3, 18, 13)>>
-<<set $potencyAge = variableAsNumber($potencyAge, 3, 18, 13)>>
-<<set $PC.mother = Number($PC.mother)>>
-<<set $PC.father = Number($PC.father)>>
-<<if $freshPC == 1 || $saveImported == 0>>
-	<<set $PC.origRace = $PC.race>>
-	<<set $PC.origSkin = $PC.skin>>
-	<<set $PC.origEye = $PC.eye.right.iris>> /* needed for compatibility */
-	<<set $PC.origHColor = $PC.hColor>>
-<</if>>
-
-<br><br>
-
-<<run App.UI.tabBar.handlePreSelectedTab($tabChoice.IntroSummary)>>
-
-<div class="tab-bar">
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'world')" id="tab world">World</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'slaves')" id="tab slaves">Slaves</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'player')" id="tab player">Player Character</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'interface')" id="tab interface">UI</button>
-</div>
-
-<div id="interface" class="tab-content">
-	<div class="content">
-
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("Help tooltips are", "tooltipsEnabled")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-	.addComment("This is mostly for new players. @@.exampleTooltip.noteworthy;Colored text@@ can have tooltips.")>>
-
-	<<run _options.addOption("Accordion on week end defaults to", "useAccordion")
-	.addValue("Open", 0).on().addValue("Collapsed", 1).off()>>
-
-	<<run _options.addOption("Economic Tabs on weekly reports are", "useTabs")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<includeDOM _options.render()>>
-
-	<h2>Images</h2>
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("Images are", "seeImages")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<includeDOM _options.render()>>
-
-	<<if $seeImages > 0>>
-		<div class="imageRef" style="width:200px;height:200px;margin-left:50px;">
-			<<= SlaveArt(BaseSlave(), 0, 0)>>
-		</div>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("", "imageChoice")
-		.addValueList([["Revamped embedded vector art", 3], ["Non-embedded vector art", 2], ["NoX/Deepmurk's vector art", 1], ["Shokushu's rendered image pack", 0]])>>
-
-		<<if $imageChoice === 1>>
-			<<run _options.addOption("").addComment('<span class="warning">Git compiled only, no exceptions.</span>')>>
-
-			<<run _options.addOption("Face artwork is", "seeFaces")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Highlights on shiny clothing are", "seeVectorArtHighlights")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Height scaling", "seeHeight")
-			.addValue("All images", 2).on().addValue("Small images", 1).neutral().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Clothing erection bulges are", "showClothingErection")
-			.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-		<<elseif $imageChoice === 0>>
-			<<run _options.addOption("").addComment(`You need """to"""
-				<a href="https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0">download the image pack</a>
-				"""and""" put the 'renders' folder into the resources/ folder where this html file is.`
-			)>>
-
-			<<run _options.addOption("Slave summary fetish images are", "seeMainFetishes")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-		<<elseif $imageChoice === 3>>
-			<<run _options.addOption("").addComment('<span class="warning">Git compiled only, no exceptions.</span>')>>
-
-			<<run _options.addOption("Clothing erection bulges are", "showClothingErection")
-			.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-		<</if>>
-
-		<<run _options.addOption("PA avatar art is", "seeAvatar")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Slave images in lists are", "seeSummaryImages")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Slave images in the weekly report are", "seeReportImages")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<includeDOM _options.render()>>
-	<</if>>
-	</div>
-</div>
-
-<div id="world" class="tab-content">
-	<div class="content">
-
-	<h2>Economy</h2>
-
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<set $localEcon = $economy>>
-	<<run _options.addOption("Economic climate", "baseDifficulty")
-	.addValue("Very Easy", 1, () => V.economy = 200).addValue("Easy", 2, () => V.economy = 125)
-	.addValue("Default Difficulty", 3, () => V.economy = 100).addValue("Hard", 4, () => V.economy = 80)
-	.addValue("Very Hard", 5, () => V.economy = 67)>>
-
-	<<if $difficultySwitch == 1>><<set $econAdvantage = -2>><</if>>
-	<<if $difficultySwitch == 0>><<set $econRate = 0>><</if>>
-	<<run _options.addOption("Economic forecast", "econRate").addComment("Some economic content requires this to be set to harder than vanilla")
-	.addValue("Vanilla", 0, () => V.difficultySwitch = 0).addValue("Easy", 1, () => V.difficultySwitch = 1)
-	.addValue("Default", 2, () => V.difficultySwitch = 1).addValue("Hard", 3, () => V.difficultySwitch = 1)>>
-
-	/* Not functional yet
-	All the things you need to run your arcology are getting more expensive
-	<<if $incomeMod == 0>>
-		while all forms of income ''remain static''.
-			[[Easier|Intro Summary][$incomeMod = 1]]
-	<<elseif $incomeMod == 1>>
-		while all forms of income ''rise but cannot keep pace''.
-		[[Harder|Intro Summary][$incomeMod = 0]] | [[Easier|Intro Summary][$incomeMod = 2]]
-	<<else>>
-		but luckily all forms of income ''rise in lockstep''.
-		[[Harder|Intro Summary][$incomeMod = 1]]
-	<</if>>
-	*/
-
-	<<set $foodCost = Math.trunc(2500/$economy)>>
-	<<set $drugsCost = Math.trunc(10000/$economy)>>
-	<<set $rulesCost = Math.trunc(10000/$economy)>>
-	<<set $modCost = Math.trunc(5000/$economy)>>
-	<<set $surgeryCost = Math.trunc(30000/$economy)>>
-
-	<<if ndef $customVariety>>
-		<<run _options.addOption("You are using standardized slave trading channels.")
-		.customButton("Customize the slave trade", () => {V.customVariety = 1; V.customWA = 0}, "Customize Slave Trade")>>
-
-		<<run _options.addOption("", "internationalTrade")
-		.addValue("Allow intercontinental trade", 1).on().customDescription("The slave trade is ''international,'' so a wider variety of slaves will be available.")
-		.addValue("Restrict the trade to continental", 0).off().customDescription("The slave trade is ''continental,'' so a narrower variety of slaves will be available.")>>
-
-		<<if $internationalTrade == 1>>
-			<<run _options.addOption("International slave variety is", "internationalVariety")
-			.addValue("Normalized national variety", 1).customDescription("''normalized,'' so small nations will appear nearly as much as large ones.")
-			.addValue("Semi-realistic national variety", 0).customDescription("''semi-realistic,'' so more populous nations will be more common.")>>
-		<</if>>
-	<<else>>
-		<<run _options.addOption("Nationality distributions are customized.")
-		.customButton("Adjust the slave trade", () => {V.customWA = 0; V.customVariety = 1}, "Customize Slave Trade")
-		.customButton("Stop customizing", () => {delete V.customVariety})>>
-
-		<<run _options.addOption("").addCustomElement(`
-			<hr style="margin:0">
-			<<includeDOM App.UI.nationalitiesDisplay()>>
-			<br style="clear:both"><hr style="margin:0">
-		`)>>
-	<</if>> /* closes $customVariety is defined */
-
-	<<run _options.addOption("", "plot")
-	.addValue("Enable non-erotic events", 1).on().customDescription("Game mode: ''two-handed''. Includes non-erotic events concerning the changing world.")
-	.addValue("Disable non-erotic events", 0).off().customDescription("Game mode: ''one-handed''. No non-erotic events concerning the changing world.")>>
-
-	<<includeDOM _options.render()>>
-
-	<h2>The Free City</h2>
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("The Free City features ''$neighboringArcologies'' arcologies in addition to your own.", "neighboringArcologies")
-	.showTextBox().addComment("Setting this to 0 will disable most content involving the rest of the Free City.")>>
-
-	<<if $targetArcology.fs == "New">>
-		<<run _options.addOption("The Free City is located on ''$terrain'' terrain.", "terrain")
-		.addValueList([["Urban", "urban"], ["Rural", "rural"], ["Ravine", "ravine"], ["Marine", "marine"], ["Oceanic", "oceanic"]])>>
-
-		<<if $terrain !== "oceanic">>
-			<<set _option = _options.addOption("The Free City is located in ''$continent''.", "continent")
-			.addValue("North America").addCallback(() => V.language = "English")
-			.addValue("South America").addCallback(() => V.language = "Spanish")
-			.addValue("Brazil").addCallback(() => V.language = "Portuguese")
-			.addValue("Western Europe").addCallback(() => V.language = "English")
-			.addValue("Central Europe").addCallback(() => V.language = "German")
-			.addValue("Eastern Europe").addCallback(() => V.language = "Russian")
-			.addValue("Southern Europe").addCallback(() => V.language = "Italian")
-			.addValue("Scandinavia").addCallback(() => V.language = "Norwegian")
-			.addValue("the Middle East").addCallback(() => V.language = "Arabic")
-			.addValue("Africa").addCallback(() => V.language = "Arabic")
-			.addValue("Asia").addCallback(() => V.language = "Chinese")
-			.addValue("Australia").addCallback(() => V.language = "English")
-			.addValue("Japan").addCallback(() => V.language = "Japanese")>>
-		<</if>>
-
-		<<run _options.addOption("The lingua franca of your arcology is", "language")
-		.showTextBox()>>
-	<<elseif !["ArabianRevivalist", "AztecRevivalist", "ChineseRevivalist", "EdoRevivalist", "EgyptianRevivalist", "RomanRevivalist"].includes($targetArcology.fs)>>
-		<<run _options.addOption("The lingua franca of your arcology is", "language")
-		.addValueList(["English", "Spanish", "Arabic"]).showTextBox()>>
-	<</if>>
-
-	<<run _options.addOption("The Free City could develop as many as ''$FSCreditCount'' future societies.", "FSCreditCount")
-	.showTextBox().addComment("5 is default, 4 behaves the same as pre-patch 0.9.9.0, max is 7. This option cannot be changed during the game.")>>
-
-	<<includeDOM _options.render()>>
-
-	<h2>Content</h2>
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("Proportion of slave girls with dicks", "seeDicks")
-	.addValueList([["None (0%)", 0], ["Nearly none (1%)", 1], ["A few (10%)", 10], ["Some (25%)", 25], ["Half (50%)", 50], ["Lots (75%)", 75], ["Most (90%)", 90], ["Almost all (99%)", 99], ["All (100%)", 100]])>>
-
-	<<if $seeDicks === 0>>
-		<<run _options.addOption("Should you be able to surgically attach a penis to your female slaves and starting girls?", "makeDicks")
-		.addValue("Yes", 1).on().addValue("No", 0).off()>>
-	<</if>>
-
-	<<run _options.addOption("Slaves getting sick is", "seeIllness")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Pregnancy related content is", "seePreg")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Children born in game strictly adhering to dick content settings is", "seeDicksAffectsPregnancy")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<if $seeDicksAffectsPregnancy === 0>>
-		<<run _options.addOption("XX slaves only fathering daughters is", "adamPrinciple")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-	<</if>>
-
-	<<run _options.addOption("Extreme pregnancy content like broodmothers is", "seeHyperPreg")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Advanced pregnancy complications such as miscarriage and premature birth are", "dangerousPregnancy")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Extreme content like amputation is", "seeExtreme")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Bestiality content is", "seeBestiality")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-
-	<<run _options.addOption("Watersports content is", "seePee")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-
-	<<run _options.addOption("Incest is", "seeIncest")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-
-	<<if $seeDicks !== 0 || $makeDicks !== 0>>
-		<<run _options.addOption("Circumcision is", "seeCircumcision")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-	<</if>>
-
-	<<includeDOM _options.render()>>
-
-	<h2>Mods</h2>
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("The Special Force Mod is", "Toggle", $SF)
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-	.addComment("<div>This mod is initially from anon1888 but expanded by SFanon offers a lategame special (started out as security but changed to special in order to try and reduce confusion with CrimeAnon's separate Security Expansion (SecExp) mod) force, that is triggered after week 72.</div>
-	<div>It is non-canon where it conflicts with canonical updates to the base game.</div>")>>
-
-	<<run _options.addOption("The Security Expansion Mod is", "secExpEnabled")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-	.addComment("<div>This mod introduces security and crime in the arcology, as well as attacks and battles.</div>
-	<div>The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.</div>")>>
-
-	<<includeDOM _options.render()>>
-	</div>
-</div>
-
-<div id="slaves" class="tab-content">
-	<div class="content">
-
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Slaves can have alternate titles", "newDescriptions")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Mention ethnicity", "seeRace")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Mention nationality", "seeNationality")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Dynasties of enslaved royalties are", "realRoyalties")
-	.addValueList([["Historical", 1], ["Random", 0]])>>
-
-	<<run _options.addOption("New slaves may have male names", "allowMaleSlaveNames").addComment("This only affects slave generation and not your ability to name your slaves.")
-	.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-
-	<<run _options.addOption("Schema for ordering slave names is", "surnameOrder")
-	.addValueList([["Country of origin", 0], ["Name Surname", 1], ["Surname Name", 2]])>>
-
-	<<run _options.addOption("Family size", "limitFamilies").addComment("Controls acquisition of additional relatives, by means other than birth, for slaves with families.")
-	.addValue("Allow extended families", 0).on().addValue("Restrict family size (Vanilla Emulation)", 1).off()>>
-
-	<<run _options.addOption("Tracking distant relatives is", "showDistantRelatives")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Successive breeding resulting in sub-average slaves is", "inbreeding")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Family titles for relatives", "allowFamilyTitles")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Slave assets affected by weight is", "weightAffectsAssets")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-	.addComment("Diet will still affect asset size.")>>
-
-	<<run _options.addOption("Curative side effects are", "curativeSideEffects")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping")
-	.addValue("Yes", 0).on().addValue("No", 1).off()>>
-
-	<<includeDOM _options.render()>>
-
-	<h2>Age</h2>
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<run _options.addOption("Slave aging", "seeAge")
-	.addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Slave age distribution", "pedo_mode").addComment("In loli mode most new slaves are under the age of 18. May not apply to custom slaves and slaves from specific events.")
-	.addValue("Loli mode", 1, () => V.minimumSlaveAge = 5).addValue("Normal mode", 0)>>
-
-	<<set $minimumSlaveAge = Math.clamp($minimumSlaveAge, 3, 18)>>
-	<<run _options.addOption("Girls appearing in the game will be no younger than", "minimumSlaveAge").showTextBox()>>
-
-	<<run _options.addOption("Molestation of slaves younger than $minimumSlaveAge is", "extremeUnderage")
-	.addValue("Permitted", 1).on().addValue("Forbidden", 0).off()>>
-
-	<<set $retirementAge = Math.clamp($retirementAge, $minimumSlaveAge + 1, 120)>>
-	<<run _options.addOption("Initial retirement age will be at", "retirementAge")
-	.addComment("May cause issues with New Game and initial slaves if set below 45.").showTextBox()>>
-
-	<<set $fertilityAge = Math.clamp($fertilityAge, 3, 18)>>
-	<<run _options.addOption("Girls will not be able to become pregnant if their age is under", "fertilityAge").showTextBox()>>
-
-	<<set $potencyAge = Math.clamp($potencyAge, 3, 18)>>
-	<<run _options.addOption("Girls will not be able to impregnate others if their age is under", "potencyAge").showTextBox()>>
-
-	<<run _options.addOption("Precocious puberty is", "precociousPuberty").addComment("Under certain conditions they can become pregnant or inseminate others younger then normal age - $fertilityAge, though they may also experience delayed puberty.")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Age penalties are", "AgePenalty").addComment("Job and career penalties due to age.")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<run _options.addOption("Children growing as they age is", "loliGrow")
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-	<<includeDOM _options.render()>>
-	</div>
-</div>
-
-<div id="player" class="tab-content">
-	<div class="content">
-
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<if $freshPC == 1 || $saveImported == 0>>
-		<<run _options.addOption("You are a", "title", $PC)
-		.addValue("Masculine Master", 1, () => V.PC.genes = "XY").addValue("Feminine Mistress", 0, () => V.PC.genes = "XX")>>
-
-		<<run _options.addOption(`Everyone calls you <b>${PlayerName()}.</b>`)>>
-		<<run _options.addOption("Your given name is", "slaveName", $PC).showTextBox()>>
-
-		<<if $PC.slaveSurname === 0>>
-			<<run _option = _options.addOption("And no surname", "slaveSurname", $PC)
-			.addValue("Add a surname", "Anon")
-			.addComment("Surnames cannot be changed during the game outside of special circumstances.")>>
-		<<else>>
-			<<run _option = _options.addOption("And your surname is", "slaveSurname", $PC).showTextBox()
-			.addValue("Go by a single name", 0)
-			.addComment("Surnames cannot be changed during the game outside of special circumstances.")>>
-		<</if>>
-
-		<<set $PC.physicalAge = $PC.actualAge, $PC.visualAge = $PC.actualAge>>
-		<<run _options.addOption("You are", "actualAge", $PC).showTextBox()
-		.addRange(25, 35, "<", "Surprisingly young").addRange(40, 50, "<", "Entering middle age")
-		.addRange(55, 65, "<", "Well into middle age").addRange(70, 65, ">=", "Old")>>
-
-		<<run _options.addOption("Your birthday was ''$PC.birthWeek'' weeks ago.", "birthWeek", $PC).showTextBox()>>
-
-		<<run _options.addOption("Player aging is", "playerAging")
-		.addValue("Enabled", 2).on().addValue("Celebrate birthdays, but don't age.", 1).neutral().addValue("Disabled", 0).off()
-		.addComment("This option cannot be changed during the game.")>>
-
-		<<if def $PC.customTitle>>
-			<<run _options.addOption("Custom title", "customTitle", $PC).showTextBox()
-			.addValue("Reset to default", "", () => {delete $PC.customTitle; delete $PC.customTitleLisp;})>>
-
-			<<run _options.addOption("Lisped custom title", "customTitleLisp", $PC).showTextBox()
-			.addComment('If using a custom title, select Master or Mistress to set the gender of your title. Make sure to replace your "s"s with "th"s to have working lisps in your lisped title.')>>
-		<<else>>
-			<<run _options.addOption("Custom title", "customTitle", $PC)
-			.addValue("Set custom title", "Master", () => $PC.customTitleLisp = 'Mather')>>
-		<</if>>
-
-		<<run _options.addOption("Your nationality is", "nationality", $PC).showTextBox()
-		.addComment("For best result capitalize it.")>>
-
-		<<run _options.addOption("Your race is", "race", $PC).showTextBox()
-		.addValueList([["White", "white"], ["Asian", "asian"], ["Latina", "latina"], ["Middle Eastern", "middle eastern"],
-			["Black", "black"], ["Semitic", "semitic"], ["Southern European", "southern european"], ["Indo-Aryan", "indo-aryan"],
-			["Amerindian", "amerindian"], ["Pacific Islander", "pacific islander"], ["Malay", "malay"], ["Mixed Race", "mixed race"]])>>
-
-		<<run _options.addOption("Your skin tone is", "skin", $PC).showTextBox()
-		.addValueList([["Pure White", "pure white"], ["Ivory", "ivory"], ["White", "white"], ["Extremely Pale", "extremely pale"],
-			["Very Pale", "very pale"], ["Pale", "pale"], ["Extremely Fair", "extremely fair"], ["Very Fair", "very fair"],
-			["Fair", "fair"], ["Light", "light"], ["Light Olive", "light olive"], ["Tan", "tan"], ["Olive", "olive"], ["Bronze", "bronze"],
-			["Dark Olive", "dark olive"], ["Dark", "dark"], ["Light Beige", "light beige"], ["Beige", "beige"],
-			["Dark Beige", "dark beige"], ["Light Brown", "light brown"], ["Brown", "brown"], ["Dark Brown", "dark brown"],
-			["Black", "black"], ["Ebony", "ebony"], ["Pure Black", "pure black"]])>>
-
-		<<run _options.addOption("Your body", "markings", $PC)
-		.addValueList([["Is clear of blemishes", "none"], ["Has light freckling", "freckles"], ["Has heavy freckling", "heavily freckled"]])>>
-
-		<<run _options.addOption("Your eyes are", "origColor", $PC.eye).showTextBox()>>
-
-		<<run _options.addOption("Your hair is", "hColor", $PC).showTextBox()>>
-
-		<<run _options.addOption("Your face is", "faceShape", $PC)
-		.addValueList([["Normal", "normal"], ["Androgynous", "androgynous"], ["Masculine", "masculine"], ["Cute", "cute"], ["Sensual", "sensual"], ["Exotic", "exotic"]])>>
-
-		<<run _options.addOption("Your preferred refreshment is", "refreshment", $PC).showTextBox()
-		.addValue("Cigars", "cigar", () => {V.PC.refreshmentType = 0})
-		.addValue("Whiskey", "whiskey", () => {V.PC.refreshmentType = 1})>>
-
-		<<set _option = _options.addOption("Which you", "refreshmentType", $PC)
-		.addValueList([["Smoke", 0], ["Drink", 1], ["Eat", 2], ["Snort", 3], ["Inject", 4], ["Pop", 5], ["Orally dissolve", 6]])>>
-
-		<<if $PC.refreshmentType == 0>>
-			<<run _option.addComment('"Smoked" must fit into the following sentence: "I smoked a $PC.refreshment" to fit events properly.')>>
-		<<elseif $PC.refreshmentType == 5>>
-			<<run _option.addComment('"Popped" must fit into the following sentence: "I shook the bottle of $PC.refreshment" to fit events properly.')>>
-		<<elseif $PC.refreshmentType == 6>>
-			<<run _option.addComment('"Orally Dissolved" must fit into the following sentence: "I placed a tab of $PC.refreshment under my tongue" to fit events properly.')>>
-		<</if>>
-
-		<<set _option = _options.addOption("Before you came to the Free Cities, you were a", "career", $PC)>>
-		<<if $PC.career == "arcology owner">>
-			<<run _option.addValue("Arcology owner", "arcology owner")>>
-		<<else>>
-			<<run _option.addValueList([["Member of the idle wealthy", "wealth"], ["Business leader", "capitalist"],
-				["Mercenary", "mercenary"], ["Slaver", "slaver"], ["Engineer", "engineer"], ["Doctor", "medicine"], ["Hacker", "BlackHat"],
-				["Minor celebrity", "celebrity"], ["Escort", "escort"], ["Servant", "servant"], ["Gang leader", "gang"]]
-			)>>
-			<<if $secExpEnabled > 0>>
-				<<switch $PC.career>>
-				<<case "capitalist">>
-					<<run _option.addComment("<div>@@.yellowgreen;The propaganda hub's upgrades will be cheaper.@@</div>")>>
-				<<case "mercenary">>
-					<<run _option.addComment("<div>@@.green;Easier to maintain security@@ and @@.yellowgreen;the security HQ's upgrades will be cheaper.@@</div>")>>
-				<<case "slaver">>
-					<<run _option.addComment("<div>@@.green;Easier to maintain authority@@ and @@.yellowgreen;the security HQ's upgrades will be cheaper.@@</div>")>>
-				<<case "engineer">>
-					<<run _option.addComment("<div>@@.yellowgreen;construction and upgrade of facilities will be cheaper.@@</div>")>>
-				<<case "medicine">>
-					<<run _option.addComment("<div>@@.yellowgreen;Drug upgrades will be cheaper.@@</div>")>>
-				<<case "celebrity">>
-					<<run _option.addComment("<div>@@.yellowgreen;The propaganda hub's upgrades will be cheaper.@@</div>")>>
-				<<case "escort">>
-					<<run _option.addComment("<div>@@.red;Harder to maintain authority.@@</div>")>>
-				<<case "servant">>
-					<<run _option.addComment("<div>@@.red;Harder to maintain authority.@@</div>")>>
-				<<case "gang">>
-					<<run _option.addComment("<div>@@.green;Easier to maintain authority@@ and @@.yellowgreen;the security HQ's upgrades will be cheaper.@@</div>")>>
-				<<case "BlackHat">>
-					<<run _option.addComment("<div>@@.red;Harder to maintain authority.@@</div>")>>
-				<<default>>
-					<<run _option.addComment("<div>@@.red;Harder to maintain authority,@@ but @@.yellowgreen;the propaganda hub's upgrades will be cheaper.@@</div>")>>
-				<</switch>>
-			<</if>>
-		<</if>>
-
-		<<run _options.addOption("It is rumored that you acquired your arcology through", "rumor", $PC)
-		.addValueList([["Wealth", "wealth"], ["Hard work", "diligence"], ["Force", "force"], ["Social engineering", "social engineering"],
-			["Blind luck", "luck"]]
-		)>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>Sexuality</h2>
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<if $PC.vagina !== -1 && $PC.dick !== 0>>
-			<<set _vagina_penis = 2>>
-		<<elseif $PC.vagina !== -1>>
-			<<set _vagina_penis = 1>>
-		<<else>>
-			<<set _vagina_penis = 0>>
-		<</if>>
-
-		<<set _option = _options.addOption("You have a", "vagina_penis", State.temporary)
-		.addValue("Penis", 0, () => {
-			V.PC.preg = 0;
-			V.PC.pregType = 0;
-			V.PC.dick = 4;
-			V.PC.balls = 3;
-			V.PC.scrotum = 3;
-			V.PC.prostate = 1;
-			V.PC.vagina = -1;
-			V.PC.ovaries = 0;
-		}).addValue("Vagina", 1, () => {
-			V.PC.dick = 0;
-			V.PC.balls = 0;
-			V.PC.scrotum = 0;
-			V.PC.prostate = 0;
-			V.PC.vagina = 1;
-			V.PC.ovaries = 1
-		}).addValue("Penis and Vagina", 2, () => {
-			V.PC.dick = 4;
-			V.PC.balls = 3;
-			V.PC.scrotum = 3;
-			V.PC.prostate = 1;
-			V.PC.vagina = 1;
-			V.PC.ovaries = 1
-		})>>
-		<<if _vagina_penis === 0>>
-			<<run _option.addComment("Standard sex scenes; easiest reputation maintenance.")>>
-		<<elseif _vagina_penis === 1>>
-			<<run _option.addComment("Sex scene variations; most difficult reputation maintenance.")>>
-		<<else>>
-			<<run _option.addComment("Sex scene variations; more difficult reputation maintenance; some unique opportunities, especially with breasts.")>>
-		<</if>>
-
-		<<if $PC.vagina !== -1>>
-			<<set _option = _options.addOption("You are", "preg", $PC)
-			.addValue("Taking contraceptives", -1, () => {V.PC.pregType = 0; V.PC.labor = 0;})
-			.addValue("Not taking contraceptives", 0, () => {V.PC.pregType = 0; V.PC.labor = 0;})
-			.addRange(16, 37, "<=", "Pregnant").addCallback(() => {V.PC.pregType = 1; V.PC.labor = 0;})
-			.addRange(40, 42, "<=", "Ready to drop").addCallback(() => {V.PC.pregType = 1; V.PC.labor = 0;})
-			.addRange(43, 42, ">", "Ready to drop with octuplets").addCallback(() => {V.PC.pregType = 8; V.PC.labor = 1;})>>
-			<<if $PC.preg == -1>>
-				<<run _option.addComment("You can't get pregnant, however there will be a slight increase to living expenses.")>>
-			<</if>>
-
-			<<if $PC.counter.birthsTotal > 0>>
-				<<run _options.addOption("").addComment("You have given birth to ''$PC.counter.birthsTotal'' babies.")>>
-			<</if>>
-
-			<<set _option = _options.addOption("Hormone effects", "pregMood", $PC)
-			.addValueList([["None", 0], ["Caring and motherly", 1], ["Aggressive and domineering", 2]])>>
-			<<if $PC.pregMood === 1>>
-				<<run _option.addComment("Sex scene alterations; slaves will trust you more, but may try to take advantage of your mercy.")>>
-			<<elseif $PC.pregMood === 2>>
-				<<run _option.addComment("Sex scene alterations; slaves will fear you more, but will become more submissive to you.")>>
-			<</if>>
-		<</if>>
-
-		<<if $PC.title === 1 && $PC.boobs <= 100>>
-			<<set _option = _options.addOption("Your chest is", "boobs", $PC).addValue("Manly", 100, () => V.PC.boobsImplant = 0)>>
-		<<else>>
-			<<set _option = _options.addOption("Your breasts are", "boobs", $PC).addValue("Flat", 100, () => V.PC.boobsImplant = 0)>>
-		<</if>>
-		<<run _option.addValueList([["C-cups", 500], ["DD-cups", 900], ["F-cups", 1100], ["G-cups", 1300]])>>
-
-		<<if $PC.boobs >= 500>>
-			<<run _options.addOption("Your breasts are", "boobsImplant", $PC)
-			.addValueList([["All natural", 0], ["Fake", 400]])>>
-		<</if>>
-
-		<<includeDOM _options.render()>>
-	<<else>>
-
-		You are a
-		<<if $PC.title == 1>>
-			masculine ''Master''
-		<<else>>
-			feminine ''Mistress'
-		<</if>>
-		and everyone that matters calls you	<<= PlayerName()>>.
-
-		You are $PC.actualAge years old which is
-		<<if $PC.actualAge >= 65>>
-			''old''.
-		<<elseif $PC.actualAge >= 50>>
-			''well into middle age''.
-		<<elseif $PC.actualAge >= 35>>
-			''entering middle age''.
-		<<else>>
-			''surprisingly young''.
-		<</if>>
-
-		<p>
-			You are a $PC.nationality $PC.race with	<<if $PC.markings == "heavily freckled">>heavily freckled<<elseif $PC.markings == "freckles">>lightly freckled<<else>>clear<</if>> $PC.skin skin, $PC.hColor hair and <<print App.Desc.eyesColor($PC)>>. You have a $PC.faceShape face.
-		</p>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-		<<run _options.addOption("Player aging is", "playerAging")
-		.addValue("Enabled", 2).on().addValue("Celebrate birthdays, but don't age.", 1).neutral().addValue("Disabled", 0).off()
-		.addComment("This option cannot be changed during the game.")>>
-
-		<<if def $PC.customTitle>>
-			<<run _options.addOption("Custom title", "customTitle", $PC).showTextBox()
-			.addValue("Reset to default", "", () => {delete $PC.customTitle; delete $PC.customTitleLisp;})>>
-
-			<<run _options.addOption("Lisped custom title", "customTitleLisp", $PC).showTextBox()
-			.addComment('If using a custom title, select Master or Mistress to set the gender of your title. Make sure to replace your "s"s with "th"s to have working lisps in your lisped title.')>>
-		<<else>>
-			<<run _options.addOption("Custom title", "customTitle", $PC)
-			.addValue("Set custom title", "Master", () => $PC.customTitleLisp = 'Mather')>>
-		<</if>>
-
-		<<run _options.addOption("Your preferred refreshment is", "refreshment", $PC).showTextBox()
-		.addValue("Cigars", "cigar", () => {V.PC.refreshmentType = 0})
-		.addValue("Whiskey", "whiskey", () => {V.PC.refreshmentType = 1})>>
-
-		<<set _option = _options.addOption("Which you", "refreshmentType", $PC)
-		.addValueList([["Smoke", 0], ["Drink", 1], ["Eat", 2], ["Snort", 3], ["Inject", 4], ["Pop", 5], ["Orally dissolve", 6]])>>
-
-		<<if $PC.refreshmentType == 0>>
-			<<run _option.addComment('"Smoked" must fit into the following sentence: "I smoked a $PC.refreshment" to fit events properly.')>>
-		<<elseif $PC.refreshmentType == 5>>
-			<<run _option.addComment('"Popped" must fit into the following sentence: "I shook the bottle of $PC.refreshment" to fit events properly.')>>
-		<<elseif $PC.refreshmentType == 6>>
-			<<run _option.addComment('"Orally Dissolved" must fit into the following sentence: "I placed a tab of $PC.refreshment under my tongue" to fit events properly.')>>
-		<</if>>
-		<<includeDOM _options.render()>>
-
-		<p>
-		<<switch $PC.career>>
-		<<case "wealth">>
-			Prior to being an arcology owner, you were a member of the idle wealthy.
-		<<case "capitalist">>
-			Prior to being an arcology owner, you were a business leader.
-		<<case "mercenary">>
-			Prior to being an arcology owner, you were a mercenary.
-		<<case "slaver">>
-			Prior to being an arcology owner, you were a slaver.
-		<<case "engineer">>
-			Prior to being an arcology owner, you were an engineer.
-		<<case "medicine">>
-			Prior to being an arcology owner, you were a surgeon.
-		<<case "celebrity">>
-			Prior to being an arcology owner, you were a minor celebrity.
-		<<case "BlackHat">>
-			Prior to being an arcology owner, you specialized in cracking databases and making mockeries of cyber security.
-		<<case "arcology owner">>
-			Being an arcology owner defines your life now.
-		<<case "escort">>
-			Prior to being an arcology owner, you knew how to survive off your looks and body.
-		<<case "servant">>
-			Prior to being an arcology owner, you served a well-off master<<if $PC.counter.birthMaster >= 2>> and bore him several children<</if>>.
-		<<case "gang">>
-			Prior to being an arcology owner, you were the leader of a ruthless gang.
-		<</switch>>
-
-		Word in the arcology is you acquired it through
-		<<switch $PC.rumor>>
-		<<case "wealth">>
-			a rather ridiculous amount of money.
-		<<case "diligence">>
-			sheer effort.
-		<<case "force">>
-			brutal force.
-		<<case "social engineering">>
-			clever social manipulation.
-		<<case "luck">>
-			blind luck.
-		<</switch>>
-		</p>
-
-		<p>
-		You have a
-		<<if $PC.vagina != -1 && $PC.dick != 0>>
-			penis and vagina
-		<<elseif $PC.dick != 0>>
-			penis.
-		<<elseif $PC.vagina != -1>>
-			vagina
-		<</if>>
-		<<if $PC.vagina != -1>>
-			and are
-			<<if $PC.pregWeek < 0>>
-				recovering from your last pregnancy.
-			<<elseif $PC.preg == -2>>
-				infertile.
-			<<elseif $PC.preg == -1>>
-				taking contraceptives.
-			<<elseif $PC.preg == 0>>
-				fertile.
-			<<elseif $PC.preg > 37>>
-				extremely pregnant.
-			<<elseif $PC.preg > 0>>
-				pregnant.
-			<</if>>
-			<<if $PC.preg > 20 || $PC.counter.birthsTotal > 0>>
-				<<if $PC.pregMood == 1>>
-					You tend to be caring and motherly when you're pregnant.
-					[[Change to no change|Intro Summary][$PC.pregMood = 0]] | [[Change to aggressive|Intro Summary][$PC.pregMood = 2]]
-				<<elseif $PC.pregMood == 0>>
-					Pregnancy doesn't really affect your mood.
-					[[Change to motherly|Intro Summary][$PC.pregMood = 1]] | [[Change to aggressive|Intro Summary][$PC.pregMood = 2]]
-				<<else>>
-					You tend to be very demanding and aggressive when you're pregnant.
-					[[Change to no change|Intro Summary][$PC.pregMood = 0]] | [[Change to motherly|Intro Summary][$PC.pregMood = 1]]
-				<</if>>
-			<<else>>
-				<<if $PC.pregMood == 1>>
-					You tend to be caring and motherly when you're hormonal.
-					[[Change to no change|Intro Summary][$PC.pregMood = 0]] | [[Change to aggressive|Intro Summary][$PC.pregMood = 2]]
-				<<elseif $PC.pregMood == 0>>
-					Your mood isn't tied to your hormones.
-					[[Change to motherly|Intro Summary][$PC.pregMood = 1]] | [[Change to aggressive|Intro Summary][$PC.pregMood = 2]]
-				<<else>>
-					You tend to be very demanding and aggressive when you're hormonal.
-					[[Change to no change|Intro Summary][$PC.pregMood = 0]] | [[Change to motherly|Intro Summary][$PC.pregMood = 1]]
-				<</if>>
-			<</if>>
-			<<if $PC.counter.birthsTotal > 0>>
-				You have given birth to $PC.counter.birthsTotal babies.
-			<</if>>
-		<</if>>
-
-		<<if $PC.boobs >= 300>>
-			You have a	<<if $PC.title > 0>>masculine<<else>>feminine<</if>> body with
-			<<if $PC.boobs >= 1400>>
-				giant<<if $PC.boobsImplant != 0>>, fake<</if>> cow tits.
-			<<elseif $PC.boobs >= 1200>>
-				huge	<<if $PC.boobsImplant != 0>>fake	<</if>>breasts.
-			<<elseif $PC.boobs >= 1000>>
-				big	<<if $PC.boobsImplant != 0>>fake	<</if>>breasts.
-			<<elseif $PC.boobs >= 800>>
-				noticeable breasts.
-			<<elseif $PC.boobs >= 650>>
-				unremarkable breasts.
-			<<elseif $PC.boobs >= 500>>
-				average breasts.
-			<<else>>
-				small breasts.
-			<</if>>
-		<<else>>
-			<<if $PC.title > 0>>
-				You have a manly chest.
-			<<else>>
-				You are flat as a board.
-			<</if>>
-		<</if>>
-		</p>
-	<</if>>
-
-	</div>
-</div>
+<<includeDOM App.Intro.summary()>>
\ No newline at end of file
diff --git a/src/events/intro/pcAppearance.js b/src/events/intro/pcAppearance.js
new file mode 100644
index 0000000000000000000000000000000000000000..96b5024ccaee1d0cc7e6bd95e6ca3d56a2f23e11
--- /dev/null
+++ b/src/events/intro/pcAppearance.js
@@ -0,0 +1,882 @@
+App.UI.Player = {};
+
+App.UI.Player.appearance = function(options) {
+	options.addOption("Your nationality is", "nationality", V.PC).showTextBox()
+		.addValueList(Object.keys(App.Data.SlaveSummary.short.nationality))
+		.addComment("For best result capitalize it.");
+
+	options.addOption("Your race is", "race", V.PC).showTextBox()
+		.addValueList(Array.from(setup.filterRaces, (k => [k, k.toLowerCase()])));
+
+	if (V.cheatMode) {
+		options.addOption("Your race is", "origRace", V.PC).showTextBox()
+			.addValueList(Array.from(setup.filterRaces, (k => [k, k.toLowerCase()])));
+	}
+
+	options.addOption("Your skin tone is", "skin", V.PC).showTextBox()
+		.addValueList(makeAList(setup.naturalSkins));
+
+	if (V.cheatMode) {
+		options.addOption("Your genetic skin tone is", "origSkin", V.PC).showTextBox()
+			.addValueList(makeAList(setup.naturalSkins));
+	}
+
+	options.addOption("Your body", "markings", V.PC)
+		.addValueList([["Is clear of blemishes", "none"], ["Has light freckling", "freckles"], ["Has heavy freckling", "heavily freckled"]]);
+
+	options.addOption("Your genetic eye color is", "origColor", V.PC.eye).showTextBox()
+		.addValueList(makeAList(App.Medicine.Modification.eyeColor.map(color => color.value)));
+
+	if (V.cheatMode) {
+		options.addOption("Your original hair is", "origHColor", V.PC).showTextBox()
+			.addValueList(makeAList(App.Medicine.Modification.Color.Primary.map(color => color.value)));
+	} else {
+		options.addOption("Your hair is", "hColor", V.PC).showTextBox()
+			.addValueList(makeAList(App.Medicine.Modification.Color.Primary.map(color => color.value)));
+	}
+
+	function makeAList(iterable) {
+		return Array.from(iterable, (k => [capFirstChar(k), k]));
+	}
+};
+
+App.UI.Player.refreshmentChoice = function(options) {
+	options.addOption("Your preferred refreshment is", "refreshment", V.PC).showTextBox()
+		.addValue("Cigars", "cigar", () => { V.PC.refreshmentType = 0; })
+		.addValue("Whiskey", "whiskey", () => { V.PC.refreshmentType = 1; });
+
+	const option = options.addOption("Which you", "refreshmentType", V.PC)
+		.addValueList(Array.from(App.Data.player.refreshmentType, (v, i) => [v, i]));
+
+	let comment = `Flavor only; no mechanical effect. If entering a custom refreshment, please assign proper usage.`;
+	if (V.PC.refreshmentType === 0) {
+		comment += ` "Smoked" must fit into the following sentence: "I smoked a ${V.PC.refreshment}" to fit events properly.`;
+	} else if (V.PC.refreshmentType === 5) {
+		comment += ` "Popped" must fit into the following sentence: "I shook the bottle of ${V.PC.refreshment}" to fit events properly.`;
+	} else if (V.PC.refreshmentType === 6) {
+		comment += ` "Orally Dissolved" must fit into the following sentence: "I placed a tab of ${V.PC.refreshment} under my tongue" to fit events properly.`;
+	}
+	option.addComment(comment);
+};
+
+App.UI.Player.names = function(options) {
+	options.addOption(`Everyone calls you <b>${PlayerName()}.</b>`);
+	options.addOption("Your given name is", "slaveName", V.PC).showTextBox();
+
+	if (V.cheatMode) {
+		options.addOption("Birth Name", "birthName", V.PC).showTextBox();
+	}
+
+	if (V.PC.slaveSurname === 0) {
+		options.addOption("And no surname", "slaveSurname", V.PC)
+			.addValue("Add a surname", "Anon")
+			.addComment("Surnames cannot be changed during the game outside of special circumstances.");
+	} else {
+		options.addOption("And your surname is", "slaveSurname", V.PC).showTextBox()
+			.addValue("Go by a single name", 0)
+			.addComment("Surnames cannot be changed during the game outside of special circumstances.");
+		if (V.cheatMode) {
+			options.addOption("Birth Surname", "birthSurname", V.PC).showTextBox();
+		}
+	}
+};
+
+App.UI.Player.design = function() {
+	const el = new DocumentFragment();
+	let options = new App.UI.OptionsGroup();
+	let option;
+	let r;
+	let linkArray;
+	const allowEdits = (V.freshPC === 1 || V.saveImported === 0 || V.cheatMode);
+
+	// Title / age
+	if (allowEdits) {
+		options.addOption("You are a", "title", V.PC)
+			.addValue("Masculine Master", 1, () => V.PC.genes = "XY").addValue("Feminine Mistress", 0, () => V.PC.genes = "XX");
+
+		App.UI.Player.names(options);
+
+		V.PC.physicalAge = V.PC.actualAge;
+		V.PC.visualAge = V.PC.actualAge;
+		if (V.cheatMode) {
+			options.addOption("Actual Age", "actualAge", V.PC).showTextBox();
+			options.addOption("Physical Age", "physicalAge", V.PC).showTextBox();
+			options.addOption("Visual Age", "visualAge", V.PC).showTextBox();
+			options.addOption("Ovary Age", "ovaryAge", V.PC).showTextBox();
+			options.addOption("Age Implant", "ageImplant", V.PC).addValue("Age altering surgery", 1).on().addValue("No surgery", 0).off();
+		} else {
+			options.addOption("You are", "actualAge", V.PC).showTextBox()
+				.addRange(25, 35, "<", "Surprisingly young").addRange(40, 50, "<", "Entering middle age")
+				.addRange(55, 65, "<", "Well into middle age").addRange(70, 65, ">=", "Old");
+
+			options.addOption(`Your birthday was <strong>${V.PC.birthWeek}</strong> weeks ago.`, "birthWeek", V.PC).showTextBox();
+		}
+	} else {
+		r = [];
+		r.push(`You are a`);
+		if (V.PC.title === 1) {
+			r.push(`masculine <strong>Master</strong>`);
+		} else {
+			r.push(`feminine ''Mistress'`);
+		}
+		r.push(`and everyone that matters calls you	${PlayerName()}.`);
+
+		r.push(`You are ${V.PC.actualAge} years old which is`);
+		if (V.PC.actualAge >= 65) {
+			r.push(`<strong>old</strong>.`);
+		} else if (V.PC.actualAge >= 50) {
+			r.push(`<strong>well into middle age</strong>.`);
+		} else if (V.PC.actualAge >= 35) {
+			r.push(`<strong>entering middle age</strong>.`);
+		} else {
+			r.push(`<strong>surprisingly young</strong>.`);
+		}
+		App.Events.addNode(el, r, "p");
+	}
+
+	option = options.addOption("Player aging is", "playerAging")
+		.addValue("Enabled", 2).on().addValue("Celebrate birthdays, but don't age.", 1).neutral().addValue("Disabled", 0).off();
+	if (!V.cheatMode) {
+		option.addComment("This option cannot be changed during the game.");
+	}
+
+	if (V.PC.customTitle) {
+		options.addOption("Custom title", "customTitle", V.PC).showTextBox()
+			.addValue("Reset to default", "", () => { delete V.PC.customTitle; delete V.PC.customTitleLisp; });
+
+		options.addOption("Lisped custom title", "customTitleLisp", V.PC).showTextBox()
+			.addComment('If using a custom title, select Master or Mistress to set the gender of your title. Make sure to replace your "s"s with "th"s to have working lisps in your lisped title.');
+	} else {
+		options.addOption("Custom title", "customTitle", V.PC)
+			.addValue("Set custom title", "Master", () => V.PC.customTitleLisp = 'Mather');
+	}
+
+	// Appearance
+	if (allowEdits) {
+		App.UI.Player.appearance(options);
+
+		options.addOption("Your face is", "faceShape", V.PC)
+			.addValueList([
+				["Normal", "normal"],
+				["Androgynous", "androgynous"],
+				["Masculine", "masculine"],
+				["Cute", "cute"],
+				["Sensual", "sensual"],
+				["Exotic", "exotic"]
+			]);
+	} else {
+		r = [];
+
+		r.push(`You are a ${V.PC.nationality} ${V.PC.race} with`);
+		if (V.PC.markings === "heavily freckled") {
+			r.push(`heavily freckled`);
+		} else if (V.PC.markings === "freckles") {
+			r.push(`lightly freckled`);
+		} else {
+			r.push(`clear`);
+		}
+		r.push(`${V.PC.skin} skin, ${V.PC.hColor} hair and ${App.Desc.eyesColor(V.PC)}. You have a ${V.PC.faceShape} face.`);
+		App.Events.addNode(el, r, "p");
+	}
+
+	// Refresh
+	App.UI.Player.refreshmentChoice(options);
+
+	// History
+	if (allowEdits) {
+		option = options.addOption("Before you came to the Free Cities, you were a", "career", V.PC);
+		if (V.PC.career === "arcology owner") {
+			option.addValue("Arcology owner", "arcology owner");
+		} else {
+			option.addValueList([
+				["Member of the idle wealthy", "wealth"],
+				["Business leader", "capitalist"],
+				["Mercenary", "mercenary"],
+				["Slaver", "slaver"],
+				["Engineer", "engineer"],
+				["Doctor", "medicine"],
+				["Hacker", "BlackHat"],
+				["Minor celebrity", "celebrity"],
+				["Escort", "escort"],
+				["Servant", "servant"],
+				["Gang leader", "gang"]]
+			);
+			if (V.secExpEnabled > 0) {
+				switch (V.PC.career) {
+					case "capitalist":
+						option.addComment(`<div><span class="yellowgreen">The propaganda hub's upgrades will be cheaper.</span></div>`);
+						break;
+					case "mercenary":
+						option.addComment(`<div><span class="green">Easier to maintain security</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
+						break;
+					case "slaver":
+						option.addComment(`<div><span class="green">Easier to maintain authority</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
+						break;
+					case "engineer":
+						option.addComment(`<div><span class="yellowgreen">construction and upgrade of facilities will be cheaper.</span></div>`);
+						break;
+					case "medicine":
+						option.addComment(`<div><span class="yellowgreen">Drug upgrades will be cheaper.</span></div>`);
+						break;
+					case "celebrity":
+						option.addComment(`<div><span class="yellowgreen">The propaganda hub's upgrades will be cheaper.</span></div>`);
+						break;
+					case "escort":
+						option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
+						break;
+					case "servant":
+						option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
+						break;
+					case "gang":
+						option.addComment(`<div><span class="green">Easier to maintain authority</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
+						break;
+					case "BlackHat":
+						option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
+						break;
+					default:
+						option.addComment(`<div><span class="red">Harder to maintain authority,</span> but <span class="yellowgreen">the propaganda hub's upgrades will be cheaper.</span></div>`);
+				}
+			}
+		}
+
+		options.addOption("It is rumored that you acquired your arcology through", "rumor", V.PC)
+			.addValueList([
+				["Wealth", "wealth"],
+				["Hard work", "diligence"],
+				["Force", "force"],
+				["Social engineering", "social engineering"],
+				["Blind luck", "luck"]
+			]);
+
+		el.append(options.render());
+	} else {
+		r = [];
+		switch (V.PC.career) {
+			case "wealth":
+				r.push(`Prior to being an arcology owner, you were a member of the idle wealthy.`);
+				break;
+			case "capitalist":
+				r.push(`Prior to being an arcology owner, you were a business leader.`);
+				break;
+			case "mercenary":
+				r.push(`Prior to being an arcology owner, you were a mercenary.`);
+				break;
+			case "slaver":
+				r.push(`Prior to being an arcology owner, you were a slaver.`);
+				break;
+			case "engineer":
+				r.push(`Prior to being an arcology owner, you were an engineer.`);
+				break;
+			case "medicine":
+				r.push(`Prior to being an arcology owner, you were a surgeon.`);
+				break;
+			case "celebrity":
+				r.push(`Prior to being an arcology owner, you were a minor celebrity.`);
+				break;
+			case "BlackHat":
+				r.push(`Prior to being an arcology owner, you specialized in cracking databases and making mockeries of cyber security.`);
+				break;
+			case "arcology owner":
+				r.push(`Being an arcology owner defines your life now.`);
+				break;
+			case "escort":
+				r.push(`Prior to being an arcology owner, you knew how to survive off your looks and body.`);
+				break;
+			case "servant":
+				r.push(`Prior to being an arcology owner, you served a well-off`);
+				if (V.PC.counter.birthMaster >= 2) {
+					r.push(`master and bore him several children.`);
+				} else {
+					r.push(`master.`);
+				}
+				break;
+			case "gang":
+				r.push(`Prior to being an arcology owner, you were the leader of a ruthless gang.`);
+				break;
+		}
+
+		r.push(`Word in the arcology is you acquired it through`);
+		switch (V.PC.rumor) {
+			case "wealth":
+				r.push(`a rather ridiculous amount of money.`);
+				break;
+			case "diligence":
+				r.push(`sheer effort.`);
+				break;
+			case "force":
+				r.push(`brutal force.`);
+				break;
+			case "social engineering":
+				r.push(`clever social manipulation.`);
+				break;
+			case "luck":
+				r.push(`blind luck.`);
+				break;
+		}
+		App.Events.addNode(el, r, "p");
+	}
+
+	// Sexuality
+	App.UI.DOM.appendNewElement("h2", el, "Sexuality");
+
+	if (allowEdits) {
+		options = new App.UI.OptionsGroup();
+
+		if (V.PC.vagina !== -1 && V.PC.dick !== 0) {
+			State.temporary.vaginaPenis = 2;
+		} else if (V.PC.vagina !== -1) {
+			State.temporary.vaginaPenis = 1;
+		} else {
+			State.temporary.vaginaPenis = 0;
+		}
+
+		option = options.addOption("You have a", "vaginaPenis", State.temporary)
+			.addValue("Penis", 0, () => {
+				V.PC.preg = 0;
+				V.PC.pregType = 0;
+				V.PC.dick = 4;
+				V.PC.balls = 3;
+				V.PC.scrotum = 3;
+				V.PC.prostate = 1;
+				V.PC.vagina = -1;
+				V.PC.ovaries = 0;
+			}).addValue("Vagina", 1, () => {
+				V.PC.dick = 0;
+				V.PC.balls = 0;
+				V.PC.scrotum = 0;
+				V.PC.prostate = 0;
+				V.PC.vagina = 1;
+				V.PC.ovaries = 1;
+			}).addValue("Penis and Vagina", 2, () => {
+				V.PC.dick = 4;
+				V.PC.balls = 3;
+				V.PC.scrotum = 3;
+				V.PC.prostate = 1;
+				V.PC.vagina = 1;
+				V.PC.ovaries = 1;
+			});
+		if (State.temporary.vaginaPenis === 0) {
+			option.addComment("Standard sex scenes; easiest reputation maintenance.");
+		} else if (State.temporary.vaginaPenis === 1) {
+			option.addComment("Sex scene variations; most difficult reputation maintenance.");
+		} else {
+			option.addComment("Sex scene variations; more difficult reputation maintenance; some unique opportunities, especially with breasts.");
+		}
+
+		if (V.cheatMode) {
+			options.addOption("Vagina", "vagina", V.PC).showTextBox();
+			options.addOption("New vagina", "newVag", V.PC).showTextBox();
+			options.addOption("Dick", "dick", V.PC).showTextBox();
+			options.addOption("Balls", "balls", V.PC).addValueList([
+				["Normal", 3],
+				["Big", 5],
+				["Huge", 9],
+				["Monstrous", 30]
+			])
+				.showTextBox();
+			options.addOption("Balls implant", "ballsImplant", V.PC).showTextBox();
+		}
+
+		if (V.PC.vagina !== -1) {
+			option = options.addOption("You are", "preg", V.PC)
+				.addValue("Taking contraceptives", -1, () => { V.PC.pregType = 0; V.PC.labor = 0; })
+				.addValue("Not taking contraceptives", 0, () => { V.PC.pregType = 0; V.PC.labor = 0; })
+				.addRange(16, 37, "<=", "Pregnant").addCallback(() => { V.PC.pregType = 1; V.PC.labor = 0; })
+				.addRange(40, 42, "<=", "Ready to drop").addCallback(() => { V.PC.pregType = 1; V.PC.labor = 0; })
+				.addRange(43, 42, ">", "Ready to drop with octuplets").addCallback(() => { V.PC.pregType = 8; V.PC.labor = 1; });
+			const r =[];
+			if (V.cheatMode) {
+				option.showTextBox();
+				r.push(`How far along your pregnancy is (pregMood kicks in at 24+ weeks) - -2: infertile, -1: contraceptives, 0: not pregnant, 1 - 42: pregnant, 43+: giving birth.`);
+			}
+			if (V.PC.preg === -1) {
+				r.push("You can't get pregnant, however there will be a slight increase to living expenses.");
+			}
+
+			if (V.PC.counter.birthsTotal > 0) {
+				r.push(`You have given birth to <strong>${V.PC.counter.birthsTotal}</strong> babies.`);
+			}
+			if (r.length >0) {
+				option.addComment(r.join(" "));
+			}
+
+			option = options.addOption("Hormone effects", "pregMood", V.PC)
+				.addValueList([
+					["None", 0],
+					["Caring and motherly", 1],
+					["Aggressive and domineering", 2]
+				]);
+			if (V.PC.pregMood === 1) {
+				option.addComment("Sex scene alterations; slaves will trust you more, but may try to take advantage of your mercy.");
+			} else if (V.PC.pregMood === 2) {
+				option.addComment("Sex scene alterations; slaves will fear you more, but will become more submissive to you.");
+			}
+
+			if (V.cheatMode) {
+				options.addOption("Fetus Count", "pregType", V.PC).showTextBox().addComment(`how many you're having (1-8)`);
+				options.addOption("Pregnancy Source", "pregSource", V.PC)
+					.addValueList([
+						["Unknown", 0],
+						["Self-impregnation", -1],
+						["Citizen", -2],
+						["Former Master", -3],
+						["Male arc owner", -4],
+						["Client", -5],
+						["Societal Elite", -6],
+						["Designer baby", -7],
+						["Futanari Sister", -9],
+					])
+					.showTextBox();
+			}
+		}
+
+		if (V.PC.title === 1 && V.PC.boobs <= 100) {
+			option = options.addOption("Your chest is", "boobs", V.PC).addValue("Manly", 100, () => V.PC.boobsImplant = 0);
+		} else {
+			option = options.addOption("Your breasts are", "boobs", V.PC).addValue("Flat", 100, () => V.PC.boobsImplant = 0);
+		}
+		option.addValueList([
+			["C-cups", 500],
+			["DD-cups", 900],
+			["F-cups", 1100],
+			["G-cups", 1300]
+		]);
+		option.showTextBox("CCs");
+
+		if (V.PC.boobs >= 500) {
+			options.addOption("Your breasts are", "boobsImplant", V.PC)
+				.addValueList([
+					["All natural", 0],
+					["Fake", 400]
+				])
+				.showTextBox("CCs");
+		}
+
+		if (V.cheatMode) {
+			if (V.PC.boobs >= 500) {
+				options.addOption("Your breasts are", "lactation", V.PC)
+					.addValueList([
+						["Lactating", 1],
+						["Not Lactating", 0]
+					]);
+			}
+		}
+
+		options.addOption("Your butt size", "butt", V.PC)
+			.addValueList([
+				["Normal", 2],
+				["Big", 3],
+				["Huge", 4],
+				["Enormous", 5],
+			])
+			.showTextBox();
+
+		options.addOption("Your butt is", "buttImplant", V.PC)
+			.addValueList([
+				["All natural", 0],
+				["Fake", 1]
+			])
+			.showTextBox("CCs");
+
+
+		options.addOption("You are genetically", "genes", V.PC)
+			.addValue("XY").addValue("XX");
+
+		el.append(options.render());
+	} else {
+		r = [];
+		r.push(`You have a`);
+		if (V.PC.vagina !== -1 && V.PC.dick !== 0) {
+			r.push(`penis and vagina`);
+		} else if (V.PC.dick !== 0) {
+			r.push(`penis.`);
+		} else if (V.PC.vagina !== -1) {
+			r.push(`vagina`);
+		}
+		if (V.PC.vagina !== -1) {
+			r.push(`and are`);
+			if (V.PC.pregWeek < 0) {
+				r.push(`recovering from your last pregnancy.`);
+			} else if (V.PC.preg === -2) {
+				r.push(`infertile.`);
+			} else if (V.PC.preg === -1) {
+				r.push(`taking contraceptives.`);
+			} else if (V.PC.preg === 0) {
+				r.push(`fertile.`);
+			} else if (V.PC.preg > 37) {
+				r.push(`extremely pregnant.`);
+			} else if (V.PC.preg > 0) {
+				r.push(`pregnant.`);
+			}
+
+			linkArray = [];
+			if (V.PC.preg > 20 || V.PC.counter.birthsTotal > 0) {
+				if (V.PC.pregMood === 1) {
+					r.push(`You tend to be caring and motherly when you're pregnant.`);
+					linkArray.push(noChange(), aggressive());
+				} else if (V.PC.pregMood === 0) {
+					r.push(`Pregnancy doesn't really affect your mood.`);
+					linkArray.push(motherly(), aggressive());
+				} else {
+					r.push(`You tend to be very demanding and aggressive when you're pregnant.`);
+					linkArray.push(noChange(), motherly());
+				}
+			} else {
+				if (V.PC.pregMood === 1) {
+					r.push(`You tend to be caring and motherly when you're hormonal.`);
+					linkArray.push(noChange(), aggressive());
+				} else if (V.PC.pregMood === 0) {
+					r.push(`Your mood isn't tied to your hormones.`);
+					linkArray.push(motherly(), aggressive());
+				} else {
+					r.push(`You tend to be very demanding and aggressive when you're hormonal.`);
+					linkArray.push(noChange(), motherly());
+				}
+			}
+
+			r.push(App.UI.DOM.generateLinksStrip(linkArray));
+			if (V.PC.counter.birthsTotal > 0) {
+				r.push(`You have given birth to ${V.PC.counter.birthsTotal} babies.`);
+			}
+		}
+
+		if (V.PC.boobs >= 300) {
+			r.push(`You have a`);
+			if (V.PC.title > 0) {
+				r.push(`masculine`);
+			} else {
+				r.push(`feminine`);
+			}
+			r.push(`body with`);
+			if (V.PC.boobs >= 1400) {
+				r.push(`giant${(V.PC.boobsImplant !== 0) ? `, fake` : ``} cow tits.`);
+			} else if (V.PC.boobs >= 1200) {
+				r.push(`huge`);
+				if (V.PC.boobsImplant !== 0) {
+					r.push(`fake`);
+				}
+				r.push(`breasts.`);
+			} else if (V.PC.boobs >= 1000) {
+				r.push(`big`);
+				if (V.PC.boobsImplant !== 0) {
+					r.push(`fake`);
+				}
+				r.push(`breasts.`);
+			} else if (V.PC.boobs >= 800) {
+				r.push(`noticeable breasts.`);
+			} else if (V.PC.boobs >= 650) {
+				r.push(`unremarkable breasts.`);
+			} else if (V.PC.boobs >= 500) {
+				r.push(`average breasts.`);
+			} else {
+				r.push(`small breasts.`);
+			}
+		} else {
+			if (V.PC.title > 0) {
+				r.push(`You have a manly chest.`);
+			} else {
+				r.push(`You are flat as a board.`);
+			}
+		}
+		App.Events.addNode(el, r, "p");
+	}
+
+
+	if (V.cheatMode) {
+		// Skills
+		App.UI.DOM.appendNewElement("h2", el, "Skills");
+		options = new App.UI.OptionsGroup();
+		options.addOption(`Trading: ${tradingDescription()}`, "trading", V.PC.skill).addValueList([
+			["Economics master", 100],
+			["Economics expert", 90],
+			["Skilled in economics", 70],
+			["Amateur economist", 50],
+			["Economics beginner", 30],
+			["Basic trader", 0],
+			["Haggler", -10],
+			["Shopper", -30],
+			["Weak saver", -50],
+			["Money sieve", -70],
+			["What's a trading?", -90],
+		]).showTextBox();
+
+		options.addOption(`Warfare: ${warfareDescription()}`, "warfare", V.PC.skill).addValueList([
+			["Warfare master", 100],
+			["Warfare expert", 90],
+			["Skilled in warfare", 70],
+			["Amateur combatant", 50],
+			["Combat beginner", 30],
+			["Basic fighter", 0],
+			["Gun haver", -10],
+			["Knife holder", -30],
+			["Throat puncher", -50],
+			["Groin kicker?", -70],
+			["Most likely to be raped", -90]
+		]).showTextBox();
+
+		options.addOption(`Slaving: ${slavingDescription()}`, "slaving", V.PC.skill).addValueList([
+			["Master slaver", 100],
+			["Expert slaver", 90],
+			["Skilled in slaving", 70],
+			["Amateur slaver", 50],
+			["Slaving beginner", 30],
+			["Basic slaver", 0],
+			["Can't make me a slave", -10],
+			["Can read contracts", -30],
+			["Careful now", -50],
+			["Don't trust that guy", -70],
+			["Potential slave", -90]
+		]).showTextBox();
+
+		options.addOption(`Engineering: ${engineeringDescription()}`, "engineering", V.PC.skill).addValueList([
+			["Master engineer", 100],
+			["Expert engineer", 90],
+			["Skilled in engineering", 70],
+			["Amateur engineer", 50],
+			["Engineering beginner", 30],
+			["Basic engineer", 0],
+			["Gingerbread house", -10],
+			["Knot tyer", -30],
+			["You can use glue", -50],
+			["You aren't handy", -70],
+			["My hovercraft is full of eels", -90]
+		]).showTextBox();
+
+		options.addOption(`Medicine: ${medicineDescription()}`, "medicine", V.PC.skill).addValueList([
+			["Master surgeon", 100],
+			["Expert surgeon", 90],
+			["Skilled in medicine", 70],
+			["Amateur surgeon", 50],
+			["Medical beginner", 30],
+			["Basic medic", 0],
+			["Can treat wounds", -10],
+			["First-aid kit user", -30],
+			["Band-aid applier", -50],
+			["MEDIC!", -70],
+			["Give me another beer", -90]
+		]).showTextBox();
+
+		options.addOption(`Hacking: ${hackingDescription()}`, "hacking", V.PC.skill).addValueList([
+			["Master hacker", 100],
+			["Expert hacker", 90],
+			["Skilled hacker", 70],
+			["Amateur hacker", 50],
+			["Hacking beginner", 30],
+			["Basic hacker", 0],
+			["Mouse clicker", -10],
+			["You can press Enter", -30],
+			[`Where's the "any" key?`, -50],
+			["Main screen turn on?", -70],
+			["Ooh, cool glowy thingy!", -90]
+		]).showTextBox();
+
+		el.append(options.render());
+
+		// Family
+		App.UI.DOM.appendNewElement("h2", el, "Family");
+		options = new App.UI.OptionsGroup();
+		options.addOption(`Your mother ID`, "mother", V.PC).showTextBox();
+		options.addOption(`Your father ID`, "father", V.PC).showTextBox();
+		el.append(options.render());
+
+		// Potential
+		App.UI.DOM.appendNewElement("h2", el, "Misc");
+		options = new App.UI.OptionsGroup();
+		options.addOption(`Sexual Energy`, "sexualEnergy", V.PC).showTextBox();
+		options.addOption(`Cum Tap`, "cumTap", V.PC.skill).showTextBox();
+		options.addOption(`Stored Cum`, "storedCum", V.PC.counter).showTextBox();
+		options.addOption(`Fertility Drugs`, "fertDrugs", V.PC)
+			.addValue("Yes", 1).on()
+			.addValue("No", 0).off();
+		options.addOption(`Forced Fertility Drugs`, "forcedFertDrugs", V.PC).showTextBox();
+		options.addOption(`Stamina Pills`, "staminaPills", V.PC)
+			.addValue("Yes", 1).on()
+			.addValue("No", 0).off();
+		el.append(options.render());
+	}
+
+	return el;
+
+	function noChange() {
+		return App.UI.DOM.link(
+			"Change to no change",
+			() => { V.PC.pregMood = 0; },
+			[],
+			"Intro Summary"
+		);
+	}
+
+	function motherly() {
+		return App.UI.DOM.link(
+			"Change to motherly",
+			() => { V.PC.pregMood = 1; },
+			[],
+			"Intro Summary"
+		);
+	}
+
+	function aggressive() {
+		return App.UI.DOM.link(
+			"Change to aggressive",
+			() => { V.PC.pregMood = 2; },
+			[],
+			"Intro Summary"
+		);
+	}
+
+	function tradingDescription() {
+		if (V.PC.skill.trading >= 100) {
+			return `You are a master at economics and trading.`;
+		} else if (V.PC.skill.trading >= 80) {
+			return `You are an expert at economics and trading.`;
+		} else if (V.PC.skill.trading >= 60) {
+			return `You are skilled in economics and trading.`;
+		} else if (V.PC.skill.trading >= 40) {
+			return `You know some things about economics and trading.`;
+		} else if (V.PC.skill.trading >= 20) {
+			return `You are a beginner in economics.`;
+		} else if (V.PC.skill.trading >= 0) {
+			return `You know only the basics of trading.`;
+		} else if (V.PC.skill.trading >= -20) {
+			return `You know how to haggle a little.`;
+		} else if (V.PC.skill.trading >= -40) {
+			return `You know how to shop around.`;
+		} else if (V.PC.skill.trading >= -60) {
+			return `You know not to pay sticker price.`;
+		} else if (V.PC.skill.trading >= -80) {
+			return 	`People always give you discounts, but you never save any money.`;
+		} else {
+			return `They said it was a bear market, so where are the bears?`;
+		}
+	}
+
+	function warfareDescription() {
+		if (V.PC.skill.warfare >= 100) {
+			return `You are a master of warfare.`;
+		} else if (V.PC.skill.warfare >= 80) {
+			return `You are an expert at tactics and strategy.`;
+		} else if (V.PC.skill.warfare >= 60) {
+			return `You are skilled in combat.`;
+		} else if (V.PC.skill.warfare >= 40) {
+			return `You know some things about combat.`;
+		} else if (V.PC.skill.warfare >= 20) {
+			return `You are a beginner in tactics and strategy.`;
+		} else if (V.PC.skill.warfare >= 0) {
+			return `You know only the basics of fighting.`;
+		} else if (V.PC.skill.warfare >= -20) {
+			return `You know how to hold a gun.`;
+		} else if (V.PC.skill.warfare >= -40) {
+			return `You know how to stab with a knife.`;
+		} else if (V.PC.skill.warfare >= -60) {
+			return `Go for the throat?`;
+		} else if (V.PC.skill.warfare >= -80) {
+			return `Just kick them in the balls, right?`;
+		} else {
+			return `People like you are usually the first raped in a war.`;
+		}
+	}
+
+	function slavingDescription() {
+		if (V.PC.skill.slaving >= 100) {
+			return `You are a master slaver.`;
+		} else if (V.PC.skill.slaving >= 80) {
+			return `You are an expert at enslaving.`;
+		} else if (V.PC.skill.slaving >= 60) {
+			return `You are skilled in slaving.`;
+		} else if (V.PC.skill.slaving >= 40) {
+			return `You know some things about getting slaves.`;
+		} else if (V.PC.skill.slaving >= 20) {
+			return `You are a beginner in slaving.`;
+		} else if (V.PC.skill.slaving >= 0) {
+			return `You know only the basics of slaving.`;
+		} else if (V.PC.skill.slaving >= -20) {
+			return `You know how to avoid becoming a slave.`;
+		} else if (V.PC.skill.slaving >= -40) {
+			return `You know to read contracts before you sign them.`;
+		} else if (V.PC.skill.slaving >= -60) {
+			return `You know to be careful.`;
+		} else if (V.PC.skill.slaving >= -80) {
+			return `You know better than to trust anyone.`;
+		} else {
+			return `It would be easy to enslave you.`;
+		}
+	}
+
+	function engineeringDescription() {
+		if (V.PC.skill.engineering >= 100) {
+			return `You are a master engineer.`;
+		} else if (V.PC.skill.engineering >= 80) {
+			return `You are an expert at engineering.`;
+		} else if (V.PC.skill.engineering >= 60) {
+			return `You are skilled in engineering.`;
+		} else if (V.PC.skill.engineering >= 40) {
+			return `You know some things about engineering.`;
+		} else if (V.PC.skill.engineering >= 20) {
+			return `You are a beginner in engineering.`;
+		} else if (V.PC.skill.engineering >= 0) {
+			return `You know only the basics of engineering.`;
+		} else if (V.PC.skill.engineering >= -20) {
+			return `You can build a gingerbread house that doesn't collapse.`;
+		} else if (V.PC.skill.engineering >= -40) {
+			return `You can tie a tight knot, does that count?`;
+		} else if (V.PC.skill.engineering >= -60) {
+			return `Glue is your friend; lots of it.`;
+		} else if (V.PC.skill.engineering >= -80) {
+			return `You know better than to even try to build something.`;
+		} else {
+			return `You can cook; that's sort of like building something, right?`;
+		}
+	}
+
+	function medicineDescription() {
+		if (V.PC.skill.medicine >= 100) {
+			return `You are a master surgeon.`;
+		} else if (V.PC.skill.medicine >= 80) {
+			return `You are an expert at medicine and surgery.`;
+		} else if (V.PC.skill.medicine >= 60) {
+			return `You are skilled in surgery.`;
+		} else if (V.PC.skill.medicine >= 40) {
+			return `You know some things about medicine.`;
+		} else if (V.PC.skill.medicine >= 20) {
+			return `You are a beginner in medicine.`;
+		} else if (V.PC.skill.medicine >= 0) {
+			return `You know the basics of treating injuries.`;
+		} else if (V.PC.skill.medicine >= -20) {
+			return `You can stop a wound from getting infected.`;
+		} else if (V.PC.skill.medicine >= -40) {
+			return `Gauze is your friend. Just keep wrapping.`;
+		} else if (V.PC.skill.medicine >= -60) {
+			return `You know how to apply a band-aid.`;
+		} else if (V.PC.skill.medicine >= -80) {
+			return `Cure-alls are wonderful. Why aren't they sold in stores, though?`;
+		} else {
+			return `Alcohol makes pain go away, right?`;
+		}
+	}
+
+	function hackingDescription() {
+		if (V.PC.skill.hacking >= 100) {
+			return `You are a master of hacking.`;
+		} else if (V.PC.skill.hacking >= 80) {
+			return `You are an expert at hacking.`;
+		} else if (V.PC.skill.hacking >= 60) {
+			return `You are skilled in hacking.`;
+		} else if (V.PC.skill.hacking >= 40) {
+			return `You know some things about hacking.`;
+		} else if (V.PC.skill.hacking >= 20) {
+			return `You are a beginner in hacking.`;
+		} else if (V.PC.skill.hacking >= 0) {
+			return `You know only the basics of hacking.`;
+		} else if (V.PC.skill.hacking >= -20) {
+			return `You know how to click a mouse.`;
+		} else if (V.PC.skill.hacking >= -40) {
+			return `Enter does something?`;
+		} else if (V.PC.skill.hacking >= -60) {
+			return `Where is the "any" key?`;
+		} else if (V.PC.skill.hacking >= -80) {
+			return `You can push the power button, good job.`;
+		} else {
+			return `This black box thingy is magical.`;
+		}
+	}
+};
diff --git a/src/events/intro/pcAppearanceIntro.tw b/src/events/intro/pcAppearanceIntro.tw
index d61a263a08065c1ae548bf786857d778336ff982..3854ab1ddaba89663f214985123f95b35b00ad02 100644
--- a/src/events/intro/pcAppearanceIntro.tw
+++ b/src/events/intro/pcAppearanceIntro.tw
@@ -6,275 +6,9 @@
 		Appearance only, no effect on gameplay (unless you make a big deal out of it).
 	</div>
 </p>
-
-<p>
-	<div class="intro question">
-		What nationality are you?
-	</div>
-	<div>
-		You are $PC.nationality.
-	</div>
-	<div>
-		<<textbox "$PC.nationality" $PC.nationality "PC Appearance Intro">>
-		<span class="note">
-			Capitalize it
-		</span>
-	</div>
-</p>
-
-<p>
-	<div class="intro question">
-		What race are you?
-	</div>
-	<div id = "ethnicity">
-		You're $PC.race.
-	</div>
-	<<link "White">>
-		<<set $PC.race = "white">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Asian">>
-		<<set $PC.race = "asian">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Latina">>
-		<<set $PC.race = "latina">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Middle Eastern">>
-		<<set $PC.race = "middle eastern">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Black">>
-		<<set $PC.race = "black">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Semitic">>
-		<<set $PC.race = "semitic">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Southern European">>
-		<<set $PC.race = "southern european">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Indo-Aryan">>
-		<<set $PC.race = "indo-aryan">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Amerindian">>
-		<<set $PC.race = "amerindian">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Pacific Islander">>
-		<<set $PC.race = "pacific islander">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Malay">>
-		<<set $PC.race = "malay">>
-		<<PlayerRace>>
-	<</link>>
-	|
-	<<link "Mixed Race">>
-		<<set $PC.race = "mixed race">>
-		<<PlayerRace>>
-	<</link>>
-</p>
-
-<p>
-	<div class="intro question">
-		What is your skin tone?
-	</div>
-	<div id = "skin">
-		You have $PC.skin skin.
-	</div>
-	<<link "Pure White">>
-		<<set $PC.skin = "pure white">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Ivory">>
-		<<set $PC.skin = "ivory">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "White">>
-		<<set $PC.skin = "white">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Extremely Pale">>
-		<<set $PC.skin = "extremely pale">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Very Pale">>
-		<<set $PC.skin = "very pale">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Pale">>
-		<<set $PC.skin = "pale">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Extremely Fair">>
-		<<set $PC.skin = "extremely fair">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Very Fair">>
-		<<set $PC.skin = "very fair">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Fair">>
-		<<set $PC.skin = "fair">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Light">>
-		<<set $PC.skin = "light">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Light Olive">>
-		<<set $PC.skin = "light olive">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Tan">>
-		<<set $PC.skin = "tan">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Olive">>
-		<<set $PC.skin = "olive">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Bronze">>
-		<<set $PC.skin = "bronze">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Dark Olive">>
-		<<set $PC.skin = "dark olive">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Dark">>
-		<<set $PC.skin = "dark">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Light Beige">>
-		<<set $PC.skin = "light beige">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Beige">>
-		<<set $PC.skin = "beige">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Dark Beige">>
-		<<set $PC.skin = "dark beige">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Light Brown">>
-		<<set $PC.skin = "light brown">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Brown">>
-		<<set $PC.skin = "brown">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Dark Brown">>
-		<<set $PC.skin = "dark brown">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Black">>
-		<<set $PC.skin = "black">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Ebony">>
-		<<set $PC.skin = "ebony">>
-		<<PlayerSkin>>
-	<</link>>
-	|
-	<<link "Pure Black">>
-		<<set $PC.skin = "pure black">>
-		<<PlayerSkin>>
-	<</link>>
-</p>
-
-<p>
-	<div class="intro question">
-		Some people have freckles.
-	</div>
-	<div id = "markings">
-		<<if $PC.markings == "none">>
-			Your skin is pure and clear of any freckles.
-		<<elseif $PC.markings == "freckles">>
-			You have some freckles on your cheeks and elsewhere.
-		<<elseif $PC.markings == "heavily freckled">>
-			You have dense freckles on your cheeks and elsewhere.
-		<</if>>
-	</div>
-	<<link "No Freckles">>
-		<<set $PC.markings = "none">>
-		<<PlayerMarkings>>
-	<</link>>
-	|
-	<<link "Light Freckles">>
-		<<set $PC.markings = "freckles">>
-		<<PlayerMarkings>>
-	<</link>>
-	|
-	<<link "Heavy Freckles">>
-		<<set $PC.markings = "heavily freckled">>
-		<<PlayerMarkings>>
-	<</link>>
-</p>
-
-<p>
-	<div class="intro question">
-		What color are your eyes?
-	</div>
-	<div>
-		You have $PC.eye.origColor eyes.
-	</div>
-	<div>
-		<<textbox "$PC.eye.origColor" $PC.eye.origColor "PC Appearance Intro">>
-	</div>
-</p>
-
-<p>
-	<div class="intro question">
-		What color is your hair?
-	</div>
-	<div>
-		You have $PC.hColor hair.
-	</div>
-	<div>
-		<<textbox "$PC.hColor" $PC.hColor "PC Appearance Intro">>
-	</div>
-</p>
+<<set _options = new App.UI.OptionsGroup()>>
+<<run App.UI.Player.appearance(_options)>>
+<<includeDOM _options.render()>>
 
 <p>
 	[[Finish player character customization|PC Experience Intro][resetEyeColor($PC)]]
diff --git a/src/events/intro/pcBodyIntro.js b/src/events/intro/pcBodyIntro.js
new file mode 100644
index 0000000000000000000000000000000000000000..64a220a2604b7079caeee607ff05d190c7dac2bb
--- /dev/null
+++ b/src/events/intro/pcBodyIntro.js
@@ -0,0 +1,172 @@
+App.Intro.PCBodyIntro = function() {
+	V.PC.actualAge = Math.clamp(V.PC.actualAge, 14, 80);
+	V.PC.physicalAge = V.PC.actualAge;
+	V.PC.visualAge = V.PC.actualAge;
+
+	const el = new DocumentFragment();
+	let r = [];
+
+	r.push(`Most slaveowners in the Free Cities are male. The preexisting power structures of the old world have mostly migrated to the new, and it can often be very hard to be a free woman in the Free Cities. Some manage to make their way, but in many arcologies, men are the owners, and women are the owned. You'll cut a striking figure as the owner and leader of your arcology, but`);
+	r.push(App.UI.DOM.makeElement("span", `what's under your business attire?`, ["intro", "question"]));
+	App.Events.addNode(el, r, "p");
+
+	el.append(body());
+	el.append(age());
+	el.append(nameIndulgence());
+	el.append(endScene());
+
+	return el;
+
+	function body() {
+		const el = document.createElement("p");
+		const options = new App.UI.OptionsGroup();
+		let r = [];
+		let comment;
+
+		// Gender
+		r.push(`You are a`);
+		if (V.PC.genes === "XX") {
+			r.push(`woman`);
+		} else {
+			r.push(`man`);
+		}
+		r.push(`with a`);
+		if (V.PC.title > 0) {
+			r.push(`masculine figure and will be referred to as <strong>Master.</strong>`);
+		} else {
+			r.push(`feminine figure and will be referred to as <strong>Mistress.</strong>`);
+		}
+		options.addOption(r.join(" "), "title", V.PC)
+			.addValue("Feminine appearance", 0)
+			.addValue("Masculine appearance", 1)
+			.addComment("This option will affect scenes. Femininity may increase difficulty in the future, but for now only your chest and junk matter.");
+
+		// Chest
+		comment = "These options will affect scenes. Sporting breasts will increase difficulty";
+		if (V.PC.boobs > 300) {
+			options.addOption(`Under my suit jacket, <strong>feminine breasts.</strong>`, "boobs", V.PC)
+				.addValue("Remove breasts", 100).addComment(comment);
+		} else {
+			options.addOption(`Under my suit jacket, <strong>${(V.PC.title > 0) ? `masculine muscles` : `a flat chest`}.</strong>`, "boobs", V.PC)
+				.addValue("Add breasts", 900).addComment(comment);
+		}
+
+		// Lower deck
+		r = [];
+		let option;
+		r.push(`Behind the front of my tailored`);
+		if (V.PC.dick !== 0) {
+			if (V.PC.vagina !== -1) {
+				r.push(`slacks, <strong>both a penis and a vagina.</strong>`);
+				option = options.addOption(r.join(" "))
+					.customButton("Remove the penis", penisRemove, "PC Body Intro")
+					.customButton("Remove the vagina", vaginaRemove, "PC Body Intro");
+			} else {
+				r.push(`slacks, a <strong>penis.</strong>`);
+				option = options.addOption(r.join(" "))
+					.customButton(
+						"Switch to vagina",
+						() => {
+							penisRemove();
+							V.PC.genes = "XX";
+							vaginaAdd();
+						},
+						"PC Body Intro"
+					)
+					.customButton("Add a vagina", vaginaAdd, "PC Body Intro");
+			}
+		} else {
+			r.push(`skirt, a <strong>vagina.</strong>`);
+			option = options.addOption(r.join(" "))
+				.customButton(
+					"Switch to penis",
+					() => {
+						penisAdd();
+						V.PC.genes = "XY";
+						vaginaRemove();
+					},
+					"PC Body Intro"
+				)
+				.customButton("Add a penis", penisAdd, "PC Body Intro");
+		}
+			option.addComment(`These options will affect sex scenes. Feminine options will increase difficulty.`);
+
+		el.append(options.render());
+
+		return el;
+
+		function penisAdd() {
+			V.PC.dick = 4;
+			V.PC.balls = 3;
+			V.PC.scrotum = 3;
+			V.PC.prostate = 1;
+		}
+
+		function penisRemove() {
+			V.PC.dick = 0;
+			V.PC.balls = 0;
+			V.PC.scrotum = 0;
+			V.PC.prostate = 0;
+		}
+
+		function vaginaAdd() {
+			V.PC.vagina = 1;
+			V.PC.ovaries = 1;
+		}
+
+		function vaginaRemove() {
+			V.PC.vagina = -1;
+			V.PC.ovaries = 0;
+		}
+	}
+
+	function age() {
+		const el = document.createElement("p");
+		const options = new App.UI.OptionsGroup();
+
+		App.UI.DOM.appendNewElement("div", el, `How old are you?`, ["intro", "question"]);
+		const r = [];
+		r.push(`I'm`);
+		if (V.PC.actualAge >= 65) {
+			r.push(`getting up in years. I've made a legacy for myself, and I'm not done yet.`);
+		} else if (V.PC.actualAge >= 50) {
+			r.push(`well into middle age. I've made a name for myself, and I've still got it.`);
+		} else if (V.PC.actualAge >= 35) {
+			r.push(`entering middle age. I'm accomplished, and I retain some youthful vigor.`);
+		} else {
+			r.push(`surprisingly young. I'll need to prove myself, but I've got energy to burn.`);
+		}
+		r.push(`My age:`);
+		options.addOption(r.join(" "), "actualAge", V.PC).showTextBox()
+			.addComment(`Older player characters start with more reputation and maintain reputation somewhat more easily, but have slightly less sexual energy.`);
+
+		el.append(options.render());
+
+		return el;
+	}
+
+	function nameIndulgence() {
+		const el = document.createElement("p");
+		const options = new App.UI.OptionsGroup();
+
+		App.UI.DOM.appendNewElement("div", el, `What is your name and alternate indulgence?`, ["intro", "question"]);
+
+		App.UI.Player.names(options);
+		App.UI.Player.refreshmentChoice(options);
+
+		el.append(options.render());
+
+		return el;
+	}
+
+	function endScene() {
+		const el = document.createElement("p");
+		const linkTitle = "Confirm player character customization";
+		if (V.PC.vagina !== -1) {
+			el.append(App.UI.DOM.passageLink(linkTitle, "PC Preg Intro"));
+		} else {
+			el.append(App.UI.DOM.passageLink(linkTitle, "PC Appearance Intro"));
+		}
+		return el;
+	}
+};
diff --git a/src/events/intro/pcBodyIntro.tw b/src/events/intro/pcBodyIntro.tw
index 79df64667f1aaae2426df5550764bf93893bdada..1d857050e432f15ae9ece9ee57952db52706c38b 100644
--- a/src/events/intro/pcBodyIntro.tw
+++ b/src/events/intro/pcBodyIntro.tw
@@ -1,152 +1,3 @@
 :: PC Body Intro [nobr]
 
-<p>
-	Most slaveowners in the Free Cities are male. The preexisting power structures of the old world have mostly migrated to the new, and it can often be very hard to be a free woman in the Free Cities. Some manage to make their way, but in many arcologies, men are the owners, and women are the owned. You'll cut a striking figure as the owner and leader of your arcology, but
-	<span class="intro question">
-		what's under your business attire?
-	</span>
-</p>
-
-<<set $PC.actualAge = Math.clamp($PC.actualAge, 14, 80)>>
-
-<p>
-	<div>
-		<<if $PC.title > 0>>
-			You are a <<if $PC.genes == "XX">>wo<</if>>man with a masculine figure and will be referred to as
-			<span style="font-weight:Bold">
-				Master.
-			</span>
-			[[Switch to a feminine appearance|PC Body Intro][$PC.title = 0]]
-		<<else>>
-			You are a <<if $PC.genes == "XX">>wo<</if>>man with a feminine figure and will be referred to as
-			<span style="font-weight:Bold">
-				Mistress.
-			</span>
-			[[Switch to a masculine appearance|PC Body Intro][$PC.title = 1]]
-		<</if>>
-	</div>
-	<div class="indent note">
-		This option will affect scenes. Femininity may increase difficulty in the future, but for now only your chest and junk matter.
-	</div>
-
-	<div>
-		Under my suit jacket,
-		<<if $PC.boobs > 300>>
-			<span style="font-weight:Bold">
-				feminine breasts.
-			</span>
-			[[Remove breasts|PC Body Intro][$PC.boobs = 100]]
-		<<else>>
-			<<if $PC.title > 0>>
-				<span style="font-weight:Bold">
-					masculine muscles.
-				</span>
-				[[Add breasts|PC Body Intro][$PC.boobs = 900]]
-			<<else>>
-				<span style="font-weight:Bold">
-					a flat chest.
-				</span>
-				[[Add breasts|PC Body Intro][$PC.boobs = 900]]
-			<</if>>
-		<</if>>
-		<div class="indent note">
-			These options will affect scenes. Sporting breasts will increase difficulty.
-		</div>
-	</div>
-
-	<div>
-		Behind the front of my tailored
-		<<if $PC.dick != 0>>
-			<<if $PC.vagina != -1>>
-				slacks,
-				<span style="font-weight:Bold">
-					both a penis and a vagina.
-				</span>
-				[[Remove the penis|PC Body Intro][$PC.dick = 0, $PC.balls = 0, $PC.scrotum = 0, $PC.prostate = 0]] | [[Remove the vagina|PC Body Intro][$PC.vagina = -1, $PC.ovaries = 0]]
-			<<else>>
-				slacks, a
-				<span style="font-weight:Bold">
-					penis.
-				</span>
-				[[Switch to vagina|PC Body Intro][$PC.dick = 0, $PC.balls = 0, $PC.scrotum = 0, $PC.prostate = 0, $PC.genes = "XX", $PC.vagina = 1, $PC.ovaries = 1]] | [[Add a vagina|PC Body Intro][$PC.vagina = 1, $PC.ovaries = 1]]
-			<</if>>
-		<<else>>
-			skirt, a
-			<span style="font-weight:Bold">
-				vagina.
-			</span>
-			[[Switch to penis|PC Body Intro][$PC.dick = 4, $PC.balls = 3, $PC.scrotum = 3, $PC.prostate = 1, $PC.genes = "XY", $PC.vagina = -1, $PC.ovaries = 0]] | [[Add a penis|PC Body Intro][$PC.dick = 4, $PC.balls = 3, $PC.scrotum = 3, $PC.prostate = 1]]
-		<</if>>
-	</div>
-	<div class="indent note">
-		These options will affect sex scenes. Feminine options will increase difficulty.
-	</div>
-</p>
-
-<p>
-	<div class="intro question">
-		How old are you?
-	</div>
-	<div>
-		I'm
-		<<if $PC.actualAge >= 65>>
-			getting up in years. I've made a legacy for myself, and I'm not done yet.
-		<<elseif $PC.actualAge >= 50>>
-			well into middle age. I've made a name for myself, and I've still got it.
-		<<elseif $PC.actualAge >= 35>>
-			entering middle age. I'm accomplished, and I retain some youthful vigor.
-		<<else>>
-			surprisingly young. I'll need to prove myself, but I've got energy to burn.
-		<</if>>
-		My age:
-		<span style="font-weight:Bold">
-			<<textbox "$PC.actualAge" $PC.actualAge "PC Body Intro">>
-		</span>
-		<<set $PC.physicalAge = $PC.actualAge, $PC.visualAge = $PC.actualAge>>
-	</div>
-	<div class="indent note">
-		Older player characters start with more reputation and maintain reputation somewhat more easily, but have slightly less sexual energy.
-	</div>
-</p>
-
-<p>
-	<div class="intro question">
-		What is your name and alternate indulgence?
-	</div>
-
-	<div>
-		Name your character: <<textbox "$PC.slaveName" $PC.slaveName "PC Body Intro">> (surname)
-		<<if $PC.slaveSurname>>
-			<<textbox "$PC.slaveSurname" $PC.slaveSurname "PC Body Intro">>
-			<<link "Go by a single name">><<set $PC.slaveSurname = 0, $PC.birthSurname = "">><<goto "PC Body Intro">><</link>>
-		<<else>>
-			<<textbox "$PC.slaveSurname" "" "PC Body Intro">>
-		<</if>>
-	</div>
-	<div class="indent note">
-		As with all text boxes in FC, press the enter key to commit your changes.
-	</div>
-	<div>
-		Preferred refreshment: <<textbox "$PC.refreshment" $PC.refreshment "PC Body Intro">> [[Cigars|PC Body Intro][$PC.refreshment = "cigar",$PC.refreshmentType = 0]] | [[Whiskey|PC Body Intro][$PC.refreshment = "whiskey",$PC.refreshmentType = 1]]
-	</div>
-	<div>
-		Preferred method of consumption:
-		<span style="font-weight:Bold"><<if $PC.refreshmentType == 0>>Smoked<<elseif $PC.refreshmentType == 1>>Drank<<elseif $PC.refreshmentType == 2>>Eaten<<elseif $PC.refreshmentType == 3>>Snorted<<elseif $PC.refreshmentType == 4>>Injected<<elseif $PC.refreshmentType == 5>>Popped<<elseif $PC.refreshmentType == 6>>Dissolved orally<</if>></span>.
-	</div>
-	[[Smoked|PC Body Intro][$PC.refreshmentType = 0]] | [[Drank|PC Body Intro][$PC.refreshmentType = 1]] | [[Eaten|PC Body Intro][$PC.refreshmentType = 2]] | [[Snorted|PC Body Intro][$PC.refreshmentType = 3]] | [[Injected|PC Body Intro][$PC.refreshmentType = 4]] | [[Popped|PC Body Intro][$PC.refreshmentType = 5]] | [[Orally Dissolved|PC Body Intro][$PC.refreshmentType = 6]]
-	<div class="indent note">
-		Flavor only; no mechanical effect. If entering a custom refreshment, please assign proper usage.
-		<<if $PC.refreshmentType == 0>>"Smoke" must fit into the following sentence: "I smoked a $PC.refreshment" to fit events properly
-		<<elseif $PC.refreshmentType == 5>>"Popped" must fit into the following sentence: "I shook the bottle of $PC.refreshment" to fit events properly
-		<<elseif $PC.refreshmentType == 6>>"Orally Dissolved" must fit into the following sentence: "I placed a tab of $PC.refreshment under my tongue" to fit events properly
-		<</if>>
-	</div>
-</p>
-
-<p>
-	<<if $PC.vagina != -1>>
-		[[Confirm player character customization|PC Preg Intro]]
-	<<else>>
-		[[Confirm player character customization|PC Appearance Intro]]
-	<</if>>
-</p>
+<<includeDOM App.Intro.PCBodyIntro()>>
\ No newline at end of file
diff --git a/src/events/randomEvent.js b/src/events/randomEvent.js
index f1317ff7fbd749f602edf5ca4ddecb6396f35336..2762760c2dd46d7c119645cdded41c73b26d82a3 100644
--- a/src/events/randomEvent.js
+++ b/src/events/randomEvent.js
@@ -49,6 +49,7 @@ App.Events.getIndividualEvents = function() {
 
 		new App.Events.RETSSiblingTussle(),
 		new App.Events.RETSSimpleAssault(),
+		new App.Events.RETSFucktoyPrefersRelative(),
 	];
 };
 
diff --git a/src/events/reFullBed.js b/src/events/reFullBed.js
index 05f3bbcf828be7e1c88f9e3f615f1aebc18c7bd6..8f57412720504059a4b3830979defe5847388493 100644
--- a/src/events/reFullBed.js
+++ b/src/events/reFullBed.js
@@ -108,16 +108,16 @@ App.Events.REFullBed = class REFullBed extends App.Events.BaseEvent {
 				} else {
 					t.push(`They stiffen in unison when you pinch each clit, but immediately relax and begin to work you harder. They orgasm one after the other from your manipulations, before eagerly cleaning`);
 				}
-				seX(bedSlaves[0], "vaginal");
-				seX(bedSlaves[1], "vaginal");
+				actX(bedSlaves[0], "vaginal");
+				actX(bedSlaves[1], "vaginal");
 			} else if (canDoAnal(bedSlaves[1]) && canDoAnal(bedSlaves[0])) {
 				t.push(`They stiffen in unison when you hook two fingers up each asshole, but immediately relax and begin to work you harder. They orgasm one after the other, their butts clenching against your intruding fingers, and then eagerly clean`);
-				seX(bedSlaves[0], "anal");
-				seX(bedSlaves[1], "anal");
+				actX(bedSlaves[0], "anal");
+				actX(bedSlaves[1], "anal");
 			} else if (bedSlaves[1].dick > 0 && bedSlaves[1].chastityPenis === 0 && bedSlaves[0].dick > 0 && bedSlaves[0].chastityPenis === 0) {
 				t.push(`They stiffen in unison when you take hold of each prick, but immediately relax and begin to work you harder. They cum one after the other from your manipulations, before eagerly cleaning`);
-				seX(bedSlaves[0], "penetrative");
-				seX(bedSlaves[1], "penetrative");
+				actX(bedSlaves[0], "penetrative");
+				actX(bedSlaves[1], "penetrative");
 			} else {
 				t.push(`They stiffen as your hands get more adventurous, but immediately relax and begin to work you harder. They orgasm one after the other from your manipulations, before eagerly cleaning`);
 				let hole;
@@ -131,7 +131,7 @@ App.Events.REFullBed = class REFullBed extends App.Events.BaseEvent {
 					} else {
 						hole = "mammary";
 					}
-					seX(s, hole);
+					actX(s, hole);
 				});
 			}
 			t.push(`you with their mouths when you climax yourself. They have become <span class="devotion inc">still more devoted to you.</span>`);
diff --git a/src/events/reRelativeRecruiter.js b/src/events/reRelativeRecruiter.js
index 441c24819d93a194831499711784b8ba3777108d..d468e0b59fafc4f071501b5c2376854dad158e61 100644
--- a/src/events/reRelativeRecruiter.js
+++ b/src/events/reRelativeRecruiter.js
@@ -169,7 +169,7 @@ App.Events.RERelativeRecruiter = class RERelativeRecruiter extends App.Events.Ba
 		let eventTextDiv = App.UI.DOM.makeElement("div", eventText());
 		node.appendChild(eventTextDiv);
 
-		/** produce the event text and response links.  might be regenerated by changing slave selection in cheat mode
+		/** produce the event text and response links. might be regenerated by changing slave selection in cheat mode
 		 * @returns {DocumentFragment}
 		 */
 		function eventText() {
diff --git a/src/events/scheduled/murderAttempt.js b/src/events/scheduled/murderAttempt.js
index daed121f4a5bcf3f971c75fc1bd94de2e11646be..2cec793ad25e113a322a1e4e4b5d022984c546fe 100644
--- a/src/events/scheduled/murderAttempt.js
+++ b/src/events/scheduled/murderAttempt.js
@@ -1,6 +1,6 @@
 App.Events.murderAttempt = function() {
 	// setup next time, 2-4 months
-	V.murderAttemptWeek += 26 + Math.floor(Math.random() * 26);
+	V.murderAttemptWeek = V.week + 26 + Math.floor(Math.random() * 26);
 	// disable Continue
 	V.nextButton = " ";
 	App.Utils.updateUserButton();
@@ -218,7 +218,7 @@ App.Events.murderAttempt = function() {
 
 	function bgDefense(fragment, paragraphStart) {
 		paragraphStart.push(`Before you can react, the ${woman} draws a weapon and`);
-		if (S.Bodyguard.muscles > 95 || S.Bodyguard.muscles < -30 || S.Bodyguard.boobs > 4000 || S.Bodyguard.weight > 130 || S.Bodyguard.health.tired > 30 || S.Bodyguard.belly >= 100000) { // BG is too big or weak to draw fast enough to matter
+		if ((S.Bodyguard.muscles > 95 && S.Bodyguard.height < 185) || S.Bodyguard.muscles < -30 || S.Bodyguard.boobs > 4000 || S.Bodyguard.weight > 130 || S.Bodyguard.health.tired > 30 || S.Bodyguard.belly >= 100000) { // BG is too big or weak to draw fast enough to matter
 			paragraphStart.push("shoots you through the heart; having a bodyguard who cannot actually defend you might have been a poor choice in retrospect.");
 			paragraphStart.push(`With you dead, ${S.Bodyguard.slaveName} knows ${heBG} has to put this to rest.`);
 			if (deadliness(S.Bodyguard) > 1) {
@@ -230,8 +230,12 @@ App.Events.murderAttempt = function() {
 			gameOver(fragment);
 		} else {
 			paragraphStart.push(paragraphStart.pop() + ",");
-			if (deadliness(S.Bodyguard) > 1) {
-				paragraphStart.push(`just a little quicker, ${S.Bodyguard.slaveName} draws ${hisBG} own, shooting ${him} in the shoulder. Screaming in pain, ${he} drops ${his} weapon and ${S.Bodyguard.slaveName} swiftly moves to secure ${him}.`);
+			if (deadliness(S.Bodyguard) > 1 || (S.Bodyguard.muscles > 95 && S.Bodyguard.height >= 185)) {
+				if (deadliness(S.Bodyguard) > 1) {
+					paragraphStart.push(`just a little quicker, ${S.Bodyguard.slaveName} draws ${hisBG} own, shooting ${him} in the shoulder. Screaming in pain, ${he} drops ${his} weapon and ${S.Bodyguard.slaveName} swiftly moves to secure ${him}.`);
+				} else {
+					paragraphStart.push(`to your surprise and later annoyance, find ${S.Bodyguard.slaveName} has used ${hisBG} impressive strength to flip your desk at ${him}. Crushed beneath the heavy piece of furniture and writhing in pain, ${he} is in no position to take any further actions against you.`);
+				}
 				App.Events.addParagraph(fragment, paragraphStart);
 				let r = [];
 				r.push(`After the initial shock has waned and you ready to begin interrogating ${him}, ${he} clamps ${his} jaw down hard, foams for several seconds and dies. Seems like whomever sent ${him} cares a lot about not being tracked down. And indeed, all the already sparse information about ${companyName} is gone and any leads you may have had are useless. You task ${V.assistant.name} to continue searching for the one behind this, but you don't expect anything to come out of it. Someone wanting you dead isn't all that surprising, and without any idea as to why, there are thousands of small groups and individuals who want arcology owners, owners like you, or maybe just you personally, dead.`);
diff --git a/src/events/scheduled/pitFight.js b/src/events/scheduled/pitFight.js
index c2beeb04f384f153ea884c08f5c8372863c4af37..d3c3e05e9f10ad9d50e4e073cf5587d8d9eaac6e 100644
--- a/src/events/scheduled/pitFight.js
+++ b/src/events/scheduled/pitFight.js
@@ -16,19 +16,18 @@ App.Facilities.Pit.fight = function(lethal) {
 		fighters.push(S.Bodyguard.ID, V.pit.slaveFightingBodyguard);
 	} else {
 		if (available.length > 0) {
-			if (S.Bodyguard) {
-				available.filter(id => id !== S.Bodyguard.ID);
-
-				if (V.pit.bodyguardFights) {
-					fighters.push(S.Bodyguard.ID);
-				}
+			// first fighter
+			if (S.Bodyguard && V.pit.bodyguardFights) {
+				available.delete(S.Bodyguard.ID);
+				fighters.push(S.Bodyguard.ID);
+			} else {
+				fighters.push(available.pluck());
 			}
 
-			if (available.length > 1 && !V.pit.animal) {
+			// second fighter
+			if (available.length > 0 && !V.pit.animal) {
 				fighters.push(available.pluck());
 			}
-
-			fighters.push(available.pluck());
 		} else {
 			throw new Error(`Pit fight triggered with ${V.pit.fighterIDs.length} fighters.`);	// should technically never be triggered
 		}
diff --git a/src/events/scheduled/pitFightLethal.js b/src/events/scheduled/pitFightLethal.js
index 3fc8d9ca1f5fc0d217f0d7046bb76041aeaa2df1..74f51f495f6ebd85ba8c855fa6056d7cdbf93474 100644
--- a/src/events/scheduled/pitFightLethal.js
+++ b/src/events/scheduled/pitFightLethal.js
@@ -433,7 +433,7 @@ App.Facilities.Pit.fight.lethal = function(fighters) {
 						const clawMarks = App.UI.DOM.makeElement("span", `deep claw marks in ${his} side.`, ["health", "dec"]);
 
 						r.push(`${loser.slaveName} is reasonably confident in ${his} fighting abilities, but ${he} isn't sure how to go about attacking the ${animal.species}. ${He} decides to open with a series of wide horizontal slashes, but the beast manages to stay out of range of ${his} sword. Realizing this isn't working, ${he} lunges at the animal, leaving ${his} side exposed. The ${animal.species} sees this and ${animal.type === "hooved" ?
-							`headbutts ${him} as hard as it can, sending ${him} flying into the side of ${V.pit.name}.  Battered` :
+							`headbutts ${him} as hard as it can, sending ${him} flying into the side of ${V.pit.name}. Battered` :
 							`leaves some ${clawMarks} Bleeding`} and unable to stand, ${loser.slaveName} can do little to stop the ${animal.species} from finishing ${him} off with a ${animal.type === "canine" ? `fierce bite of its fangs to ${his} throat.` : animal.type === "hooved" ? `swift kick to the head.` : `ferocious swipe of its claws to ${his} throat.`}`);
 
 						if (V.pit.audience === "paid") {
diff --git a/src/events/scheduled/sePCBirthday.desc.js b/src/events/scheduled/sePCBirthday.desc.js
index b08fe9d143fe8834df06727bcd7c942af4a300a0..2bbe2bf0622612c8f4f2d7e91714cb82950f19ac 100644
--- a/src/events/scheduled/sePCBirthday.desc.js
+++ b/src/events/scheduled/sePCBirthday.desc.js
@@ -261,7 +261,7 @@ App.Events.pcBirthday.Desc = (function(bday) {
 			html += ` Two of the slaves each take a breast of the third in their mouths and clamp their lips down on the nipples.
 				Both move a hand to the third's ${genital1}, rubbing and stroking vigorously.
 				The effect is too strong for the hapless ${slaveDescSing} who feels nothing but a ceaseless attack on her ${genital1} and tits.
-				Her hips tremble and buckle as a loud wail escape her mouth.  It's only then that one of her companions releases her tender nipple and locks
+				Her hips tremble and buckle as a loud wail escape her mouth. It's only then that one of her companions releases her tender nipple and locks
 				their mouths together, the two ${slaveDescPlural} sharing a kiss as frenetic as the hands touching her ${genital2}.
 			</p>`;
 			return html;
@@ -277,7 +277,7 @@ App.Events.pcBirthday.Desc = (function(bday) {
 				html += `
 				<p>
 					After thanking ${V.assistant.name} for ${data.assistPN.her} dedication, you head out of your office and are immediately taken aback by a festive display of
-					colors and shapes. Standing before you and blowing a party horn, ${data.planner.slave.slaveName} is dressed in a snug and flattering teddy.  "Surprise!"
+					colors and shapes. Standing before you and blowing a party horn, ${data.planner.slave.slaveName} is dressed in a snug and flattering teddy. "Surprise!"
 				</p>
 				<p>
 					You don't know how ${data.planner.pn.she} did it. Certainly not on ${data.planner.pn.his} own, so ${data.planner.pn.she} must have enlisted some helpers. Nothing
@@ -299,7 +299,7 @@ App.Events.pcBirthday.Desc = (function(bday) {
 				html += `
 				<p>
 					${data.planner.slave.slaveName} breaks the kiss, takes your hand, and leads you over to a table. "So. What did you want to do today? Anything special?"
-					${data.planner.pn.She} steps aside so you can see the tabletop.  A little cupcake sits there, ${App.Events.pcBirthday.eyeColors()} like your eyes, with a charming little candle
+					${data.planner.pn.She} steps aside so you can see the tabletop. A little cupcake sits there, ${App.Events.pcBirthday.eyeColors()} like your eyes, with a charming little candle
 					burning upon it.
 				</p>
 				<p>
@@ -577,7 +577,7 @@ App.Events.pcBirthday.Desc = (function(bday) {
 				soft mound between ${pn.his} legs. ${pn.His} slit allows a finger inside, and you find ${pn.his} attentive button swimming in ${pn.his} juices. ${pn.She}
 				mirrors your actions, and soon two fingers are straddling your clit, stroking it and occasionally slipping down to the opening of your impatient
 				canal. As you lie together, your bodies slowly swaying, you position your nipples to dance together and jostle each other.
-				${data.planner.slave.slaveName}'s leg pets your thigh and calf.  You finally roll onto your sides, allowing for an easier exploration of each other's
+				${data.planner.slave.slaveName}'s leg pets your thigh and calf. You finally roll onto your sides, allowing for an easier exploration of each other's
 				nethers.
 			</p>
 			<p>
@@ -754,7 +754,6 @@ App.Events.pcBirthday.Desc = (function(bday) {
 		 * @param {App.Events.pcBirthday.EventData} data
 		 */
 		renderPartyScene_Arrival: function(data) {
-
 			let html = `
 			<p>
 				Approaching the multi-purpose conference room, you can hear `;
@@ -1040,7 +1039,7 @@ App.Events.pcBirthday.Desc = (function(bday) {
 				html += `Jury's still out on whether you're missing a dong. But I'm hoping you are.`;
 			}
 
-			`"
+			html += `"
 				She grazes a palm toward your inner thigh.
 			</p>`;
 
diff --git a/src/events/scheduled/sePCBirthday.js b/src/events/scheduled/sePCBirthday.js
index 77233b8fa0b5330232b44a5762aa794235d0cb78..4ba873ac3e6ae9ff8afdac2366426ef1a37e71ef 100644
--- a/src/events/scheduled/sePCBirthday.js
+++ b/src/events/scheduled/sePCBirthday.js
@@ -21,7 +21,7 @@
  * The PC birthday event can trigger when `birthWeek` reaches 0.
  *
  * Depending on the game state (or depending on RNG), the event may have different "moods",
- * leading to their own variants.  A "mood" is just a string describing the general emotion
+ * leading to their own variants. A "mood" is just a string describing the general emotion
  * of the day: happy, solemn, etc. (In practice this isn't used much yet.)
  *
  * If the PC has a slave in an important role that meets certain criteria, she may be selected
diff --git a/src/facilities/ads.js b/src/facilities/ads.js
index 8494ed0c014a0ca0c1886618efb15300bc44a70f..3bdf35f2471472058f5a769152574aec937ee927 100644
--- a/src/facilities/ads.js
+++ b/src/facilities/ads.js
@@ -356,19 +356,7 @@ App.Ads.report = function(building, preview = false) {
 		return t;
 	}
 
-	function payVarietyBonus() {
-		if (!preview) {
-			if (building === "brothel") {
-				const adsIncome = DL*random(20, 30);
-				V.facility[building].adsIncome += adsIncome;
-				cashX(adsIncome, (building + "Ads"));
-			} else if (building === "club") {
-				repX(DL*random(5, 10), (building + "Ads"));
-			}
-		}
-	}
-
-	function payAdBonus() {
+	function payBonus() {
 		if (!preview) {
 			if (building === "brothel") {
 				const adsIncome = DL*random(20, 30);
@@ -407,7 +395,7 @@ App.Ads.report = function(building, preview = false) {
 		} else {
 			let variety = adMgr.varietyBonus(App.Ads.Categories.assetSize);
 			if (variety === 1) {
-				payVarietyBonus();
+				payBonus();
 				t += `The ${building} offers a `;
 				if (building === "brothel") {
 					t+= `<span class="yellowgreen">`;
@@ -432,7 +420,7 @@ App.Ads.report = function(building, preview = false) {
 		if (pref === 0) { /* customers don't care*/
 		} else if (adCampaign.spending > 0) {
 			if ((adCampaign.stacked === pref) && (girls === adCampaign.stacked)) {
-				payAdBonus();
+				payBonus();
 				t += `Its advertising for `;
 				if (adCampaign.stacked === 1) {
 					t += `stacked `;
@@ -544,7 +532,7 @@ App.Ads.report = function(building, preview = false) {
 			} else {
 				let variety = adMgr.varietyBonus(App.Ads.Categories.preg);
 				if (variety === 1) {
-					payVarietyBonus();
+					payBonus();
 					t += `The ${building} offers a `;
 					if (building === "brothel") {
 						t+= `<span class="yellowgreen">`;
@@ -569,7 +557,7 @@ App.Ads.report = function(building, preview = false) {
 			if (pref === 0) { /* customers don't care*/
 			} else if (adCampaign.spending > 0) {
 				if ((adCampaign.preg === pref) && (girls === adCampaign.preg)) {
-					payAdBonus();
+					payBonus();
 					t += `Its advertising for `;
 					if (adCampaign.preg === 1) {
 						t += `pregnant `;
@@ -682,7 +670,7 @@ App.Ads.report = function(building, preview = false) {
 		} else {
 			let variety = adMgr.varietyBonus(App.Ads.Categories.mods);
 			if (variety === 1) {
-				payVarietyBonus();
+				payBonus();
 				t += `The ${building} offers `;
 				if (building === "brothel") {
 					t+= `<span class="yellowgreen">`;
@@ -707,7 +695,7 @@ App.Ads.report = function(building, preview = false) {
 		if (pref === 0) { /* customers don't care*/
 		} else if (adCampaign.spending > 0) {
 			if ((adCampaign.modded === pref) && (girls === adCampaign.modded)) {
-				payAdBonus();
+				payBonus();
 				t += `Its advertising for `;
 				if (adCampaign.modded === 1) {
 					t += `heavily modified `;
@@ -819,7 +807,7 @@ App.Ads.report = function(building, preview = false) {
 		} else {
 			let variety = adMgr.varietyBonus(App.Ads.Categories.assetOrigin);
 			if (variety === 1) {
-				payVarietyBonus();
+				payBonus();
 				t += `The ${building} offers `;
 				if (building === "brothel") {
 					t+= `<span class="yellowgreen">`;
@@ -844,7 +832,7 @@ App.Ads.report = function(building, preview = false) {
 		if (pref === 0) { /* customers don't care*/
 		} else if (adCampaign.spending > 0) {
 			if ((adCampaign.implanted === pref) && (girls === adCampaign.implanted)) {
-				payAdBonus();
+				payBonus();
 				t += `Its advertising for `;
 				if (adCampaign.implanted === 1) {
 					t += `implanted or surgically improved `;
@@ -956,7 +944,7 @@ App.Ads.report = function(building, preview = false) {
 			} else {
 				let variety = adMgr.varietyBonus(App.Ads.Categories.genitalia);
 				if (variety === 1) {
-					payVarietyBonus();
+					payBonus();
 					t += `The ${building} offers a `;
 					if (building === "brothel") {
 						t+= `<span class="yellowgreen">`;
@@ -987,7 +975,7 @@ App.Ads.report = function(building, preview = false) {
 			if (pref === 0) { /* customers don't care*/
 			} else if (adCampaign.spending > 0) {
 				if ((adCampaign.XX === pref) && (girls === adCampaign.XX)) {
-					payAdBonus();
+					payBonus();
 					t += `Its advertising for girls `;
 					if (adCampaign.XX === 1) {
 						t += `with pussies `;
@@ -1104,7 +1092,7 @@ App.Ads.report = function(building, preview = false) {
 		} else {
 			let variety = adMgr.varietyBonus(App.Ads.Categories.age);
 			if (variety === 1) {
-				payVarietyBonus();
+				payBonus();
 				t += `The ${building} offers girls `;
 				if (building === "brothel") {
 					t+= `<span class="yellowgreen">`;
@@ -1129,7 +1117,7 @@ App.Ads.report = function(building, preview = false) {
 		if (pref === 0) { /* customers don't care*/
 		} else if (adCampaign.spending > 0) {
 			if ((adCampaign.age === pref) && (girls === adCampaign.age)) {
-				payAdBonus();
+				payBonus();
 				t += `Its advertising matches most customers' age preferences and the girls in the ${building} match the ages as advertised. `;
 				t += reputation(building, 1, preview);
 			} else if ((adCampaign.age === pref) && (girls !== adCampaign.age)) {
diff --git a/src/facilities/armory/armoryFramework.js b/src/facilities/armory/armoryFramework.js
index ac077b9815b7b0b0617e224f3e181fa685bac713..c2a595acecf80bdf0230b4a64636faa1bc82ca73 100644
--- a/src/facilities/armory/armoryFramework.js
+++ b/src/facilities/armory/armoryFramework.js
@@ -7,7 +7,7 @@ App.Data.Facilities.armory = {
 		position: "bodyguard",
 		positionAbbreviation: "BG",
 		assignment: Job.BODYGUARD,
-		careers: App.Data.misc.bodyguardCareers,
+		careers: App.Data.Careers.Leader.bodyguard,
 		skill: "bodyguard",
 		publicSexUse: true,
 		fuckdollAccepted: false,
diff --git a/src/facilities/bodyModification/bodyModification.js b/src/facilities/bodyModification/bodyModification.js
new file mode 100644
index 0000000000000000000000000000000000000000..96ec38bc8c26a8acdfae6c6a872ece274ec4c163
--- /dev/null
+++ b/src/facilities/bodyModification/bodyModification.js
@@ -0,0 +1,1409 @@
+/**
+ * UI for the Body Modification system/studio.  Refreshes without refreshing the passage.
+ * @param {App.Entity.SlaveState} slave
+ * @param {boolean} cheat if true, will hide scenes, prevent damage to slaves, and keep the player from being billed for mods.
+ */
+App.UI.bodyModification = function(slave, cheat = false) {
+	const container = document.createElement("span");
+	container.id = "body-modification";
+	const {
+		He, His,
+		he, his, him, himself
+	} = getPronouns(slave);
+	Enunciate(slave);
+	let piercingLevel;
+	let modReaction;
+	let scarApplied;
+	let tattooChoice;
+	let brandApplied;
+	let degradation;
+
+	container.append(createPage());
+	return container;
+
+	function createPage() {
+		const el = new DocumentFragment();
+		if (!cheat) {
+			if (V.seeImages > 0) {
+				App.Events.drawEventArt(el, slave);
+			}
+			el.append(intro());
+			el.append(reaction());
+		}
+		el.append(piercings());
+		el.append(tattoos());
+		el.append(branding());
+		el.append(scar());
+		return el;
+	}
+
+	function intro() {
+		const el = new DocumentFragment();
+		App.UI.DOM.appendNewElement("h1", el, "Body Modification Studio");
+		App.UI.DOM.appendNewElement("div", el, `${SlaveFullName(slave)} is lying strapped down on the table in your body modification studio. ${He} is entirely at your mercy.`);
+		return el;
+	}
+
+	function reaction() {
+		const el = new DocumentFragment();
+		let r = [];
+		if (brandApplied || degradation || scarApplied || modReaction) {
+			if (slave.fuckdoll === 0) {
+				if (canSee(slave)) {
+					r.push(`There's a mirror on the ceiling, so ${he} can see`);
+				} else {
+					r.push(`${He} can't see, so `);
+					if (canHear(slave)) {
+						r.push(`you're careful to describe`);
+					} else {
+						r.push(`${he} must, by ${himself}, get a feel for`);
+					}
+				}
+				r.push(`${his} new appearance.`);
+			}
+			if (brandApplied) {
+				r.push(`The smell of burnt flesh hangs in the air. Being branded <span class="health.dec">has hurt ${his} health a little.</span>`);
+				healthDamage(slave, 10);
+				brandApplied = false;
+			}
+			if (scarApplied) {
+				if (V.scarTarget.local === "entire body") {
+					switch (V.scarDesign.local) {
+						case "burn":
+							r.push(`Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the skin with an open flame.`);
+							break;
+						case "surgical":
+							if (V.PC.skill.medicine >= 100) {
+								r.push(`Your medical mastery is perfect, so creating Frankenstein's monster was a deliberate work of art.`);
+							} else if (V.PC.skill.medicine > 0) {
+								r.push(`Your medical skills are progressing, and the Frankenstein effect reminds you of your earliest attempts.`);
+							} else {
+								r.push(`You really slashed away with your knife, but were careful not to allow ${him} to bleed out.`);
+							}
+							break;
+						default:
+							r.push(`The best way to apply scarring to the entire body is with a good old fashioned whip. ${His} body is a mess of crisscrossed lines`);
+							if (hasAnyNaturalLimbs(slave)) {
+								r.push(`, and ${his} `);
+								if (getLimbCount(slave, piercingLevel) > 1) {
+									r.push(`limbs twisted so violently in their restraints that they too have`);
+								} else {
+									r.push(`only limb twists so violently in its restraints that it too has`);
+								}
+								r.push(` become scarred`);
+							}
+							r.push(r.pop() + ".");
+					}
+					r.push(`No matter how you chose to apply it, scarring so much of ${his} body has <span class="health.dec"> hurt ${his} health.</span>`);
+					healthDamage(slave, 20);
+				} else {
+					if (slave.scar[V.scarTarget.local][V.scarDesign.local] > 0) {
+						r.push(`This is not the first time ${he} was scarred like this.`);
+					}
+					switch (V.scarDesign.local) {
+						case "whip":
+							r.push(`Targeting a single area with a whip is not easy. You set the mood by carefully arranging candles dripping on to a whimpering ${slave.slaveName}, then got ${his} attention with a quick`);
+							if (canSee(slave)) {
+								r.push(`wave`);
+							} else if (canHear(slave)) {
+								r.push(`crack`);
+							} else {
+								r.push(`tap`);
+							}
+							r.push(`of the whip. One by one, you carefully snuffed out the candles, flicking hot wax as you went. After pausing a moment, you prepared to leave your mark.`);
+							if (["penis", "vagina"].includes(V.scarTarget.local)) {
+								if (slave.dick > 4 && V.seeDicks) {
+									r.push(`${His} dick was large enough that it was not too difficult to hit,`);
+								} else if (slave.dick > 0 && V.seeDicks) {
+									r.push(`${His} dick was a challengingly small target,`);
+								} else {
+									if (slave.clit > 0) {
+										r.push(`${His} clit was a difficult target,`);
+									} else {
+										r.push(`${His} clit was an impossibly tiny target,`);
+									}
+								}
+								r.push(`but the end was never in doubt. The tip connected with ${his} most intimate place on the first try, and plunged ${him} into absolute agony.`);
+							} else {
+								r.push(`The	end was never in doubt. A few strokes of the whip plunged ${him} into agony ${his} body will not allow ${him} to forget.`);
+							}
+							break;
+						case "burn":
+							r.push(`Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the ${slave.skin} skin of ${his} ${V.scarTarget.local} with an open flame.`);
+							break;
+						case "surgical":
+							if (V.PC.skill.medicine >= 100) {
+								r.push(`Your medical mastery is perfect, so creating such a scar was a deliberate act of degradation.`);
+							} else if (V.PC.skill.medicine > 0) {
+								r.push(`Your medical skills are progressing, and the sloppy scar reminds you of your earliest attempts.`);
+							} else {
+								r.push(`You really slashed away at ${V.scarTarget.local} with your knife, but were careful not to allow ${him} to bleed out.`);
+							}
+							break;
+						default:
+							r.push(`You had no shortage of kinky and medical tools for applying scars. ${His} ${slave.skin} ${V.scarTarget.local} is bleeding profusely.`);
+					}
+
+					r.push(`No matter how you chose to apply it, being scarred <span class="health.dec"> hurt ${his} health a little.</span>`);
+					healthDamage(slave, 10);
+				}
+				r.push(`Afterwards you seal the wounds with a white medical spray. Infection is no risk to ${slave.slaveName} thanks to your curatives, but in order to form obvious scar tissue you had to keep air out and delay normal healing as long as possible.`);
+				scarApplied = false;
+			}
+			if (slave.fetish !== "mindbroken" && slave.fuckdoll === 0) {
+				if (degradation > 1) {
+					if (degradation > 5) {
+						if (slave.devotion <= 50 && slave.trust < -50) {
+							r.push(`${He} is appalled by the whorish spectacle you have made of ${him}. ${He} <span class="gold">fears</span> you all the more for this but is so terrified of you it does not affect ${his} submission.`);
+							slave.trust -= 10;
+						} else if (slave.devotion <= 50) {
+							r.push(`${He} is appalled by the whorish spectacle you have made of ${him}. ${He} <span class="mediumorchid">hates</span> and <span class="gold">fears</span> you for this.`);
+							slave.devotion -= 10;
+							slave.trust -= 10;
+						} else {
+							r.push(`${He} is shocked by the whorish spectacle you have made of ${him}. However, ${he} is so submissive to your will that ${he} <span class="hotpink">accepts</span> that the slave `);
+							if (canSee(slave)) {
+								r.push(`in the mirror`);
+							} else {
+								r.push(`${he} pictures`);
+							}
+							r.push(`is who ${he} is now.`);
+							slave.devotion += 4;
+						}
+					} else {
+						if (slave.devotion < -20 && slave.trust < 20) {
+							r.push(`${He} is <span class="gold">afraid</span> that ${he} has been permanently altered against ${his} will, but is also scared of your reaction to any objection and suppresses ${his} disgust.`);
+							slave.trust -= 5;
+						} else if (slave.devotion < -20) {
+							r.push(`${He} is <span class="mediumorchid">angry</span> and <span class="gold">afraid</span> that ${he} has been permanently altered against ${his} will.`);
+							slave.devotion -= 5;
+							slave.trust -= 5;
+						} else {
+							r.push(`${He} is saddened to have been altered against ${his} will. However, ${he} realizes that ${he} is a slave, so ${he} <span class="hotpink">accepts</span> your work.`);
+							slave.devotion += 2;
+						}
+					}
+					degradation = 0;
+				}
+				if (modReaction) {
+					r.push(modReaction);
+					modReaction = false;
+				}
+			}
+		}
+		App.Events.addNode(el, r, "p");
+		return el;
+	}
+
+	function piercings() {
+		const el = new DocumentFragment();
+		let r = [];
+		const piercingLocations = ["ear", "nose", "eyebrow", "lips", "tongue", "nipples", "areolae", "navel", "corset", "clit", "vagina", "dick", "anus"];
+		// DESCRIPTIONS
+		App.UI.DOM.appendNewElement("h2", el, "Piercings");
+
+		for (const piercing of piercingLocations.concat(["chastity"])) {
+			r.push(App.UI.DOM.makeElement("div", App.Desc.piercing(slave, piercing)));
+		}
+		if (r.length === 0) {
+			r.push(App.UI.DOM.makeElement("div", `${His} smooth ${slave.skin} skin is completely unpierced.`));
+		}
+		App.Events.addNode(el, r);
+		r = [];
+
+		// Apply piercings
+		r.push(`Choose piercing style:`);
+		const piercingLevelNames = new Map([
+			["Remove", 0],
+			["Light", 1],
+			["Heavy", 2],
+			["Smart", 3]
+		]);
+		let linkArray = [];
+		for (const [title, num] of piercingLevelNames) {
+			if (piercingLevel === num) {
+				linkArray.push(App.UI.DOM.disabledLink(title, ["Currently selected"]));
+			} else {
+				linkArray.push(
+					App.UI.DOM.link(
+						title,
+						() => {
+							piercingLevel = num;
+							refresh();
+						}
+					)
+				);
+			}
+		}
+		r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		App.Events.addNode(el, r, "div");
+		r = [];
+
+		// Determine parts that cannot be pierced
+		let validPiercingLocations = Array.from(piercingLocations);
+
+		if (piercingLevel !== 0) { // Sometimes a piercing winds up in a place that is no longer valid.  Make sure players can always remove an existing piercing.
+			if (slave.nipples !== "fuckable") {
+				removePiercingLocation("nipples");
+			}
+
+			if (slave.vagina === -1) {
+				removePiercingLocation("vagina");
+			}
+
+			if (slave.dick === 0) {
+				removePiercingLocation("dick");
+				if (slave.vagina === -1) {
+					removePiercingLocation("clit");
+				}
+			}
+		}
+
+		function removePiercingLocation(location) {
+			const index = validPiercingLocations.indexOf(location);
+			validPiercingLocations.splice(index, 1);
+		}
+
+		if (piercingLevel < 3) {
+			if (piercingLevel === 0) {
+				r.push(`Remove piercings from:`);
+			} else if (piercingLevel === 1) {
+				r.push(`Lightly pierce ${his}:`);
+			} else if (piercingLevel === 2) {
+				r.push(`Heavily pierce ${his}:`);
+			}
+			// Entire body
+			linkArray = [];
+			linkArray.push(
+				App.UI.DOM.link(
+					"Entire body",
+					() => {
+						modReaction = "";
+						for (const location of validPiercingLocations) {
+							if (slave[`${location}Piercing`] !== piercingLevel) {
+								modReaction += App.Medicine.Modification.setPiercing(slave, location, piercingLevel);
+								if (piercingLevel > 1) {
+									degradation += 1;
+								}
+							}
+						}
+						refresh();
+					}
+				)
+			);
+
+			// Each individual piercing
+			for (const location of validPiercingLocations) {
+				if (slave[`${location}Piercing`] !== piercingLevel) {
+					linkArray.push(
+						App.UI.DOM.link(
+							capFirstChar(location),
+							() => {
+								modReaction = "";
+								modReaction += App.Medicine.Modification.setPiercing(slave, location, piercingLevel);
+								if (piercingLevel > 1) {
+									degradation += 1;
+								}
+								refresh();
+							}
+						)
+					);
+				}
+			}
+			r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		} else if (piercingLevel === 3) {
+			// Smart piercings
+			if (slave.vagina !== -1 || slave.dick !== 0) {
+				if (slave.clitPiercing !== 3) {
+					r.push(`Give ${him} a`);
+					r.push(
+						App.UI.DOM.link(
+							"smart piercing",
+							() => {
+								modReaction = App.Medicine.Modification.setPiercing(slave, "clit", 3);
+								slave.clitSetting = "all";
+								degradation += 1;
+								refresh();
+							},
+							[],
+							"",
+							`Costs ${cashFormat(V.SPcost)}, unlocks options to mold sexuality`
+						)
+					);
+				} else {
+					r.push(`${He} already has a smart piercing!`);
+				}
+			}
+		}
+		App.Events.addNode(el, r, "div");
+		return el;
+	}
+
+	function tattoos() {
+		const el = new DocumentFragment();
+		let r = [];
+		let noTats;
+		const tattooLocations = new Map([
+			["shoulder", "shoulders"],
+			["lips", "lips"],
+			["breast", "boobs"],
+			["upper arm", "arms"],
+			["back", "back"],
+			["lower back", "stamp"],
+			["buttock", "butt"],
+			["vagina", "vagina"],
+			["dick", "dick"],
+			["anus", "anus"],
+			["thigh", "legs"]
+		]);
+		// DESCRIPTIONS
+		App.UI.DOM.appendNewElement("h2", el, "Tattoos");
+
+		for (const name of tattooLocations.keys()) {
+			r.push(App.UI.DOM.makeElement("div", App.Desc.tattoo(slave, name)));
+		}
+		if (r.length === 0) {
+			r.push(App.UI.DOM.makeElement("div", `${His} smooth ${slave.skin} skin is completely unmarked.`));
+			noTats = true;
+		}
+		App.Events.addNode(el, r);
+		r = [];
+
+		// Apply tattoos
+		r.push(`Choose a tattoo style:`);
+		const tattooChoiceNames = new Set([
+			"tribal patterns",
+			"flowers",
+			"counting",
+			"advertisements",
+			"rude words",
+			"degradation",
+			"Asian art",
+			"scenes",
+			"bovine patterns",
+			"permanent makeup",
+			"sacrilege",
+			"sacrament",
+			"possessive",
+			"paternalist",
+		]);
+		if (slave.anusTat !== 0) {
+			tattooChoiceNames.add("bleached");
+		}
+		let linkArray = [];
+		for (const style of tattooChoiceNames) {
+			if (tattooChoice === style) {
+				linkArray.push(App.UI.DOM.disabledLink(capFirstChar(style), ["Currently selected"]));
+			} else {
+				linkArray.push(
+					App.UI.DOM.link(
+						capFirstChar(style),
+						() => {
+							tattooChoice = style;
+							refresh();
+						}
+					)
+				);
+			}
+		}
+		if (!noTats) {
+			linkArray.push(
+				App.UI.DOM.link(
+					"Remove a tattoo",
+					() => {
+						tattooChoice = 0;
+						refresh();
+					}
+				)
+			);
+		}
+		r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		App.Events.addNode(el, r, "div");
+		r = [];
+
+		// Determine parts that cannot be pierced
+		let validTattooLocations;
+		if (tattooChoice === "bleached") {
+			validTattooLocations = ["anus"];
+		} else {
+			validTattooLocations = Array.from(tattooLocations.keys());
+			if (!hasAnyNaturalArms(slave)) {
+				removeTattooLocation("upper arm");
+			}
+
+			if (!hasAnyNaturalArms(slave)) {
+				removeTattooLocation("thigh");
+			} else {
+				// There is a disagreement between description and application if we are talking about the leg or thigh.  Switch to leg now for application.
+				const index = validTattooLocations.indexOf("thigh");
+				validTattooLocations.splice(index, 1, "leg");
+			}
+
+			if (slave.dick === 0 || tattooChoice === "scenes") {
+				removeTattooLocation("dick");
+			}
+			if ((tattooChoice === "Asian art" || tattooChoice === "scenes") && slave.anusTat === "bleached") { // leave existing bleached anus alone
+				removeTattooLocation("anus");
+			}
+		}
+
+		function removeTattooLocation(location) {
+			const index = validTattooLocations.indexOf(location);
+			validTattooLocations.splice(index, 1);
+		}
+
+		if (tattooChoice === 0) {
+			r.push(`Clean the ink off of ${him}:`);
+		} else if (tattooChoice === "counting") {
+			r.push(`Add tallies of ${his} sexual exploits to ${him}:`);
+		} else if (tattooChoice === "bleached") {
+			r.push(`Bleach ${his}:`);
+		} else if (tattooChoice) {
+			r.push(`Add ${tattooChoice} to ${his}:`);
+		}
+		// Entire body
+		linkArray = [];
+		if (tattooChoice !== undefined && tattooChoice !== "bleached") {
+			linkArray.push(
+				App.UI.DOM.link(
+					"Entire body",
+					() => {
+						modReaction = "";
+						for (const location of validTattooLocations) {
+							if (slave[`${location}tattoo`] !== tattooChoice) {
+								applyTat(location);
+							}
+						}
+						refresh();
+					}
+				)
+			);
+		}
+		// Each individual tattoo
+		for (const location of validTattooLocations) {
+			if (slave[`${location}tattoo`] !== tattooChoice) {
+				linkArray.push(
+					App.UI.DOM.link(
+						capFirstChar(location),
+						() => {
+							modReaction = "";
+							applyTat(location);
+							refresh();
+						}
+					)
+				);
+			}
+		}
+		r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		App.Events.addNode(el, r, "div");
+
+		el.append(oddTattoos());
+
+		const customEl = document.createElement("div");
+		customEl.id = "custom-el";
+		customEl.append(
+			App.UI.DOM.link(
+				"Show custom tattoo locations",
+				() => {
+					jQuery("#custom-el").empty().append(customTats());
+				}
+			)
+		);
+		el.append(customEl);
+
+		return el;
+
+		function applyTat(location) {
+			tattooChoice = (location === "lips" && tattooChoice === "scenes") ? "permanent makeup" : tattooChoice;
+			modReaction += App.Medicine.Modification.setTattoo(slave, tattooLocations.get(location), tattooChoice);
+			if (!["flowers", "paternalist", "tribal patterns", 0].includes(tattooChoice)) {
+				degradation += 1;
+			}
+		}
+
+		function oddTattoos() {
+			const el = new DocumentFragment();
+			let linkArray;
+			let r = [];
+			if (slave.belly >= 10000 && slave.bellyPreg < 450000 && slave.bellyFluid < 5000) {
+				if (slave.bellyTat === 0) {
+					r.push(`${He} has no navel tattoos.`);
+				} else {
+					r.push(`${His} navel is tattooed with ${slave.bellyTat}.`);
+				}
+				if (slave.bellyTat === 0) {
+					const bellyTats = new Map([
+						["Heart", "a heart"],
+						["Star", "a star"],
+						["Butterfly", "a butterfly"],
+					]);
+					linkArray = [];
+					for (const [title, value] of bellyTats) {
+						linkArray.push(
+							App.UI.DOM.link(
+								title,
+								() => {
+									tattooChoice = value;
+									applyTat("belly");
+									refresh();
+								}
+							)
+						);
+					}
+				}
+				if (slave.bellyTat !== 0) {
+					linkArray.push(
+						App.UI.DOM.link(
+							"Remove tattoos",
+							() => {
+								tattooChoice = 0;
+								applyTat("belly");
+								refresh();
+							}
+						)
+					);
+				}
+				r.push(App.UI.DOM.generateLinksStrip(linkArray));
+			} else if (slave.bellyPreg >= 450000) {
+				r.push(`${His} middle is large and taut enough to be a suitable canvas for a navel focused tattoo, but ${his} brood is too active to permit the needle to do its work.`);
+			} else if (slave.bellyFluid >= 10000) {
+				r.push(`${His} middle is large and taut enough to be a suitable canvas for a navel focused tattoo, but the pressure applied to ${his} stomach will likely force ${him} to release its contents.`);
+			} else {
+				r.push(`${His} middle isn't large enough to be a suitable canvas for a navel focused tattoo.`);
+			}
+			App.Events.addNode(el, r, "div");
+
+			r = [];
+			linkArray = [];
+			if (slave.birthsTat > 0) {
+				if (slave.birthsTat > 1) {
+					r.push(`${He} has a series of ${num(slave.birthsTat)} baby-shaped tattoos adorning ${his} stomach; one for each successful`);
+					if (slave.pregKnown === 1) {
+						r.push(`pregnancy and a temporary one for ${his} current pregnancy.`);
+					} else {
+						r.push(`pregnancy.`);
+					}
+				} else {
+					r.push(`${He} has a single baby-shaped tattoo${(slave.pregKnown === 1) ? `, and one temporary one,` : ``} adorning ${his} stomach.`);
+				}
+				if (slave.bellyTat !== 0) {
+					linkArray.push(
+						App.UI.DOM.link(
+							"Remove tattoos",
+							() => {
+								slave.birthsTat = -1;
+								billMod();
+								refresh();
+							}
+						)
+					);
+				}
+			} else if (slave.birthsTat === 0) {
+				if (slave.pregKnown === 1) {
+					r.push(`${He} has a single baby-shaped temporary tattoo adorning ${his} stomach.`);
+					r.push(
+						App.UI.DOM.link(
+							"Remove it",
+							() => {
+								slave.birthsTat = -1;
+								refresh();
+							}
+						)
+					);
+					if (slave.abortionTat > -1) {
+						r.push(App.UI.DOM.makeElement("span", `This will only remove birth tracking`, "note"));
+					}
+				} else {
+					r.push(`${He} is scheduled to receive a tattoo each time ${he} gives birth.`);
+					r.push(
+						App.UI.DOM.link(
+							"Cancel",
+							() => {
+								slave.birthsTat = -1;
+								refresh();
+							}
+						)
+					);
+				}
+			} else {
+				r.push(`Have ${him} receive a tattoo each time ${he} gives birth.`);
+				r.push(
+					App.UI.DOM.link(
+						"Begin keeping track",
+						() => {
+							slave.birthsTat = 0;
+							refresh();
+						}
+					)
+				);
+			}
+			App.Events.addNode(el, r, "div");
+
+			r = [];
+
+			if (slave.abortionTat > 0) {
+				if (slave.abortionTat > 1) {
+					r.push(`${He} has a series of ${num(slave.abortionTat)} crossed out baby-shaped tattoos${(slave.pregKnown === 1) ? `, and one uncrossed one,` : ``} adorning ${his} stomach.`);
+				} else {
+					r.push(`${He} has a single crossed out baby-shaped tattoo${(slave.pregKnown === 1) ? `, and one uncrossed one,` : ``} adorning ${his} stomach.`);
+				}
+				r.push(
+					App.UI.DOM.link(
+						"Remove tattoos",
+						() => {
+							slave.abortionTat = -1;
+							billMod();
+							refresh();
+						}
+					)
+				);
+			} else if (slave.abortionTat === 0) {
+				if (slave.pregKnown === 1) {
+					r.push(`${He} has a single baby-shaped temporary tattoo adorning ${his} stomach.`);
+					r.push(
+						App.UI.DOM.link(
+							"Remove it",
+							() => {
+								slave.abortionTat = -1;
+								refresh();
+							}
+						)
+					);
+					if (slave.birthsTat > -1) {
+						r.push(App.UI.DOM.makeElement("span", `This will only remove abortion tracking`, "note"));
+					}
+				} else {
+					r.push(`${He} is scheduled to receive a tattoo each time ${he} gets an abortion or miscarries.`);
+					r.push(
+						App.UI.DOM.link(
+							"Cancel",
+							() => {
+								slave.abortionTat = -1;
+								refresh();
+							}
+						)
+					);
+				}
+			} else {
+				r.push(`Have ${him} receive a tattoo for each abortion or miscarriage ${he} has.`);
+				r.push(
+					App.UI.DOM.link(
+						"Begin keeping track",
+						() => {
+							slave.abortionTat = 0;
+							refresh();
+						}
+					)
+				);
+			}
+			App.Events.addNode(el, r, "div");
+			return el;
+		}
+
+		function customTats() {
+			const el = new DocumentFragment();
+			App.UI.DOM.appendNewElement("h3", el, "Custom Tattoos");
+			const r = [];
+			for (const location of validTattooLocations) {
+				const varName = tattooLocations.get(location);
+				if (varName) {
+					r.push(App.UI.DOM.makeElement("div", `${capFirstChar(location)}: `));
+					r.push(
+						App.UI.DOM.makeElement(
+							"div",
+							App.UI.DOM.makeTextBox(
+								slave[`${varName}Tat`],
+								(v) => {
+									modReaction += App.Medicine.Modification.setTattoo(slave, varName, v);
+									refresh();
+								}
+							)
+						)
+					);
+				}
+			}
+
+			r.push(App.UI.DOM.makeElement("div", `Custom: `));
+			r.push(
+				App.UI.DOM.makeElement(
+					"div",
+					App.UI.DOM.makeTextBox(
+						slave.custom.tattoo,
+						(v) => {
+							slave.custom.tattoo = v;
+							billMod();
+							refresh();
+						}
+					)
+				)
+			);
+			App.Events.addNode(el, r, "div", "grid-2columns-auto");
+			if (slave.custom.tattoo !== "") {
+				el.append(
+					App.UI.DOM.link(
+						"Remove custom Tattoo",
+						() => {
+							slave.custom.tattoo = "";
+							billMod();
+							refresh();
+						}
+					)
+				);
+			}
+			return el;
+		}
+	}
+
+	function branding() {
+		const el = new DocumentFragment();
+		const selection = document.createElement("span");
+		selection.id = "brand-selection";
+		selection.append(brand(slave, cheat));
+		el.append(selection);
+
+		if (slave.breedingMark === 1 && (V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
+			const r = [];
+			r.push(`${He} has an intricate tattoo on ${his} lower belly that suggests ${he} was made to be bred.`);
+			r.push(
+				App.UI.DOM.link(
+					"Remove it",
+					() => {
+						slave.breedingMark = 0;
+						refresh();
+					}
+				)
+			);
+			App.Events.addNode(el, r, "div");
+		}
+		return el;
+	}
+
+	function scar() {
+		const el = new DocumentFragment();
+		App.UI.DOM.appendNewElement("h2", el, "Scars");
+		let r = [];
+
+		for (const _scarName in slave.scar) {
+			const scarDiv = document.createElement("div");
+			scarDiv.append(`${His} ${_scarName} is marked with ${App.Desc.expandScarString(slave, _scarName)}: `);
+			scarDiv.append(
+				App.UI.DOM.link(
+					"Remove Scar",
+					() => {
+						scarApplied = 0;
+						delete slave.scar[_scarName];
+						billSurgery();
+						degradation -= 10;
+						refresh();
+					}
+				)
+			);
+			r.push(scarDiv);
+		}
+		if (r.length > 0) {
+			App.Events.addNode(el, r, "div");
+		} else {
+			App.UI.DOM.appendNewElement("div", el, `${His} skin is not scarred.`);
+		}
+
+		r = [];
+		r.push(`Use <strong>${V.scarDesign.local}</strong> or choose another scar:`);
+		const scarTypes = new Set([
+			"whip",
+			"burn",
+			"surgical",
+			"menacing",
+			"exotic"
+		]); // Other common scars might be battle scars or c-Section but it makes little sense to include them here
+		let linkArray = [];
+		for (const scarType of scarTypes) {
+			linkArray.push(
+				App.UI.DOM.link(
+					capFirstChar(scarType),
+					() => {
+						V.scarDesign.local = scarType;
+						refresh();
+					}
+				)
+			);
+		}
+		r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		App.Events.addNode(el, r, "div");
+
+		r = [];
+		r.push(`Or design your own:`);
+		r.push(
+			App.UI.DOM.makeTextBox(
+				V.scarDesign.local,
+				(v) => {
+					V.scarDesign.local = v;
+					refresh();
+				}
+			)
+		);
+		App.Events.addNode(el, r, "div");
+
+		r = [];
+		r.push(`Choose a site for scaring:`);
+
+		let scarLocations = new Map();
+
+		if (["exotic", "menacing"].includes(V.scarDesign.local)) {
+			scarLocations.set("Cheeks", "cheek");
+		} else {
+			// Sorted head to toe
+			scarLocations.set("Entire body", "entire body");
+
+			// Head
+			if (slave.earShape !== "none") {
+				scarLocations.set("Ears", "ear");
+			}
+			scarLocations.set("Cheeks", "cheek");
+			scarLocations.set("Neck", "neck");
+
+			// Torso
+			scarLocations.set("Chest", "chest");
+			scarLocations.set("Breasts", "breast");
+			scarLocations.set("Back", "back");
+			scarLocations.set("Lower Back", "lower back");
+			scarLocations.set("Belly", "belly");
+			scarLocations.set("Pubic Mound", "pubic mound");
+
+			if (slave.dick > 0) {
+				scarLocations.set("Penis", "penis");
+			}
+			if (slave.balls > 0 && slave.scrotum > 0) {
+				scarLocations.set("Testicles", "testicle");
+			}
+
+			// Arms
+			scarLocations.set("Shoulders", "shoulder");
+			if (hasAnyNaturalArms(slave)) {
+				scarLocations.set("Arm, upper", "upper arm");
+				scarLocations.set("Arm, lower", "lower arm");
+				scarLocations.set("Wrists", "wrist");
+				scarLocations.set("Hands", "hand");
+			}
+
+			// Legs
+			scarLocations.set("Buttocks", "buttock");
+			if (hasAnyNaturalLegs(slave)) {
+				scarLocations.set("Thighs", "thigh");
+				scarLocations.set("Calves", "calf");
+				scarLocations.set("Ankles", "ankle");
+				scarLocations.set("Feet", "foot");
+			}
+		}
+
+		linkArray = [];
+		for (const [text, value] of scarLocations) {
+			linkArray.push(
+				App.UI.DOM.link(
+					text,
+					() => {
+						V.scarTarget.local = value;
+						refresh();
+					}
+				)
+			);
+		}
+		r.push(App.UI.DOM.generateLinksStrip(linkArray));
+		App.Events.addNode(el, r, "div");
+
+		r = [];
+		r.push(`Or a custom site:`);
+		r.push(
+			App.UI.DOM.makeTextBox(
+				V.scarTarget.local,
+				(v) => {
+					V.scarTarget.local = v;
+					refresh();
+				}
+			)
+		);
+		App.Events.addNode(el, r, "div");
+
+		// Correct some "bad" choices"
+		if (["exotic", "menacing"].includes(V.scarDesign.local)) {
+			if (V.scarTarget.local !== "cheek") {
+				V.scarTarget.local = "cheek";
+			}
+		}
+
+		r = [];
+
+		if (["ankle", "breast", "buttock", "calf", "cheek", "ear", "foot", "hand", "lower arm", "shoulder", "testicle", "thigh", "upper arm", "wrist"].includes(V.scarTarget.local)) {
+			const _leftTarget = ("left " + V.scarTarget.local);
+			const _rightTarget = ("right " + V.scarTarget.local);
+			if (slave.scar[_leftTarget]) {
+				r.push(`${His}${_leftTarget} is already marked with ${App.Desc.expandScarString(slave, _leftTarget)}.`);
+			}
+			if (slave.scar[_rightTarget]) {
+				r.push(`${His}${_rightTarget} is already marked with ${App.Desc.expandScarString(slave, _rightTarget)}.`);
+			}
+			r.push(`Scar ${him} now with ''${V.scarDesign.local}'' on the`);
+			let _left = 0, _right = 0;
+			// overwrite brand
+
+			if (!(["upper arm", "lower arm", "wrist", "hand"].includes(V.scarTarget.local) && getLeftArmID(slave) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes(V.scarTarget.local) && getLeftLegID(slave) !== 1)) {
+				_left = 1;
+				// make next checks easier
+				r.push(
+					App.UI.DOM.link(
+						"left",
+						() => {
+							V.scarTarget.local = _leftTarget;
+							scarApplied = 1;
+							App.Medicine.Modification.addScar(slave, _leftTarget, V.scarDesign.local);
+							billMod();
+							degradation += 10;
+							refresh();
+						}
+					)
+				);
+			}
+			if (!(["upper arm", "lower arm", "wrist", "hand"].includes(V.scarTarget.local) && getRightArmID(slave) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes(V.scarTarget.local) && getRightLegID(slave) !== 1)) {
+				_right = 1;
+				// make next checks easier
+			}
+			if (_left && _right) {
+				r.push(`${V.scarTarget.local}, or the`);
+			}
+			if (_right) {
+				r.push(
+					App.UI.DOM.link(
+						"right",
+						() => {
+							V.scarTarget.local = _rightTarget;
+							scarApplied = 1;
+							App.Medicine.Modification.addScar(slave, _rightTarget, V.scarDesign.local);
+							billSurgery();
+							degradation += 10;
+							refresh();
+						}
+					)
+				);
+			}
+			if (!_left || !_right) {
+				r.push(`${V.scarTarget.local}?`);
+			}
+		} else {
+			if (slave.scar.hasOwnProperty(V.scarTarget.local)) {
+				if (slave.scar[V.scarTarget.local][V.scarDesign.local]) {
+					r.push(`${He} already has ${V.scarDesign.local} scars on ${his} V.scarTarget.local. You can make it worse.`);
+				} else {
+					// check how much scarring is on this part
+					const _scarTotalValue = (Object.values(slave.scar[V.scarTarget.local])).reduce((a, b) => a + b, 0);
+					if (_scarTotalValue) {
+						r.push(`That would be a new kind of scar to add to the growing collection on ${his} ${V.scarTarget.local}. Life can always be worse for a slave.`);
+					}
+				}
+			}
+			r.push(
+				App.UI.DOM.link(
+					"Scar",
+					() => {
+						let _scarArray;
+						if (V.scarTarget.local === "entire body" && V.scarDesign.local.includes("whip")) {
+							// Special case for whipping scene, produces two kinds of scars
+							App.Medicine.Modification.addScourged(slave);
+						} else {
+							// Normal entire body scarring
+							if (V.scarTarget.local === "entire body") {
+								_scarArray = ["left breast", "right breast", "back", "lower back", "left buttock", "right buttock"];
+								if (getLeftArmID(slave) === 0) {
+									_scarArray.push("left upper arm");
+								}
+								if (getRightArmID(slave) === 0) {
+									_scarArray.push("right upper arm");
+								}
+								if (getLeftLegID(slave) === 0) {
+									_scarArray.push("left thigh");
+								}
+								if (getRightLegID(slave) === 0) {
+									_scarArray.push("right thigh");
+								}
+							} else { // Single scar
+								_scarArray = [V.scarTarget.local];
+							}
+							for (const scar of _scarArray) {
+								App.Medicine.Modification.addScar(slave, scar, V.scarDesign.local);
+								degradation += 10;
+							}
+						}
+						billMod();
+						scarApplied = 1;
+						degradation += 10;
+						refresh();
+					}
+				)
+			);
+			r.push(`with ${V.scarDesign.local} on the ${V.scarTarget.local}${(slave.scar[V.scarTarget.local]) ? `, adding to the scars that are already there?` : `.`}`);
+		}
+		App.Events.addNode(el, r, "div");
+
+		return el;
+	}
+
+	function brand(slave, cheat = false) {
+		const el = new DocumentFragment();
+		let p = document.createElement('p');
+		let div = document.createElement('div');
+		const {
+			him, He,
+			his, His
+		} = getPronouns(slave);
+
+		App.UI.DOM.appendNewElement("h2", el, "Branding");
+
+		for (const brandPlace in slave.brand) {
+			div = document.createElement('div');
+			div.append(`${His} ${brandPlace} is marked with ${slave.brand[brandPlace]}`);
+			if (slave.brand[brandPlace] === V.brandDesign.official) {
+				div.append(`, your `);
+				div.append(App.UI.DOM.passageLink("official brand", "Universal Rules"));
+			}
+			div.append(": ");
+			if (!cheat) {
+				div.append(
+					App.UI.DOM.link(
+						"Remove Brand",
+						() => {
+							brandApplied = false;
+							delete slave.brand[brandPlace];
+							billSurgery();
+							degradation -= 10;
+							refresh();
+						},
+					)
+				);
+			} else {
+				div.append(
+					App.UI.DOM.link(
+						"Remove Brand",
+						() => {
+							delete slave.brand[brandPlace];
+							brandRefresh();
+						},
+					)
+				);
+			}
+			p.append(div);
+		}
+
+		if (jQuery.isEmptyObject(slave.brand)) {
+			App.UI.DOM.appendNewElement("div", p, `${His} skin is unmarked.`);
+		}
+
+		if (!(Object.values(slave.brand).includes(V.brandDesign.official)) && !cheat) {
+			div = document.createElement('div');
+			div.append(`${He} lacks your `);
+			div.append(App.UI.DOM.passageLink("official brand", "Universal Rules"));
+			div.append(`, "${V.brandDesign.official}."`);
+			p.append(div);
+		}
+
+		el.append(p);
+		p = document.createElement('p');
+
+		div = document.createElement('div');
+		div.append(`Use ''${V.brandDesign.local}'' or choose another brand: `);
+		div.append(symbolOptions("personal"));
+		p.append(div);
+
+		p.append(symbolBlock("dirtyWord"));
+		p.append(symbolBlock("genitalSymbol"));
+		p.append(symbolBlock("silhouettes"));
+		p.append(symbolBlock("FS"));
+
+		div = document.createElement('div');
+		div.append(`Or design your own: `);
+		div.append(
+			App.UI.DOM.makeTextBox(
+				V.brandDesign.local,
+				v => {
+					V.brandDesign.local = v;
+					brandRefresh();
+				},
+			)
+		);
+		p.append(div);
+		el.append(p);
+
+		p = document.createElement('p');
+		App.UI.DOM.appendNewElement("div", p, "Choose a site for branding: ");
+		const body = slaveBody();
+		p.append(partLinks(body.head));
+		p.append(partLinks(body.torso));
+		p.append(partLinks(body.arms));
+		p.append(partLinks(body.legs));
+
+		div = document.createElement('div');
+		div.append(`Or a custom site: `);
+		div.append(
+			App.UI.DOM.makeTextBox(
+				V.brandTarget.local,
+				v => {
+					V.brandTarget.local = v;
+					brandRefresh();
+				},
+			)
+		);
+		p.append(div);
+		el.append(p);
+
+		p = document.createElement('p');
+
+		if (["ankle", "breast", "buttock", "calf", "cheek", "ear", "foot", "hand", "lower arm", "shoulder", "testicle", "thigh", "upper arm", "wrist"].includes(V.brandTarget.local)) {
+			const leftTarget = ("left " + V.brandTarget.local);
+			const rightTarget = ("right " + V.brandTarget.local);
+			if (slave.brand[leftTarget]) {
+				p.append(`${His} ${leftTarget} is already marked with ${slave.brand[leftTarget]}. `);
+			}
+			if (slave.brand[rightTarget]) {
+				p.append(`${His} ${rightTarget} is already marked with ${slave.brand[rightTarget]}. `);
+			}
+			p.append(`Brand ${him} now with ''${V.brandDesign.local}'' on the `); // todo: break out bold
+			let _left;
+			let _right;
+			if (
+				!(["upper arm", "lower arm", "wrist", "hand"].includes(V.brandTarget.local) && getLeftArmID(slave) !== 1) &&
+				!(["thigh", "calf", "ankle", "foot"].includes(V.brandTarget.local) && getLeftLegID(slave) !== 1)
+			) {
+				_left = 1;// make next checks easier
+				if (!cheat) {
+					p.append(
+						App.UI.DOM.link(
+							"left",
+							() => {
+								brandApplied = true;
+								slave.brand[leftTarget] = check(V.brandDesign.local);
+								billMod();
+								degradation += 10;
+								refresh();
+							},
+						)
+					);
+				} else {
+					p.append(
+						App.UI.DOM.link(
+							"left",
+							() => {
+								slave.brand[leftTarget] = check(V.brandDesign.local);
+								refresh();
+							},
+						)
+					);
+				}
+
+				if (!(["upper arm", "lower arm", "wrist", "hand"].includes(V.brandTarget.local) && getRightArmID(slave) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes(V.brandTarget.local) && getRightLegID(slave) !== 1)) {
+					_right = 1; // make next checks easier
+				}
+				if (_left && _right) {
+					p.append(` ${V.brandTarget.local}, or the `);
+				}
+				if (_right) {
+					if (!cheat) {
+						p.append(
+							App.UI.DOM.link(
+								"right",
+								() => {
+									brandApplied = true;
+									slave.brand[rightTarget] = check(V.brandDesign.local);
+									billMod();
+									degradation += 10;
+									refresh();
+								},
+							)
+						);
+					} else {
+						p.append(
+							App.UI.DOM.link(
+								"right",
+								() => {
+									slave.brand[rightTarget] = check(V.brandDesign.local);
+									refresh();
+								},
+							)
+						);
+					}
+				}
+				p.append(`? `);
+				if (!_left || !_right) {
+					p.append(` ${V.brandTarget.local}`);
+					App.UI.DOM.appendNewElement("span", p, `Branding will slightly reduce ${his} beauty but may slowly increase your reputation.`, "note");
+				}
+			}
+		} else {
+			if (slave.brand[V.brandTarget.local] === V.brandDesign.local) {
+				p.append(`${He} already has ${V.brandDesign.local} on ${his} ${V.brandTarget.local}.`);
+			} else {
+				if (!cheat) {
+					p.append(
+						App.UI.DOM.link(
+							"Brand",
+							() => {
+								brandApplied = true;
+								slave.brand[V.brandTarget.local] = V.brandDesign.local;
+								billMod();
+								degradation += 10;
+								refresh();
+							},
+						)
+					);
+				} else {
+					p.append(
+						App.UI.DOM.link(
+							"Brand",
+							() => {
+								slave.brand[V.brandTarget.local] = V.brandDesign.local;
+								refresh();
+							},
+						)
+					);
+				}
+				p.append(` with ${V.brandDesign.local} on the ${V.brandTarget.local}`);
+				if (slave.brand[V.brandTarget.local]) {
+					p.append(`, covering the "${slave.brand[V.brandTarget.local]}" that is already there? `);
+				} else {
+					p.append(`. `);
+				}
+				App.UI.DOM.appendNewElement("span", p, `Branding will slightly reduce ${his} beauty but may slowly increase your reputation.`, "note");
+			}
+		}
+		el.append(p);
+		return el;
+
+		function symbolBlock(brandList) {
+			const div = document.createElement('div');
+			div.classList.add("choices");
+			div.append(symbolOptions(brandList));
+			return div;
+		}
+
+		function symbolOptions(brandList) {
+			const list = App.Medicine.Modification.Brands[brandList];
+			const array = [];
+			for (const brand in list) {
+				const frag = new DocumentFragment();
+				if (!cheat && list[brand].hasOwnProperty("requirements")) {
+					if (!list[brand].requirements(slave)) {
+						continue;
+					}
+				}
+				if (brandList === "FS") {
+					App.UI.DOM.appendNewElement("span", frag, "FS ", "note");
+				}
+				frag.append(
+					App.UI.DOM.link(
+						list[brand].displayName,
+						() => {
+							V.brandDesign.local = check(brand);
+							brandRefresh();
+						}
+					)
+				);
+				array.push(frag);
+			}
+			return App.UI.DOM.generateLinksStrip(array);
+		}
+
+		function slaveBody() {
+			const body = {};
+			// Sorted head to toe
+			// Head
+			body.head = {};
+			if (slave.earShape !== "none") {
+				body.head.ears = "Ears";
+			}
+			body.head.cheek = "Cheeks";
+			body.head.neck = "Neck";
+
+			// Torso
+			body.torso = {};
+			body.torso.chest = "Chest";
+			body.torso.breast = "Breasts";
+			body.torso.back = "Back";
+			body.torso["lower back"] = "Lower Back";
+			body.torso.belly = "Belly";
+			body.torso["pubic mound"] = "Pubic Mound";
+
+			if (slave.dick > 0) {
+				body.torso.penis = "Penis";
+			}
+			if (slave.balls > 0 && slave.scrotum > 0) {
+				body.torso.testicle = "Testicles";
+			}
+
+			// Arms
+			body.arms = {};
+			body.arms.shoulder = "Shoulders";
+			if (hasAnyNaturalArms(slave)) {
+				body.arms["upper arm"] = "Arm, upper";
+				body.arms["lower arm"] = "Arm, lower";
+				body.arms.wrist = "Wrists";
+				body.arms.hand = "Hands";
+			}
+
+			// Legs
+			body.legs = {};
+			body.legs.buttock = "Buttocks";
+			if (hasAnyNaturalLegs(slave)) {
+				body.legs.thigh = "Thighs";
+				body.legs.calf = "Calves";
+				body.legs.ankle = "Ankles";
+				body.legs.foot = "Feet";
+			}
+			return body;
+		}
+
+		function partLinks(bodyPartObj) {
+			const div = document.createElement("div");
+			div.classList.add("choices");
+			const array = [];
+			for (const bp in bodyPartObj) {
+				array.push(
+					App.UI.DOM.link(
+						bodyPartObj[bp],
+						() => {
+							V.brandTarget.local = check(bp);
+							brandRefresh();
+						}
+					)
+				);
+			}
+			div.append(App.UI.DOM.generateLinksStrip(array));
+			return div;
+		}
+
+		function check(brand) {
+			switch (brand) {
+				case "a big helping of your favorite food":
+					return "a big helping of " + V.PC.refreshment;
+				default:
+					return brand;
+			}
+		}
+
+		function brandRefresh() {
+			jQuery('#brand-selection').empty().append(brand(slave, cheat));
+		}
+	}
+
+
+	function refresh() {
+		jQuery("#body-modification").empty().append(createPage());
+	}
+
+	function billMod() {
+		if (!cheat) {
+			cashX(forceNeg(V.modCost), "slaveMod", slave);
+		}
+	}
+
+	function billSurgery() {
+		if (!cheat) {
+			cashX(forceNeg(V.surgeryCost), "slaveSurgery", slave);
+		}
+	}
+};
diff --git a/src/facilities/bodyModification/bodyModification.tw b/src/facilities/bodyModification/bodyModification.tw
new file mode 100644
index 0000000000000000000000000000000000000000..74cbfe0d6445fe245e36d74688fd68b05eacfec5
--- /dev/null
+++ b/src/facilities/bodyModification/bodyModification.tw
@@ -0,0 +1,5 @@
+:: Body Modification [nobr jump-from-safe]
+
+<<set $nextButton = "Confirm changes", $nextLink = "Slave Interact", $encyclopedia = "The Studio">>
+
+<<includeDOM App.UI.bodyModification(getSlave($AS))>>
\ No newline at end of file
diff --git a/src/facilities/brothel/brothelFramework.js b/src/facilities/brothel/brothelFramework.js
index c295f6c7d0331ae64d7ebd695c4db22425fae443..1230827d0d541a6de0c078b0056317913dcb87b1 100644
--- a/src/facilities/brothel/brothelFramework.js
+++ b/src/facilities/brothel/brothelFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.brothel = {
 	manager: {
 		position: "madam",
 		assignment: Job.MADAM,
-		careers: App.Data.misc.madamCareers,
+		careers: App.Data.Careers.Leader.madam,
 		skill: null,
 		publicSexUse: true,
 		fuckdollAccepted: false,
diff --git a/src/facilities/cellblock/cellblockFramework.js b/src/facilities/cellblock/cellblockFramework.js
index 6e44aa9628b87b869192b6a213d219ada49479fa..e5a7219eb9276ba6642e3f239b041aa4b2132e53 100644
--- a/src/facilities/cellblock/cellblockFramework.js
+++ b/src/facilities/cellblock/cellblockFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.cellblock = {
 	manager: {
 		position: "wardeness",
 		assignment: Job.WARDEN,
-		careers: App.Data.misc.wardenessCareers,
+		careers: App.Data.Careers.Leader.wardeness,
 		skill: "wardeness",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/clinic/clinicFramework.js b/src/facilities/clinic/clinicFramework.js
index c351774e0b3c6d1f481093381acd2c519694ce1f..23dd472c60def174c852bae283bbe3f97835d0db 100644
--- a/src/facilities/clinic/clinicFramework.js
+++ b/src/facilities/clinic/clinicFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.clinic = {
 	manager: {
 		position: "nurse",
 		assignment: Job.NURSE,
-		careers: App.Data.misc.nurseCareers,
+		careers: App.Data.Careers.Leader.nurse,
 		skill: "nurse",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/club/clubFramework.js b/src/facilities/club/clubFramework.js
index efe1827dadbbc500b3cd25e791bc14b4dfd514fb..e34db004dfcebbd2ca273ba0be575a48ed1fb8f8 100644
--- a/src/facilities/club/clubFramework.js
+++ b/src/facilities/club/clubFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.club = {
 	manager: {
 		position: "DJ",
 		assignment: Job.DJ,
-		careers: App.Data.misc.DJCareers,
+		careers: App.Data.Careers.Leader.DJ,
 		skill: "DJ",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/dairy/dairyFramework.js b/src/facilities/dairy/dairyFramework.js
index a6cb48cbaba2daaa7027470cba24f6a08222b0bb..1ff08a4391365e089fad0c391315c81c26a1f865 100644
--- a/src/facilities/dairy/dairyFramework.js
+++ b/src/facilities/dairy/dairyFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.dairy = {
 	manager: {
 		position: "milkmaid",
 		assignment: Job.MILKMAID,
-		careers: App.Data.misc.milkmaidCareers,
+		careers: App.Data.Careers.Leader.milkmaid,
 		skill: "milkmaid",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/farmyard/farmyardFramework.js b/src/facilities/farmyard/farmyardFramework.js
index f4f6630cc534a0eadadb00652b98ee6083c4c9f7..b79c787e6c8c1c5e224dbe567e50bc4b5ec5d4c1 100644
--- a/src/facilities/farmyard/farmyardFramework.js
+++ b/src/facilities/farmyard/farmyardFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.farmyard = {
 	manager: {
 		position: "farmer",
 		assignment: Job.FARMER,
-		careers: App.Data.misc.farmerCareers,
+		careers: App.Data.Careers.Leader.farmer,
 		skill: "farmer",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/farmyard/food/foodAmount.js b/src/facilities/farmyard/food/foodAmount.js
index 3b47b233838ff39efb603df9258cfd6528e87ebf..fcb94ca96039c2f7e8212ba04a458455fc95147e 100644
--- a/src/facilities/farmyard/food/foodAmount.js
+++ b/src/facilities/farmyard/food/foodAmount.js
@@ -32,7 +32,7 @@ App.Facilities.Farmyard.foodAmount = function(slave) {
 			food *= 1.2;
 		}
 
-		if (setup.farmerCareers.includes(S.Farmer.career)) {
+		if (App.Data.Careers.Leader.farmer.includes(S.Farmer.career)) {
 			food *= 1.2;
 		}
 	}
diff --git a/src/facilities/farmyard/shows/farmShowsIncome.js b/src/facilities/farmyard/shows/farmShowsIncome.js
index a9ec8eb55a203225e1e4ec1362e84a06eee8cc40..d8d51fd2540be95b0d2cbfd7bd4b7fe4b0ca7ada 100644
--- a/src/facilities/farmyard/shows/farmShowsIncome.js
+++ b/src/facilities/farmyard/shows/farmShowsIncome.js
@@ -311,12 +311,12 @@ App.Facilities.Farmyard.farmShowsIncome = function(slave) {
 
 		// Close FS Effects
 
-		if (setup.entertainmentCareers.includes(slave.career)) {
+		if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 			cash *= 1.1;
 		}
 
 		// FIXME: can slaves' careers overlap categories?
-		if (setup.farmerCareers.includes(slave.career)) {
+		if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
 			cash *= 1.1;
 		}
 
diff --git a/src/facilities/farmyard/shows/saFarmyardShows.js b/src/facilities/farmyard/shows/saFarmyardShows.js
index 89137bb1506dcc34b117b48f0ad3640e9d18fb57..9b863f728ef632fdfb8ec7b8a2af50a74f307a4d 100644
--- a/src/facilities/farmyard/shows/saFarmyardShows.js
+++ b/src/facilities/farmyard/shows/saFarmyardShows.js
@@ -254,13 +254,13 @@ App.Facilities.Farmyard.putOnShows = function(slave) {
 
 	// Close FS Subsection
 
-	if (setup.entertainmentCareers.includes(slave.career)) {
+	if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 		r.push(`${He} has experience with putting on shows from ${his} life before ${he} was a slave, making ${him} more effective at putting on shows.`);
 	}
 
 	// FIXME: can slaves' careers overlap categories?
-	if (setup.farmerCareers.includes(slave.career)) {
-		r.push(`${He} ${setup.entertainmentCareers.includes(slave.career) ? `also` : ``} has experience in working with animals from ${his} life before ${he} was a slave, making ${him} more effective at putting on shows.`);
+	if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
+		r.push(`${He} ${App.Data.Careers.General.entertainment.includes(slave.career) ? `also` : ``} has experience in working with animals from ${his} life before ${he} was a slave, making ${him} more effective at putting on shows.`);
 	}
 
 	if (slave.prestige === 1) {
diff --git a/src/facilities/fsDecoration.js b/src/facilities/fsDecoration.js
new file mode 100644
index 0000000000000000000000000000000000000000..56ae1ec98b48f22ecd192c9998518af9d958aa23
--- /dev/null
+++ b/src/facilities/fsDecoration.js
@@ -0,0 +1,29 @@
+/** Replaces <<SetFacilityDecoration>> widget
+ * @param {string} variable - global property name for the facility decoration (no $ etc)
+ * @returns {DocumentFragment}
+ */
+App.UI.facilityRedecoration = function(variable) {
+	const frag = new DocumentFragment();
+	const arc = V.arcologies[0];
+	for (const FS of FutureSocieties.activeFSes(arc)) {
+		if (arc[FS] > 20) {
+			const decorationName = FutureSocieties.decorationName(FS);
+			if (decorationName && V[variable] !== decorationName) {
+				const link = App.UI.DOM.link(`${decorationName} Redecoration`, () => {
+					V[variable] = decorationName;
+					cashX(-5000, "capEx");
+					App.UI.reload();
+				});
+				App.UI.DOM.appendNewElement("div", frag, link, "indent");
+			}
+		}
+	}
+	if (V[variable] !== "standard") {
+		const link = App.UI.DOM.link(`Remove all decorations`, () => {
+			V[variable] = "standard";
+			App.UI.reload();
+		});
+		App.UI.DOM.appendNewElement("div", frag, link, "indent");
+	}
+	return frag;
+};
diff --git a/src/facilities/headGirlSuite/headGirlSuiteFramework.js b/src/facilities/headGirlSuite/headGirlSuiteFramework.js
index 13549f2a2bba07eee511aed03525ea6b96566225..cfc1d1c3e3c05660d37be15d75ccac12c44abe74 100644
--- a/src/facilities/headGirlSuite/headGirlSuiteFramework.js
+++ b/src/facilities/headGirlSuite/headGirlSuiteFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.headGirlSuite = {
 	manager: {
 		position: "Head Girl",
 		assignment: Job.HEADGIRL,
-		careers: App.Data.misc.HGCareers,
+		careers: App.Data.Careers.Leader.HG,
 		skill: "headGirl",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/incubator/incubatorInteract.js b/src/facilities/incubator/incubatorInteract.js
index deaea3a58b6d211de4038f165444daea6c343cb8..8e3d2aeb432cf1bd8898de6778d10865fd6b060a 100644
--- a/src/facilities/incubator/incubatorInteract.js
+++ b/src/facilities/incubator/incubatorInteract.js
@@ -138,7 +138,6 @@ App.UI.incubator = function() {
 
 			V.sortIncubatorList = V.sortIncubatorList || 'Unsorted';
 
-			linkArray = [];
 			const sortingOptions = new Map([
 				["Name", sortByName],
 				["Reserved Incubator Spots", sortByReservedSpots],
@@ -849,7 +848,7 @@ App.UI.incubator = function() {
 					appendRow(p, `${He} is ready to be released from ${his} tank.`);
 				} else {
 					const _weekDisplay = Math.round(V.tanks[i].growTime / V.incubatorUpgradeSpeed);
-					appendRow(p, `${His} growth is currently being accelerated. ${He} will be ready for release in about ${_weekDisplay}  ${(_weekDisplay > 1) ? `weeks` : `week`}.`);
+					appendRow(p, `${His} growth is currently being accelerated. ${He} will be ready for release in about ${_weekDisplay} ${(_weekDisplay > 1) ? `weeks` : `week`}.`);
 				}
 
 				if (V.tanks[i].tankBaby !== 3) {
@@ -1118,7 +1117,7 @@ App.UI.incubator = function() {
 		let cost;
 		let p;
 		let r = [];
-		let row = document.createElement("div");
+		let row;
 		let linkArray;
 		r.push("Target age for release:");
 		r.push(
@@ -1207,58 +1206,62 @@ App.UI.incubator = function() {
 			cost = Math.trunc(500000 * V.upgradeMultiplierArcology);
 			row.append(`It has been upgraded with advanced experimental growth accelerants; children grow at the rate of 3 weeks to 1 year. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Fund speculative research into maximizing growth rate`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeSpeed = 52;
 						refresh();
 					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		} else if (V.incubatorUpgradeSpeed === 9) {
 			cost = Math.trunc(75000 * V.upgradeMultiplierArcology);
 			row.append(`It has been upgraded with advanced growth accelerants; children grow at the rate of 6 weeks to 1 year. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Fund research into increasing growth rate even further`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeSpeed = 18;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		} else if (V.incubatorUpgradeSpeed === 6) {
 			cost = Math.trunc(30000 * V.upgradeMultiplierArcology);
 			row.append(`It has been upgraded with growth accelerants; children grow at the rate of 9 weeks to 1 year. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Further upgrade the incubators with specialized stem cells to speed growth`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeSpeed = 9;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		} else if (V.incubatorUpgradeSpeed === 5) {
 			cost = Math.trunc(30000 * V.upgradeMultiplierArcology);
 			row.append(`The incubation tanks are basic; children grow at the rate of 12 weeks to 1 year. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the incubators with growth accelerating drugs`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeSpeed = 6;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		}
 
 		el.append(row);
@@ -1292,16 +1295,17 @@ App.UI.incubator = function() {
 			cost = Math.trunc(20000 * V.upgradeMultiplierArcology);
 			row.append(`There are no systems in place to control a growing child's weight; they will likely come out emaciated from the rapid growth. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the growth tanks with weight monitoring systems`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeWeight = 1;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		}
 
 		p.append(row);
@@ -1338,16 +1342,17 @@ App.UI.incubator = function() {
 			cost = Math.trunc(20000 * V.upgradeMultiplierArcology);
 			row.append(`There are no systems in place to control a growing child's musculature; they will likely come out frail and weak from the rapid growth. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the growth tanks with muscle monitoring systems`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeMuscles = 1;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		}
 
 		p.append(row);
@@ -1445,22 +1450,22 @@ App.UI.incubator = function() {
 			cost = Math.trunc(50000 * V.upgradeMultiplierArcology);
 			row.append(`There are no systems in place to control a growing child's reproductive capability. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the growth tanks with hormone monitoring systems`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeReproduction = 1;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		}
 
 		p.append(row);
 		el.append(p);
 
-		p = document.createElement("p");
 		row = document.createElement("div");
 
 		if (V.incubatorUpgradeOrgans === 1) {
@@ -1469,16 +1474,17 @@ App.UI.incubator = function() {
 			cost = Math.trunc(10000 * V.upgradeMultiplierArcology);
 			row.append(`The tanks lack the ability to extract tissue samples to be used by the organ fabricator. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the growth tanks with surgical extraction tools`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeOrgans = 1;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		} else {
 			row.append(`The tanks lack the ability to extract tissue samples and the dispensary lacks the ability to make use of them to fabricate organs.`);
 		}
@@ -1514,16 +1520,17 @@ App.UI.incubator = function() {
 			cost = Math.trunc(20000 * V.upgradeMultiplierArcology);
 			row.append(`There are no systems in place to control a growing child's height. `);
 			row.append(
-				App.UI.DOM.link(
+				choice(
 					`Upgrade the growth tanks with stimulants injection systems`,
 					() => {
 						cashX(forceNeg(cost), "capEx");
 						V.incubatorUpgradeGrowthStims = 1;
 						refresh();
-					}
+					},
+					"",
+					`Costs ${cashFormat(cost)} and will increase upkeep costs`
 				)
 			);
-			App.UI.DOM.appendNewElement("span", row, `Costs ${cashFormat(cost)} and will increase upkeep costs`, "note");
 		} else {
 			row.append(`There are no systems in place to control a growing child's height and you lack the capability to fabricate growth stimulants.`);
 		}
@@ -1583,6 +1590,7 @@ App.UI.incubator = function() {
 						`Switch the system to focus on preparation for body-swapping`,
 						() => {
 							V.incubatorImprintSetting = "husk";
+							refresh();
 						}
 					)
 				);
@@ -1592,6 +1600,7 @@ App.UI.incubator = function() {
 					`Switch the system to focus on attachment`,
 					() => {
 						V.incubatorImprintSetting = "trust";
+						refresh();
 					}
 				)
 			);
@@ -1603,6 +1612,7 @@ App.UI.incubator = function() {
 						`Switch the system to focus preparation for body-swapping`,
 						() => {
 							V.incubatorImprintSetting = "husk";
+							refresh();
 						}
 					)
 				);
@@ -1612,6 +1622,7 @@ App.UI.incubator = function() {
 					`Switch the system to focus on dependence`,
 					() => {
 						V.incubatorImprintSetting = "terror";
+						refresh();
 					}
 				)
 			);
@@ -1622,6 +1633,7 @@ App.UI.incubator = function() {
 					`Switch the system to focus on dependence`,
 					() => {
 						V.incubatorImprintSetting = "terror";
+						refresh();
 					}
 				)
 			);
@@ -1630,6 +1642,7 @@ App.UI.incubator = function() {
 					`Switch the system to focus on attachment`,
 					() => {
 						V.incubatorImprintSetting = "trust";
+						refresh();
 					}
 				)
 			);
@@ -1705,7 +1718,7 @@ App.UI.incubator = function() {
 	 * @param {string} [note]
 	 * @returns {HTMLElement}
 	 */
-	function choice(title, func, passage = "", note = "") {
+	function choice(title, func, passage = "", note) {
 		const div = document.createElement("div");
 		div.classList.add("choices");
 		div.append(
@@ -1713,12 +1726,10 @@ App.UI.incubator = function() {
 				title,
 				func,
 				[],
-				passage
+				passage,
+				note
 			)
 		);
-		if (note) {
-			App.UI.DOM.appendNewElement("span", div, ` ${note}`, "note");
-		}
 		return div;
 	}
 
diff --git a/src/facilities/nursery/nurseryFramework.js b/src/facilities/nursery/nurseryFramework.js
index 12f33efb971608e1a0768fad38f588329b07a8e0..daa83f65ceeb4065faef20f304a1e19b84844514 100644
--- a/src/facilities/nursery/nurseryFramework.js
+++ b/src/facilities/nursery/nurseryFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.nursery = {
 	manager: {
 		position: "matron",
 		assignment: Job.MATRON,
-		careers: App.Data.misc.matronCareers,
+		careers: App.Data.Careers.Leader.matron,
 		skill: "matron",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/penthouse/penthouseFramework.js b/src/facilities/penthouse/penthouseFramework.js
index b64a186b4b4f6d7761d45a6fc339bcae5eb08ae6..8d9ba1d62e2ddac823ade8be12dd6dff0a5f6e8f 100644
--- a/src/facilities/penthouse/penthouseFramework.js
+++ b/src/facilities/penthouse/penthouseFramework.js
@@ -74,7 +74,7 @@ App.Data.Facilities.penthouse = {
 	manager: {
 		position: "Recruiter",
 		assignment: Job.RECRUITER,
-		careers: App.Data.misc.recruiterCareers,
+		careers: App.Data.Careers.Leader.recruiter,
 		skill: "recruiter",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/schoolroom/schoolroomFramework.js b/src/facilities/schoolroom/schoolroomFramework.js
index 9f2b65111433ea3f158980ca83d7532979752cbe..d27511c7e178d12959a38e67b4a6a575eb2bdb00 100644
--- a/src/facilities/schoolroom/schoolroomFramework.js
+++ b/src/facilities/schoolroom/schoolroomFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.schoolroom = {
 	manager: {
 		position: "schoolteacher",
 		assignment: Job.TEACHER,
-		careers: App.Data.misc.schoolteacherCareers,
+		careers: App.Data.Careers.Leader.schoolteacher,
 		skill: "teacher",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/servantsQuarters/servantsQuartersFramework.js b/src/facilities/servantsQuarters/servantsQuartersFramework.js
index 24e00796f48c9c58057ea520712229de15922eff..17c0dbd7c6e1b084141279da5ed0591132c4dbc4 100644
--- a/src/facilities/servantsQuarters/servantsQuartersFramework.js
+++ b/src/facilities/servantsQuarters/servantsQuartersFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.servantsQuarters = {
 	manager: {
 		position: "stewardess",
 		assignment: Job.STEWARD,
-		careers: App.Data.misc.stewardessCareers,
+		careers: App.Data.Careers.Leader.stewardess,
 		skill: "stewardess",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/spa/spaFramework.js b/src/facilities/spa/spaFramework.js
index 95d8bd88f82745086d6e45f6df33e9a4f953dac2..ed4e8fc5215a2bffd3b8a1ad021e53c64c317121 100644
--- a/src/facilities/spa/spaFramework.js
+++ b/src/facilities/spa/spaFramework.js
@@ -13,7 +13,7 @@ App.Data.Facilities.spa = {
 	manager: {
 		position: "attendant",
 		assignment: Job.ATTENDANT,
-		careers: App.Data.misc.attendantCareers,
+		careers: App.Data.Careers.Leader.attendant,
 		skill: "attendant",
 		publicSexUse: false,
 		fuckdollAccepted: false,
diff --git a/src/facilities/statistics.js b/src/facilities/statistics.js
index 448b0da00ff0648068614b0ab03032249c2f60de..380e5499cf6ce95d5624ade61565755554b1ed56 100644
--- a/src/facilities/statistics.js
+++ b/src/facilities/statistics.js
@@ -1,6 +1,6 @@
 App.Facilities.StatsHelper = class {
 	/** Make a statistics table with given column labels
-	 * @param {string[]} columns - Array of four labels for data columns.  The first is wider than the others (typically used for facility revenue).
+	 * @param {string[]} columns - Array of four labels for data columns. The first is wider than the others (typically used for facility revenue).
 	 * @returns {HTMLTableElement}
 	 */
 	makeStatsTable(columns) {
diff --git a/src/facilities/wardrobe/wardrobeShoppingData.js b/src/facilities/wardrobe/wardrobeShoppingData.js
index 1ce4f1d3f2ff0f4f638abcacddc49ea2414fd185..c57af76fc4fde4b77db038a3f630dcde8eec8d1c 100644
--- a/src/facilities/wardrobe/wardrobeShoppingData.js
+++ b/src/facilities/wardrobe/wardrobeShoppingData.js
@@ -13,7 +13,7 @@ App.Data.WardrobeShopping.Clothing = {
 	 */
 
 	/**
-	 *  @type {Object.<string, wardrobeItem>} String will be the property checked to see if the item is owned.  So for "bunny", it will check V.boughtItem.clothing["bunny"].
+	 * @type {Object.<string, wardrobeItem>} String will be the property checked to see if the item is owned. So for "bunny", it will check V.boughtItem.clothing["bunny"].
 	 */
 	FS: {
 		"bunny": {
@@ -222,7 +222,7 @@ App.Data.WardrobeShopping.Clothing = {
 	}
 };
 /**
- *  @type {Object.<string, wardrobeItem>} String will be the property checked to see if the item is owned.  So for "bunny", it will check V.boughtItem.clothing["bunny"].
+ * @type {Object.<string, wardrobeItem>} String will be the property checked to see if the item is owned. So for "bunny", it will check V.boughtItem.clothing["bunny"].
  */
 App.Data.WardrobeShopping.Accessories = {
 	"shoes.heels": {
diff --git a/src/gui/Encyclopedia/encyclopedia.tw b/src/gui/Encyclopedia/encyclopedia.tw
index 8f8f74f2341f4a534c9a7c6468cc564ec8edc473..97011f770b9bdd63f7ed072a2eeadf344d8ba90c 100644
--- a/src/gui/Encyclopedia/encyclopedia.tw
+++ b/src/gui/Encyclopedia/encyclopedia.tw
@@ -73,7 +73,7 @@ PLAYING FREE CITIES
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Spend the rest of your @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@ on prospects: slaves that are @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("cheap", "Money")>>@@ now, but can be improved quickly. As long as you keep @@.hotpink;devotion@@ pretty high, low @@.mediumaquamarine;<<= App.Encyclopedia.Dialog.linkSC("trust", "Trust")>>@@ can be fixed reliably. Unknown fetishes, emaciated or fat, flaws, deep voice, and poor skills are all good ways to drive prices down, and can all be fixed quickly. Virginities are a bad idea because they drive costs up and are easy to break. @@.cyan;Education@@ can take awhile and will take slaves away from other jobs, so make them all educated for now, and keep their @@.cyan;intelligence@@ reasonably high.
 
 	<br><br>__First turn__
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assign one of your Head Girls to be ''the'' Head Girl and make the other whore. Assign everyone else to whore. The <<= App.Encyclopedia.Dialog.linkSC("rules assistant", "Rules Assistant")>> will speed things up a lot when you know the basics, but leave it off for now; it's easy to miss a lot of stuff if you set it up without a bit of experience. Go through your girls one by one and experiment with their options, but anyone who's @@.hotpink;Accepting@@ or better should get nice clothes, accessories, and <<= App.Encyclopedia.Dialog.linkSC("living conditions", "Living Conditions")>>; anyone who's not should not. When slaves tip over into @@.hotpink;Accepting,@@ switch them over from bedrolls and uncomfortable straps; until then, the good life is a waste of @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@ and will spoil them. Give @@.red;unhealthy@@ slaves curatives, and give everyone hormones, since they're @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("cheap", "Money")>>@@ and have good front end benefits. Get everyone working out or dieting to reach a basic fitness level and an attractive (not @@.red;red@@) <<= App.Encyclopedia.Dialog.linkSC("weight", "Weight")>>. Sell the girl(s) your predecessor left behind for seed @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>,@@ and choose the most profitable option; there are ways to maximize this, but worry about that later. Check out the arcology management menu. You should have the @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@ to upgrade the security systems, build the <<= App.Encyclopedia.Dialog.linkSC("Head Girl suite", "Head Girl Suite")>>, and to buy the kitchen upgrade; this will make dieting work faster. Check out the slave market, and buy a single bargain slave: @@.yellowgreen;<<print cashFormat(2000)>>@@ is good. Put her in the suite: if she won't go, abuse her until she will. Open the personal attention menu, and fix your Head Girl's flaws; softening is powerful but it takes longer and we're focusing on the basics. ''Save the game'' and end the turn.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assign one of your Head Girls to be ''the'' Head Girl and make the other whore. Assign everyone else to whore. The <<= App.Encyclopedia.Dialog.linkSC("rules assistant", "Rules Assistant")>> will speed things up a lot when you know the basics, but leave it off for now; it's easy to miss a lot of stuff if you set it up without a bit of experience. Go through your girls one by one and experiment with their options, but anyone who's @@.hotpink;Accepting@@ or better should get nice clothes, accessories, and <<= App.Encyclopedia.Dialog.linkSC("living conditions", "Living Conditions")>>; anyone who's not should not. When slaves tip over into @@.hotpink;Accepting,@@ switch them over from bedrolls and uncomfortable straps; until then, the good life is a waste of @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@ and will spoil them. Give @@.health.dec;unhealthy@@ slaves curatives, and give everyone hormones, since they're @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("cheap", "Money")>>@@ and have good front end benefits. Get everyone working out or dieting to reach a basic fitness level and an attractive (not @@.red;red@@) <<= App.Encyclopedia.Dialog.linkSC("weight", "Weight")>>. Sell the girl(s) your predecessor left behind for seed @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>,@@ and choose the most profitable option; there are ways to maximize this, but worry about that later. Check out the arcology management menu. You should have the @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@ to upgrade the security systems, build the <<= App.Encyclopedia.Dialog.linkSC("Head Girl suite", "Head Girl Suite")>>, and to buy the kitchen upgrade; this will make dieting work faster. Check out the slave market, and buy a single bargain slave: @@.yellowgreen;<<print cashFormat(2000)>>@@ is good. Put her in the suite: if she won't go, abuse her until she will. Open the personal attention menu, and fix your Head Girl's flaws; softening is powerful but it takes longer and we're focusing on the basics. ''Save the game'' and end the turn.
 
 	<br><br>__The end turn report__
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Read this, and note all the colored text. Pay particular attention to @@.red;red,@@ @@.gold;gold,@@ or @@.mediumorchid;orchid@@ text; these are generally bad. Being a slave whore is a hard life, and some trouble is inevitable. But take particular note of things like slaves losing <<= App.Encyclopedia.Dialog.linkSC("health", "Health")>>, becoming <<= App.Encyclopedia.Dialog.linkSC("fearful", "Trust")>>, or <<= App.Encyclopedia.Dialog.linkSC("hating", "From Rebellious to Devoted")>> you due to their <<= App.Encyclopedia.Dialog.linkSC("rules", "Rules Assistant")>>, <<= App.Encyclopedia.Dialog.linkSC("conditions", "Living Conditions")>>, or other slaves — these things you can control. Reload your save and fiddle around with the options to address these areas. (The Head Girl's girl may have a rough time; you can't affect that.) Since your Head Girl has her own slave to help her around the house, she'll work with two of your slaves.
@@ -98,8 +98,8 @@ PLAYING FREE CITIES
 	<br><br>The first button in the left-side UI bar will always continue the game without taking any actions. If you wish to advance the game rapidly, click that button repeatedly. If you are presented with a menu and do not wish to select any of the options, that button will also serve as a none of the above choice. ''The spacebar will also advance the game.''
 
 	<br><br>The author has played several otherwise good H-Games in which it can be hard to parse exactly what the flavor descriptions mean in terms of stats. Therefore, this game will usually flag stat impacts in descriptions by means of colored text. For example:
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.green;Green text@@ means something good, like a healthy slave.
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.red;Red text@@ means something bad, like a sick slave.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.green;Green text@@ means something good, like a @@.health.inc;healthy.@@ slave
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.red;Red text@@ means something bad, like a sick or injured slave.
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.yellow;Yellow text@@ means something neutral but noteworthy.
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.hotpink;Hot pink text@@ means an increase in a slave's regard for to you.
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.mediumorchid;Orchid text@@ means a decrease in a slave's regard for to you.
@@ -111,6 +111,10 @@ PLAYING FREE CITIES
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.yellowgreen;Yellow-green text@@ is for a @@.yellowgreen;<<= App.Encyclopedia.Dialog.linkSC("money", "Money")>>@@-related event.
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.coral;Coral text@@ is used for simple identifiers that can be used to check a slave's general type at a glance, also weakening fetishes.
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.lightcoral;Light coral text@@ is used when a slave's fetish strengthens or develops.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.lightgreen;Light green text@@ is used to indicate positive relationships.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.lightsalmon;Light salmon text@@ is used when a rivalry develops or strenghtens.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.libido.inc;Violet text@@ means an increase in sex drive.
+	<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@.libido.dec;Khaki text@@ means a decrease in sex drive.
 
 	<br><br>__It is important to note__ that if a scene doesn't have colored text, it doesn't impact a slave's stats. For example, the short sex scenes available from the main screen are for the most part unlimited and have no real gameplay effect: they are for fun and flavor only.
 
@@ -631,166 +635,166 @@ SLAVE ASSIGNMENTS:
 	Slaves may retain useful experience from their lives before enslavement. Freedom and slavery are so different that the bonuses slaves get are minor. Careers fall into categories, each with its own bonus; these are:
 
 	<br><br><br>__Grateful__ (offering a potential bonus to @@.mediumaquamarine;<<= App.Encyclopedia.Dialog.linkSC("trust", "Trust")>>@@), including slaves who were
-	<<for $i = 0; $i < setup.gratefulCareers.length; $i++>>
-		<<if $i == setup.gratefulCareers.length-1>>
-			and <<print setup.gratefulCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.General.grateful.length; $i++>>
+		<<if $i == App.Data.Careers.General.grateful.length-1>>
+			and <<print App.Data.Careers.General.grateful[$i]>>.
 		<<else>>
-			<<print setup.gratefulCareers[$i]>>,
+			<<print App.Data.Careers.General.grateful[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Menial__ (offering a potential bonus to @@.hotpink;<<= App.Encyclopedia.Dialog.linkSC("devotion", "From Rebellious to Devoted")>>@@), including slaves who were
-	<<for $i = 0; $i < setup.menialCareers.length; $i++>>
-		<<if $i == setup.menialCareers.length-1>>
-			and <<print setup.menialCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.General.menial.length; $i++>>
+		<<if $i == App.Data.Careers.General.menial.length-1>>
+			and <<print App.Data.Careers.General.menial[$i]>>.
 		<<else>>
-			<<print setup.menialCareers[$i]>>,
+			<<print App.Data.Careers.General.menial[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Servant__ (offering a bonus to <<= App.Encyclopedia.Dialog.linkSC("keeping your estate", "Servitude")>>), including slaves who were
-	<<for $i = 0; $i < setup.servantCareers.length; $i++>>
-		<<if $i == setup.servantCareers.length-1>>
-			and <<print setup.servantCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.servant.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.servant.length-1>>
+			and <<print App.Data.Careers.Leader.servant[$i]>>.
 		<<else>>
-			<<print setup.servantCareers[$i]>>,
+			<<print App.Data.Careers.Leader.servant[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Entertainment__ (offering a bonus to <<= App.Encyclopedia.Dialog.linkSC("public service", "Public Service")>>), including slaves who were
-	<<for $i = 0; $i < setup.entertainmentCareers.length; $i++>>
-		<<if $i == setup.entertainmentCareers.length-1>>
-			and <<print setup.entertainmentCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.General.entertainment.length; $i++>>
+		<<if $i == App.Data.Careers.General.entertainment.length-1>>
+			and <<print App.Data.Careers.General.entertainment[$i]>>.
 		<<else>>
-			<<print setup.entertainmentCareers[$i]>>,
+			<<print App.Data.Careers.General.entertainment[$i]>>,
 		<</if>>
 	<</for>>
 	If a slave has been in the arcology for a long time and is intelligent, she can qualify for this bonus without career experience.
 
 	<br><br><br>__Sex work__ (offering a bonus to <<= App.Encyclopedia.Dialog.linkSC("whoring", "Whoring")>>), including slaves who were
-	<<for $i = 0; $i < setup.whoreCareers.length; $i++>>
-		<<if $i == setup.whoreCareers.length-1>>
-			and <<print setup.whoreCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.General.whore.length; $i++>>
+		<<if $i == App.Data.Careers.General.whore.length-1>>
+			and <<print App.Data.Careers.General.whore[$i]>>.
 		<<else>>
-			<<print setup.whoreCareers[$i]>>,
+			<<print App.Data.Careers.General.whore[$i]>>,
 		<</if>>
 	<</for>>
 	If a slave is very sexually experienced, she can qualify for this bonus without career experience.
 
 	<br><br><br>__Leadership__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Head Girl", "Head Girl")>>), including slaves who were
-	<<for $i = 0; $i < setup.HGCareers.length; $i++>>
-		<<if $i == setup.HGCareers.length-1>>
-			and <<print setup.HGCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.HG.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.HG.length-1>>
+			and <<print App.Data.Careers.Leader.HG[$i]>>.
 		<<else>>
-			<<print setup.HGCareers[$i]>>,
+			<<print App.Data.Careers.Leader.HG[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Procuring__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Madam", "Madam")>>), including slaves who were
-	<<for $i = 0; $i < setup.madamCareers.length; $i++>>
-		<<if $i == setup.madamCareers.length-1>>
-			and <<print setup.madamCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.madam.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.madam.length-1>>
+			and <<print App.Data.Careers.Leader.madam[$i]>>.
 		<<else>>
-			<<print setup.madamCareers[$i]>>,
+			<<print App.Data.Careers.Leader.madam[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Musical__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("DJ", "DJ")>>), including slaves who were
-	<<for $i = 0; $i < setup.DJCareers.length; $i++>>
-		<<if $i == setup.DJCareers.length-1>>
-			and <<print setup.DJCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.DJ.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.DJ.length-1>>
+			and <<print App.Data.Careers.Leader.DJ[$i]>>.
 		<<else>>
-			<<print setup.DJCareers[$i]>>,
+			<<print App.Data.Careers.Leader.DJ[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Defensive__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Bodyguard", "Bodyguard")>>), including slaves who were
-	<<for $i = 0; $i < setup.bodyguardCareers.length; $i++>>
-		<<if $i == setup.bodyguardCareers.length-1>>
-			and <<print setup.bodyguardCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.bodyguard.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.bodyguard.length-1>>
+			and <<print App.Data.Careers.Leader.bodyguard[$i]>>.
 		<<else>>
-			<<print setup.bodyguardCareers[$i]>>,
+			<<print App.Data.Careers.Leader.bodyguard[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Convincing__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Recruiter", "Recruiter")>>), including slaves who were
-	<<for $i = 0; $i < setup.recruiterCareers.length; $i++>>
-		<<if $i == setup.recruiterCareers.length-1>>
-			and <<print setup.recruiterCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.recruiter.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.recruiter.length-1>>
+			and <<print App.Data.Careers.Leader.recruiter[$i]>>.
 		<<else>>
-			<<print setup.recruiterCareers[$i]>>,
+			<<print App.Data.Careers.Leader.recruiter[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Security__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Wardeness", "Wardeness")>>), including slaves who were
-	<<for $i = 0; $i < setup.wardenessCareers.length; $i++>>
-		<<if $i == setup.wardenessCareers.length-1>>
-			and <<print setup.wardenessCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.wardeness.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.wardeness.length-1>>
+			and <<print App.Data.Careers.Leader.wardeness[$i]>>.
 		<<else>>
-			<<print setup.wardenessCareers[$i]>>,
+			<<print App.Data.Careers.Leader.wardeness[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Medical__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Nurse", "Nurse")>>), including slaves who were
-	<<for $i = 0; $i < setup.nurseCareers.length; $i++>>
-		<<if $i == setup.nurseCareers.length-1>>
-			and <<print setup.nurseCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.nurse.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.nurse.length-1>>
+			and <<print App.Data.Careers.Leader.nurse[$i]>>.
 		<<else>>
-			<<print setup.nurseCareers[$i]>>,
+			<<print App.Data.Careers.Leader.nurse[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Counseling__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Attendant", "Attendant")>>), including slaves who were
-	<<for $i = 0; $i < setup.attendantCareers.length; $i++>>
-		<<if $i == setup.attendantCareers.length-1>>
-			and <<print setup.attendantCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.attendant.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.attendant.length-1>>
+			and <<print App.Data.Careers.Leader.attendant[$i]>>.
 		<<else>>
-			<<print setup.attendantCareers[$i]>>,
+			<<print App.Data.Careers.Leader.attendant[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Nannying__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Matron", "Matron")>>), including slaves who were
-	<<for $i = 0; $i < setup.matronCareers.length; $i++>>
-		<<if $i == setup.matronCareers.length-1>>
-			and <<print setup.matronCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.matron.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.matron.length-1>>
+			and <<print App.Data.Careers.Leader.matron[$i]>>.
 		<<else>>
-			<<print setup.matronCareers[$i]>>,
+			<<print App.Data.Careers.Leader.matron[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Accounting__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Stewardess", "Stewardess")>>), including slaves who were
-	<<for $i = 0; $i < setup.stewardessCareers.length; $i++>>
-		<<if $i == setup.stewardessCareers.length-1>>
-			and <<print setup.stewardessCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.stewardess.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.stewardess.length-1>>
+			and <<print App.Data.Careers.Leader.stewardess[$i]>>.
 		<<else>>
-			<<print setup.stewardessCareers[$i]>>,
+			<<print App.Data.Careers.Leader.stewardess[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Husbandry__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Milkmaid", "Milkmaid")>>), including slaves who were
-	<<for $i = 0; $i < setup.milkmaidCareers.length; $i++>>
-		<<if $i == setup.milkmaidCareers.length-1>>
-			and <<print setup.milkmaidCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.milkmaid.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.milkmaid.length-1>>
+			and <<print App.Data.Careers.Leader.milkmaid[$i]>>.
 		<<else>>
-			<<print setup.milkmaidCareers[$i]>>,
+			<<print App.Data.Careers.Leader.milkmaid[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Farming__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Farmer", "Farmer")>>), including slaves who were
-	<<for $i = 0; $i < setup.farmerCareers.length; $i++>>
-		<<if $i == setup.farmerCareers.length-1>>
-			and <<print setup.farmerCareers[$i]>>.
+	<<for $i = 0; $i < App.Data.Careers.Leader.farmer.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.farmer.length-1>>
+			and <<print App.Data.Careers.Leader.farmer[$i]>>.
 		<<else>>
-			<<print setup.farmerCareers[$i]>>,
+			<<print App.Data.Careers.Leader.farmer[$i]>>,
 		<</if>>
 	<</for>>
 
 	<br><br><br>__Teaching__ (offering a bonus as <<= App.Encyclopedia.Dialog.linkSC("Schoolteacher", "Schoolteacher")>>, including slaves who were
-	<<for $i = 0; $i < setup.schoolteacherCareers.length; $i++>>
-		<<if $i == setup.schoolteacherCareers.length-1>>
-			and <<print setup.schoolteacherCareers[$i]>>,
+	<<for $i = 0; $i < App.Data.Careers.Leader.schoolteacher.length; $i++>>
+		<<if $i == App.Data.Careers.Leader.schoolteacher.length-1>>
+			and <<print App.Data.Careers.Leader.schoolteacher[$i]>>,
 		<<else>>
-			<<print setup.schoolteacherCareers[$i]>>,
+			<<print App.Data.Careers.Leader.schoolteacher[$i]>>,
 		<</if>>
 	<</for>>
 	<br><br>Slaves who have been in slavery long enough that it is effectively their career get a bonus to @@.hotpink;<<= App.Encyclopedia.Dialog.linkSC("devotion", "Devotion")>>.@@ Slaves can forget their career experience in an industrialized Dairy, but if they do so and remain sane, they will get a special bonus to both @@.hotpink;<<= App.Encyclopedia.Dialog.linkSC("devotion", "Devotion")>>@@ and @@.mediumaquamarine;<<= App.Encyclopedia.Dialog.linkSC("trust", "Trust")>>.@@
@@ -799,7 +803,7 @@ SLAVE ASSIGNMENTS:
 
 
 <<case "Attendant">>
-	An ''Attendant'' can be selected once the <<= App.Encyclopedia.Dialog.linkSC("Spa", "Spa")>> facility has been built. Attendants provide emotional help to slaves in the spa, and can also soften flaws and even fix mindbroken slaves. Good Attendants are free of fetishes or submissive, have a calm libido, appear older than 35, a motherly air, @@.cyan;intelligent,@@ and naturally female.
+	An ''Attendant'' can be selected once the <<= App.Encyclopedia.Dialog.linkSC("Spa", "Spa")>> facility has been built. Attendants provide emotional help to slaves in the spa, and can also soften flaws and even fix mindbroken slaves. Good Attendants are free of <<= App.Encyclopedia.Dialog.linkSC("fetishes", "Fetishes")>> or <<= App.Encyclopedia.Dialog.linkSC("submissive", "Submissives")>>, have a calm libido, appear older than 35, a motherly air, @@.cyan;intelligent,@@ and naturally female.
 
 /* TODO: will still need more updating */
 <<case "Matron">>
diff --git a/src/gui/css/mainStyleSheet.css b/src/gui/css/mainStyleSheet.css
index ef081dc3f8d3afa249f9f4b931ca3df74a8597e3..7c5b01938e6b32475f8084c2c965795994bbe61f 100644
--- a/src/gui/css/mainStyleSheet.css
+++ b/src/gui/css/mainStyleSheet.css
@@ -179,9 +179,11 @@ img.paperdoll {
 .chocolate, .chocolate a { color: chocolate }
 .saddlebrown, .saddlebrown a { color: saddlebrown }
 .teal, .teal a { color: teal }
-.yellow, .yellow a, .noteworthy, .noteworthy a, .paraphilia.gain, .paraphilia.gain a, .devotion.ambivalent, .devotion.ambivalent a, .trust.fearful, .trust.fearful a { color: yellow }
+.yellow, .yellow a, .noteworthy, .noteworthy a, .paraphilia.gain, .paraphilia.gain a, .devotion.ambivalent, .devotion.ambivalent a, .trust.fearful, .trust.fearful a, .job.change { color: yellow }
 .yellowgreen, .yellowgreen a, .cash.inc, .cash.inc a, .cash, .cash a { color: yellowgreen }
 .white a { color: white }
+.violet, .libido.inc { color: violet }
+.khaki, .libido.dec { color: khaki }
 
 /*! <<checkvars>> macro for SugarCube 2.x */
 #ui-dialog-body.checkvars{padding:1em}#ui-dialog-body.checkvars h1{font-size:1em;margin-top:0}#ui-dialog-body.checkvars table{border-collapse:collapse;border-spacing:0}#ui-dialog-body.checkvars thead tr{border-bottom:2px solid #444}#ui-dialog-body.checkvars tr:not(:first-child){border-top:1px solid #444}#ui-dialog-body.checkvars td,#ui-dialog-body.checkvars th{padding:.25em 1em}#ui-dialog-body.checkvars td:first-child,#ui-dialog-body.checkvars th:first-child{padding-left:.5em;border-right:1px solid #444}#ui-dialog-body.checkvars td:last-child,#ui-dialog-body.checkvars th:last-child{padding-right:.5em}#ui-dialog-body.checkvars th:first-child{text-align:center}#ui-dialog-body.checkvars td:first-child{font-weight:700;text-align:right}#ui-dialog-body.checkvars td{font-family:monospace,monospace;vertical-align:top;white-space:pre-wrap}#ui-dialog-body.checkvars .scroll-pad{margin:0;padding:0}
@@ -299,7 +301,7 @@ div.double-choices, p.double-choices {
 	font-style: italic;
 }
 
-.story-label, .underline {
+.story-label, .regularParties, .underline {
 	text-decoration: underline;
 }
 
diff --git a/src/gui/css/options.css b/src/gui/css/options.css
index eaaa502438caf7810c0b83ed84863fa6c76f1aac..7ce9fb2705cc625456d3c1fb5e887d9fd6954241 100644
--- a/src/gui/css/options.css
+++ b/src/gui/css/options.css
@@ -20,7 +20,7 @@ div.options-group div.button-group {
 	margin: 6px 0;
 }
 
-div.options-group span.comment {
+div.options-group .comment {
 	color: gray;
 	font-style: italic;
 	margin-left: 10px;
@@ -83,3 +83,29 @@ div.options-group input {
 div.options-group input.number {
 	width: 50px;
 }
+
+.subHeading {
+	width: 85%;
+	text-align: center;
+	margin-top: 1.5em;
+}
+.scLink {
+	width: 85%;
+	text-align: center;
+}
+.scLink2 {
+	width: 73%;
+	text-align: center;
+}
+
+table.invisible {
+    table-layout: fixed;
+    border-collapse: separate;
+    border-spacing: 5px;
+    margin:1em auto;
+}
+
+/* custom row */
+.options-group .custom-row {
+	grid-column-start: span 2;
+}
diff --git a/src/gui/favorite.js b/src/gui/favorite.js
new file mode 100644
index 0000000000000000000000000000000000000000..1c6e89ba298f847c588b2113b43cb8f9854bc76c
--- /dev/null
+++ b/src/gui/favorite.js
@@ -0,0 +1,29 @@
+
+/** Render a link that toggles the slave's favorite status
+ * @param {App.Entity.SlaveState} slave
+ * @returns {HTMLSpanElement}
+ */
+App.UI.favoriteToggle = function(slave) {
+	function favLink() {
+		const linkID = `fav-link-${slave.ID}`;
+		if (V.favorites.includes(slave.ID)) {
+			const link = App.UI.DOM.link(String.fromCharCode(0xe800), () => {
+				V.favorites.delete(slave.ID);
+				$(`#${linkID}`).replaceWith(favLink());
+			});
+			link.classList.add("icons", "favorite");
+			link.id = linkID;
+			return link;
+		} else {
+			const link = App.UI.DOM.link(String.fromCharCode(0xe801), () => {
+				V.favorites.push(slave.ID);
+				$(`#${linkID}`).replaceWith(favLink());
+			});
+			link.classList.add("icons", "not-favorite");
+			link.id = linkID;
+			return link;
+		}
+	}
+
+	return favLink();
+};
diff --git a/src/gui/options/options.js b/src/gui/options/options.js
index ce2ac0d11c8185047def6ae2d36dd40bdac643e4..f4ad29f7a0ecf6353d51a332d513d23adb284d9d 100644
--- a/src/gui/options/options.js
+++ b/src/gui/options/options.js
@@ -1,4 +1,12 @@
 App.UI.OptionsGroup = (function() {
+	class Row {
+		/**
+		 * @param {HTMLDivElement} container
+		 * @param {boolean} doubleColumn
+		 */
+		render(container, doubleColumn) {} // jshint ignore:line
+	}
+
 	/**
 	 * @typedef value
 	 * @property {*} value
@@ -12,13 +20,14 @@ App.UI.OptionsGroup = (function() {
 	 * @property {Function} [callback]
 	 */
 
-	const Option = class {
+	class Option extends Row {
 		/**
 		 * @param {string} description can be SC markup
 		 * @param {string} property
 		 * @param {object} [object=V]
 		 */
 		constructor(description, property, object = V) {
+			super();
 			this.description = description;
 			this.property = property;
 			this.object = object;
@@ -161,50 +170,25 @@ App.UI.OptionsGroup = (function() {
 			this.valuePairs.last().neutral = true;
 			return this;
 		}
-	};
-
-	return class {
-		constructor() {
-			/**
-			 * @type {Array<Option>}
-			 */
-			this.options = [];
-		}
 
 		/**
-		 * @returns {App.UI.OptionsGroup}
+		 * @param {HTMLDivElement} container
 		 */
-		enableDoubleColumn() {
-			this.doubleColumn = true;
-			return this;
-		}
+		render(container) {
+			/* left side */
+			const desc = document.createElement("div");
+			desc.className = "description";
+			$(desc).wiki(this.description);
+			container.append(desc);
 
-		addOption(name, property, object = V) {
-			const option = new Option(name, property, object);
-			this.options.push(option);
-			return option;
-		}
+			/* right side */
+			const currentValue = this.object[this.property];
+			let anySelected = false;
 
-		render() {
-			const container = document.createElement("div");
-			container.className = "options-group";
-			if (this.doubleColumn) {
-				container.classList.add("double");
-			}
-			for (/** @type {Option} */ const option of this.options) {
-				/* left side */
-				const desc = document.createElement("div");
-				desc.className = "description";
-				$(desc).wiki(option.description);
-				container.append(desc);
-
-				/* right side */
-				const currentValue = option.object[option.property];
-				let anySelected = false;
-
-				const buttonGroup = document.createElement("div");
-				buttonGroup.classList.add("button-group");
-				for (const value of option.valuePairs) {
+			const buttonGroup = document.createElement("div");
+			buttonGroup.classList.add("button-group");
+			if (this.valuePairs.length < 6) {
+				for (const value of this.valuePairs) {
 					if (value.mode === "plain") {
 						/* insert custom SC markup and go to next element */
 						$(buttonGroup).wiki(value.value);
@@ -249,7 +233,7 @@ App.UI.OptionsGroup = (function() {
 							}
 						}
 						button.onclick = () => {
-							option.object[option.property] = value.value;
+							this.object[this.property] = value.value;
 							if (value.callback) {
 								value.callback();
 							}
@@ -258,30 +242,52 @@ App.UI.OptionsGroup = (function() {
 					}
 					buttonGroup.append(button);
 				}
-				if (option.textbox) {
-					const isNumber = typeof currentValue === "number";
-					const textbox = App.UI.DOM.makeTextBox(currentValue, input => {
-						option.object[option.property] = input;
-						App.UI.reload();
-					}, isNumber);
-					if (isNumber) {
-						textbox.classList.add("number");
+			} else {
+				let select = document.createElement("select");
+				select.classList.add("rajs-list");
+
+				for (const value of this.valuePairs) {
+					let el = document.createElement("option");
+					el.textContent = value.name;
+					el.value = value.value;
+					if (this.object[this.property] === value.value) {
+						el.selected = true;
 					}
-					buttonGroup.append(textbox);
-					if (option.unit) {
-						buttonGroup.append(" ", option.unit);
+					select.appendChild(el);
+				}
+				select.onchange = () => {
+					const O = select.options[select.selectedIndex];
+					if (isNaN(Number(O.value))) {
+						this.object[this.property] = O.value;
+					} else {
+						this.object[this.property] = Number(O.value);
 					}
+					App.UI.reload();
+				};
+				buttonGroup.append(select);
+			}
+
+			if (this.textbox) {
+				const isNumber = typeof currentValue === "number";
+				const textbox = App.UI.DOM.makeTextBox(currentValue, input => {
+					this.object[this.property] = input;
+					App.UI.reload();
+				}, isNumber);
+				if (isNumber) {
+					textbox.classList.add("number");
 				}
-				if (option.comment) {
-					const comment = document.createElement("span");
-					comment.classList.add("comment");
-					$(comment).wiki(option.comment);
-					buttonGroup.append(comment);
+				buttonGroup.append(textbox);
+				if (this.unit) {
+					buttonGroup.append(" ", this.unit);
 				}
-				container.append(buttonGroup);
 			}
-
-			return container;
+			if (this.comment) {
+				const comment = document.createElement("span");
+				comment.classList.add("comment");
+				$(comment).wiki(this.comment);
+				buttonGroup.append(comment);
+			}
+			container.append(buttonGroup);
 
 			function inRange(mode, compareValue, value) {
 				if (mode === "<") {
@@ -296,5 +302,131 @@ App.UI.OptionsGroup = (function() {
 				return false;
 			}
 		}
+	}
+
+	class Comment extends Row {
+		/**
+		 * @param {string} comment can be SC markup
+		 */
+		constructor(comment) {
+			super();
+			this.comment = comment;
+			this.long = false;
+		}
+
+		/**
+		 * @param {HTMLDivElement} container
+		 */
+		render(container) {
+			/* left */
+			container.append(document.createElement("div"));
+
+			/* right */
+			const comment = document.createElement("div");
+			comment.classList.add("comment");
+			$(comment).wiki(this.comment);
+			container.append(comment);
+		}
+	}
+
+	class CustomRow extends Row {
+		/**
+		 * @param {HTMLElement|string} element
+		 */
+		constructor(element) {
+			super();
+			this.element = element;
+		}
+
+		/**
+		 * @param {HTMLDivElement} container
+		 * @param {boolean}doubleColumn
+		 */
+		render(container, doubleColumn) {
+			/** @type {HTMLDivElement} */
+			const div = App.UI.DOM.makeElement("div", this.element, "custom-row");
+			let factor = 0.7;
+			if (doubleColumn) {
+				factor *= 0.5;
+			}
+			div.style.width = `${Math.round(window.innerWidth * factor)}px`;
+			container.append(div);
+		}
+	}
+
+	return class {
+		constructor() {
+			/**
+			 * @type {Array<Row>}
+			 */
+			this.rows = [];
+			this.doubleColumn = false;
+		}
+
+		/**
+		 * @returns {App.UI.OptionsGroup}
+		 */
+		enableDoubleColumn() {
+			this.doubleColumn = true;
+			return this;
+		}
+
+		/**
+		 * @template {Row} T
+		 * @param {T} row
+		 * @returns {T}
+		 * @private
+		 */
+		_addRow(row) {
+			this.rows.push(row);
+			return row;
+		}
+
+		/**
+		 * @param {string} name
+		 * @param {string} property
+		 * @param {object} [object=V]
+		 * @returns {Option}
+		 */
+		addOption(name, property, object = V) {
+			const option = new Option(name, property, object);
+			return this._addRow(option);
+		}
+
+		/**
+		 * @param {string} comment may contain SC markup
+		 * @returns {Comment}
+		 */
+		addComment(comment) {
+			const c = new Comment(comment);
+			return this._addRow(c);
+		}
+
+		/**
+		 * Adds a custom element taking up both rows
+		 *
+		 * @param {HTMLElement|string} element
+		 * @returns {CustomRow}
+		 */
+		addCustom(element) {
+			return this._addRow(new CustomRow(element));
+		}
+
+		/**
+		 * @returns {HTMLDivElement}
+		 */
+		render() {
+			const container = document.createElement("div");
+			container.className = "options-group";
+			if (this.doubleColumn) {
+				container.classList.add("double");
+			}
+
+			for (/** @type {Row} */ const row of this.rows) {
+				row.render(container, this.doubleColumn);
+			}
+
+			return container;
+		}
 	};
 })();
diff --git a/src/gui/options/options.tw b/src/gui/options/options.tw
index b5df4232895fee2e73c527ec9edc780472672753..ca66ab0ffb92b01a2d5066879ccc78c592c88172 100644
--- a/src/gui/options/options.tw
+++ b/src/gui/options/options.tw
@@ -1,21 +1,5 @@
 :: Options [nobr jump-to-safe jump-from-safe]
 
-<style>
-.subHeading {
-	width: 85%;
-	text-align: center;
-	margin-top: 1.5em;
-}
-.scLink {
-	width: 85%;
-	text-align: center;
-}
-.scLink2 {
-	width: 73%;
-	text-align: center;
-}
-</style>
-
 <<if lastVisited("Slave Interact") === 1>>
 	<<set $storedLink = "Slave Interact">>
 <<else>>
@@ -23,724 +7,5 @@
 <</if>>
 
 <<set $nextButton = "Back", $nextLink = $storedLink, $encyclopedia = "How to Play">>
-<<set $passageSwitchHandler = App.EventHandlers.optionsChanged>>
-
-<<set _options = new App.UI.OptionsGroup()>>
-<<run _options.addOption("End of week autosaving is currently", "autosave")
-.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-<<includeDOM _options.render()>>
-
-<div>
-	This save was created using FC version $ver build $releaseID. You are currently playing version: <<= App.Version.base>>, mod version: <<= App.Version.pmod>>, build: <<= App.Version.release>><<if App.Version.commitHash>>, commit: <<= App.Version.commitHash>><</if>>
-</div>
-<div class="indent">
-	[[Apply Backwards Compatibility Update|Backwards Compatibility]]
-</div>
-<<link "Reset extended family mode controllers">>
-	<<run resetFamilyCounters()>>
-	<<replace "#family-hint">>//@@.lightgreen;Done:@@ all family relations flushed and rebuilt.//<</replace>>
-<</link>>
-<span id="family-hint">//Clears and rebuilds .sister and .daughter tracking.//</span>
-<<if isNaN($rep)>>
-	<br>[[Reset Reputation|Options][$rep = 0]]
-<</if>>
-<<if isNaN($cash)>>
-	<br>[[Reset Money|Options][$cash = 500]]
-<</if>>
-<<if $releaseID == 1057>>
-	<br>[[Free male anatomy removal due to accidentally flawed updater|Options][$PC.dick = 0, $PC.balls = 0, $PC.prostate = 0, $PC.scrotum = 0, $PC.ballsImplant = 0]] //Use this if your female PC picked up a few extra parts during the conversion process.//
-<</if>>
-
-<<if ($releaseID >= 1000) || $ver.startsWith("0.9") || $ver.startsWith("0.8") || $ver.startsWith("0.7") || $ver.startsWith("0.6")>>
-	<br><br>
-	''NEW GAME PLUS''
-	<br>
-	//You can begin a new game with up to five (or more) of your current slaves, although starting resources other than these slaves will be reduced. New Game Plus @@.yellow;MAY@@ work across versions. To attempt to migrate a save across versions://
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;//1) Save on this screen//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;//2) Re-open the .html in a new tab then load the above save.//
-	<br>&nbsp;&nbsp;&nbsp;&nbsp;//3) [[Activate New Game Plus.|New Game Plus][$ui = "start"]]
-<<else>>
-	//New Game Plus is not available because this game was not started with a compatible version.//
-<</if>>
-<br>
-
-<<run App.UI.tabBar.handlePreSelectedTab($tabChoice.Options)>>
-
-<div class="tab-bar">
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'display')" id="tab display">Display</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'content&flavor')" id="tab content&flavor">Content & flavour</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'mods')" id="tab mods">Mods</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'debug&cheating')" id="tab debug&cheating">Debug/cheating</button>
-	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'experimental')" id="tab experimental">Experimental</button>
-</div>
-
-<div id="display" class="tab-content">
-	<div class="content">
-		<h2>Reports</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("End week report descriptive details are", "showEWD")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("End week report performance details are", "showEWM")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("End week societal effects from slaves are", "compressSocialEffects", $UI)
-		.addValue("Expanded", 0).on().addValue("Compacted", 1).off()>>
-
-		<<run _options.addOption("Accordion on week end defaults to", "useAccordion")
-		.addValue("Open", 0).on().addValue("Collapsed", 1).off()>>
-
-		<<run _options.addOption("Economic Tabs on weekly reports are", "useTabs")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Economic detail sheets for facilities are", "showEconomicDetails")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Economic report neighbor details such as trade impacts on culture are", "showNeighborDetails")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Numeric formatting is currently", "formatNumbers")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will comma-format numbers in some areas.")>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>General</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("Main menu leadership controls displayed", "positionMainLinks")
-		.addValueList([["Above", 1], ["Above and below", 0], ["Below", -1]])>>
-
-		<<run _options.addOption("New Model UI", "newModelUI")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Penthouse Facility Display", "verticalizeArcologyLinks")
-		.addValueList([["Triple column", 3], ["Double Coloumn", 2], ["Single Column", 1], ["Collapsed", 0]])>>
-
-		<<run _options.addOption("Main menu arcology description", "seeArcology")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Main menu desk description", "seeDesk")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Main menu newsfeed", "seeFCNN")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Tips from the Encyclopedia are", "showTipsFromEncy")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Help tooltips are", "tooltipsEnabled")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This is mostly for new players. @@.exampleTooltip.noteworthy;Colored text@@ can have tooltips.")>>
-
-		<<run _options.addOption("Main menu slave tabs are", "useSlaveSummaryTabs")
-		.addValue("Enabled", 1).on().addValue("CardStyle", 2).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("The slave Quick list in-page scroll-to is", "useSlaveListInPageJSNavigation")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Condense special slaves into their own tab", "useSlaveSummaryOverviewTab")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Interactions with your fucktoys are", "fucktoyInteractionsPosition")
-		.addValueList([["next to them", 1], ["at page bottom", 0]])>>
-
-		<<run _options.addOption("Hide tabs in Slave Interact", "slaveInteractLongForm")
-		.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-
-		<<run _options.addOption("Line separations are", "lineSeparations")
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<includeDOM _options.render()>>
-
-		<p>
-			UI theme selector. Allows to select a single CSS file to be loaded. <span class="red">The file has to be located in the same directory as the HTML file otherwise it will simply not load at all.</span>
-			<<includeDOM App.UI.Theme.selector()>>
-		</p>
-
-		<h2>Sidebar</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("Cash is", "Cash", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("Upkeep is", "Upkeep", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("Sex slave count is", "SexSlaveCount", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("Room population is", "roomPop", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("GSP is", "GSP", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("Rep is", "Rep", $sideBarOptions)
-		.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-		<<run _options.addOption("Confirmation before ending a week is", "confirmWeekEnd", $sideBarOptions)
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Enabling this will open a dialog box to confirm you meant to end a week.")>>
-
-		<<if $secExpEnabled > 0>>
-			<<run _options.addOption("Authority is", "Authority", $sideBarOptions)
-			.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-			<<run _options.addOption("Security is", "Security", $sideBarOptions)
-			.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-			<<run _options.addOption("Crime is", "Crime", $sideBarOptions)
-			.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-		<</if>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>Images</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("Images are", "seeImages")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<includeDOM _options.render()>>
-
-		<<if $seeImages > 0>>
-			<div class="imageRef" style="width:200px;height:200px;margin-left:50px;">
-				<<= SlaveArt(BaseSlave(), 0, 0)>>
-			</div>
-
-			<<set _options = new App.UI.OptionsGroup()>>
-
-			<<run _options.addOption("", "imageChoice")
-			.addValueList([["Revamped embedded vector art", 3], ["Non-embedded vector art", 2], ["NoX/Deepmurk's vector art", 1], ["Shokushu's rendered imagepack", 0]])>>
-
-			<<if $imageChoice === 1>>
-				<<run _options.addOption("").addComment('<span class="warning">Git compiled only, no exceptions.</span>')>>
-
-				<<run _options.addOption("Face artwork is", "seeFaces")
-				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-				<<run _options.addOption("Highlights on shiny clothing are", "seeVectorArtHighlights")
-				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-				<<run _options.addOption("Height scaling", "seeHeight")
-				.addValue("All images", 2).on().addValue("Small images", 1).neutral().addValue("Disabled", 0).off()>>
-
-				<<run _options.addOption("Clothing erection bulges are", "showClothingErection")
-				.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-			<<elseif $imageChoice === 0>>
-				<<run _options.addOption("").addComment(`You need """to"""
-					<a href="https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0">download the image pack</a>
-					"""and""" put the 'renders' folder into the resources/ folder where this html file is.`
-				)>>
-
-				<<run _options.addOption("Slave summary fetish images are", "seeMainFetishes")
-				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-			<<elseif $imageChoice === 3>>
-				<<run _options.addOption("").addComment('<span class="warning">Git compiled only, no exceptions.</span>')>>
-
-				<<run _options.addOption("Clothing erection bulges are", "showClothingErection")
-				.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-			<</if>>
-
-			<<run _options.addOption("PA avatar art is", "seeAvatar")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Slave images in lists are", "seeSummaryImages")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Slave images in the weekly report are", "seeReportImages")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<includeDOM _options.render()>>
-		<</if>>
-	</div>
-</div>
-
-<div id="content&flavor" class="tab-content">
-	<div class="content">
-		<h2>Content</h2>
-
-		//More granular control of what appears is in [[Description Options]].//
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("The difficulty setting is currently set to", "baseDifficulty")
-		.addValueList([["Very easy", 1], ["Easy", 2], ["Default", 3], ["Hard", 4], ["Very hard", 5]])>>
-
-		<<run _options.addOption("Slaves falling ill is currently", "seeIllness")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Will not affect existing ill slaves already in-game.")>>
-
-		<<run _options.addOption("Extreme content like amputation is currently", "seeExtreme")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Will not affect extreme surgeries already applied in-game.")>>
-
-		<<run _options.addOption("Bestiality related content is currently", "seeBestiality")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Watersports related content is currently", "seePee")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Incest content is currently", "seeIncest")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Pregnancy related content is currently", "seePreg")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Will not affect existing pregnancies already in-game.")>>
-
-		<<run _options.addOption("Child gender to be generated based off dick content settings", "seeDicksAffectsPregnancy")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("<<if $seeDicksAffectsPregnancy === 1>>Currently <<print $seeDicks>>% of children will be born male. <</if>>Will not affect existing pregnancies already in-game.")>>
-
-		<<if $seeDicksAffectsPregnancy === 0>>
-			<<run _options.addOption("XX slaves only father daughters", "adamPrinciple")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-			.addComment("Will not affect existing pregnancies already in-game.")>>
-		<</if>>
-
-		<<run _options.addOption("Extreme pregnancy content like broodmothers is currently", "seeHyperPreg")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Will not affect existing hyperpregnancies already in-game.")>>
-
-		<<run _options.addOption("Pregnancy complications due to multiples and body size are currently", "dangerousPregnancy")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Precocious puberty (pregnancy younger than $fertilityAge)", "precociousPuberty")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Will not affect existing precocious puberty cases already in-game.")>>
-
-		<<run _options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping")
-		.addValue("Yes", 0).on().addValue("No", 1).off()>>
-
-		<<run _options.addOption("Disables the long term damage mechanic. //Temp option//", "disableLongDamage")
-		.addValue("Enabled", 0).on().addValue("Disabled", 1).off()>>
-
-		<<run _options.addOption("Experimental male pronouns are currently", "diversePronouns")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Apply Backwards Compatibility after changing to update slave's pronouns. Not all scenes support male pronouns and this is not yet incorporated into the lore or mechanics.")>>
-
-		<<run _options.addOption("Male slave names are currently", "allowMaleSlaveNames")
-		.addValue("Enabled", true).on().addValue("Disabled", false).off()
-		.addComment("This only affects slave generation and not your ability to name your slaves.")>>
-
-		<<run _options.addOption("Missing slave names are currently", "showMissingSlaves")
-		.addValue("Enabled", true).on().addValue("Disabled", false).off()>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>Intersecting mechanics</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("Slave assets affected by weight is", "weightAffectsAssets")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Diet will still affect asset size.")>>
-
-		<<run _options.addOption("Curative side effects are", "curativeSideEffects")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("If enabled, curatives have a chance to give slaves harmful side effects.")>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>Flavour</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("Slave reactions to facility assignments are", "showAssignToScenes")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Post sex clean up", "postSexCleanUp")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Appraisal miniscenes on slave sale are", "showAppraisal")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Assignment performance vignettes on the end week report are", "showVignettes")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Slaves can have alternate titles", "newDescriptions")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Family titles for relatives", "allowFamilyTitles")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<run _options.addOption("Limit family growth", "limitFamilies")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Restricts acquisition of additional relatives, by means other than birth, for slaves with families.")>>
-
-		<<run _options.addOption("Distant relatives such as aunts, nieces and cousins are", "showDistantRelatives")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-		<<includeDOM _options.render()>>
-	</div>
-</div>
-
-<div id="mods" class="tab-content">
-	<div class="content">
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("The Special Force Mod is currently", "Toggle", $SF)
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("<div>This mod is triggered after week 72. It is non-canon where it conflicts with canonical updates to the base game.</div>")>>
-
-		<<run _options.addOption("The Security Expansion mod is", "secExpEnabled")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("<div>The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.</div>")>>
-
-		<<includeDOM _options.render()>>
-
-		<<if $secExpEnabled > 0>>
-			<<if Object.values(V.SecExp).length === 0>>
-				<<include "SecExpBackwardCompatibility">>
-				<<goto "Options">>
-			<</if>>
-			<h2>Security Expansion mod options</h2>
-			<<if $terrain === "oceanic">>
-				<div>Oceanic arcologies are not by default subject to external attacks. You can however allow them to happen anyway. If you choose to do so please keep in mind that descriptions and mechanics are not intended for naval combat but land combat.</div>
-			<</if>>
-			<<set _options = new App.UI.OptionsGroup()>>
-
-			<<if $SecExp.settings.battle.enabled > 0 || $SecExp.settings.rebellion.enabled > 0>>
-				<<run _options.addOption("Detailed battle statistics are", "showStats", $SecExp.settings)
-				.addValue("Shown", 1).on().addValue("Hidden", 0).off()
-				.addComment("Visibility of detailed statistics and battle turns.")>>
-
-				<<run _options.addOption("Difficulty is", "difficulty", $SecExp.settings)
-				.addValueList([["Extremely hard", 2], ["Very hard", 1.5], ["Hard", 1.25], ["Normal", 1], ["Easy", 0.75], ["Very easy", 0.5]])>>
-
-				<<run _options.addOption("Unit descriptions are", "unitDescriptions", $SecExp.settings)
-				.addValue("Abbreviated", 1).addValue("Summarized", 0)>>
-			<</if>>
-
-			<<run _options.addOption("Battles are", "enabled", $SecExp.settings.battle)
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<run _options.addOption("Rebellions are", "enabled", $SecExp.settings.rebellion)
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-
-			<<if $SecExp.settings.battle.enabled > 0>>
-				<<run _options.addOption("Battle frequency", "frequency", $SecExp.settings.battle)
-				.addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]])>>
-			<</if>>
-
-			<<if $SecExp.settings.rebellion.enabled > 0>>
-				<<run _options.addOption("Rebellion buildup", "speed", $SecExp.settings.rebellion)
-				.addValueList([["Extremely fast", 2], ["Very fast", 1.5], ["Fast", 1.25], ["Normal", 1], ["Slow", 0.75], ["Very slow", 0.5]])>>
-			<</if>>
-
-
-			<<if $SecExp.settings.battle.enabled > 0>>
-				<<run _options.addOption("Commanders gain a prestige rank every 10 victories", "allowSlavePrestige", $SecExp.settings.battle)
-				.addValue("Yes", 1).on().addValue("No", 0).off()>>
-			<</if>>
-
-			<<if $SecExp.settings.battle.enabled > 0>>
-				<<run _options.addOption("Force battles", "force", $SecExp.settings.battle)
-				.addValue("Yes", 1).on().addValue("No", 0).off()>>
-			<</if>>
-			<<if $SecExp.settings.rebellion.enabled > 0>>
-				<<run _options.addOption("Force rebellions", "force", $SecExp.settings.rebellion)
-				.addValue("Yes", 1).on().addValue("No", 0).off()
-				.addComment("Rebellions take precedence over Battles.")>>
-			<</if>>
-
-			<<if $SecExp.settings.battle.enabled > 0>>
-				<<run _options.addOption("Late game major battles are", "enabled", $SecExp.settings.battle.major)
-				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-			<</if>>
-
-			<<if $SecExp.settings.battle.enabled > 0 && $SecExp.settings.battle.major.enabled > 0>>
-				<<run _options.addOption("Multiplier is", "mult", $SecExp.settings.battle.major)
-				.addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]])>>
-
-				<<run _options.addOption("This week a major battle is", "force", $SecExp.settings.battle.major)
-				.addValue("Guaranteed", 1).on().addValue("Not guaranteed", 0).off()>>
-			<</if>>
-
-			<<if $SecExp.settings.battle.enabled > 0 && $SecExp.settings.battle.major.enabled > 0>>
-				<<run _options.addOption("Gameover on battle loss", "gameOver", $SecExp.settings.battle.major)
-				.addValue("Yes", 1).on().addValue("No", 0).off()>>
-			<</if>>
-
-			<<if $SecExp.settings.rebellion.enabled > 0>>
-				<<run _options.addOption("Gameover on rebellion loss", "gameOver", $SecExp.settings.rebellion)
-				.addValue("Yes", 1).on().addValue("No", 0).off()>>
-			<</if>>
-
-			<<includeDOM _options.render()>>
-
-			<div class="subHeading">
-			<<if $debugMode || $cheatMode || $cheatModeM>>
-				''Debug/cheat''
-				<<set _popCap = menialPopCap()>>
-
-				<style>
-					table.invisible {
-						table-layout: fixed;
-						border-collapse: separate;
-						border-spacing: 5px;
-						margin:1em auto;
-					}
-				</style>
-
-				<table class="invisible">
-				<tr style="text-align:center">
-				<td colspan="2" >
-				<<link "Set loyalty high" "Options">>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<set $militiaUnits[_i].loyalty = random(80,100)>>
-					<</for>>
-
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<set $slaveUnits[_i].loyalty = random(80,100)>>
-					<</for>>
-
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<set $mercUnits[_i].loyalty = random(80,100)>>
-					<</for>>
-				<</link>>
-				| <<link "Set loyalty average" "Options">>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<set $militiaUnits[_i].loyalty = random(40,60)>>
-					<</for>>
-
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<set $slaveUnits[_i].loyalty = random(40,60)>>
-					<</for>>
-
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<set $mercUnits[_i].loyalty = random(40,60)>>
-					<</for>>
-				<</link>>
-				| <<link "Set loyalty low" "Options">>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<set $militiaUnits[_i].loyalty = random(20)>>
-					<</for>>
-
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<set $slaveUnits[_i].loyalty = random(20)>>
-					<</for>>
-
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<set $mercUnits[_i].loyalty = random(20)>>
-					<</for>>
-				<</link>>
-				| <<link "Randomize loyalty" "Options">>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<set $militiaUnits[_i].loyalty = random(100)>>
-					<</for>>
-
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<set $slaveUnits[_i].loyalty = random(100)>>
-					<</for>>
-
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<set $mercUnits[_i].loyalty = random(100)>>
-					<</for>>
-				<</link>>
-				</td>
-				</tr>
-
-				<tr style="text-align:center">
-				<td colspan="2">
-				<<link "Give Authority" "Options">>
-					<<set $SecExp.core.authority = Math.clamp($SecExp.core.authority + 1000, 0, 20000)>>
-				<</link>>
-				| <<link "Remove Authority" "Options">>
-					<<set $SecExp.core.authority = Math.clamp($SecExp.core.authority - 1000, 0, 20000)>>
-				<</link>>
-				</td>
-				</tr>
-
-				<tr>
-				<td style="text-align:right">
-				<<link "Raise security" "Options">>
-					<<set $SecExp.core.security = Math.clamp($SecExp.core.security + 5, 0, 100)>>
-				<</link>>
-				| <<link "Lower security" "Options">>
-					<<set $SecExp.core.security = Math.clamp($SecExp.core.security - 5, 0, 100)>>
-				<</link>>
-				</td>
-
-				<td style="text-align:left">
-				<<link "Raise crime" "Options">>
-					<<set $SecExp.core.crimeLow = Math.clamp($SecExp.core.crimeLow + 5, 0, 100)>>
-				<</link>>
-				| <<link "Lower crime" "Options">>
-					<<set $SecExp.core.crimeLow = Math.clamp($SecExp.core.crimeLow - 5, 0, 100)>>
-				<</link>>
-				</td>
-				</tr>
-
-				<tr>
-				<td style="text-align:right">
-				<<link "Give militia manpower" "Options">>
-					<<set $militiaFreeManpower += 30>>
-				<</link>>
-				| <<link "Remove militia manpower" "Options">>
-					<<set $militiaFreeManpower = Math.max($militiaFreeManpower - 30, 0)>>
-				<</link>>
-				</td>
-
-				<td style="text-align:left">
-				<<link "Give mercs manpower" "Options">>
-					<<set $mercFreeManpower += 30>>
-				<</link>>
-				| <<link "Remove mercs manpower" "Options">>
-					<<set $mercFreeManpower = Math.max($mercFreeManpower - 30, 0)>>
-				<</link>>
-				</td>
-				</tr>
-
-				</table>
-			<</if>>	/* closes cheatmode check */
-			</div>
-		<</if>> /* closes SecExp check*/
-	</div>
-</div>
-
-<div id="debug&cheating" class="tab-content">
-	<div class="content">
-		<h2>Debug</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("DebugMode is", "debugMode")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will add a Display Variables and Bug Report passage to the sidebar.")>>
-
-		<<if $debugMode > 0>>
-			<<run _options.addOption("The custom function part of debug mode is", "debugModeCustomFunction")
-			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
-		<</if>>
-
-		<<set _option =  _options.addOption("Genetics array")
-		.customButton("Run test", () => Engine.play("test genetics"))>>
-		<<if $cheatMode == 1>>
-			<<run _option.customButton("Edit Genetics", () => Engine.play("Edit Genetics"))>>
-		<<else>>
-			<<run _option.addComment("Enable cheat mode to edit genetics.")>>
-		<</if>>
-
-		<<run _options.addOption("Rules Assistant").addCustomElement('<<button "Reset Rules">><<run initRules()>><<goto "Rules Assistant">><</button>>')>>
-
-		<<run _options.addOption("Passage Profiler is", "profiler")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("Outputs performance data at the bottom of every passage.")>>
-
-		<<includeDOM _options.render()>>
-
-		<h2>Cheating</h2>
-
-		<<set _options = new App.UI.OptionsGroup()>>
-
-		<<run _options.addOption("CheatMode is", "cheatMode")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will allow manual selection of events and unlock some options that would usually be restricted by progress.")>>
-
-		<<if $cheatMode === 0>>
-			<<includeDOM _options.render()>>
-		<<else>>
-			<<run _options.addOption("Sidebar Cheats are currently", "cheatModeM")
-			.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-			<<run _options.addOption("Slave aging", "seeAge")
-			.addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off()>>
-
-			<<includeDOM _options.render()>>
-
-			<div class="scLink2">
-			[[Add 100000 money|Options][$cheater = 1, cashX(100000, "cheating")]] | [[Add 10000 rep|Options][$cheater = 1, repX(10000, "cheating")]] //Cheating will be flagged in your save//
-			</div>
-
-			<div class="scLink2">
-			<<= SectorCounts()>>
-			<<link "Raise prosperity cap" "Options">>
-				<<set $AProsperityCapModified += 10>>
-			<</link>>
-			| <<link "Lower prosperity cap" "Options">>
-				<<set $AProsperityCapModified -= 10>>
-			<</link>>
-
-			<br>
-			<<link "Raise prosperity" "Options">>
-				<<set $arcologies[0].prosperity = Math.clamp($arcologies[0].prosperity + 10, 0, $AProsperityCap)>>
-			<</link>>
-			| <<link "Lower prosperity" "Options">>
-				<<set $arcologies[0].prosperity = Math.clamp($arcologies[0].prosperity - 10, 0, $AProsperityCap)>>
-			<</link>>
-
-			<br>
-			<<link "Give menial slaves" "Options">>
-				<<set $menials = Math.clamp($menials + 30, 0, _popCap.value)>>
-			<</link>>
-			| <<link "Remove menial slaves" "Options">>
-				<<set $menials = Math.clamp($menials - 30, 0, _popCap.value)>>
-			<</link>>
-			</div>
-
-			/%
-			<br>Thanks for making this a right pain twine
-			<br>
-			<<link "Add citizens" "Options">> /*Will no longer work as intended due to population changes*/
-				<<set $lowerClass = Math.max($lowerClass + 200, 0)>>
-			<</link>>
-			| <<link "Remove citizens" "Options">> /*also no longer properly functional*/
-				<<set $lowerClass = Math.max($lowerClass - 200, 0)>>
-			<</link>>
-
-			<br>
-			<<link "Add slaves" "Options">> /*Will work to a limited degree, minimums and maximums for slaves are set through population*/
-				<<set $NPCSlaves = Math.max($NPCSlaves + 200, 0)>>
-			<</link>>
-			| <<link "Remove slaves" "Options">> /*Will work to a limited degree*/
-				<<set $NPCSlaves = Math.max($NPCSlaves - 200, 0)>>
-			<</link>>
-			%/
-		<</if>>
-	</div>
-</div>
-
-<div id="experimental" class="tab-content">
-	<div style="fontWeight:bold">
-		Experimental means just that: experimental. Options below are likely to be in an <span class="yellow">even more incomplete or broken state than usual.</span> <span class="red">THEY MAY NOT WORK AT ALL.</span> Make sure you back up your save before enabling any of these, and if you are that interested, consider helping to improve them.
-	</div>
-
-	<<set _options = new App.UI.OptionsGroup()>>
-
-	<<if $seePreg !== 0>>
-		<<run _options.addOption("Nursery is", "nursery", $experimental)
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will enable the experimental nursery, which allows players to interact with growing slave children. An alternative to the incubator.")>>
-	<</if>>
-
-	<<run _options.addOption("Food is", "food", $experimental)
-	.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-	.addComment("This will enable the experimental food supply and demand system, as well as a new farmyard building and assignments.")>>
-
-	<<if $seeExtreme === 1 && $seeBestiality === 1>>
-		<<run _options.addOption("Animal Ovaries are", "animalOvaries", $experimental)
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will allow slaves to be impregnated by animals.")>>
-	<</if>>
-
-	<<if $seeExtreme === 1>>
-		<<run _options.addOption("Dinner party", "dinnerParty", $experimental)
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
-		.addComment("This will enable a controversial but very broken event. Warning: Snuff, cannibalism.")>>
-	<</if>>
-
-	<<run _options.addOption("New event", "tempEventToggle")
-		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()>>
 
-	<<includeDOM _options.render()>>
-</div>
+<<includeDOM App.UI.optionsPassage()>>
\ No newline at end of file
diff --git a/src/gui/options/optionsPassage.js b/src/gui/options/optionsPassage.js
new file mode 100644
index 0000000000000000000000000000000000000000..02f2dbfbe2006106f142f4b51489151709ce3bec
--- /dev/null
+++ b/src/gui/options/optionsPassage.js
@@ -0,0 +1,1067 @@
+App.UI.optionsPassage = function() {
+	const el = new DocumentFragment();
+	V.passageSwitchHandler = App.EventHandlers.optionsChanged;
+	el.append(intro());
+
+	// Results
+	const results = document.createElement("div");
+	results.id = "results";
+	el.append(results);
+
+	App.UI.tabBar.handlePreSelectedTab(V.tabChoice.Options);
+
+	// TODO: move me
+	/**
+	 *
+	 * @param {string} id
+	 * @param {Node} element
+	 * @returns {HTMLSpanElement}
+	 */
+	function makeSpanIded(id, element) {
+		const span = document.createElement("span");
+		span.id = id;
+		span.append(element);
+		return span;
+	}
+
+	const tabCaptions = {
+		"display": 'Display',
+		"contentFlavor": 'Content & flavour',
+		"mods": 'Mods',
+		"debugCheating": 'Debug & cheating',
+		"experimental": 'Experimental'
+	};
+
+	const tabBar = App.UI.DOM.appendNewElement("div", el, '', "tab-bar");
+	tabBar.append(
+		App.UI.tabBar.tabButton('display', tabCaptions.display),
+		App.UI.tabBar.tabButton('content-flavor', tabCaptions.contentFlavor),
+		App.UI.tabBar.tabButton('mods', tabCaptions.mods),
+		App.UI.tabBar.tabButton('debug-cheating', tabCaptions.debugCheating),
+		App.UI.tabBar.tabButton('experimental', tabCaptions.experimental),
+	);
+
+	el.append(App.UI.tabBar.makeTab('display', makeSpanIded("content-display", display())));
+	el.append(App.UI.tabBar.makeTab('content-flavor', makeSpanIded("content-content-flavor", contentFlavor())));
+	el.append(App.UI.tabBar.makeTab('mods', makeSpanIded("content-mods", mods())));
+	el.append(App.UI.tabBar.makeTab('debug-cheating', makeSpanIded("content-debug-cheating", debugCheating())));
+	el.append(App.UI.tabBar.makeTab('experimental', makeSpanIded("content-experimental", experimental())));
+
+	return el;
+
+	function intro() {
+		let links;
+		let options;
+		let r;
+		const el = new DocumentFragment();
+
+		options = new App.UI.OptionsGroup();
+		options.addOption("End of week autosaving is currently", "autosave")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("div", el, `This save was created using FC version ${V.ver} build ${V.releaseID}. You are currently playing version: ${App.Version.base}, mod version: ${App.Version.pmod}, build: ${App.Version.release}${App.Version.commitHash ? `, commit: ${App.Version.commitHash}` : ``}`);
+
+		links = [];
+		links.push(App.UI.DOM.passageLink("Apply Backwards Compatibility Update", "Backwards Compatibility"));
+
+		links.push(
+			App.UI.DOM.link(
+				`Reset extended family mode controllers`,
+				() => {
+					resetFamilyCounters();
+					const span = document.createElement("span");
+					span.classList.add("note");
+					App.UI.DOM.appendNewElement("span", span, "Done: ", "lightgreen");
+					span.append("all family relations flushed and rebuilt.");
+					jQuery("#results").empty().append(span);
+				},
+				[],
+				"",
+				"Clears and rebuilds .sister and .daughter tracking."
+			)
+		);
+
+
+		if (isNaN(V.rep)) {
+			links.push(
+				App.UI.DOM.link(
+					`Reset Reputation (${V.rep})`,
+					() => {
+						V.rep = 0;
+						jQuery("#results").empty().append(`Reputation reset to ${V.rep}`);
+					},
+					[],
+					"Options"
+				)
+			);
+		}
+
+		if (isNaN(V.rep)) {
+			links.push(
+				App.UI.DOM.link(
+					`Reset Money (${V.cash})`,
+					() => {
+						V.cash = 500;
+						jQuery("#results").empty().append(`Cash reset to ${V.cash}`);
+					},
+					[],
+					"Options"
+				)
+			);
+		}
+
+		if (V.releaseID === 1057) {
+			links.push(
+				App.UI.DOM.link(
+					`Free male anatomy removal due to accidentally flawed updater`,
+					() => {
+						V.PC.dick = 0;
+						V.PC.balls = 0;
+						V.PC.prostate = 0;
+						V.PC.scrotum = 0;
+						V.PC.ballsImplant = 0;
+						jQuery("#results").empty().append(`Cash reset to ${V.cash}`);
+					},
+					[],
+					"Options",
+					"Use this if your female PC picked up a few extra parts during the conversion process.",
+				)
+			);
+		}
+
+		App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links));
+
+		if ((V.releaseID >= 1000) || V.ver.startsWith("0.9") || V.ver.startsWith("0.8") || V.ver.startsWith("0.7") || V.ver.startsWith("0.6")) {
+			App.UI.DOM.appendNewElement("h3", el, `NEW GAME PLUS`);
+			r = [];
+			r.push(`You can begin a new game with up to five (or more) of your current slaves, although starting resources other than these slaves will be reduced. New Game Plus`);
+			r.push(App.UI.DOM.makeElement("span", "MAY", "yellow"));
+			r.push(`work across versions. To attempt to migrate a save across versions:`);
+			App.Events.addNode(el, r, "div", "note");
+
+			const ngpInstructions = document.createElement("ol");
+			App.UI.DOM.appendNewElement("li", ngpInstructions, "Save on this screen", "note");
+			App.UI.DOM.appendNewElement("li", ngpInstructions, "Re-open the .html in a new tab then load the above save.", "note");
+			App.UI.DOM.appendNewElement(
+				"li",
+				ngpInstructions,
+				App.UI.DOM.link(
+					"Activate New Game Plus.",
+					() => {
+						V.ui = "start";
+					},
+					[],
+					"New Game Plus"
+				),
+				"note"
+			);
+			el.append(ngpInstructions);
+		} else {
+			App.UI.DOM.appendNewElement("div", el, `New Game Plus is not available because this game was not started with a compatible version.`, "note");
+		}
+		return el;
+	}
+
+	function display() {
+		const el = new DocumentFragment();
+		let options;
+		let r;
+
+		App.UI.DOM.appendNewElement("h2", el, "Reports");
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("End week report descriptive details are", "showEWD")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("End week report performance details are", "showEWM")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("End week societal effects from slaves are", "compressSocialEffects", V.UI)
+			.addValue("Expanded", 0).on().addValue("Compacted", 1).off();
+
+		options.addOption("Accordion on week end defaults to", "useAccordion")
+			.addValue("Open", 0).on().addValue("Collapsed", 1).off();
+
+		options.addOption("Economic Tabs on weekly reports are", "useTabs")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Economic detail sheets for facilities are", "showEconomicDetails")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Economic report neighbor details such as trade impacts on culture are", "showNeighborDetails")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Numeric formatting is currently", "formatNumbers")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("This will comma-format numbers in some areas.");
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, "General");
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Main menu leadership controls displayed", "positionMainLinks")
+			.addValueList([["Above", 1], ["Above and below", 0], ["Below", -1]]);
+
+		options.addOption("New Model UI", "newModelUI")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Penthouse Facility Display", "verticalizeArcologyLinks")
+			.addValueList([["Triple column", 3], ["Double Column", 2], ["Single Column", 1], ["Collapsed", 0]]);
+
+		options.addOption("Main menu arcology description", "seeArcology")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Main menu desk description", "seeDesk")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Main menu newsfeed", "seeFCNN")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Tips from the Encyclopedia are", "showTipsFromEncy")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Help tooltips are", "tooltipsEnabled")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment(`This is mostly for new players. <span class='exampleTooltip noteworthy'>Colored text</span> can have tooltips.`);
+
+		options.addOption("Main menu slave tabs are", "useSlaveSummaryTabs")
+			.addValue("Enabled", 1).on().addValue("CardStyle", 2).on().addValue("Disabled", 0).off();
+
+		options.addOption("The slave Quick list in-page scroll-to is", "useSlaveListInPageJSNavigation")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Condense special slaves into their own tab", "useSlaveSummaryOverviewTab")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Interactions with your fucktoys are", "fucktoyInteractionsPosition")
+			.addValueList([["next to them", 1], ["at page bottom", 0]]);
+
+		options.addOption("Hide tabs in Slave Interact", "slaveInteractLongForm")
+			.addValue("Enabled", true).on().addValue("Disabled", false).off();
+
+		options.addOption("Line separations are", "lineSeparations")
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		el.append(options.render());
+
+		r = [];
+		r.push(`UI theme selector. Allows to select a single CSS file to be loaded.`);
+		r.push(App.UI.DOM.makeElement("span", `The file has to be located in the same directory as the HTML file otherwise it will simply not load at all.`, "red"));
+		r.push(App.UI.Theme.selector());
+		App.Events.addParagraph(el, r);
+
+		App.UI.DOM.appendNewElement("h2", el, "Sidebar");
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Cash is", "Cash", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("Upkeep is", "Upkeep", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("Sex slave count is", "SexSlaveCount", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("Room population is", "roomPop", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("GSP is", "GSP", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("Rep is", "Rep", V.sideBarOptions)
+			.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+		options.addOption("Confirmation before ending a week is", "confirmWeekEnd", V.sideBarOptions)
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Enabling this will open a dialog box to confirm you meant to end a week.");
+
+		if (V.secExpEnabled > 0) {
+			options.addOption("Authority is", "Authority", V.sideBarOptions)
+				.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+			options.addOption("Security is", "Security", V.sideBarOptions)
+				.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+			options.addOption("Crime is", "Crime", V.sideBarOptions)
+				.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+		}
+
+		el.append(options.render());
+
+
+		App.UI.DOM.appendNewElement("h2", el, "Images");
+		el.append(App.UI.artOptions());
+
+		return el;
+	}
+
+	function contentFlavor() {
+		const el = new DocumentFragment();
+		let r;
+		let options;
+
+		App.UI.DOM.appendNewElement("h2", el, "Content");
+
+		r = [];
+		r.push("More granular control of what appears is in");
+		r.push(App.UI.DOM.passageLink("Description Options", "Description Options"));
+		App.Events.addNode(el, r, "div", "note");
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("The difficulty setting is currently set to", "baseDifficulty")
+			.addValueList([["Very easy", 1], ["Easy", 2], ["Default", 3], ["Hard", 4], ["Very hard", 5]]);
+
+		options.addOption("Slaves falling ill is currently", "seeIllness")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Will not affect existing ill slaves already in-game.");
+
+		options.addOption("Extreme content like amputation is currently", "seeExtreme")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Will not affect extreme surgeries already applied in-game.");
+
+		options.addOption("Bestiality related content is currently", "seeBestiality")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Watersports related content is currently", "seePee")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Incest content is currently", "seeIncest")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Pregnancy related content is currently", "seePreg")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Will not affect existing pregnancies already in-game.");
+
+		options.addOption("Child gender to be generated based off dick content settings", "seeDicksAffectsPregnancy")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment(`${(V.seeDicksAffectsPregnancy === 1) ? `Currently ${V.seeDicks}% of children will be born male. ` : ``}Will not affect existing pregnancies already in-game.`);
+
+		if (V.seeDicksAffectsPregnancy === 0) {
+			options.addOption("XX slaves only father daughters", "adamPrinciple")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+				.addComment("Will not affect existing pregnancies already in-game.");
+		}
+
+		options.addOption("Extreme pregnancy content like broodmothers is currently", "seeHyperPreg")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Will not affect existing hyperpregnancies already in-game.");
+
+		options.addOption("Pregnancy complications due to multiples and body size are currently", "dangerousPregnancy")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption(`Precocious puberty (pregnancy younger than ${V.fertilityAge})`, "precociousPuberty")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Will not affect existing precocious puberty cases already in-game.");
+
+		options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping")
+			.addValue("Yes", 0).on().addValue("No", 1).off();
+
+		options.addOption("Disables the long term damage mechanic. //Temp option//", "disableLongDamage")
+			.addValue("Enabled", 0).on().addValue("Disabled", 1).off();
+
+		options.addOption("Experimental male pronouns are currently", "diversePronouns")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Apply Backwards Compatibility after changing to update slave's pronouns. Not all scenes support male pronouns and this is not yet incorporated into the lore or mechanics.");
+
+		options.addOption("Male slave names are currently", "allowMaleSlaveNames")
+			.addValue("Enabled", true).on().addValue("Disabled", false).off()
+			.addComment("This only affects slave generation and not your ability to name your slaves.");
+
+		options.addOption("Missing slave names are currently", "showMissingSlaves")
+			.addValue("Enabled", true).on().addValue("Disabled", false).off();
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, `Intersecting mechanics`);
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Slave assets affected by weight is", "weightAffectsAssets")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Diet will still affect asset size.");
+
+		options.addOption("Curative side effects are", "curativeSideEffects")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("If enabled, curatives have a chance to give slaves harmful side effects.");
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, `Flavour`);
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("Slave reactions to facility assignments are", "showAssignToScenes")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Post sex clean up", "postSexCleanUp")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Appraisal miniscenes on slave sale are", "showAppraisal")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Assignment performance vignettes on the end week report are", "showVignettes")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slaves can have alternate titles", "newDescriptions")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Family titles for relatives", "allowFamilyTitles")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Limit family growth", "limitFamilies")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Restricts acquisition of additional relatives, by means other than birth, for slaves with families.");
+
+		options.addOption("Distant relatives such as aunts, nieces and cousins are", "showDistantRelatives")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		el.append(options.render());
+		return el;
+	}
+
+	function mods() {
+		const el = new DocumentFragment();
+		let options;
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("The Special Force Mod is currently", "Toggle", V.SF)
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("<div>This mod is triggered after week 72. It is non-canon where it conflicts with canonical updates to the base game.</div>");
+
+		options.addOption("The Security Expansion mod is", "secExpEnabled")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("<div>The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.</div>");
+
+		el.append(options.render());
+
+		if (V.secExpEnabled > 0) {
+			if (Object.values(V.SecExp).length === 0) {
+				el.append(App.UI.DOM.renderPassage("SecExpBackwardCompatibility"));
+				Engine.play("Options");
+			}
+			App.UI.DOM.appendNewElement("h2", el, `Security Expansion mod options`);
+			if (V.terrain === "oceanic") {
+				App.UI.DOM.appendNewElement("div", el, `Oceanic arcologies are not by default subject to external attacks. You can however allow them to happen anyway. If you choose to do so please keep in mind that descriptions and mechanics are not intended for naval combat but land combat.`);
+			}
+			options = new App.UI.OptionsGroup();
+
+			if (V.SecExp.settings.battle.enabled > 0 || V.SecExp.settings.rebellion.enabled > 0) {
+				options.addOption("Detailed battle statistics are", "showStats", V.SecExp.settings)
+					.addValue("Shown", 1).on().addValue("Hidden", 0).off()
+					.addComment("Visibility of detailed statistics and battle turns.");
+
+				options.addOption("Difficulty is", "difficulty", V.SecExp.settings)
+					.addValueList([["Extremely hard", 2], ["Very hard", 1.5], ["Hard", 1.25], ["Normal", 1], ["Easy", 0.75], ["Very easy", 0.5]]);
+
+				options.addOption("Unit descriptions are", "unitDescriptions", V.SecExp.settings)
+					.addValue("Abbreviated", 1).addValue("Summarized", 0);
+			}
+
+			options.addOption("Battles are", "enabled", V.SecExp.settings.battle)
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+			options.addOption("Rebellions are", "enabled", V.SecExp.settings.rebellion)
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+			if (V.SecExp.settings.battle.enabled > 0) {
+				options.addOption("Battle frequency", "frequency", V.SecExp.settings.battle)
+					.addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]);
+			}
+
+			if (V.SecExp.settings.rebellion.enabled > 0) {
+				options.addOption("Rebellion buildup", "speed", V.SecExp.settings.rebellion)
+					.addValueList([["Extremely fast", 2], ["Very fast", 1.5], ["Fast", 1.25], ["Normal", 1], ["Slow", 0.75], ["Very slow", 0.5]]);
+			}
+
+
+			if (V.SecExp.settings.battle.enabled > 0) {
+				options.addOption("Commanders gain a prestige rank every 10 victories", "allowSlavePrestige", V.SecExp.settings.battle)
+					.addValue("Yes", 1).on().addValue("No", 0).off();
+			}
+
+			if (V.SecExp.settings.battle.enabled > 0) {
+				options.addOption("Force battles", "force", V.SecExp.settings.battle)
+					.addValue("Yes", 1).on().addValue("No", 0).off();
+			}
+			if (V.SecExp.settings.rebellion.enabled > 0) {
+				options.addOption("Force rebellions", "force", V.SecExp.settings.rebellion)
+					.addValue("Yes", 1).on().addValue("No", 0).off()
+					.addComment("Rebellions take precedence over Battles.");
+			}
+
+			if (V.SecExp.settings.battle.enabled > 0) {
+				options.addOption("Late game major battles are", "enabled", V.SecExp.settings.battle.major)
+					.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+			}
+
+			if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) {
+				options.addOption("Multiplier is", "mult", V.SecExp.settings.battle.major)
+					.addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]);
+
+				options.addOption("This week a major battle is", "force", V.SecExp.settings.battle.major)
+					.addValue("Guaranteed", 1).on().addValue("Not guaranteed", 0).off();
+			}
+
+			if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) {
+				options.addOption("Gameover on battle loss", "gameOver", V.SecExp.settings.battle.major)
+					.addValue("Yes", 1).on().addValue("No", 0).off();
+			}
+
+			if (V.SecExp.settings.rebellion.enabled > 0) {
+				options.addOption("Gameover on rebellion loss", "gameOver", V.SecExp.settings.rebellion)
+					.addValue("Yes", 1).on().addValue("No", 0).off();
+			}
+
+			el.append(options.render());
+
+			const subHeading = document.createElement("div");
+			subHeading.classList.add("subHeading");
+
+			if (V.debugMode || V.cheatMode || V.cheatModeM) { // TODO make me a fucking function
+				App.UI.DOM.appendNewElement("div", subHeading, "Debug/cheat", "bold");
+				let td;
+				let links;
+				const table = document.createElement("table");
+				table.classList.add("invisible");
+				el.append(table);
+
+				let tr = document.createElement("tr");
+				tr.style.textAlign = "center";
+
+				td = createTd();
+				links = [];
+				links.push(
+					App.UI.DOM.link(
+						"Set loyalty high",
+						() => {
+							changeLoyalty("high");
+						},
+						[],
+						"Options"
+					)
+				);
+				links.push(
+					App.UI.DOM.link(
+						"Set loyalty average",
+						() => {
+							changeLoyalty("average");
+						},
+						[],
+						"Options"
+					)
+				);
+				links.push(
+					App.UI.DOM.link(
+						"Set loyalty low",
+						() => {
+							changeLoyalty("low");
+						},
+						[],
+						"Options"
+					)
+				);
+				links.push(
+					App.UI.DOM.link(
+						"Randomize loyalty",
+						() => {
+							changeLoyalty("random");
+						},
+						[],
+						"Options"
+					)
+				);
+
+				td.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+				table.append(tr);
+
+				tr = document.createElement("tr");
+				tr.style.textAlign = "center";
+				td = createTd();
+				links = [];
+				links.push(App.UI.DOM.link(
+					"Give Authority",
+					() => {
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 1000, 0, 20000);
+					},
+					[],
+					"Options"
+				));
+				links.push(App.UI.DOM.link(
+					"Remove Authority",
+					() => {
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 1000, 0, 20000);
+					},
+					[],
+					"Options"
+				));
+				td.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+				table.append(tr);
+
+
+				tr = document.createElement("tr");
+				td = document.createElement("td");
+				td.style.textAlign = "right";
+				links = [];
+				links.push(App.UI.DOM.link(
+					"Raise security",
+					() => {
+						V.SecExp.core.security = Math.clamp(V.SecExp.core.security + 5, 0, 100);
+					},
+					[],
+					"Options"
+				));
+				links.push(App.UI.DOM.link(
+					"Lower security",
+					() => {
+						V.SecExp.core.security = Math.clamp(V.SecExp.core.security - 5, 0, 100);
+					},
+					[],
+					"Options"
+				));
+				tr.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+
+				td = document.createElement("td");
+				td.style.textAlign = "left";
+				links = [];
+				links.push(App.UI.DOM.link(
+					"Raise crime",
+					() => {
+						V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + 5, 0, 100);
+					},
+					[],
+					"Options"
+				));
+				links.push(App.UI.DOM.link(
+					"Lower crime",
+					() => {
+						V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow - 5, 0, 100);
+					},
+					[],
+					"Options"
+				));
+				tr.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+				table.append(tr);
+
+				tr = document.createElement("tr");
+				td = document.createElement("td");
+				td.style.textAlign = "right";
+				links = [];
+				links.push(App.UI.DOM.link(
+					"Give militia manpower",
+					() => {
+						V.militiaFreeManpower += 30;
+					},
+					[],
+					"Options"
+				));
+				links.push(App.UI.DOM.link(
+					"Remove militia manpower",
+					() => {
+						V.militiaFreeManpower = Math.max(V.militiaFreeManpower - 30, 0);
+					},
+					[],
+					"Options"
+				));
+				tr.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+
+				td = document.createElement("td");
+				td.style.textAlign = "left";
+				links = [];
+				links.push(App.UI.DOM.link(
+					"Give mercs manpower",
+					() => {
+						V.mercFreeManpower += 30;
+					},
+					[],
+					"Options"
+				));
+				links.push(App.UI.DOM.link(
+					"Remove mercs manpower",
+					() => {
+						V.mercFreeManpower = Math.max(V.mercFreeManpower - 30, 0);
+					},
+					[],
+					"Options"
+				));
+				tr.append(App.UI.DOM.generateLinksStrip(links));
+				tr.append(td);
+				table.append(tr);
+				subHeading.append(table);
+				el.append(subHeading);
+			}	/* closes cheatmode check */
+		} /* closes SecExp check*/
+		return el;
+
+		function createTd() {
+			const td = document.createElement("td");
+			td.style.columnSpan = "2";
+			return td;
+		}
+
+		/**
+		 *
+		 * @param {"high"|"average"|"low"|"random"} level
+		 */
+		function changeLoyalty(level) {
+			const numberMap = new Map([
+				["high", [80, 100]],
+				["average", [40, 60]],
+				["low", [20]],
+				["random", [100]],
+			]);
+
+			for (const unit of V.militiaUnits) {
+				unit.loyalty = numberGenerator();
+			}
+			for (const unit of V.slaveUnits) {
+				unit.loyalty = numberGenerator();
+			}
+			for (const unit of V.mercUnits) {
+				unit.loyalty = numberGenerator();
+			}
+
+			function numberGenerator() {
+				const range = numberMap.get(level);
+				if (range[1]) {
+					return random(range[0], range[1]);
+				} else {
+					return random(range[0]);
+				}
+			}
+		}
+	}
+
+	function debugCheating() {
+		const el = new DocumentFragment();
+		let options;
+		let option;
+		let links;
+		let r;
+		const popCap = menialPopCap();
+
+		App.UI.DOM.appendNewElement("h2", el, `Debug`);
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("DebugMode is", "debugMode")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("This will add a Display Variables and Bug Report passage to the sidebar.");
+
+		if (V.debugMode > 0) {
+			options.addOption("The custom function part of debug mode is", "debugModeCustomFunction")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		}
+
+		option = options.addOption("Genetics array")
+			.customButton("Run test", () => { }, "test genetics");
+		if (V.cheatMode === 1) {
+			option.customButton("Edit Genetics", () => { }, "Edit Genetics");
+		} else {
+			option.addComment("Enable cheat mode to edit genetics.");
+		}
+
+		options.addOption("Rules Assistant").customButton("Reset Rules", () => { initRules(); }, "Rules Assistant");
+
+		options.addOption("Passage Profiler is", "profiler")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("Outputs performance data at the bottom of every passage.");
+
+		el.append(options.render());
+
+		App.UI.DOM.appendNewElement("h2", el, `Cheating`);
+
+		options = new App.UI.OptionsGroup();
+
+		options.addOption("CheatMode is", "cheatMode")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("This will allow manual selection of events and unlock some options that would usually be restricted by progress.");
+
+		if (V.cheatMode === 0) {
+			el.append(options.render());
+		} else {
+			options.addOption("Sidebar Cheats are currently", "cheatModeM")
+				.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+			options.addOption("Slave aging", "seeAge")
+				.addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off();
+
+			el.append(options.render());
+
+			links = [];
+
+			links.push(
+				App.UI.DOM.link(
+					`Add ${commaNum(100000)} money`,
+					() => {
+						V.cheater = 1;
+						cashX(100000, "cheating");
+					},
+					[],
+					"Options"
+				)
+			);
+
+			links.push(
+				App.UI.DOM.link(
+					`Add ${commaNum(10000)} rep`,
+					() => {
+						V.cheater = 1;
+						repX(10000, "cheating");
+					},
+					[],
+					"Options"
+				)
+			);
+
+			r = [];
+			r.push(App.UI.DOM.generateLinksStrip(links));
+			r.push(App.UI.DOM.makeElement("span", "Cheating will be flagged in your save", "note"));
+			App.Events.addNode(el, r, "div", "scLink2");
+
+
+			SectorCounts();
+
+			links = [];
+			links.push(
+				App.UI.DOM.link(
+					"Raise prosperity cap",
+					() => {
+						V.AProsperityCapModified += 10;
+					},
+					[],
+					"Options"
+				)
+			);
+
+			links.push(
+				App.UI.DOM.link(
+					"Lower prosperity cap",
+					() => {
+						V.AProsperityCapModified -= 10;
+					},
+					[],
+					"Options"
+				)
+			);
+			App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2");
+
+
+			links = [];
+			links.push(
+				App.UI.DOM.link(
+					"Raise prosperity",
+					() => {
+						V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity + 10, 0, V.AProsperityCap);
+					},
+					[],
+					"Options"
+				)
+			);
+
+			links.push(
+				App.UI.DOM.link(
+					"Lower prosperity",
+					() => {
+						V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity - 10, 0, V.AProsperityCap);
+					},
+					[],
+					"Options"
+				)
+			);
+			App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2");
+
+			links = [];
+			links.push(
+				App.UI.DOM.link(
+					"Give menial slaves",
+					() => {
+						V.menials = Math.clamp(V.menials + 30, 0, popCap.value);
+					},
+					[],
+					"Options"
+				)
+			);
+
+			links.push(
+				App.UI.DOM.link(
+					"Remove menial slaves",
+					() => {
+						V.menials = Math.clamp(V.menials - 30, 0, popCap.value);
+					},
+					[],
+					"Options"
+				)
+			);
+			App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2");
+
+			links = [];
+
+			// Will no longer work as intended due to population changes
+			links.push(
+				App.UI.DOM.link(
+					"Add citizens",
+					() => {
+						V.lowerClass = Math.max(V.lowerClass + 200, 0);
+					},
+					[],
+					"Options"
+				)
+			);
+
+			// also no longer properly functional
+			links.push(
+				App.UI.DOM.link(
+					"Remove citizens",
+					() => {
+						V.lowerClass = Math.max(V.lowerClass - 200, 0);
+					},
+					[],
+					"Options"
+				)
+			);
+			App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2");
+
+			links = [];
+			// Will work to a limited degree, minimums and maximums for slaves are set through population
+			links.push(
+				App.UI.DOM.link(
+					"Add slaves",
+					() => {
+						V.NPCSlaves = Math.max(V.NPCSlaves + 200, 0);
+					},
+					[],
+					"Options"
+				)
+			);
+
+			// Will work to a limited degree
+			links.push(
+				App.UI.DOM.link(
+					"Remove slaves",
+					() => {
+						V.NPCSlaves = Math.max(V.NPCSlaves - 200, 0);
+					},
+					[],
+					"Options"
+				)
+			);
+			App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2");
+		}
+		return (el);
+	}
+
+	function experimental() {
+		const el = new DocumentFragment();
+		let options;
+		let r;
+
+		r = [];
+		r.push(`Experimental means just that: experimental. Options below are likely to be in an`);
+		r.push(App.UI.DOM.makeElement("span", `even more incomplete or broken state than usual.`, "yellow"));
+		r.push(App.UI.DOM.makeElement("span", `THEY MAY NOT WORK AT ALL.`, "red"));
+		r.push(`Make sure you back up your save before enabling any of these, and if you are that interested, consider helping to improve them.`);
+		App.Events.addNode(el, r, "div", "bold");
+
+		options = new App.UI.OptionsGroup();
+
+		if (V.seePreg !== 0) {
+			options.addOption("Nursery is", "nursery", V.experimental)
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+				.addComment("This will enable the experimental nursery, which allows players to interact with growing slave children. An alternative to the incubator.");
+		}
+
+		options.addOption("Food is", "food", V.experimental)
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("This will enable the experimental food supply and demand system, as well as a new farmyard building and assignments.");
+
+		if (V.seeExtreme === 1 && V.seeBestiality === 1) {
+			options.addOption("Animal Ovaries are", "animalOvaries", V.experimental)
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+				.addComment("This will allow slaves to be impregnated by animals.");
+		}
+
+		if (V.seeExtreme === 1) {
+			options.addOption("Dinner party", "dinnerParty", V.experimental)
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+				.addComment("This will enable a controversial but very broken event. Warning: Snuff, cannibalism.");
+		}
+
+		options.addOption("New event", "tempEventToggle")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		el.append(options.render());
+		return el;
+	}
+};
+
+App.UI.artOptions = function() {
+	const el = new DocumentFragment();
+	let options = new App.UI.OptionsGroup();
+
+	if (V.seeImages > 0) {
+		App.Events.drawEventArt(el, BaseSlave());
+	}
+
+	options.addOption("Images are", "seeImages")
+		.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+	if (V.seeImages > 0) {
+		options.addOption("Image style is", "imageChoice")
+			.addValueList([["Revamped embedded vector art", 3], ["Non-embedded vector art", 2], ["NoX/Deepmurk's vector art", 1], ["Shokushu's rendered imagepack", 0]]);
+
+		if (V.imageChoice === 1) {
+			options.addComment('<span class="warning">Git compiled only, no exceptions.</span>');
+
+			options.addOption("Face artwork is", "seeFaces")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+			options.addOption("Highlights on shiny clothing are", "seeVectorArtHighlights")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+			options.addOption("Height scaling", "seeHeight")
+				.addValue("All images", 2).on().addValue("Small images", 1).neutral().addValue("Disabled", 0).off();
+
+			options.addOption("Clothing erection bulges are", "showClothingErection")
+				.addValue("Enabled", true).on().addValue("Disabled", false).off();
+		} else if (V.imageChoice === 0) {
+			options.addComment(`You need """to"""
+				<a href="https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0">download the image pack</a>
+				"""and""" put the 'renders' folder into the resources/ folder where this html file is.`
+			);
+
+			options.addOption("Slave summary fetish images are", "seeMainFetishes")
+				.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+		} else if (V.imageChoice === 3) {
+			options.addComment('<span class="warning">Git compiled only, no exceptions.</span>');
+
+			options.addOption("Clothing erection bulges are", "showClothingErection")
+				.addValue("Enabled", true).on().addValue("Disabled", false).off();
+		}
+
+		options.addOption("PA avatar art is", "seeAvatar")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slave images in lists are", "seeSummaryImages")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+
+		options.addOption("Slave images in the weekly report are", "seeReportImages")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off();
+	}
+	el.append(options.render());
+	return el;
+};
diff --git a/src/gui/options/summaryOptions.js b/src/gui/options/summaryOptions.js
new file mode 100644
index 0000000000000000000000000000000000000000..969394b0fa1f8620a1a6837a230899588624dd20
--- /dev/null
+++ b/src/gui/options/summaryOptions.js
@@ -0,0 +1,105 @@
+App.UI.summaryOptions = function() {
+	const el = new DocumentFragment();
+	let options;
+
+	App.UI.DOM.appendNewElement("h1", el, "Summary Options");
+
+	App.UI.DOM.appendNewElement("p", el, `These options will affect the short slave summaries that appear on the main menu and the facility management screens.`, "scene-intro");
+
+	App.UI.DOM.appendNewElement("h2", el, "Main menu features");
+
+	options = new App.UI.OptionsGroup();
+
+	options.addOption("Rules Assistant visibility", "rulesAssistantMain")
+		.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+	options.addOption("Facilities in the sidebar are", "abbreviateSidebar")
+		.addValueList([["Summarized", 2], ["Abbreviated", 1]]);
+
+	options.addOption("Sorting main menu options are", "sortSlavesMain")
+		.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+	if (V.sortSlavesMain > 0) {
+		options.addOption("Sorting direction", "sortSlavesOrder")
+			.addValueList([["Ascending", "ascending"], ["Descending", "descending"]]);
+
+		options.addOption("Slaves are sorted by", "sortSlavesBy")
+			.addValueList([
+				["Devotion", "devotion"],
+				["Name", "name"],
+				["Date purchased", "seniority"],
+				["Age", "actualAge"],
+				["How old they look", "visualAge"],
+				["Age of their body", "physicalAge"],
+				["Assignment", "assignment"],
+				["Weekly Income", "weeklyIncome"]
+			]);
+	}
+
+	el.append(options.render());
+
+	App.UI.DOM.appendNewElement("h2", el, "Individual panels");
+	App.UI.DOM.appendNewElement("div", el, "Sample summary:");
+	el.append(App.UI.SlaveList.render([V.slaves.random().ID], new Array(), App.UI.SlaveList.SlaveInteract.stdInteract));
+
+	options = (new App.UI.OptionsGroup()).enableDoubleColumn();
+
+	options.addOption("Panel style is", "slavePanelStyle")
+		.addValueList([
+			["None", 0],
+			["Line Separator", 1],
+			["Card", 2]
+		]);
+
+	App.UI.SlaveSummary.addOptions(options);
+
+	options.addOption("Granular slave stat numbers are", "summaryStats")
+		.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+	options.addOption("Main menu assignment shortcuts are", "displayAssignments")
+		.addValue("Shown", 1).on().addValue("Hidden", 0).off();
+
+	if (V.showMissingSlaves) {
+		options.addOption("Missing slave parents are", "showMissingSlavesSD")
+			.addValue("Shown", true).on().addValue("Hidden", false).off();
+	}
+
+	options.addOption("FC Dev's preferred options")
+		.customButton(
+			"Apply",
+			() => {
+				V.seeDesk = 0;
+				V.seeFCNN = 0;
+				V.sortSlavesBy = "devotion";
+				V.sortSlavesOrder = "descending";
+				V.sortSlavesMain = 0;
+				V.rulesAssistantMain = 1;
+				Object.assign(
+					V.UI.slaveSummary.abbreviation,
+					{
+						devotion: 1,
+						mental: 1,
+						rules: 1,
+						health: 1,
+						diet: 1,
+						drugs: 1,
+						hormoneBalance: 1,
+						race: 1,
+						genitalia: 1,
+						physicals: 1,
+						skills: 1,
+						nationality: 1,
+						rulesets: 1,
+						clothes: 0,
+						origins: 0
+					}
+				);
+				V.abbreviateSidebar = 1;
+			},
+			"Summary Options"
+		);
+
+	el.append(options.render());
+
+	return el;
+};
diff --git a/src/gui/options/summaryOptions.tw b/src/gui/options/summaryOptions.tw
index 6b73edd5b495bceb5d7eb8e488d3e4125666ecf0..a5fcb72e82afd398b58281ee7dc2ec44b94d0462 100644
--- a/src/gui/options/summaryOptions.tw
+++ b/src/gui/options/summaryOptions.tw
@@ -11,60 +11,4 @@
 <<set $nextLink = $storedLink>>
 <<set $passageSwitchHandler = App.EventHandlers.optionsChanged>>
 
-<h1>Summary Options</h1>
-
-<p class="scene-intro">
-	These options will affect the short slave summaries that appear on the main menu and the facility management screens.
-</p>
-
-<h2>Main menu features</h2>
-
-<<set _options = new App.UI.OptionsGroup()>>
-
-<<run _options.addOption("Rules Assistant visibility", "rulesAssistantMain")
-.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-<<run _options.addOption("Facilities in the sidebar are", "abbreviateSidebar")
-.addValueList([["Summarized", 2], ["Abbreviated", 1]])>>
-
-<<run _options.addOption("Sorting main menu options are", "sortSlavesMain")
-.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-<<if $sortSlavesMain > 0>>
-	<<run _options.addOption("Sorting direction", "sortSlavesOrder")
-	.addValueList([["Ascending", "ascending"], ["Descending", "descending"]])>>
-
-	<<run _options.addOption("Slaves are sorted by", "sortSlavesBy")
-	.addValueList([["Devotion", "devotion"], ["Name", "name"], ["Date purchased", "seniority"], ["Age", "actualAge"],
-	["How old they look", "visualAge"], ["Age of their body", "physicalAge"], ["Assignment", "assignment"], ["Weekly Income", "weeklyIncome"]])>>
-<</if>>
-
-<<includeDOM _options.render()>>
-
-<h2>Individual panels</h2>
-Sample summary:
-<<includeDOM App.UI.SlaveList.render([$slaves.random().ID], new Array(), App.UI.SlaveList.SlaveInteract.stdInteract)>>
-
-<<set _options = (new App.UI.OptionsGroup()).enableDoubleColumn()>>
-
-<<run _options.addOption("Panel style is", "slavePanelStyle")
-.addValueList([["None", 0], ["Line Separator", 1], ["Card", 2]])>>
-
-<<run App.UI.SlaveSummary.addOptions(_options)>>
-
-<<run _options.addOption("Granular slave stat numbers are", "summaryStats")
-.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-<<run _options.addOption("Main menu assignment shortcuts are", "displayAssignments")
-.addValue("Shown", 1).on().addValue("Hidden", 0).off()>>
-
-<<if $showMissingSlaves>>
-	<<run _options.addOption("Missing slave parents are", "showMissingSlavesSD")
-	.addValue("Shown", true).on().addValue("Hidden", false).off()>>
-<</if>>
-
-<<includeDOM _options.render()>>
-
-<p style="font-style:italic">
-	[[FC Dev's preferred options|Summary Options ][$seeDesk = 0, $seeFCNN = 0, $sortSlavesBy = "devotion",$sortSlavesOrder = "descending",$sortSlavesMain = 0,$rulesAssistantMain = 1, $UI.slaveSummary.abbreviation = {devotion: 1, mental: 1, rules: 1, clothes: 2, health: 1, diet: 1, drugs: 1, hormoneBalance: 1, race: 1, genitalia: 1, physicals: 1, skills: 1, nationality: 1, rulesets: 1, clothes: 0, origins: 0} ,$abbreviateSidebar = 1]]
-</p>
+<<includeDOM App.UI.summaryOptions()>>
\ No newline at end of file
diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js
index e265483613cdf5f3ade5447aeab1663551ebd113..c7a5dbb9347e69d586d7466daa550ebb0a43e461 100644
--- a/src/gui/quicklinks.js
+++ b/src/gui/quicklinks.js
@@ -26,45 +26,45 @@ App.UI.quickMenu = (function() {
 			"Manage Arcology": true,
 			"Manage Personal Affairs": true,
 			"Manage Corporation": true,
-			"Personal assistant options":true,
-			Firebase: true,
-			propagandaHub: true,
-			securityHQ: true,
-			secBarracks: true,
-			riotControlCenter: true,
+			"Personal assistant options": true,
+			"Firebase": true,
+			"propagandaHub": true,
+			"securityHQ": true,
+			"secBarracks": true,
+			"riotControlCenter": true,
 		},
 		Social: {
 			"Future Society": true,
-			Policies: true,
-			edicts: true,
+			"Policies": true,
+			"edicts": true,
 			"Universal Rules": true,
 			"Neighbor Interact": true,
 		},
 		Facilities: {
 			"Head Girl Suite": true,
 			"BG Select": true,
-			Brothel: true,
-			Club: true,
-			Arcade: true,
-			Dairy: true,
-			Farmyard: true,
+			"Brothel": true,
+			"Club": true,
+			"Arcade": true,
+			"Dairy": true,
+			"Farmyard": true,
 			"Servants' Quarters": true,
 			"Master Suite": true,
-			Schoolroom: true,
-			Spa: true,
-			Nursery: true,
-			Clinic: true,
-			Cellblock: true,
-			Incubator: true,
-			Pit: true,
+			"Schoolroom": true,
+			"Spa": true,
+			"Nursery": true,
+			"Clinic": true,
+			"Cellblock": true,
+			"Incubator": true,
+			"Pit": true,
 		},
 		Locations: {
 			"Gene Lab": true,
-			Dispensary: true,
+			"Dispensary": true,
 			"Organ Farm": true,
 			"Implant Manufactory": true,
 			"Prosthetic Lab": true,
-			Wardrobe: true,
+			"Wardrobe": true,
 			"The Black Market": true,
 		},
 		Tools: {
@@ -74,7 +74,7 @@ App.UI.quickMenu = (function() {
 			"Personal Attention Select": true,
 		},
 		Options: {
-			Options: true,
+			"Options": true,
 			"Summary Options": true,
 			"Description Options": true,
 			"Hotkey Settings": true,
@@ -83,99 +83,99 @@ App.UI.quickMenu = (function() {
 
 	// true means hidden
 	const hiddenPassages = cleanPassageMapping({
-		Arcade: () => !V.arcade,
+		"Arcade": () => !V.arcade,
 		"BG Select": () => V.dojo <= 1,
-		Brothel: () => !V.brothel,
-		Cellblock: () => !V.cellblock,
-		Clinic: () => !V.clinic,
-		Club: () => !V.club,
-		Dairy: () => !V.dairy,
-		Dispensary: () => !V.dispensary,
-		edicts: () => V.secExpEnabled <= 0,
-		Farmyard: () => !V.farmyard,
-		Firebase: () => !V.SF.Toggle || V.SF.Toggle < 1 || V.SF.Active < 1,
+		"Brothel": () => !V.brothel,
+		"Cellblock": () => !V.cellblock,
+		"Clinic": () => !V.clinic,
+		"Club": () => !V.club,
+		"Dairy": () => !V.dairy,
+		"Dispensary": () => !V.dispensary,
+		"edicts": () => V.secExpEnabled <= 0,
+		"Farmyard": () => !V.farmyard,
+		"Firebase": () => !V.SF.Toggle || V.SF.Toggle < 1 || V.SF.Active < 1,
 		"Future Society": () => !V.FSAnnounced,
 		"Gene Lab": () => !V.geneticMappingUpgrade,
 		"Head Girl Suite": () => !V.HGSuite,
 		"Implant Manufactory": () => !V.ImplantProductionUpgrade,
-		Incubator: () => !V.incubator,
+		"Incubator": () => !V.incubator,
 		"Manage Corporation": () => V.corp.Announced !== 1,
 		"Master Suite": () => !V.masterSuite,
 		"Neighbor Interact": () => V.arcologies.length === 0,
-		Nursery: () => !V.nursery,
+		"Nursery": () => !V.nursery,
 		"Organ Farm": () => !V.organFarmUpgrade,
-		Pit: () => !V.pit,
-		propagandaHub: () => V.secExpEnabled === 0 || !V.SecExp.buildings.propHub,
+		"Pit": () => !V.pit,
+		"propagandaHub": () => V.secExpEnabled === 0 || !V.SecExp.buildings.propHub,
 		"Prosthetic Lab": () => V.researchLab.level === 0,
-		riotControlCenter: () => V.secExpEnabled === 0 || !V.SecExp.buildings.riotCenter,
-		Schoolroom: () => !V.schoolroom,
-		secBarracks: () => V.secExpEnabled === 0 || !V.SecExp.buildings.barracks,
-		securityHQ: () => V.secExpEnabled === 0 || !V.SecExp.buildings.secHub,
+		"riotControlCenter": () => V.secExpEnabled === 0 || !V.SecExp.buildings.riotCenter,
+		"Schoolroom": () => !V.schoolroom,
+		"secBarracks": () => V.secExpEnabled === 0 || !V.SecExp.buildings.barracks,
+		"securityHQ": () => V.secExpEnabled === 0 || !V.SecExp.buildings.secHub,
 		"Servants' Quarters": () => !V.servantsQuarters,
-		Spa: () => !V.spa,
+		"Spa": () => !V.spa,
 		"The Black Market": () => V.rep < 10000,
 		"weaponsManufacturing": () => true,
 	});
 
 	// show different names than the actual passage name, can be a function
 	const uiNames = cleanPassageMapping({
-		Arcade: () => App.Entity.facilities.arcade.UIName,
+		"Arcade": () => App.Entity.facilities.arcade.UIName,
 		"BG Select": "Armory",
-		Brothel: () => App.Entity.facilities.brothel.UIName,
-		Cellblock: () => App.Entity.facilities.cellblock.UIName,
-		Clinic: () => App.Entity.facilities.clinic.UIName,
-		Club: () => App.Entity.facilities.club.UIName,
-		Dairy: () => App.Entity.facilities.dairy.UIName,
-		Dispensary: "Pharmaceutical Fabricator",
-		edicts: "Edicts",
-		Farmyard: () => App.Entity.facilities.farmyard.UIName,
+		"Brothel": () => App.Entity.facilities.brothel.UIName,
+		"Cellblock": () => App.Entity.facilities.cellblock.UIName,
+		"Clinic": () => App.Entity.facilities.clinic.UIName,
+		"Club": () => App.Entity.facilities.club.UIName,
+		"Dairy": () => App.Entity.facilities.dairy.UIName,
+		"Dispensary": "Pharmaceutical Fabricator",
+		"edicts": "Edicts",
+		"Farmyard": () => App.Entity.facilities.farmyard.UIName,
 		"Find Slave": "Locate Slave",
-		Firebase: () => `${capFirstChar(V.SF.Lower || "No one")}'s Firebase`,
+		"Firebase": () => `${capFirstChar(V.SF.Lower || "No one")}'s Firebase`,
 		"Future Society": "Future Societies",
 		"Head Girl Suite": () => App.Entity.facilities.headGirlSuite.UIName,
-		Incubator: () => App.Entity.facilities.incubator.UIName,
+		"Incubator": () => App.Entity.facilities.incubator.UIName,
 		"Master Suite": () => App.Entity.facilities.masterSuite.UIName,
 		"Neighbor Interact": "Diplomacy",
-		Nursery: () => App.Entity.facilities.nursery.UIName,
-		Options: "Game Options",
+		"Nursery": () => App.Entity.facilities.nursery.UIName,
+		"Options": "Game Options",
 		"Personal assistant options": "Personal Assistant",
-		"Personal Attention Select":"Personal Attention",
-		Pit: () => App.Entity.facilities.pit.UIName,
-		propagandaHub: "Manage PR",
-		riotControlCenter: "Manage Rebels",
-		Schoolroom: () => App.Entity.facilities.schoolroom.UIName,
-		secBarracks: "Manage Military",
-		securityHQ: "Manage Security",
+		"Personal Attention Select": "Personal Attention",
+		"Pit": () => App.Entity.facilities.pit.UIName,
+		"propagandaHub": "Manage PR",
+		"riotControlCenter": "Manage Rebels",
+		"Schoolroom": () => App.Entity.facilities.schoolroom.UIName,
+		"secBarracks": "Manage Military",
+		"securityHQ": "Manage Security",
 		"Servants' Quarters": () => App.Entity.facilities.servantsQuarters.UIName,
-		Spa: () => App.Entity.facilities.spa.UIName,
+		"Spa": () => App.Entity.facilities.spa.UIName,
 		"The Black Market": "Black Market",
 	});
 
 	// extra information behind the link, is a function
 	const f = App.Entity.facilities;
 	const extraInfo = cleanPassageMapping({
-		Arcade: () => occupancy(f.arcade),
+		"Arcade": () => occupancy(f.arcade),
 		"BG Select": () => occupancy(f.armory),
-		Brothel: () => occupancy(f.brothel),
-		Cellblock: () => occupancy(f.cellblock),
-		Clinic: () => occupancy(f.clinic),
-		Club: () => occupancy(f.club),
-		Dairy: () => occupancy(f.dairy),
-		Farmyard: () => occupancy(f.farmyard),
+		"Brothel": () => occupancy(f.brothel),
+		"Cellblock": () => occupancy(f.cellblock),
+		"Clinic": () => occupancy(f.clinic),
+		"Club": () => occupancy(f.club),
+		"Dairy": () => occupancy(f.dairy),
+		"Farmyard": () => occupancy(f.farmyard),
 		"Head Girl Suite": () => occupancy(f.headGirlSuite),
-		Incubator: () => occupancy(f.incubator),
+		"Incubator": () => occupancy(f.incubator),
 		"Master Suite": () => occupancy(f.masterSuite),
-		Nursery: () => occupancy(f.nursery),
-		Pit: () => occupancy(f.pit),
-		Schoolroom: () => occupancy(f.schoolroom),
+		"Nursery": () => occupancy(f.nursery),
+		"Pit": () => occupancy(f.pit),
+		"Schoolroom": () => occupancy(f.schoolroom),
 		"Servants' Quarters": () => occupancy(f.servantsQuarters),
-		Spa: () => occupancy(f.spa),
+		"Spa": () => occupancy(f.spa),
 	});
 
 	// true shows a notification symbol
 	const notifications = cleanPassageMapping({
 		"Future Society": () => FutureSocieties.availCredits() > 0 || V.FSReminder,
-		Incubator: () => V.readySlaves > 0,
+		"Incubator": () => V.readySlaves > 0,
 		"Manage Corporation": () => V.corp.SpecToken > 0 && V.corp.SpecTimer === 0,
 	});
 
@@ -200,41 +200,41 @@ App.UI.quickMenu = (function() {
 		const hotkeys = cleanPassageMapping({
 			"BG Select": "b",
 			"Buy Slaves": "s",
-			edicts: "shift+e",
-			Firebase: "z",
+			"edicts": "shift+e",
+			"Firebase": "z",
 			"Future Society": "f",
-			Main: "m",
+			"Main": "m",
 			"Manage Arcology": "c",
 			"Manage Corporation": "shift+c",
 			"Manage Penthouse": "p",
 			"Manage Personal Affairs": "x",
 			"Neighbor Interact": "d",
-			Options: "o",
+			"Options": "o",
 			"Personal assistant options": "t",
 			"Personal Attention Select": "a",
-			Policies: "y",
-			propagandaHub: "shift+h",
+			"Policies": "y",
+			"propagandaHub": "shift+h",
 			"Recruiter Select": "u",
-			riotControlCenter: "shift+r",
+			"riotControlCenter": "shift+r",
 			"Rules Assistant": "r",
-			secBarracks: "shift+a",
-			securityHQ: "shift+s",
+			"secBarracks": "shift+a",
+			"securityHQ": "shift+s",
 			"Universal Rules": "v",
 			// Facilities
-			Brothel: "1",
-			Club: "2",
-			Arcade: "3",
-			Dairy: "4",
-			Farmyard: "5",
+			"Brothel": "1",
+			"Club": "2",
+			"Arcade": "3",
+			"Dairy": "4",
+			"Farmyard": "5",
 			"Servants' Quarters": "6",
 			"Master Suite": "7",
-			Schoolroom: "8",
-			Spa: "9",
-			Nursery: "0",
-			Clinic: "shift+1",
-			Cellblock: "shift+2",
-			Incubator: "shift+3",
-			Pit: "shift+4",
+			"Schoolroom": "8",
+			"Spa": "9",
+			"Nursery": "0",
+			"Clinic": "shift+1",
+			"Cellblock": "shift+2",
+			"Incubator": "shift+3",
+			"Pit": "shift+4",
 		});
 
 		// register
diff --git a/src/gui/tooltips.js b/src/gui/tooltips.js
index 86079833941412672219a2054ce4afbd31db509c..dcb1e897e1876e4ada667f32f9722d31a572990f 100644
--- a/src/gui/tooltips.js
+++ b/src/gui/tooltips.js
@@ -10,7 +10,9 @@ App.UI.updateSidebarTooltips = (function() {
 
 		flaw: "Flaws impend your slaves performance. Try removing or converting them into quirks.",
 		intelligent: "More intelligent slaves tend to perform better.",
+		stupid: "More intelligent slaves tend to perform better.",
 		health: "The healthier your slaves, the better they perform.",
+		libido: "Sex drive has various effects, generally higher is good.",
 		positive: "This is good.",
 		// noteworthy: "This is important.",
 		warning: "This is very bad. Try removing the cause for this.",
@@ -20,13 +22,13 @@ App.UI.updateSidebarTooltips = (function() {
 		cash: "Money. Always useful.",
 		reputation: "Your reputation as a slaveowner. The more, the better.",
 		/*
+		red: "This is red, it's bad, if indicating a stat change it could be about money, reputation, mindbreak or flaw gain.",
 		skill: "t",
 		fetish: "t",
 		relationship: "t",
 		change: "t",
 		virginity: "t",
 		pregnant: "t",
-		stupid: "t",
 		education: "t",
 		*/
 	};
diff --git a/src/init/storyInit.tw b/src/init/storyInit.tw
index 4f9e2482233c86e912ffd48c4d04fa1d5dfc6f1f..2aad4145719a1fd06fff7c34499655d220ee14c2 100644
--- a/src/init/storyInit.tw
+++ b/src/init/storyInit.tw
@@ -50,7 +50,7 @@ You should have received a copy of the GNU General Public License along with thi
 
 <<set $foodConsumption = (($lowerClass*$foodRate.lower) + ($middleClass*$foodRate.middle) + ($upperClass*$foodRate.upper) + ($topClass*$foodRate.top))>>	/* total amount food consumed per week */
 
-<<set $weatherToday = App.Data.weather.hotnice.random()>>
+<<set $weatherToday = App.Data.Weather.hotNice.random()>>
 <<set $weatherLastWeek = 1>>
 <<set $weatherType = 1>>
 <<set $weatherRemaining = 6>>
diff --git a/src/interaction/fSlaveFeed.js b/src/interaction/fSlaveFeed.js
new file mode 100644
index 0000000000000000000000000000000000000000..8badbbd789cc31162109b9e58e85ae3f4b489a0a
--- /dev/null
+++ b/src/interaction/fSlaveFeed.js
@@ -0,0 +1,2072 @@
+globalThis.FSlaveFeed = function(slave, milkTap) {
+	const el = new DocumentFragment();
+	let _pregDiscovery = 0;
+	const {
+		His, He,
+		his, he, him, himself, wife, girl, hers
+	} = getPronouns(slave);
+	const {
+		His2, He2,
+		his2, he2, him2, himself2, wife2
+	} = getPronouns(milkTap).appendSuffix("2");
+	let _incestGive;
+	let _incestTake;
+	let r = [];
+	const relative = relativeTerm(slave, milkTap);
+
+	App.Events.drawEventArt(el, [slave, milkTap]);
+
+	if (slave.inflationType === "milk") {
+		slave.milkSource = milkTap.ID;
+		if (milkTap.behavioralQuirk === "sinful" || milkTap.sexualQuirk === "perverted" || milkTap.fetish === "incest") { // incest is a planned fetish, don't touch it!
+			_incestGive = 1;
+		}
+		if (slave.behavioralQuirk === "sinful" || slave.sexualQuirk === "perverted" || slave.fetish === "incest") {
+			_incestTake = 1;
+		}
+
+		r.push(`The first necessary step is to prepare the milk cow and ${his2} udders.`);
+
+		if (milkTap.fuckdoll > 0) {
+			r.push(`This is hilariously easy, as you have complete control over how ${milkTap.slaveName} is posed.`);
+		} else if (milkTap.fetish === "mindbroken") {
+			r.push(`This is very easy, as ${milkTap.slaveName} blankly follows your every will. Combined with ${his2} instinct to relieve the pressure in ${his2} breasts, ${he2} is simple to position.`);
+		} else if (milkTap.rivalryTarget === slave.ID) {
+			r.push(`This is rather easy, as ${milkTap.slaveName} wants to`);
+			if (canSee(milkTap)) {
+				r.push(`see`);
+			} else {
+				r.push(`feel`);
+			}
+			r.push(`${slave.slaveName}'s belly swell painfully as ${he} is force-fed ${his2} milk.`);
+			if (milkTap.lactation > 1) {
+				r.push(`${He} is practically gushing milk with excitement.`);
+			} else {
+				r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+			}
+		} else if (milkTap.relationshipTarget === slave.ID) {
+			r.push(`This is rather easy, as ${milkTap.slaveName}`);
+			if (milkTap.relationship === 1) {
+				r.push(`wants ${his2} friend to try ${his2} milk, fresh from the source.`);
+			} else if (milkTap.relationship === 2) {
+				r.push(`wants ${his2} best friend to try ${his2} milk, fresh from the source.`);
+			} else if (milkTap.relationship === 3) {
+				r.push(`can't wait to have some fun with ${his2} friend with benefits.`);
+			} else if (milkTap.relationship === 4) {
+				r.push(`enjoys spending intimate time with ${his2} lover, and having ${his2} breasts suckled is one of ${his2} favorites.`);
+			} else if (milkTap.relationship === 5) {
+				r.push(`enjoys spending intimate time with ${his2} ${wife}, and having ${his2} breasts suckled is one of ${his2} favorites.`);
+			}
+			if (milkTap.lactation > 1) {
+				r.push(`${He2} is practically gushing milk with excitement.`);
+			} else {
+				r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+			}
+		} else if ((milkTap.fetish === "boobs") && (milkTap.fetishKnown === 1) && (milkTap.fetishStrength > 60) && (milkTap.devotion >= -20)) {
+			r.push(`This is very easy, since ${milkTap.slaveName} loves ${his2} tits played with and can't wait to get suckled.`);
+			if (milkTap.lactation > 1) {
+				r.push(`${He2} is practically gushing milk with excitement.`);
+			} else {
+				r.push(`It takes next to no effort to get ${his2} milk flowing.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} fondly remembers nursing ${his2} ${relativeTerm(milkTap, slave)}.`);
+			} else {
+				r.push(`This is fairly easy, as ${milkTap.slaveName}'s body remembers nursing ${his2} ${relativeTerm(milkTap, slave)}, even if ${he} is resistant to the idea.`);
+			}
+			if (milkTap.lactation > 1) {
+				r.push(`${He2} is practically gushing milk with nostalgia.`);
+			} else {
+				r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+			}
+		} else if (slave.father === milkTap.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} is aroused at the thought of breast feeding the ${girl} ${he2} sired.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${He2} is practically gushing milk with excitement.`);
+				} else {
+					r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+				}
+			} else {
+				r.push(`This is extremely tough, as ${milkTap.slaveName} is very uncomfortable breast feeding the ${girl} ${he2} sired.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${His2} excessive milk production quickly leaves ${him2} eager for release, however.`);
+				} else {
+					r.push(`It takes some coaxing and kneading to get ${his2} milk flowing and ${him2} eager for relief.`);
+				}
+			}
+		} else if (milkTap.mother === slave.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} is aroused at the lewdity of breast feeding ${his2} own mother.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${He2} is practically gushing milk with excitement.`);
+				} else {
+					r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+				}
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} finds it awkward to nurse ${his2} own mother.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${His2} excessive milk production quickly leaves ${him2} eager for release.`);
+				} else {
+					r.push(`It takes some coaxing and kneading to get ${his2} milk flowing and ${him2} eager for relief.`);
+				}
+			}
+		} else if (milkTap.father === slave.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as having ${his2} father drink ${his2} milk is a taboo ${milkTap.slaveName} can't wait to break.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${He2} is practically gushing milk with excitement.`);
+				} else {
+					r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+				}
+			} else {
+				r.push(`This is very tough, as ${milkTap.slaveName} finds it weird to let ${his2} father suckle from ${him2}.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${His2} excessive milk production quickly leaves ${him2} eager for release, however.`);
+				} else {
+					r.push(`It takes some coaxing and kneading to get ${his2} milk flowing and ${him2} eager for relief.`);
+				}
+			}
+		} else if (areSisters(slave, milkTap) === 1) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as having ${milkTap.slaveName} enjoys sexually experimenting with ${his2} ${relative}.`);
+			} else {
+				r.push(`This is easy enough, as ${milkTap.slaveName} wants ${his2} ${relative} to try ${his2} milk, but only if ${he} can taste ${hers} too.`);
+			}
+			if (milkTap.lactation > 1) {
+				r.push(`${He2} is practically gushing milk with excitement.`);
+			} else {
+				r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+			}
+		} else if (areSisters(slave, milkTap) === 2) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as having ${milkTap.slaveName} enjoys sexually experimenting with ${his2} ${relative}.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${He2} is practically gushing milk with excitement.`);
+				} else {
+					r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+				}
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} is uncomfortable getting so intimate with ${his2} ${relativeTerm(milkTap, slave)}.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${His2} excessive milk production quickly leaves ${him2} eager for release.`);
+				} else {
+					r.push(`It takes some coaxing and kneading to get ${his2} milk flowing and ${him2} eager for relief.`);
+				}
+			}
+		} else if (areSisters(slave, milkTap) === 3) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as having ${milkTap.slaveName} enjoys sexually experimenting with ${his2} ${relative}.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${He2} is practically gushing milk with excitement.`);
+				} else {
+					r.push(`It takes minimal effort to get ${his2} milk flowing.`);
+				}
+			} else {
+				r.push(`This is slightly difficult, as ${milkTap.slaveName} is uncomfortable getting so intimate with ${his2} ${relativeTerm(milkTap, slave)}.`);
+				if (milkTap.lactation > 1) {
+					r.push(`${His2} excessive milk production quickly leaves ${him2} eager for release.`);
+				} else {
+					r.push(`It takes some coaxing and kneading to get ${his2} milk flowing and ${him2} eager for relief.`);
+				}
+			}
+		} else if ((milkTap.lactation > 1) && (milkTap.devotion >= -20)) {
+			r.push(`Since ${milkTap.slaveName} produces so much milk, ${he2} eagerly accepts any source of relief ${he2} can manage.`);
+		} else if ((milkTap.devotion > 50)) {
+			r.push(`Since ${milkTap.slaveName} is devoted to you, ${he2}'ll allow anyone you want to drink deeply from ${him2}.`);
+		} else if ((milkTap.devotion > 20)) {
+			r.push(`Since ${milkTap.slaveName} is obedient, ${he2} appreciates any relief from ${his2} swollen breasts.`);
+		} else if ((milkTap.devotion >= -20)) {
+			r.push(`Since ${milkTap.slaveName} does not resist your will, ${he2} should comply reasonably well. If anything, ${he}'ll at least be thankful to be relieved of some pressure.`);
+		} else {
+			r.push(`Since ${milkTap.slaveName} is unlikely to comply willingly, you simply restrain ${him2} with ${his2} tits exposed and ready to be drank from.`);
+			if (milkTap.lactation > 1) {
+				r.push(`You affix nipple clamps to ${his2} ${milkTap.nipples} nipples and step back to watch ${his2} breasts back up with milk. When ${he2} is unclamped, the flow should certainly be strong enough for your desires.`);
+			} else {
+				r.push(`You make sure to roughly coax ${his2} milk into flowing, all the while reminding ${him2} that ${he2} is nothing more than a cow now.`);
+			}
+		}
+
+		App.Events.addNode(el, r, "p");
+		r = [];
+
+		r.push(`Next, you see to ${slave.slaveName}.`);
+
+		if (isAmputee(slave)) {
+			r.push(`You move the limbless ${girl} to ${milkTap.slaveName}'s nipple and strap ${him} to it to prevent it from falling out of ${his} mouth.`);
+		} else if (tooBigBreasts(slave)) {
+			r.push(`You set ${him} up so that ${his} massive breasts pin ${him} on ${milkTap.slaveName}'s milky nipple.`);
+		} else if (milkTap.fuckdoll > 0) {
+			r.push(`${He} hesitantly brings ${his} mouth to its milky nipple, uncertain about suckling from a living doll.`);
+		} else if (slave.rivalryTarget === milkTap.ID) {
+			r.push(`Knowing ${his} relationship with ${milkTap.slaveName}, you feel it best to restrain ${him} and anchor ${him} to ${milkTap.slaveName}'s milky nipple so ${he} has no choice but to drink until you release ${him}.`);
+		} else if (slave.relationshipTarget === milkTap.ID) {
+			r.push(`This is rather easy, as ${slave.slaveName}`);
+			if (slave.relationship === 1) {
+				r.push(`licks ${his} lips as ${he} approaches ${his} friend's breasts.`);
+			} else if (slave.relationship === 2) {
+				r.push(`eagerly licks ${his} lips as ${he} approaches ${his} best friend's breasts.`);
+			} else if (slave.relationship === 3) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} friend with benefits' breasts, knowing well how ${his2}`);
+				if (canTaste(slave)) {
+					r.push(`milk tastes.`);
+				} else {
+					r.push(`body feels.`);
+				}
+			} else if (slave.relationship === 4) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} lover's breasts. This won't be the first time ${he}'s suckled from ${him2} like this.`);
+			} else if (slave.relationship === 5) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} ${wife2}'s breasts. This won't be the first time ${he}'s suckled from ${him2} like this.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			r.push(`${He} draws close to ${his} mother's nipples, trying to remember if ${he} once had a favorite.`);
+		} else if (slave.father === milkTap.ID) {
+			if (_incestTake) {
+				r.push(`${He} eagerly wraps ${his} lips around ${his} father's nipple.`);
+				if (canAchieveErection(milkTap)) {
+					r.push(`${He} shudders with budding lust when ${he} feels the dick that sired ${him} poking at ${his} belly.`);
+				}
+			} else {
+				r.push(`${He} hesitatingly lowers ${himself} to ${his} father's nipple.`);
+				if (canAchieveErection(milkTap)) {
+					r.push(`${He} nearly backs away when ${he} feels the dick that sired ${him} poking at ${his} belly.`);
+				}
+			}
+		} else if (milkTap.mother === slave.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`happily`);
+			} else {
+				r.push(`awkwardly`);
+			}
+			r.push(`brings ${his} lips to ${his} relative's nipple.`);
+		} else if (milkTap.father === slave.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly`);
+			} else {
+				r.push(`awkwardly`);
+			}
+			r.push(`brings ${his} lips to ${his} relative's nipple${(canAchieveErection(slave)) ? `, ${his} cock steadily hardening at the perversion of suckling from ${his} child`:``}.`);
+		} else if (areSisters(slave, milkTap) === 1) {
+			r.push(`${He} readily gets in position to`);
+			if (canTaste(slave)) {
+				r.push(`taste`);
+			} else {
+				r.push(`suckle from`);
+			}
+			r.push(`${his} ${relative}${(slave.lactation > 0) ? `while coaxing ${his} own milk to flow` : ``}.`);
+		} else if (areSisters(slave, milkTap) === 2) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`hesitatingly lowers ${himself} to`);
+			}
+			r.push(`${his} relative's nipple.`);
+		} else if (areSisters(slave, milkTap) === 3) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`hesitatingly lowers ${himself} to`);
+			}
+			r.push(`${his} relative's nipple.`);
+		} else if ((slave.fetish === "boobs") && (slave.fetishKnown === 1) && (slave.fetishStrength > 60) && (slave.devotion >= -20)) {
+			r.push(`${He} can't wait to`);
+			if (hasBothArms(slave)) {
+				r.push(`wrap ${his} hands around`);
+			} else {
+				r.push(`get between`);
+			}
+			r.push(`${milkTap.slaveName}'s massive milky breasts and eagerly approaches ${his} nipples to suckle.`);
+		} else if ((slave.fetish === "submissive") && (slave.fetishStrength > 60) && (slave.fetishKnown === 1)) {
+			r.push(`${He} is accustomed to submitting to you, but as a natural submissive ${he} doesn't have much trouble submitting to ${milkTap.slaveName}'s mothering instead.`);
+		} else if (slave.devotion < -20) {
+			r.push(`${He} tries to refuse, so you strap ${him} to ${milkTap.slaveName}'s breast, milky ${milkTap.nipples} nipple wedged in ${his} mouth.`);
+		} else if (slave.devotion <= 20) {
+			r.push(`${He} obeys your orders reluctantly, drawing near ${milkTap.slaveName}'s breasts despite ${his} obvious hesitation to be filled with milk.`);
+		} else if (slave.devotion <= 50) {
+			r.push(`${He} obeys your orders, drawing near ${milkTap.slaveName}'s breasts despite ${his} slight hesitation at the idea of being filled with milk.`);
+		} else {
+			r.push(`${He} happily obeys your orders, eagerly wrapping ${his} lips around ${milkTap.slaveName}'s milky ${milkTap.nipples} nipple and suckling enthusiastically.`);
+		}
+
+		App.Events.addNode(el, r, "p");
+		r = [];
+
+		if (slave.preg > slave.pregData.normalBirth / 13.33 && slave.pregKnown === 0 && slave.inflation > 1) {
+			r.push(`It becomes abundantly clear that something is wrong with ${slave.slaveName} as ${he} struggles to down ${his} milky meal. Before ${his} health can be affected further, you pull ${him} into a medical exam. While most of the tests come back normal, one in particular catches your eye; <span class="lime">${he} is pregnant${(slave.preg > slave.pregData.normalBirth / 4) ? `, and surprisingly far along` : ``}.</span> ${he} should be able to still handle at least two liters of milk, however.`);
+			deflate(slave);
+			slave.pregKnown = 1;
+			_pregDiscovery = 1;
+		} else if (milkTap.fuckdoll > 0) {
+			r.push(`Slight moaning emanates from the Fuckdoll as ${slave.slaveName} drinks from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his2} nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his2} nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his2} nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.rivalryTarget === slave.ID) {
+			r.push(`${milkTap.slaveName} grins as ${his2} rival is forced to drink until ${his} belly is`);
+			if (slave.inflation === 3) {
+				r.push(`nearly bursting with milk. ${slave.slaveName} struggles against ${his} bindings until the pressure building in ${him} overwhelms ${him}, causing ${him} to pass out directly into ${milkTap.slaveName}'s cushiony breasts. You quickly remove ${him} from the nipple before ${he} drowns`);
+			} else if (slave.inflation === 2) {
+				r.push(`is rounded, jiggling and sloshing with milk. You release ${his} bindings, allowing ${him} to flop to the floor.`);
+				if (hasAnyArms(slave)) {
+					r.push(`${He} gingerly crawls away from ${milkTap.slaveName},`);
+					if (hasBothArms(slave)) {
+						r.push(`one`);
+					} else {
+						r.push(`${his}`);
+					}
+					r.push(`hand cradling ${his} overfull stomach`);
+				} else {
+					r.push(`${He} rolls onto ${his} side, groaning with discomfort`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`bloated with milk. You release ${his} bindings, allowing ${him} to flop to the floor.`);
+				if (hasAnyArms(slave)) {
+					r.push(`${He} gingerly sits up and begins massaging ${his} full stomach`);
+				} else {
+					r.push(`${He} rolls onto ${his} back, hiccupping pathetically`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.relationshipTarget === slave.ID) {
+			if (milkTap.relationship === 1) {
+				r.push(`${milkTap.slaveName} sighs contently as ${his2} friend drinks deeply from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 2) {
+				r.push(`${milkTap.slaveName} sighs contently as ${his2} best friend drinks deeply from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} best friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} best friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} best friend's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 3) {
+				r.push(`${milkTap.slaveName} moans lewdly as ${his2} friend with benefits drinks deeply from ${his2} breasts, savoring it despite commonly nursing ${slave.slaveName} during their lovemaking. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} friend with benefits' nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} friend with benefits' nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} friend with benefits' nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 4) {
+				r.push(`${milkTap.slaveName} moans lewdly as ${his2} lover drinks deeply from ${his2} breasts, savoring it despite commonly nursing ${slave.slaveName} during their lovemaking. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} lover's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} lover's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} lover's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 5) {
+				r.push(`${milkTap.slaveName} moans lewdly as ${his2} wife drinks deeply from ${his2} breasts, savoring it despite commonly nursing ${slave.slaveName} during their lovemaking. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${wife2}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${wife2}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${wife2}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			r.push(`${milkTap.slaveName} sighs contently as ${his2} little ${girl} once again suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} mother's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} mother's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} mother's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (slave.father === milkTap.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relativeTerm(milkTap, slave)} suckles from ${his2} breasts${(canAchieveErection(milkTap)) ? `, ${his2} dick throbbing with lust` : ``}. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} father's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} father's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} father's nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			if (canAchieveErection(milkTap)) {
+				r.push(r.pop() + `.`);
+				if (_incestTake) {
+					r.push(`The way ${he} is wiggling ${his} hips suggests ${he} isn't finished with ${his} daddy just yet, and ${his} father's moaning confirms ${he} is teasing ${him2} with ${his} rear. ${He} giggles as the horny cow unloads on ${his} backside`);
+				} else {
+					r.push(`${He} doesn't stay put for long, as a strong moan and a blast of cum across ${his} rear from the horny cow startles ${him} from ${his} rest`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.mother === slave.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${he2} enjoys some role reversal as ${his2} mother suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.father === slave.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} father suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 1) {
+			r.push(`${milkTap.slaveName} sighs contently as ${his2} ${relativeTerm(milkTap, slave)} suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping}`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 2) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2}`);
+			if (milkTap.actualAge >= slave.actualAge) {
+				r.push(`little`);
+			} else {
+				r.push(`big`);
+			}
+			r.push();
+			r.push(`${relativeTerm(milkTap, slave)} suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 3) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relative} suckles from ${his2} breasts. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with milk. ${He} pops off ${his} ${relative}'s nipple and settles into ${his2} breasts for a short rest while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if ((slave.devotion < -20) && (milkTap.devotion < -20)) {
+			r.push(`Since you have two restrained and unwilling slaves, the work of milking ${milkTap.slaveName}'s breasts falls to you. That doesn't mean you can't have fun doing it though.`);
+			if (canDoVaginal(milkTap)) {
+				r.push(`Moving behind the restrained cow while`);
+				if (V.PC.dick === 0) {
+					r.push(`donning a strap-on,`);
+				} else {
+					r.push(`teasing your erect cock,`);
+				}
+				r.push(`you push ${him2} forward to allow you to insert yourself into ${his2}`);
+				if (milkTap.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy. Getting comfortable, you reach around to ${his2} immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms in both yourself and the sobbing cow, is ${slave.slaveName} bloated with enough milk.`);
+				actX(milkTap, "vaginal");
+				if (canImpreg(milkTap, V.PC)) {
+					r.push(knockMeUp(milkTap, 40, 0, -1));
+				}
+			} else if (canDoAnal(milkTap)) {
+				r.push(`Moving behind the restrained cow while`);
+				if (V.PC.dick === 0) {
+					r.push(`donning a strap-on,`);
+				} else {
+					r.push(`teasing your erect cock,`);
+				}
+				r.push(`you push ${him2} forward to allow you to insert yourself into ${his2}`);
+				if (milkTap.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`rear. Getting comfortable, you reach around to ${his2} immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms in both yourself and the sobbing cow, is ${slave.slaveName} bloated with enough milk.`);
+				actX(milkTap, "anal");
+				if (canImpreg(milkTap, V.PC)) {
+					r.push(knockMeUp(milkTap, 40, 1, -1));
+				}
+			} else if (V.PC.dick !== 0 && milkTap.butt > 4) {
+				r.push(`Moving behind the restrained cow while teasing your erect cock, you push ${him2} forward to allow you to press your dick between ${his2} huge butt cheeks. Getting comfortable, you reach around to ${his2} immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms across the back of the sobbing cow, is ${slave.slaveName} bloated with enough milk.`);
+			} else if (V.PC.dick !== 0 && !hasAnyLegs(milkTap)) {
+				r.push(`Moving behind the restrained cow while teasing your erect cock, you find a severe lack of places to stick your dick. Sighing, you hoist ${his2} belted ass into the air so you may thrust between ${his2}`);
+				if (milkTap.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs. Getting comfortable, you reach around to ${his2} immense mammaries and begin kneading them in time to your thrusts. After some time, and several loads blown ${(milkTap.belly >= 1500) ? `onto the rounded belly of the sobbing cow`: ``}, is ${slave.slaveName} bloated with enough milk.`);
+			} else {
+				r.push(`With a lack of holes to penetrate, you simply wrap your arms around ${milkTap.slaveName} and begin fondling and milking ${his2} luscious breasts. After some time, ${slave.slaveName} is finally bloated to your desired size.`);
+			}
+			r.push(`Standing and releasing ${him} from ${milkTap.slaveName}, gives you the opportunity to finally see ${slave.slaveName}'s`);
+
+			if (slave.inflation === 3) {
+				r.push(`taut, round belly.`);
+			} else if (slave.inflation === 2) {
+				r.push(`rounded, jiggling belly.`);
+			} else {
+				r.push(`distended, sloshing belly.`);
+			}
+			r.push(`You just wish you could have enjoyed it a bit more, though forcing milk into the squirming slave was quite enjoyable. Both slaves <span class="mediumorchid">resent</span> what you made them do and <span class="gold">fear you</span> as a result.`);
+			slave.devotion -= 5;
+			slave.trust -= 5;
+			milkTap.devotion -= 5;
+			milkTap.trust -= 5;
+			if (canDoVaginal(milkTap) && (milkTap.vagina === 0)) {
+				r.push(`${milkTap.slaveName} <span class="mediumorchid">especially,</span> having just <span class="lime">lost ${his} virginity</span> to your inconvenience.`);
+				milkTap.devotion -= 5;
+				milkTap.vagina = 1;
+			} else if (canDoAnal(milkTap) && (milkTap.anus === 0)) {
+				r.push(`${milkTap.slaveName} <span class="mediumorchid">especially,</span> having just <span class="lime">lost ${his} anal virginity</span> to your inconvenience.`);
+				milkTap.devotion -= 5;
+				milkTap.anus = 1;
+			}
+		} else if ((milkTap.devotion < -20)) {
+			r.push(`Since your cow is restrained, you order the more obedient ${slave.slaveName} to enjoy ${himself} with ${milkTap.slaveName}'s breasts. As ${he} suckles, you can't help but notice the tantalizing way ${he} wiggles ${his} rear.`);
+			if (canDoVaginal(slave)) {
+				if (V.PC.dick === 0) {
+					r.push(`Donning a strap-on,`);
+				} else {
+					r.push(`Teasing your stiffening cock,`);
+				}
+				r.push(`you push ${him} deeper into the protesting ${milkTap.slaveName} and mount ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy, doggy style. You wrap your arms around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk.`);
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+				}
+				r.push(`Only once your weight is removed from the squirming milk balloon is ${he} allowed to pull ${himself} off of the <span class="mediumorchid">resentful ${milkTap.slaveName}</span> and catch ${his} breath.`);
+				if (slave.vagina === 0) {
+					r.push(`${His} senses were so overwhelmed, ${he} didn't even notice you <span class="lime">broke in ${his} vagina.</span>`);
+				}
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				if (V.PC.dick === 0) {
+					r.push(`Donning a strap-on,`);
+				} else {
+					r.push(`Teasing your stiffening cock,`);
+				}
+				r.push(`you push ${him} deeper into the protesting ${milkTap.slaveName} and mount ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`asshole, doggy style. You wrap your arms around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+				}
+				r.push(`Only once your weight is removed from the squirming milk balloon is ${he} allowed to pull ${himself} off of the <span class="mediumorchid">resentful ${milkTap.slaveName}</span> and catch ${his} breath.`);
+				if (slave.anus === 0) {
+					r.push(`${His} senses were so overwhelmed, ${he} didn't even notice you <span class="lime">broke in ${his} anus.</span>`);
+				}
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`Teasing your stiffening cock, you push ${him} deeper into the protesting ${milkTap.slaveName} and squeeze your dick between ${his} huge butt cheeks. You wrap your arms around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk as you fuck ${his} butt.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+				}
+				r.push(`Only once your weight is removed from the squirming milk balloon is ${he} allowed to pull ${himself} off of the <span class="mediumorchid">resentful ${milkTap.slaveName}</span> and catch ${his} breath.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`Teasing your stiffening cock, you find a severe lack of places to stick your dick. Sighing, you hoist ${his} belted ass into the air, push ${him} deeper into the protesting ${milkTap.slaveName} and squeeze your dick between ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs. You wrap your arms around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk as you fuck ${his} butt.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+				}
+				r.push(`Only once your weight is removed from the squirming milk balloon is ${he} allowed to pull ${himself} off of the <span class="mediumorchid">resentful ${milkTap.slaveName}</span> and catch ${his} breath.`);
+			} else {
+				r.push(`With a lack of holes to penetrate, you simply wrap your arms around ${him} and push ${him} deeper into the protesting ${milkTap.slaveName}. You bring a hand to ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and lead the other to your`);
+
+				if (V.PC.dick === 0) {
+					r.push(`soaked pussy.`);
+				} else {
+					r.push(`stiff prick.`);
+				}
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+				}
+				r.push(`Only once your weight is removed from the squirming milk balloon is ${he} allowed to pull ${himself} off of the <span class="mediumorchid">resentful ${milkTap.slaveName}</span> and catch ${his} breath.`);
+			}
+			r.push(`${He} gives the shaking ${milkTap.slaveName} an apologetic look before taking a seat. The poor cow isn't used to this yet and <span class="gold">is terrified of your willingness</span> to take what you want from your slaves.`);
+			milkTap.devotion -= 5;
+			milkTap.trust -= 5;
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				slave.anus = 1;
+			}
+		} else if ((milkTap.fetish === "boobs") && (milkTap.fetishStrength > 60) && (milkTap.devotion > 20) && (slave.devotion < -20)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} is forced to drink from ${milkTap.slaveName}'s breasts. With every thrust into the squirming slave, you push ${him} into the moaning ${milkTap.slaveName} forcing even more milk down ${his} throat. You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he} loves it groped.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers`);
+				}
+				r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} well milked breasts, you know ${he2}'ll come out of it and be eagerly begging you for another milking soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen stomach <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+				if (slave.anus === 0) {
+					r.push(`${He} <span class="mediumorchid">hates you so much more</span> that you <span class="lime">broke in ${his} virgin vagina.</span>`);
+				}
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} is forced to drink from ${milkTap.slaveName}'s breasts. With every thrust into the squirming slave, you push ${him} into the moaning ${milkTap.slaveName} forcing even more milk down ${his} throat. You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he} loves it groped.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers`);
+				}
+				r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} well milked breasts, you know ${he2}'ll come out of it and be eagerly begging you for another milking soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen stomach <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+				if (slave.anus === 0) {
+					r.push(`${He} <span class="mediumorchid">hates you so much more</span> that you <span class="lime">broke in ${his} virgin anus.</span>`);
+				}
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can rub your dick between ${his} huge butt cheeks while ${he} is forced to drink from ${milkTap.slaveName}'s breasts. With every thrust against the squirming slave, you push ${him} into the moaning ${milkTap.slaveName} forcing even more milk down ${his} throat. You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he} loves it groped.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers`);
+				}
+				r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} well milked breasts, you know ${he}'ll come out of it and be eagerly begging you for another milking soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen stomach, and cum soaked back, <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs, for a lack of anything better, while ${he} is forced to drink from ${milkTap.slaveName}'s breasts. With every thrust against the squirming slave, you push ${him} into the moaning ${milkTap.slaveName} forcing even more milk down ${his} throat. You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he} loves it groped.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers`);
+				}
+				r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} well milked breasts, you know ${he2}'ll come out of it and be eagerly begging you for another milking soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen, cum-covered stomach <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+			} else {
+				r.push(`You position the restrained ${slave.slaveName} so that you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} is forced to drink from ${milkTap.slaveName}'s breasts, since ${he} lacks any better way to please you while you lavish attention on your eager cow. With every thrust against the squirming slave, you push ${him} into the moaning ${milkTap.slaveName} forcing even more milk down ${his} throat. You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he} loves it groped.`);
+
+				if (slave.inflation === 3) {
+					r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+				} else if (slave.inflation === 2) {
+					r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers`);
+				} else {
+					r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers`);
+				}
+				r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} well milked breasts, you know ${he2}'ll come out of it and be eagerly begging you for another milking soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen stomach <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+			}
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				slave.vagina = 1;
+				slave.devotion -= 5;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				slave.anus = 1;
+				slave.devotion -= 5;
+			}
+			slave.devotion -= 5;
+			slave.trust -= 5;
+			milkTap.devotion += 4;
+		} else if ((slave.devotion <= 20) || (milkTap.devotion <= 20)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} drinks from ${milkTap.slaveName}'s breasts. With every thrust into the squirming slave, you push ${him} into the docile ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} drinks from ${milkTap.slaveName}'s breasts. With every thrust into the squirming slave, you push ${him} into the docile ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can rub your dick between ${his} huge butt cheeks while ${he} drinks from ${milkTap.slaveName}'s breasts. With every thrust against the squirming slave, you push ${him} into the docile ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs, for a lack of anything better, while ${he} drinks from ${milkTap.slaveName}'s breasts. With every thrust against the squirming slave, you push ${him} into the docile ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else {
+				r.push(`You order ${slave.slaveName} to position ${himself} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} drinks from ${milkTap.slaveName}'s breasts, since ${he} lacks any better way to please you while you lavish praise on your obedient cow. With every thrust against the squirming slave, you push ${him} into the docile ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he2} loves it groped.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting. Neither slave seems to have enjoyed it, instead opting to just get it over with, though ${milkTap.slaveName} makes sure to thank ${slave.slaveName} for lightening ${his2} milky breasts.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				slave.anus = 1;
+			}
+		} else if ((slave.devotion <= 50) || (milkTap.devotion <= 50)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} drinks from ${milkTap.slaveName}'s breasts. ${He} submissively obeys. With every thrust into the moaning slave, you push ${him} into the smiling ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} drinks from ${milkTap.slaveName}'s breasts. ${He} submissively obeys. With every thrust into the moaning slave, you push ${him} into the smiling ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can rub your dick between ${his} huge butt cheeks while ${he} drinks from ${milkTap.slaveName}'s breasts. ${He} submissively obeys. With every thrust against the chaste slave, you push ${him} into the smiling ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs, for a lack of anything better, while ${he} drinks from ${milkTap.slaveName}'s breasts. ${He} submissively obeys. With every thrust against the chaste slave, you push ${him} into the smiling ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else {
+				r.push(`You order ${slave.slaveName} to position ${himself} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} drinks from ${milkTap.slaveName}'s breasts, since ${he} lacks any better way to please you while you lavish attention on your happy cow. With every thrust against the squirming slave, you push ${him} into the smiling ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple, knowing just how much ${he2} gets backed up.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting. Both slaves enjoyed their union, though ${milkTap.slaveName} even more so thanks to ${his2} lighter breasts.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`feels <span class="hotpink">closer to you</span> after losing ${his} virginity to you.`);
+				slave.vagina = 1;
+				slave.devotion += 2;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`feels <span class="hotpink">closer to you</span> after losing ${his} anal virginity to you.`);
+				slave.anus = 1;
+				slave.devotion += 2;
+			}
+		} else {
+			r.push(`${slave.slaveName}`);
+			r.push(`eagerly lifts ${his} ass and jiggles it seductively as ${he} suckles from the moaning cow.`);
+			if (canDoVaginal(slave)) {
+				r.push(`You know that signal, so you hilt yourself in`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`and begin fucking ${him} against ${milkTap.slaveName}'s tits. With every thrust into the moaning slave, you push ${him} into the grinning ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You know that signal, so you hilt yourself in`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`and begin fucking ${him} against ${milkTap.slaveName}'s tits. With every thrust into the moaning slave, you push ${him} into the grinning ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You know that signal, but ${he} isn't allowed to get penetrated, so you settle for sticking your dick between ${his} huge butt cheeks and fucking ${him} against ${milkTap.slaveName}'s tits. With every thrust against the moaning slave, you push ${him} into the grinning ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You know that signal, but ${he} isn't allowed to get penetrated, so you settle for sticking your dick between ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs, for a lack of anything better, and fuck ${him} against ${milkTap.slaveName}'s tits. With every thrust against the moaning slave, you push ${him} into the grinning ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			} else {
+				r.push(`You know that signal, but ${he} isn't allowed to get fucked, so you reposition ${him} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} drinks from ${milkTap.slaveName}'s tits. With every thrust against the moaning slave, you push ${him} into the grinning ${milkTap.slaveName} forcing even more milk down ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with milk and place your other hand to ${milkTap.slaveName}'s free nipple to prevent ${him2} from feeling left out.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with milk under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting from ${his} meal`);
+			if (canDoVaginal(slave) || canDoAnal(slave)) {
+				r.push(`and from the pleasure you drove into ${him}`);
+			}
+			r.push(r.pop() + `.`);
+			r.push(`Both slaves <span class="hotpink">loved the attention,</span> though ${milkTap.slaveName} even more so thanks to ${his2} lighter breasts.`);
+			slave.devotion += 4;
+			milkTap.devotion += 4;
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`got off quite strongly from the growing pressure within ${him}, <span class="hotpink">cementing</span> ${his} <span class="lime">first fucking</span> as something special.`);
+				slave.devotion += 4;
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`got off quite strongly from the growing pressure within ${him}, <span class="hotpink">cementing</span> ${his} <span class="lime">first anal</span> as something special.`);
+				slave.devotion += 4;
+				slave.anus = 1;
+			}
+		}
+
+		if (milkTap.lactation > 0) {
+			milkTap.lactationDuration = 2;
+			milkTap.boobs -= milkTap.boobsMilk;
+			milkTap.boobsMilk = 0;
+		}
+	} else { /* cum variant */
+		slave.cumSource = milkTap.ID;
+		if (milkTap.behavioralQuirk === "sinful" || milkTap.sexualQuirk === "perverted" || milkTap.fetish === "incest") { /* incest is a planned fetish, don't touch it! */
+			_incestGive = 1;
+		}
+		if (slave.behavioralQuirk === "sinful" || slave.sexualQuirk === "perverted" || slave.fetish === "incest") {
+			_incestTake = 1;
+		}
+
+		r.push(`The first necessary step is to prepare the cum slave and ${his} cock and balls.`);
+
+		if (milkTap.fuckdoll > 0) {
+			r.push(`This is hilariously easy, as you have complete control over how ${milkTap.slaveName} is posed.`);
+		} else if (milkTap.fetish === "mindbroken") {
+			r.push(`This is very easy, as ${milkTap.slaveName} blankly follows your every will. Combined with ${his2} instinct to relieve the building pressure in ${his2} loins, ${he2} is simple to position.`);
+		} else if (milkTap.rivalryTarget === slave.ID) {
+			r.push(`This is rather easy, as ${milkTap.slaveName} wants to`);
+			if (canSee(milkTap)) {
+				r.push(`see`);
+			} else {
+				r.push(`feel`);
+			}
+			r.push(`${slave.slaveName}'s belly swell painfully as ${he} is forced to suck down ${his2} huge loads.`);
+		} else if (milkTap.relationshipTarget === slave.ID) {
+			r.push(`This is rather easy, as ${milkTap.slaveName}`);
+			if (milkTap.relationship === 1) {
+				r.push(`always wanted to get ${his2} dick sucked by ${his2} friend.`);
+			} else if (milkTap.relationship === 2) {
+				r.push(`always wanted to get ${his2} dick sucked by ${his2} best friend.`);
+			} else if (milkTap.relationship === 3) {
+				r.push(`enjoys getting ${his2} dick sucked by ${his2} friend with benefits.`);
+			} else if (milkTap.relationship === 4) {
+				r.push(`loves getting ${his2} dick sucked by ${his2} lover, something that commonly happens due to ${his2} overproduction.`);
+			} else if (milkTap.relationship === 5) {
+				r.push(`loves getting ${his2} dick sucked by ${his2} ${wife}, something that commonly happens due to ${his2} overproduction.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} savors the thought of having ${his2} dick sucked by ${his2} ${relative}.`);
+			} else {
+				r.push(`This is tough, as ${milkTap.slaveName} is very uncomfortable having ${his2} dick sucked by ${his2} ${relative}, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if (slave.father === milkTap.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} cherishes the sheer lewdity of having ${his2} dick sucked by ${his2} ${relative}.`);
+			} else {
+				r.push(`This is tough, as ${milkTap.slaveName} is rather uncomfortable having ${his2} dick sucked by ${his2} ${relative}, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if (milkTap.mother === slave.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} savors the thought of having ${his2} dick sucked by ${his2} own mother.`);
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} is very uncomfortable having ${his2} dick sucked by ${his2} own mother, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if (milkTap.father === slave.ID) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} cherishes the sheer lewdity of having ${his2} dick sucked by ${his2} own father.`);
+			} else {
+				r.push(`This is tough, as ${milkTap.slaveName} is very uncomfortable having ${his2} dick sucked by ${his2} own father, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if (areSisters(slave, milkTap) === 1) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} enjoys the notion of twincest quite a lot.`);
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} is uncomfortable getting so intimate with ${his2}`);
+				if (milkTap.energy >= 95) {
+					r.push(`${relative}, though as a nymphomaniac, the thought of someone who looks so much like ${his2} is a major turn on.`);
+				} else {
+					r.push(`${relative} but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+				}
+			}
+		} else if (areSisters(slave, milkTap) === 2) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} is quite eager to get intimate with ${his2} ${relative}.`);
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} is uncomfortable getting so intimate with ${his2} ${relative}, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if (areSisters(slave, milkTap) === 3) {
+			if (_incestGive) {
+				r.push(`This is easy enough, as ${milkTap.slaveName} is quite eager to get intimate with ${his2} ${relative}.`);
+			} else {
+				r.push(`This is moderately tough, as ${milkTap.slaveName} is uncomfortable getting so intimate with ${his2} ${relative}, but ${he2} can't really complain about getting ${his2} overfilled nuts drained.`);
+			}
+		} else if ((milkTap.fetish === "cumslut") && (milkTap.fetishKnown === 1) && (milkTap.fetishStrength > 60) && (milkTap.devotion >= -20)) {
+			r.push(`This is very easy, since ${milkTap.slaveName} loves blasting loads whenever ${he2} can, and it is just a bonus to ${him2} that ${he2} gets a blowjob in the process.`);
+		} else if ((milkTap.energy > 95)) {
+			r.push(`This is very easy, since ${milkTap.slaveName} is so sexually charged ${he2} is practically overflowing at the thought of getting ${his2} dick sucked.`);
+		} else if ((milkTap.devotion > 50)) {
+			r.push(`Since ${milkTap.slaveName} is devoted to you, ${he2}'d allow anyone you want to suck ${his2} dick.`);
+		} else if ((milkTap.devotion > 20)) {
+			r.push(`Since ${milkTap.slaveName} is obedient, ${he2} appreciates being allowed to have ${his2} dick sucked.`);
+		} else if ((milkTap.devotion >= -20)) {
+			r.push(`Since ${milkTap.slaveName} does not resist your will, ${he2} should comply reasonably well. If anything, ${he2}'ll at least be thankful for the pleasure and relief.`);
+		} else {
+			r.push(`Since ${milkTap.slaveName} is unlikely to comply willingly, you simply restrain ${him2} with ${his2} dick exposed and ready to be sucked. To get ${him2} going,`);
+			if (canDoAnal(milkTap) && milkTap.prostate > 0) {
+				r.push(`you circle around behind ${him2},`);
+				if (V.PC.dick === 0) {
+					r.push(`while donning a strap-on,`);
+				} else {
+					r.push(`teasing your erect cock,`);
+				}
+				r.push(`and force ${him2} forward to allow you to insert yourself into ${his}`);
+				if (milkTap.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`rear. After a quick and brutal bit of prostrate stimulation, you finish and remove yourself from ${him2}. Before ${he2} has a chance to reclench ${his2} anus, you ram an electroshock stimulator in your stead.`);
+			} else {
+				r.push(`you attach a number of vibrators to ${his2} oversized balls and turn them to full power, stirring up ${his2} overzealous cum factories.`);
+			}
+			r.push(`${He2} cries in <span class="mediumorchid">disgust</span> and <span class="gold">fear</span> as ${his2} penis twitches from the sensation, begging for unwelcome release.`);
+			milkTap.devotion -= 5;
+			milkTap.trust -= 5;
+			if (canDoAnal(milkTap)) {
+				if (canImpreg(milkTap, V.PC)) {
+					r.push(knockMeUp(milkTap, 40, 1, -1));
+				}
+				if (milkTap.anus === 0) {
+					r.push(`${milkTap.slaveName} feels <span class="mediumorchid">especially violated</span> having just <span class="lime">lost ${his} anal virginity</span> in such a manner.`);
+					milkTap.devotion -= 5;
+					milkTap.anus = 1;
+				}
+				actX(milkTap, "anal");
+			}
+		}
+
+		App.Events.addNode(el, r, "p");
+		r = [];
+
+		r.push(`Next, you see to ${slave.slaveName}.`);
+
+		if (isAmputee(slave)) {
+			r.push(`You tip the limbless ${girl} face-first into ${milkTap.slaveName}'s dick.`);
+		} else if (tooBigBreasts(slave)) {
+			r.push(`You set ${him} up so that ${his} massive breasts pin ${him}, face-first, to ${milkTap.slaveName}'s dick.`);
+		} else if (milkTap.fuckdoll > 0) {
+			r.push(`${He} hesitantly brings ${his} mouth to its precum tipped dick, uncertain about sucking off a doll.`);
+		} else if (slave.rivalryTarget === milkTap.ID) {
+			r.push(`Knowing ${his} relationship with ${milkTap.slaveName}, you feel it best to restrain ${him} and anchor ${him} to ${milkTap.slaveName}'s eager cock so ${he} has no choice but to suck ${his} way to release.`);
+		} else if (slave.relationshipTarget === milkTap.ID) {
+			r.push(`This is rather easy, as ${slave.slaveName}`);
+			if (slave.relationship === 1) {
+				r.push(`licks ${his} lips as ${he} approaches ${his} friend's cock.`);
+			} else if (slave.relationship === 2) {
+				r.push(`eagerly licks ${his} lips as ${he} approaches ${his} best friend's cock.`);
+			} else if (slave.relationship === 3) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} friend with benefits' cock, knowing well how ${his2}`);
+				if (canTaste(slave)) {
+					r.push(`cum tastes.`);
+				} else {
+					r.push(`body feels.`);
+				}
+			} else if (slave.relationship === 4) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} lover's cock. This won't be the first time ${he}'s sucked ${his2} dick and swallowed ${his2} huge loads.`);
+			} else if (slave.relationship === 5) {
+				r.push(`licks ${his} lips and smiles as ${he} approaches ${his} ${wife2}'s cock. This won't be the first time ${he}'s sucked ${his2} dick and swallowed ${his2} huge loads.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`awkwardly brings ${his} lips to`);
+			}
+			r.push(`${his} mother's cock.`);
+		} else if (slave.father === milkTap.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`awkwardly brings ${his} lips to`);
+			}
+			r.push(`daddy's cock.`);
+		} else if (milkTap.mother === slave.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`awkwardly brings ${his} lips to`);
+			}
+			r.push(`${his} ${relative}'s cock.`);
+		} else if (milkTap.father === slave.ID) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`awkwardly brings ${his} lips to`);
+			}
+			r.push(`${his} ${relative}'s cock.`);
+		} else if (areSisters(slave, milkTap) === 1) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`readily`);
+			}
+			r.push(`gets in position to suck ${his} ${relative}'s dick.`);
+		} else if (areSisters(slave, milkTap) === 2) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`hesitatingly lowers ${himself} to`);
+			}
+			r.push(`${his} ${relative}'s cock.`);
+		} else if (areSisters(slave, milkTap) === 3) {
+			r.push(`${He}`);
+			if (_incestTake) {
+				r.push(`eagerly wraps ${his} lips around`);
+			} else {
+				r.push(`hesitatingly lowers ${himself} to`);
+			}
+			r.push(`${his} ${relative}'s cock.`);
+		} else if ((slave.fetish === "cumslut") && (slave.fetishKnown === 1) && (slave.fetishStrength > 60) && (slave.devotion >= -20)) {
+			r.push(`${He} can't wait to wrap ${his} lips around ${milkTap.slaveName}'s cock and balloon with cum, so ${he} eagerly approaches the waiting shaft.`);
+		} else if ((slave.fetish === "submissive") && (slave.fetishStrength > 60) && (slave.fetishKnown === 1)) {
+			r.push(`${He} is accustomed to submitting to you, but as a natural submissive ${he} doesn't have much trouble submitting to ${milkTap.slaveName} instead.`);
+		} else if (slave.devotion < -20) {
+			r.push(`${He} tries to refuse, so you tie ${him} up, force a mouth spreader into ${him}, and position ${him} for ${milkTap.slaveName} to thrust into.`);
+		} else if (slave.devotion <= 20) {
+			r.push(`${He} obeys your orders reluctantly, drawing near ${milkTap.slaveName}'s cock despite ${his} obvious hesitation to amount of cum that will be gushing into ${him}.`);
+		} else if (slave.devotion <= 50) {
+			r.push(`${He} obeys your orders, drawing near ${milkTap.slaveName}'s cock despite ${his} slight hesitation at the idea of being filled with cum.`);
+		} else {
+			r.push(`${He} happily obeys your orders, eagerly`);
+			if (canTaste(slave)) {
+				r.push(`tasting`);
+			} else {
+				r.push(`licking up`);
+			}
+			r.push(`${milkTap.slaveName}'s beading precum before wrapping ${his} lips around ${milkTap.slaveName}'s cock and sucking enthusiastically.`);
+		}
+
+		App.Events.addNode(el, r, "p");
+		r = [];
+
+		if (slave.preg > 3 && slave.pregKnown === 0 && slave.inflation > 1) {
+			r.push(`It becomes abundantly clear that something is wrong with ${slave.slaveName} as ${he} struggles to down ${his} thick meal. Before ${his} health can be affected further, you pull ${him} into a medical exam. While most of the tests come back normal, one in particular catches your eye; <span class="lime">${he} is pregnant${(slave.preg > 10) ? `and surprisingly far along` :``}.</span> ${he} should be able to still handle at least two liters of cum, however.`);
+			deflate(slave);
+			slave.pregKnown = 1;
+			_pregDiscovery = 1;
+		} else if (milkTap.fuckdoll > 0) {
+			r.push(`Slight moaning emanates from the Fuckdoll as ${slave.slaveName} sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his2} cock and settles into ${his2} balls for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his2} cock and settles into ${his2} balls for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his2} cock and settles into ${his2} balls for a short rest while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.rivalryTarget === slave.ID) {
+			r.push(`${milkTap.slaveName} grins as ${his2} rival is forced to suck down loads until ${his} belly is`);
+			if (slave.inflation === 3) {
+				r.push(`nearly bursting with cum. ${slave.slaveName} struggles against ${his} bindings until the pressure building in ${his} overwhelms ${him}, causing ${him} to pass out with ${milkTap.slaveName}'s ejaculating cock still stuck in ${him}. You quickly remove ${him} from it, making sure ${he} gets roused from ${his} stupor by one last blast of cum directly to the face.`);
+			} else if (slave.inflation === 2) {
+				r.push(`is rounded, jiggling and sloshing with cum. You release ${his} bindings, allowing ${him} to flop to the floor.`);
+				if (hasAnyArms(slave)) {
+					r.push(`${He} gingerly crawls away from ${milkTap.slaveName}, ${his} hand cradling ${his} overfull stomach.`);
+				} else {
+					r.push(`${He} rolls onto ${his} side, groaning with discomfort.`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`bloated with cum. You release ${his} bindings, allowing ${him} to flop to the floor.`);
+				if (hasAnyArms(slave)) {
+					r.push(`${He} gingerly sits up and begins massaging ${his} full stomach.`);
+				} else {
+					r.push(`${He} rolls onto ${his} back, hiccupping pathetically.`);
+				}
+			}
+		} else if (milkTap.relationshipTarget === slave.ID) {
+			if (milkTap.relationship === 1) {
+				r.push(`${milkTap.slaveName} moans as ${his2} friend energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 2) {
+				r.push(`${milkTap.slaveName} moans as ${his2} best friend energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} best friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} best friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} best friend's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 3) {
+				r.push(`${milkTap.slaveName} moans lewdly as ${his2} friend with benefits energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} friend with benefits' cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} friend with benefits' cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} friend with benefits' cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 4) {
+				r.push(`${milkTap.slaveName} moans lustfully as ${his2} lover teases ${his} dick perfectly with ${his2} tongue, savoring it despite commonly being sucked off by ${slave.slaveName} during their lovemaking. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} lover's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} lover's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} lover's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			} else if (milkTap.relationship === 5) {
+				r.push(`${milkTap.slaveName} moans lustfully as ${his2} wife teases ${his2} dick perfectly with ${his} tongue, savoring it despite commonly being sucked off by ${slave.slaveName} during their lovemaking. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger until`);
+
+				if (slave.inflation === 3) {
+					r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${wife2}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and rubbing ${his} gurgling stomach`);
+					}
+				} else if (slave.inflation === 2) {
+					r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${wife2}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} wobbling, gurgling stomach`);
+					}
+				} else if (slave.inflation === 1) {
+					r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${wife2}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+					if (hasAnyArms(slave)) {
+						r.push(`and teasing ${his} gurgling stomach`);
+					}
+				}
+				r.push(r.pop() + `.`);
+			}
+		} else if (slave.mother === milkTap.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relative} energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} mother's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} mother's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} mother's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (slave.father === milkTap.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relative} energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} father's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} father's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} father's cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.mother === slave.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} mother energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (milkTap.father === slave.ID) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} father energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			if (slave.dick > 0 && canAchieveErection(slave)) {
+				r.push(r.pop() + `,`);
+				r.push(`${his} own stiff prick throbbing against the underside of ${his} new belly`);
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 1) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relative} sucks ${him2} off. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 2) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2}`);
+			if (milkTap.actualAge >= slave.actualAge) {
+				r.push(`little`);
+			} else {
+				r.push(`big`);
+			}
+			r.push(`${relative} energetically sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until`);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if (areSisters(slave, milkTap) === 3) {
+			r.push(`${milkTap.slaveName} moans lewdly as ${his2} ${relative} sucks ${his2} dick. You enjoy the show, specifically the sight of ${slave.slaveName}'s belly steadily growing larger with each orgasm until `);
+
+			if (slave.inflation === 3) {
+				r.push(`${his} belly is round and taut, making ${him} look pregnant. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and rubbing ${his} gurgling stomach`);
+				}
+			} else if (slave.inflation === 2) {
+				r.push(`${his} belly is round, jiggling and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} wobbling, gurgling stomach`);
+				}
+			} else if (slave.inflation === 1) {
+				r.push(`${his} belly is distended and sloshing with cum. ${He} pops off ${his} ${relative}'s cock and takes a seat facing the smiling ${milkTap.slaveName} while hiccupping`);
+
+				if (hasAnyArms(slave)) {
+					r.push(`and teasing ${his} gurgling stomach`);
+				}
+			}
+			r.push(r.pop() + `.`);
+		} else if ((slave.devotion < -20) && (milkTap.devotion < -20)) {
+			r.push(`Since you have two restrained and unwilling slaves, though ${milkTap.slaveName}'s twitching penis betrays ${him2}, you are going to have to take an active role in forcing ${slave.slaveName} to suck.`);
+			if (canDoVaginal(slave)) {
+				r.push(`Moving behind the struggling cocksleeve while`);
+				if (V.PC.dick === 0) {
+					r.push(`donning a strap-on,`);
+				} else {
+					r.push(`teasing your erect cock,`);
+				}
+				r.push(`you pull ${him} into a comfortable position to penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy. Once you are firmly mounted, you reach around, bringing one hand to ${his} empty stomach and the other to ${his} exposed throat. As you thrust into ${him}, you force ${him} to choke down ${milkTap.slaveName}'s dick, applying pressure to ${his} throat any time ${he} attempts to pull away.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`Moving behind the struggling cocksleeve while`);
+				if (V.PC.dick === 0) {
+					r.push(`donning a strap-on,`);
+				} else {
+					r.push(`teasing your erect cock,`);
+				}
+				r.push(`you pull ${him} into a comfortable position to penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`rear. Once you are firmly mounted, you reach around, bringing one hand to ${his} empty stomach and the other to ${his} exposed throat. As you thrust into ${him}, you force ${him} to choke down ${milkTap.slaveName}'s dick, applying pressure to ${his} throat any time ${he} attempts to pull away.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`Moving behind the struggling cocksleeve while teasing your erect cock, you pull ${him} into a comfortable position to rub your dick between ${his} huge butt cheeks. Once you are firmly slotted, you reach around, bringing one hand to ${his} empty stomach and the other to ${his} exposed throat. As you thrust against ${him}, you force ${him} to choke down ${milkTap.slaveName}'s dick, applying pressure to ${his} throat any time ${he} attempts to pull away.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`Moving behind the struggling cocksleeve while teasing your erect cock, you pull ${him} into a comfortable position to fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs, for a lack of anything better. Once you are firmly seated, you reach around, bringing one hand to ${his} empty stomach and the other to ${his} exposed throat. As you thrust against ${him}, you force ${him} to choke down ${milkTap.slaveName}'s dick, applying pressure to ${his} throat any time ${he} attempts to pull away.`);
+			} else {
+				r.push(`Moving behind the struggling cocksleeve while teasing your erect cock, you find a distinct lack of ways to use ${him} to get off.`);
+				if (V.PC.dick !== 0) {
+					r.push(`You settle for rubbing your erection against ${his} back,`);
+				} else {
+					r.push(`You settle for a vibrating dildo stuck deep into your pussy,`);
+				}
+				r.push(`you'll need both hands to fondle your toy. Once you are positioned, you reach around, bringing one hand to ${his} empty stomach and the other to ${his} exposed throat. As you masturbate, you force ${him} to choke down ${milkTap.slaveName}'s dick, applying pressure to ${his} throat any time ${he} attempts to pull away.`);
+			}
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers and the seemingly endless supply of ejaculate from ${milkTap.slaveName}.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers and the seemingly endless supply of ejaculate from ${milkTap.slaveName}.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers and the seemingly endless supply of ejaculate from ${milkTap.slaveName}.`);
+			}
+			r.push(`Standing and releasing ${him} from ${milkTap.slaveName}, gives you a lovely sight of ${slave.slaveName}'s`);
+
+			if (slave.inflation === 3) {
+				r.push(`taut, round belly`);
+			} else if (slave.inflation === 2) {
+				r.push(`rounded, jiggling belly`);
+			} else {
+				r.push(`distended, sloshing belly`);
+			}
+			r.push(`quivering as ${he} comes down from ${his} own forced climax${(V.PC.dick !== 0 && (canDoAnal(slave) || canDoVaginal(slave))) ? `, cum leaking from both ends` : ``}. Both slaves <span class="mediumorchid">resent</span> what you made them do and <span class="gold">fear you</span> as a result.`);
+			slave.devotion -= 5;
+			slave.trust -= 5;
+			milkTap.devotion -= 5;
+			milkTap.trust -= 5;
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`<span class="mediumorchid">especially,</span> having just <span class="lime">lost ${his} virginity</span> in such a demeaning manner.`);
+				slave.devotion -= 5;
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`<span class="mediumorchid">especially,</span> having just <span class="lime">lost ${his} anal virginity</span> in such a demeaning manner.`);
+				slave.devotion -= 5;
+				slave.anus = 1;
+			}
+		} else if ((milkTap.devotion < -20)) {
+			r.push(`Since your sperm tank is restrained, you order the more obedient ${slave.slaveName} to enjoy ${himself} with ${milkTap.slaveName}'s dick. As ${he} teases and licks, you can't help but notice the tantalizing way ${he} wiggles ${his} rear.`);
+			if (canDoVaginal(slave)) {
+				if (V.PC.dick === 0) {
+					r.push(`Donning a strap-on,`);
+				} else {
+					r.push(`Stroking your stiffening cock,`);
+				}
+				r.push(`you wait for the perfect moment and mount ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy, doggy style.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				if (V.PC.dick === 0) {
+					r.push(`Donning a strap-on,`);
+				} else {
+					r.push(`Stroking your stiffening cock,`);
+				}
+				r.push(`you wait for the perfect moment and mount ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`asshole, doggy style.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`Stroking your stiffening cock, you wait for the perfect moment and slip your dick between ${his} huge butt cheeks.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`Stroking your stiffening cock, you wait for the perfect moment, hoist up ${his} rear and slip your dick between ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs.`);
+			} else {
+				r.push(`As you watch ${his} butt, it becomes clear just how few ways there are to use ${him} to get off.`);
+				if (V.PC.dick !== 0) {
+					r.push(`You settle for rubbing your erection against ${his} back,`);
+				} else {
+					r.push(`You settle for a vibrating dildo stuck deep into your pussy,`);
+				}
+				r.push(`you'll need both hands to fondle your toy.`);
+			}
+			r.push(`You wrap your arms around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with ejaculate.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers.`);
+			}
+			r.push(`Only once your weight is removed from the squirming cum balloon is ${he} allowed to pull off of the <span class="mediumorchid">exhausted ${milkTap.slaveName}'s</span> cock and catch ${his} breath.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${His} senses were so overwhelmed, ${he} didn't even notice you <span class="lime">broke in ${his} pussy.</span>`);
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${His} senses were so overwhelmed, ${he} didn't even notice you <span class="lime">broke in ${his} anus.</span>`);
+				slave.anus = 1;
+			}
+			r.push(`${He} gives the shaking ${milkTap.slaveName} an apologetic look before taking a seat. The poor slave isn't used to this yet and <span class="gold">is terrified of your willingness</span> to take what you want from your slaves.`);
+			milkTap.devotion -= 5;
+			milkTap.trust -= 5;
+		} else if ((milkTap.fetish === "cumslut") && (milkTap.fetishStrength > 60) && (milkTap.devotion > 20) && (slave.devotion < -20)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} is forced to suck ${milkTap.slaveName}'s dick. With every thrust into the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} is forced to suck ${milkTap.slaveName}'s dick. With every thrust into the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can rub your dick between ${his} huge butt cheeks while ${he} is forced to suck ${milkTap.slaveName}'s dick. With every thrust against the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You position the restrained ${slave.slaveName} so that you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs while ${he} is forced to suck ${milkTap.slaveName}'s dick. With every thrust against the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+			} else {
+				r.push(`You position ${slave.slaveName} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} is forced to suck ${milkTap.slaveName}'s dick, since ${he} lacks any better way to please you. With every thrust against the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with ejaculate and place your other hand to ${milkTap.slaveName}'s swollen testicles, knowing just how much ${he2} loves to jettison cum.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers`);
+			}
+			r.push(`and ${milkTap.slaveName} even more. ${He2} is semi-conscious, drooling in <span class="hotpink">pleasure and satisfaction,</span> by the time you release the bloated ${slave.slaveName} from ${his} harness. Patting ${his2} spasming, dribbling cock, you know ${he2}'ll come out of it and be eagerly begging you for another slave to fuck soon. ${slave.slaveName}, on the other hand, is regarding ${his} swollen stomach <span class="mediumorchid">with disgust</span> and <span class="gold">fear</span> of your power over ${him}.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${He} <span class="mediumorchid">hates you so much more</span> that you <span class="lime">broke in ${his} virgin pussy.</span>`);
+				slave.vagina = 1;
+				slave.devotion -= 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${He} <span class="mediumorchid">hates you so much more</span> that you <span class="lime">broke in ${his} virgin anus.</span>`);
+				slave.anus = 1;
+				slave.devotion -= 1;
+			}
+			slave.devotion -= 5;
+			slave.trust -= 5;
+			milkTap.devotion += 4;
+		} else if ((slave.devotion <= 20) || (milkTap.devotion <= 20)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} sucks ${milkTap.slaveName}'s cock. With every thrust into the squirming slave, you push ${milkTap.slaveName}'s cock deeper down ${his} throat, giving ${milkTap.slaveName}'s orgasms a straight shot into the moaning slave's gullet.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} sucks ${milkTap.slaveName}'s cock. With every thrust into the squirming slave, you push ${milkTap.slaveName}'s cock deeper down ${his} throat, giving ${milkTap.slaveName}'s orgasms a straight shot into the moaning slave's gullet.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You order ${slave.slaveName} to position ${his} ass so you can rub your dick between ${his} huge butt cheeks while ${he} sucks ${milkTap.slaveName}'s cock. With every thrust against the squirming slave, you push ${milkTap.slaveName}'s cock deeper down ${his} throat, giving ${milkTap.slaveName}'s orgasms a straight shot into the moaning slave's gullet.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You order ${slave.slaveName} to position ${his} ass so you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs while ${he} sucks ${milkTap.slaveName}'s cock. With every thrust against the squirming slave, you push ${milkTap.slaveName}'s cock deeper down ${his} throat, giving ${milkTap.slaveName}'s orgasms a straight shot into the moaning slave's gullet.`);
+			} else {
+				r.push(`You order ${slave.slaveName} to position ${himself} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} is forced to suck ${milkTap.slaveName}'s dick, since ${he} lacks any better way to please you. With every thrust against the squirming slave, you force the moaning ${milkTap.slaveName}'s cock deep into ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with ejaculate and place your other hand to ${milkTap.slaveName}'s balls, planning to coax even stronger orgasms out of ${him2}.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting. Neither slave seems to have truly enjoyed it, instead opting to just get it over with, though ${milkTap.slaveName} makes sure to thank ${slave.slaveName} for dealing with ${his} pent up loads.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				slave.vagina = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				slave.anus = 1;
+			}
+		} else if ((slave.devotion <= 50) || (milkTap.devotion <= 50)) {
+			if (canDoVaginal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} sucks ${milkTap.slaveName}'s cock. ${He} submissively obeys. With every thrust into the moaning slave, you push ${milkTap.slaveName}'s dick deeper down ${his} throat.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can penetrate ${his}`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`while ${he} sucks ${milkTap.slaveName}'s cock. ${He} submissively obeys. With every thrust into the moaning slave, you push ${milkTap.slaveName}'s dick deeper down ${his} throat.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can rub your dick between ${his} huge butt cheeks while ${he} sucks ${milkTap.slaveName}'s cock. ${He} submissively obeys. With every thrust against the moaning slave, you push ${milkTap.slaveName}'s dick deeper down ${his} throat.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You order ${slave.slaveName} to lift ${his} ass so you can fuck ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs while ${he} sucks ${milkTap.slaveName}'s cock. ${He} submissively obeys. With every thrust against the moaning slave, you push ${milkTap.slaveName}'s dick deeper down ${his} throat.`);
+			} else {
+				r.push(`You order ${slave.slaveName} to position ${himself} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} sucks ${milkTap.slaveName}'s cock, since ${he} lacks any better way to please you. ${He} submissively obeys. With every thrust against the moaning slave, you push ${milkTap.slaveName}'s dick deeper down ${his} throat.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with ejaculate and place your other hand to ${milkTap.slaveName}'s balls, knowing just how much ${he2} gets backed up.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting. Both slaves enjoyed their union, though ${milkTap.slaveName} even more so after that many orgasms.`);
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`feels <span class="hotpink">closer to you</span> after losing ${his} virginity to you.`);
+				slave.vagina = 1;
+				slave.devotion += 5;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`feels <span class="hotpink">closer to you</span> after losing ${his} anal virginity to you.`);
+				slave.anus = 1;
+				slave.devotion += 5;
+			}
+		} else {
+			r.push(`${slave.slaveName}`);
+			r.push(`eagerly lifts ${his} ass and jiggles it seductively as ${he} sucks the moaning slut.`);
+			if (canDoVaginal(slave)) {
+				r.push(`You know that signal, so you hilt yourself in`);
+				if (slave.vagina === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`pussy`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`and begin spitroasting ${him} with ${milkTap.slaveName}. With every thrust into the moaning slave, every participant comes closer to their own climax.`);
+				actX(slave, "vaginal");
+			} else if (canDoAnal(slave)) {
+				r.push(`You know that signal, so you hilt yourself in`);
+				if (slave.anus === 0) {
+					r.push(`virgin`);
+				}
+				r.push(`ass`);
+				if (V.PC.dick === 0) {
+					r.push(`with a strap-on`);
+				}
+				r.push(`and begin spitroasting ${him} with ${milkTap.slaveName}. With every thrust into the moaning slave, every participant comes closer to their own climax.`);
+				actX(slave, "anal");
+			} else if (V.PC.dick !== 0 && slave.butt > 4) {
+				r.push(`You know that signal, but ${he} isn't allowed to get penetrated, so you settle for sticking your dick between ${his} huge butt cheeks and fucking ${him} along with ${milkTap.slaveName}. With every thrust against the moaning slave, both you and ${milkTap.slaveName} come closer to climax.`);
+			} else if (V.PC.dick !== 0 && hasBothLegs(slave)) {
+				r.push(`You know that signal, but ${he} isn't allowed to get penetrated, so you settle for sticking your dick between ${his}`);
+				if (slave.weight > 95) {
+					r.push(`soft`);
+				}
+				r.push(`thighs and fucking ${him} along with ${milkTap.slaveName}. With every thrust against the moaning slave, both you and ${milkTap.slaveName} come closer to climax.`);
+			} else {
+				r.push(`You know that signal, but ${he} isn't allowed to get fucked, so you reposition ${him} so you can rub your`);
+				if (V.PC.dick === 0) {
+					r.push(`clit`);
+				} else {
+					r.push(`dick`);
+				}
+				r.push(`against ${him} while ${he} deepthroats ${milkTap.slaveName}. With every thrust against the moaning slave, both you and ${milkTap.slaveName} come closer to climax.`);
+			}
+			r.push(`You wrap an arm around ${slave.slaveName}'s middle so you may feel ${his} stomach swell with ejaculate and place your other hand to ${milkTap.slaveName}'s breasts to prevent ${him2} from feeling left out from your attention.`);
+
+			if (slave.inflation === 3) {
+				r.push(`You cum multiple times as you feel ${his} belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers.`);
+			} else if (slave.inflation === 2) {
+				r.push(`You cum several times as you feel ${his} belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers.`);
+			} else {
+				r.push(`You cum as you feel ${his} belly slowly round with cum under your molesting fingers.`);
+			}
+			r.push(`When you release ${him} from under your weight, ${he} drops to the ground panting from ${his} meal and from the pleasure you drove into ${him}. Both slaves <span class="hotpink">loved the attention,</span> though ${milkTap.slaveName} even more so after so much relief.`);
+			slave.devotion += 4;
+			milkTap.devotion += 4;
+			if (canDoVaginal(slave) && (slave.vagina === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`got off quite strongly from the growing pressure within ${him}, <span class="hotpink">cementing</span> ${his} <span class="lime">first fucking</span> as something special.`);
+				slave.devotion += 4;
+				slave.anus = 1;
+			} else if (canDoAnal(slave) && (slave.anus === 0)) {
+				r.push(`${slave.slaveName}`);
+				r.push(`got off quite strongly from the growing pressure within ${him}, <span class="hotpink">cementing</span> ${his} <span class="lime">first anal</span> as something special.`);
+				slave.devotion += 4;
+				slave.anus = 1;
+			}
+		}
+	}
+
+	App.Events.addNode(el, r, "p");
+	r = [];
+
+	if (_pregDiscovery === 0) {
+		seX(slave, "oral", milkTap, "oral");
+		r.push(`You help the bloated ${slave.slaveName} to the couch to recover and, more importantly, keep ${his} meal down. Only once ${he} has had several minutes to unwind`);
+		if (slave.devotion > 10) {
+			r.push(`and plenty of time to tease you with ${his} swollen body, do you tell`);
+		} else {
+			r.push(`do you order`);
+		}
+		r.push(`${him} to keep drinking from ${milkTap.slaveName} so that ${he} is always filled with`);
+		if (slave.inflation === 3) {
+			r.push(`two gallons`);
+		} else if (slave.inflation === 2) {
+			r.push(`four liters`);
+		} else {
+			r.push(`two liters`);
+		}
+		r.push(`of ${slave.inflationType}. You give ${his}`);
+		if (slave.inflation === 3) {
+			r.push(`taut, firm globe of a belly a pat`);
+		} else if (slave.inflation === 2) {
+			r.push(`wobbly, sloshing belly a pat`);
+		} else {
+			r.push(`distended, sloshing belly a pat`);
+		}
+		r.push(`and send ${him} on ${his} way.`);
+
+		if (slave.inflation === 3) {
+			if (canWalk(slave) || (canMove(slave) && slave.rules.mobility === "permissive")) {
+				r.push(`${He} gingerly leaves your office, massaging ${his} over-stuffed belly as ${he} goes.`);
+			} else {
+				r.push(`${His} belly is so taut it barely wobbles at all as ${he} is helped from your office.`);
+			}
+			r.push(`Being filled so full <span class="health.dec">surely had negative effects</span> on ${his} health.`);
+			healthDamage(slave, 1);
+		} else if (slave.inflation === 2) {
+			if (canWalk(slave) || (canMove(slave) && slave.rules.mobility === "permissive")) {
+				r.push(`${He} gingerly leaves your office, massaging ${his} stuffed belly as ${he} goes.`);
+			} else {
+				r.push(`${His} belly wobbles heavily as ${he} is helped from your office.`);
+			}
+		} else if (slave.inflation === 1) {
+			if (canWalk(slave) || (canMove(slave) && slave.rules.mobility === "permissive")) {
+				r.push(`${He} gingerly leaves your office, massaging ${his} distended belly as ${he} goes.`);
+			} else {
+				r.push(`${His} belly wobbles as ${he} is helped from your office.`);
+			}
+		}
+		App.Events.addNode(el, r, "p");
+		r = [];
+		if (milkTap.fuckdoll === 0) {
+			r.push(`Once ${he} is gone, you see to it that the contented ${milkTap.slaveName} is helped back to ${his2} assignment, but only after ${his2} dribbling`);
+			if (slave.inflationType === "milk") {
+				r.push(`teats are dealt with,`);
+			} else {
+				r.push(`cock is dealt with,`);
+			}
+			r.push(`causing the waiting servant to gulp nervously at what that may entail.`);
+		} else {
+			r.push(`Once ${he} is gone, you see to it that the dribbling Fuckdoll is cleaned up and returned to ${his2} proper place, but only after ${his2} leaking`);
+			if (slave.inflationType === "milk") {
+				r.push(`teats are dealt with,`);
+			} else {
+				r.push(`cock is dealt with,`);
+			}
+			r.push(`causing the waiting servant to gulp nervously at what that may entail.`);
+		}
+	} else {
+		if (milkTap.fuckdoll === 0) {
+			r.push(`With ${slave.slaveName} unable to continue, you are left with the backed up ${milkTap.slaveName} to deal with. ${He2}'ll have to figure out some other way to relieve ${himself2} as ${he2} is helped back to ${his2} assignment.`);
+		} else {
+			r.push(`With ${slave.slaveName} unable to continue, you are left with the backed up ${milkTap.slaveName} to deal with. Hopefully ${he2} doesn't leak too much as ${he2} waits for further use.`);
+		}
+	}
+	App.Events.addNode(el, r, "p");
+	SetBellySize(slave);
+	return el;
+};
diff --git a/src/interaction/policies/policies.js b/src/interaction/policies/policies.js
index 3192492153e0961edebb26855fe63ae65f567439..429fdf50a6ac5dc3cfeb1fc04c1c04dc752b7887 100644
--- a/src/interaction/policies/policies.js
+++ b/src/interaction/policies/policies.js
@@ -133,7 +133,7 @@ globalThis.policy = function(category) {
 		return el;
 
 		/**
-		 * @param {PolicySelector} p  The data object that describes the policy being considered.
+		 * @param {PolicySelector} p The data object that describes the policy being considered.
 		 * @returns {Node} Link to repeal.
 		 */
 		function repeal(p) {
diff --git a/src/interaction/siModify.js b/src/interaction/siModify.js
index 21effcd53d69fc9ff12bf7c551332e9173387727..5d603360c88de7d7163387fc6b58b219dc5b4520 100644
--- a/src/interaction/siModify.js
+++ b/src/interaction/siModify.js
@@ -1,6 +1,6 @@
 /**
  * @param {App.Entity.SlaveState} slave
- * @returns {Node}
+ * @returns {DocumentFragment}
  */
 App.UI.SlaveInteract.modify = function(slave) {
 	const {he, his} = getPronouns(slave);
@@ -15,7 +15,7 @@ App.UI.SlaveInteract.modify = function(slave) {
 	}
 
 	/**
-	 * Create a link with a note to send a  slave to a specific room
+	 * Create a link with a note to send a slave to a specific room
 	 * @param {Node} c
 	 * @param {string} caption
 	 * @param {string} passage
@@ -34,7 +34,6 @@ App.UI.SlaveInteract.modify = function(slave) {
 
 	makeRoomLink(el, "Body mod studio", "Body Modification", ' Mark your slave with piercings, tattoos, brands or even scars.',
 		() => {
-			V.degradation = 0;
 			V.tattooChoice = undefined;
 		},
 	);
diff --git a/src/interaction/siNavigation.js b/src/interaction/siNavigation.js
index 328123adbc1d207757c706b7d781c68154ccc8d0..78190796d7458d67357bb7ebd0f9da227c2155db 100644
--- a/src/interaction/siNavigation.js
+++ b/src/interaction/siNavigation.js
@@ -25,12 +25,7 @@ App.UI.SlaveInteract.navigation = function(slave) {
 	centerSpan.classList.add("interact-name");
 
 	App.UI.DOM.appendNewElement("span", centerSpan, slave.slaveName, "slave-name");
-
-	const favSpan = document.createElement("span");
-	favSpan.id = "fav-span";
-	favSpan.append(favLink());
-
-	centerSpan.append(" ", favSpan);
+	centerSpan.append(" ", App.UI.favoriteToggle(slave));
 	p.append(centerSpan);
 
 	const nextSpan = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Next", "Slave Interact",
@@ -40,22 +35,4 @@ App.UI.SlaveInteract.navigation = function(slave) {
 	App.UI.DOM.appendNewElement("span", p, App.UI.Hotkeys.hotkeys("next-slave"), "hotkey");
 
 	return p;
-
-	function favLink() {
-		if (V.favorites.includes(slave.ID)) {
-			const link = App.UI.DOM.link(String.fromCharCode(0xe800), () => {
-				V.favorites.delete(slave.ID);
-				App.UI.DOM.replace("#fav-span", favLink());
-			});
-			link.classList.add("icons", "favorite");
-			return link;
-		} else {
-			const link = App.UI.DOM.link(String.fromCharCode(0xe801), () => {
-				V.favorites.push(slave.ID);
-				App.UI.DOM.replace("#fav-span", favLink());
-			});
-			link.classList.add("icons", "not-favorite");
-			return link;
-		}
-	}
 };
diff --git a/src/interaction/siRules.js b/src/interaction/siRules.js
index 36e60ce7c78167a305fae5064d9f92661ac6933a..b5e02a194109390fe129440b443977390d131e8e 100644
--- a/src/interaction/siRules.js
+++ b/src/interaction/siRules.js
@@ -21,9 +21,9 @@ App.UI.SlaveInteract.rules = function(slave) {
 
 	function updateBreederLink(breeder, exclude) {
 		p = document.createElement('p');
-		const exempt = slave[exclude] ? "Exempt" : "Include";
+		const exempt = slave[exclude] ? "Include" : "Exempt";
 
-		p.append(`Will be bred by ${breeder} when fertile. `);
+		p.append(`Will ${slave[exclude] ? "not " : ""}be bred by ${breeder} when fertile. `);
 		p.append(
 			App.UI.DOM.link(`${exempt} ${him}`, () => {
 				slave[exclude] = slave[exclude] ^ 1; // switch 0 and 1
@@ -143,7 +143,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 		div.append("Sleep rules: ");
 		if ([Job.NURSE, Job.HEADGIRL, Job.TEACHER, Job.STEWARD, Job.MATRON, Job.FARMER, Job.MADAM, Job.WARDEN, Job.DJ, Job.MILKMAID].includes(slave.assignment)) {
 			App.UI.DOM.appendNewElement("span", div, ` ${His} sleeping schedule is managed by ${his} assignment.`, "note");
-		} else if ([Job.QUARTER, Job.DAIRY, Job.FUCKTOY, Job.CLUB, Job.PUBLIC, Job.FARMYARD, Job.WHORE, Job.GLORYHOLE].includes(slave.assignment) || (V.dairyRestraintsSetting < 2 && slave.assignment === Job.DAIRY)) {
+		} else if ([Job.GLORYHOLE, Job.FARMYARD, Job.CLUB, Job.PUBLIC, Job.WHORE, Job.BROTHEL, Job.SUBORDINATE, Job.HOUSE, Job.QUARTER, Job.FUCKTOY, Job.MASTERSUITE, Job.MILKED].includes(slave.assignment) || (V.dairyRestraintsSetting < 2 && slave.assignment === Job.DAIRY)) {
 			choices = [
 				{value: "none"},
 				{value: "cruel"},
diff --git a/src/interaction/siUtilities.js b/src/interaction/siUtilities.js
index 55bd7902426f6cdba60dfb7262d838f0a9ea670c..c95185faa662bf01efbf4e2f5def262bd2095d7f 100644
--- a/src/interaction/siUtilities.js
+++ b/src/interaction/siUtilities.js
@@ -26,7 +26,7 @@ App.UI.SlaveInteract.placeInLine = function(slave) {
 
 /** @typedef RowItem
  * @type {object}
- * @property {string} [FS] - FS requirement, if any
+ * @property {FC.FutureSociety} [FS] - FS requirement, if any
  * @property {string} [text] - link text
  * @property {object} [updateSlave] - properties to be merged onto the slave
  * @property {object} [update] - properties to be merged into global state
diff --git a/src/interaction/siWardrobe.js b/src/interaction/siWardrobe.js
index 553180d0779c0dd76a72129f53b3a2a5df4f4acd..68b650df96294f3f58080236e807a20e0fbca294 100644
--- a/src/interaction/siWardrobe.js
+++ b/src/interaction/siWardrobe.js
@@ -37,15 +37,14 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 			let label = document.createElement('div');
 			label.append(`Clothes: `);
 
-			let choice = document.createElement('span');
+			let choice = App.UI.DOM.disabledLink(`${slave.clothes}`, [clothTooltip(`${slave.clothes}`)]);
 			choice.style.fontWeight = "bold";
-			choice.textContent = (`${slave.clothes} `);
 			label.appendChild(choice);
 
 			// Choose her own
 			if (slave.clothes !== `choosing her own clothes`) {
 				let choiceOptionsArray = [];
-				choiceOptionsArray.push({text: `Let ${him} choose`, updateSlave: {clothes: `choosing her own clothes`, choosesOwnClothes: 1}});
+				choiceOptionsArray.push({text: ` Let ${him} choose`, updateSlave: {clothes: `choosing her own clothes`, choosesOwnClothes: 1}});
 				label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "clothes", false, refresh));
 			}
 			el.appendChild(label);
@@ -115,15 +114,14 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Collar: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.collar}`, [clothTooltip(`${slave.collar}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.collar} `);
 		label.appendChild(choice);
 
 		// Choose her own
 		if (slave.collar !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {collar: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {collar: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "collar", false, refresh));
 		}
 
@@ -179,15 +177,14 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Mask: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.faceAccessory} `, [clothTooltip(`${slave.faceAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.faceAccessory} `);
 		label.appendChild(choice);
 
 		// Choose her own
 		if (slave.faceAccessory !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {faceAccessory: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {faceAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "faceAccessory", false, refresh));
 		}
 
@@ -235,15 +232,14 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Gag: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.mouthAccessory}`, [clothTooltip(`${slave.mouthAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.mouthAccessory} `);
 		label.appendChild(choice);
 
 		// Choose her own
 		if (slave.mouthAccessory !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {mouthAccessory: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {mouthAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "mouthAccessory", false, refresh));
 		}
 
@@ -280,16 +276,15 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Arm accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.armAccessory}`, [clothTooltip(`${slave.armAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.armAccessory} `);
 		label.appendChild(choice);
 
 		let array = [];
 
 		// Choose her own
 		if (slave.armAccessory !== "none") {
-			array.push({text: `None`, updateSlave: {armAccessory: `none`}});
+			array.push({text: ` None`, updateSlave: {armAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(array, slave, "armAccessory", false, refresh));
 		}
 
@@ -313,9 +308,8 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Shoes: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.shoes}`, [clothTooltip(`${slave.shoes}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.shoes} `);
 		label.appendChild(choice);
 
 		/* We have "barefoot" in App.Data.slaveWear to cover for this
@@ -367,16 +361,15 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Leg accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.legAccessory}`, [clothTooltip(`${slave.legAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.legAccessory} `);
 		label.appendChild(choice);
 
 		let array = [];
 
 		// Choose her own
 		if (slave.legAccessory !== "none") {
-			array.push({text: `None`, updateSlave: {legAccessory: `none`}});
+			array.push({text: ` None`, updateSlave: {legAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(array, slave, "legAccessory", false, refresh));
 		}
 
@@ -397,7 +390,7 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 	function bellyAccessory() {
 		// <<waistDescription>><<= App.Desc.pregnancy($activeSlave)>><<clothingCorsetDescription>>
 		let choiceOptionsArray = [];
-		choiceOptionsArray.push({text: `None`, updateSlave: {bellyAccessory: `none`}});
+		choiceOptionsArray.push({text: ` None`, updateSlave: {bellyAccessory: `none`}});
 
 		let optionsArray = [];
 
@@ -421,9 +414,8 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Belly accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.bellyAccessory}`, [clothTooltip(`${slave.bellyAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.bellyAccessory} `);
 		label.appendChild(choice);
 
 		// Choose her own
@@ -455,14 +447,13 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Anal accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.buttplug}`, [clothTooltip(`${slave.buttplug}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.buttplug} `);
 		label.appendChild(choice);
 
 		if (slave.buttplug !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {buttplug: `none`, buttplugAttachment: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {buttplug: `none`, buttplugAttachment: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "buttplug", false, refresh));
 		}
 		el.appendChild(label);
@@ -503,14 +494,13 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Anal accessory attachment: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.buttplugAttachment}`, [clothTooltip(`${slave.buttplugAttachment}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.buttplugAttachment} `);
 		label.appendChild(choice);
 
 		if (slave.buttplugAttachment !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {buttplugAttachment: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {buttplugAttachment: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "buttplugAttachment", false, refresh));
 		}
 		el.appendChild(label);
@@ -550,14 +540,13 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Vaginal accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.vaginalAccessory}`, [clothTooltip(`${slave.vaginalAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.vaginalAccessory} `);
 		label.appendChild(choice);
 
 		if (slave.vaginalAccessory !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {vaginalAccessory: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {vaginalAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "vaginalAccessory", false, refresh));
 		}
 		el.appendChild(label);
@@ -598,14 +587,13 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Vaginal accessory attachment: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.vaginalAttachment}`, [clothTooltip(`${slave.vaginalAttachment}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.vaginalAttachment} `);
 		label.appendChild(choice);
 
 		if (slave.vaginalAttachment !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {vaginalAttachment: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {vaginalAttachment: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "vaginalAttachment", false, refresh));
 		}
 		el.appendChild(label);
@@ -644,14 +632,13 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Dick accessory: `);
 
-		let choice = document.createElement('span');
+		let choice = App.UI.DOM.disabledLink(`${slave.dickAccessory}`, [clothTooltip(`${slave.dickAccessory}`)]);
 		choice.style.fontWeight = "bold";
-		choice.textContent = (`${slave.dickAccessory} `);
 		label.appendChild(choice);
 
 		if (slave.dickAccessory !== `none`) {
 			let choiceOptionsArray = [];
-			choiceOptionsArray.push({text: `None`, updateSlave: {dickAccessory: `none`}});
+			choiceOptionsArray.push({text: ` None`, updateSlave: {dickAccessory: `none`}});
 			label.appendChild(App.UI.SlaveInteract.generateRows(choiceOptionsArray, slave, "dickAccessory", false, refresh));
 		}
 		el.appendChild(label);
@@ -689,35 +676,37 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		let label = document.createElement('div');
 		label.append(`Chastity devices: `);
 
-		let choice = document.createElement('span');
-		choice.style.fontWeight = "bold";
+		let chasCho = "";
 		if (slave.choosesOwnChastity === 1) {
-			choice.textContent = `choosing ${his} own chastity `;
+			chasCho = `choosing ${his} own chastity`;
 		} else if (slave.chastityAnus === 1 && slave.chastityPenis === 1 && slave.chastityVagina === 1) {
-			choice.textContent = `full chastity `;
+			chasCho = `full chastity`;
 		} else if (slave.chastityPenis === 1 && slave.chastityVagina === 1) {
-			choice.textContent = `genital chastity `;
+			chasCho = `genital chastity`;
 		} else if (slave.chastityAnus === 1 && slave.chastityPenis === 1) {
-			choice.textContent = `combined chastity cage `;
+			chasCho = `combined chastity cage`;
 		} else if (slave.chastityAnus === 1 && slave.chastityVagina === 1) {
-			choice.textContent = `combined chastity belt `;
+			chasCho = `combined chastity belt`;
 		} else if (slave.chastityVagina === 1) {
-			choice.textContent = `chastity belt `;
+			chasCho = `chastity belt`;
 		} else if (slave.chastityPenis === 1) {
-			choice.textContent = `chastity cage `;
+			chasCho = `chastity cage`;
 		} else if (slave.chastityAnus === 1) {
-			choice.textContent = `anal chastity `;
+			chasCho = `anal chastity`;
 		} else if (slave.chastityAnus === 0 && slave.chastityPenis === 0 && slave.chastityVagina === 0) {
-			choice.textContent = `none `;
+			chasCho = `none `;
 		} else {
-			choice.textContent = `THERE HAS BEEN AN ERROR `;
+			chasCho = `THERE HAS BEEN AN ERROR `;
 		}
+
+		let choice = App.UI.DOM.disabledLink(chasCho, [clothTooltip(chasCho)]);
+		choice.style.fontWeight = "bold";
 		label.appendChild(choice);
 
 		if (slave.chastityAnus !== 0 || slave.chastityPenis !== 0 || slave.chastityVagina !== 0) {
 			let choiceOptionsArray = [];
 			choiceOptionsArray.push({
-				text: `None`,
+				text: ` None`,
 				updateSlave: {
 					choosesOwnChastity: 0,
 					chastityAnus: 0,
@@ -797,4 +786,195 @@ App.UI.SlaveInteract.wardrobe = function(slave) {
 		App.Art.refreshSlaveArt(slave, 3, "art-frame");
 		jQuery("#content-appearance").empty().append(App.UI.SlaveInteract.wardrobe(slave));
 	}
+
+	/**
+	 * Figure out a tooltip text to use based on clothing name.
+	 * Described effects are mainly from saClothes.js some are from saLongTermMentalEffects.js or saLongTermPhysicalEffects.js
+	 * Potential fetish relevations are not mentioned.
+	 * Chastity options could mention that at least fucktoys can appreciate maintaining their virginity but I assume just choosing a hole to focus on has the same effect so it's not really a clothing effect.
+	 * what's the word for below 20 devotion slaves? Disobedient?
+	 * Also accepting is a bit weird for ones above, I think I've seen obedient being used instead.
+	 */
+	function clothTooltip(cloth) {
+		let Cloth = capFirstChar(cloth);
+
+		switch (cloth) {
+			/* nice clothes without specific effects(besides FS or being slutty/humiliating/modest) are handled at the end */
+			case "choosing her own clothes":
+			case "choosing his own clothes":
+				return Cloth + ", increases or greatly reduces devotion based on whether the slave is obedient(devotion at accepting or higher).";
+			case "no clothing":
+				return Cloth + " increases devotion for resistant humiliations fetishists and nymphos.";
+			case "a penitent nuns habit":
+				return Cloth + " increases devotion and fear but damages health, may cause masochism.";
+			case "restrictive latex":
+				return Cloth + ", it's modest and humiliating, increases fear and devotion for resistant slaves and just devotion for obedient, non-terrified submissives.";
+			case "shibari ropes":
+				return Cloth + ", it's humiliating, increases fear and devotion for resistant slaves and just devotion for obedient, non-terrified submissives.";
+			case "uncomfortable straps":
+				return Cloth + ", it's humiliating, increase devotion and fear for slaves who are disobedient and not terrified. Masochists who are at least ambivalent gain devotion, may also cause masochism.";
+			case "chains":
+				return Cloth + " increase devotion and fear for slaves who are disobedient and not terrified. Masochists who are at least ambivalent gain devotion, may also cause masochism.";
+
+			case "an apron":
+				return Cloth + ", nice clothing that increases just devotion for submissives, humiliation fetishists and visibly pregnant pregnancy fetishists regardless of devotion level.";
+			case "a monokini":
+				return Cloth + ", nice clothing that boob fetishists enjoy.";
+
+			case "none":
+				return "No effect one way or another.";
+
+			case "heavy gold":
+			case "ancient Egyptian":
+			case "bowtie":
+			case "neck tie":
+			case "nice retirement counter":
+			case "pretty jewelry":
+			case "satin choker":
+			case "silk ribbon":
+			case "stylish leather":
+				return Cloth + " on obedient slaves reduces fear, on non-obedient ones reduces fear a lot and devotion somewhat.";
+			case "preg biometrics":
+				return Cloth + " increases devotion for those who have pregnancy fetish while fertile or a humiliation fetish. For others obedient ones gain devotion, ambivalent ones gain fear and devotion and resistant ones lose devotion and gain fear.";
+			case "bell collar":
+				return Cloth + " on non-obedient slaves reduces fear a lot and devotion somewhat.";
+			case "leather with cowbell":
+				return Cloth + " on obedient slaves with boob fetish increases devotion, on disobedient slaves reduces fear a lot and devotion somewhat.";
+
+			case "tight steel":
+			case "uncomfortable leather":
+			case "neck corset":
+			case "cruel retirement counter":
+				return Cloth + " increases fear for non-obedient slaves.";
+			case "shock punishment":
+				return Cloth + " for non-obedient slaves increases fear a great deal and reduces devotion, for resistant non-odd slaves it affects both much more a single time and gives the odd flaw.";
+
+			case "cat ears":
+				return Cloth + " increase fear and devotion for disobedient slaves, submissives and nymphos also enjoy wearing one.";
+			case "porcelain mask":
+				return Cloth + " obscures the face, increases fear and devotion for disobedient slaves, submissives and nymphos also enjoy wearing one.";
+
+			case "ball gag":
+			case "bit gag":
+			case "ring gag":
+				return Cloth + " increases fear and devotion for disobedient slaves, submissives and nymphos also enjoy wearing one.";
+			case "dildo gag":
+			case "massive dildo gag":
+				return Cloth + " increases oral skill up to a point and causes fear for disobedient slaves.";
+
+			case "hand gloves":
+			case "elbow gloves":
+				return Cloth + " have no effect one way or another.";
+
+			case "flats":
+				return Cloth + " have no effect one way or another.";
+			case "heels":
+			case "boots":
+			case "platform heels":
+				return Cloth + " increase height, resistant slaves with natural legs resent wearing them.";
+			case "pumps":
+			case "platform shoes":
+				return Cloth + " increase height.";
+			case "extreme heels":
+			case "extreme platform heels":
+				return Cloth + " increase height, slaves with natural legs who are resistant resent and fear wearing them while non-resistant ones become more fearful(unless masochistic) and obedient.";
+
+			case "short stockings":
+				return Cloth + " have no effect one way or another.";
+			case "long stockings":
+				return Cloth + " have no effect one way or another.";
+
+			case "a tight corset":
+				return Cloth + " slowly narrows the waist into wispy one.";
+			case "an extreme corset":
+				return Cloth + " narrows the waist up to absurd level, painfully, if waist is feminine or wider(scaring and increasing obedience on resistant slaves), but risks miscarriage if a pregnant belly becomes too big";
+			case "a supportive band":
+				return Cloth + " reduces chance of miscarriage.";
+			case "a small empathy belly":
+			case "a medium empathy belly":
+			case "a large empathy belly":
+			case "a huge empathy belly":
+				return Cloth + " strenghtens or removes(a weak) pregnancy fetish and affects devotion in various ways depending on devotion, fertility and having a pregnancy fetish or breeder flaw.";
+
+			case "bullet vibrator":
+				return Cloth + " increases devotion but weakens fetish and libido.";
+			case "smart bullet vibrator":
+				return Cloth + " increases devotion and affects a specific fetish, attraction or sex drive.";
+			case "dildo":
+				return Cloth + " stretches vagina from virgin to tight, might remove hatred of penetration.";
+			case "long dildo":
+				return Cloth + " stretches vagina from virging to tight, might remove hatred of penetration. Makes size queens happy while others less trusting.";
+			case "large dildo":
+			case "long, large dildo":
+				return Cloth + " stretches vagina into a loose one, on a tight vagina increases obedience and fear.";
+			case "huge dildo":
+			case "long, huge dildo":
+				return Cloth + " stretches vagina into a cavernous one, on smaller vaginas size queens get much more devoted, masochists and submissives much more devoted and fearful and anyone else becomes much less devoted and trusting. Might cause miscarriage.";
+
+			case "vibrator":
+				return Cloth + " ";
+			case "smart vibrator":
+				return Cloth + " ";
+
+			case "plug":
+				return Cloth + " stretches butthole from virgin to tight, might remove hatred of anal.";
+			case "long plug":
+				return Cloth + " stretches vagina from virging to tight, might remove hatred of penetration. Makes size queens happy.";
+			case "large plug":
+			case "long, large plug":
+				return Cloth + " stretches vagina into a loose one, on a tight vagina increases obedience and fear.";
+			case "huge plug":
+			case "long, huge plug":
+				return Cloth + " stretches vagina into a cavernous one, on smaller vaginas size queens get much more devoted, masochists and submissives much more devoted and fearful and anyone else becomes much less devoted and trusting. Might cause miscarriage.";
+
+			case "tail":
+			case "fox tail":
+			case "cat tail":
+			case "cow tail":
+				return Cloth + " makes it more scary to wear a plug but might give humiliation fetish,";
+
+
+			case "anal chastity":
+				return Cloth + " prevents losing anal virginity.";
+			case "chastity belt":
+				return Cloth + " prevents losing virginity, has various effects, obedient virgins, buttsluts and ones with relatively high sex drive are most affected.";
+			case "":
+			case "combined chastity belt":
+				return Cloth + " prevents losing virginities, has various effects, obedient virgins, buttsluts and ones with relatively high sex drive are most affected.";
+			case "chastity cage":
+				return Cloth + " prevents using penis, has various effects, devotion, trust and sex drive of unresistant slaves with healthy sex drive all suffer from wearing one unless they're a masochist, nympho, neglectful, buttslut, sterile or lack balls.";
+			case "combined chastity cage":
+				return Cloth + " protects both penis and anus from sex, has various effects, devotion and trust and sex drive of unresistant slaves with healthy sex drive all suffer from wearing one unless they're a masochist, nympho, neglectful, buttslut, sterile or lack balls.";
+			case "genital chastity":
+				return Cloth + " protects both penis and vagina from sex, has various effects.";
+			case "full chastity":
+				return Cloth + " protects penis, vagina and anus, has various effects.";
+			case "choosing her own chastity":
+			case "choosing his own chastity":
+			case "choose own chastity":
+				return Cloth + " ";
+			case "revoke choosing own chastity":
+				return Cloth + " ";
+
+			default: {
+				/* assuming nice clothes, could actually add some sort of check to make sure. */
+				/* which clothes have these is decided in miscData.js */
+				let clothTooltip = Cloth + "";
+				if (setup.humiliatingClothes.includes(cloth)) {
+					clothTooltip += ", it's humiliating";
+				}
+				if (setup.sluttyClothes.includes(cloth)) {
+					clothTooltip += ", it's slutty";
+				}
+				if (setup.modestClothes.includes(cloth)) {
+					clothTooltip += ", it's modest";
+				}
+				if (clothTooltip ===  Cloth + "") {
+					clothTooltip += ", it's only nice(meaning non-obedients lose devotion and fear while obedients gain devotion and trust).";
+				}
+				clothTooltip += ".";
+				return clothTooltip;
+			}
+		}
+	}
 };
diff --git a/src/interaction/siWork.js b/src/interaction/siWork.js
index bbb807339aeabe3da5bc2bb9d6696368473db564..b48e346a5c60cc45644866849f42001a7c3532db 100644
--- a/src/interaction/siWork.js
+++ b/src/interaction/siWork.js
@@ -43,7 +43,6 @@ App.UI.SlaveInteract.work = function(slave) {
 	}
 	el.append(p);
 
-	p = document.createElement('p');
 	span = document.createElement('span');
 	span.className = "note";
 	switch (slave.assignment) {
@@ -189,7 +188,7 @@ App.UI.SlaveInteract.work = function(slave) {
 		}
 		if (slave.assignment === Job.SUBORDINATE) {
 			const target = getSlave(slave.subTarget);
-			let linkText = ``;
+			let linkText;
 			if (target) {
 				title.appendChild(document.createTextNode(`Serving ${target.slaveName} exclusively. `));
 				linkText = `Change`;
@@ -451,7 +450,7 @@ App.UI.SlaveInteract.work = function(slave) {
 							fillFaceOptions.push({text: `Two gallons of slave food`, scene: `forceFeeding`, updateSlave: {inflation: 3, inflationType: "food", inflationMethod: 1}});
 						}
 					}
-					fillFaceOptions.push({text: `Get another slave to do it`, goto: `SlaveOnSlaveFeedingWorkAround`});
+					fillFaceOptions.push({text: `Get another slave to do it`, goto: `SlaveOnSlaveFeeding`});
 				}
 			}
 			if (canDoVaginal(slave)) {
diff --git a/src/interaction/slaveInteract.js b/src/interaction/slaveInteract.js
index 0b85f03a0340a1269c12a7ba07df3954d0bc8a89..f7084dd551061b3c05bedc4c16ea37f237eb705c 100644
--- a/src/interaction/slaveInteract.js
+++ b/src/interaction/slaveInteract.js
@@ -70,7 +70,7 @@ App.UI.SlaveInteract.mainPage = function(slave) {
 	 * @typedef {Object} siCategory
 	 * @property {string} title
 	 * @property {string} id
-	 * @property {Node} node
+	 * @property {DocumentFragment|HTMLElement} node
 	 * @property {Function} [onClick]
 	 */
 
@@ -156,6 +156,10 @@ App.UI.SlaveInteract.mainPage = function(slave) {
 
 		return el;
 
+		/**
+		 * @param {siCategory} item
+		 * @returns {HTMLElement}
+		 */
 		function makeTabButton(item) {
 			const btn = document.createElement("button");
 			btn.className = "tab-links";
@@ -173,18 +177,26 @@ App.UI.SlaveInteract.mainPage = function(slave) {
 		}
 	}
 
+	/**
+	 * @returns {DocumentFragment}
+	 */
 	function displayWithoutTabs() {
 		const el = new DocumentFragment();
 		App.Events.drawEventArt(el, slave);
 		for (const item of buttons) {
 			App.UI.DOM.appendNewElement("h2", el, item.title);
-			el.append(item.node);
+			el.append(makeContentSpan(item));
 			if (item.hasOwnProperty("onClick")) {
 				item.onClick();
 			}
 		}
 		return el;
 	}
+
+	/**
+	 * @param {siCategory} item
+	 * @returns {HTMLElement}
+	 */
 	function makeTabContents(item) {
 		const wrapperEl = document.createElement("div");
 		wrapperEl.id = item.id;
@@ -193,14 +205,21 @@ App.UI.SlaveInteract.mainPage = function(slave) {
 		const classEl = document.createElement("div");
 		classEl.classList.add("content");
 
+		classEl.append(makeContentSpan(item));
+		wrapperEl.append(classEl);
+
+		return wrapperEl;
+	}
+
+	/**
+	 * @param {siCategory} item
+	 * @returns {HTMLElement}
+	 */
+	function makeContentSpan(item) {
 		const uniqueContent = document.createElement("span");
 		uniqueContent.id = `content-${item.id}`;
 
 		uniqueContent.append(item.node);
-		classEl.append(uniqueContent);
-		wrapperEl.append(classEl);
-
-
-		return wrapperEl;
+		return uniqueContent;
 	}
 };
diff --git a/src/interaction/slaveOnSlaveFeeding.css b/src/interaction/slaveOnSlaveFeeding.css
new file mode 100644
index 0000000000000000000000000000000000000000..e4fb2cc907eab6d4d2fc07e8aef967eeb0ec919a
--- /dev/null
+++ b/src/interaction/slaveOnSlaveFeeding.css
@@ -0,0 +1,7 @@
+table.slave-on-slave-feeding {
+	width: 90%;
+}
+
+table.slave-on-slave-feeding tr {
+	vertical-align: top;
+}
\ No newline at end of file
diff --git a/src/interaction/slaveOnSlaveFeeding.js b/src/interaction/slaveOnSlaveFeeding.js
new file mode 100644
index 0000000000000000000000000000000000000000..7ca00156ab1acde44f9ad4412b15d24cbc1a8710
--- /dev/null
+++ b/src/interaction/slaveOnSlaveFeeding.js
@@ -0,0 +1,165 @@
+/**
+ * Choose which slave will feed the selected slave
+ * @param {App.Entity.SlaveState} slave
+ */
+App.UI.SlaveInteract.slaveOnSlaveFeedingSelection = function(slave) {
+	const el = document.createElement("span");
+	el.id = "scene";
+
+	el.append(intro());
+
+	tabs();
+
+	return el;
+
+	function intro() {
+		const el = new DocumentFragment();
+		const {his} = getPronouns(slave);
+
+		App.UI.DOM.appendNewElement("div", el, `${slave.slaveName} is prepped to drink ${his} fill; now you must select a slave capable of producing the required amount of milk or ejaculate.`);
+		App.UI.DOM.appendNewElement("h2", el, "Select an eligible slave to serve as the tap");
+		return el;
+	}
+
+	function slaveChoice(inflationType) {
+		const el = new DocumentFragment();
+		const twoLiterSlaves = [];
+		const fourLiterSlaves = [];
+		const eightLiterSlaves = [];
+		const {he, his} = getPronouns(slave);
+
+		const table = document.createElement("table");
+		table.classList.add("slave-on-slave-feeding");
+
+		const header = table.createTHead();
+		table.append(header);
+		el.append(table);
+
+		const columnNames = ["2 Liter", "4 Liter", "8 Liter"];
+		for (const name of columnNames) {
+			header.append(App.UI.DOM.makeElement("th", name));
+		}
+
+		for (const tapSlave of V.slaves) {
+			let output;
+			if (inflationType === "milk") {
+				output = (tapSlave.lactation > 0) ? Math.trunc(milkAmount(tapSlave) / 14) : 0;
+			} else if (inflationType === "cum") {
+				output = (tapSlave.balls > 0 && tapSlave.dick > 0 && tapSlave.chastityPenis !== 1) ? Math.trunc(cumAmount(tapSlave) / 70) : 0;
+			} else {
+				throw `inflationType "${inflationType}" not found`;
+			}
+			if (tapSlave.ID !== slave.ID && (inflationType !== "milk" || tapSlave.nipples !== "fuckable")) {
+				if (output >= 2) {
+					twoLiterSlaves.push(createTapLink(tapSlave, 1, inflationType));
+					if (output >= 4 && slave.pregKnown === 0) {
+						fourLiterSlaves.push(createTapLink(tapSlave, 2, inflationType));
+						if (output >= 8) {
+							eightLiterSlaves.push(createTapLink(tapSlave, 3, inflationType));
+						}
+					}
+				}
+			}
+		}
+
+		if (twoLiterSlaves.length === 0) {
+			twoLiterSlaves.push(App.UI.DOM.makeElement("td", `You have no slaves capable of producing two liters of ${inflationType}.`));
+		} else {
+			if (slave.pregKnown !== 0) {
+				fourLiterSlaves.push(App.UI.DOM.makeElement("td", `Due to ${his} pregnancy, ${he} is incapable of keeping down more than two liters of ${inflationType}.`));
+			} else {
+				if (fourLiterSlaves.length === 0) {
+					fourLiterSlaves.push(App.UI.DOM.makeElement("td", `You have no slaves capable of producing four liters of ${inflationType}.`));
+				}
+				if (eightLiterSlaves.length === 0) {
+					eightLiterSlaves.push(App.UI.DOM.makeElement("td", `You have no slaves capable of producing eight liters of ${inflationType}.`));
+				}
+			}
+		}
+
+		const dataRow = document.createElement("tr");
+		dataRow.append(makeColumn(twoLiterSlaves));
+		dataRow.append(makeColumn(fourLiterSlaves));
+		dataRow.append(makeColumn(eightLiterSlaves));
+		table.append(dataRow);
+
+		return el;
+	}
+
+	/**
+	 * @param {App.Entity.SlaveState} tapSlave
+	 * @param {number} inflation
+	 * @param {FC.InflationLiquid} inflationType
+	 */
+	function createTapLink(tapSlave, inflation, inflationType) {
+		const el = document.createElement("div");
+		el.append(
+			App.UI.DOM.link(
+				tapSlave.slaveName,
+				() => {
+					slave.inflation = inflation;
+					slave.inflationType = inflationType;
+					slave.inflationMethod = 3;
+					jQuery("#scene").empty().append(FSlaveFeed(slave, tapSlave));
+				},
+			)
+		);
+		const relTerm = relativeTerm(slave, tapSlave);
+		if (relTerm) {
+			el.append(` ${relTerm}`);
+		}
+		if (tapSlave.relationshipTarget === slave.ID) {
+			const {wife} = getPronouns(tapSlave);
+			switch (tapSlave.relationship) {
+				case 1:
+					el.append(` friends`);
+					break;
+				case 2:
+					el.append(` best friends`);
+					break;
+				case 3:
+					el.append(` friends with benefits`);
+					break;
+				case 4:
+					el.append(` lover`);
+					break;
+				case 5:
+					el.append(` slave${wife}`);
+					break;
+			}
+		}
+		if (tapSlave.rivalryTarget === getSlave(V.AS).ID) {
+			switch (tapSlave.relationship) {
+				case 1:
+					el.append(`dislikes`);
+					break;
+				case 2:
+					el.append(`rival`);
+					break;
+				case 3:
+					el.append(`bitterly hates`);
+					break;
+			}
+		}
+		return el;
+	}
+
+	function makeColumn(array) {
+		const td = document.createElement("td");
+		for (const cell of array) {
+			td.append(cell);
+		}
+		return td;
+	}
+
+	function tabs() {
+		const tabBar = App.UI.DOM.appendNewElement("div", el, '', "tab-bar");
+		tabBar.append(
+			App.UI.tabBar.tabButton('milk', 'Milk Slaves'),
+			App.UI.tabBar.tabButton('cum', 'Cum Slaves'),
+		);
+
+		el.append(App.UI.tabBar.makeTab('milk', slaveChoice("milk")));
+		el.append(App.UI.tabBar.makeTab('cum', slaveChoice("cum")));
+	}
+};
diff --git a/src/interaction/slaveOnSlaveFeeding.tw b/src/interaction/slaveOnSlaveFeeding.tw
new file mode 100644
index 0000000000000000000000000000000000000000..eb76ca5c03a99a60215323121d4b4953c300fc74
--- /dev/null
+++ b/src/interaction/slaveOnSlaveFeeding.tw
@@ -0,0 +1,5 @@
+:: SlaveOnSlaveFeeding [nobr]
+
+<<set $nextButton = "Back", $nextLink = "Slave Interact">>
+
+<<includeDOM App.UI.SlaveInteract.slaveOnSlaveFeedingSelection(getSlave($AS))>>
diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js
index f0d83440ac5c590323d4e24a640ae73f2efeeba3..2be10f42af25719beac240af7d229d475669a3c6 100644
--- a/src/js/DefaultRules.js
+++ b/src/js/DefaultRules.js
@@ -149,7 +149,7 @@ globalThis.DefaultRules = (function() {
 				if (job.checkRequirements(slave).length !== 0) {
 					// no message to prevent spam
 					removeAssignment();
-				} else if (!job.facility.hasFreeSpace) {
+				} else if (!job.facility.hasFreeSpace && slave.assignment !== rule.setAssignment) {
 					r += getAssignmentDescription({
 						rule, slave, assignmentResult: "unable",
 						append: " because it was full"
@@ -1166,15 +1166,20 @@ globalThis.DefaultRules = (function() {
 	 */
 	function ProcessDrugs(slave, rule) {
 		// First we check AssetGrowthDrugs, then if 1. no growth targets or 2. targets have been hit, we check other drugs.
+		if (slave.indentureRestrictions === 2) {
+			return;
+		}
 		if ((slave.drugs === "super fertility drugs" || slave.drugs === "fertility drugs") && isFertile(slave)) {
 			r += `<br>${slave.slaveName} is on ${slave.drugs} and will not be considered for drug enhancement until that regime is complete.`;
 			ProcessOtherDrugs(slave, rule);
-		} else if (slave.indentureRestrictions > 1 || (rule.growth.boobs === null && rule.growth.butt === null && rule.growth.lips === null && rule.growth.dick === null && rule.growth.balls === null)) {
+			return;
+		} else if (rule.growth.boobs === null && rule.growth.butt === null && rule.growth.lips === null && rule.growth.dick === null && rule.growth.balls === null) {
 			ProcessOtherDrugs(slave, rule);
+			return;
 		}
 
-		/** @typedef {"lips" | "boobs" | "butt" | "dick" | "balls"} DrugTarget */
 
+		/** @typedef {"lips" | "boobs" | "butt" | "dick" | "balls"} DrugTarget */
 		// Asset Growth
 		const growthDrugs = new Set(["breast injections", "breast redistributors", "butt injections", "butt redistributors", "hyper breast injections", "hyper butt injections", "hyper penis enhancement", "hyper testicle enhancement", "intensive breast injections", "intensive butt injections", "intensive penis enhancement", "intensive testicle enhancement", "lip atrophiers", "lip injections", "penis atrophiers", "penis enhancement", "testicle atrophiers", "testicle enhancement"]);
 
@@ -1313,9 +1318,15 @@ globalThis.DefaultRules = (function() {
 					r += `that is the only part of ${his} body that does not meet the targeted size.`;
 				}
 			}
-		} else if (growthDrugs.has(slave.drugs)) {
-			r += `<br>${slave.slaveName}'s body has met all relevant growth targets, so ${his} pharmaceutical regime has been ended.`;
-			ProcessOtherDrugs(slave,rule);
+		} else if (slave.drugs !== rule.drug) {
+			if (growthDrugs.has(slave.drugs)) {
+				r += `<br>${slave.slaveName}'s body has met all relevant growth targets, so ${his} pharmaceutical regime has been ended.`;
+				if (rule.drug === null) {
+					slave.drugs = "no drugs";
+					r += `<br>${slave.slaveName} has been defaulted to ${slave.drugs}`;
+				}
+			}
+			ProcessOtherDrugs(slave, rule);
 		}
 	}
 
@@ -1325,7 +1336,7 @@ globalThis.DefaultRules = (function() {
 	 */
 	function ProcessOtherDrugs(slave, rule) {
 		// Other Drugs
-		if (slave.indentureRestrictions < 2 && rule.drug !== null && slave.drugs !== rule.drug) {
+		if (rule.drug !== undefined && rule.drug !== null && slave.drugs !== rule.drug) {
 			let flag = true;
 			switch (rule.drug) {
 				case "anti-aging cream":
@@ -1589,9 +1600,9 @@ globalThis.DefaultRules = (function() {
 						slave.diet = "fattening";
 						r += `<br>${slave.slaveName} is too skinny so ${his} diet has been set to fattening.`;
 					}
-				} else if (["restricted", "fattening"].includes(slave.diet)){
-						r += `<br>${slave.slaveName} is at the target weight, so ${his} diet has been normalized.`;
-						muscleRule(slave, rule);
+				} else if (["restricted", "fattening"].includes(slave.diet)) {
+					r += `<br>${slave.slaveName} is at the target weight, so ${his} diet has been normalized.`;
+					muscleRule(slave, rule);
 				} else {
 					muscleRule(slave, rule);
 				}
@@ -1646,7 +1657,7 @@ globalThis.DefaultRules = (function() {
 					slave.diet = "fattening";
 					r += `<br>${slave.slaveName} is on drugs designed to expand major body parts, so ${he}'s been put on a fattening diet to provide ${his} body as much fuel for growth as possible.`;
 				}
-			}  else if ((rule.diet === "XX")) {
+			} else if ((rule.diet === "XX")) {
 				if ((slave.diet !== "XX")) {
 					slave.diet = "XX";
 					r += `<br>${slave.slaveName} has been put on a diet that favors feminine development.`;
@@ -1710,13 +1721,13 @@ globalThis.DefaultRules = (function() {
 			}
 		}
 
-		if (rule.weight !== null) {
+		if (rule.weight !== null || (rule.diet === "attractive") ) {
 			weightRule(slave, rule);
 		}
-		if (rule.weight === null && rule.muscles !== null) {
+		if (rule.weight === null && rule.diet !== "attractive" && rule.muscles !== null) {
 			muscleRule(slave, rule);
 		}
-		if (rule.weight === null && rule.muscles === null && (rule.diet !== undefined && rule.diet !== null)) {
+		if (rule.weight === null && rule.muscles === null && (rule.diet !== undefined && rule.diet !== null && rule.diet !== "attractive")) {
 			dietRule(slave, rule);
 		}
 
@@ -1870,9 +1881,9 @@ globalThis.DefaultRules = (function() {
 		if (rule.livingRules !== undefined && rule.livingRules !== null && slave.rules.living !== rule.livingRules) {
 			if (setup.facilityCareers.includes(slave.assignment)) {
 				// Handled in Rules tab of SI now.
-				//r += `<br>${slave.slaveName}'s living standards are controlled by ${his} assignment.`;
+				// r += `<br>${slave.slaveName}'s living standards are controlled by ${his} assignment.`;
 			} else if (((slave.assignment === Job.HEADGIRL) && (V.HGSuite === 1)) || ((slave.assignment === Job.BODYGUARD) && (V.dojo > 1))) {
-				//r += `<br>${slave.slaveName} has a private room.`;
+				// r += `<br>${slave.slaveName} has a private room.`;
 			} else if ((slave.fetish === "mindbroken")) {
 				if ((slave.rules.living !== "spare")) {
 					slave.rules.living = "spare";
@@ -1892,8 +1903,8 @@ globalThis.DefaultRules = (function() {
 					r += `<br>${slave.slaveName}'s living standard has been set to ${rule.livingRules}.`;
 				}
 			}
+			penthouseCensus();
 		}
-		penthouseCensus();
 	}
 
 	/**
@@ -2504,6 +2515,7 @@ globalThis.DefaultRules = (function() {
 
 					if (rule.clitPiercing === 3) {
 						cashX(forceNeg(V.SPcost), "slaveMod", slave);
+						slave.clitSetting = "all";
 					} else {
 						cashX(forceNeg(V.modCost), "slaveMod", slave);
 					}
@@ -2988,7 +3000,7 @@ globalThis.DefaultRules = (function() {
 						slave.brand[rule.brandTarget] = rule.brandDesign;
 						r += `${rule.brandTarget}`;
 					}
-					r += `, with <span class="trust dec">fear</span>${slave.devotion < 18 ? `, <span class="devotion dec">regard,</span>` : ``} and <span class="red">health</span> consequences.`;
+					r += `, with <span class="trust dec">fear</span>${slave.devotion < 18 ? `, <span class="devotion dec">regard,</span>` : ``} and <span class="health dec">health</span> consequences.`;
 					if (slave.devotion < 18) {
 						slave.devotion -= 5;
 					}
@@ -2997,7 +3009,7 @@ globalThis.DefaultRules = (function() {
 					slave.brand[left] = rule.brandDesign;
 					slave.brand[right] = rule.brandDesign;
 					healthDamage(slave, 20);
-					r += `<br>${slave.slaveName} has been branded on both ${rule.brandTarget}, with <span class="trust dec">fear</span>${slave.devotion < 18 ? `, <span class="devotion dec">regard,</span>` : ``} and <span class="red">health</span> consequences.`;
+					r += `<br>${slave.slaveName} has been branded on both ${rule.brandTarget}, with <span class="trust dec">fear</span>${slave.devotion < 18 ? `, <span class="devotion dec">regard,</span>` : ``} and <span class="health dec">health</span> consequences.`;
 					if (slave.devotion < 18) {
 						slave.devotion -= 10;
 					}
@@ -3118,9 +3130,9 @@ globalThis.DefaultRulesError = () => V.defaultRules.some(r => RuleHasError(r));
  * @param {App.Entity.SlaveState} slave
  */
 globalThis.removeFromRulesToApplyOnce = function(slave) {
-    for (const rule of Object.keys(V.rulesToApplyOnce)) {
-        if (V.rulesToApplyOnce[rule].includes(slave.ID)) {
-            V.rulesToApplyOnce[rule].delete(slave.ID);
-        }
-    }
+	for (const rule of Object.keys(V.rulesToApplyOnce)) {
+		if (V.rulesToApplyOnce[rule].includes(slave.ID)) {
+			V.rulesToApplyOnce[rule].delete(slave.ID);
+		}
+	}
 };
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index 9bf380863a1e3f51be99fd54840e2279666edded..fc2c38abf9e1af86adf4483c4c793e4b8e2cc214 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -2485,8 +2485,6 @@ App.Entity.SlaveState = class SlaveState {
 		this.origBodyOwner = "";
 		/** Who, if relevant, the body belonged to. */
 		this.origBodyOwnerID = 0;
-		/** Cause of slave death. */
-		this.death = "";
 		/**
 		 * Slave's current hormonal balance, directs saHormones changes
 		 *
diff --git a/src/js/assayJS.js b/src/js/assayJS.js
index 843314ad8e0b45e223841d5e9ea7979e93a3a6c5..98536871a96b5e5bbee09b900988f205b1fc63f7 100644
--- a/src/js/assayJS.js
+++ b/src/js/assayJS.js
@@ -1404,21 +1404,17 @@ globalThis.faceIncrease = function(slave, amount) {
 };
 
 /**
- * @param {App.Entity.SlaveState|App.Entity.Animal} slave
+ * @param {App.Entity.SlaveState} slave
  * @returns {number}
  */
 globalThis.deadliness = function(slave) {
-	if (slave instanceof App.Entity.Animal || slave.hasOwnProperty('species')) {	// FIXME: temporary workaround
-		return slave.deadliness;
-	}
-
 	let deadliness = 2;
 
 	if (slave.skill.combat > 0) {
 		deadliness += 2;
 	}
 
-	if (setup.bodyguardCareers.includes(slave.career)) {
+	if (App.Data.Careers.Leader.bodyguard.includes(slave.career)) {
 		deadliness += 1;
 	} else if (slave.skill.bodyguard >= V.masteredXP) {
 		deadliness += 1;
diff --git a/src/js/birth/birth.js b/src/js/birth/birth.js
index 84d4ec3d3850ab54108d5167f8f0d4c62db858c2..1332f6d618a9d23090dda7c7798f9e786a27c4d0 100644
--- a/src/js/birth/birth.js
+++ b/src/js/birth/birth.js
@@ -333,12 +333,12 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 
 			if (burstCheck(slave)) {
 				r.push(App.UI.DOM.makeElement("span", `gave birth`, "orange"));
-				r.push(App.UI.DOM.makeElement("span", `in the worst possible way`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `in the worst possible way`, ["health", "dec"]));
 				slaveDead = 1;
 			} else {
 				r.push(App.UI.DOM.makeElement("span", `gave birth`, "orange"));
 				if (slave.prematureBirth === 1) {
-					r.push(App.UI.DOM.makeElement("span", `prematurely`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `prematurely`, ["health", "dec"]));
 				}
 				if (diffSize < 1.15) {
 					r.push(`but ${his} overfilled womb barely lost any size. ${His} body gave life`);
@@ -417,7 +417,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (burstCheck(slave)) {
 				r.push(`${He}`);
 				r.push(App.UI.DOM.makeElement("span", `gave birth`, "orange"));
-				r.push(App.UI.DOM.makeElement("span", `in the worst possible way`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `in the worst possible way`, ["health", "dec"]));
 				r.push(`to`);
 				slaveDead = 1;
 			} else if (cSection) {
@@ -435,7 +435,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				r.push(`${He}`);
 				r.push(App.UI.DOM.makeElement("span", `gave birth`, "orange"));
 				if (slave.prematureBirth === 1) {
-					r.push(App.UI.DOM.makeElement("span", `prematurely`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `prematurely`, ["health", "dec"]));
 				}
 
 				if (diffSize < 1.15) {
@@ -473,7 +473,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			}
 			if (cSection && slave.wombImplant === "restraint") {
 				r.push(`The uterine support mesh built into ${his} womb was irreversibly damaged and had to be carefully extracted. Such an invasive surgery carried`);
-				r.push(App.UI.DOM.makeElement("span", `major health complications.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `major health complications.`, ["health", "dec"]));
 				slave.wombImplant = "none";
 				healthDamage(slave, 30);
 			}
@@ -491,7 +491,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (newMother) {
 				r.push([
 					`${His} inexperience`,
-					App.UI.DOM.makeElement("span", `complicated ${his} first birth.`, "red")
+					App.UI.DOM.makeElement("span", `complicated ${his} first birth.`, ["health", "dec"])
 				]);
 				compoundCondition = 1;
 				birthDamage += 2;
@@ -500,7 +500,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (slave.anus < 2) {
 					r.push([
 						`${His} tight ass`,
-						App.UI.DOM.makeElement("span", `hindered ${his} ${(numBeingBorn > 1) ? `babies` : `baby's`} birth.`, "red")
+						App.UI.DOM.makeElement("span", `hindered ${his} ${(numBeingBorn > 1) ? `babies` : `baby's`} birth.`, ["health", "dec"])
 					]);
 					birthDamage += 3;
 				}
@@ -508,14 +508,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (slave.vagina < 2) {
 					r.push([
 						`${His} tight vagina`,
-						App.UI.DOM.makeElement("span", `hindered ${his} ${(numBeingBorn > 1) ? `babies` : `baby's`} birth.`, "red")
+						App.UI.DOM.makeElement("span", `hindered ${his} ${(numBeingBorn > 1) ? `babies` : `baby's`} birth.`, ["health", "dec"])
 					]);
 					birthDamage += 3;
 				}
 				if (slave.vaginaLube === 0) {
 					r.push([
 						`${His} dry vagina made pushing ${his} ${children} out`,
-						App.UI.DOM.makeElement("span", `painful.`, "red")
+						App.UI.DOM.makeElement("span", `painful.`, ["health", "dec"])
 					]);
 					birthDamage += 1;
 				}
@@ -523,28 +523,28 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.hips < 0) {
 				r.push([
 					`${His} narrow hips made birth`,
-					App.UI.DOM.makeElement("span", `troublesome.`, "red")
+					App.UI.DOM.makeElement("span", `troublesome.`, ["health", "dec"])
 				]);
 				birthDamage += (2 - slave.hips);
 			}
 			if (slave.weight < -95) {
 				r.push([
 					`${His} very thin body`,
-					App.UI.DOM.makeElement("span", `was nearly incapable of birthing ${his} ${children}.`, "red")
+					App.UI.DOM.makeElement("span", `was nearly incapable of birthing ${his} ${children}.`, ["health", "dec"])
 				]);
 				birthDamage += 7;
 				compoundCondition = 1;
 			} else if (slave.weight <= -30) {
 				r.push([
 					`${His} thin body was`,
-					App.UI.DOM.makeElement("span", `ill-suited to ${his} childbirth.`, "red")
+					App.UI.DOM.makeElement("span", `ill-suited to ${his} childbirth.`, ["health", "dec"])
 				]);
 				birthDamage += 5;
 			}
 			if (slave.health.condition < -20) {
 				r.push([
 					`${His} poor health made laboring`,
-					App.UI.DOM.makeElement("span", `exhausting.`, "red")
+					App.UI.DOM.makeElement("span", `exhausting.`, ["health", "dec"])
 				]);
 				compoundCondition = 1;
 				birthDamage += (4 - (slave.health.condition / 10));
@@ -552,7 +552,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.health.illness >= 3) {
 				r.push([
 					`${His} ongoing illness`,
-					App.UI.DOM.makeElement("span", `already sapped most of ${his} strength.`, "red")
+					App.UI.DOM.makeElement("span", `already sapped most of ${his} strength.`, ["health", "dec"])
 				]);
 				birthDamage += slave.health.illness;
 				compoundCondition = 1;
@@ -560,14 +560,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.physicalAge < 6) {
 				r.push([
 					`${His} very young body was`,
-					App.UI.DOM.makeElement("span", `not designed to be able pass a baby.`, "red")
+					App.UI.DOM.makeElement("span", `not designed to be able pass a baby.`, ["health", "dec"])
 				]);
 				birthDamage += 5;
 				compoundCondition = 1;
 			} else if (slave.physicalAge < 9) {
 				r.push([
 					`${His} young body had`,
-					App.UI.DOM.makeElement("span", `a lot of trouble`, "red"),
+					App.UI.DOM.makeElement("span", `a lot of trouble`, ["health", "dec"]),
 					`birthing ${his} ${babies}.`
 				]);
 				birthDamage += 3;
@@ -575,7 +575,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			} else if (slave.physicalAge < 13) {
 				r.push([
 					`${His} young body had`,
-					App.UI.DOM.makeElement("span", `trouble birthing`, "red"),
+					App.UI.DOM.makeElement("span", `trouble birthing`, ["health", "dec"]),
 					`birthing ${his} ${babies}.`
 				]);
 				birthDamage += 1;
@@ -583,14 +583,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			} else if (slave.physicalAge >= 100) {
 				r.push([
 					`${His} very old body was`,
-					App.UI.DOM.makeElement("span", `not capable of passing a baby anymore.`, "red")
+					App.UI.DOM.makeElement("span", `not capable of passing a baby anymore.`, ["health", "dec"])
 				]);
 				birthDamage += 5;
 				compoundCondition = 1;
 			} else if (slave.physicalAge >= 85) {
 				r.push([
 					`${His} old body had`,
-					App.UI.DOM.makeElement("span", `a lot of trouble`, "red"),
+					App.UI.DOM.makeElement("span", `a lot of trouble`, ["health", "dec"]),
 					`birthing ${his} ${babies}.`
 				]);
 				birthDamage += 3;
@@ -598,7 +598,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			} else if (slave.physicalAge >= 70) {
 				r.push([
 					`${His} old body had`,
-					App.UI.DOM.makeElement("span", `trouble birthing`, "red"),
+					App.UI.DOM.makeElement("span", `trouble birthing`, ["health", "dec"]),
 					`birthing ${his} ${babies}.`
 				]);
 				birthDamage += 1;
@@ -607,14 +607,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.health.tired > 80) {
 				r.push([
 					`${He} was thoroughly exhausted to begin with; ${he}`,
-					App.UI.DOM.makeElement("span", `lacked the energy to push at all.`, "red")
+					App.UI.DOM.makeElement("span", `lacked the energy to push at all.`, ["health", "dec"])
 				]);
 				birthDamage += 20;
 				compoundCondition = 1;
 			} else if (slave.health.tired > 50) {
 				r.push([
 					`${He} was so tired, ${he}`,
-					App.UI.DOM.makeElement("span", `lacked the energy to effectively push.`, "red")
+					App.UI.DOM.makeElement("span", `lacked the energy to effectively push.`, ["health", "dec"])
 				]);
 				birthDamage += 2;
 				compoundCondition = 1;
@@ -622,14 +622,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.muscles < -95) {
 				r.push([
 					`${He} tried and tried but ${his} frail body`,
-					App.UI.DOM.makeElement("span", `could not push ${his} ${children} out.`, "red")
+					App.UI.DOM.makeElement("span", `could not push ${his} ${children} out.`, ["health", "dec"])
 				]);
 				birthDamage += 30;
 				compoundCondition = 1;
 			} else if (slave.muscles < -30) {
 				r.push([
 					`${His} very weak body`,
-					App.UI.DOM.makeElement("span", `barely managed to push`, "red"),
+					App.UI.DOM.makeElement("span", `barely managed to push`, ["health", "dec"]),
 					`out ${his} ${children}`
 				]);
 				birthDamage += 4;
@@ -637,7 +637,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			} else if (slave.muscles < -5) {
 				r.push([
 					`${His} weak body`,
-					App.UI.DOM.makeElement("span", `struggled to push`, "red"),
+					App.UI.DOM.makeElement("span", `struggled to push`, ["health", "dec"]),
 					`out ${his} ${children}.`
 				]);
 				birthDamage += 2;
@@ -646,7 +646,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.preg > slave.pregData.normalBirth * 1.25) { // better get her a c-sec
 				r.push([
 					`${His} ${children} had extra time to grow`,
-					App.UI.DOM.makeElement("span", `greatly complicating childbirth.`, "red")
+					App.UI.DOM.makeElement("span", `greatly complicating childbirth.`, ["health", "dec"])
 				]);
 				if (slave.preg >= slave.pregData.normalBirth * 1.5) {
 					if (slave.physicalAge < 6) {
@@ -676,7 +676,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.wombImplant === "restraint") {
 				r.push([
 					`${His} support implant`,
-					App.UI.DOM.makeElement("span", `weakens ${his} contractions`, "red"),
+					App.UI.DOM.makeElement("span", `weakens ${his} contractions`, ["health", "dec"]),
 					`and inhibits ${his} womb's ability to give birth.`
 				]);
 				birthDamage += 2;
@@ -695,7 +695,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (slave.anus >= 2) {
 					r.push([
 						`${His}`,
-						App.UI.DOM.makeElement("span", `loose ass`, "green"),
+						App.UI.DOM.makeElement("span", `loose ass`, ["health", "inc"]),
 						`made birthing ${his} ${children} easier.`
 					]);
 				}
@@ -704,7 +704,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (slave.vagina >= 2) {
 					r.push([
 						`${His}`,
-						App.UI.DOM.makeElement("span", `loose vagina`, "green"),
+						App.UI.DOM.makeElement("span", `loose vagina`, ["health", "inc"]),
 						`made birthing ${his} ${children} easier.`
 					]);
 					birthDamage -= slave.vagina;
@@ -712,7 +712,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (slave.vaginaLube > 0) {
 					r.push([
 						`${His}`,
-						App.UI.DOM.makeElement("span", `moist vagina`, "green"),
+						App.UI.DOM.makeElement("span", `moist vagina`, ["health", "inc"]),
 						`hastened ${his} ${children}'s birth.`
 					]);
 					birthDamage -= slave.vaginaLube;
@@ -722,7 +722,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (!newMother) {
 				r.push([
 					`${He} has`,
-					App.UI.DOM.makeElement("span", `given birth before,`, "green"),
+					App.UI.DOM.makeElement("span", `given birth before,`, ["health", "inc"]),
 					`so ${he} knows just what to do.`
 				]);
 				birthDamage -= 3;
@@ -731,7 +731,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.hips > 0) {
 				r.push([
 					`${His} `,
-					App.UI.DOM.makeElement("span", `wide hips`, "green"),
+					App.UI.DOM.makeElement("span", `wide hips`, ["health", "inc"]),
 					`greatly aided childbirth.`
 				]);
 				birthDamage -= slave.hips;
@@ -740,21 +740,21 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.pregAdaptation >= 1000) {
 				r.push([
 					`${His} body has`,
-					App.UI.DOM.makeElement("span", `completely adapted to pregnancy;`, "green"),
+					App.UI.DOM.makeElement("span", `completely adapted to pregnancy;`, ["health", "inc"]),
 					`when it is time to give birth, that baby is coming out fast.`
 				]);
 				birthDamage -= 10;
 			} else if (slave.pregAdaptation >= 500) {
 				r.push([
 					`${His} body is`,
-					App.UI.DOM.makeElement("span", `highly adapted to bearing life`, "green"),
+					App.UI.DOM.makeElement("span", `highly adapted to bearing life`, ["health", "inc"]),
 					`and birth is no small part of that.`
 				]);
 				birthDamage -= 3;
 			} else if (slave.pregAdaptation >= 100) {
 				r.push([
 					`${His} body has`,
-					App.UI.DOM.makeElement("span", `become quite adept at bearing children,`, "green"),
+					App.UI.DOM.makeElement("span", `become quite adept at bearing children,`, ["health", "inc"]),
 					`birth included.`
 				]);
 				birthDamage -= 1;
@@ -763,23 +763,23 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			if (slave.curatives > 0) {
 				r.push([
 					`${His} `,
-					App.UI.DOM.makeElement("span", `curatives`, "green"),
+					App.UI.DOM.makeElement("span", `curatives`, ["health", "inc"]),
 					`helped protect ${him}.`
 				]);
 				birthDamage -= 3;
 			}
 
-			if (App.Data.misc.nurseCareers.includes(slave.career) && slave.fetish !== "mindbroken" && slave.muscles >= -95) {
+			if (App.Data.Careers.Leader.nurse.includes(slave.career) && slave.fetish !== "mindbroken" && slave.muscles >= -95) {
 				r.push([
 					`Thanks to ${his}`,
-					App.UI.DOM.makeElement("span", `previous career,`, "green"),
+					App.UI.DOM.makeElement("span", `previous career,`, ["health", "inc"]),
 					`childbirth went smoothly.`
 				]);
 				birthDamage = 0;
 			} else if (slave.intelligenceImplant >= 15) {
 				r.push([
 					`${He} was`,
-					App.UI.DOM.makeElement("span", `taught how to handle birth`, "green"),
+					App.UI.DOM.makeElement("span", `taught how to handle birth`, ["health", "inc"]),
 					`in class.`
 				]);
 				birthDamage -= 2;
@@ -789,13 +789,13 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				if (V.geneticMappingUpgrade >= 1) {
 					r.push([
 						`${His} hypersensitive uterus made birth`,
-						App.UI.DOM.makeElement("span", `a very pleasant experience,`, "green"),
+						App.UI.DOM.makeElement("span", `a very pleasant experience,`, ["health", "inc"]),
 						`distracting from the pain.`
 					]);
 				} else {
 					r.push([
 						`${He} oddly climaxed multiple times during birth,`,
-						App.UI.DOM.makeElement("span", `keeping ${his} mind off the pain.`, "green")
+						App.UI.DOM.makeElement("span", `keeping ${his} mind off the pain.`, ["health", "inc"])
 					]);
 				}
 				birthDamage -= 5;
@@ -861,7 +861,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				r.push(
 					App.UI.DOM.makeElement("span", `disappointed`, "mediumorchid")
 				);
-				(`that you would forbid ${him} from such a pleasure, but`);
+				r.push(`that you would forbid ${him} from such a pleasure, but`);
 				r.push(
 					App.UI.DOM.makeElement("span", `understands`, "mediumaquamarine")
 				);
@@ -1088,7 +1088,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			) {
 				r = [];
 				r.push(`Since ${he} was a virgin, giving birth was a`);
-				r.push(App.UI.DOM.makeElement("span", `terribly painful`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `terribly painful`, ["health", "dec"]));
 				r.push(`experience.`);
 				if (slave.fetish !== "mindbroken") {
 					if (slave.fetish === "masochist") {
@@ -1130,7 +1130,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 					r.push(`first`);
 				}
 				r.push(`child, ${his} pelvic bone strained under the pressure until it could no longer hold together and`);
-				r.push(App.UI.DOM.makeElement("span", `agonizingly snapped.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `agonizingly snapped.`, ["health", "dec"]));
 				if (slave.fetish !== "mindbroken") {
 					if (slave.fetish === "masochist") {
 						if (slave.fetishKnown === 0) {
@@ -1170,7 +1170,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			r = [];
 			if (birthStorm) {
 				r.push(
-					App.UI.DOM.makeElement("span", `The ordeal of constant birthing has had a massive effect on ${his} health as well as completely destroying ${his} vagina.`, "red")
+					App.UI.DOM.makeElement("span", `The ordeal of constant birthing has had a massive effect on ${his} health as well as completely destroying ${his} vagina.`, ["health", "dec"])
 				);
 			} else if (slave.mpreg === 1) {
 				if (slave.anus < 0) { // you somehow got a pregnant slave with no vagina catch
@@ -1247,7 +1247,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				r.push(`Since you`);
 				r.push(App.UI.DOM.makeElement("span", `performed the surgery yourself,`, "springgreen"));
 				r.push(`and you do an artist's work, ${his} health is`);
-				r.push(App.UI.DOM.makeElement("span", `less affected`, "green"));
+				r.push(App.UI.DOM.makeElement("span", `less affected`, ["health", "inc"]));
 				r.push(`by the surgery than it would have been if you'd paid some hack to do it remotely.`);
 				if (slave.fetish !== "mindbroken" && slave.fuckdoll === 0) {
 					r.push(`${He} went into the surgery very aware that you were performing it manually.`);
@@ -1285,7 +1285,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				}
 			} else {
 				r.push(`As with all surgery`);
-				r.push(App.UI.DOM.makeElement("span", `${his} health has been slightly affected.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `${his} health has been slightly affected.`, ["health", "dec"]));
 				surgeryDamage(slave, 5);
 			}
 			App.Events.addNode(el, r, "div");
@@ -1298,19 +1298,19 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			r.push(`All in all,`);
 			if (birthDamage > 15) {
 				r.push(`childbirth was`);
-				r.push(App.UI.DOM.makeElement("span", `horrifically difficult for ${him} and nearly claimed ${his} life.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `horrifically difficult for ${him} and nearly claimed ${his} life.`, ["health", "dec"]));
 			} else if (birthDamage > 10) {
 				r.push(`childbirth was extremely difficult for ${him} and`);
-				r.push(App.UI.DOM.makeElement("span", `greatly damaged ${his} health.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `greatly damaged ${his} health.`, ["health", "dec"]));
 			} else if (birthDamage > 5) {
 				r.push(`childbirth was difficult for ${him} and`);
-				r.push(App.UI.DOM.makeElement("span", `damaged ${his} health.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `damaged ${his} health.`, ["health", "dec"]));
 			} else if (birthDamage > 0) {
 				r.push(`childbirth was painful for ${him}, though not abnormally so, and`);
-				r.push(App.UI.DOM.makeElement("span", `damaged ${his} health.`, "red"));
+				r.push(App.UI.DOM.makeElement("span", `damaged ${his} health.`, ["health", "dec"]));
 			} else {
 				r.push(`childbirth was`);
-				r.push(App.UI.DOM.makeElement("span", `no problem`, "green"));
+				r.push(App.UI.DOM.makeElement("span", `no problem`, ["health", "inc"]));
 				r.push(`for ${him}.`);
 			}
 			if (birthDamage > 0) {
@@ -1318,7 +1318,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				slave.health.tired += Math.round((birthDamage / 2) * 10);
 				if (birthDamage > 5 && compoundCondition === 1 && numBeingBorn > 1) {
 					r.push(`Or it would have been, were ${he} only having one. With each additional child that needed to be birthed,`);
-					r.push(App.UI.DOM.makeElement("span", `the damage to ${his} health was compounded.`, "red"));
+					r.push(App.UI.DOM.makeElement("span", `the damage to ${his} health was compounded.`, ["health", "dec"]));
 					healthDamage(slave, numBeingBorn);
 					slave.health.tired += numBeingBorn * 5;
 				}
@@ -1328,7 +1328,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 			}
 			if (slave.geneticQuirks.uterineHypersensitivity === 2 && slave.fetish !== "mindbroken" && slave.fuckdoll === 0) {
 				r.push(`Not only that, but`);
-				r.push(App.UI.DOM.makeElement("span", `the entire process was extremely pleasurable for ${him}${(numBeingBorn > 1) ? ',' : '.'}`, "green"));
+				r.push(App.UI.DOM.makeElement("span", `the entire process was extremely pleasurable for ${him}${(numBeingBorn > 1) ? ',' : '.'}`, ["health", "inc"]));
 				if (numBeingBorn > 1) {
 					r.push(`with orgasms growing more powerful with each baby ${he} brought to the world.`);
 				}
@@ -1459,14 +1459,14 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 				r = [];
 				if (slave.breedingMark === 1 && V.propOutcome === 1 && (slave.pregSource === -1 || slave.pregSource === -6)) {
 					r.push(`The ${societalElite}`);
-					r.push("span", `are pleased`, "green");
+					r.push(App.UI.DOM.makeElement("span", `are pleased`, "green"));
 					r.push(`at the new additions to their class.`);
 					V.failedElite -= (2 * numBeingBorn);
 				} else if (V.eugenicsFullControl !== 1) {
 					r.push(`The ${societalElite}`);
-					r.push("span", `are disappointed`, "red");
+					r.push(App.UI.DOM.makeElement("span", `are disappointed`, "red"));
 					r.push(`that you would allow subhuman filth to dirty the arcology under your watch. Society`);
-					r.push("span", `frowns`, "red");
+					r.push(App.UI.DOM.makeElement("span", `frowns`, "red"));
 					r.push(`on the unwelcome addition of more subhumans into the world.`);
 					V.failedElite += (5 * numBeingBorn);
 					repX(forceNeg(10 * numBeingBorn), "birth", slave);
@@ -4933,7 +4933,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 						if (slave.fetish === "mindbroken") {
 							r.push(`${He} is helped back to ${his} bed and stripped before slipping into it. Instinctively, ${he} begins to push out ${his} ${babies}. ${His} ${childrenAre} promptly taken and ${he} is encouraged to keep resting.`);
 						} else {
-							r.push(`${He} is helped back to ${his} bed and stripped before slipping into it. ${He} makes ${himself} comfortable and begins working on birthing ${his} ${(slave.pregType > 1) ? `babies` : `baby`} ${(slave.geneticQuirks.uterineHypersensitivity === 2) ? `, convulsing with orgasms in the process` : ``}. ${His}  ${(slave.pregType > 1) ? `children are` : `child is`} promptly taken and ${he} is encouraged to keep resting.`);
+							r.push(`${He} is helped back to ${his} bed and stripped before slipping into it. ${He} makes ${himself} comfortable and begins working on birthing ${his} ${(slave.pregType > 1) ? `babies` : `baby`} ${(slave.geneticQuirks.uterineHypersensitivity === 2) ? `, convulsing with orgasms in the process` : ``}. ${His} ${(slave.pregType > 1) ? `children are` : `child is`} promptly taken and ${he} is encouraged to keep resting.`);
 						}
 					} else {
 						if (slave.fetish === "mindbroken") {
@@ -7448,7 +7448,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false} = {})
 							r.push(`of ${his} bodysuit until someone helps them from their nylon prison.`);
 						}
 					} else {
-						r.push(`Quickly ${he} attempts to remove ${his} bodysuit but fails to do so before having to push out ${babyIntro}.  ${He} can't hide what's happening between ${his} legs,`);
+						r.push(`Quickly ${he} attempts to remove ${his} bodysuit but fails to do so before having to push out ${babyIntro}. ${He} can't hide what's happening between ${his} legs,`);
 						if (slave.fetish === "humiliation") {
 							r.push(`but that only makes it more exciting.`);
 						} else {
diff --git a/src/js/death.js b/src/js/death.js
deleted file mode 100644
index fa243db18c739c200f81e1fb0aced54634d28afd..0000000000000000000000000000000000000000
--- a/src/js/death.js
+++ /dev/null
@@ -1,4 +0,0 @@
-globalThis.planDeath = function(slave, reason) {
-	slave.death = reason;
-	V.slaveDeath = 1;
-};
diff --git a/src/js/economyJS.js b/src/js/economyJS.js
index 28a85dd937dbc5588f064c3162864b53e4f18dd3..c321c0d4adc74c494410ff278645f06b7eaac91c 100644
--- a/src/js/economyJS.js
+++ b/src/js/economyJS.js
@@ -37,7 +37,7 @@ globalThis.DJRepBonus = function() {
 		value += 0.05;
 		// $His great beauty is a further draw, even when $he's in $his DJ booth, but especially when $he comes out to dance.
 	}
-	if (setup.DJCareers.includes(S.DJ.career)) {
+	if (App.Data.Careers.Leader.DJ.includes(S.DJ.career)) {
 		value += 0.05;
 		// $He has musical experience from $his life before $he was a slave, a grounding that gives $his tracks actual depth.
 	} else if (S.DJ.skill.DJ >= V.masteredXP) {
@@ -867,7 +867,7 @@ globalThis.calculateCosts = (function() {
 		if (slave.assignment === Job.QUARTER) {
 			effectiveness *= 1.1;
 		}
-		if (setup.servantCareers.includes(slave.career) || slave.skill.servant >= V.masteredXP) {
+		if (App.Data.Careers.Leader.servant.includes(slave.career) || slave.skill.servant >= V.masteredXP) {
 			effectiveness *= 1.1;
 		}
 		effectiveness = Math.trunc(effectiveness * restEffects(slave) / 10);
@@ -1504,7 +1504,7 @@ globalThis.slaveJobValues = function(lowerClassSexDemandRef, middleClassSexDeman
 		} else if (V.AgePenalty === 0) {
 			App.EndWeek.saVars.madamBonus += 0.05;
 		}
-		if (setup.madamCareers.includes(madam.career)) {
+		if (App.Data.Careers.Leader.madam.includes(madam.career)) {
 			// $He has experience from $his life before $he was a slave that helps $him in the seedy business of selling other people's bodies for sex.
 			App.EndWeek.saVars.madamBonus += 0.05;
 		} else if (madam.skill.madam >= V.masteredXP) {
@@ -2411,7 +2411,7 @@ globalThis.agentBonus = function(arcology) {
 	if (agent.actualAge > 35) {
 		bonus++;
 	}
-	if (agent.career === "an arcology owner" || setup.HGCareers.includes(agent.career)) {
+	if (agent.career === "an arcology owner" || App.Data.Careers.Leader.HG.includes(agent.career)) {
 		bonus++;
 	}
 	if (agent.fetishStrength > 95) {
diff --git a/src/js/futureSocietyJS.js b/src/js/futureSocietyJS.js
index e96505c2a2fcc7e35d2a594dbb24b6f2ade510fa..619ed9494a760e87ff15afbf38612e067d3821f0 100644
--- a/src/js/futureSocietyJS.js
+++ b/src/js/futureSocietyJS.js
@@ -1,43 +1,39 @@
 globalThis.FutureSocieties = (function() {
-	"use strict";
-	const FSString2Property = { // blame Hedonism and Eugenics for this - TODO: probably can be cleaned up, maybe eliminated
-		Supremacist: "FSSupremacist",
-		Subjugationist: "FSSubjugationist",
-		GenderRadicalist: "FSGenderRadicalist",
-		GenderFundamentalist: "FSGenderFundamentalist",
-		Degradationist: "FSDegradationist",
-		Paternalist: "FSPaternalist",
-		BodyPurist: "FSBodyPurist",
-		TransformationFetishist: "FSTransformationFetishist",
-		YouthPreferentialist: "FSYouthPreferentialist",
-		MaturityPreferentialist: "FSMaturityPreferentialist",
-		SlimnessEnthusiast: "FSSlimnessEnthusiast",
-		AssetExpansionist: "FSAssetExpansionist",
-		Pastoralist: "FSPastoralist",
-		PhysicalIdealist: "FSPhysicalIdealist",
-		Hedonistic: "FSHedonisticDecadence",
-		Hedonism: "FSHedonisticDecadence",
-		ChattelReligionist: "FSChattelReligionist",
-		Multiculturalist: "FSNull",
-		RomanRevivalist: "FSRomanRevivalist",
-		NeoImperialist: "FSNeoImperialist",
-		EgyptianRevivalist: "FSEgyptianRevivalist",
-		EdoRevivalist: "FSEdoRevivalist",
-		ArabianRevivalist: "FSArabianRevivalist",
-		ChineseRevivalist: "FSChineseRevivalist",
-		AztecRevivalist: "FSAztecRevivalist",
-		RepopulationFocus: "FSRepopulationFocus",
-		Repopulationist: "FSRepopulationFocus",
-		Eugenics: "FSRestart",
-		IntellectualDependency: "FSIntellectualDependency",
-		SlaveProfessionalism: "FSSlaveProfessionalism",
-		PetiteAdmiration: "FSPetiteAdmiration",
-		StatuesqueGlorification: "FSStatuesqueGlorification"
+	/** @type {Record<FC.FutureSociety, {noun: FC.FutureSocietyNoun, adj: FC.FutureSocietyAdj, deco: FC.FutureSocietyDeco, NPCOnly?: boolean}>} */
+	const FSRecords = {
+		FSSupremacist: {noun: "Racial Supremacism", adj: "Supremacist", deco: "Supremacist"},
+		FSSubjugationist: {noun: "Racial Subjugationism", adj: "Subjugationist", deco: "Subjugationist"},
+		FSGenderRadicalist: {noun: "Gender Radicalism", adj: "Gender Radicalist", deco: "Gender Radicalist"},
+		FSGenderFundamentalist: {noun: "Gender Fundamentalism", adj: "Gender Fundamentalist", deco: "Gender Fundamentalist"},
+		FSDegradationist: {noun: "Degradationism", adj: "Degradationist", deco: "Degradationist"},
+		FSPaternalist: {noun: "Paternalism", adj: "Paternalist", deco: "Paternalist"},
+		FSBodyPurist: {noun: "Body Purism", adj: "Body Purist", deco: "Body Purist"},
+		FSTransformationFetishist: {noun: "Transformation Fetishism", adj: "Transformation Fetishist", deco: "Transformation Fetishist"},
+		FSYouthPreferentialist: {noun: "Youth Preferentialism", adj: "Youth Preferentialist", deco: "Youth Preferentialist"},
+		FSMaturityPreferentialist: {noun: "Maturity Preferentialism", adj: "Maturity Preferentialist", deco: "Maturity Preferentialist"},
+		FSSlimnessEnthusiast: {noun: "Slimness Enthusiasm", adj: "Slimness Enthusiast", deco: "Slimness Enthusiast"},
+		FSAssetExpansionist: {noun: "Asset Expansionism", adj: "Asset Expansionist", deco: "Asset Expansionist"},
+		FSPastoralist: {noun: "Pastoralism", adj: "Pastoralist", deco: "Pastoralist"},
+		FSCummunism: {noun: "Cummunism", adj: "Cummunist", deco: "", NPCOnly: true},
+		FSPhysicalIdealist: {noun: "Physical Idealism", adj: "Physical Idealist", deco: "Physical Idealist"},
+		FSHedonisticDecadence: {noun: "Decadent Hedonism", adj: "Decadent Hedonist", deco: "Hedonistic"},
+		FSChattelReligionist: {noun: "Chattel Religionism", adj: "Chattel Religionist", deco: "Chattel Religionist"},
+		FSNull: {noun: "Multiculturalism", adj: "Multiculturalist", deco: ""},
+		FSIncestFetishist: {noun: "Incest Fetishism", adj: "Incest Fetishist", deco: "", NPCOnly: true},
+		FSRomanRevivalist: {noun: "Roman Revivalism", adj: "Roman Revivalist", deco: "Roman Revivalist"},
+		FSNeoImperialist: {noun: "Neo-Imperialism", adj: "Neo-Imperialist", deco: "Neo-Imperialist"},
+		FSEgyptianRevivalist: {noun: "Egyptian Revivalism", adj: "Egyptian Revivalist", deco: "Egyptian Revivalist"},
+		FSEdoRevivalist: {noun: "Edo Revivalism", adj: "Edo Revivalist", deco: "Edo Revivalist"},
+		FSArabianRevivalist: {noun: "Arabian Revivalism", adj: "Arabian Revivalist", deco: "Arabian Revivalist"},
+		FSChineseRevivalist: {noun: "Chinese Revivalism", adj: "Chinese Revivalist", deco: "Chinese Revivalist"},
+		FSAztecRevivalist: {noun: "Aztec Revivalism", adj: "Aztec Revivalist", deco: "Aztec Revivalist"},
+		FSRepopulationFocus: {noun: "Repopulation Focus", adj: "Repopulationist", deco: "Repopulationist"},
+		FSRestart: {noun: "Eugenics", adj: "Eugenics", deco: "Eugenics"},
+		FSIntellectualDependency: {noun: "Intellectual Dependency", adj: "Intellectual Dependency", deco: "Intellectual Dependency"},
+		FSSlaveProfessionalism: {noun: "Slave Professionalism", adj: "Slave Professional", deco: "Slave Professionalism"},
+		FSPetiteAdmiration: {noun: "Petite Admiration", adj: "Petite Admiration", deco: "Petite Admiration"},
+		FSStatuesqueGlorification: {noun: "Statuesque Glorification", adj: "Statuesque Glorification", deco: "Statuesque Glorification"}
 	};
-	/** @type {FC.FutureSociety[]} */
-	const SocietyList = [...new Set(Object.keys(FSString2Property).map(key => FSString2Property[key]))]; // This returns an array containing the unique values of FSString2Property. E.g. "FSSupremacist" and "FSSubjugationist"
-	/** @type {FC.FutureSociety[]} */
-	const NPCSocietyList = [ "FSCummunism", "FSIncestFetishist" ]; // NPC arcologies may use these FSes, but the PC can't
 
 	/** @type {FC.FutureSociety[][]} */
 	const FSMutexGroups = [
@@ -58,41 +54,17 @@ globalThis.FutureSocieties = (function() {
 		[ "FSPetiteAdmiration", "FSStatuesqueGlorification"]
 	];
 
-	/** @type {Record<FC.FutureSociety, {noun: FC.FutureSocietyNoun, adj: FC.FutureSocietyAdj}>} */
-	const DisplayName = {
-		FSSupremacist: {noun: "Racial Supremacism", adj: "Supremacist"},
-		FSSubjugationist: {noun: "Racial Subjugationism", adj: "Subjugationist"},
-		FSGenderRadicalist: {noun: "Gender Radicalism", adj: "Gender Radicalist"},
-		FSGenderFundamentalist: {noun: "Gender Fundamentalism", adj: "Gender Fundamentalist"},
-		FSDegradationist: {noun: "Degradationism", adj: "Degradationist"},
-		FSPaternalist: {noun: "Paternalism", adj: "Paternalist"},
-		FSBodyPurist: {noun: "Body Purism", adj: "Body Purist"},
-		FSTransformationFetishist: {noun: "Transformation Fetishism", adj: "Transformation Fetishist"},
-		FSYouthPreferentialist: {noun: "Youth Preferentialism", adj: "Youth Preferentialist"},
-		FSMaturityPreferentialist: {noun: "Maturity Preferentialism", adj: "Maturity Preferentialist"},
-		FSSlimnessEnthusiast: {noun: "Slimness Enthusiasm", adj: "Slimness Enthusiast"},
-		FSAssetExpansionist: {noun: "Asset Expansionism", adj: "Asset Expansionist"},
-		FSPastoralist: {noun: "Pastoralism", adj: "Pastoralist"},
-		FSCummunism: {noun: "Cummunism", adj: "Cummunist"},
-		FSPhysicalIdealist: {noun: "Physical Idealism", adj: "Physical Idealist"},
-		FSHedonisticDecadence: {noun: "Decadent Hedonism", adj: "Decadent Hedonist"},
-		FSChattelReligionist: {noun: "Chattel Religionism", adj: "Chattel Religionist"},
-		FSNull: {noun: "Multiculturalism", adj: "Multiculturalist"},
-		FSIncestFetishist: {noun: "Incest Fetishism", adj: "Incest Fetishist"},
-		FSRomanRevivalist: {noun: "Roman Revivalism", adj: "Roman Revivalist"},
-		FSNeoImperialist: {noun: "Neo-Imperialism", adj: "Neo-Imperialist"},
-		FSEgyptianRevivalist: {noun: "Egyptian Revivalism", adj: "Egyptian Revivalist"},
-		FSEdoRevivalist: {noun: "Edo Revivalism", adj: "Edo Revivalist"},
-		FSArabianRevivalist: {noun: "Arabian Revivalism", adj: "Arabian Revivalist"},
-		FSChineseRevivalist: {noun: "Chinese Revivalism", adj: "Chinese Revivalist"},
-		FSAztecRevivalist: {noun: "Aztec Revivalism", adj: "Aztec Revivalist"},
-		FSRepopulationFocus: {noun: "Repopulation Focus", adj: "Repopulationist"},
-		FSRestart: {noun: "Eugenics", adj: "Eugenics"},
-		FSIntellectualDependency: {noun: "Intellectual Dependency", adj: "Intellectual Dependency"},
-		FSSlaveProfessionalism: {noun: "Slave Professionalism", adj: "Slave Professional"},
-		FSPetiteAdmiration: {noun: "Petite Admiration", adj: "Petite Admiration"},
-		FSStatuesqueGlorification: {noun: "Statuesque Glorification", adj: "Statuesque Glorification"}
-	};
+	/** @type {FC.FutureSociety[]} */
+	const SocietyList = (/** @type {FC.FutureSociety[]} */ Object.keys(FSRecords));
+
+	/** @type {Map<FC.FutureSocietyDeco, FC.FutureSociety>} */
+	const DecoToFSMap = new Map();
+	for (const [fsName, details] of Object.entries(FSRecords)) {
+		if (details.deco) {
+			// @ts-ignore - Object.entries loses type information, apparently intentionally
+			DecoToFSMap.set(details.deco, fsName);
+		}
+	}
 
 	return {
 		activeFSes: activeFSes,
@@ -100,6 +72,7 @@ globalThis.FutureSocieties = (function() {
 		applyBroadProgress: applyBroadProgress,
 		availCredits: calcFSCredits,
 		influenceSources: influenceSources,
+		decorationName: decorationName,
 		diplomaticFSes: diplomaticFSes,
 		displayName: displayName,
 		displayAdj: displayAdj,
@@ -115,21 +88,19 @@ globalThis.FutureSocieties = (function() {
 
 	/** get the list of FSes active for a particular arcology
 	 * helper function, not callable externally
-	 * @param {number} arcologyID
+	 * @param {FC.ArcologyState} arcology
 	 * @returns {FC.FutureSociety[]}
 	 */
-	function activeFSes(arcologyID) {
-		let isSet = (fs) => V.arcologies[arcologyID][fs] !== "unset";
-		const npcFSes = arcologyID !== 0 ? NPCSocietyList.filter(isSet) : [];
-		return SocietyList.filter(isSet).concat(npcFSes);
+	function activeFSes(arcology) {
+		return SocietyList.filter((fs) => Number.isFinite(arcology[fs]));
 	}
 
-	/** call as FutureSocieties.activeCount(arcologyID)
-	 * @param {number} arcologyID
+	/** call as FutureSocieties.activeCount(arcology)
+	 * @param {FC.ArcologyState} arcology
 	 * @returns {number}
 	 */
-	function activeCount(arcologyID) {
-		return activeFSes(arcologyID).length;
+	function activeCount(arcology) {
+		return activeFSes(arcology).length;
 	}
 
 	/** call as FutureSocieties.applyBroadProgress(arcologyID, progress)
@@ -137,9 +108,10 @@ globalThis.FutureSocieties = (function() {
 	 * @param {number} progress
 	 */
 	function applyBroadProgress(arcologyID, progress) {
-		for (const fs of activeFSes(arcologyID)) {
+		const arcology = V.arcologies[arcologyID];
+		for (const fs of activeFSes(arcology)) {
 			if (fs !== "FSNull") { // does not progress this way
-				V.arcologies[arcologyID][fs] += progress;
+				arcology[fs] += progress;
 			}
 		}
 	}
@@ -150,7 +122,7 @@ globalThis.FutureSocieties = (function() {
 	 */
 	function overflowToInfluence(arcologyID) {
 		const arcology = V.arcologies[arcologyID];
-		for (const fs of activeFSes(arcologyID)) {
+		for (const fs of activeFSes(arcology)) {
 			if (fs !== "FSNull") { // no conventional progress
 				if (arcology[fs] > V.FSLockinLevel) {
 					arcology.influenceBonus += arcology[fs] - V.FSLockinLevel;
@@ -167,7 +139,7 @@ globalThis.FutureSocieties = (function() {
 	function influenceSources(arcologyID) {
 		let fses = [];
 		const arcology = V.arcologies[arcologyID];
-		for (const fs of activeFSes(arcologyID)) {
+		for (const fs of activeFSes(arcology)) {
 			if (fs !== "FSNull") { // no influence from Multiculturalism?
 				if (arcology[fs] > 60) {
 					fses.push(fs);
@@ -199,8 +171,8 @@ globalThis.FutureSocieties = (function() {
 	 */
 	function validAdoptions(arcID) {
 		const arcology = V.arcologies[arcID];
-		const societies = Array.from(arcID === 0 ? SocietyList : SocietyList.concat(NPCSocietyList));
-		const arcFSes = activeFSes(arcID);
+		const societies = Array.from(arcID !== 0 ? SocietyList : SocietyList.filter(fs => !FSRecords[fs].NPCOnly));
+		const arcFSes = activeFSes(arcology);
 
 		// apply game rules
 		if (!V.seeIncest) {
@@ -218,7 +190,7 @@ globalThis.FutureSocieties = (function() {
 
 		// 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(0);
+			const playerFSes = activeFSes(V.arcologies[0]);
 			societies.deleteWith(fs1 => playerFSes.some(fs2 => conflictingFSes(fs1, fs2)));
 		}
 
@@ -227,18 +199,18 @@ globalThis.FutureSocieties = (function() {
 
 	/** 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(arc1ID, arc2ID)
-	 * @param {number} arc1ID
-	 * @param {number} arc2ID
+	 * call as FutureSocieties.diplomaticFSes(arc1, arc2)
+	 * @param {FC.ArcologyState} arc1
+	 * @param {FC.ArcologyState} arc2
 	 * @returns {{shared: FC.FutureSociety[], conflicting: FC.FutureSociety[][]}}
 	 */
-	function diplomaticFSes(arc1ID, arc2ID) {
+	function diplomaticFSes(arc1, arc2) {
 		/** @type {FC.FutureSociety[]} */
 		let shared = [];
 		/** @type {FC.FutureSociety[][]} */
 		let conflicting = [];
-		const arc1FSes = activeFSes(arc1ID);
-		const arc2FSes = activeFSes(arc2ID);
+		const arc1FSes = activeFSes(arc1);
+		const arc2FSes = activeFSes(arc2);
 		// find ordinary shared and conflicting FSes
 		for (const fs1 of arc1FSes) {
 			for (const fs2 of arc2FSes) {
@@ -250,8 +222,6 @@ globalThis.FutureSocieties = (function() {
 			}
 		}
 		// special cases: racial FSes might be conflicting even when shared
-		const arc1 = V.arcologies[arc1ID];
-		const arc2 = V.arcologies[arc2ID];
 		if (shared.contains("FSSupremacist")) {
 			// a different race is supreme
 			if (arc1.FSSupremacistRace !== arc2.FSSupremacistRace) {
@@ -284,7 +254,7 @@ globalThis.FutureSocieties = (function() {
 	 * @returns {FC.FutureSocietyNoun}
 	 */
 	function displayName(FSProp) {
-		return DisplayName[FSProp].noun;
+		return FSRecords[FSProp].noun;
 	}
 
 	/** returns the future society adjective (typically an "ist") for the given property
@@ -292,7 +262,15 @@ globalThis.FutureSocieties = (function() {
 	 * @returns {FC.FutureSocietyAdj}
 	 */
 	function displayAdj(FSProp) {
-		return DisplayName[FSProp].adj;
+		return FSRecords[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 FSRecords[FSProp].deco;
 	}
 
 	/** decays all the FSes adopted by a particular arcology (for example, because of government instability)
@@ -301,8 +279,8 @@ globalThis.FutureSocieties = (function() {
 	 * @returns {FC.FutureSociety[]} FSes which purged completely
 	 */
 	function decayFSes(arcologyID) {
-		const fses = activeFSes(arcologyID);
 		const arc = V.arcologies[arcologyID];
+		const fses = activeFSes(arc);
 		/** @type {FC.FutureSociety[]} */
 		let purged = [];
 		for (const fs of fses) {
@@ -442,7 +420,7 @@ globalThis.FutureSocieties = (function() {
 	 */
 	function calcFSCredits() {
 		const arcology = V.arcologies[0];
-		let activeFS = activeCount(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) {
@@ -474,13 +452,22 @@ globalThis.FutureSocieties = (function() {
 		ValidateFacilityDecoration("farmyardDecoration");
 	}
 
-	/* helper function, not callable */
-	/* decoration should be passed as "facilityDecoration" in quotes. For example, ValidateFacilityDecoration("brothelDecoration"). The quotes are important, do not pass it as a story variable. */
+	/** helper function, not callable externally
+	 * @param {string} decoration should be passed as "facilityDecoration" in quotes. For example, ValidateFacilityDecoration("brothelDecoration"). The quotes are important, do not pass it as a story variable.
+	 */
 	function ValidateFacilityDecoration(decoration) {
-		const FSString = V[decoration].replace(/ /g, ''); // removes spaces
-		const activeFS = FSString2Property[FSString]; // gets the property name
+		// Backwards compatibility, do not remove
+		if (V[decoration] === "Hedonism" || V[decoration] === "Hedonistic Decadence") {
+			V[decoration] = "Hedonistic";
+		} else if (V[decoration] === "Repopulation Focus") {
+			V[decoration] = "Repopulationist";
+		} else if (V[decoration] === "Neo Imperialist") {
+			V[decoration] = "Neo-Imperialist";
+		}
+
+		const activeFS = DecoToFSMap.get(V[decoration]); // gets the property name
 
-		if (FSString === "standard") {
+		if (V[decoration] === "standard") {
 			// nothing to do
 		} else if (activeFS === undefined) {
 			// eslint-disable-next-line no-console
@@ -497,7 +484,7 @@ globalThis.FutureSocieties = (function() {
 
 	/** Apply the decoration bonus for a slave working in a facility to the FS
 	 * call as FutureSocieties.DecorationBonus()
-	 * @param {string} decoration - not quoted, just pass it straight in
+	 * @param {FC.FutureSocietyDeco} decoration - not quoted, just pass it straight in
 	 * @param {number} magnitude - will be multiplied by V.FSSingleSlaveRep
 	 */
 	function FSDecorationBonus(decoration, magnitude) {
@@ -505,8 +492,7 @@ globalThis.FutureSocieties = (function() {
 			return; // no bonus
 		}
 
-		const FSString = decoration.replace(/ /g, ''); // removes spaces
-		const FSProp = FSString2Property[FSString]; // gets the property name
+		const FSProp = DecoToFSMap.get(decoration); // gets the property name
 		const arc = V.arcologies[0];
 
 		if (FSProp && Number.isFinite(arc[FSProp])) {
@@ -515,18 +501,17 @@ globalThis.FutureSocieties = (function() {
 	}
 
 	/** call as FutureSocieties.Change()
-	 * @param {string} FSString should be in the FSString2Property object above
+	 * @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];
-		const activeFS = FSString2Property[FSString];
+		// @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 (activeFS === undefined) {
-			throw `Bad FS reference ${FSString}`;
-		} else if (Number.isFinite(arcology[activeFS])) {
+		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
@@ -534,6 +519,9 @@ globalThis.FutureSocieties = (function() {
 			repX(repChange, 'futureSocieties');
 			arcology[activeFS] += 0.05 * magnitude * V.FSSingleSlaveRep * bonusMultiplier;
 			return repChange;
+		} else {
+			console.log(`Attempted to change unset FS ${activeFS}`);
+			return 0;
 		}
 	}
 
diff --git a/src/js/health.js b/src/js/health.js
index 4e7d09d9896c10b9e76dc2133fef486d0ba9c8fb..20613e8d670ea8d76f15d317ae7954ddff06348d 100644
--- a/src/js/health.js
+++ b/src/js/health.js
@@ -69,11 +69,14 @@ globalThis.healthCure = function(slave, cure) {
  * Surgical procedures also depend on the PC's medicine skill
  * @param {App.Entity.SlaveState} slave
  * @param {number} damage
+ * @param {boolean} [cheat]
  * @returns {void}
  */
-globalThis.surgeryDamage = function(slave, damage) {
-	const playerSkillFactor = 1 + Math.clamp(Math.pow(V.PC.skill.medicine / 100, 2), 0, 1);
-	healthDamage(slave, Math.trunc(damage / playerSkillFactor));
+globalThis.surgeryDamage = function(slave, damage, cheat = false) {
+	if (!cheat) {
+		const playerSkillFactor = 1 + Math.clamp(Math.pow(V.PC.skill.medicine / 100, 2), 0, 1);
+		healthDamage(slave, Math.trunc(damage / playerSkillFactor));
+	}
 };
 
 /**
diff --git a/src/js/modification.js b/src/js/modification.js
index 7fbad8112b485f680e1361c873ac36a2e2e8aa15..ea240fbf91036a28ecd7694cc92d5ae128d55643 100644
--- a/src/js/modification.js
+++ b/src/js/modification.js
@@ -8,7 +8,6 @@
 App.Medicine.Modification.addScar = function(slave, scar, design, weight) {
 	/*
 	V.scarApplied = 1;
-	V.degradation += 10;
 	surgeryDamage(slave, 10); // dangerous to uncomment this as sometimes many scars are applied at once.
 	cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
 	surgeryDamage(slave, (V.PC.skill.medicine >= 100) ? Math.round(surgery.healthCosts / 2) : surgery.healthCosts);*/
@@ -33,7 +32,6 @@ App.Medicine.Modification.addScar = function(slave, scar, design, weight) {
 App.Medicine.Modification.removeScar = function(slave, scar, design) {
 	/*
 	V.scarApplied = 1;
-	V.degradation += 10;
 	surgeryDamage(slave, 10); //dangerous to uncomment this as sometimes many scars are applied at once.
 	cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
 	surgeryDamage(slave, (V.PC.skill.medicine >= 100) ? Math.round(surgery.healthCosts / 2) : surgery.healthCosts);*/
diff --git a/src/js/physicalDevelopment.js b/src/js/physicalDevelopment.js
index 818228a3ba2d2776e2de8ac19dddb27174ebc651..3c7c22eb6173886520d2d89fa0fe97ed0560164e 100644
--- a/src/js/physicalDevelopment.js
+++ b/src/js/physicalDevelopment.js
@@ -2350,12 +2350,6 @@ globalThis.physicalDevelopment = (function physicalDevelopment() {
 						slave.hips++;
 					}
 				}
-			} else if (slave.geneticQuirks.uterineHypersensitivity === 2 && physicalAgeSwap > 9) {
-				if (slave.hips < 3) {
-					if (jsRandom(1, 100) > 95 / uterineHypersensitivityMod) {
-						slave.hips++;
-					}
-				}
 			}
 		} else {
 			if (physicalAgeSwap === 14) {
diff --git a/src/js/porn.js b/src/js/porn.js
index 29e8c142f9a41a613b38ff4da6ee7bfd72b3b014..845823791d8a469b9bcc40eca826efd3cb8f3b0a 100644
--- a/src/js/porn.js
+++ b/src/js/porn.js
@@ -73,7 +73,7 @@ App.Porn.Genre.analAddict = {
 	prestigeDesc2: "$His many fans relish the sight of $him doing anything for a dick up $his ass",
 	prestigeDesc3: "Millions are intimately familiar with the sight of $his well-versed anus.",
 	hitText: function(slave) { return `${getPronouns(slave).His} complete obsession with taking things up ${getPronouns(slave).his} ass makes ${getPronouns(slave).him} a hit with viewers that enjoy hardcore anal.`; },
-	trinketShotDesc: function(slave) { return `showing ${getPronouns(slave).him} TEMPLATE`; },
+	trinketShotDesc: function(slave) { return `showing ${getPronouns(slave).him} taking a series of huge cocks up ${getPronouns(slave).his} ass.`; },
 	valid: function(slave) { return slave.sexualFlaw === "anal addict" && canDoAnal(slave); },
 	uiName: function() { return capFirstChar(this.fameName); }
 };
diff --git a/src/js/releaseRules.js b/src/js/releaseRules.js
index e16e71cac7e98ea7233a9e7f3156962923b418a5..b029004c3b2edd4bf6437706322092bf9f45da9b 100644
--- a/src/js/releaseRules.js
+++ b/src/js/releaseRules.js
@@ -42,7 +42,18 @@ App.Utils.hasFamilySex = function hasFamilySex(slave) {
 	if (V.seeIncest === 0 || slave.rules.release.family === 0) {
 		return false;
 	}
-	return jsDef(randomRelatedSlave(slave, (s) => this.sexAllowed(slave, s)));
+	// check fast cases first
+	if (slave.mother > 0 && this.sexAllowed(slave, getSlave(slave.mother))) {
+		return true;
+	}
+	if (slave.father > 0 && this.sexAllowed(slave, getSlave(slave.father))) {
+		return true;
+	}
+	// search exhaustively for sisters/daughters only if necessary
+	if (slave.sisters + slave.daughters === 0) {
+		return false;
+	}
+	return V.slaves.some(s => areRelated(slave, s) && this.sexAllowed(slave, s));
 };
 
 /**
diff --git a/src/js/removeSlave.js b/src/js/removeSlave.js
index 57cadb798d59a47d16cd46689c47519fd40a557e..bfb00233a59b00e978be4475f9ea221638db8578 100644
--- a/src/js/removeSlave.js
+++ b/src/js/removeSlave.js
@@ -35,6 +35,8 @@ globalThis.removeSlave = function(slave) {
 
 	V.favorites.delete(AS_ID);
 
+	V.researchLab.tasks = V.researchLab.tasks.filter((t) => t.slaveID !== AS_ID);
+
 	if (INDEX >= 0 && INDEX < LENGTH) {
 		if (V.incubator > 0) {
 			V.tanks.forEach(child => {
diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js
index 1971f0be30704a53d6b211fa31950d2f6255dc79..03ce426239d71eb0f92e498c642749506d27e6be 100644
--- a/src/js/rulesAssistantOptions.js
+++ b/src/js/rulesAssistantOptions.js
@@ -1071,7 +1071,8 @@ globalThis.rulesAssistantOptions = (function() {
 
 		render(element) {
 			const greeting = document.createElement("p");
-			greeting.innerHTML = `<em>${properTitle()}, I will review your slaves and make changes that will have a beneficial effect. Apologies, ${properTitle()}, but this function is... not fully complete. It may have some serious limitations. Please use the '${noDefaultSetting.text}' option to identify areas I should not address.</em>`;
+			greeting.innerHTML = `<em>${properTitle()}, I will review your slaves and make changes that will have a beneficial effect. Apologies, ${properTitle()}, but this function is... not fully complete. It may have some serious limitations. Please use the '${noDefaultSetting.text}' option to identify areas I should not address.
+			<br>For things like breast, butt, lip, dick and ball injections you need to only set the growth targets in Physical Regimen and I'll try to figure out how to best achieve them, you probably won't need a separate rules for each of them or have to worry about ending the injections.</em>`;
 			element.appendChild(greeting);
 			return element;
 		}
@@ -1315,7 +1316,7 @@ globalThis.rulesAssistantOptions = (function() {
 			}
 
 			const explanation = document.createElement("div");
-			explanation.innerHTML = `Insert <kbd>(slave) =></kbd> followed by a valid <a target='_blank' class='link-external' href='https://www.w3schools.com/js/js_comparisons.asp'>JavaScript comparison and/or logical operation</a>.`;
+			explanation.innerHTML = `Insert <kbd>(slave) =></kbd> followed by a valid <a target='_blank' class='link-external' href='https://www.w3schools.com/js/js_comparisons.asp'>JavaScript comparison and/or logical operation</a>. For variable names to use see <a target='_blank' class='link-external' href='https://gitgud.io/pregmodfan/fc-pregmod/-/blob/pregmod-master/slave%20variables%20documentation%20-%20Pregmod.txt'>this list</a>.`;
 			elem.appendChild(explanation);
 			return elem;
 		}
diff --git a/src/js/sexActsJS.js b/src/js/sexActsJS.js
index b0b2ba35957b642d2450482f83ab0c80b98a5358..818930d7541957bd65cef9aac1441b4832af4fac 100644
--- a/src/js/sexActsJS.js
+++ b/src/js/sexActsJS.js
@@ -319,6 +319,7 @@ globalThis.SimpleSexAct = (function() {
 	function SimpleSexActPlayer(slave, fuckCount = 1) {
 		let fuckTarget = 0;
 		let r = "";
+		/** @type {Array<FC.SlaveActs>} */
 		const sexArray = ["penetrative"];
 		if (V.PC.dick > 0) {
 			sexArray.push("penetrative", "penetrative");
@@ -326,6 +327,7 @@ globalThis.SimpleSexAct = (function() {
 		if (V.PC.vagina > -1) {
 			sexArray.push("vaginal");
 		}
+		/** @type {FC.SlaveActs} */
 		const playerSex = either(sexArray);
 
 		for (let i = 0; i < fuckCount; i++) {
@@ -416,7 +418,7 @@ globalThis.SimpleSexAct = (function() {
 /**
  * Increments a slave's personal counter and the global counter for a particular action.
  * @param {App.Entity.SlaveState | App.Entity.PlayerState} slave
- * @param {string} act oral, anal, etc
+ * @param {FC.SlaveActs} act oral, anal, etc
  * @param {number} count
  */
 globalThis.actX = function(slave, act, count = 1) {
@@ -481,9 +483,9 @@ globalThis.actX = function(slave, act, count = 1) {
 /**
  * Sex is between two. This is a handy wrapper for actX that emphasizes that.
  * @param {App.Entity.SlaveState} slave1 always a slave
- * @param {string} act1 oral, anal, etc
+ * @param {FC.SlaveActs} act1 oral, anal, etc
  * @param {FC.HumanState | "public" | "slaves"} slave2 slave or PC or "public"
- * @param {string} act2 oral, anal, etc
+ * @param {FC.SlaveActs} act2 oral, anal, etc
  * @param {number} [count=1]
  */
 globalThis.seX = function(slave1, act1, slave2, act2, count = 1) {
diff --git a/src/js/slaveCostJS.js b/src/js/slaveCostJS.js
index 3cf15015d3fa36147fa0f9063df63f57f415a021..86af6b013caccd66e70b7c522269f06c4a223713 100644
--- a/src/js/slaveCostJS.js
+++ b/src/js/slaveCostJS.js
@@ -492,7 +492,7 @@ globalThis.BeautyArray = (function() {
 		adjustBeauty(`Skill: Entertainment (${slave.skill.entertainment})`, (slave.skill.entertainment / 10));
 		adjustBeauty(`Skill: Whoring (${slave.skill.whoring})`, (slave.skill.whoring / 10));
 		adjustBeauty(`Age: Visual Age (${slave.visualAge})`, -(3 * slave.visualAge));
-		if (setup.entertainmentCareers.includes(slave.career)) {
+		if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 			adjustBeauty("Career: Entertainment", (20));
 		} else if (V.week - slave.weekAcquired >= 20 && slave.skill.entertainment >= 100) {
 			adjustBeauty("Experience: Entertainment", (10));
@@ -1811,26 +1811,28 @@ globalThis.FResultArray = (function() {
 	 * @param {App.Entity.SlaveState} slave
 	 */
 	function calcWorksWithRelatives(slave) {
-		V.slaves.forEach(potentialRel => {
-			if (isParentP(slave, potentialRel) && sameAssignmentP(slave, potentialRel)) {
-				adjustFResult(`Works with their parent(s)`, 1);
-				if (incestBonus) {
-					adjustFResult(`Works with their parent(s): incest bonus`, 1);
+		for (const potentialRel of V.slaves) {
+			if (sameAssignmentP(slave, potentialRel)) {
+				if (isParentP(slave, potentialRel)) {
+					adjustFResult(`Works with their parent(s)`, 1);
+					if (incestBonus) {
+						adjustFResult(`Works with their parent(s): incest bonus`, 1);
+					}
 				}
-			}
-			if (isParentP(potentialRel, slave) && sameAssignmentP(slave, potentialRel)) {
-				adjustFResult(`Works with their kid(s)`, 1);
-				if (incestBonus) {
-					adjustFResult(`Works with their kid(s): incest bonus`, 1);
+				if (isParentP(potentialRel, slave)) {
+					adjustFResult(`Works with their kid(s)`, 1);
+					if (incestBonus) {
+						adjustFResult(`Works with their kid(s): incest bonus`, 1);
+					}
 				}
-			}
-			if (areSisters(slave, potentialRel) > 0 && sameAssignmentP(slave, potentialRel)) {
-				adjustFResult(`Works with their sibling(s)`, 1);
-				if (incestBonus) {
-					adjustFResult(`Works with their sibling(s): incest bonus`, 1);
+				if (areSisters(slave, potentialRel) > 0) {
+					adjustFResult(`Works with their sibling(s)`, 1);
+					if (incestBonus) {
+						adjustFResult(`Works with their sibling(s): incest bonus`, 1);
+					}
 				}
 			}
-		});
+		}
 	}
 
 	/**
@@ -1895,7 +1897,7 @@ globalThis.FResultArray = (function() {
 			}
 		}
 		if ((V.arcologies[0].FSRepopulationFocusMilfPolicy === 1 || V.arcologies[0].FSRepopulationFocus > 20) && slave.counter.birthsTotal > 0) {
-			adjustFResult(`Belly: Elitist, MILF Policy`, 1);
+			adjustFResult(`Belly: Repopulationist, MILF Policy`, 1);
 		}
 	}
 
@@ -1958,7 +1960,7 @@ globalThis.FResultArray = (function() {
 	 * @param {App.Entity.SlaveState} slave
 	 */
 	function calcCareer(slave) {
-		if (setup.whoreCareers.includes(slave.career)) {
+		if (App.Data.Careers.General.whore.includes(slave.career)) {
 			adjustFResult(`Whore Knowledge: Career`, 1);
 		} else if (slave.counter.oral + slave.counter.anal + slave.counter.vaginal + slave.counter.mammary + slave.counter.penetrative > 1000) {
 			adjustFResult(`Whore Knowledge: Experience`, 1);
@@ -2534,95 +2536,95 @@ globalThis.slaveCostBeauty = (function() {
 		if (slave.career !== 0) {
 			if (slave.career === "a slave") {
 				multiplier += 0.1;
-			} else if (setup.bodyguardCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.bodyguard.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.wardenessCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.wardeness.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.attendantCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.attendant.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.matronCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.matron.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.schoolteacherCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.schoolteacher.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.stewardessCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.stewardess.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.milkmaidCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.milkmaid.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.farmerCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.madamCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.madam.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.DJCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.DJ.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.HGCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.HG.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.recruiterCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.recruiter.includes(slave.career)) {
 				multiplier += 0.1;
-			} else if (setup.entertainmentCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 				multiplier += 0.05;
-			} else if (setup.whoreCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.General.whore.includes(slave.career)) {
 				multiplier += 0.05;
-			} else if (setup.gratefulCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.General.grateful.includes(slave.career)) {
 				multiplier += 0.05;
-			} else if (setup.menialCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.General.menial.includes(slave.career)) {
 				multiplier += 0.05;
-			} else if (setup.servantCareers.includes(slave.career)) {
+			} else if (App.Data.Careers.Leader.servant.includes(slave.career)) {
 				multiplier += 0.05;
 			}
 		}
 		if (V.week - slave.weekAcquired >= 20 && slave.skill.entertainment >= 100) {
-			if (!setup.entertainmentCareers.includes(slave.career)) {
+			if (!App.Data.Careers.General.entertainment.includes(slave.career)) {
 				multiplier += 0.05;
 			}
 		}
 		if (slave.counter.oral + slave.counter.anal + slave.counter.vaginal + slave.counter.mammary + slave.counter.penetrative > 1000) {
-			if (!setup.whoreCareers.includes(slave.career)) {
+			if (!App.Data.Careers.General.whore.includes(slave.career)) {
 				multiplier += 0.05;
 			}
 		}
-		if (!setup.bodyguardCareers.includes(slave.career) && slave.skill.bodyguard >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.bodyguard.includes(slave.career) && slave.skill.bodyguard >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.wardenessCareers.includes(slave.career) && slave.skill.wardeness >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.wardeness.includes(slave.career) && slave.skill.wardeness >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.attendantCareers.includes(slave.career) && slave.skill.attendant >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.attendant.includes(slave.career) && slave.skill.attendant >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.matronCareers.includes(slave.career) && slave.skill.matron >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.matron.includes(slave.career) && slave.skill.matron >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.schoolteacherCareers.includes(slave.career) && slave.skill.teacher >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.schoolteacher.includes(slave.career) && slave.skill.teacher >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.stewardessCareers.includes(slave.career) && slave.skill.stewardess >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.stewardess.includes(slave.career) && slave.skill.stewardess >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.milkmaidCareers.includes(slave.career) && slave.skill.milkmaid >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.milkmaid.includes(slave.career) && slave.skill.milkmaid >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.farmerCareers.includes(slave.career) && slave.skill.farmer >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.farmer.includes(slave.career) && slave.skill.farmer >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.madamCareers.includes(slave.career) && slave.skill.madam >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.madam.includes(slave.career) && slave.skill.madam >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.DJCareers.includes(slave.career) && slave.skill.DJ >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.DJ.includes(slave.career) && slave.skill.DJ >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.HGCareers.includes(slave.career) && slave.skill.headGirl >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.HG.includes(slave.career) && slave.skill.headGirl >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.recruiterCareers.includes(slave.career) && slave.skill.recruiter >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.recruiter.includes(slave.career) && slave.skill.recruiter >= V.masteredXP) {
 			multiplier += 0.1;
 		}
-		if (!setup.servantCareers.includes(slave.career) && slave.skill.servant >= V.masteredXP) {
+		if (!App.Data.Careers.Leader.servant.includes(slave.career) && slave.skill.servant >= V.masteredXP) {
 			multiplier += 0.05;
 		}
-		if (!setup.entertainmentCareers.includes(slave.career) && slave.skill.entertainer >= V.masteredXP) {
+		if (!App.Data.Careers.General.entertainment.includes(slave.career) && slave.skill.entertainer >= V.masteredXP) {
 			multiplier += 0.05;
 		}
-		if (!setup.whoreCareers.includes(slave.career) && slave.skill.whore >= V.masteredXP) {
+		if (!App.Data.Careers.General.whore.includes(slave.career) && slave.skill.whore >= V.masteredXP) {
 			multiplier += 0.05;
 		}
 	}
diff --git a/src/js/slaveSummaryHelpers.js b/src/js/slaveSummaryHelpers.js
index 1272f093489d429dab11ff629df79f67f7d1b0b0..560dbf618cfe12cf8929f0a6864f63feec3e2fa8 100644
--- a/src/js/slaveSummaryHelpers.js
+++ b/src/js/slaveSummaryHelpers.js
@@ -1656,7 +1656,8 @@ App.UI.SlaveSummaryImpl = function() {
 				skills: short_skills,
 				prestige: short_prestige,
 				porn_prestige: short_porn_prestige,
-/*				clothes: short_clothes,
+				/*
+				clothes: short_clothes,
 				collar: short_collar,
 				belly: short_belly,
 				arms: short_arms,
@@ -1666,7 +1667,7 @@ App.UI.SlaveSummaryImpl = function() {
 				vaginal_acc: short_vaginal_acc,
 				dick_acc: short_dick_acc,
 				buttplug: short_buttplug,
-*/
+				*/
 				fetish: short_fetish,
 				attraction: short_attraction,
 				smart_piercing: short_smart_piercing,
@@ -1686,3 +1687,23 @@ App.UI.SlaveSummaryImpl = function() {
 		bits: bits
 	};
 }();
+
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @returns {node}
+ */
+App.UI.personalAttentionDevotionText = function(slave) {
+	const node = document.createDocumentFragment();
+	App.UI.SlaveSummaryImpl.helpers.makeRatedStyledSpan(node, App.Data.SlaveSummary.long.mental.devotion, slave.devotion, 100, true);
+	return node;
+};
+
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @returns {node}
+ */
+App.UI.personalAttentionHealthText = function(slave) {
+	const node = document.createDocumentFragment();
+	App.UI.SlaveSummaryImpl.helpers.makeRatedStyledSpan(node, App.Data.SlaveSummary.long.health.health, slave.health.condition, 100, true);
+	return node;
+};
diff --git a/src/js/slaveSummaryWidgets.js b/src/js/slaveSummaryWidgets.js
index 8922b784515fbfaa35304617c7824a3ecb85365c..80c2ef83d6e2e26b62397319f9e1cc831472f429 100644
--- a/src/js/slaveSummaryWidgets.js
+++ b/src/js/slaveSummaryWidgets.js
@@ -934,7 +934,7 @@ App.UI.SlaveSummary = function() {
 		if (slave.custom.label) {
 			helpers.makeSpan(res, `${capFirstChar(slave.custom.label)}.`, ["custom-label"]);
 		}
-		if ((slave.relationship !== 0) || (slave.relationship !== 0) || (abbrSettings.clothes === 2) || (abbrSettings.rulesets === 2)) {
+		if ((slave.relationship !== 0) || (totalRelatives(slave) > 0) || (abbrSettings.clothes === 2) || (abbrSettings.rulesets === 2)) {
 			para = helpers.makeParagraph(res);
 		}
 		delegates.relations(slave, para);
diff --git a/src/js/statsChecker/statsChecker.js b/src/js/statsChecker/statsChecker.js
index 5982f0690fdecd940fc94c1ecc43e6d687dccc57..461e30fdb1421aad863e3782e08c782694da47d0 100644
--- a/src/js/statsChecker/statsChecker.js
+++ b/src/js/statsChecker/statsChecker.js
@@ -834,9 +834,7 @@ globalThis.canStand = function(slave) {
 		return false;
 	} else if (!hasAnyLegs(slave)) {
 		return false;
-	} else if (slave.heels === 1 && (slave.shoes !== "pumps")) {
-		return false;
-	} else if (slave.heels === 1 && !setup.highHeels.includes(slave.shoes)) {
+	} else if (slave.heels === 1 && !setup.highHeels.includes(slave.shoes) && (slave.shoes !== "pumps")) {
 		return false;
 	} else if (tooFatSlave(slave)) {
 		return false;
@@ -905,6 +903,33 @@ globalThis.canMove = function(slave) {
 	return true;
 };
 
+/** Assuming the actor can walk, is their movement hindered by anything? Intended for the player, but could be applied to slaves.
+ * @param {App.Entity.SlaveState} slave
+ * @returns {boolean}
+ */
+globalThis.isHindered = function(slave) {
+	if (!slave) {
+		return null;
+	} else if (!canWalk(slave) && canMove(slave)) {
+		return true;
+	} else if (slave.weight >= 130 || (slave.weight >= 95 + ((slave.physicalAge - 9) * 5))) {
+		return true;
+	} else if (slave.belly >= 60000 || slave.belly >= 60000 / (1 + Math.Pow(Math.E, -0.4 * (slave.physicalAge - 14))) || slave.belly >= Math.max(10000, ((12500 / 19) * slave.height) - (1172500 / 19))) {
+		return true;
+	} else if (slave.boobs > 5000) {
+		return true;
+	} else if (slave.balls >= 14) {
+		return true;
+	} else if (slave.hips > 2) {
+		return true;
+	} else if (slave.butt > 6) {
+		return true;
+	} else if (slave.muscles < -30) {
+		return true;
+	}
+	return false;
+};
+
 /**
  * @param {App.Entity.SlaveState} slave
  * @param {boolean} checkLanguage Does a bad accent count as being unable to speak?
diff --git a/src/js/storyJS.js b/src/js/storyJS.js
index b2a89cadc6606761587d14c51aeb082f6324b25a..25cab312c4d852543b53db801848003cf59626eb 100644
--- a/src/js/storyJS.js
+++ b/src/js/storyJS.js
@@ -453,6 +453,33 @@ globalThis.overpowerCheck = function(slave, PC) {
 	return strength;
 };
 
+/**
+ * returns if arg2 can pick up and carry arg1
+ * @param {App.Entity.SlaveState} liftee
+ * @param {App.Entity.SlaveState} lifter
+ * @returns {boolean}
+ */
+globalThis.canLift = function(liftee, lifter) {
+	let sumWeight;
+
+	sumWeight = liftee.height - lifter.height;
+	sumWeight += liftee.weight / 2;
+	sumWeight += liftee.boobs / 500;
+	sumWeight += liftee.belly / 1000;
+	sumWeight += liftee.butt / 2;
+	if (liftee.hips > 0) {
+		sumWeight += Math.pow(liftee.hips, 2);
+	}
+	if (liftee.dick >= 20) {
+		sumWeight += Math.trunc(liftee.dick / 10);
+	}
+	if (liftee.balls >= 10) {
+		sumWeight += Math.pow(Math.trunc(liftee.balls / 10), 3);
+	}
+
+	return 20 + lifter.strength >= sumWeight && hasBothArms(lifter);
+};
+
 /**
  * returns array of IDs of all characters who impregnated slave
  * @param {FC.HumanState} slave
diff --git a/src/js/underperformingSlaves.js b/src/js/underperformingSlaves.js
index 04f1444bb0278fddb639b02ac3d012eb352ce9dc..1c97116d9dae19b4448fa991296741cb0ec8ed6d 100644
--- a/src/js/underperformingSlaves.js
+++ b/src/js/underperformingSlaves.js
@@ -15,7 +15,7 @@ App.Underperformers.highSale = function() {
 	const frag = App.UI.SlaveList.render(
 		getBestSlavesIDs(
 			{
-				part:(slave) => {
+				part: (slave) => {
 					const ratio = slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave));
 					return ratio > 0 ? ratio : 100000000 + ratio;
 				},
@@ -40,7 +40,7 @@ App.Underperformers.expensive = function() {
 	const frag = App.UI.SlaveList.render(
 		getBestSlavesIDs(
 			{
-				part:(slave) => (slave.lastWeeksCashIncome - getSlaveCost(slave)),
+				part: (slave) => (slave.lastWeeksCashIncome - getSlaveCost(slave)),
 				largest: false,
 				count: 7,
 				filter: App.Underperformers.expectIncome
diff --git a/src/js/utilsFC.js b/src/js/utilsFC.js
index 9528b93dd3353a294b5c8334b36c600a2fee823e..fcc21db0f89f8de5da5b7a41af6dcd795d5bdb67 100644
--- a/src/js/utilsFC.js
+++ b/src/js/utilsFC.js
@@ -2410,13 +2410,13 @@ globalThis.upgradeMultiplier = function(skill) {
  */
 globalThis.randomCareer = function(slave) {
 	if (slave.actualAge < 16) {
-		return setup.veryYoungCareers.random();
+		return App.Data.Careers.General.veryYoung.random();
 	} else if (slave.actualAge <= 24) {
-		return setup.youngCareers.random();
+		return App.Data.Careers.General.young.random();
 	} else if (slave.intelligenceImplant >= 10) {
-		return setup.educatedCareers.random();
+		return App.Data.Careers.General.educated.random();
 	} else {
-		return setup.uneducatedCareers.random();
+		return App.Data.Careers.General.uneducated.random();
 	}
 };
 
@@ -2509,7 +2509,7 @@ globalThis.randomRaceSkin = function(raceName) {
 };
 
 /**
- * @param {string} raceName
+ * @param {FC.Race} raceName
  * @returns {string}
  */
 globalThis.randomRaceEye = function(raceName) {
diff --git a/src/js/vignettes.js b/src/js/vignettes.js
index da558832ccd4373c724c1e8717dc868cb887af16..3254aa300aa9131da33d769abaca8470b5daa858 100644
--- a/src/js/vignettes.js
+++ b/src/js/vignettes.js
@@ -4195,7 +4195,7 @@ globalThis.GetVignette = function(slave) {
 			effect: 1,
 		});
 		vignettes.push({
-			text: `${he} was ordered to clean all the exterior windows around your estate using only ${his} tits. The people passing by seemed to appreciate the show,`,
+			text: `${he} was ordered to clean all the exterior windows around your estate using only ${his} ${slave.boobs >= 300 ? "tits" : "bare flat chest"}. The people passing by seemed to appreciate the show,`,
 			type: "rep",
 			effect: 1,
 		});
@@ -4620,7 +4620,6 @@ globalThis.GetVignette = function(slave) {
 	return jsEither(vignettes);
 
 
-
 	// MARK: Farmyard
 
 	function farmyardVignettes(slave) {
diff --git a/src/markets/bulkSlave/bulkSlaveIntro.js b/src/markets/bulkSlave/bulkSlaveIntro.js
index a1184fd8a8e0f8e53fcb44d9c5b3de06c7317df8..faf979a0d19879a2003d951196124b2bebd45f7b 100644
--- a/src/markets/bulkSlave/bulkSlaveIntro.js
+++ b/src/markets/bulkSlave/bulkSlaveIntro.js
@@ -201,7 +201,7 @@ App.Markets.bulkSlaveIntro = function() {
 			if (V.market.numArcology >= V.arcologies.length) {
 				V.market.numArcology = 1;
 			}
-			opinion = App.Neighbor.opinion(0, V.market.numArcology);
+			opinion = App.Neighbor.opinion(V.arcologies[0], V.arcologies[V.market.numArcology]);
 			opinion = Math.clamp(Math.trunc(opinion/20), -10, 10);
 			discount -= (opinion * 25);
 		} else if (App.Data.misc.schools.has(V.market.slaveMarket)) {
diff --git a/src/markets/specificMarkets/customSlaveMarket.js b/src/markets/specificMarkets/customSlaveMarket.js
index 2a6c7ad6d3b78fca0d792a1676834d89afb0c038..16c09e908488c3e92ab4e2bfc453ea1bd37cc6c1 100644
--- a/src/markets/specificMarkets/customSlaveMarket.js
+++ b/src/markets/specificMarkets/customSlaveMarket.js
@@ -80,7 +80,7 @@ App.Markets["Custom Slave"] = function() {
 			const option = document.createElement("option");
 			option.text = `${low}-${high}`;
 			option.value = high.toString();
-			if (slave.age === option.value) {
+			if (slave.age === high) {
 				option.selected = true;
 			}
 			select.append(option);
@@ -120,7 +120,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.health === Number(value)) {
+				if (slave.health === value) {
 					return `${text}. `;
 				}
 			}
@@ -133,14 +133,14 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "muscles";
 		const choices = new Map([
-			["96", "Ripped"],
-			["65", "Muscular"],
-			["45", "Well built"],
-			["20", "Toned"],
-			["0", "Normal"],
-			["-21", "Weak"],
-			["-51", "Very weak"],
-			["-97", "Frail"],
+			[96, "Ripped"],
+			[65, "Muscular"],
+			[45, "Well built"],
+			[20, "Toned"],
+			[0, "Normal"],
+			[-21, "Weak"],
+			[-51, "Very weak"],
+			[-97, "Frail"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -148,7 +148,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.muscles >= Number(value)) {
+				if (slave.muscles >= value) {
 					return `${text}. `;
 				}
 			}
@@ -161,12 +161,12 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "lips";
 		const choices = new Map([
-			["100", "Facepussy"],
-			["85", "Enormous"],
-			["65", "Big"],
-			["35", "Plush"],
-			["15", "Normal"],
-			["5", "Thin"],
+			[100, "Facepussy"],
+			[85, "Enormous"],
+			[65, "Big"],
+			[35, "Plush"],
+			[15, "Normal"],
+			[5, "Thin"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -174,8 +174,8 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.lips >= Number(value)) {
-					if (Number(value) < 100) {
+				if (slave.lips >= value) {
+					if (value < 100) {
 						return `Lips are ${text.toLowerCase()}. `;
 					} else {
 						return `Lips are a ${text.toLowerCase()}. `;
@@ -191,11 +191,11 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "voice";
 		const choices = new Map([
-			["3", "High, girly"],
-			["2", "Feminine"],
-			["1", "Deep"],
-			["0", "Mute"],
-			["-1", "Voice is unimportant"],
+			[3, "High, girly"],
+			[2, "Feminine"],
+			[1, "Deep"],
+			[0, "Mute"],
+			[-1, "Voice is unimportant"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -203,7 +203,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.voice === Number(value)) {
+				if (slave.voice === value) {
 					if (slave.voice === -1) {
 						return `${text}. `;
 					} else {
@@ -246,14 +246,14 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "weight";
 		const choices = new Map([
-			["200", "Immobile"],
-			["150", "Very Fat"],
-			["100", "Fat"],
-			["50", "Chubby"],
-			["15", "Plush"],
-			["0", "Average"],
-			["-15", "Thin"],
-			["-50", "Very thin"],
+			[200, "Immobile"],
+			[150, "Very Fat"],
+			[100, "Fat"],
+			[50, "Chubby"],
+			[15, "Plush"],
+			[0, "Average"],
+			[-15, "Thin"],
+			[-50, "Very thin"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -261,7 +261,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.weight >= Number(value)) {
+				if (slave.weight >= value) {
 					return `${text} weight. `;
 				}
 			}
@@ -274,11 +274,11 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "face";
 		const choices = new Map([
-			["55", "Very attractive"],
-			["15", "Attractive"],
-			["0", "Average"],
-			["-15", "Unattractive"],
-			["-55", "Very unattractive"],
+			[55, "Very attractive"],
+			[15, "Attractive"],
+			[0, "Average"],
+			[-15, "Unattractive"],
+			[-55, "Very unattractive"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -286,7 +286,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.face >= Number(value)) {
+				if (slave.face >= value) {
 					return `${text} face. `;
 				}
 			}
@@ -314,7 +314,7 @@ App.Markets["Custom Slave"] = function() {
 			const option = document.createElement("option");
 			option.text = text;
 			option.value = value;
-			if (slave.race == option.value) {
+			if (slave.race === option.value) {
 				option.selected = true;
 			}
 			select.append(option);
@@ -361,7 +361,7 @@ App.Markets["Custom Slave"] = function() {
 			const option = document.createElement("option");
 			option.text = text;
 			option.value = value;
-			if (slave.skin == option.value) {
+			if (slave.skin === option.value) {
 				option.selected = true;
 			}
 			select.append(option);
@@ -395,12 +395,12 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "boobs";
 		const choices = new Map([
-			["6000", "Massive"],
-			["2100", "Giant"],
-			["1400", "Huge"],
-			["800", "Big"],
-			["500", "Healthy"],
-			["200", "Flat"],
+			[6000, "Massive"],
+			[2100, "Giant"],
+			[1400, "Huge"],
+			[800, "Big"],
+			[500, "Healthy"],
+			[200, "Flat"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -408,7 +408,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.boobs >= Number(value)) {
+				if (slave.boobs >= value) {
 					if (slave.boobs <= 200) {
 						return `${text} chest. `;
 					} else {
@@ -425,10 +425,10 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "butt";
 		const choices = new Map([
-			["8", "Massive"],
-			["5", "Huge"],
-			["3", "Healthy"],
-			["1", "Flat"],
+			[8, "Massive"],
+			[5, "Huge"],
+			[3, "Healthy"],
+			[1, "Flat"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -436,7 +436,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.butt >= Number(value)) {
+				if (slave.butt >= value) {
 					return `${text} buttocks. `;
 				}
 			}
@@ -449,9 +449,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "sex";
 		const choices = new Map([
-			["3", "Both"],
-			["2", "Male"],
-			["1", "Female"],
+			[3, "Both"],
+			[2, "Male"],
+			[1, "Female"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -475,8 +475,8 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "virgin";
 		const choices = new Map([
-			["1", "Not Important"],
-			["0", "Vaginal virgin"],
+			[1, "Not Important"],
+			[0, "Vaginal virgin"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -484,7 +484,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.virgin === Number(value)) {
+				if (slave.virgin === value) {
 					return `${text}. `;
 				}
 			}
@@ -498,9 +498,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "dick";
 		const choices = new Map([
-			["4", "Large penis"],
-			["2", "Small penis"],
-			["0", "No penis"],
+			[4, "Large penis"],
+			[2, "Small penis"],
+			[0, "No penis"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -508,7 +508,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.dick >= Number(value)) {
+				if (slave.dick >= value) {
 					return `${text}. `;
 				}
 			}
@@ -521,10 +521,10 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "balls";
 		const choices = new Map([
-			["6", "Huge testicles"],
-			["4", "Large testicles"],
-			["2", "Small testicles"],
-			["0", "No testicles"],
+			[6, "Huge testicles"],
+			[4, "Large testicles"],
+			[2, "Small testicles"],
+			[0, "No testicles"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -532,7 +532,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.balls >= Number(value)) {
+				if (slave.balls >= value) {
 					return `${text}. `;
 				}
 			}
@@ -545,10 +545,10 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "clit";
 		const choices = new Map([
-			["5", "Clit dick"],
-			["3", "Enormous clitoris"],
-			["1", "Big clitoris"],
-			["0", "Normal clitoris"],
+			[5, "Clit dick"],
+			[3, "Enormous clitoris"],
+			[1, "Big clitoris"],
+			[0, "Normal clitoris"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -559,7 +559,7 @@ App.Markets["Custom Slave"] = function() {
 				if (slave.clit === 5) {
 					return `Pseudophallus. `;
 				} else {
-					if (slave.clit >= Number(value)) {
+					if (slave.clit >= value) {
 						return `${text}. `;
 					}
 				}
@@ -573,10 +573,10 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "labia";
 		const choices = new Map([
-			["3", "Enormous labia"],
-			["2", "Huge labia"],
-			["1", "Big labia"],
-			["0", "Normal labia"],
+			[3, "Enormous labia"],
+			[2, "Huge labia"],
+			[1, "Big labia"],
+			[0, "Normal labia"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -584,7 +584,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.labia === Number(value)) {
+				if (slave.labia === value) {
 					return `${text}. `;
 				}
 			}
@@ -597,9 +597,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "vaginaLube";
 		const choices = new Map([
-			["2", "Sopping wet vagina"],
-			["1", "Wet vagina"],
-			["0", "Dry vagina"],
+			[2, "Sopping wet vagina"],
+			[1, "Wet vagina"],
+			[0, "Dry vagina"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -607,7 +607,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.vaginaLube === Number(value)) {
+				if (slave.vaginaLube === value) {
 					return `${text}. `;
 				}
 			}
@@ -620,8 +620,8 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "analVirgin";
 		const choices = new Map([
-			["1", "Anal virginity is not important"],
-			["0", "Anal virgin"],
+			[1, "Anal virginity is not important"],
+			[0, "Anal virgin"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -629,7 +629,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.analVirgin === Number(value)) {
+				if (slave.analVirgin === value) {
 					return `${text}. `;
 				}
 			}
@@ -642,9 +642,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "skills";
 		const choices = new Map([
-			["65", "Expert"],
-			["35", "Skilled"],
-			["0", "Unskilled"],
+			[65, "Expert"],
+			[35, "Skilled"],
+			[0, "Unskilled"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -667,9 +667,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "skill.whore";
 		const choices = new Map([
-			["35", "Expert"],
-			["15", "Skilled"],
-			["0", "Unskilled"],
+			[35, "Expert"],
+			[15, "Skilled"],
+			[0, "Unskilled"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -692,8 +692,8 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "skill.combat";
 		const choices = new Map([
-			["1", "Skilled"],
-			["0", "Unskilled"],
+			[1, "Skilled"],
+			[0, "Unskilled"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -701,7 +701,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.skill.combat >= Number(value)) {
+				if (slave.skill.combat >= value) {
 					return `${text} at combat. `;
 				}
 			}
@@ -714,13 +714,13 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "intelligence";
 		const choices = new Map([
-			["-3", "Moronic"],
-			["-2", "Very stupid"],
-			["-1", "Stupid"],
-			["0", "Average intelligence"],
-			["1", "Smart"],
-			["2", "Very smart"],
-			["3", "Brilliant"],
+			[-3, "Moronic"],
+			[-2, "Very stupid"],
+			[-1, "Stupid"],
+			[0, "Average intelligence"],
+			[1, "Smart"],
+			[2, "Very smart"],
+			[3, "Brilliant"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -728,7 +728,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.intelligence === Number(value)) {
+				if (slave.intelligence === value) {
 					return `${text}. `;
 				}
 			}
@@ -741,9 +741,9 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "intelligenceImplant";
 		const choices = new Map([
-			["30", "Well educated"],
-			["15", "Educated"],
-			["0", "Uneducated"],
+			[30, "Well educated"],
+			[15, "Educated"],
+			[0, "Uneducated"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -751,7 +751,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.intelligenceImplant >= Number(value)) {
+				if (slave.intelligenceImplant >= value) {
 					return `${text}. `;
 				}
 			}
@@ -822,11 +822,11 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "hears";
 		const choices = new Map([
-			["0", "Normal Hearing"],
-			["-1", "Hard of Hearing"],
+			[0, "Normal Hearing"],
+			[-1, "Hard of Hearing"],
 		]);
 		if (V.seeExtreme) {
-			choices.set("-2", "Deaf");
+			choices.set(-2, "Deaf");
 		}
 
 		createDescription(el, description, slaveProperty);
@@ -834,7 +834,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.hears === Number(value)) {
+				if (slave.hears === value) {
 					return `${text}. `;
 				}
 			}
@@ -848,8 +848,8 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "smells";
 		const choices = new Map([
-			["0", "Normal Sense of smell"],
-			["-1", "No Sense of smell"],
+			[0, "Normal Sense of smell"],
+			[-1, "No Sense of smell"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -857,7 +857,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.smells === Number(value)) {
+				if (slave.smells === value) {
 					return `${text}.`;
 				}
 			}
@@ -871,8 +871,8 @@ App.Markets["Custom Slave"] = function() {
 		const el = document.createElement("div");
 		const slaveProperty = "tastes";
 		const choices = new Map([
-			["0", "Normal Sense of taste"],
-			["-1", "No Sense of taste"],
+			[0, "Normal Sense of taste"],
+			[-1, "No Sense of taste"],
 		]);
 
 		createDescription(el, description, slaveProperty);
@@ -880,7 +880,7 @@ App.Markets["Custom Slave"] = function() {
 
 		function description() {
 			for (const [value, text] of choices) {
-				if (slave.tastes === Number(value)) {
+				if (slave.tastes === value) {
 					return `${text}.`;
 				}
 			}
@@ -1056,7 +1056,7 @@ App.Markets["Custom Slave"] = function() {
 				App.UI.DOM.link(
 					text,
 					() => {
-						_.set(slave, slaveParam, (Number(value) || value));
+						_.set(slave, slaveParam, value);
 						jQuery(`#${(slaveParam).replace(/\./g, "-")}-text`).empty().append(description());
 					}
 				)
diff --git a/src/markets/specificMarkets/householdLiquidator.js b/src/markets/specificMarkets/householdLiquidator.js
index efd7de4b4905e6cf605849f2590e277d2c74c7ca..a2a355b198cdb3d2614093dba2e76fba00e7655e 100644
--- a/src/markets/specificMarkets/householdLiquidator.js
+++ b/src/markets/specificMarkets/householdLiquidator.js
@@ -9,7 +9,10 @@ App.Markets["Household Liquidator"] = function() {
 	const _newSlaves = [];
 	let _totalCost;
 	if (jsRandom(1, 100) > 50) {
-		slave = GenerateNewSlave(null, {disableDisability: 1});
+		// Old enough to have a younger sibling who can be a slave.
+		slave = GenerateNewSlave(null, {
+			minAge: (V.minimumSlaveAge + 2), disableDisability: 1
+		});
 		finishSlave();
 		setMissingParents(slave);
 
diff --git a/src/markets/specificMarkets/slaveMarkets.js b/src/markets/specificMarkets/slaveMarkets.js
index 43ac7235a419d0952288f13a4f9c623b62b13a0d..7052572814da2ce4f30b9b6a94357493466f2360 100644
--- a/src/markets/specificMarkets/slaveMarkets.js
+++ b/src/markets/specificMarkets/slaveMarkets.js
@@ -193,19 +193,20 @@ App.Markets.raiders = function() {
 };
 
 App.Markets.neighbor = function() {
+	const arcology = V.arcologies[V.market.numArcology];
 	const el = new DocumentFragment();
 	el.append(`You're in the area of the slave market that specializes in slaves from within the Free City, viewing slaves from `);
-	App.UI.DOM.appendNewElement("span", el, `${V.arcologies[V.market.numArcology].name}.`, "bold");
+	App.UI.DOM.appendNewElement("span", el, `${arcology.name}.`, "bold");
 	el.append(` Some were trained there, specifically for sale, while others are simply being sold.`);
-	let _opinion = App.Neighbor.opinion(0, V.market.numArcology);
+	const opinion = App.Neighbor.opinion(V.arcologies[0], arcology);
 	let costMod = 1;
-	if (_opinion !== 0) {
-		if (_opinion > 2) {
-			el.append(` Your cultural ties with ${V.arcologies[V.market.numArcology].name} helps keep the price reasonable.`);
-		} else if (_opinion < -2) {
-			el.append(` Your social misalignment with ${V.arcologies[V.market.numArcology].name} drives up the price.`);
+	if (opinion !== 0) {
+		if (opinion > 2) {
+			el.append(` Your cultural ties with ${arcology.name} helps keep the price reasonable.`);
+		} else if (opinion < -2) {
+			el.append(` Your social misalignment with ${arcology.name} drives up the price.`);
 		}
-		costMod = (1 - (_opinion * 0.05));
+		costMod = (1 - (opinion * 0.05));
 	}
 
 	el.append(App.Markets.purchaseFramework("neighbor", {costMod: costMod}));
diff --git a/src/markets/specificMarkets/slaveShelter.js b/src/markets/specificMarkets/slaveShelter.js
index fbc363122d8e45dc313426f502a9c23588c427a3..35d494e7ec28acd64f2587cc061814ac0c23b04e 100644
--- a/src/markets/specificMarkets/slaveShelter.js
+++ b/src/markets/specificMarkets/slaveShelter.js
@@ -175,7 +175,7 @@ App.Markets["Slave Shelter"] = function() {
 					break;
 				case "degraded DoL":
 					V.shelterSlave = GenerateNewSlave(null, {minAge: 14, disableDisability: 1, ageOverridesPedoMode: 1});
-					V.shelterSlave.origin = "$He is an enslaved Daughter of Liberty, caught some weeks after the failed coup. $His previous owner used $him as a punching bag and dart board, then when $he was bored of $him tattooed obscenities all over $his body and threw $him away.";
+					V.shelterSlave.origin = "$He is an enslaved Daughter of Liberty, caught some weeks after the failed coup. $His previous owner used $him as a punching bag and dart board, then when he was bored of $him tattooed obscenities all over $his body and threw $him away.";
 					V.shelterSlave.career = "a slave";
 					V.shelterSlave.devotion = jsRandom(-100, -90);
 					V.shelterSlave.trust = jsRandom(-100, -90);
@@ -283,7 +283,7 @@ App.Markets["Slave Shelter"] = function() {
 						minAge = 30;
 						maxAge = 42;
 					}
-					V.shelterSlave = GenerateNewSlave("XX", {minAge: minAge, maxAge: maxAge, disableDisability: 1});
+					V.shelterSlave = GenerateNewSlave("XX", {minAge: minAge, maxAge: maxAge, disableDisability: 1, ageOverridesPedoMode: pedo});
 					V.shelterSlave.origin = "$His previous owner discarded $him after many pregnancies.";
 					V.shelterSlave.career = "a breeder";
 					V.shelterSlave.devotion = jsRandom(-75, -60);
diff --git a/src/neighbor/arcologyDiplomacy.js b/src/neighbor/arcologyDiplomacy.js
index 26343a9e6c546724fa8de6e81bbb11126c24e7e7..e9931855a1497187063b9edad42c1275808a17e1 100644
--- a/src/neighbor/arcologyDiplomacy.js
+++ b/src/neighbor/arcologyDiplomacy.js
@@ -1,12 +1,10 @@
 /** get one arcology's opinion of another
- * @param {number} activeID index
- * @param {number} targetID index
+ * @param {FC.ArcologyState} activeArcology index
+ * @param {FC.ArcologyState} targetArcology index
  * @returns {number} opinion
  */
-App.Neighbor.opinion = function(activeID, targetID) {
-	const activeArcology = V.arcologies[activeID];
-	const targetArcology = V.arcologies[targetID];
-	const {shared, conflicting} = FutureSocieties.diplomaticFSes(activeID, targetID);
+App.Neighbor.opinion = function(activeArcology, targetArcology) {
+	const {shared, conflicting} = FutureSocieties.diplomaticFSes(activeArcology, targetArcology);
 
 	let opinion = 0;
 
@@ -43,11 +41,10 @@ App.Neighbor.selectInfluenceTarget = function(arcID) {
 		let eligibleTargets = [];
 		const obedient = (arcology.government === "your trustees" || arcology.government === "your agent");
 
-		for (let targetID = 0; targetID < V.arcologies.length; ++targetID) {
-			const target = V.arcologies[targetID];
+		for (const target of V.arcologies) {
 			if (arcology.direction !== target.direction) {
 				if (!obedient || target.direction !== 0) {
-					const {shared, conflicting} = FutureSocieties.diplomaticFSes(arcID, targetID);
+					const {shared, conflicting} = FutureSocieties.diplomaticFSes(arcology, target);
 					let count = 0;
 					count += shared.filter(notMulticulturalism).length;
 					count += conflicting.filter((pair) => pair.every(notMulticulturalism)).length;
@@ -68,15 +65,16 @@ App.Neighbor.PassiveFSInfluence = class {
 	 */
 	constructor(arcID) {
 		this._arcID = arcID;
-		/** @type {Map<number, {shared: string[], conflicting: string[][]}>} */
+		/** @type {Map<number, {shared: FC.FutureSociety[], conflicting: FC.FutureSociety[][]}>} */
 		this._relationships = new Map();
+
+		const arcology = V.arcologies[this._arcID];
 		for (let i = 0; i < V.arcologies.length; ++i) {
 			if (i !== arcID) {
-				this._relationships.set(i, FutureSocieties.diplomaticFSes(arcID, i));
+				this._relationships.set(i, FutureSocieties.diplomaticFSes(arcology, V.arcologies[i]));
 			}
 		}
 
-		const arcology = V.arcologies[this._arcID];
 		this._thresh = 5;
 		if (arcology.direction === 0) {
 			this._thresh -= V.policies.culturalOpenness * 5;
@@ -87,12 +85,12 @@ App.Neighbor.PassiveFSInfluence = class {
 	}
 
 	/** output the neighbors that have passively influenced a particular FS in this arcology
-	 * @param {string} fs
+	 * @param {FC.FutureSociety} fs
 	 */
 	output(fs) {
 		/** @type {number[]} */
 		let shared = [];
-		/** @type {Map<string, number[]>} */
+		/** @type {Map<FC.FutureSociety, number[]>} */
 		let conflicting = new Map();
 		const arcology = V.arcologies[this._arcID];
 
@@ -125,9 +123,9 @@ App.Neighbor.PassiveFSInfluence = class {
 		arcology[fs] += shared.length;
 		if (V.showNeighborDetails && shared.length > 0) {
 			t.push(FutureSocieties.displayName(fs));
-			if (fs === "FSSubjugationism") {
+			if (fs === "FSSubjugationist") {
 				t.push(`of ${arcology.FSSubjugationistRace} people`);
-			} else if (fs === "FSSupremacy") {
+			} else if (fs === "FSSupremacist") {
 				t.push(`for ${arcology.FSSupremacistRace} people`);
 			}
 			t.push(`in ${arcology.name} is influenced by`);
@@ -139,10 +137,11 @@ App.Neighbor.PassiveFSInfluence = class {
 		// passive slowing influence
 		arcology[fs] -= conflicting.size;
 		if (V.showNeighborDetails && conflicting.size > 0) {
+			/** @type {string} */
 			let actualDisplayName = FutureSocieties.displayName(fs);
-			if (fs === "FSSubjugationism") {
+			if (fs === "FSSubjugationist") {
 				actualDisplayName = `${arcology.FSSubjugationistRace} Subjugationism`;
-			} else if (fs === "FSSupremacy") {
+			} else if (fs === "FSSupremacist") {
 				actualDisplayName = `${arcology.FSSupremacistRace} Supremacy`;
 			}
 			t.push(`Development of ${actualDisplayName} in ${arcology.name} is slowed by contact with`);
diff --git a/src/neighbor/neighborDisplay.js b/src/neighbor/neighborDisplay.js
index 7f035f2e115259b5160c28b1555675ac7fe98c5b..8e5d962fd1b7d81fe2e2fee7580d6cebfb709a04 100644
--- a/src/neighbor/neighborDisplay.js
+++ b/src/neighbor/neighborDisplay.js
@@ -127,14 +127,13 @@ App.Neighbor.Display = class {
 		function fsFrag() {
 			let frag = document.createDocumentFragment();
 			frag.appendChild(document.createTextNode("FS: "));
+			const fses = FutureSocieties.activeFSes(arcology);
 			if (arcID === 0) {
-				const fses = FutureSocieties.activeFSes(0);
 				for (const fs of fses) {
 					frag.appendChild(withTooltip("⯁", FutureSocieties.displayName(fs), "steelblue"));
 				}
 			} else {
-				const fses = FutureSocieties.activeFSes(arcID);
-				const diplo = FutureSocieties.diplomaticFSes(arcID, 0);
+				const diplo = FutureSocieties.diplomaticFSes(arcology, V.arcologies[0]);
 				for (const fs of fses) {
 					let style = "white";
 					if (diplo.shared.includes(fs)) {
diff --git a/src/neighbor/neighborInteract.js b/src/neighbor/neighborInteract.js
index 85b8f77f57046793841670212a2c1d185f0d4033..d97e7bf72689244e2ca69640739276515373f89c 100644
--- a/src/neighbor/neighborInteract.js
+++ b/src/neighbor/neighborInteract.js
@@ -63,11 +63,11 @@ App.Neighbor.Interact = (function() {
 	function details(arcID) {
 		let frag = document.createDocumentFragment();
 		if (arcID === 0) {
-			const desc = FutureSocieties.activeFSes(0).map((f) => FutureSocieties.displayName(f));
+			const desc = FutureSocieties.activeFSes(V.arcologies[0]).map((f) => FutureSocieties.displayName(f));
 			if (desc.length === 0) {
 				App.UI.DOM.appendNewElement("p", frag, `Your arcology's culture has not developed to the point where it can meaningfully influence other arcologies.`);
 			} else if (desc.length > 2) {
-				App.UI.DOM.appendNewElement("p", frag, `Your arcology's mature culture is capable of exerting great cultural sway over other arcologies. It can readily project ${desc.reduce((res, ch, i, arr) => res + (i === arr.length - 1 ? ' and ' : ', ') + ch)}.`);
+				App.UI.DOM.appendNewElement("p", frag, `Your arcology's mature culture is capable of exerting great cultural sway over other arcologies. It can readily project ${arrayToSentence(desc)}.`);
 			} else if (desc.length === 2) {
 				App.UI.DOM.appendNewElement("p", frag, `Your arcology's culture is capable of exerting some cultural sway over other arcologies. It can effectively project ${desc[0]} and ${desc[1]}.`);
 			} else {
@@ -203,7 +203,7 @@ App.Neighbor.Interact = (function() {
 			}
 			if (agent.career === "an arcology owner") {
 				r.push(`${His} career as an arcology owner ${himself} is, obviously, useful to ${him}.`);
-			} else if (setup.HGCareers.includes(agent.career)) {
+			} else if (App.Data.Careers.Leader.HG.includes(agent.career)) {
 				r.push(`${His} career in leadership helps ${him}.`);
 			}
 			if (agent.fetishStrength > 95) {
@@ -223,7 +223,7 @@ App.Neighbor.Interact = (function() {
 
 		container.append(document.createElement("br"));
 		const forceAbandonment = (fs) => { V.arcologies[arcID][fs] = "unset"; arcChanged(arcID); };
-		for (const fs of FutureSocieties.activeFSes(arcID)) {
+		for (const fs of FutureSocieties.activeFSes(V.arcologies[arcID])) {
 			App.UI.DOM.appendNewElement("div", container, App.UI.DOM.link(`Force abandonment of ${FutureSocieties.displayName(fs)}`, forceAbandonment, [fs]));
 		}
 
@@ -236,17 +236,18 @@ App.Neighbor.Interact = (function() {
 	 */
 	function fsGoods(arcID) {
 		const container = document.createElement("div");
+		const arcology = V.arcologies[arcID];
 		let r = [];
-		r.push(`If ${V.arcologies[arcID].name} has developed enough to begin exporting worthwhile goods, it may be of interest to acquire some.`);
-		const opinionDiscount = App.Neighbor.opinion(arcID, 0)*10;
+		r.push(`If ${arcology.name} has developed enough to begin exporting worthwhile goods, it may be of interest to acquire some.`);
+		const opinionDiscount = App.Neighbor.opinion(arcology, V.arcologies[0])*10;
 		const basePrice = Math.trunc((7500-opinionDiscount)*V.upgradeMultiplierTrade);
-		const controlled = (V.arcologies[arcID].government === "your trustees") || (V.arcologies[arcID].government === "your agent");
+		const controlled = (arcology.government === "your trustees") || (arcology.government === "your agent");
 		if (controlled) {
 			r.push(`Since it is under your control, it is no problem at all to request the transfer of goods to ${V.arcologies[0].name}.`);
 		} else if (V.PC.skill.hacking >= 50) {
 			r.push(`It is within your skills to redirect an outgoing shipment to ${V.arcologies[0].name} for your retrieval.`);
-		} else if (V.arcologies[arcID].direction === V.arcologies[0].embargoTarget) {
-			r.push(`However, due to your active embargo, trade with ${V.arcologies[arcID].name} is not possible.`);
+		} else if (arcology.direction === V.arcologies[0].embargoTarget) {
+			r.push(`However, due to your active embargo, trade with ${arcology.name} is not possible.`);
 		}
 		App.UI.DOM.appendNewElement("p", container, r.join(' '));
 
@@ -261,7 +262,7 @@ App.Neighbor.Interact = (function() {
 		 * @param {number} [itemPrice] - the price the player should pay for the item; by default, basePrice (computed above)
 		 */
 		function addAcquisitionBlock(fsRequired, itemName, category, itemDisplay, property, itemPrice = basePrice) {
-			if (V.arcologies[arcID][fsRequired] > 95) {
+			if (arcology[fsRequired] > 95) {
 				if (!isItemAccessible.entry(itemName, category)) {
 					if (controlled) {
 						const link = App.UI.DOM.link(`Request a shipment of ${itemDisplay}`, (f) => {
@@ -275,7 +276,7 @@ App.Neighbor.Interact = (function() {
 							replaceDetails(arcID);
 						});
 						App.UI.DOM.appendNewElement("div", container, link);
-					} else if (V.arcologies[arcID].direction !== V.arcologies[0].embargoTarget) {
+					} else if (arcology.direction !== V.arcologies[0].embargoTarget) {
 						const link = App.UI.DOM.link(`Divert an outgoing shipment of ${itemDisplay}`, (f) => {
 							property();
 							cashX(forceNeg(itemPrice), "capEx");
@@ -313,7 +314,7 @@ App.Neighbor.Interact = (function() {
 		addAcquisitionBlock("FSStatuesqueGlorification", "platform heels", "shoes", "platform shoes", () => { V.boughtItem.shoes.heels = 1; });
 
 		if (exports !== 1) {
-			const luck = (V.arcologies[arcID].direction === V.arcologies[0].embargoTarget) ? `Fortunately` : `Unfortunately`;
+			const luck = (arcology.direction === V.arcologies[0].embargoTarget) ? `Fortunately` : `Unfortunately`;
 			App.UI.DOM.appendNewElement("p", container, `${luck}, they have nothing of value.`);
 		}
 
diff --git a/src/npc/acquisition.tw b/src/npc/acquisition.tw
deleted file mode 100644
index fc154982980471561a7fc3cdd785b868107bcc10..0000000000000000000000000000000000000000
--- a/src/npc/acquisition.tw
+++ /dev/null
@@ -1,889 +0,0 @@
-:: Acquisition [nobr]
-
-<<set $encyclopedia = "How to Play">>
-<<unset $showSGNationalities>>
-
-<<if $saveImported == 1>><<set _valueOwed = 5000>><<else>><<set _valueOwed = 50000>><</if>>
-<<if $freshPC == 1 || $saveImported == 0>>
-	<<set $PC.birthName = $PC.slaveName>>
-	<<if $PC.slaveSurname>><<set $PC.birthSurname = $PC.slaveSurname>><</if>>
-	<<if $PC.title == 1>>
-		<<set $PC.muscles = 50>>
-	<<else>>
-		<<set $PC.hLength = 15>>
-		<<set $PC.waist = -20>>
-		<<set $PC.voice = 2>>
-		<<set $PC.shoulders = -1>>
-		<<set $PC.hips = 1>>
-		<<set $PC.muscles = 30>>
-	<</if>>
-	<<if $PC.career == "escort">>
-		<<set $PC.anus = 1>>
-		<<set $PC.clothes = "a slutty outfit">>
-		<<set $PC.intelligenceImplant = 15>>
-	<<elseif $PC.career == "servant">>
-		<<set $PC.geneticQuirks.fertility = 1>>
-		<<set $PC.clothes = "a nice maid outfit">>
-		<<set $PC.intelligenceImplant = 0>>
-	<</if>>
-	<<if $PC.vagina == -1>>
-		<<set $PC.ovaries = 0>>
-	<<elseif $PC.vagina > 0>>
-		<<set $PC.vaginaLube = 1>>
-		<<set $PC.counter.birthsTotal = 0>>
-		<<if $PC.career == "servant">>
-			<<if $PC.pregType != 8>>
-				<<if $PC.actualAge >= 50>>
-					<<set $PC.counter.birthsTotal = 9>>
-					<<set $PC.counter.birthMaster = 9>>
-				<<elseif $PC.actualAge >= 35>>
-					<<set $PC.counter.birthsTotal = 6>>
-					<<set $PC.counter.birthMaster = 6>>
-				<<else>>
-					<<set $PC.counter.birthsTotal = 3>>
-					<<set $PC.counter.birthMaster = 3>>
-				<</if>>
-			<<else>> /* Master kept you pregged up */
-				<<if $PC.actualAge >= 50>>
-					<<set $PC.counter.birthsTotal = 70>>
-					<<set $PC.counter.birthMaster = 70>>
-				<<elseif $PC.actualAge >= 35>>
-					<<set $PC.counter.birthsTotal = 40>>
-					<<set $PC.counter.birthMaster = 40>>
-				<<else>>
-					<<set $PC.counter.birthsTotal = 16>>
-					<<set $PC.counter.birthMaster = 16>>
-				<</if>>
-			<</if>>
-			<<set $PC.geneticQuirks.fertility = 2>>
-			<<for $i = 0; $i < $slaves.length; $i++>>
-				<<if $slaves[$i].mother == -1>>
-					<<set $PC.counter.birthsTotal += 1>>
-					<<if $slaves[$i].father == -1>>
-						<<set $PC.counter.birthSelf += 1>>
-					<<else>>
-						<<set $slaves[$i].father = -3>>
-						<<set $PC.counter.birthMaster += 1>>
-					<</if>>
-				<</if>>
-			<</for>>
-		<<elseif $PC.career == "escort">>
-			<<for $i = 0; $i < $slaves.length; $i++>>
-				<<if $slaves[$i].mother == -1>>
-					<<set $PC.counter.birthsTotal += 1>>
-					<<if $slaves[$i].father == -1>>
-						<<set $PC.counter.birthSelf += 1>>
-					<<else>>
-						<<set $slaves[$i].father = -5>>
-						<<set $PC.counter.birthClient += 1>>
-					<</if>>
-				<<elseif $slaves[$i].father == -1>>
-					<<set $slaves[$i].mother = -5>>
-				<</if>>
-			<</for>>
-		<<else>>
-			<<for $i = 0; $i < $slaves.length; $i++>>
-				<<if $slaves[$i].mother == -1>>
-					<<set $PC.counter.birthsTotal += 1>>
-					<<if $slaves[$i].father == -1>>
-						<<set $PC.counter.birthSelf += 1>>
-					<<else>>
-						<<set $PC.counter.birthOther += 1>>
-					<</if>>
-				<</if>>
-			<</for>>
-		<</if>>
-		<<if $PC.preg > 0>>
-			<<set $PC.pregWeek = $PC.preg>>
-			<<if $PC.pregType != 8>>
-				<<set $PC.pregType = 1>>
-			<<else>>
-				<<set $PC.geneticQuirks.hyperFertility = 2>>
-			<</if>>
-			<<if $PC.career == "servant">>
-				<<set $PC.pregSource = -3>>
-				<<if $PC.pregType != 8>>
-					<<set $PC.pregType += random(0,0,1)>>
-				<</if>>
-			<<elseif $PC.career == "escort">>
-				<<set $PC.pregSource = -5>>
-			<</if>>
-			<<set $PC.pregKnown = 1>>
-			<<set $PC.belly = getPregBellySize($PC)>>
-			<<set WombImpregnate($PC, $PC.pregType, $PC.pregSource, $PC.preg)>>
-		<</if>>
-	<</if>>
-	<<if $PC.dick != 0>>
-		<<set $PC.geneticQuirks.wellHung = 2>>
-	<<else>>
-		<<set $PC.balls = 0>>
-		<<set $PC.scrotum = 0>>
-		<<set $PC.prostate = 0>>
-	<</if>>
-	<<set $PC.ovaryAge = $PC.physicalAge>>
-<</if>>
-<<set _pcMomFound = 0, _pcDadFound = 0>>
-<<if def $slaveIndices[$PC.mother]>>
-	<<set _pcMomFound = 1>>
-<</if>>
-<<if def $slaveIndices[$PC.father]>>
-	<<set _pcDadFound = 1>>
-<</if>>
-<<if _pcMomFound == 0 && $PC.mother > 0>>
-	<<set _lostMom = $PC.mother>>
-	<<set $PC.mother = $missingParentId>>
-	<<for _i = 0; _i < $slaves.length; _i++>>
-		<<if $slaves[_i].mother == _lostMom>>
-			<<set $slaves[_i].mother = $missingParentId>>
-		<</if>>
-	<</for>>
-	<<set $missingParentId-->>
-<</if>>
-<<if _pcDadFound == 0 && $PC.father > 0>>
-	<<set _lostDad = $PC.father>>
-	<<set $PC.father = $missingParentId>>
-	<<for _i = 0; _i < $slaves.length; _i++>>
-		<<if $slaves[_i].father == _lostDad>>
-			<<set $slaves[_i].father = $missingParentId>>
-		<</if>>
-	<</for>>
-	<<set $missingParentId-->>
-<</if>>
-<<for _i = 0; _i < $slaves.length; _i++>>
-	<<set _slaveMomFound = 0, _slaveDadFound = 0>>
-	<<if def $slaveIndices[$slaves[_i].mother]>>
-		<<set _slaveMomFound = 1>>
-	<</if>>
-	<<if def $slaveIndices[$slaves[_i].father]>>
-		<<set _slaveDadFound = 1>>
-	<</if>>
-	<<if _pcMomFound == 0 && $PC.mother > 0>>
-		<<set _lostMom = $PC.mother>>
-		<<set $PC.mother = $missingParentID>>
-		<<for _i = 0; _i < $slaves.length; _i++>>
-			<<if $slaves[_i].mother == _lostMom>>
-				<<set $slaves[_i].mother = $missingParentID>>
-			<</if>>
-		<</for>>
-		<<set $missingParentID-->>
-	<</if>>
-	<<if _pcDadFound == 0 && $PC.father > 0>>
-		<<set _lostDad = $PC.father>>
-		<<set $PC.father = $missingParentID>>
-		<<for _i = 0; _i < $slaves.length; _i++>>
-			<<if $slaves[_i].father == _lostDad>>
-				<<set $slaves[_i].father = $missingParentID>>
-			<</if>>
-		<</for>>
-		<<set $missingParentID-->>
-	<</if>>
-<</for>>
-<<set _coeffSlaves = []>>
-<<set _genePoolMap = {}>>
-<<for _i = 0; _i < $genePool.length; _i++>>
-	<<set _genePoolMap[$genePool[_i].ID] = $genePool[_i]>>
-<</for>>
-<<for _i = 0; _i < $slaves.length; _i++>>
-	<<if $slaves[_i].newGamePlus == 0>>
-		<<set $slaves[_i].inbreedingCoeff = -1>>
-		<<set _genePoolMap[$slaves[_i].ID].inbreedingCoeff = -1>>
-		<<run _coeffSlaves.push($slaves[_i])>>
-	<</if>>
-<</for>>
-<<set _ibcoeffs = ibc.coeff_slaves(_coeffSlaves)>>
-<<for _i = 0; _i < _coeffSlaves.length; _i++>>
-	<<set _coeffSlaves[_i].inbreedingCoeff = _ibcoeffs[_coeffSlaves[_i].ID]>>
-	<<set _genePoolMap[_coeffSlaves[_i].ID].inbreedingCoeff = _ibcoeffs[_coeffSlaves[_i].ID]>>
-<</for>>
-<<if $plot == 1 && $neighboringArcologies > 0>>
-	<<set _bestProsperity = 0, _bestProsperityIndex = 1>>
-	<<for _acq = 1; _acq < $arcologies.length; _acq++>>
-		<<if $arcologies[_acq].prosperity > _bestProsperity>>
-			<<set _bestProsperity = $arcologies[_acq].prosperity>>
-			<<set _bestProsperityIndex = _acq>>
-		<</if>>
-	<</for>>
-	<<set $rivalSet = 1>>
-	<<set $arcologies[_bestProsperityIndex].rival = 1>>
-<</if>>
-<<set $targetAge = $minimumSlaveAge>>
-<<set $targetAgeNursery = $minimumSlaveAge>>
-<<run resetFamilyCounters()>>
-
-You've done it.
-<br><br>
-You arrive at your new arcology, $arcologies[0].name, and head straight to the penthouse to enter the access codes that will tell the $arcologies[0].name systems to recognize you as their owner. The penthouse office is ready to receive the codes, and they authenticate. A voice activates in your earpiece.
-<br><br>
-//Congratulations. I am a personal assistant program, and it is my pleasure to assist you,
-<<= PlayerName()>>
-the new owner of $arcologies[0].name. I will offer useful information whenever possible in italics. Your new arcology has some unusual equipment. The previous owner kept a small stable of sex slaves. The penthouse therefore has a body modification studio for tattooing, bleaching and piercing, and an auto salon for more prosaic things like hair care. It also has a remote surgery, a small surgical theater that can be operated remotely by a qualified surgeon if you can pay the fee. Finally, it has a slave nutrition system connected to the arcology's hydroponics bays. This system produces a tasty protein-rich drink that provides the physically active female body all its necessary nutrients while leaving the lower digestive tract extremely clean. It even causes a mild increase in sex drive.
-<br><br>
-The previous owner seems to have left in something of a hurry.
-<<set _valueGiven = 0>>
-<<set _heroSlaves = App.Utils.buildHeroArray().shuffle()>>
-<<if $cheatMode == 1>>
-	Since you've elected to take over an arcology with special advantages, you've acquired a very special group of slaves.
-	<<include [[Cheatmode Database]]>>
-<<elseif $saveImported == 1>>
-	Since it took some time for you to ensure that your existing stable of slaves were safely moved to $arcologies[0].name, the previous owner had the time to get most of their things away.
-<<elseif ($targetArcology.fs != "New") && ($targetArcology.fs != "Multiculturalist")>>
-	<<for $j = 0; $j < 5; $j++>>
-		<<if _valueOwed - _valueGiven <= 5000>>
-			<<break>>
-		<</if>>
-		<<switch $targetArcology.fs>>
-		<<case "Supremacist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = $arcologies[0].FSSupremacistRace>><<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(0, 20), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "uncomfortable straps", _slave.collar = "uncomfortable leather", _slave.shoes = "flats">>
-			<<set _slave.assignment = "please you">>
-		<<case "Subjugationist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = $arcologies[0].FSSubjugationistRace>><<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(0, 20), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.whoring = random(15,35)>>
-			<<set _slave.clothes = "uncomfortable straps", _slave.collar = "uncomfortable leather", _slave.shoes = "flats">>
-			<<set _slave.assignment = "whore">>
-		<<case "GenderRadicalist">>
-			<<set $activeSlaveOneTimeMaxAge = 25>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave("XY")>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(15,-15)>>
-			<<run setHealth(_slave, 100, 0, 0, 0, jsRandom(0, 20))>>
-			<<set _slave.face = random(0,55)>>
-			<<set _slave.boobs += 100*random(2,4)>>
-			<<set _slave.butt += random(1,2)>>
-			<<set _slave.attrXY = random(70,90), _slave.attrXX = 0>>
-			<<set _slave.skill.oral = random(35,65), _slave.skill.anal = random(35,65), _slave.skill.whoring = random(35,65)>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.clothes = "uncomfortable straps", _slave.collar = "stylish leather", _slave.shoes = "heels">>
-			<<set _slave.assignment = "whore">>
-		<<case "GenderFundamentalist">>
-			<<set $activeSlaveOneTimeMinAge = $fertilityAge>>
-			<<set $activeSlaveOneTimeMaxAge = 25>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave("XX")>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(55, 65), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.preg = random(1,40), _slave.lactation = 1, _slave.lactationDuration = 2>>
-			<<run SetBellySize(_slave)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "a nice maid outfit", _slave.collar = "tight steel", _slave.shoes = "flats">>
-			<<set _slave.assignment = "be a servant">>
-		<<case "Paternalist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, jsRandom(55, 65), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.intelligence = random(0,100), _slave.intelligenceImplant = 30>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "conservative clothing", _slave.collar = "none", _slave.shoes = "flats">>
-			<<set _slave.assignment = "be a servant">>
-		<<case "Degradationist">>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = 25, _slave.trust = -25>>
-			<<run setHealth(_slave, jsRandom(0, 15), 0, 0, 0, 0)>>
-			<<set _slave.fuckdoll = 100>>
-			<<set _slave.career = "a Fuckdoll">>
-			<<set _slave.fetish = "mindbroken">>
-			<<set _slave.boobs += 100*random(10,20)>>
-			<<set _slave.butt += random(2,3)>>
-			<<set _slave.lips = random(2,4)>>
-			<<set _slave.weight = random(-15,15)>>
-			<<set _slave.skill.oral = 0, _slave.skill.anal = 0, _slave.skill.vaginal = 0, _slave.skill.entertainment = 0, _slave.skill.whoring = 0>>
-			<<set _slave.behavioralFlaw = "none", _slave.sexualFlaw = "none">>
-			<<set _slave.clothes = "a Fuckdoll suit">>
-			<<set _slave.assignment = "please you">>
-		<<case "AssetExpansionist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.chem = 50>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.boobs += 100*random(10,20)>>
-			<<set _slave.butt += random(2,3)>>
-			<<set _slave.lips += random(0,1)>>
-			<<if _slave.balls > 0>><<set _slave.balls++>><</if>>
-			<<if _slave.dick > 0>><<set _slave.dick++>><</if>>
-			<<set _slave.weight = random(15,90)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = 0, _slave.anus = 0>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "slutty jewelry", _slave.collar = "pretty jewelry", _slave.shoes = "heels">>
-			<<set _slave.assignment = "please you">>
-		<<case "SlimnessEnthusiast">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, jsRandom(55, 65), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.boobs = 100*random(1,4)>>
-			<<set _slave.butt = random(1,2)>>
-			<<set _slave.weight = random(-25,-15)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = 0, _slave.anus = 0>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "a leotard", _slave.collar = "pretty jewelry", _slave.shoes = "flats">>
-			<<set _slave.assignment = "please you">>
-		<<case "TransformationFetishist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(-15, 0), Math.max(normalRandInt(5, 3), 0), Math.max(normalRandInt(5, 3), 0), 0, 0)>>
-			<<set _slave.faceImplant = random(40,70)>>
-			<<set _slave.face = Math.trunc(_slave.face+_slave.faceImplant/2,-100,100)>>
-			<<set _slave.boobsImplant = 200*random(4,8)>>
-			<<set _slave.boobs += _slave.boobsImplant>>
-			<<set _slave.boobsImplantType = "normal">>
-			<<set _slave.buttImplant = random(2,4)>>
-			<<set _slave.butt += _slave.buttImplant>>
-			<<set _slave.buttImplant = "normal">>
-			<<set _slave.lipsImplant = random(1,2)>>
-			<<set _slave.lips = Math.trunc(_slave.lipsImplant+2,-3,3)>>
-			<<set _slave.weight = random(-25,-15)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "a string bikini", _slave.collar = "shock punishment", _slave.shoes = "extreme heels">>
-			<<set _slave.assignment = "whore">>
-		<<case "BodyPurist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(25,45)>>
-			<<run setHealth(_slave, 100, 0, 0, 0, jsRandom(0, 20))>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.weight = random(-5,5)>>
-			<<set _slave.muscles = random(10,25)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "a nice maid outfit", _slave.collar = "pretty jewelry", _slave.shoes = "heels">>
-			<<set _slave.assignment = "be a servant">>
-		<<case "MaturityPreferentialist">>
-			<<set $activeSlaveOneTimeMinAge = 36>>
-			<<set $activeSlaveOneTimeMaxAge = 39>>
-			<<set $one_time_age_overrides_pedo_mode = 1>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.boobs += 100*random(1,4)>>
-			<<set _slave.butt += random(1,2)>>
-			<<set _slave.weight = random(-5,90)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<run SoftenBehavioralFlaw(_slave)>>
-			<<set _slave.clothes = "a slutty maid outfit", _slave.collar = "pretty jewelry", _slave.shoes = "heels">>
-			<<set _slave.assignment = "be a servant">>
-		<<case "YouthPreferentialist">>
-			<<set $activeSlaveOneTimeMaxAge = 19>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.boobs = 100*random(1,4)>>
-			<<set _slave.butt = random(1,3)>>
-			<<set _slave.weight = random(-25,25)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = 0, _slave.anus = 0>>
-			<<if _slave.vagina > -1>><<set _slave.skill.vaginal = 0, _slave.vagina = 0>><</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "a schoolgirl outfit", _slave.collar = "pretty jewelry", _slave.shoes = "heels">>
-			<<set _slave.assignment = "be a servant">>
-		<<case "Pastoralist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.boobs += 100*random(10,20)>>
-			<<if _slave.balls > 0>><<set _slave.balls++>><</if>>
-			<<set _slave.lactation = 2, _slave.lactationDuration = 2>>
-			<<set _slave.clothes = "Western clothing", _slave.collar = "leather with cowbell", _slave.shoes = "flats">>
-			<<set _slave.assignment = "get milked">>
-		<<case "PhysicalIdealist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(25,45)>>
-			<<run setHealth(_slave, 100, 0, 0, 0, jsRandom(10, 40))>>
-			<<set _slave.muscles = random(50,100)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.whoring = random(15,35)>>
-			<<set _slave.clothes = "body oil", _slave.collar = "none", _slave.shoes = "none">>
-			<<set _slave.assignment = "whore">>
-		<<case "ChattelReligionist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, jsRandom(0, 15), 0, 0, 0, 0)>>
-			<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-			<<set _slave.skill.whoring = random(10,20)>>
-			<<set _slave.behavioralFlaw = "none", _slave.behavioralQuirk = "sinful">>
-			<<set _slave.clothes = "a chattel habit", _slave.collar = "heavy gold", _slave.shoes = "flats">>
-			<<set _slave.assignment = "whore">>
-		<<case "RomanRevivalist">>
-			<<set $activeSlaveOneTimeMaxAge = 19>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = 100, _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, 100, 0, 0, 0, jsRandom(10, 30))>>
-			<<set _slave.face = random(0,55)>>
-			<<set _slave.muscles = random(25,50)>>
-			<<set _slave.skill.combat = 1>>
-			<<set _slave.behavioralFlaw = "none", _slave.behavioralQuirk = "fitness">>
-			<<set _slave.behavioralFlaw = "none">>
-			<<set _slave.clothes = "a toga", _slave.collar = "pretty jewelry", _slave.shoes = "flats">>
-			<<set _slave.assignment = "guard you", $BodyguardID = _slave.ID>>
-		<<case "NeoImperialist">>
-			<<set $activeSlaveOneTimeMinAge = 16>>
-			<<set $activeSlaveOneTimeMaxAge = 28>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = "white">>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = 100, _slave.trust = random(55,65)>>
-			<<run setHealth(_slave, 100, 0, 0, 0, jsRandom(10, 30))>>
-			<<set _slave.face = random(20,75)>>
-			<<set _slave.muscles = random(25,60)>>
-			<<set _slave.skill.combat = 1>>
-			<<set _slave.behavioralFlaw = "none", _slave.behavioralQuirk = "fitness">>
-			<<set _slave.behavioralFlaw = "none">>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.clothes = "a tight Imperial bodysuit", _slave.shoes = "flats">>
-			<<set _slave.assignment = "guard you", $BodyguardID = _slave.ID>>
-		<<case "AztecRevivalist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = "latina">>
-			<<set _slave = GenerateNewSlave(null, {nationality: "Mexican"})>>
-			<<set _slave.accent = 0>>
-			<<set _slave.devotion = 75, _slave.trust = 75>>
-			<<run setHealth(_slave, jsRandom(-20, 20), 0, 0, 0, 0)>>
-			<<set _slave.muscles = random(50,75)>>
-			<<set _slave.skill.combat = 1>>
-			<<set _slave.behavioralFlaw = "malicious", _slave.behavioralQuirk = "none">>
-			<<set _slave.clothes = "a huipil", _slave.collar = "pretty jewelry", _slave.shoes = "none">>
-			<<set _slave.assignment = "be your Head Girl", $HeadGirlID = _slave.ID>>
-		<<case "EgyptianRevivalist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(25,45)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.clothes = "slutty jewelry", _slave.collar = "ancient Egyptian", _slave.shoes = "flats">>
-			<<set _slave.assignment = "please you">>
-		<<case "EdoRevivalist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = "asian">>
-			<<set _slave = GenerateNewSlave(null, {nationality: "Japanese"})>>
-			<<set _slave.accent = 0>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(25,45)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.intelligence = random(0,100), _slave.intelligenceImplant = 30>>
-			<<set _slave.skill.entertainment = 100>>
-			<<set _slave.clothes = "a kimono", _slave.collar = "satin choker", _slave.shoes = "heels">>
-			<<set _slave.assignment = "serve the public">>
-		<<case "ArabianRevivalist">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.intelligence = random(-15,80), _slave.intelligenceImplant = 0>>
-			<<set _slave.clothes = "harem gauze", _slave.collar = "uncomfortable leather", _slave.shoes = "flats">>
-			<<set _slave.assignment = "take classes">>
-		<<case "ChineseRevivalist">>
-			<<set $activeSlaveOneTimeMinAge = 36>>
-			<<set $activeSlaveOneTimeMaxAge = 38>>
-			<<set $one_time_age_overrides_pedo_mode = 1>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set $fixedRace = "asian">>
-			<<set _slave = GenerateNewSlave(null, {nationality: "Chinese"})>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(25,45)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(0,55)>>
-			<<set _slave.accent = 0>>
-			<<set _slave.intelligence = 100, _slave.intelligenceImplant = 30>>
-			<<set _slave.skill.oral = 100, _slave.skill.anal = 100>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = 100>>
-			<</if>>
-			<<set _slave.skill.entertainment = 100, _slave.skill.whoring = 100>>
-			<<run SoftenBehavioralFlaw(_slave)>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.clothes = "a slutty qipao", _slave.collar = "pretty jewelry", _slave.shoes = "heels">>
-			<<set _slave.assignment = "be your Head Girl", $HeadGirlID = _slave.ID>>
-		<<case "Eugenics">>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = -100, _slave.trust = -100>>
-			<<run setHealth(_slave, jsRandom(80, 90), 0, 0, 0, 0)>>
-			<<set _slave.intelligence = 100>>
-			<<set _slave.intelligenceImplant = 30>>
-			<<set _slave.face = 100>>
-			<<set _slave.faceShape = "sensual">>
-			<<set _slave.skill.oral = random(35,75), _slave.skill.anal = random(35,75)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(35,75)>>
-			<</if>>
-			<<set _slave.skill.entertainment = random(15,35)>>
-			<<set _slave.skill.whoring = 0>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.clothes = "a ball gown", _slave.shoes = "flats">>
-			<<set _slave.assignment = "rest">>
-		<<case "Repopulationist">>
-			<<set $activeSlaveOneTimeMinAge = $fertilityAge+3>>
-			<<set $activeSlaveOneTimeMaxAge = 25>>
-			<<set $one_time_age_overrides_pedo_mode = 1>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave("XX")>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(55, 65), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.preg = random(10,40), _slave.pregType = random(3,8), _slave.lactation = 1, _slave.lactationDuration = 2>>
-			<<run SetBellySize(_slave)>>
-			<<set _slave.counter.birthsTotal = 5>>
-			<<set _slave.bellySag = 20, _slave.bellySagPreg = 20>>
-			<<if _slave.vagina > -1>>
-				<<set _slave.vagina = 4>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "a nice maid outfit", _slave.shoes = "flats">>
-			<<set _slave.assignment = "please you">>
-		<<case "HedonisticDecadence">>
-			<<set $activeSlaveOneTimeMaxAge = 25>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave("XX")>>
-			<<set _slave.devotion = random(25,45), _slave.trust = random(-25,-45)>>
-			<<run setHealth(_slave, jsRandom(-20, 20), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,40)>>
-			<<set _slave.boobs += 100*random(3,6)>>
-			<<set _slave.butt += random(2,5)>>
-			<<set _slave.weight = random(100,200)>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35), _slave.anus = 2>>
-			<<if _slave.vagina > -1>><<set _slave.skill.vaginal = random(15,35), _slave.vagina = 3>><</if>>
-			<<set _slave.skill.entertainment = 0>>
-			<<set _slave.energy = random(60,80)>>
-			<<set _slave.behavioralFlaw = "gluttonous">>
-			<<set _slave.clothes = "attractive lingerie", _slave.shoes = "flats">>
-			<<set _slave.diet = "fattening">>
-			<<set _slave.rules.living = "luxurious">>
-			<<set _slave.assignment = "rest">>
-		<<case "IntellectualDependency">>
-			<<set $activeSlaveOneTimeMinAge = 14>>
-			<<set $activeSlaveOneTimeMaxAge = 18>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<run setHealth(_slave, jsRandom(55, 65), 0, 0, 0, 0)>>
-			<<set _slave.devotion = random(45,65), _slave.trust = random(-15,45)>>
-			<<set _slave.face = random(30,100)>>
-			<<set _slave.energy = 100>>
-			<<set _slave.weight = random(-25,-15)>>
-			<<set _slave.skill.oral = 0, _slave.skill.anal = 0, _slave.skill.vaginal = 0, _slave.skill.entertainment = 0, _slave.skill.whoring = 0>>
-			<<set _slave.intelligence = -100>>
-			<<set _slave.intelligenceImplant = 0>>
-			<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-			<<set _slave.anus++>>
-		<<case "SlaveProfessionalism">>
-			<<set $activeSlaveOneTimeMinAge = 18>>
-			<<set $activeSlaveOneTimeMaxAge = 25>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = 100, _slave.trust = 20>>
-			<<run setHealth(_slave, 80, 0, 0, 0, jsRandom(10, 30))>>
-			<<set _slave.face = random(30,100)>>
-			<<set _slave.energy = 10>>
-			<<set _slave.weight = random(-15,5)>>
-			<<set _slave.skill.oral = 100, _slave.skill.anal = 100, _slave.skill.vaginal = 100, _slave.skill.entertainment = 100, _slave.skill.whoring = 100>>
-			<<set _slave.intelligence = 100>>
-			<<set _slave.intelligenceImplant = 30>>
-			<<set _slave.accent = 0>>
-			<<run SoftenBehavioralFlaw(_slave)>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.clothes = "a nice maid outfit", _slave.collar = "pretty jewelry">>
-			<<set _slave.assignment = "be your Head Girl", $HeadGirlID = _slave.ID>>
-		<<case "PetiteAdmiration">>
-			<<set $activeSlaveOneTimeMinAge = 14>>
-			<<set $activeSlaveOneTimeMaxAge = 18>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<if _slave.height >= 150>>
-				<<set _slave.height = Math.trunc(Height.random(_slave, {limitMult: [-3, -1]}))>>
-				<<if _slave.height >= 150>>
-					<<set _slave.height = Math.trunc(Height.random(_slave, {limitMult: [-4, -2]}))>>
-					<<if _slave.height >= 150>>
-						<<set _slave.height = random(90,130)>>
-						<<set _slave.geneticQuirks.dwarfism = 2>>
-					<</if>>
-				<</if>>
-			<</if>>
-			<<set _slave.skill.oral = random(35,65), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "a succubus outfit", _slave.legAccessory = "long stockings">>
-			<<set _slave.assignment = "please you">>
-		<<case "StatuesqueGlorification">>
-			<<set $activeSlaveOneTimeMinAge = 18>>
-			<<set $activeSlaveOneTimeMaxAge = 30>>
-			<<set $one_time_age_overrides_pedo_mode = 1>>
-			<<set $oneTimeDisableDisability = 1>>
-			<<set _slave = GenerateNewSlave()>>
-			<<set _slave.devotion = random(55,65), _slave.trust = random(-15,15)>>
-			<<run setHealth(_slave, jsRandom(25, 45), 0, 0, 0, 0)>>
-			<<set _slave.face = random(15,100)>>
-			<<set _slave.weight = random(-5,5)>>
-			<<set _slave.muscles = random(20,40)>>
-			<<if _slave.height < 185>>
-				<<set _slave.height = Math.trunc(Height.random(_slave, {limitMult: [1, 3]}))>>
-				<<if _slave.height < 185>>
-					<<set _slave.height = Math.trunc(Height.random(_slave, {limitMult: [2, 4]}))>>
-					<<if _slave.height < 185>>
-						<<set _slave.height = random(200,264)>>
-						<<set _slave.geneticQuirks.gigantism = 2>>
-					<</if>>
-				<</if>>
-			<</if>>
-			<<set _slave.skill.oral = random(15,35), _slave.skill.anal = random(15,35)>>
-			<<if _slave.vagina > -1>>
-				<<if _slave.vagina == 0>><<set _slave.vagina++>><</if>>
-				<<set _slave.skill.vaginal = random(15,35)>>
-			<</if>>
-			<<set _slave.clothes = "slutty business attire", _slave.shoes = "heels">>
-			<<set _slave.assignment = "please you">>
-		<<default>>
-			ERROR: bad arcology type
-		<</switch>>
-		<<set _slave.origin = "You acquired $him along with the arcology.", _slave.career = "a slave">>
-		<<set _slaveCost = slaveCost(_slave)>>
-		<<if ["AztecRevivalist", "ChineseRevivalist", "NeoImperialist", "Eugenics", "SlaveProfessionalism"].includes($targetArcology.fs)>>
-			<<set _valueGiven += _slaveCost*4>>
-		<<else>>
-			<<set _valueGiven += _slaveCost>>
-		<</if>>
-		<<run newSlave(_slave)>>
-	<</for>>
-	<<run App.Utils.setLocalPronouns(_slave)>>
-	<<switch $targetArcology.fs>>
-	<<case "Supremacist">>
-		They kept a personal stable of fearful $arcologies[0].FSSupremacistRace sex slaves, but their sexual training is incomplete. Several of them are still here.
-	<<case "Subjugationist">>
-		They made it a special goal to enslave and whore out as many $arcologies[0].FSSubjugationistRace people as possible. Several of them are still here.
-	<<case "GenderRadicalist">>
-		They were in the process of building a stable of pretty young shemale whores. Several of them are still here. They're are all very attracted to men, and skilled at pleasing them.
-	<<case "GenderFundamentalist">>
-		They kept a personal stable of slaves for breeding purposes. Several of them are still here. They've been kept pregnant, and work as servants when they aren't being bred.
-	<<case "Paternalist">>
-		Their slaves were all very well treated. Several of them are still here. They were allowed to work as maids, and weren't even forced to have sex.
-	<<case "Degradationist">>
-		Their personal slaves were all Fuckdolls, slaves who have been permanently encased in advanced latex suits and converted into living sex toys. Several of them are still here.
-	<<case "AssetExpansionist">>
-		They kept a collection of bejeweled boobs for company, but they focused on breast expansion to the exclusion the slaves' emotional training. Several of them are still here.
-	<<case "SlimnessEnthusiast">>
-		They kept a harem of slim, pretty girls, and treated them very well. Several of them are still here. They should be very trusting of a new owner.
-	<<case "TransformationFetishist">>
-		They were just putting the finishing touches on a planned brothel's worth of surgically enhanced whores. Several of them are still here. They are already used to prostitution.
-	<<case "BodyPurist">>
-		Their slaves were trained for sexual use, but their health, fitness, and natural appearance were the main priorities. Several of them are still here.
-	<<case "MaturityPreferentialist">>
-		They preferred to keep their MILFs as scantily clad servants. Several of them are still here. They aren't all happy to be sex objects, but they're used to it.
-	<<case "YouthPreferentialist">>
-		They treated their young slaves very well. Several of them are still here. Virgins have been carefully preserved, but have learned to use their mouths through experience.
-	<<case "Pastoralist">>
-		Their herd of cow girls was progressing nicely, though more progress had been made on milk production than on training. Several of them are still here.
-	<<case "PhysicalIdealist">>
-		Their slaves worked as prostitutes, but mostly to fund a program of muscle building for all of them, which was nearing completion. Several of them are still here.
-	<<case "ChattelReligionist">>
-		They were recruiting a stable of slave whores by targeting people with religious complexes that made them particularly vulnerable to recruitment. Several of them are still here.
-	<<case "RomanRevivalist">>
-		The only one of their slaves left is the bodyguard. $He should be very willing to transfer $his loyalty to you, as $his new owner.
-	<<case "NeoImperialist">>
-		This arcology kept their Knights as actively enslaved and docile servants; you find one of them remaining here. $He immediately pledges $his loyalty over to you.
-	<<case "AztecRevivalist">>
-		They maintained a combination of disobedient slaves, deemed incorrigible and blood priestesses. Since they offer themselves as slaves willingly, one has remained loyal to the owner of the arcology.
-	<<case "EgyptianRevivalist">>
-		They kept a harem of beautiful slave girls, who have been well-treated and well-trained. They should be very willing to serve you in turn.
-	<<case "EdoRevivalist">>
-		They put considerable effort into creating modern slave geisha, elegant Japanese slaves who were assigned to serve the public. Several of them are still here.
-	<<case "ArabianRevivalist">>
-		They trained slaves personally, and never kept slaves for very long. The slaves they left are all partway through being trained.
-	<<case "ChineseRevivalist">>
-		The only one of their slaves left is the Head Girl. $He should be willing to transfer $his loyalty to you, as $his proper superior.
-	<<case "Eugenics">>
-		The only one of their slaves left is an absolute beauty of a $girl. $He is too smart for $his own good and will likely not trust you.
-	<<case "Repopulationist">>
-		They kept a personal stable of slaves for breeding purposes. Several of them are still here. They've been kept heavily pregnant, and are used to being used whilst so.
-	<<case "HedonisticDecadence">>
-		Their slaves were heavily pampered; free to lie about, fuck, and eat as much as they wanted. Several of them are still here, too lazy and spoiled to leave. They eagerly paw at you upon your arrival, begging for their bellies to be filled with food and for a good, hard fucking.
-	<<case "IntellectualDependency">>
-		They kept several vapid sluts, who are now desperately begging you for sex. It seems they've drained the life out of every toy left behind and have no idea how to recharge them.
-	<<case "SlaveProfessionalism">>
-		The only one of their slaves left has made it $his mission to have the penthouse ready for whomever should come to own it. $He greets you cordially, hands you a detailed summary of $his skills and information on your new arcology, and calmly awaits further orders.
-	<<case "PetiteAdmiration">>
-		They had quite the impish little harem. Several of them are still here and immediately swarm you as a welcome, eager to see someone taller again.
-	<<case "StatuesqueGlorification">>
-		They had quite the collection of towering slaves. Several of them are still here. They gaze down on you, evaluating how best to serve their new owner.
-	<<default>>
-		ERROR: bad arcology type
-	<</switch>>
-<<else>>
-	They could not get all of their personal effects away. Since they <<if $targetArcology.fs == "Multiculturalist">>tried to sample different kinds of sexual slavery<<else>>did not have the time in control of the arcology to develop a specific stable of sex slaves<</if>>, their slaves were quite varied.
-	<<for $j = 0; $j < _heroSlaves.length; $j++>>
-		<<if _valueOwed - _valueGiven <= 5000>>
-			<<break>>
-		<</if>>
-		<<set _slave = App.Utils.getHeroSlave(_heroSlaves[$j])>>
-		<<run _heroSlaves.splice($j,1)>>
-		<<set _slaveCost = slaveCost(_slave)>>
-		<<if _valueGiven + _slaveCost < _valueOwed*2>>
-			<<run nationalityToAccent(_slave)>>
-			<<set _slave.oldDevotion = _slave.devotion>>
-			<<set _slave.oldTrust = _slave.trust>>
-			<<set _slave.health.tired = 0>>
-			<<set _valueGiven += _slaveCost>>
-			<<run newSlave(_slave)>>
-			<<run App.Utils.setLocalPronouns(_slave)>>
-			<<if _slave.fetish == "mindbroken">>
-				_slave.slaveName is, sadly, not mentally competent, and is wandering through the penthouse at the moment.
-			<<elseif isAmputee(_slave)>>
-				_slave.slaveName is a quadruple amputee and is quite helpless, so you can attend to $him at your leisure.
-			<<elseif _slave.devotion < -50>>
-				_slave.slaveName is quite rebellious and was attempting to escape, so I have locked $him in the slave quarters.
-			<<elseif _slave.devotion < -20>>
-				_slave.slaveName resists my orders and was considering escape, so I have locked $him in the slave quarters.
-			<<elseif _slave.devotion <= 20>>
-				_slave.slaveName is reasonably obedient, and is waiting for you in the dormitory, I believe in the hope of making a good impression.
-			<<elseif _slave.energy > 95>>
-				_slave.slaveName is a remarkable sexual addict, and I believe $he will be very happy to meet you.
-			<<elseif _slave.fetish == "pregnancy">>
-				<<if _slave.bellyPreg >= 500>>
-					_slave.slaveName is currently in the dormitory masturbating over $his growing pregnancy, and <<if $PC.belly >= 5000>>will certainly be eager to meet you<<else>>I believe $he will be happy to show it to you<</if>>.
-				<<else>>
-					_slave.slaveName is currently in the dormitory examining $himself to try to discern $his fertility, and I believe $he will be <<if $PC.belly >= 5000>>eager to get acquainted with you<<else>>happy to meet you<</if>>.
-				<</if>>
-			<<elseif _slave.belly >= 5000>> /*had to be placed after pregnancy or it would intercept*/
-				_slave.slaveName is currently in the dormitory massaging $his greatly distended belly.
-			<<elseif _slave.fetish == "buttslut">>
-				_slave.slaveName is currently in the dormitory masturbating anally, and I believe $he will be happy to meet you.
-			<<elseif _slave.fetish == "cumslut">>
-				_slave.slaveName is currently in the dormitory exhibiting oral fixation, and I believe $he will be happy to meet you.
-			<<elseif _slave.fetish == "boobs">>
-				_slave.slaveName is currently in the dormitory playing with $his nipples, and I believe $he will be happy to meet you.
-			<<elseif _slave.fetish == "humiliation">>
-				_slave.slaveName is currently in the entryway flashing passersby, and I believe $he will be happy to meet you.
-			<<elseif _slave.fetish == "submissive">>
-				_slave.slaveName is currently in the dormitory, experimenting with self-bondage using the sheets; I believe $he will be happy to meet you.
-			<<elseif _slave.fetish == "dom">>
-				_slave.slaveName is currently in the exercise area keeping fit; $he likes to take an active role sexually and is using this down time to work out.
-			<<elseif _slave.fetish == "sadist">>
-				_slave.slaveName is currently outside your office; $he enjoys being superior to other slaves and I believe $he means to ingratiate $himself to you.
-			<<elseif _slave.fetish == "masochist">>
-				_slave.slaveName is a sexual masochist; $he is currently in the bathroom, experimenting with auto-flagellation with a wet towel.
-			<<else>>
-				_slave.slaveName is currently outside your office, and I believe $he is attempting to maintain sexual arousal to make a good first impression on you.
-			<</if>>
-		<</if>>
-	<</for>>
-<</if>>
-<<if _valueOwed-_valueGiven > 0>>
-	There are some valuables present, worth <<print cashFormat(_valueOwed-_valueGiven)>>.
-	<<run cashX((_valueOwed-_valueGiven), "event", _slave)>>
-<</if>>
-//
-
-<<set $averageTrust = 0>>
-<<set $averageDevotion = 0>>
-<<set _slavesContributing = 0>>
-<<for $i = 0; $i < $slaves.length; $i++>>
-    <<run updateHealth($slaves[$i])>>
-	<<set $slaves[$i].oldDevotion = $slaves[$i].devotion>>
-	<<set $slaves[$i].oldTrust = $slaves[$i].trust>>
-	/* AVERAGE VALUES UPDATE */
-	<<if assignmentVisible($slaves[$i])>>
-		<<set $averageTrust += $slaves[$i].trust, $averageDevotion += $slaves[$i].devotion, _slavesContributing += 1>>
-	<<else>>
-		<<if $slaves[$i].assignment != "be confined in the cellblock">>
-			<<if $slaves[$i].assignment != "be confined in the arcade">>
-				<<if ($slaves[$i].assignment != "work in the dairy") || ($dairyRestraintsSetting < 2)>>
-					<<set $averageTrust += $slaves[$i].trust*0.5, $averageDevotion += $slaves[$i].devotion*0.5, _slavesContributing += 0.5>>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</for>>
-<<if _slavesContributing != 0>>
-	<<set $averageTrust = $averageTrust/_slavesContributing>>
-	<<set $averageDevotion = $averageDevotion/_slavesContributing>>
-<</if>>
-<<set $enduringTrust = $averageTrust>>
-<<set $enduringDevotion = $averageDevotion>>
-<<run App.UI.SlaveSummary.settingsChanged()>>
-<br><br>
-
-<<link "Continue">>
-	<<set $ui = "main">>
-	<<if $terrain == "urban">>
-		<<set $slaveCostFactor = 0.85>>
-		<<set $menialSupplyFactor = 30000>>
-		<<set $menialDemandFactor = -30000>>
-	<<elseif $terrain == "marine">>
-		<<set $slaveCostFactor = 1>>
-	<<else>>
-		<<set $slaveCostFactor = 1.15>>
-		<<set $menialDemandFactor = 30000>>
-		<<set $menialSupplyFactor = -30000>>
-	<</if>>
-	<<script>>Save.autosave.save("Week Start Autosave")<</script>>
-	<<goto "Main">>
-<</link>>
diff --git a/src/npc/children/childSummary.js b/src/npc/children/childSummary.js
index f4cce037cde2a5ba5c090a26998ec9b512ccb96e..c91ea74877b4b9d727eb0df66e226e3ba35fd58f 100644
--- a/src/npc/children/childSummary.js
+++ b/src/npc/children/childSummary.js
@@ -2306,7 +2306,7 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 		}
 		r += `</span>`;
 
-		// If no NCS, then do the standard - however, due to the wrinkles of the  Incubator, as long as visual age is greater
+		// If no NCS, then do the standard - however, due to the wrinkles of the Incubator, as long as visual age is greater
 		// than or equal to physical age, we do the old physical body/Looks for fresh out of the can NCS slaves.
 
 		if (((child.geneMods.NCS === 0) || (child.visualAge >= child.physicalAge))) {
@@ -3098,9 +3098,6 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 				case "submissive":
 					r += `SP:sub`;
 					break;
-				case "lesbian":
-					r += `SP:les`;
-					break;
 				case "oral":
 					r += `SP:oral`;
 					break;
@@ -3134,6 +3131,8 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 				case "none":
 					r += `SP:none`;
 					break;
+				default:
+					r += `SP:monitor`;
 			}
 		}
 	}
diff --git a/src/npc/databases/cheatmodeDatabase.js b/src/npc/databases/cheatmodeDatabase.js
new file mode 100644
index 0000000000000000000000000000000000000000..d7054ea13f9c2b0265ac7834b7f623461ce7061f
--- /dev/null
+++ b/src/npc/databases/cheatmodeDatabase.js
@@ -0,0 +1,421 @@
+App.Intro.cheatModeSlaves = function() {
+	const el = new DocumentFragment();
+	const r = [];
+	const slaveNames = [];
+	let _slave = BaseSlave();
+	let _HS = clone(_slave);
+	_HS.slaveName = "Miss Anne";
+	_HS.birthName = "Lindy Anne";
+	_HS.ID = 990000;
+	_HS.relationship = 4;
+	_HS.relationshipTarget = 990001;
+	_HS.rivalry = 1;
+	_HS.rivalryTarget = 990002;
+	_HS.assignment = "be your Head Girl";
+	_HS.birthWeek = random(0, 51);
+	_HS.actualAge = 42;
+	_HS.physicalAge = 42;
+	_HS.visualAge = 42;
+	_HS.ovaryAge = 42;
+	_HS.ageImplant = 1;
+	setHealth(_HS, 50);
+	_HS.devotion = 100;
+	_HS.nationality = "Stateless";
+	_HS.height = 175;
+	_HS.race = "white";
+	_HS.eye.origColor = "green";
+	_HS.origHColor = "honey blonde";
+	_HS.origSkin = "pale";
+	_HS.hStyle = "long";
+	_HS.waist = -55;
+	_HS.boobs = 1000;
+	_HS.boobsImplant = 600;
+	_HS.boobsImplantType = "normal";
+	_HS.areolae = 1;
+	_HS.butt = 4;
+	_HS.buttImplant = 2;
+	_HS.buttImplantType = "normal";
+	_HS.face = 55;
+	_HS.lips = 35;
+	_HS.anus = 1;
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.skill.oral = 100;
+	_HS.skill.anal = 100;
+	_HS.skill.whoring = 100;
+	_HS.skill.entertainment = 100;
+	_HS.clothes = "a slave gown";
+	_HS.intelligence = 100;
+	_HS.energy = 65;
+	_HS.attrXY = 40;
+	_HS.fetishKnown = 1;
+	_HS.custom.tattoo = "'Miss Anne' is tattooed in lovely flowing script over $his collarbone.";
+	_HS.custom.desc = "$He speaks with the rich accent of the Old South.";
+	if (V.seeDicks !== 100) {
+		_HS.genes = "XX";
+		_HS.vagina = 2;
+		_HS.ovaries = 1;
+		_HS.skill.vaginal = 100;
+		_HS.pubertyXX = 1;
+	} else {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 4;
+		_HS.balls = 4;
+		_HS.scrotum = 4;
+		_HS.foreskin = 4;
+		_HS.prostate = 1;
+		_HS.pubertyXY = 1;
+	}
+	newSlave(_HS);
+	V.HeadGirlID = _HS.ID;
+	r.push(`There are quite a few left; their names are`);
+	slaveNames.push(_HS.slaveName);
+
+	_HS = clone(_slave);
+	_HS.slaveName = "Cornelia";
+	_HS.birthName = "Cora";
+	_HS.ID = 990001;
+	_HS.relationship = 4;
+	_HS.relationshipTarget = 990000;
+	_HS.assignment = "whore";
+	_HS.birthWeek = random(0, 51);
+	_HS.actualAge = 36;
+	_HS.physicalAge = 36;
+	_HS.visualAge = 36;
+	_HS.ovaryAge = 36;
+	_HS.ageImplant = 1;
+	setHealth(_HS, 10);
+	_HS.devotion = 100;
+	_HS.nationality = "Stateless";
+	_HS.muscles = 20;
+	_HS.height = 190;
+	_HS.race = "black";
+	_HS.origHColor = "black";
+	_HS.pubicHColor = "black";
+	_HS.origSkin = "dark";
+	_HS.hStyle = "long";
+	_HS.pubicHStyle = "waxed";
+	_HS.waist = -55;
+	_HS.boobs = 1200;
+	_HS.boobsImplant = 1000;
+	_HS.boobsImplantType = "fillable";
+	_HS.areolae = 2;
+	_HS.butt = 5;
+	_HS.buttImplant = 3;
+	_HS.buttImplantType = "fillable";
+	_HS.preg = -2;
+	_HS.face = 15;
+	_HS.faceImplant = 65;
+	_HS.lips = 35;
+	_HS.lipsImplant = 10;
+	_HS.anus = 2;
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.skill.oral = 100;
+	_HS.skill.anal = 100;
+	_HS.skill.whoring = 100;
+	_HS.skill.entertainment = 100;
+	_HS.clothes = "a slave gown";
+	_HS.energy = 65;
+	_HS.attrXX = 80;
+	_HS.attrXY = 40;
+	_HS.fetishKnown = 1;
+	_HS.brand["left hand"] = "a large letter 'S'";
+	_HS.custom.desc = "$He speaks with the demeaning accent of slaves from the Old South.";
+	if (V.seeDicks !== 0) {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 3;
+		_HS.balls = 3;
+		_HS.scrotum = 3;
+		_HS.foreskin = 3;
+		_HS.prostate = 1;
+		_HS.pubertyXY = 1;
+	} else {
+		_HS.genes = "XX";
+		_HS.vagina = 3;
+		_HS.ovaries = 1;
+		_HS.pubertyXX = 1;
+	}
+	newSlave(_HS);
+	slaveNames.push(_HS.slaveName);
+
+	_HS = clone(_slave);
+	_HS.slaveName = "Sheba";
+	_HS.birthName = "Shaneequa";
+	_HS.ID = 990002;
+	_HS.rivalry = 1;
+	_HS.rivalryTarget = 990000;
+	_HS.assignment = "whore";
+	_HS.birthWeek = random(0, 51);
+	_HS.actualAge = 19;
+	_HS.physicalAge = 19;
+	_HS.visualAge = 19;
+	_HS.ovaryAge = 19;
+	setHealth(_HS, 10);
+	_HS.devotion = 60;
+	_HS.nationality = "Stateless";
+	_HS.height = 175;
+	_HS.race = "black";
+	_HS.pubicHColor = "black";
+	_HS.origSkin = "brown";
+	_HS.hStyle = "long";
+	_HS.pubicHStyle = "waxed";
+	_HS.waist = -55;
+	_HS.boobs = 1600;
+	_HS.boobsImplant = 600;
+	_HS.boobsImplantType = "normal";
+	_HS.nipplesPiercing = 1;
+	_HS.areolae = 1;
+	_HS.butt = 6;
+	_HS.buttImplant = 2;
+	_HS.buttImplantType = "normal";
+	_HS.face = 55;
+	_HS.faceImplant = 15;
+	_HS.lips = 55;
+	_HS.lipsImplant = 10;
+	_HS.anus = 1;
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.skill.oral = 35;
+	_HS.skill.anal = 35;
+	_HS.skill.whoring = 35;
+	_HS.skill.entertainment = 35;
+	_HS.clothes = "a slave gown";
+	_HS.energy = 100;
+	_HS.attrXY = 40;
+	_HS.fetishKnown = 1;
+	_HS.brand["left hand"] = "a large letter 'S'";
+	_HS.custom.desc = "$He speaks with the demeaning accent of slaves from the Old South.";
+	_HS.mother = 990001;
+	if (V.seeDicks !== 100) {
+		_HS.genes = "XX";
+		_HS.vagina = 1;
+		_HS.ovaries = 1;
+		_HS.skill.vaginal = 35;
+		_HS.pubertyXX = 1;
+	} else {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 2;
+		_HS.balls = 2;
+		_HS.scrotum = 2;
+		_HS.foreskin = 2;
+		_HS.prostate = 1;
+		_HS.pubertyXY = 1;
+	}
+	newSlave(_HS);
+	slaveNames.push(_HS.slaveName);
+
+	_HS = clone(_slave);
+	_HS.slaveName = "Cornflower";
+	_HS.birthName = "Alysa";
+	_HS.ID = 990003;
+	_HS.relationship = 3;
+	_HS.relationshipTarget = 990005;
+	_HS.assignment = "get milked";
+	_HS.birthWeek = random(0, 51);
+	setHealth(_HS, 20);
+	_HS.devotion = 60;
+	_HS.nationality = "Stateless";
+	_HS.muscles = 50;
+	_HS.height = 190;
+	_HS.race = "black";
+	_HS.origHColor = "black";
+	_HS.pubicHColor = "black";
+	_HS.origSkin = "brown";
+	_HS.hLength = 0;
+	_HS.hStyle = "shaved bald";
+	_HS.pubicHStyle = "waxed";
+	_HS.waist = -100;
+	_HS.heels = 1;
+	_HS.voice = 0;
+	_HS.boobs = 6000;
+	_HS.nipples = "huge";
+	_HS.areolae = 2;
+	_HS.boobsTat = "bovine patterns";
+	_HS.lactation = 2;
+	_HS.lactationDuration = 2;
+	_HS.butt = 3;
+	_HS.buttTat = "bovine patterns";
+	_HS.face = 15;
+	_HS.lips = 35;
+	_HS.lipsTat = "bovine patterns";
+	_HS.anus = 3;
+	_HS.anusTat = "bovine patterns";
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.nosePiercing = 2;
+	_HS.shouldersTat = "bovine patterns";
+	_HS.armsTat = "bovine patterns";
+	_HS.legsTat = "bovine patterns";
+	_HS.stampTat = "bovine patterns";
+	_HS.skill.oral = 15;
+	_HS.skill.anal = 35;
+	_HS.energy = 65;
+	_HS.attrXY = 40;
+	_HS.fetish = "boobs";
+	_HS.fetishKnown = 1;
+	_HS.custom.tattoo = "A pretty blue cornflower is tattooed on each of $his cheeks.";
+	_HS.custom.desc = "$He once spoke with the demeaning accent of slaves from the Old South.";
+	_HS.mother = 990005;
+	if (V.seeDicks !== 100) {
+		_HS.genes = "XX";
+		_HS.vagina = 1;
+		_HS.vaginaTat = "bovine patterns";
+		_HS.ovaries = 1;
+		_HS.skill.vaginal = 15;
+		_HS.pubertyXX = 1;
+	} else {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 3;
+		_HS.balls = 3;
+		_HS.scrotum = 3;
+		_HS.foreskin = 3;
+		_HS.prostate = 1;
+		_HS.dickTat = "bovine patterns";
+		_HS.pubertyXY = 1;
+	}
+	newSlave(_HS);
+	slaveNames.push(_HS.slaveName);
+
+	_HS = clone(_slave);
+	_HS.slaveName = "Miss Lily";
+	_HS.birthName = "Lillian";
+	_HS.ID = 990004;
+	_HS.assignment = "guard you";
+	_HS.birthWeek = random(0, 51);
+	setHealth(_HS, 20);
+	_HS.devotion = 30;
+	_HS.nationality = "Stateless";
+	_HS.muscles = 50;
+	_HS.height = 175;
+	_HS.race = "white";
+	_HS.eye.origColor = "green";
+	_HS.origHColor = "straw blonde";
+	_HS.origSkin = "pale";
+	_HS.hLength = 40;
+	_HS.hStyle = "in a short ponytail";
+	_HS.waist = -55;
+	_HS.boobs = 600;
+	_HS.butt = 3;
+	_HS.face = 15;
+	_HS.lips = 35;
+	_HS.preg = -2;
+	_HS.anus = 2;
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.skill.anal = 35;
+	_HS.skill.combat = 1;
+	_HS.energy = 65;
+	_HS.attrXY = 40;
+	_HS.fetish = "buttslut";
+	_HS.fetishKnown = 1;
+	_HS.custom.tattoo = "'Miss Lily' is tattooed in lovely flowing script over $his collarbone.";
+	_HS.custom.desc = "$He once spoke with the rich accent of the Old South.";
+	_HS.mother = 990000;
+	if (V.seeDicks !== 100) {
+		_HS.genes = "XX";
+		_HS.ovaries = 1;
+		_HS.pubertyXX = 1;
+	} else {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 4;
+		_HS.balls = 4;
+		_HS.scrotum = 4;
+		_HS.foreskin = 4;
+		_HS.prostate = 1;
+		_HS.pubertyXY = 1;
+	}
+	newSlave(_HS);
+	V.BodyguardID = _HS.ID;
+	slaveNames.push(_HS.slaveName);
+
+	_HS = clone(_slave);
+	_HS.slaveName = "Lilac";
+	_HS.birthName = "Lillian";
+	_HS.ID = 990005;
+	_HS.relationship = 3;
+	_HS.relationshipTarget = 990003;
+	_HS.assignment = "get milked";
+	_HS.birthWeek = random(0, 51);
+	_HS.actualAge = 36;
+	_HS.physicalAge = 36;
+	_HS.visualAge = 36;
+	_HS.ovaryAge = 36;
+	setHealth(_HS, 20);
+	_HS.devotion = 60;
+	_HS.nationality = "Stateless";
+	_HS.muscles = 50;
+	_HS.height = 190;
+	_HS.race = "black";
+	_HS.origHColor = "black";
+	_HS.pubicHColor = "black";
+	_HS.origSkin = "brown";
+	_HS.hLength = 0;
+	_HS.hStyle = "shaved bald";
+	_HS.pubicHStyle = "waxed";
+	_HS.waist = -55;
+	_HS.heels = 1;
+	_HS.boobs = 8000;
+	_HS.nipples = "huge";
+	_HS.areolae = 2;
+	_HS.boobsTat = "bovine patterns";
+	_HS.lactation = 2;
+	_HS.lactationDuration = 2;
+	_HS.butt = 4;
+	_HS.buttTat = "bovine patterns";
+	_HS.face = 15;
+	_HS.lips = 35;
+	_HS.lipsTat = "bovine patterns";
+	_HS.anus = 3;
+	_HS.anusTat = "bovine patterns";
+	_HS.makeup = 1;
+	_HS.nails = 1;
+	_HS.earPiercing = 1;
+	_HS.nosePiercing = 2;
+	_HS.shouldersTat = "bovine patterns";
+	_HS.armsTat = "bovine patterns";
+	_HS.legsTat = "bovine patterns";
+	_HS.stampTat = "bovine patterns";
+	_HS.skill.oral = 15;
+	_HS.skill.anal = 35;
+	_HS.energy = 65;
+	_HS.attrXY = 40;
+	_HS.fetish = "boobs";
+	_HS.fetishKnown = 1;
+	_HS.custom.tattoo = "A pretty purple lilac is tattooed on each of $his cheeks.";
+	_HS.custom.desc = "$He once spoke with the demeaning accent of slaves from the Old South.";
+	if (V.seeDicks !== 100) {
+		_HS.genes = "XX";
+		_HS.vagina = 1;
+		_HS.vaginaTat = "bovine patterns";
+		_HS.ovaries = 1;
+		_HS.skill.vaginal = 15;
+		_HS.pubertyXX = 1;
+	} else {
+		_HS.genes = "XY";
+		_HS.vagina = -1;
+		_HS.dick = 5;
+		_HS.balls = 5;
+		_HS.scrotum = 5;
+		_HS.foreskin = 5;
+		_HS.prostate = 1;
+		_HS.dickTat = "bovine patterns";
+		_HS.pubertyXY = 1;
+	}
+	newSlave(_HS);
+	slaveNames.push(_HS.slaveName);
+
+	r.push(`${arrayToSentence(slaveNames)}.`);
+	App.Events.addNode(el, r);
+	return el;
+};
diff --git a/src/npc/databases/cheatmodeDatabase.tw b/src/npc/databases/cheatmodeDatabase.tw
deleted file mode 100644
index 5149a727ca44ee438b0c862607d9d37a6004facd..0000000000000000000000000000000000000000
--- a/src/npc/databases/cheatmodeDatabase.tw
+++ /dev/null
@@ -1,113 +0,0 @@
-:: Cheatmode Database [nobr]
-
-<<set _slave = BaseSlave()>>
-<<set _HS = clone(_slave)>>
-<<set
-	_HS.slaveName = "Miss Anne",
-	_HS.birthName = "Lindy Anne",
-	_HS.ID = 990000,
-	_HS.relationship = 4,
-	_HS.relationshipTarget = 990001,
-	_HS.rivalry = 1,
-	_HS.rivalryTarget = 990002,
-	_HS.assignment = "be your Head Girl",
-	_HS.birthWeek = random(0,51),
-	_HS.actualAge = 42,
-	_HS.physicalAge = 42,
-	_HS.visualAge = 42,
-	_HS.ovaryAge = 42,
-	_HS.ageImplant = 1,
-	setHealth(_HS, 50),
-	_HS.devotion = 100,
-	_HS.nationality = "Stateless",
-	_HS.height = 175,
-	_HS.race = "white",
-	_HS.eye.origColor = "green",
-	_HS.origHColor = "honey blonde",
-	_HS.origSkin = "pale",
-	_HS.hStyle = "long",
-	_HS.waist = -55,
-	_HS.boobs = 1000,
-	_HS.boobsImplant = 600,
-	_HS.boobsImplantType = "normal",
-	_HS.areolae = 1,
-	_HS.butt = 4,
-	_HS.buttImplant = 2,
-	_HS.buttImplantType = "normal",
-	_HS.face = 55,
-	_HS.lips = 35,
-	_HS.anus = 1,
-	_HS.makeup = 1,
-	_HS.nails = 1,
-	_HS.earPiercing = 1,
-	_HS.skill.oral = 100,
-	_HS.skill.anal = 100,
-	_HS.skill.whoring = 100,
-	_HS.skill.entertainment = 100,
-	_HS.clothes = "a slave gown",
-	_HS.intelligence = 100,
-	_HS.energy = 65,
-	_HS.attrXY = 40,
-	_HS.fetishKnown = 1,
-	_HS.custom.tattoo = "'Miss Anne' is tattooed in lovely flowing script over $his collarbone.",
-	_HS.custom.desc = "$He speaks with the rich accent of the Old South."
->>
-<<if $seeDicks != 100>>
-	<<set _HS.genes = "XX", _HS.vagina = 2, _HS.ovaries = 1, _HS.skill.vaginal = 100, _HS.pubertyXX = 1>>
-<<else>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 4, _HS.balls = 4, _HS.scrotum = 4, _HS.foreskin = 4, _HS.prostate = 1, _HS.pubertyXY = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-<<set $HeadGirlID = _HS.ID>>
-//There are quite a few left; their names are _HS.slaveName,//
-
-<<set _HS = clone(_slave)>>
-<<set _HS.slaveName = "Cornelia", _HS.birthName = "Cora", _HS.ID = 990001, _HS.relationship = 4, _HS.relationshipTarget = 990000, _HS.assignment = "whore", _HS.birthWeek = random(0,51), _HS.actualAge = 36, _HS.physicalAge = 36, _HS.visualAge = 36, _HS.ovaryAge = 36, _HS.ageImplant = 1, setHealth(_HS, 10), _HS.devotion = 100, _HS.nationality = "Stateless", _HS.muscles = 20, _HS.height = 190, _HS.race = "black", _HS.origHColor = "black", _HS.pubicHColor = "black", _HS.origSkin = "dark", _HS.hStyle = "long", _HS.pubicHStyle = "waxed", _HS.waist = -55, _HS.boobs = 1200, _HS.boobsImplant = 1000, _HS.boobsImplantType = "fillable", _HS.areolae = 2, _HS.butt = 5, _HS.buttImplant = 3, _HS.buttImplantType = "fillable", _HS.preg = -2, _HS.face = 15, _HS.faceImplant = 65, _HS.lips = 35, _HS.lipsImplant = 10, _HS.anus = 2, _HS.makeup = 1, _HS.nails = 1, _HS.earPiercing = 1, _HS.skill.oral = 100, _HS.skill.anal = 100, _HS.skill.whoring = 100, _HS.skill.entertainment = 100, _HS.clothes = "a slave gown", _HS.energy = 65, _HS.attrXX = 80, _HS.attrXY = 40, _HS.fetishKnown = 1, _HS.brand["left hand"] = "a large letter 'S'", _HS.custom.desc = "$He speaks with the demeaning accent of slaves from the Old South.">>
-<<if $seeDicks != 0>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 3, _HS.balls = 3, _HS.scrotum = 3, _HS.foreskin = 3, _HS.prostate = 1, _HS.pubertyXY = 1>>
-<<else>>
-	<<set _HS.genes = "XX", _HS.vagina = 3, _HS.ovaries = 1, _HS.pubertyXX = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-//_HS.slaveName,//
-
-<<set _HS = clone(_slave)>>
-<<set _HS.slaveName = "Sheba", _HS.birthName = "Shaneequa", _HS.ID = 990002, _HS.rivalry = 1, _HS.rivalryTarget = 990000, _HS.assignment = "whore", _HS.birthWeek = random(0,51), _HS.actualAge = 19, _HS.physicalAge = 19, _HS.visualAge = 19, _HS.ovaryAge = 19, setHealth(_HS, 10), _HS.devotion = 60, _HS.nationality = "Stateless", _HS.height = 175, _HS.race = "black", _HS.pubicHColor = "black", _HS.origSkin = "brown", _HS.hStyle = "long", _HS.pubicHStyle = "waxed", _HS.waist = -55, _HS.boobs = 1600, _HS.boobsImplant = 600, _HS.boobsImplantType = "normal", _HS.nipplesPiercing = 1, _HS.areolae = 1, _HS.butt = 6, _HS.buttImplant = 2, _HS.buttImplantType = "normal", _HS.face = 55, _HS.faceImplant = 15, _HS.lips = 55, _HS.lipsImplant = 10, _HS.anus = 1, _HS.makeup = 1, _HS.nails = 1, _HS.earPiercing = 1, _HS.skill.oral = 35, _HS.skill.anal = 35, _HS.skill.whoring = 35, _HS.skill.entertainment = 35, _HS.clothes = "a slave gown", _HS.energy = 100, _HS.attrXY = 40, _HS.fetishKnown = 1, _HS.brand["left hand"] = "a large letter 'S'", _HS.custom.desc = "$He speaks with the demeaning accent of slaves from the Old South.", _HS.mother = 990001>>
-<<if $seeDicks != 100>>
-	<<set _HS.genes = "XX", _HS.vagina = 1, _HS.ovaries = 1, _HS.skill.vaginal = 35, _HS.pubertyXX = 1>>
-<<else>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 2, _HS.balls = 2, _HS.scrotum = 2, _HS.foreskin = 2, _HS.prostate = 1, _HS.pubertyXY = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-//_HS.slaveName,//
-
-<<set _HS = clone(_slave)>>
-<<set _HS.slaveName = "Cornflower", _HS.birthName = "Alysa", _HS.ID = 990003, _HS.relationship = 3, _HS.relationshipTarget = 990005, _HS.assignment = "get milked", _HS.birthWeek = random(0,51), setHealth(_HS, 20), _HS.devotion = 60, _HS.nationality = "Stateless", _HS.muscles = 50, _HS.height = 190, _HS.race = "black", _HS.origHColor = "black", _HS.pubicHColor = "black", _HS.origSkin = "brown", _HS.hLength = 0, _HS.hStyle = "shaved bald", _HS.pubicHStyle = "waxed", _HS.waist = -100, _HS.heels = 1, _HS.voice = 0, _HS.boobs = 6000, _HS.nipples = "huge", _HS.areolae = 2, _HS.boobsTat = "bovine patterns", _HS.lactation = 2, _HS.lactationDuration = 2, _HS.butt = 3, _HS.buttTat = "bovine patterns", _HS.face = 15, _HS.lips = 35, _HS.lipsTat = "bovine patterns", _HS.anus = 3, _HS.anusTat = "bovine patterns", _HS.makeup = 1, _HS.nails = 1, _HS.earPiercing = 1, _HS.nosePiercing = 2, _HS.shouldersTat = "bovine patterns", _HS.armsTat = "bovine patterns", _HS.legsTat = "bovine patterns", _HS.stampTat = "bovine patterns", _HS.skill.oral = 15, _HS.skill.anal = 35, _HS.energy = 65, _HS.attrXY = 40, _HS.fetish = "boobs", _HS.fetishKnown = 1, _HS.custom.tattoo = "A pretty blue cornflower is tattooed on each of $his cheeks.", _HS.custom.desc = "$He once spoke with the demeaning accent of slaves from the Old South.", _HS.mother = 990005>>
-<<if $seeDicks != 100>>
-	<<set _HS.genes = "XX", _HS.vagina = 1, _HS.vaginaTat = "bovine patterns", _HS.ovaries = 1, _HS.skill.vaginal = 15, _HS.pubertyXX = 1>>
-<<else>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 3, _HS.balls = 3, _HS.scrotum = 3, _HS.foreskin = 3, _HS.prostate = 1, _HS.dickTat = "bovine patterns", _HS.pubertyXY = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-//_HS.slaveName,//
-
-<<set _HS = clone(_slave)>>
-<<set _HS.slaveName = "Miss Lily", _HS.birthName = "Lillian", _HS.ID = 990004, _HS.assignment = "guard you", _HS.birthWeek = random(0,51), setHealth(_HS, 20), _HS.devotion = 30, _HS.nationality = "Stateless", _HS.muscles = 50, _HS.height = 175, _HS.race = "white", _HS.eye.origColor = "green", _HS.origHColor = "straw blonde", _HS.origSkin = "pale", _HS.hLength = 40, _HS.hStyle = "in a short ponytail", _HS.waist = -55, _HS.boobs = 600, _HS.butt = 3, _HS.face = 15, _HS.lips = 35, _HS.preg = -2, _HS.anus = 2, _HS.makeup = 1, _HS.nails = 1, _HS.earPiercing = 1, _HS.skill.anal = 35, _HS.skill.combat = 1, _HS.energy = 65, _HS.attrXY = 40, _HS.fetish = "buttslut", _HS.fetishKnown = 1, _HS.custom.tattoo = "'Miss Lily' is tattooed in lovely flowing script over $his collarbone.", _HS.custom.desc = "$He once spoke with the rich accent of the Old South.", _HS.mother = 990000>>
-<<if $seeDicks != 100>>
-	<<set _HS.genes = "XX", _HS.ovaries = 1, _HS.pubertyXX = 1>>
-<<else>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 4, _HS.balls = 4, _HS.scrotum = 4, _HS.foreskin = 4, _HS.prostate = 1, _HS.pubertyXY = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-<<set $BodyguardID = _HS.ID>>
-//_HS.slaveName,//
-
-<<set _HS = clone(_slave)>>
-<<set _HS.slaveName = "Lilac", _HS.birthName = "Lillian", _HS.ID = 990005, _HS.relationship = 3, _HS.relationshipTarget = 990003, _HS.assignment = "get milked", _HS.birthWeek = random(0,51), _HS.actualAge = 36, _HS.physicalAge = 36, _HS.visualAge = 36, _HS.ovaryAge = 36, setHealth(_HS, 20), _HS.devotion = 60, _HS.nationality = "Stateless", _HS.muscles = 50, _HS.height = 190, _HS.race = "black", _HS.origHColor = "black", _HS.pubicHColor = "black", _HS.origSkin = "brown", _HS.hLength = 0, _HS.hStyle = "shaved bald", _HS.pubicHStyle = "waxed", _HS.waist = -55, _HS.heels = 1, _HS.boobs = 8000, _HS.nipples = "huge", _HS.areolae = 2, _HS.boobsTat = "bovine patterns", _HS.lactation = 2, _HS.lactationDuration = 2, _HS.butt = 4, _HS.buttTat = "bovine patterns", _HS.face = 15, _HS.lips = 35, _HS.lipsTat = "bovine patterns", _HS.anus = 3, _HS.anusTat = "bovine patterns", _HS.makeup = 1, _HS.nails = 1, _HS.earPiercing = 1, _HS.nosePiercing = 2, _HS.shouldersTat = "bovine patterns", _HS.armsTat = "bovine patterns", _HS.legsTat = "bovine patterns", _HS.stampTat = "bovine patterns", _HS.skill.oral = 15, _HS.skill.anal = 35, _HS.energy = 65, _HS.attrXY = 40, _HS.fetish = "boobs", _HS.fetishKnown = 1, _HS.custom.tattoo = "A pretty purple lilac is tattooed on each of $his cheeks.", _HS.custom.desc = "$He once spoke with the demeaning accent of slaves from the Old South.">>
-<<if $seeDicks != 100>>
-	<<set _HS.genes = "XX", _HS.vagina = 1, _HS.vaginaTat = "bovine patterns", _HS.ovaries = 1, _HS.skill.vaginal = 15, _HS.pubertyXX = 1>>
-<<else>>
-	<<set _HS.genes = "XY", _HS.vagina = -1, _HS.dick = 5, _HS.balls = 5, _HS.scrotum = 5, _HS.foreskin = 5, _HS.prostate = 1, _HS.dickTat = "bovine patterns", _HS.pubertyXY = 1>>
-<</if>>
-<<run newSlave(_HS)>>
-//and _HS.slaveName.//
diff --git a/src/npc/descriptions/belly/bellyImplant.js b/src/npc/descriptions/belly/bellyImplant.js
index 88a9154692ecc1448db67b52e67c7c9e97237e6b..eb4db89db8c861c3b66dfe55acae8f6abf902edd 100644
--- a/src/npc/descriptions/belly/bellyImplant.js
+++ b/src/npc/descriptions/belly/bellyImplant.js
@@ -493,7 +493,7 @@ App.Desc.bellyImplant = function(slave, {market, eventDescription} = {}) {
 								r.push(`${slave.slaveName}'s gigantic implant-filled belly adds even more strain to ${his} struggling oversized sweater.`);
 							} else if (slave.boobs > 8000) {
 								r.push(`${slave.slaveName}'s gigantic implant-filled belly parts ${his} poorly covered breasts.`);
-							} else if (slave.boobs > 8000) {
+							} else if (slave.boobs > 6000) {
 								r.push(`${slave.slaveName}'s oversized breasts keep ${his} sweater far from ${his} gigantic implant-filled belly.`);
 							} else if (slave.boobs > 4000) {
 								r.push(`${slave.slaveName}'s sweater rests atop ${his} gigantic implant-filled belly.`);
diff --git a/src/npc/descriptions/butt/anus.js b/src/npc/descriptions/butt/anus.js
index 901843c1d23ee531ef59a07f9171962be8e0b198..1a5cbe7675f171da341cc2ad5e5dd3d1b2e207e7 100644
--- a/src/npc/descriptions/butt/anus.js
+++ b/src/npc/descriptions/butt/anus.js
@@ -55,7 +55,7 @@ App.Desc.anus = function(slave, {market, eventDescription} = {}) {
 				r.push(`base of ${his} cock.`);
 			}
 		} else if (slave.analArea - slave.anus > 1) {
-			r.push(`but it's surrounded by an oval of ${analSkinDesc} ${skinDesc} skin that occupies ${his} entire ${either("asscrack", "buttcrack")}`);
+			r.push(`but it's surrounded by an oval of ${analSkinDesc} ${skinDesc} skin that occupies ${his} entire ${either("asscrack", "buttcrack")}.`);
 		} else if (slave.analArea - slave.anus > 0) {
 			r.push(`and it's surrounded by a big ring of ${analSkinDesc} ${skinDesc} skin.`);
 		} else {
diff --git a/src/npc/descriptions/career.js b/src/npc/descriptions/career.js
index 8a9c7826e1a0bca65b128c90610a16e644e57125..25d069597e058747b878e145a380d987fc5d74dd 100644
--- a/src/npc/descriptions/career.js
+++ b/src/npc/descriptions/career.js
@@ -27,96 +27,96 @@ App.Desc.career = function(slave) {
 				r.push(`${He} has spent time as a cow in an industrial dairy, an experience that marked ${him} deeply.`);
 			} else {
 				r.push(`Before ${he} was a slave, ${he} was`);
-				if (setup.bodyguardCareers.includes(slave.career)) {
+				if (App.Data.Careers.Leader.bodyguard.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Bodyguard.`);
-				} else if (setup.wardenessCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.wardeness.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Wardeness for`);
 					if (V.cellblock === 0) {
 						r.push(`a Cellblock.`);
 					} else {
 						r.push(`${V.cellblockName}.`);
 					}
-				} else if (setup.attendantCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.attendant.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as an Attendant for`);
 					if (V.spa === 0) {
 						r.push(`a Spa.`);
 					} else {
 						r.push(`${V.spaName}.`);
 					}
-				} else if (setup.matronCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.matron.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Matron for`);
 					if (V.nursery === 0 && V.nurseryNannies === 0) {
 						r.push(`a Nursery.`);
 					} else {
 						r.push(`${V.nurseryName}.`);
 					}
-				} else if (setup.nurseCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.nurse.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Nurse for`);
 					if (V.clinic === 0) {
 						r.push(`a Clinic.`);
 					} else {
 						r.push(`${V.clinicName}.`);
 					}
-				} else if (setup.schoolteacherCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.schoolteacher.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Schoolteacher for`);
 					if (V.schoolroom === 0) {
 						r.push(`a Schoolroom.`);
 					} else {
 						r.push(`${V.schoolroomName}.`);
 					}
-				} else if (setup.stewardessCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.stewardess.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Stewardess for`);
 					if (V.servantsQuarters === 0) {
 						r.push(`a Servant's Quarters.`);
 					} else {
 						r.push(`${V.servantsQuartersName}.`);
 					}
-				} else if (setup.milkmaidCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.milkmaid.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Milkmaid for`);
 					if (V.dairy === 0) {
 						r.push(`a Dairy.`);
 					} else {
 						r.push(`${V.dairyName}.`);
 					}
-				} else if (setup.farmerCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.farmer.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Farmer for`);
 					if (V.farmyard === 0) {
 						r.push(`a Farmyard.`);
 					} else {
 						r.push(`${V.farmyardName}.`);
 					}
-				} else if (setup.madamCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.madam.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Madam for`);
 					if (V.brothel === 0) {
 						r.push(`a Brothel.`);
 					} else {
 						r.push(`${V.brothelName}.`);
 					}
-				} else if (setup.DJCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.DJ.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a DJ for`);
 					if (V.club === 0) {
 						r.push(`a Club.`);
 					} else {
 						r.push(`${V.clubName}.`);
 					}
-				} else if (setup.HGCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.HG.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a Head Girl.`);
-				} else if (setup.recruiterCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.recruiter.includes(slave.career)) {
 					r.push(`${career}, giving ${him} potential as a recruiter.`);
-				} else if (setup.entertainmentCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.General.entertainment.includes(slave.career)) {
 					r.push(`${career}, giving ${him} a slight edge at entertainment.`);
-				} else if (setup.whoreCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.General.whore.includes(slave.career)) {
 					r.push(`${career}, giving ${him} a slight edge at sexual commerce.`);
-				} else if (setup.gratefulCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.General.grateful.includes(slave.career)) {
 					r.push(`${career}, so ${he} can remember what it's like`);
 					if (slave.career === "prisoner") {
 						r.push(`no one looking out for you.`);
 					} else {
 						r.push(`to have the freedom to starve.`);
 					}
-				} else if (setup.menialCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.General.menial.includes(slave.career)) {
 					r.push(`${career}, giving ${him} experience following orders.`);
-				} else if (setup.servantCareers.includes(slave.career)) {
+				} else if (App.Data.Careers.Leader.servant.includes(slave.career)) {
 					r.push(`${career}, giving ${him} a slight edge in housekeeping.`);
 				} else {
 					r.push(`${career}.`);
@@ -124,7 +124,7 @@ App.Desc.career = function(slave) {
 			}
 		}
 		if (V.week - slave.weekAcquired >= 20 && slave.skill.entertainment >= 100) {
-			if (!setup.entertainmentCareers.includes(slave.career)) {
+			if (!App.Data.Careers.General.entertainment.includes(slave.career)) {
 				r.push(`${He} has gotten enough experience to be as charismatic as any professional`);
 				if (slave.counter.oral + slave.counter.anal + slave.counter.vaginal + slave.counter.mammary + slave.counter.penetrative > 1000) {
 					r.push(`entertainer, and has been fucked so many times that a free sex worker could teach ${him} nothing.`);
@@ -133,7 +133,7 @@ App.Desc.career = function(slave) {
 				}
 			}
 		} else if (slave.counter.oral + slave.counter.anal + slave.counter.vaginal + slave.counter.mammary + slave.counter.penetrative > 1000) {
-			if (!setup.whoreCareers.includes(slave.career)) {
+			if (!App.Data.Careers.General.whore.includes(slave.career)) {
 				r.push(`${He} has been fucked so many times that a free sex worker could teach ${him} nothing.`);
 			}
 		}
diff --git a/src/npc/descriptions/describePiercings.js b/src/npc/descriptions/describePiercings.js
index 5b1ee13cd0f9dd3e9c8857afaa3056e65beb1a59..cbc1985d22c0cb17eb79825d89ec4493df290275 100644
--- a/src/npc/descriptions/describePiercings.js
+++ b/src/npc/descriptions/describePiercings.js
@@ -520,9 +520,9 @@ App.Desc.piercing = function(slave, surface) {
 						switch (slave.clothes) {
 							case "a hijab and blouse":
 							case "a schoolgirl outfit":
-							case  "conservative clothing":
-							case  "nice business attire":
-							case  "slutty business attire":
+							case "conservative clothing":
+							case "nice business attire":
+							case "slutty business attire":
 								r.push(`${his} blouse hides them completely, but they're laced tightly, so ${he}'s aware they're there.`);
 								break;
 							case "chains":
@@ -538,7 +538,7 @@ App.Desc.piercing = function(slave, surface) {
 								r.push(`${his} huipil hides them partially, so the leather straps that pull on ${his} skin are clearly evident.`);
 								break;
 							case "a long qipao":
-							case  "a slutty qipao":
+							case "a slutty qipao":
 								r.push(`${his} qipao hides them completely, but they're laced tightly with silk cord, so ${he}'s aware they're there.`);
 								break;
 							case "uncomfortable straps":
@@ -548,7 +548,7 @@ App.Desc.piercing = function(slave, surface) {
 								r.push(`they're laced together as part of ${his} bindings.`);
 								break;
 							case "a latex catsuit":
-							case  "restrictive latex":
+							case "restrictive latex":
 								r.push(`these are clipped into the latex covering them, making it almost a part of ${his} body.`);
 								if (slave.devotion > 50) {
 									r.push(`${He} couldn't remove it, even if ${he} wanted to.`);
@@ -602,8 +602,8 @@ App.Desc.piercing = function(slave, surface) {
 								}
 								break;
 							case "attractive lingerie":
-							case  "attractive lingerie for a pregnant woman":
-							case  "kitty lingerie":
+							case "attractive lingerie for a pregnant woman":
+							case "kitty lingerie":
 								r.push(`they're laced together with a lacy ribbon finished off with a bow.`);
 								break;
 							case "a succubus outfit":
@@ -619,12 +619,12 @@ App.Desc.piercing = function(slave, surface) {
 								r.push(`${his} habit hides them completely, but they're laced tightly, so ${he}'s aware they're there.`);
 								break;
 							case "a gothic lolita dress":
-							case  "a hanbok":
-							case  "a Santa dress":
+							case "a hanbok":
+							case "a Santa dress":
 								r.push(`${his} dress hides them completely, but they're laced tightly, so ${he}'s aware they're there.`);
 								break;
 							case "a burkini":
-							case  "a one-piece swimsuit":
+							case "a one-piece swimsuit":
 								r.push(`${his} swimsuit hides them completely, but they're laced tightly, so ${he}'s aware they're there.`);
 								break;
 							case "a monokini":
diff --git a/src/npc/descriptions/describeTattoos.js b/src/npc/descriptions/describeTattoos.js
index 31be28fb299484947585494885e9e98c3886ee95..52e9e03baddadb7fb70a981726bb897d898d711f 100644
--- a/src/npc/descriptions/describeTattoos.js
+++ b/src/npc/descriptions/describeTattoos.js
@@ -804,7 +804,7 @@ App.Desc.tattoo = function(slave, surface) {
 						r.push(`${His} anus is bleached.`);
 						break;
 					case "tribal patterns":
-						r.push(`${His} anus is bleached. It is tattooed with a tribal pattern that changes interestingly when ${he} relaxes or tightens ${his} sphincter.`);
+						r.push(`${His} anus is tattooed with a tribal pattern that changes interestingly when ${he} relaxes or tightens ${his} sphincter.`);
 						break;
 					case "flowers":
 						r.push(`${He} has a huge blooming flower tattooed right over ${his} anus.`);
diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js
index 50c04bce23830c5408f9264359fc6739aa8fe28c..ee287553a7c6622338eaec17783d83d11a55ad3a 100644
--- a/src/npc/descriptions/descriptionWidgets.js
+++ b/src/npc/descriptions/descriptionWidgets.js
@@ -419,38 +419,32 @@ App.Desc.ageAndHealth = function(slave) {
 			} else if (H.shortDamage >= 40) {
 				array.push(`is <span class="red">seriously injured</span> with some lasting effects`);
 			} else if (H.shortDamage >= 20) {
-				array.push(`is <span class="orange">injured`);
+				array.push(`is <span class="orange">injured</span>`);
 			} else if (H.shortDamage > 5) {
 				array.push(`seems to have suffered a <span class="yellow">minor injury</span> recently`);
 			}
 
 			if (H.longDamage >= 70) {
-				array.push(`is suffering heavily under accumulated <span class="red">permanent health problems`);
+				array.push(`is suffering heavily under accumulated <span class="red">permanent health problems</span>`);
 			} else if (H.longDamage >= 40) {
-				array.push(`has some clear <span class="red">permanent health issues`);
+				array.push(`has some clear <span class="red">permanent health issues</span>`);
 			} else if (H.longDamage >= 20) {
-				array.push(`shows signs of <span class="orange">lasting health problems`);
+				array.push(`shows signs of <span class="orange">lasting health problems</span>`);
 			} else if (H.longDamage > 5) {
-				array.push(`carries some <span class="yellow">minor niggles`);
+				array.push(`carries some <span class="yellow">minor niggles</span>`);
 			}
 
 			if (H.condition < -80 && H.shortDamage !== 0 && H.longDamage !== 0) {
-				array.push(`has been treated so badly ${he} <span class="red">is close to the brink`);
+				array.push(`has been treated so badly ${he} <span class="red">is close to the brink</span>`);
 			} else if (H.condition < -50) {
-				array.push(`appears to be in <span class="red">terrible condition`);
+				array.push(`appears to be in <span class="red">terrible condition</span>`);
 			} else if (H.condition < -20) {
-				array.push(`appears to be in <span class="orange">poor condition`);
+				array.push(`appears to be in <span class="orange">poor condition</span>`);
 			} else if (H.condition < 0) {
-				array.push('could be in <span class="yellow">better condition');
+				array.push(`could be in <span class="yellow">better condition</span>`);
 			}
 
-			if (array.length === 3) {
-				r += (` ${array[0]}, ${array[1]} and ${array[2]}.</span>`);
-			} else if (array.length === 2) {
-				r += (` ${array[0]} and ${array[1]}.</span>`);
-			} else if (array.length === 1) {
-				r += (` ${array[0]}.</span>`);
-			}
+			r += ` ${arrayToSentence(array)}.`;
 		}
 
 		if (H.tired > 30) {
@@ -1471,7 +1465,7 @@ App.Desc.geneticQuirkAssessment = function(slave) {
 				r.push(`fertility.`);
 			}
 		} else if (slave.geneticQuirks.hyperFertility === 2) {
-			r.push(`${He} prone to extreme`);
+			r.push(`${He} is prone to extreme`);
 			if (slave.ovaries === 1 || slave.mpreg === 1) {
 				r.push(`fertility and will likely undergo multiple pregnancy.`);
 			} else {
diff --git a/src/npc/descriptions/sceneIntro.js b/src/npc/descriptions/sceneIntro.js
index 015ee8697e6d849e8584d2ec65230f5aaf4f5604..37535faaa78693829e212550fba9c3a3ac7fefea 100644
--- a/src/npc/descriptions/sceneIntro.js
+++ b/src/npc/descriptions/sceneIntro.js
@@ -43,15 +43,8 @@ App.Desc.sceneIntro = function(slave, {market, eventDescription} = {}) {
 		} else if ((slave.assignment === "work in the dairy") && (V.dairyRestraintsSetting > 1)) {
 			r.push(`You go down to ${V.dairyName} to inspect ${his} heaving body.`);
 		} else if (slave.assignment === "be your agent") {
-			r.push(`You place a call to`);
-			let agentLocation = `${his} current location`;
-			for (let _i = 0; _i < V.arcologies.length; _i++) {
-				if (V.arcologies[_i].leaderID === slave.ID) {
-					agentLocation = V.arcologies[_i].name;
-					break;
-				}
-			}
-			r.push(`${agentLocation}, and ${he} instantly appears on camera.`);
+			const arc = V.arcologies.find(a => a.leaderID === slave.ID);
+			r.push(`You place a call to ${arc ? arc.name : `${his} current location`}, and ${he} instantly appears on camera.`);
 		} else {
 			r.push(`${He} comes to you for an inspection`);
 			switch (slave.assignment) {
@@ -112,13 +105,8 @@ App.Desc.sceneIntro = function(slave, {market, eventDescription} = {}) {
 						if (V.arcologies[0].influenceTarget === -1) {
 							r.push(`right away, since you haven't decided on an arcology to target for cultural influence, leaving ${him} with nothing to do.`);
 						} else {
-							r.push(`from where ${he} was resting after ${his} latest sexually exhausting visit to`);
-							for (let i = 0; i < V.arcologies.length; i++) {
-								if (V.arcologies[i].direction === V.arcologies[0].influenceTarget) {
-									r.push(`${V.arcologies[i].name}.`);
-									break;
-								}
-							}
+							const arc = V.arcologies.find(a => a.direction === V.arcologies[0].influenceTarget);
+							r.push(`from where ${he} was resting after ${his} latest sexually exhausting visit to ${arc ? arc.name : "a nearby arcology"}.`);
 						}
 					}
 					break;
diff --git a/src/npc/descriptions/womb/superfetation.js b/src/npc/descriptions/womb/superfetation.js
index 38538fec09b106a26a7c1d785552311c02c6cc1d..18bcf1506751a2fcfa444ff1a7e8c7241e290f06 100644
--- a/src/npc/descriptions/womb/superfetation.js
+++ b/src/npc/descriptions/womb/superfetation.js
@@ -14,7 +14,7 @@ App.Desc.superfetation = function(slave, {market, eventDescription} = {}) {
 	let daddy;
 	const slaveWD = WombGetLittersData(slave);
 	if (slave.geneticQuirks.superfetation === 2 && slaveWD.litters.length > 1 && V.pregnancyMonitoringUpgrade === 1 && !market) {
-		r.push(`${His} womb contains ${num(slaveWD.litters.length)} separate pregnancies`);
+		r.push(`${His} womb contains ${num(slaveWD.litters.length)} separate pregnancies:`);
 		for (let litCount = 0; litCount < slaveWD.litters.length; litCount++) {
 			const countLitter = slaveWD.countLitter[litCount];
 			const is = countLitter > 1 ? "are" : "is";
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index e8454fa374204938fcc005cd70674707a6063407..710bb4afc6a7ba61a8049a96c298f6602e6a3292 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -7,6 +7,8 @@ globalThis.generateGenetics = (function() {
 	let father;
 	/** @type {FC.HumanState|0} */
 	let activeFather;
+	/** @type {FC.Zeroable<FC.Race>} - if father is unknown, what race was he? */
+	let fatherRace;
 
 
 	// intelligence and face parameters are the same so we can use the same distribution for both values
@@ -93,6 +95,7 @@ globalThis.generateGenetics = (function() {
 			activeFather = 0;
 		}
 
+		chooseUnknownFatherRace(actor2);
 		genes.gender = setGender(father, mother);
 		genes.name = name;
 		genes.mother = actor1.ID;
@@ -102,16 +105,16 @@ globalThis.generateGenetics = (function() {
 		genes.inbreedingCoeff = ibc.kinship(mother, father);
 		genes.nationality = setNationality(father, mother);
 		genes.geneticQuirks = setGeneticQuirks(activeFather, activeMother, genes.gender);
-		genes.skin = setSkin(father, mother, actor2);
-		genes.race = setRace(father, mother, actor2);
+		genes.skin = setSkin(father, mother);
+		genes.race = setRace(father, mother);
 		genes.intelligence = setIntelligence(father, mother, activeMother, actor2, genes.inbreedingCoeff);
 		genes.face = setFace(father, mother, activeMother, actor2, genes.geneticQuirks, genes.inbreedingCoeff);
 		genes.faceShape = setFaceShape(father, mother, genes.geneticQuirks);
-		genes.eyeColor = setEyeColor(father, mother, actor2);
+		genes.eyeColor = setEyeColor(father, mother);
 		if (genes.geneticQuirks.heterochromia === 2) {
-			genes.geneticQuirks.heterochromia = setHeterochromaticEyeColor(father, mother, actor2);
+			genes.geneticQuirks.heterochromia = setHeterochromaticEyeColor(father, mother);
 		}
-		genes.hColor = setHColor(father, mother, actor2);
+		genes.hColor = setHColor(father, mother);
 		genes.underArmHStyle = setUnderArmHStyle(father, mother);
 		genes.pubicHStyle = setPubicHStyle(father, mother);
 		genes.markings = setMarkings(father, mother);
@@ -230,16 +233,26 @@ globalThis.generateGenetics = (function() {
 		}
 	}
 
+	/** Pick a fixed race for the child's unknown father.
+	 * @param {number} actor2
+	 */
+	function chooseUnknownFatherRace(actor2) {
+		fatherRace = 0; // usually this will be interpreted as "copy from mother"
+		if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
+			fatherRace = V.arcologies[0].FSSupremacistRace;
+		} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
+			fatherRace = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace).random();
+		}
+	}
+
 	/**
 	 * race
 	 * @param {*} father
 	 * @param {*} mother
-	 * @param {*} actor2
 	 * @returns {FC.Race}
 	 */
-	function setRace(father, mother, actor2) {
+	function setRace(father, mother) {
 		let race;
-		let fatherRace = 0;
 		if (father !== 0) {
 			if (mother.origRace === father.origRace) {
 				race = mother.origRace;
@@ -249,12 +262,6 @@ globalThis.generateGenetics = (function() {
 				race = "mixed race";
 			}
 		} else {
-			if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
-				fatherRace = V.arcologies[0].FSSupremacistRace;
-			} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
-				let racesList = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace);
-				fatherRace = racesList.random();
-			}
 			if (fatherRace !== 0) {
 				if (mother.origRace === fatherRace) {
 					race = mother.origRace;
@@ -271,7 +278,8 @@ globalThis.generateGenetics = (function() {
 	}
 
 	// skin
-	function setSkin(father, mother, actor2) {
+	function setSkin(father, mother) {
+		/** @type {FC.Zeroable<string>} */
 		let fatherSkin = 0;
 		let dadSkinIndex;
 		const skinToMelanin = {
@@ -302,17 +310,12 @@ globalThis.generateGenetics = (function() {
 			"pure white": 1
 		};
 		const momSkinIndex = mother ? (skinToMelanin[mother.origSkin] || 13) : 8;
-		if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
-			fatherSkin = randomRaceSkin(V.arcologies[0].FSSupremacistRace);
-		} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
-			let racesList = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace);
-			fatherSkin = randomRaceSkin(racesList.random());
-		}
-		if (fatherSkin !== 0) {
-			dadSkinIndex = father !== 0 ? (skinToMelanin[fatherSkin] || 13) : 8;
-		} else {
-			dadSkinIndex = father !== 0 ? (skinToMelanin[father.origSkin] || 13) : 8;
+		if (father !== 0) {
+			fatherSkin = father.origSkin;
+		} else if (fatherRace !== 0) {
+			fatherSkin = randomRaceSkin(fatherRace);
 		}
+		dadSkinIndex = fatherSkin !== 0 ? (skinToMelanin[fatherSkin] || 13) : 8;
 		const skinIndex = Math.round(Math.random() * (dadSkinIndex - momSkinIndex) + momSkinIndex);
 
 		let prop = "";
@@ -323,9 +326,27 @@ globalThis.generateGenetics = (function() {
 		return prop; // skinIndex can be zero - now false?
 	}
 
+	/** Make sure a given eye color is a valid genetic eye color and not the result of some modification.
+	 * @param {string} eyeColor
+	 * @returns {string}
+	 */
+	function validGeneticEyeColor(eyeColor) {
+		switch (eyeColor) {
+			case "blind blue":
+				eyeColor = "deep blue";
+				break;
+			case "milky white":
+			case "implant":
+				eyeColor = jsEither(["blue", "brown", "dark blue", "dark green", "green", "hazel", "light blue", "light green"]);
+				break;
+		}
+		return eyeColor;
+	}
+
 	// eyeColor
-	function setEyeColor(father, mother, actor2) {
+	function setEyeColor(father, mother) {
 		let eyeColor;
+		/** @type {FC.Zeroable<string>} */
 		let fatherEye = 0;
 
 		// during BC WombInit, the mother has been updated but the father might not have been yet.
@@ -361,13 +382,8 @@ globalThis.generateGenetics = (function() {
 				eyeColor = jsEither([fatherEye, mother.eye.origColor]);
 			}
 		} else {
-			if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
-				fatherEye = randomRaceEye(V.arcologies[0].FSSupremacistRace);
-			} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
-				let racesList = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace);
-				fatherEye = randomRaceEye(racesList.random());
-			}
-			if (fatherEye !== 0) {
+			if (fatherRace) {
+				fatherEye = randomRaceEye(fatherRace);
 				if (mother.eye.origColor === fatherEye) {
 					eyeColor = mother.eye.origColor;
 				} else if (["light red", "milky white", "pale gray", "pale red", "red"].contains(mother.eye.origColor)) {
@@ -391,22 +407,10 @@ globalThis.generateGenetics = (function() {
 				eyeColor = mother.eye.origColor;
 			}
 		}
-		// just in case something wrong gets through
-		switch (eyeColor) {
-			case "blind blue":
-				eyeColor = "deep blue";
-				break;
-			case "milky white":
-			case "implant":
-				eyeColor = jsEither(["blue", "brown", "dark blue", "dark green", "green", "hazel", "light blue", "light green"]);
-				break;
-		}
-		return eyeColor;
+		return validGeneticEyeColor(eyeColor);
 	}
 
-	function setHeterochromaticEyeColor(father, mother, actor2) {
-		let hEyeColor;
-		let fatherHEye = 0;
+	function setHeterochromaticEyeColor(father, mother) {
 		let eyeColorArray = [];
 		if (father !== 0) {
 			eyeColorArray.push(mother.eye.origColor);
@@ -415,14 +419,8 @@ globalThis.generateGenetics = (function() {
 				eyeColorArray.push(father.geneticQuirks.heterochromia);
 			}
 		} else {
-			if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
-				fatherHEye = randomRaceEye(V.arcologies[0].FSSupremacistRace);
-			} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
-				let racesList = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace);
-				fatherHEye = randomRaceEye(racesList.random());
-			}
-			if (fatherHEye !== 0) {
-				eyeColorArray.push(fatherHEye);
+			if (fatherRace) {
+				eyeColorArray.push(randomRaceEye(fatherRace));
 				eyeColorArray.push(mother.eye.origColor);
 			} else {
 				eyeColorArray.push(mother.eye.origColor);
@@ -431,23 +429,12 @@ globalThis.generateGenetics = (function() {
 		if (mother.geneticQuirks.heterochromia !== 0 && mother.geneticQuirks.heterochromia !== 1) {
 			eyeColorArray.push(mother.geneticQuirks.heterochromia);
 		}
-		// just in case something wrong gets through
-		switch (hEyeColor) {
-			case "blind blue":
-				hEyeColor = ["deep blue"];
-				break;
-			case "milky white":
-			case "implant":
-				hEyeColor = jsEither(["blue", "brown", "dark blue", "dark green", "green", "hazel", "light blue", "light green"]);
-				break;
-		}
-		return jsEither(eyeColorArray);
+		return validGeneticEyeColor(jsEither(eyeColorArray));
 	}
 
 	// hColor
-	function setHColor(father, mother, actor2) {
+	function setHColor(father, mother) {
 		let hairColor;
-		let fatherHair = 0;
 		if (father !== 0) {
 			if (mother.origHColor === father.origHColor) {
 				hairColor = mother.origHColor;
@@ -467,24 +454,19 @@ globalThis.generateGenetics = (function() {
 				hairColor = jsEither([father.origHColor, mother.origHColor]);
 			}
 		} else {
-			if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSupremacistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSupremacist !== "unset")) {
-				fatherHair = randomRaceHair(V.arcologies[0].FSSupremacistRace);
-			} else if (((actor2 === -2 || actor2 === -5) && V.arcologies[0].FSSubjugationistLawME === 1) || (actor2 === -6 && V.arcologies[0].FSSubjugationist !== "unset")) {
-				let racesList = setup.filterRacesLowercase.filter(race => race !== V.arcologies[0].FSSubjugationistRace);
-				fatherHair = randomRaceHair(racesList.random());
-			}
-			if (fatherHair !== 0) {
+			if (fatherRace !== 0) {
+				const fatherHair = randomRaceHair(fatherRace);
 				if (mother.origHColor === fatherHair) {
 					hairColor = mother.origHColor;
 				} else if (mother.origHColor === "white") {
 					hairColor = jsRandom(1, 100) === 69 ? mother.origHColor : fatherHair;
 				} else if (["black", "jet black"].contains(mother.origHColor)) {
 					hairColor = jsEither([fatherHair, mother.origHColor, mother.origHColor, mother.origHColor, mother.origHColor, mother.origHColor, mother.origHColor, mother.origHColor]);
-				} else if (["black", "jet black"].contains(father.origHColor)) {
+				} else if (["black", "jet black"].contains(fatherHair)) {
 					hairColor = jsEither([fatherHair, fatherHair, fatherHair, fatherHair, fatherHair, fatherHair, fatherHair, mother.origHColor]);
 				} else if (["brown", "chestnut", "chocolate", "dark brown"].contains(mother.origHColor)) {
 					hairColor = jsEither([fatherHair, mother.origHColor, mother.origHColor, mother.origHColor]);
-				} else if (["brown", "chestnut", "chocolate", "dark brown"].contains(father.origHColor)) {
+				} else if (["brown", "chestnut", "chocolate", "dark brown"].contains(fatherHair)) {
 					hairColor = jsEither([fatherHair, fatherHair, fatherHair, mother.origHColor]);
 				} else {
 					hairColor = jsEither([fatherHair, mother.origHColor]);
diff --git a/src/npc/generate/generateLeadershipSlave.js b/src/npc/generate/generateLeadershipSlave.js
index 11f45fad4cd8e30681522643861d6cf397e46842..f8530474b7dac4c3f6f51c2abc24895e93137b9a 100644
--- a/src/npc/generate/generateLeadershipSlave.js
+++ b/src/npc/generate/generateLeadershipSlave.js
@@ -71,7 +71,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			if (jsRandom(0, 2) === 0) {
 				configureLimbs(slave, "all", 5);
 			}
-			slave.career = either(App.Data.misc.bodyguardCareers);
+			slave.career = either(App.Data.Careers.Leader.bodyguard);
 			break;
 		case "Wardeness":
 			slave.energy = jsRandom(80, 100);
@@ -81,7 +81,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			slave.muscles = jsRandom(50, 80);
 			slave.skill.combat = 1;
 			applyMaleGenitalia({dick: jsRandom(3, 6), balls: jsRandom(3, 6), prostate: either(1, 1, 1, 2, 2, 3)});
-			slave.career = either(App.Data.misc.wardenessCareers);
+			slave.career = either(App.Data.Careers.Leader.wardeness);
 			break;
 			// Management
 		case "Headgirl":
@@ -94,7 +94,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			Object.assign(slave.skill, {entertainment: 100, whoring: 100, anal: 100, oral: 100, vaginal: 100});
 			slave.vagina = jsRandom(3, 4);
 			applyMaleGenitalia({dick: jsRandom(3, 5), balls: jsRandom(3, 6), prostate: either(1, 1, 2)});
-			slave.career = either(App.Data.misc.HGCareers);
+			slave.career = either(App.Data.Careers.Leader.HG);
 			break;
 		case "Teacher":
 			slave.fetish = "dom";
@@ -105,7 +105,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			slave.face = jsRandom(41, 90);
 			slave.vagina = jsRandom(3, 4);
 			applyMaleGenitalia({dick: jsRandom(3, 5), balls: jsRandom(3, 6), prostate: either(1, 1, 1, 2, 2, 3)});
-			slave.career = either(App.Data.misc.schoolteacherCareers);
+			slave.career = either(App.Data.Careers.Leader.schoolteacher);
 			break;
 		case "Nurse":
 			slave.fetish = "dom";
@@ -113,7 +113,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			slave.muscles = jsRandom(6, 50);
 			slave.face = jsRandom(41, 90);
 			slave.sexualQuirk = "caring";
-			slave.career = either(App.Data.misc.nurseCareers);
+			slave.career = either(App.Data.Careers.Leader.nurse);
 			break;
 		case "Attendant":
 		case "Motherly Attendant":
@@ -131,20 +131,20 @@ globalThis.generateLeadershipSlave = function(input, location) {
 				slave.preg = 0;
 			}
 			eyeSurgery(slave, "both", either(0, 2, 2) === 2 ? "normal" : "blind");
-			slave.career = either(App.Data.misc.attendantCareers);
+			slave.career = either(App.Data.Careers.Leader.attendant);
 			break;
 		case "Matron":
 			slave.sexualQuirk = "caring";
 			slave.counter.birthsTotal = jsRandom(2, 4);
 			slave.vagina = 3;
 			slave.face = jsRandom(60, 90);
-			slave.career = either(App.Data.misc.matronCareers);
+			slave.career = either(App.Data.Careers.Leader.matron);
 			break;
 		case "Stewardess":
 			slave.energy = jsRandom(70, 90);
 			slave.fetish = "dom";
 			slave.fetishStrength = 100;
-			slave.career = either(App.Data.misc.stewardessCareers);
+			slave.career = either(App.Data.Careers.Leader.stewardess);
 			break;
 		case "Milkmaid":
 			slave.muscles = jsRandom(31, 60);
@@ -152,7 +152,7 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			slave.sexualQuirk = "caring";
 			slave.behavioralQuirk = "funny";
 			applyMaleGenitalia({dick: jsRandom(3, 5), balls: jsRandom(4, 9), prostate: either(1, 1, 1, 2)});
-			slave.career = either(App.Data.misc.milkmaidCareers);
+			slave.career = either(App.Data.Careers.Leader.milkmaid);
 			break;
 		case "Farmer":
 			slave.muscles = jsRandom(41, 70);
@@ -160,19 +160,19 @@ globalThis.generateLeadershipSlave = function(input, location) {
 			slave.weight = jsRandom(0, 30);
 			slave.height = Math.round(Height.random(slave, {skew: 3, spread: .2, limitMult: [1, 4]}));
 			applyMaleGenitalia({dick: jsRandom(3, 5), balls: jsRandom(4, 9), prostate: either(1, 1, 1, 2)});
-			slave.career = either(App.Data.misc.farmerCareers);
+			slave.career = either(App.Data.Careers.Leader.farmer);
 			break;
 			// Entertain
 		case "DJ":
 			slave.skill.entertainment = 100;
 			slave.muscles = jsRandom(6, 30);
 			slave.face = 100;
-			slave.career = either(App.Data.misc.DJCareers);
+			slave.career = either(App.Data.Careers.Leader.DJ);
 			break;
 		case "Madam":
 			slave.skill.whoring = 100;
 			applyMaleGenitalia({dick: jsRandom(3, 5), balls: jsRandom(3, 5), prostate: either(1, 1, 1, 2)});
-			slave.career = either(App.Data.misc.madamCareers);
+			slave.career = either(App.Data.Careers.Leader.madam);
 			break;
 		case "Concubine":
 			slave.prestige = 3;
diff --git a/src/npc/generate/generateMarketSlave.js b/src/npc/generate/generateMarketSlave.js
index 785166e73b90f8c03901454c8117e336209d13a0..f27e294a7720258564a0a6b42da02bd2a7e9edad 100644
--- a/src/npc/generate/generateMarketSlave.js
+++ b/src/npc/generate/generateMarketSlave.js
@@ -462,7 +462,7 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1
 		case "neighbor": {
 			const neighborID = (typeof V.arcologies[numArcology] === 'object') ? numArcology : 1;
 			const neighbor = V.arcologies[neighborID];
-			const opinion = Math.clamp(Math.trunc(App.Neighbor.opinion(0, neighborID)/20), -10, 10);
+			const opinion = Math.clamp(Math.trunc(App.Neighbor.opinion(V.arcologies[0], neighbor)/20), -10, 10);
 
 			let genes = "";
 			if (neighbor.FSSubjugationist > 20) {
@@ -1375,7 +1375,7 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1
 			slave.origin = "You bought $him from the underage raiders' slave market.";
 			slave.trust -= 25;
 			setHealth(slave, jsRandom(-30, 70), Math.max(normalRandInt(10, 4), 0), Math.max(normalRandInt(0, 2), 0), Math.max(normalRandInt(0, 0.7), 0), jsRandom(30, 100));
-			slave.career = setup.veryYoungCareers.random();
+			slave.career = App.Data.Careers.General.veryYoung.random();
 			generateSalonModifications(slave);
 			slave.birthWeek = 0;
 			if (slave.vagina !== -1) {
diff --git a/src/npc/generate/generateNewSlaveJS.js b/src/npc/generate/generateNewSlaveJS.js
index f6c0b21286197af5aa7fa3d7a27d70a20e95f8b3..ebc7c1822f3b2f9cfd75d73cc7b4f7bf1ca24d62 100644
--- a/src/npc/generate/generateNewSlaveJS.js
+++ b/src/npc/generate/generateNewSlaveJS.js
@@ -1404,23 +1404,23 @@ globalThis.GenerateNewSlave = (function() {
 	function generateCareer() {
 		if (V.AgePenalty === 1) {
 			if (slave.actualAge < 16) {
-				slave.career = setup.veryYoungCareers.random();
+				slave.career = App.Data.Careers.General.veryYoung.random();
 			} else if (slave.actualAge <= 24) {
-				slave.career = setup.youngCareers.random();
+				slave.career = App.Data.Careers.General.young.random();
 			} else if (slave.intelligenceImplant >= 15) {
-				slave.career = setup.educatedCareers.random();
+				slave.career = App.Data.Careers.General.educated.random();
 			} else {
-				slave.career = setup.uneducatedCareers.random();
+				slave.career = App.Data.Careers.General.uneducated.random();
 			}
 		} else {
 			if (slave.actualAge < 16) {
-				slave.career = setup.veryYoungCareers.random();
+				slave.career = App.Data.Careers.General.veryYoung.random();
 			} else if (slave.intelligenceImplant >= 15) {
-				slave.career = setup.educatedCareers.random();
+				slave.career = App.Data.Careers.General.educated.random();
 			} else if (slave.actualAge <= 24) {
-				slave.career = setup.youngCareers.random();
+				slave.career = App.Data.Careers.General.young.random();
 			} else {
-				slave.career = setup.uneducatedCareers.random();
+				slave.career = App.Data.Careers.General.uneducated.random();
 			}
 		}
 	}
diff --git a/src/npc/generate/generateRelatedSlave.js b/src/npc/generate/generateRelatedSlave.js
index 96c0c5aa34c14c4a4a2b3dc43bf8545fb1402472..58464cbc6dfe07527c68518d07a5a8100c3bd5c8 100644
--- a/src/npc/generate/generateRelatedSlave.js
+++ b/src/npc/generate/generateRelatedSlave.js
@@ -5,7 +5,7 @@ globalThis.generateRelatedSlave = (function() {
 	 * Generate a very similar relative for an existing slave (for use in Household Liquidators, for example).
 	 * @param {App.Entity.SlaveState} slave - the source relative. Note: this slave is NOT changed, calling code is responsible for setting up the source end of the relationship!
 	 * @param {string} relationship - the relationship that the new relative has with the source. Currently supports "parent", "child", "older sibling", "younger sibling", "twin", and applicable gender-specific variants of those (i.e. mother/father, daughter/son, older/younger brother/sister).
-	 * @param {boolean} oppositeSex - set to true if the new relative should be the opposite sex of the old one (otherwise it will be the same sex).  will be ignored if gender is implied by relationship.
+	 * @param {boolean} oppositeSex - set to true if the new relative should be the opposite sex of the old one (otherwise it will be the same sex). will be ignored if gender is implied by relationship.
 	 * @returns {App.Entity.SlaveState} - new relative
 	 */
 	function generateRelative(slave, relationship, oppositeSex=false) {
diff --git a/src/npc/generate/lawCompliance.js b/src/npc/generate/lawCompliance.js
index 105b40a315607c27e324bf8384ea9922a17a4876..8dd108b574270746be41aaa6a7d9a88e6cb2238f 100644
--- a/src/npc/generate/lawCompliance.js
+++ b/src/npc/generate/lawCompliance.js
@@ -143,7 +143,7 @@ App.Desc.lawCompliance = function(slave, market = 0) {
 
 	r.push(slaveFears(fearList));
 
-	if (policies.countEugenicsSMRs() > 0) {
+	if (market !== "Elite Slave" && policies.countEugenicsSMRs() > 0) {
 		r.push(eugenicsSMRsCount());
 	}
 
@@ -305,7 +305,7 @@ App.Desc.lawCompliance = function(slave, market = 0) {
 
 	function gumjobFetishismSMR() {
 		slave.teeth = "removable";
-		if(slave.devotion <= 50) {
+		if (slave.devotion <= 50) {
 			slave.trust -= 10;
 		}
 		healthDamage(slave, 10);
diff --git a/src/npc/generate/newChildIntro.js b/src/npc/generate/newChildIntro.js
index 8b5739af97aaddf21abc9ef419186047c6ca8dea..b9b76aa3dd198d5f1b141b49075c72679d86bf9d 100644
--- a/src/npc/generate/newChildIntro.js
+++ b/src/npc/generate/newChildIntro.js
@@ -365,7 +365,7 @@ App.UI.newChildIntro = function(slave) {
 			slave.devotion += 20;
 		}
 		if (tempMom.boobs > 10000) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s ${tempMom.boobs}cc tits, taking in every `);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s ${tempMom.boobs}cc tits, taking in every `);
 			if (V.showInches === 2) {
 				r.push(`inch`);
 			} else {
@@ -380,22 +380,22 @@ App.UI.newChildIntro = function(slave) {
 			r.push(`${tempMom.nipples} nipples.`);
 			momInterest = "boobs";
 		} else if (tempMom.bellyPreg >= 450000) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s massively distended, child-filled belly, taking in every obvious motion beneath ${his2} taut skin.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s massively distended, child-filled belly, taking in every obvious motion beneath ${his2} taut skin.`);
 			if (slave.readyOva >= 20) {
 				r.push(`A hand runs across ${his} own bloated middle and another down to ${his} moist pussy.`);
 			}
 			momInterest = "belly";
 		} else if (tempMom.belly >= 5000) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s rounded middle, staring in fascination at the unfamiliar bulge.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s rounded middle, staring in fascination at the unfamiliar bulge.`);
 			if (slave.readyOva >= 20) {
 				r.push(`A hand runs across ${his} own middle and another down to ${his} moist pussy.`);
 			}
 			momInterest = "belly";
 		} else if (tempMom.dick > 15) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s ${dickToEitherUnit(tempMom.dick).replace("es", "")} long dick, trying to understand how it can fit in a girl.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s ${dickToEitherUnit(tempMom.dick).replace("es", "")} long dick, trying to understand how it can fit in a girl.`);
 			momInterest = "dick";
 		} else if (tempMom.balls > 60) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s ${ballsToEitherUnit(tempMom.balls).replace("es", "")} long testicles, taking in every`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s ${ballsToEitherUnit(tempMom.balls).replace("es", "")} long testicles, taking in every`);
 			if (V.showInches === 2) {
 				r.push(`inch`);
 			} else {
@@ -409,19 +409,19 @@ App.UI.newChildIntro = function(slave) {
 			}
 			momInterest = "balls";
 		} else if (tempMom.hips > 2) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s abnormally wide hips. ${He} runs ${his} hands down ${his} own sides, pondering how such a wonder occurs.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s abnormally wide hips. ${He} runs ${his} hands down ${his} own sides, pondering how such a wonder occurs.`);
 			momInterest = "hips";
 		} else if (tempMom.butt > 12) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s massive ass, staring in fascination at every jiggle that runs through it.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s massive ass, staring in fascination at every jiggle that runs through it.`);
 			momInterest = "butt";
 		} else if (tempMom.dick > 0 && tempMom.vagina > -1) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s crotch, staring in wonder and confusion at the presence of both a penis and a vagina.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s crotch, staring in wonder and confusion at the presence of both a penis and a vagina.`);
 		} else if (tempMom.dick === 0 && tempMom.vagina === -1 && tempMom.scrotum === 0) {
-			r.push(`${His} eyes focus on${tempMom.slaveName}'s crotch, staring in wonder and confusion at the lack of any sexual organs.`);
+			r.push(`${His} eyes focus on ${tempMom.slaveName}'s crotch, staring in wonder and confusion at the lack of any sexual organs.`);
 		} else if (isAmputee(tempMom)) {
-			r.push(`${His} eyes dart from limb to missing limb on${tempMom.slaveName}'s body, trying desperately to understand what happened to them.`);
+			r.push(`${His} eyes dart from limb to missing limb on ${tempMom.slaveName}'s body, trying desperately to understand what happened to them.`);
 		} else if (hasAnyProstheticLimbs(tempMom)) {
-			r.push(`${His} eyes dart from limb to prosthetic limb on${tempMom.slaveName}'s body, trying desperately to understand what these wonderful things are and how ${he} could get ${his} own.`);
+			r.push(`${His} eyes dart from limb to prosthetic limb on ${tempMom.slaveName}'s body, trying desperately to understand what these wonderful things are and how ${he} could get ${his} own.`);
 		}
 	}
 
@@ -506,7 +506,7 @@ App.UI.newChildIntro = function(slave) {
 			slave.devotion += 20;
 		}
 		if (tempDad.boobs > 10000) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s ${tempDad.boobs} cc tits, taking in every `);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s ${tempDad.boobs} cc tits, taking in every `);
 			if (V.showInches === 2) {
 				r.push(`inch`);
 			} else {
@@ -521,22 +521,22 @@ App.UI.newChildIntro = function(slave) {
 			r.push(`${tempDad.nipples} nipples.`);
 			dadInterest = "boobs";
 		} else if (tempDad.bellyPreg >= 450000) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s massively distended, child-filled belly, taking in every obvious motion beneath ${his2} taut skin.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s massively distended, child-filled belly, taking in every obvious motion beneath ${his2} taut skin.`);
 			if (slave.readyOva >= 20) {
 				r.push(`A hand runs across ${his} own bloated middle and another down to ${his} moist pussy.`);
 			}
 			dadInterest = "belly";
 		} else if (tempDad.belly >= 5000) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s rounded middle, staring in fascination at the unfamiliar bulge.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s rounded middle, staring in fascination at the unfamiliar bulge.`);
 			if (slave.readyOva >= 20) {
 				r.push(`A hand runs across ${his} own middle and another down to ${his} moist pussy.`);
 			}
 			dadInterest = "belly";
 		} else if (tempDad.dick > 15) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s ${dickToEitherUnit(tempDad.dick).replace("es", "")} long dick, trying to understand how it can fit in anybody.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s ${dickToEitherUnit(tempDad.dick).replace("es", "")} long dick, trying to understand how it can fit in anybody.`);
 			dadInterest = "dick";
 		} else if (tempDad.balls > 60) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s ${ballsToEitherUnit(tempDad.balls).replace("es", "")} long testicles, taking in every `);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s ${ballsToEitherUnit(tempDad.balls).replace("es", "")} long testicles, taking in every `);
 			if (V.showInches === 2) {
 				r.push(`inch`);
 			} else {
@@ -550,19 +550,19 @@ App.UI.newChildIntro = function(slave) {
 			}
 			dadInterest = "balls";
 		} else if (tempDad.hips > 2) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s abnormally wide hips. ${He} runs ${his} hands down ${his} own sides, pondering how such a wonder occurs.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s abnormally wide hips. ${He} runs ${his} hands down ${his} own sides, pondering how such a wonder occurs.`);
 			dadInterest = "hips";
 		} else if (tempDad.butt > 12) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s massive ass, staring in fascination at every jiggle that runs through it.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s massive ass, staring in fascination at every jiggle that runs through it.`);
 			dadInterest = "butt";
 		} else if (tempDad.dick > 0 && tempDad.vagina > -1) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s crotch, staring in wonder and confusion at the presence of both a penis and a vagina.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s crotch, staring in wonder and confusion at the presence of both a penis and a vagina.`);
 		} else if (tempDad.dick === 0 && tempDad.vagina === -1 && tempDad.scrotum === 0) {
-			r.push(`${His} eyes focus on${tempDad.slaveName}'s crotch, staring in wonder and confusion at the lack of any sexual organs.`);
+			r.push(`${His} eyes focus on ${tempDad.slaveName}'s crotch, staring in wonder and confusion at the lack of any sexual organs.`);
 		} else if (isAmputee(tempDad)) {
-			r.push(`${His} eyes dart from limb to missing limb on${tempDad.slaveName}'s body, trying desperately to understand what happened to them.`);
+			r.push(`${His} eyes dart from limb to missing limb on ${tempDad.slaveName}'s body, trying desperately to understand what happened to them.`);
 		} else if (hasAnyProstheticLimbs(tempDad)) {
-			r.push(`${His} eyes dart from limb to prosthetic limb on${tempDad.slaveName}'s body, trying desperately to understand what these wonderful things are and how ${he} could get ${his} own.`);
+			r.push(`${His} eyes dart from limb to prosthetic limb on ${tempDad.slaveName}'s body, trying desperately to understand what these wonderful things are and how ${he} could get ${his} own.`);
 		}
 	}
 
@@ -745,7 +745,7 @@ App.UI.newChildIntro = function(slave) {
 	}
 	if (V.arcologies[0].FSHedonisticDecadence >= 50) {
 		if (slave.weight > 50) {
-			r.push(`${He} noticed all fat slaves lazing about your penthouse; ${he} pats ${his} own soft belly, <span class="mediumaquamarine">feeling right at home.</span>`);
+			r.push(`${He} noticed all the fat slaves lazing about your penthouse; ${he} pats ${his} own soft belly, <span class="mediumaquamarine">feeling right at home.</span>`);
 			slave.trust += 2;
 		}
 	}
diff --git a/src/npc/generate/newSlaveIntro.js b/src/npc/generate/newSlaveIntro.js
index b30e47e753eae6a327a09ff866b5ca509949f187..235abc56aecde66acc2f3891b26b7719709073c3 100644
--- a/src/npc/generate/newSlaveIntro.js
+++ b/src/npc/generate/newSlaveIntro.js
@@ -891,7 +891,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						linkName: `Brand ${him} on the ${brandTarget} to introduce ${him} to life as a slave whore`,
 						result: function(slave) {
 							const r = [];
-							r.push(`You tell ${him} you'll be marking ${him} as one of your working ${girl}s. ${He} looks resigned as ${he} follows you to the body modification studio, and lets you strap ${him} down with ${his} ${brandTarget} bare. ${He} understands what's coming. You've got ${him} positioned just right${canDoAnal(slave) ? `, so your cock slides up ${his} experienced asshole easily` : ``}. You bring the brand in close so ${he} can feel the radiated heat, which breaks through even ${his} jaded exterior and makes ${him} tighten with fear. When you're close, you apply the brand${canDoAnal(slave) ? `, making the poor whore cinch ${his} sphincter down hard in agony, bringing you to climax` : ``}. ${He} knows you know how to <span class="gold">apply pain,</span> now, and ${he} <span class="mediumorchid">learns to dislike you</span> even as ${his} <span class="red">wound</span> heals.`);
+							r.push(`You tell ${him} you'll be marking ${him} as one of your working ${girl}s. ${He} looks resigned as ${he} follows you to the body modification studio, and lets you strap ${him} down with ${his} ${brandTarget} bare. ${He} understands what's coming. You've got ${him} positioned just right${canDoAnal(slave) ? `, so your cock slides up ${his} experienced asshole easily` : ``}. You bring the brand in close so ${he} can feel the radiated heat, which breaks through even ${his} jaded exterior and makes ${him} tighten with fear. When you're close, you apply the brand${canDoAnal(slave) ? `, making the poor whore cinch ${his} sphincter down hard in agony, bringing you to climax` : ``}. ${He} knows you know how to <span class="gold">apply pain,</span> now, and ${he} <span class="mediumorchid">learns to dislike you</span> even as ${his} <span class="health dec">wound</span> heals.`);
 							if (canDoAnal(slave)) {
 								r.push(VCheck.Anal(slave, 1));
 							}
@@ -906,7 +906,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						linkName: `Brand ${him} on the ${brandTarget} to make it clear that ${his} life of luxury and prestige is over`,
 						result: function(slave) {
 							const r = [];
-							r.push(`The former knight stands in front of you, glaring down as you casually announce that you'll be branding ${his} ${brandTarget} to mark ${him} as property. ${He}'s an absolutely enormous mass of corded muscle, all of it on naked display, and the furious glint in ${his} eyes makes it look like ${he}'s thinking about snapping you in two at any moment. As the captive man tries ${his} best to silently intimidate you, you laugh, once, and bring your face up close to ${his}, making it disturbingly clear that ${his} muscle and strength is no use anymore. Escorted by your drones, you bring the ex-Knight to the body modification studio, strap ${him} down, and take your time to heat up the brand before bringing it to ${his} virgin flesh with a sudden ferocity that makes the huge man scream in agony. As ${he} writhes and thrashes under the straps, ${he} gets his first taste of <span class="gold">genuine pain,</span> and ${he} <span class="mediumorchid">hates you</span> even more than ${he} did before as ${his} <span class="red">wound</span> heals.`);
+							r.push(`The former knight stands in front of you, glaring down as you casually announce that you'll be branding ${his} ${brandTarget} to mark ${him} as property. ${He}'s an absolutely enormous mass of corded muscle, all of it on naked display, and the furious glint in ${his} eyes makes it look like ${he}'s thinking about snapping you in two at any moment. As the captive man tries ${his} best to silently intimidate you, you laugh, once, and bring your face up close to ${his}, making it disturbingly clear that ${his} muscle and strength is no use anymore. Escorted by your drones, you bring the ex-Knight to the body modification studio, strap ${him} down, and take your time to heat up the brand before bringing it to ${his} virgin flesh with a sudden ferocity that makes the huge man scream in agony. As ${he} writhes and thrashes under the straps, ${he} gets his first taste of <span class="gold">genuine pain,</span> and ${he} <span class="mediumorchid">hates you</span> even more than ${he} did before as ${his} <span class="health dec">wound</span> heals.`);
 							if (canDoAnal(slave)) {
 								r.push(VCheck.Anal(slave, 1));
 							}
@@ -1024,7 +1024,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else {
 								r.push(`learns`);
 							}
-							r.push(`${he}'ll be heading to surgery immediately, ${he} bursts into <span class="hotpink">tears of gratitude</span> and makes to run around your desk to hug you before checking ${himself}. ${He} clearly doesn't want to put a foot wrong and isn't sure it would be appropriate. You solve ${his} dilemma by meeting ${him} with an embrace. ${He} <span class="mediumaquamarine">cries into your chest</span> and promises to be your best slave. The surgery does affect ${his} <span class="red">health</span> a little.`);
+							r.push(`${he}'ll be heading to surgery immediately, ${he} bursts into <span class="hotpink">tears of gratitude</span> and makes to run around your desk to hug you before checking ${himself}. ${He} clearly doesn't want to put a foot wrong and isn't sure it would be appropriate. You solve ${his} dilemma by meeting ${him} with an embrace. ${He} <span class="mediumaquamarine">cries into your chest</span> and promises to be your best slave. The surgery does affect ${his} <span class="health dec">health</span> a little.`);
 							slave.devotion += 15;
 							surgeryDamage(slave, 10);
 							slave.trust += 10;
@@ -1082,7 +1082,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else {
 								r.push(`wilting`);
 							}
-							r.push(`butthole. ${He} gingerly stumbles back to your office with you and, without even being ordered to, arranges ${himself} on the couch with ${his} fuckhole ready. The gelding does affect ${his} <span class="red">health</span> somewhat.`);
+							r.push(`butthole. ${He} gingerly stumbles back to your office with you and, without even being ordered to, arranges ${himself} on the couch with ${his} fuckhole ready. The gelding does affect ${his} <span class="health dec">health</span> somewhat.`);
 							r.push(VCheck.Anal(slave, 1));
 							slave.devotion -= 10;
 							slave.trust -= -10;
@@ -1741,7 +1741,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 									if (V.PC.dick === 0) {
 										r.push(`fake`);
 									}
-									r.push(`cock down ${his} throat as far as it will go. Over the course of the next several hours, you ensure that ${he} understands the fine points of nonconsensual oral${slave.vagina > -1 ? `, vaginal,` : ``} and anal intercourse as intimately as possible. When you're finally too tired to continue,you unshackle ${his} <span class="red">bruised and bloody body</span> and ask ${him} what ${he} learned. ${His} voice hoarse from the same brutal fucking that has gaped ${his} <span class="lime">asshole</span> ${(slave.vagina > -1) ? `and <span class="lime">pussy</span>` : ``}, ${he} hesitantly replies that ${he} has <span class="hotpink">learned a great deal about true dominance,</span> before fainting on the spot from a mixture of total exhaustion and pure terror. You've taught your student well.`);
+									r.push(`cock down ${his} throat as far as it will go. Over the course of the next several hours, you ensure that ${he} understands the fine points of nonconsensual oral${slave.vagina > -1 ? `, vaginal,` : ``} and anal intercourse as intimately as possible. When you're finally too tired to continue,you unshackle ${his} <span class="health dec">bruised and bloody body</span> and ask ${him} what ${he} learned. ${His} voice hoarse from the same brutal fucking that has gaped ${his} <span class="lime">asshole</span> ${(slave.vagina > -1) ? `and <span class="lime">pussy</span>` : ``}, ${he} hesitantly replies that ${he} has <span class="hotpink">learned a great deal about true dominance,</span> before fainting on the spot from a mixture of total exhaustion and pure terror. You've taught your student well.`);
 									actX(slave, "oral", 15);
 									slave.anus = 2;
 									actX(slave, "anal", 15);
@@ -1827,7 +1827,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						} else {
 							r.push(`Since ${he}'s in rough shape,`);
 						}
-						r.push(`you give ${him} a comprehensive medical exam with the help of the remote surgery. You apply care to <span class="green">address</span> some of the most outstanding concerns.`);
+						r.push(`you give ${him} a comprehensive medical exam with the help of the remote surgery. You apply care to <span class="health inc">address</span> some of the most outstanding concerns.`);
 						if (tankBorn) {
 							r.push(`After the checkup, ${he} happily <span class="mediumaquamarine">expresses ${his} thanks</span> for making ${him} feel better.`);
 						} else {
@@ -1919,11 +1919,11 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else {
 								r.push(`${he} sits back up, you tell ${him} calmly and bluntly what ${his} new tattoo says:`);
 								if (slave.stampTat === "advertisements") {
-									"Fuck My Ass.";
+									r.push(`"Fuck My Ass."`);
 								} else if (slave.stampTat === "rude words") {
-									"Rear Entrance.";
+									r.push(`"Rear Entrance."`);
 								} else if (slave.stampTat === "degradation") {
-									"Anal Whore.";
+									r.push(`"Anal Whore."`);
 								}
 							}
 						} else {
@@ -1965,7 +1965,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 					if (!canSee(slave)) {
 						r.push(`of course,`);
 					}
-					r.push(`but eventually ${he} feels the radiated heat on ${his} skin and manages to get one inarticulate, wordless noise of terror out before the dreadful sizzling noise and the sweet smell of burning flesh. If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it.`);
+					r.push(`but eventually ${he} feels the radiated heat on ${his} skin and manages to get one inarticulate, wordless noise of terror out before the dreadful sizzling noise and the sweet smell of burning flesh. If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="health dec">injury</span> to prove it.`);
 					if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
 						r.push(`Society <span class="green">approves</span> of your purchase and branding of an inferior ${slave.race} person; this advances the idea that ${slave.race} people ought to be enslaved.`);
 						FutureSocieties.Change("Subjugationist", 2);
@@ -2060,7 +2060,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						} else {
 							r.push(`twists and turns, but without limbs is powerless to escape the curling whip.`);
 						}
-						r.push(`If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it. What ${he} doesn't yet know is just how permanent this lashing's effects will be. The level of violence and the coating you used will leave ${him} scarred with the marks of slavery forever.`);
+						r.push(`If ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="health dec">injury</span> to prove it. What ${he} doesn't yet know is just how permanent this lashing's effects will be. The level of violence and the coating you used will leave ${him} scarred with the marks of slavery forever.`);
 						if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
 							r.push(`Society <span class="green">approves</span> of your purchase and whipping of an inferior ${slave.race} person; this advances the idea that ${slave.race} people ought to be enslaved.`);
 							FutureSocieties.Change("Subjugationist", 2);
@@ -2079,7 +2079,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 					linkName: `Scar ${him} on the ${scarTarget}`,
 					result: function(slave) {
 						const r = [];
-						r.push(`You drag ${him} to the body modification studio and strap ${him} down with ${his} ${scarTarget} clear and defenseless. ${He} doesn't understand what's coming for a while, even as disinfectant is applied to ${his} ${scarTarget}. You have a wide selection of tools to create scars, the trick is to keep the wound from healing correctly afterwards. Of course, ${he} has no way of knowing that the pain you are inflicting as you cut into ${his} flesh will leave such a permanent mark, but the basic message is clear: if ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="red">injury</span> to prove it.`);
+						r.push(`You drag ${him} to the body modification studio and strap ${him} down with ${his} ${scarTarget} clear and defenseless. ${He} doesn't understand what's coming for a while, even as disinfectant is applied to ${his} ${scarTarget}. You have a wide selection of tools to create scars, the trick is to keep the wound from healing correctly afterwards. Of course, ${he} has no way of knowing that the pain you are inflicting as you cut into ${his} flesh will leave such a permanent mark, but the basic message is clear: if ${he} didn't know ${he} was a slave before, <span class="mediumorchid">${he} does now,</span> and ${he}'s got the <span class="gold">agonizing</span> <span class="health dec">injury</span> to prove it.`);
 						if (V.arcologies[0].FSSubjugationistRace === slave.race && V.arcologies[0].FSSubjugationist > 0) {
 							r.push(`Society <span class="green">approves</span> of your purchase and scarring of an inferior ${slave.race} person; this advances the idea that ${slave.race} people ought to be enslaved.`);
 							FutureSocieties.Change("Subjugationist", 2);
@@ -2103,7 +2103,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 					result(slave) {
 						const r = [];
 						r.push(`${He} is much too thin and seems to shirk any offered food, but there is an easy solution to that. If ${he} refuses to eat, ${he} can be made to. Pulling the reluctant slave to the feeders, binding ${him} tightly to a chair, and attaching a hose to the slave food nozzle, you give ${him} a choice; suck the hose willingly or have it forced down ${his} throat. ${He} glares defiantly and keeps ${his} mouth firmly shut. You clamp down on ${his} nose, inevitably forcing ${him} to open ${his} mouth to breath. In that moment, you shove the hose in and down ${his} throat, carefully directing it down into ${his} stomach. ${He} gags as you turn on the flow, ${his} eyes filling with tears as ${he} feels the warm food travel down the tube and into ${his} stomach. ${He} sobs as ${his} belly steadily swells with unwelcome sustenance, ${his} eyes pleading with you, desperate to let you know ${he}'ll be good. You ignore ${him}, letting ${him} fill until ${his} belly is noticeably distended compared to ${his} thin frame. Once you feel ${he} has had enough, you pull the hose from ${his} gut, spraying ${his} face with food in the process, and tell ${him} it will go right back in if ${he} doesn't keep it all down. ${He} <span class="gold">nods fearfully,</span> anything to not go through that again. ${He} hobbles away once freed,`);
-						r.push(`one hand covering ${his} retching mouth and the other clasping ${his} <span class="mediumorchid">hated, food bloated middle.</span>`); //TODO: revise for hands
+						r.push(`one hand covering ${his} retching mouth and the other clasping ${his} <span class="mediumorchid">hated, food bloated middle.</span>`); // TODO: revise for hands
 						slave.devotion -= 10;
 						slave.trust -= 10;
 						return r.join(" ");
@@ -2128,11 +2128,11 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						} else {
 							r.push(`your`);
 						}
-						r.push(`view. It depicts the silhouette of a pregnant woman, with a red "X" over it. Abortifacients. After an`);
+						r.push(`view. It depicts the silhouette of a pregnant woman, with a red "X" over it. Abortifacients. After`);
 						if (!canSee(slave)) {
-							r.push(`short period`);
+							r.push(`a short period`);
 						} else {
-							r.push(`instant`);
+							r.push(`an instant`);
 						}
 						r.push(`of horrified comprehension, ${he} flings ${himself} at your feet, crying, begging, promising. ${He} pledges to <span class="hotpink">submit to you,</span>`);
 						if (V.PC.dick !== 0) {
@@ -2309,7 +2309,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else {
 								r.push(`since the anesthetics totally deprive ${him} of any sensation.`);
 							}
-							r.push(`${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened.`);
+							r.push(`${He}'s so drugged and drowsy with <span class="health dec">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened.`);
 							if (tankBorn) {
 								r.push(`${He} spends the rest of the week dimly trying to find where ${his} balls went.`);
 							} else {
@@ -2317,7 +2317,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							}
 							if (V.arcologies[0].FSGenderRadicalist !== "unset") {
 								r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that all societal inferiors can be made female.`);
-								FutureSocieties.Change("GenderRadicalist", 2);
+								FutureSocieties.Change("Gender Radicalist", 2);
 							}
 							if (V.arcologies[0].FSRestart !== "unset" && slave.pubertyXY === 1) {
 								r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that only the elite should breed.`);
@@ -2337,7 +2337,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 					linkName: `Remove ${his} genitalia`,
 					result(slave) {
 						const r = [];
-						r.push(`You drag ${him} to the remote surgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, since the anesthetics totally deprive ${him} of any sensation. ${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. Eventually, though, ${he} realizes that ${he}'s been reduced to the status of a genital null: the only remaining feature of ${his} newly smooth groin is a tiny soft hole, ${his} urethra.`);
+						r.push(`You drag ${him} to the remote surgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, since the anesthetics totally deprive ${him} of any sensation. ${He}'s so drugged and drowsy with <span class="health dec">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. Eventually, though, ${he} realizes that ${he}'s been reduced to the status of a genital null: the only remaining feature of ${his} newly smooth groin is a tiny soft hole, ${his} urethra.`);
 						if (slave.scrotum > 0) {
 							r.push(`${He} retains ${his} ballsack beneath this, though of course you can always remove that later if you decide to geld ${him}, too.`);
 						}
@@ -2375,11 +2375,11 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						}
 						r.push(`but eventually ${he} feels a tugging at ${his} lower legs even through the local anesthetic. ${He} gets one squeal of protest out before the surgery begins to apply healing agents. ${He} now requires special heels to walk, and will`);
 						if (tankBorn) {
-							r.push(`<span class="gold">remember your power</span> with every <span class="red">painful</span> step ${he} takes. ${He} seems <span class="hotpink">inappropriately happy</span> about getting to wear pretty shoes when ${he} can no longer walk without them.`);
+							r.push(`<span class="gold">remember your power</span> with every <span class="health dec">painful</span> step ${he} takes. ${He} seems <span class="hotpink">inappropriately happy</span> about getting to wear pretty shoes when ${he} can no longer walk without them.`);
 							slave.devotion += 5;
 							slave.trust -= 5;
 						} else {
-							r.push(`<span class="mediumorchid">remember ${his} status</span> with every <span class="red">painful</span> step ${he} takes. ${He}'s barefoot, crawling, and <span class="gold">frightened</span> for now, until you decide to give ${him} heels — if you ever do.`);
+							r.push(`<span class="mediumorchid">remember ${his} status</span> with every <span class="health dec">painful</span> step ${he} takes. ${He}'s barefoot, crawling, and <span class="gold">frightened</span> for now, until you decide to give ${him} heels — if you ever do.`);
 							slave.devotion -= 5;
 							slave.trust -= 20;
 						}
@@ -2403,7 +2403,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 						} else {
 							r.push(`have the ${desc} restrained and brought`);
 						}
-						r.push(`to the remote surgery. The procedure is quick and <span class="red">minimally invasive.</span>`);
+						r.push(`to the remote surgery. The procedure is quick and <span class="health dec">minimally invasive.</span>`);
 						surgeryDamage(slave, 10);
 						r.push(`Once the process is complete and the anesthesia subsides ${he} begins to feel a rising pressure within ${his}`);
 						if (slave.boobs > 2000) {
@@ -2721,10 +2721,10 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							V.failedElite += 5;
 						} else if (V.arcologies[0].FSGenderFundamentalist !== "unset" && slave.mpreg === 0) {
 							r.push(`Society <span class="green">approves</span> of your promptly putting a new slave in ${him}; this advances the idea that all slaves should bear their masters' babies.`);
-							FutureSocieties.Change("GenderFundamentalist", 2);
+							FutureSocieties.Change("Gender Fundamentalist", 2);
 						} else if (V.arcologies[0].FSGenderFundamentalist !== "unset") {
 							r.push(`Society <span class="red">is disgusted</span> by you promptly knocking up ${his} ass; babies come from women, not men.`);
-							FutureSocieties.Change("GenderFundamentalist", -2);
+							FutureSocieties.Change("Gender Fundamentalist", -2);
 						}
 						App.Events.addParagraph(el, r);
 
@@ -3279,7 +3279,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else if (slave.dick === 6) {
 								r.push(`functional huge dick.`);
 							} else {
-								r.push(`functional.`); // Old SC code only went to 6.  I'm adding this to fix the period, but this needs writing assuming we can go beyond 6.
+								r.push(`functional.`); // Old SC code only went to 6. I'm adding this to fix the period, but this needs writing assuming we can go beyond 6.
 							}
 							if (tankBorn) {
 								r.push(`You gently begin fondling ${his} penis, grinning at the look of confusion and lust growing on ${his} face.`);
@@ -3386,10 +3386,10 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 											`Geld ${him} as punishment`,
 											() => {
 												const r = [];
-												r.push(`Righting yourself, you grab the gloating slave, drag ${him} to the autosurgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, but giggles at the sensations running through ${his} numb body. ${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened. ${He} spends the rest of the week dimly trying to find where ${his} balls went.`);
+												r.push(`Righting yourself, you grab the gloating slave, drag ${him} to the autosurgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, but giggles at the sensations running through ${his} numb body. ${He}'s so drugged and drowsy with <span class="health dec">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened. ${He} spends the rest of the week dimly trying to find where ${his} balls went.`);
 												if (V.arcologies[0].FSGenderRadicalist !== "unset") {
 													r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that all societal inferiors can be made female.`);
-													FutureSocieties.Change("GenderRadicalist", 2);
+													FutureSocieties.Change("Gender Radicalist", 2);
 												}
 												if (V.arcologies[0].FSRestart !== "unset" && slave.pubertyXY === 1) {
 													r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that only the Elite should breed.`);
@@ -3751,10 +3751,10 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 													`Geld ${him} as punishment`,
 													() => {
 														const r = [];
-														r.push(`Righting yourself, you grab the cowering slave, drag ${him} to the autosurgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, but giggles at the sensations running through ${his} numb body. ${He}'s so drugged and drowsy with <span class="red">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened. ${He} spends the rest of the week dimly trying to find where ${his} balls went.`);
+														r.push(`Righting yourself, you grab the cowering slave, drag ${him} to the autosurgery and strap ${him} face-down with ${his} legs spread. ${He} doesn't understand what's happening, but giggles at the sensations running through ${his} numb body. ${He}'s so drugged and drowsy with <span class="health dec">surgical recovery</span> that it takes a while for ${him} to figure out what's happened. When ${he} does, ${his} poor mind scarcely processes the <span class="gold">horror</span> of what's happened. ${He} spends the rest of the week dimly trying to find where ${his} balls went.`);
 														if (V.arcologies[0].FSGenderRadicalist !== "unset") {
 															r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that all societal inferiors can be made female.`);
-															FutureSocieties.Change("GenderRadicalist", 2);
+															FutureSocieties.Change("Gender Radicalist", 2);
 														}
 														if (V.arcologies[0].FSRestart !== "unset") {
 															r.push(`Society <span class="green">approves</span> of your promptly gelding ${him}; this advances the idea that only the Elite should breed.`);
@@ -4665,7 +4665,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 
 				});
 
-				if (App.Entity.facilities.arcade.hasFreeSpace || V.arcadeUpgradeFuckdolls === 2) {
+				if (App.Entity.facilities.arcade.hasFreeSpace || V.arcadeUpgradeFuckdolls > 1) {
 					choice({
 						linkName: `Send ${him} straight to the Arcade`,
 						result(slave) {
@@ -4678,7 +4678,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								r.push(S.HeadGirl.slaveName);
 							}
 							r.push(`to get ${slave.slaveName} set up in ${V.arcadeName}. The new slave does not know what ${V.arcadeName} is, not really, and ${he} doesn't know what being set up there means, either. ${He}'ll be confined inside a small space, not too different from the indignities ${he}'s suffered already. It's only when the restraints lock into place that ${he}'ll understand ${his} doom. ${His} mouth will be forced open and presented at one wall of ${V.arcadeName}, and ${his} ass will protrude from its other side, ${his} holes available for public relief at both ends. ${He}'ll probably refuse to believe the truth, until the first cockhead enters ${his} mouth${(slave.vagina > -1) ? `, parts ${his} pussylips,` : ``} or presses against ${his} poor anus.`);
-							if (V.arcade <= App.Entity.facilities.arcade.employeesIDs().size) {
+							if (!App.Entity.facilities.arcade.hasFreeSpace) {
 								r.push(`Mere`);
 								if (V.showInches === 2) {
 									r.push(`yards`);
@@ -4808,7 +4808,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} new ridiculous fake tits. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of giant breasts hanging from ${his} stretched chest and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger, even though ${he} is already struggling to keep upright. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`${his} new ridiculous fake tits. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of giant breasts hanging from ${his} stretched chest and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger, even though ${he} is already struggling to keep upright. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -4841,7 +4841,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`discovers`);
 								}
-								r.push(`they'll keep growing, despite ${his} near inability to stay upright. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`they'll keep growing, despite ${his} near inability to stay upright. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -4869,7 +4869,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} new fake balloons. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of big breasts hanging from ${his} chest and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`${his} new fake balloons. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of big breasts hanging from ${his} chest and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -4903,7 +4903,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`discovers`);
 								}
-								r.push(`they'll keep growing. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`they'll keep growing. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -4979,7 +4979,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`discovers`);
 								}
-								r.push(`you can make it bigger, despite the fact that it is nearly as large as ${he} is and pins ${him} to the bed ${he} lies upon. As it was an invasive surgery, <span class="red">${his} health has been greatly affected.</span>`);
+								r.push(`you can make it bigger, despite the fact that it is nearly as large as ${he} is and pins ${him} to the bed ${he} lies upon. As it was an invasive surgery, <span class="health dec">${his} health has been greatly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -5011,7 +5011,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 							} else {
 								r.push(`discovers`);
 							}
-							r.push(`you can make it bigger. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+							r.push(`you can make it bigger. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 							jQuery("#introResult").empty().append(r.join(" "));
 						},
 						[],
@@ -5165,7 +5165,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} wide hips, especially since ${he} can't figure out how to roll over with them. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} is ridiculously wide and <span class="mediumaquamarine">wiggles ${his} door-jammers cheerfully</span> at you whenever ${he} gets the chance. Since the surgery was invasive, <span class="red">${his} health has been greatly affected.</span>`);
+								r.push(`${his} wide hips, especially since ${he} can't figure out how to roll over with them. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} is ridiculously wide and <span class="mediumaquamarine">wiggles ${his} door-jammers cheerfully</span> at you whenever ${he} gets the chance. Since the surgery was invasive, <span class="health dec">${his} health has been greatly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -5191,7 +5191,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} wide hips. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} is wider than ever and <span class="mediumaquamarine">wiggles ${his} hips cheerfully</span> at you whenever ${he} gets the chance. Since the surgery was invasive, <span class="red">${his} health has been greatly affected.</span>`);
+								r.push(`${his} wide hips. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} is wider than ever and <span class="mediumaquamarine">wiggles ${his} hips cheerfully</span> at you whenever ${he} gets the chance. Since the surgery was invasive, <span class="health dec">${his} health has been greatly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -5278,7 +5278,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} new ridiculous fake ass, not that ${he} has much choice, since it has ${him} pinned to the bed. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of giant butt cheeks ballooning from ${his} bottom and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger, even though ${he} is already struggling to escape from under them. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`${his} new ridiculous fake ass, not that ${he} has much choice, since it has ${him} pinned to the bed. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of giant butt cheeks ballooning from ${his} bottom and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger, even though ${he} is already struggling to escape from under them. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -5305,7 +5305,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`stop shaking around`);
 								}
-								r.push(`${his} new fake bottom. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of big butt cheeks hanging from ${his} rear and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`${his} new fake bottom. ${He}'s <span class="hotpink">deliriously happy</span> that ${he} has ${his} own pair of big butt cheeks hanging from ${his} rear and <span class="mediumaquamarine">hopes</span> you have plans to make them even bigger. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
@@ -5337,7 +5337,7 @@ App.UI.newSlaveIntro = function(slave, slave2, {tankBorn = false, momInterest =
 								} else {
 									r.push(`discovers`);
 								}
-								r.push(`they'll keep growing. As with all surgery <span class="red">${his} health has been slightly affected.</span>`);
+								r.push(`they'll keep growing. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
 								jQuery("#introResult").empty().append(r.join(" "));
 							},
 							[],
diff --git a/src/npc/infants/InfantState.js b/src/npc/infants/InfantState.js
index bad32d731988231611f9575cd12549d622658b95..86f2b4d8e24a22954cfc982e095dd883562c30ea 100644
--- a/src/npc/infants/InfantState.js
+++ b/src/npc/infants/InfantState.js
@@ -39,7 +39,7 @@ App.Facilities.Nursery.InfantState = class InfantState {
 		this.markings = "none";
 		/**
 		 * The infant's eyes
-		 * @type App.Entity.EyeState
+		 * @type {App.Entity.EyeState}
 		 */
 		this.eye = new App.Entity.EyeState();
 		/** hair color */
diff --git a/src/npc/interaction/fAbuse.tw b/src/npc/interaction/fAbuse.tw
index 502df8cbda3c03f658c4dcc25ec6ec8bb271e66b..bc3ca37031738e907fb72e9831c5b55700323280 100644
--- a/src/npc/interaction/fAbuse.tw
+++ b/src/npc/interaction/fAbuse.tw
@@ -439,7 +439,7 @@ from your victim.
 <</if>>
 
 <<if getSlave($AS).bellyPreg >= 600000>>
-	The rough fucking was @@.red;very unhealthy@@ for $his massive pregnancy.
+	The rough fucking was @@.health.dec;very unhealthy@@ for $his massive pregnancy.
 	<<run healthDamage(getSlave($AS), 40)>>
 <</if>>
 
diff --git a/src/npc/interaction/fBeg.tw b/src/npc/interaction/fBeg.tw
index 6ffe453d8cfffa24ccc510693973cb3056284b9a..67a2bb7eb50e4dd41922a5692c2afbf059978eb8 100644
--- a/src/npc/interaction/fBeg.tw
+++ b/src/npc/interaction/fBeg.tw
@@ -271,7 +271,7 @@ Now kneeling at your feet naked before you, your slave waits for $his <<= getWri
 			<</if>>
 			for $his <<= getWrittenTitle(getSlave($AS))>>.
 			<<if (getSlave($AS).lactation > 0)>>
-				milk dribbles down the soft curves of $his chest as a further sign of $his arousal.
+				Milk dribbles down the soft curves of $his chest as a further sign of $his arousal.
 			<</if>>
 			This is, of course, how all slaves are supposed to kneel, but $he takes the pose with added dedication.
 		<<default>>
diff --git a/src/npc/interaction/fDance.tw b/src/npc/interaction/fDance.tw
index 5f9d8d0dd11ba50e947fb6d33523b29121172608..b15cbb66520dd67de19c794fbef3af5e28b0bada 100644
--- a/src/npc/interaction/fDance.tw
+++ b/src/npc/interaction/fDance.tw
@@ -191,7 +191,7 @@ $His face is
 				the ring attaching $his bell to $his collar, and
 			<<case "bowtie">>
 				$his bowtie collar
-			<<case "bowtie">>
+			<<case "neck tie">>
 				$his neck tie
 			<<case "ancient Egyptian">>
 				<<if getSlave($AS).nosePiercing == 2>>
diff --git a/src/npc/interaction/fFeelings.tw b/src/npc/interaction/fFeelings.tw
index a15861b31270ab09c5b395db55b96ca5b50c837f..dafa73b59919e7683d3d7612d05beff52e6b412b 100644
--- a/src/npc/interaction/fFeelings.tw
+++ b/src/npc/interaction/fFeelings.tw
@@ -163,7 +163,7 @@ My favorite part of my body i<<s>>
 		<<if getSlave($AS).lips > 40>>
 			my <<if getSlave($AS).lips > 70>>huge <</if>>lip<<s>>, I like how everyone e<<x>>pect<<s>> to fa<<c>>efuck me, and how my lip<<s>> wrap around their dick<<s>> to keep all that <<if canTaste(getSlave($AS))>>yummy<<else>>warm<</if>> cum in my belly. Oh! I like my belly, too, and that warm, <<s>>lo<<sh>>y feeling a<<s>> it'<<s>> packed full of baby jui<<c>>e. It'<<s>> <<s>>o — I'm <<s>>orry, <<Master>>. I think my mouth i<<s>> watering. Plea<<s>>e give me a moment to collect my<<s>>elf.
 		<<elseif $PC.dick != 0>>
-			my tummy — and my womb! The <<s>>lo<<sh>>y feeling when I'm all packed full of cum in both end<<s>> get<<s>> me <<s>>o incredibly horny. <<S>>ometime<<s>> I wonder what it would be like if I were ju<<s>>t a puffed up, cum-balloon of a $woman, helple<<ss>> and filled with cum, over, and over, and — I'm <<s>>orry, <<Master>>. I'm being weird again, aren't I?
+			my tummy<<if (getSlave($AS).vagina > -1)>> — and my womb<</if>>! The <<s>>lo<<sh>>y feeling when I'm all packed full of cum in both end<<s>> get<<s>> me <<s>>o incredibly horny. <<S>>ometime<<s>> I wonder what it would be like if I were ju<<s>>t a puffed up cum-balloon of a $woman, helple<<ss>> and filled with cum, over, and over, and — I'm <<s>>orry, <<Master>>. I'm being weird again, aren't I?
 		<<else>>
 			my mouth, I love how it feel<<s>> to — to eat pu<<ss>>y, <<Master>>. I love eating out your pu<<ss>>y. E<<s>>pe<<c>>ially when it'<<s>> been filled up with <<s>>ome <<if canTaste(getSlave($AS))>>yummy<<else>>warm<</if>> cum. Maybe you could let me eat cum out of your pu<<ss>>y <<s>>oon?
 		<</if>>
diff --git a/src/npc/interaction/fPat.tw b/src/npc/interaction/fPat.tw
index f32a5d02a077f9892587cb148e6408edcd521580..579b5865c98a4a8071020c3de3614cbcbcc0886e 100644
--- a/src/npc/interaction/fPat.tw
+++ b/src/npc/interaction/fPat.tw
@@ -137,7 +137,7 @@ You walk around $him, drawing closer and slowly resting your hand on $his head.
 <<elseif getSlave($AS).devotion > 20>>
 	$He willingly accepts it. As you tenderly and lovingly brush your fingers along $his head, enjoying the feeling of your slave's roiling emotions, and finally submission. You can still sense considerable turmoil in the <<if (getSlave($AS).physicalAge > 30)>>$woman<<else>>$girl<</if>>; $he's doing $his duty as a slave by complying with your wishes, and is probably struggling with the mixture of resistance, obedience and perhaps even devotion forced to the forefront of $his mind by your touch. As you continue your ministrations, your slave slowly, and with more then a bit of uncertainty, finally starts to relax. $He looks at you with confusion and trepidation. Your eyes betray nothing however — you simply smile and press $his head into your <<if $PC.boobs >= 300>>soft breasts<<else>>masculine chest<</if>>, all the while enjoying the feeling of your <<if getSlave($AS).hStyle == "shaven bald" || getSlave($AS).bald == 1>>hands gliding over $his smooth head<<else>>fingers gliding through $his hair<</if>>.
 <<elseif (getSlave($AS).devotion < -20) && (getSlave($AS).trust >= 20)>>
-	$He doesn't react to your touch, remaining as still as statue, the same defiant expression on $his face. $He still doesn't react when you brush your fingers down the side of $his head or when you <<if getSlave($AS).hStyle == "shaven bald" || getSlave($AS).bald == 1>>stroke $his smooth head<<else>>run your fingers through $his hair<</if>>. $He does react when you gently rest your thumb on $his lips, however, by suddenly giving you a fierce bite. Cursing, you pull your hand back and examine it — $he didn't bite deep enough to draw blood, but the area is now red and puffy. Furious, you backhand $him across the face<<if random(1,100) > 80>><<set getSlave($AS).minorInjury = either("black eye", "bruise", "split lip")>>, giving $him a @@.red;<<= getSlave($AS).minorInjury>>@@<</if>>.
+	$He doesn't react to your touch, remaining as still as statue, the same defiant expression on $his face. $He still doesn't react when you brush your fingers down the side of $his head or when you <<if getSlave($AS).hStyle == "shaven bald" || getSlave($AS).bald == 1>>stroke $his smooth head<<else>>run your fingers through $his hair<</if>>. $He does react when you gently rest your thumb on $his lips, however, by suddenly giving you a fierce bite. Cursing, you pull your hand back and examine it — $he didn't bite deep enough to draw blood, but the area is now red and puffy. Furious, you backhand $him across the face<<if random(1,100) > 80>><<set getSlave($AS).minorInjury = either("black eye", "bruise", "split lip")>>, giving $him a @@.health.dec;<<= getSlave($AS).minorInjury>>@@<</if>>.
 <<elseif getSlave($AS).devotion >= -20 && getSlave($AS).trust < -20>>
 	$He shakes at your touch fearfully. As you tenderly brush your fingers down $his unresisting head, you appreciate this expression of your slave's subservience, $his eagerness to avoid punishment making $him stiffen, $his nervousness easily apparent. You continue stroking $him, enjoying $his fear, as the physical intimacy slowly does its work. $He starts to relax, $his resistance easing as $his eyes start to close. Your hands continue to gently scratch at $his scalp, and you enjoy the sensation as well as the feeling of power over your hapless slave. Gently, slowly, so not as to spook $him, you ease your property's head back into your <<if $PC.boobs >= 300>>breasts<<else>>chest<</if>>. Nevertheless your slave starts at the action, but at your insistence finally gives in to the motion, and finally relaxes against you.
 <<elseif getSlave($AS).trust < -50>>
diff --git a/src/npc/interaction/fRival.tw b/src/npc/interaction/fRival.tw
index 22090c6aac697ed83a4820a442e82fbd70d0b7ae..978c1e4a9f60cb4ba2d56faa74cd77d2999c2d53 100644
--- a/src/npc/interaction/fRival.tw
+++ b/src/npc/interaction/fRival.tw
@@ -68,7 +68,7 @@ You call <<= getSlave($AS).slaveName>> to your office and let $him know you'll b
 	<<else>>
 		suck your dick.
 	<</if>>
-	_He2 does, and once _he2's working away, you tell <<= getSlave($AS).slaveName>> to use the bitch. <<= getSlave(_partnerID).slaveName>> begins to lift _his2 head as though to protest, so you shove _him2 back down onto your 
+	_He2 does, and once _he2's working away, you tell <<= getSlave($AS).slaveName>> to use the bitch. <<= getSlave(_partnerID).slaveName>> begins to lift _his2 head as though to protest, so you shove _him2 back down onto your
 	<<if ($PC.dick == 0)>>
 		pussy,
 	<<else>>
diff --git a/src/npc/interaction/fSlaveFeed.tw b/src/npc/interaction/fSlaveFeed.tw
deleted file mode 100644
index 296eaebd404d9418e7c7cf036699fc73240d8908..0000000000000000000000000000000000000000
--- a/src/npc/interaction/fSlaveFeed.tw
+++ /dev/null
@@ -1,849 +0,0 @@
-:: FSlaveFeed [nobr no-history]
-
-<<set _pregDiscovery = 0>>
-<<run App.Utils.setLocalPronouns(getSlave($AS))>>
-<<setLocalPronouns $milkTap 2>>
-
-<span id="art-frame">
-/* 000-250-006 */
-<<if $seeImages == 1>>
-	<div class="imageColumn">
-		<div class="imageRef medImg">
-			<<= SlaveArt(getSlave($AS), 2, 0)>>
-		</div>
-		<div class="imageRef medImg">
-			<<= SlaveArt($milkTap, 2, 0)>>
-		</div>
-	</div>
-<</if>>
-/* 000-250-006 */
-</span>
-
-<<if getSlave($AS).inflationType == "milk">>
-<<set getSlave($AS).milkSource = $milkTap.ID>>
-<<if $milkTap.behavioralQuirk == "sinful" || $milkTap.sexualQuirk == "perverted" || $milkTap.fetish == "incest">> /* incest is a planned fetish, don't touch it! */
-	<<set _incestGive = 1>>
-<</if>>
-<<if getSlave($AS).behavioralQuirk == "sinful" || getSlave($AS).sexualQuirk == "perverted" || getSlave($AS).fetish == "incest">>
-	<<set _incestTake = 1>>
-<</if>>
-
-The first necessary step is to prepare the milk cow and _his2 udders.
-
-<<if $milkTap.fuckdoll > 0>>
-	This is hilariously easy, as you have complete control over how $milkTap.slaveName is posed.
-
-<<elseif $milkTap.fetish == "mindbroken">>
-	This is very easy, as $milkTap.slaveName blankly follows your every will. Combined with _his2 instinct to relieve the pressure in _his2 breasts, _he2 is simple to position.
-
-<<elseif $milkTap.rivalryTarget == getSlave($AS).ID>>
-	This is rather easy, as $milkTap.slaveName wants to <<if canSee($milkTap)>>see<<else>>feel<</if>> <<= getSlave($AS).slaveName>>'s belly swell painfully as $he is force-fed _his2 milk. <<if $milkTap.lactation > 1>>$he is practically gushing milk with excitement<<else>>It takes minimal effort to get _his2 milk flowing<</if>>.
-
-<<elseif $milkTap.relationshipTarget == getSlave($AS).ID>>
-	This is rather easy, as $milkTap.slaveName
-	<<if $milkTap.relationship == 1>>
-		wants _his2 friend to try _his2 milk, fresh from the source.
-	<<elseif $milkTap.relationship == 2>>
-		wants _his2 best friend to try _his2 milk, fresh from the source.
-	<<elseif $milkTap.relationship == 3>>
-		can't wait to have some fun with _his2 friend with benefits.
-	<<elseif $milkTap.relationship == 4>>
-		enjoys spending intimate time with _his2 lover, and having _his2 breasts suckled is one of _his2 favorites.
-	<<elseif $milkTap.relationship == 5>>
-		enjoys spending intimate time with _his2 $wife, and having _his2 breasts suckled is one of _his2 favorites.
-	<</if>> <<if $milkTap.lactation > 1>>_He2 is practically gushing milk with excitement<<else>>It takes minimal effort to get _his2 milk flowing<</if>>.
-
-<<elseif ($milkTap.fetish == "boobs") && ($milkTap.fetishKnown == 1) && ($milkTap.fetishStrength > 60) && ($milkTap.devotion >= -20)>>
-	This is very easy, since $milkTap.slaveName loves _his2 tits played with and can't wait to get suckled. <<if $milkTap.lactation > 1>>_he2 is practically gushing milk with excitement<<else>>It takes next to no effort to get _his2 milk flowing<</if>>.
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName fondly remembers nursing _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-	<<else>>
-		This is fairly easy, as $milkTap.slaveName's body remembers nursing _his2 <<print relativeTerm($milkTap, getSlave($AS))>>, even if $he is resistant to the idea.
-	<</if>>
-	<<if $milkTap.lactation > 1>>
-		_He2 is practically gushing milk with nostalgia.
-	<<else>>
-		It takes minimal effort to get _his2 milk flowing.
-	<</if>>
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName is aroused at the thought of breast feeding the $girl _he2 sired.
-		<<if $milkTap.lactation > 1>>
-			_He2 is practically gushing milk with excitement.
-		<<else>>
-			It takes minimal effort to get _his2 milk flowing.
-		<</if>>
-	<<else>>
-		This is extremely tough, as $milkTap.slaveName is very uncomfortable breast feeding the $girl _he2 sired.
-		<<if $milkTap.lactation > 1>>
-			_His2 excessive milk production quickly leaves _him2 eager for release, however.
-		<<else>>
-			It takes some coaxing and kneading to get _his2 milk flowing and _him2 eager for relief.
-		<</if>>
-	<</if>>
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName is aroused at the lewdity of breast feeding _his2 own mother.
-		<<if $milkTap.lactation > 1>>
-			_He2 is practically gushing milk with excitement.
-		<<else>>
-			It takes minimal effort to get _his2 milk flowing.
-		<</if>>
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName finds it awkward to nurse _his2 own mother.
-		<<if $milkTap.lactation > 1>>
-			_His2 excessive milk production quickly leaves _him2 eager for release.
-		<<else>>
-			It takes some coaxing and kneading to get _his2 milk flowing and _him2 eager for relief.
-		<</if>>
-	<</if>>
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	<<if _incestGive>>
-		This is easy enough, as having _his2 father drink _his2 milk is a taboo $milkTap.slaveName can't wait to break.
-		<<if $milkTap.lactation > 1>>
-			_He2 is practically gushing milk with excitement.
-		<<else>>
-			It takes minimal effort to get _his2 milk flowing.
-		<</if>>
-	<<else>>
-		This is very tough, as $milkTap.slaveName finds it weird to let _his2 father suckle from _him2.
-		<<if $milkTap.lactation > 1>>
-			_His2 excessive milk production quickly leaves _him2 eager for release, however.
-		<<else>>
-			It takes some coaxing and kneading to get _his2 milk flowing and _him2 eager for relief.
-		<</if>>
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	<<if _incestGive>>
-		This is easy enough, as having $milkTap.slaveName enjoys sexually experimenting with _his2 <<print relativeTerm(getSlave($AS), $milkTap)>>.
-	<<else>>
-		This is easy enough, as $milkTap.slaveName wants _his2 <<print relativeTerm(getSlave($AS), $milkTap)>> to try _his2 milk, but only if $he can taste $hers too.
-	<</if>>
-	<<if $milkTap.lactation > 1>>
-		_He2 is practically gushing milk with excitement.
-	<<else>>
-		It takes minimal effort to get _his2 milk flowing.
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	<<if _incestGive>>
-		This is easy enough, as having $milkTap.slaveName enjoys sexually experimenting with _his2 <<print relativeTerm(getSlave($AS), $milkTap)>>.
-		<<if $milkTap.lactation > 1>>
-			_He2 is practically gushing milk with excitement.
-		<<else>>
-			It takes minimal effort to get _his2 milk flowing.
-		<</if>>
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName is uncomfortable getting so intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-		<<if $milkTap.lactation > 1>>
-			_His2 excessive milk production quickly leaves _him2 eager for release.
-		<<else>>
-			It takes some coaxing and kneading to get _his2 milk flowing and _him2 eager for relief.
-		<</if>>
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	<<if _incestGive>>
-		This is easy enough, as having $milkTap.slaveName enjoys sexually experimenting with _his2 <<print relativeTerm(getSlave($AS), $milkTap)>>.
-		<<if $milkTap.lactation > 1>>
-			_He2 is practically gushing milk with excitement.
-		<<else>>
-			It takes minimal effort to get _his2 milk flowing.
-		<</if>>
-	<<else>>
-		This is slightly difficult, as $milkTap.slaveName is uncomfortable getting so intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-		<<if $milkTap.lactation > 1>>
-			_His2 excessive milk production quickly leaves _him2 eager for release.
-		<<else>>
-			It takes some coaxing and kneading to get _his2 milk flowing and _him2 eager for relief.
-		<</if>>
-	<</if>>
-
-<<elseif ($milkTap.lactation > 1) && ($milkTap.devotion >= -20)>>
-	Since $milkTap.slaveName produces so much milk, _he2 eagerly accepts any source of relief _he2 can manage.
-
-<<elseif ($milkTap.devotion > 50)>>
-	Since $milkTap.slaveName is devoted to you, _he2'll allow anyone you want to drink deeply from _him2.
-
-<<elseif ($milkTap.devotion > 20)>>
-	Since $milkTap.slaveName is obedient, _he2 appreciates any relief from _his2 swollen breasts.
-
-<<elseif ($milkTap.devotion >= -20)>>
-	Since $milkTap.slaveName does not resist your will, _he2 should comply reasonably well. If anything, $he'll at least be thankful to be relieved of some pressure.
-
-<<else>>
-	Since $milkTap.slaveName is unlikely to comply willingly, you simply restrain _him2 with _his2 tits exposed and ready to be drank from. <<if $milkTap.lactation > 1>>You affix nipple clamps to _his2 $milkTap.nipples nipples and step back to watch _his2 breasts back up with milk. When _he2 is unclamped, the flow should certainly be strong enough for your desires<<else>>You make sure to roughly coax _his2 milk into flowing, all the while reminding _him2 that _he2 is nothing more than a cow now<</if>>.
-
-<</if>>
-
-<br><br>
-
-Next, you see to <<= getSlave($AS).slaveName>>.
-
-<<if (isAmputee(getSlave($AS)))>>
-	You move the limbless $girl to $milkTap.slaveName's nipple and strap $him to it to prevent it from falling out of $his mouth.
-
-<<elseif tooBigBreasts(getSlave($AS))>>
-	You set $him up so that $his massive breasts pin $him on $milkTap.slaveName's milky nipple.
-
-<<elseif $milkTap.fuckdoll > 0>>
-	$He hesitantly brings $his mouth to its milky nipple, uncertain about suckling from a living doll.
-
-<<elseif getSlave($AS).rivalryTarget == $milkTap.ID>>
-	Knowing $his relationship with $milkTap.slaveName, you feel it best to restrain $him and anchor $him to $milkTap.slaveName's milky nipple so $he has no choice but to drink until you release $him.
-
-<<elseif getSlave($AS).relationshipTarget == $milkTap.ID>>
-	This is rather easy, as <<= getSlave($AS).slaveName>>
-	<<if getSlave($AS).relationship == 1>>
-		licks $his lips as $he approaches $his friend's breasts.
-	<<elseif getSlave($AS).relationship == 2>>
-		eagerly licks $his lips as $he approaches $his best friend's breasts.
-	<<elseif getSlave($AS).relationship == 3>>
-		licks $his lips and smiles as $he approaches $his friend with benefits' breasts, knowing well how _his2 <<if canTaste(getSlave($AS))>>milk tastes<<else>>body feels<</if>>.
-	<<elseif getSlave($AS).relationship == 4>>
-		licks $his lips and smiles as $he approaches $his lover's breasts. This won't be the first time $he's suckled from _him2 like this.
-	<<elseif getSlave($AS).relationship == 5>>
-		licks $his lips and smiles as $he approaches $his _wife2's breasts. This won't be the first time $he's suckled from _him2 like this.
-	<</if>>
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	$He draws close to $his mother's nipples, trying to remember if $he once had a favorite.
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	<<if _incestTake>>
-		$He eagerly wraps $his lips around $his father's nipple.<<if canAchieveErection($milkTap)>> $He shudders with budding lust when $he feels the dick that sired $him poking at $his belly.<</if>>
-	<<else>>
-		$He hesitatingly lowers $himself to $his father's nipple.<<if canAchieveErection($milkTap)>> $He nearly backs away when $he feels the dick that sired $him poking at $his belly.<</if>>
-	<</if>>
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	$He <<if _incestTake>>happily<<else>>awkwardly<</if>> brings $his lips to $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple.
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	$He <<if _incestTake>>eagerly<<else>>awkwardly<</if>> brings $his lips to $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple<<if canAchieveErection(getSlave($AS))>>, $his cock steadily hardening at the perversion of suckling from $his child<</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	$He readily gets in position to <<if canTaste(getSlave($AS))>>taste<<else>>suckle from<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>><<if getSlave($AS).lactation > 0>> while coaxing $his own milk to flow<</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>hesitatingly lowers $himself to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple.
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>hesitatingly lowers $himself to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple.
-
-<<elseif (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).devotion >= -20)>>
-	$He can't wait to <<if hasBothArms(getSlave($AS))>>wrap $his hands around<<else>>get between<</if>> $milkTap.slaveName's massive milky breasts and eagerly approaches $his nipples to suckle.
-
-<<elseif (getSlave($AS).fetish == "submissive") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-	$He is accustomed to submitting to you, but as a natural submissive $he doesn't have much trouble submitting to $milkTap.slaveName's mothering instead.
-
-<<elseif getSlave($AS).devotion < -20>>
-	$He tries to refuse, so you strap $him to $milkTap.slaveName's breast, milky $milkTap.nipples nipple wedged in $his mouth.
-
-<<elseif getSlave($AS).devotion <= 20>>
-	$He obeys your orders reluctantly, drawing near $milkTap.slaveName's breasts despite $his obvious hesitation to be filled with milk.
-
-<<elseif getSlave($AS).devotion <= 50>>
-	$He obeys your orders, drawing near $milkTap.slaveName's breasts despite $his slight hesitation at the idea of being filled with milk.
-
-<<else>>
-	$He happily obeys your orders, eagerly wrapping $his lips around $milkTap.slaveName's milky $milkTap.nipples nipple and suckling enthusiastically.
-<</if>>
-
-<br><br>
-
-<<if getSlave($AS).preg > getSlave($AS).pregData.normalBirth/13.33 && getSlave($AS).pregKnown == 0 && getSlave($AS).inflation > 1>>
-	It becomes abundantly clear that something is wrong with <<= getSlave($AS).slaveName>> as $he struggles to down $his milky meal. Before $his health can be affected further, you pull $him into a medical exam. While most of the tests come back normal, one in particular catches your eye; @@.lime;$he is pregnant<<if getSlave($AS).preg > getSlave($AS).pregData.normalBirth/4>> and surprisingly far along<</if>>.@@ $he should be able to still handle at least two liters of milk, however.
-	<<run deflate(getSlave($AS))>>
-	<<set getSlave($AS).pregKnown = 1, _pregDiscovery = 1>>
-
-<<elseif $milkTap.fuckdoll > 0>>
-	Slight moaning emanates from the Fuckdoll as <<= getSlave($AS).slaveName>> drinks from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off _his2 nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off _his2 nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off _his2 nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-
-<<elseif $milkTap.rivalryTarget == getSlave($AS).ID>>
-	$milkTap.slaveName grins as _his2 rival is forced to drink until $his belly is <<if getSlave($AS).inflation == 3>>nearly bursting with milk. <<= getSlave($AS).slaveName>> struggles against $his bindings until the pressure building in $him overwhelms $him, causing $him to pass out directly into $milkTap.slaveName's cushiony breasts. You quickly remove $him from the nipple before $he drowns<<elseif getSlave($AS).inflation == 2>>is rounded, jiggling and sloshing with milk. You release $his bindings, allowing $him to flop to the floor. <<if hasAnyArms(getSlave($AS))>>$He gingerly crawls away from $milkTap.slaveName, <<if hasBothArms(getSlave($AS))>>one<<else>>$his<</if>> hand cradling $his overfull stomach<<else>>$He rolls onto $his side, groaning with discomfort<</if>><<elseif getSlave($AS).inflation == 1>>bloated with milk. You release $his bindings, allowing $him to flop to the floor. <<if hasAnyArms(getSlave($AS))>>$He gingerly sits up and begins massaging $his full stomach<<else>>$He rolls onto $his back, hiccupping pathetically<</if>><</if>>.
-
-<<elseif $milkTap.relationshipTarget == getSlave($AS).ID>>
-	<<if $milkTap.relationship == 1>>
-		$milkTap.slaveName sighs contently as _his2 friend drinks deeply from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 2>>
-		$milkTap.slaveName sighs contently as _his2 best friend drinks deeply from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his best friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his best friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his best friend's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 3>>
-		$milkTap.slaveName moans lewdly as _his2 friend with benefits drinks deeply from _his2 breasts, savoring it despite commonly nursing <<= getSlave($AS).slaveName>> during their lovemaking. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his friend with benefits' nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his friend with benefits' nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his friend with benefits' nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 4>>
-		$milkTap.slaveName moans lewdly as _his2 lover drinks deeply from _his2 breasts, savoring it despite commonly nursing <<= getSlave($AS).slaveName>> during their lovemaking. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his lover's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his lover's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his lover's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 5>>
-		$milkTap.slaveName moans lewdly as _his2 $wife drinks deeply from _his2 breasts, savoring it despite commonly nursing <<= getSlave($AS).slaveName>> during their lovemaking. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his _wife2's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his _wife2's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his _wife2's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<</if>>
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	$milkTap.slaveName sighs contently as _his2 little $girl once again suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his mother's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his mother's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his mother's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> suckles from _his2 breasts<<if canAchieveErection($milkTap)>>, _his2 dick throbbing with lust<</if>>. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his father's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his father's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his father's nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>><<if canAchieveErection($milkTap)>><<if _incestTake>>. The way $he is wiggling $his hips suggests $he isn't finished with $his daddy just yet, and $his father's moaning confirms $he is teasing _him2 with $his rear. $He giggles as the horny cow unloads on $his backside<<else>>. $He doesn't stay put for long, as a strong moan and a blast of cum across $his rear from the horny cow startles $him from $his rest<</if>><</if>>.
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	$milkTap.slaveName moans lewdly as _he2 enjoys some role reversal as _his2 mother suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	$milkTap.slaveName moans lewdly as _his2 father suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	$milkTap.slaveName sighs contently as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	$milkTap.slaveName moans lewdly as _his2 <<if $milkTap.actualAge >= getSlave($AS).actualAge>>little<<else>>big<</if>> <<print relativeTerm($milkTap, getSlave($AS))>> suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> suckles from _his2 breasts. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with milk. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s nipple and settles into _his2 breasts for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-
-<<elseif (getSlave($AS).devotion < -20) && ($milkTap.devotion < -20)>>
-	Since you have two restrained and unwilling slaves, the work of milking $milkTap.slaveName's breasts falls to you. That doesn't mean you can't have fun doing it though.
-	<<if canDoVaginal($milkTap)>>
-		Moving behind the restrained cow while<<if $PC.dick == 0>> donning a strap-on<<else>>teasing your erect cock<</if>>, you push _him2 forward to allow you to insert yourself into _his2 <<if $milkTap.vagina == 0>>virgin <</if>>pussy. Getting comfortable, you reach around to _his2 immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms in both yourself and the sobbing cow, is <<= getSlave($AS).slaveName>> bloated with enough milk.
-		<<set $milkTap.counter.vaginal++, $vaginalTotal++>>
-		<<if canImpreg($milkTap, $PC)>>
-			<<= knockMeUp($milkTap, 40, 0, -1)>>
-		<</if>>
-	<<elseif canDoAnal($milkTap)>>
-		Moving behind the restrained cow while<<if $PC.dick == 0>> donning a strap-on<<else>> teasing your erect cock<</if>>, you push _him2 forward to allow you to insert yourself into _his2 <<if $milkTap.anus == 0>>virgin <</if>>rear. Getting comfortable, you reach around to _his2 immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms in both yourself and the sobbing cow, is <<= getSlave($AS).slaveName>> bloated with enough milk.
-		<<set $milkTap.counter.anal++, $analTotal++>>
-		<<if canImpreg($milkTap, $PC)>>
-			<<= knockMeUp($milkTap, 40, 1, -1)>>
-		<</if>>
-	<<elseif $PC.dick != 0 && $milkTap.butt > 4>>
-		Moving behind the restrained cow while teasing your erect cock, you push _him2 forward to allow you to press your dick between _his2 huge butt cheeks. Getting comfortable, you reach around to _his2 immense mammaries and begin kneading them in time to your thrusts. After some time, and several orgasms across the back of the sobbing cow, is <<= getSlave($AS).slaveName>> bloated with enough milk.
-	<<elseif $PC.dick != 0 && !hasAnyLegs($milkTap)>>
-		Moving behind the restrained cow while teasing your erect cock, you find a severe lack of places to stick your dick. Sighing, you hoist _his2 belted ass into the air so you may thrust between _his2 <<if $milkTap.weight > 95>>soft <</if>>thighs. Getting comfortable, you reach around to _his2 immense mammaries and begin kneading them in time to your thrusts. After some time, and several loads blown<<if $milkTap.belly >= 1500>> onto the rounded belly of the sobbing cow<</if>>, is <<= getSlave($AS).slaveName>> bloated with enough milk.
-	<<else>>
-		With a lack of holes to penetrate, you simply wrap your arms around $milkTap.slaveName and begin fondling and milking _his2 luscious breasts. After some time, <<= getSlave($AS).slaveName>> is finally bloated to your desired size.
-	<</if>>
-	Standing and releasing $him from $milkTap.slaveName, gives you the opportunity to finally see <<= getSlave($AS).slaveName>>'s <<if getSlave($AS).inflation == 3>>taut, round belly<<elseif getSlave($AS).inflation == 2>>rounded, jiggling belly<<else>>distended, sloshing belly<</if>>. You just wish you could have enjoyed it a bit more, though forcing milk into the squirming slave was quite enjoyable.
-	Both slaves @@.mediumorchid;resent@@ what you made them do and @@.gold;fear you@@ as a result.
-	<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 5>>
-	<<set $milkTap.devotion -= 5, $milkTap.trust -= 5>>
-	<<if canDoVaginal($milkTap) && ($milkTap.vagina == 0)>>
-		$milkTap.slaveName @@.mediumorchid;especially,@@ having just @@.lime;lost $his virginity@@ to your inconvenience.
-		<<set $milkTap.devotion -= 5, $milkTap.vagina = 1>>
-	<<elseif canDoAnal($milkTap) && ($milkTap.anus == 0)>>
-		$milkTap.slaveName @@.mediumorchid;especially,@@ having just @@.lime;lost $his anal virginity@@ to your inconvenience.
-		<<set $milkTap.devotion -= 5, $milkTap.anus = 1>>
-	<</if>>
-
-<<elseif ($milkTap.devotion < -20)>>
-	Since your cow is restrained, you order the more obedient <<= getSlave($AS).slaveName>> to enjoy $himself with $milkTap.slaveName's breasts. As $he suckles, you can't help but notice the tantalizing way $he wiggles $his rear.
-	<<if canDoVaginal(getSlave($AS))>>
-		<<if $PC.dick == 0>>Donning a strap-on<<else>>Teasing your stiffening cock<</if>>, you push $him deeper into the protesting $milkTap.slaveName and mount $his <<if getSlave($AS).vagina == 0>>virgin <</if>> pussy, doggy style. You wrap your arms around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. Only once your weight is removed from the squirming milk balloon is $he allowed to pull $himself off of the @@.mediumorchid;resentful $milkTap.slaveName@@ and catch $his breath.<<if getSlave($AS).vagina == 0>> $His senses were so overwhelmed, $he didn't even notice you @@.lime;broke in $his vagina.@@<</if>>
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		<<if $PC.dick == 0>>Donning a strap-on<<else>>Teasing your stiffening cock<</if>>, you push $him deeper into the protesting $milkTap.slaveName and mount $his <<if getSlave($AS).anus == 0>>virgin <</if>> asshole, doggy style. You wrap your arms around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. Only once your weight is removed from the squirming milk balloon is $he allowed to pull $himself off of the @@.mediumorchid;resentful $milkTap.slaveName@@ and catch $his breath.<<if getSlave($AS).anus == 0>> $His senses were so overwhelmed, $he didn't even notice you @@.lime;broke in $his anus.@@<</if>>
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		Teasing your stiffening cock, you push $him deeper into the protesting $milkTap.slaveName and squeeze your dick between $his huge butt cheeks. You wrap your arms around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk as you fuck $his butt. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. Only once your weight is removed from the squirming milk balloon is $he allowed to pull $himself off of the @@.mediumorchid;resentful $milkTap.slaveName@@ and catch $his breath.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		Teasing your stiffening cock, you find a severe lack of places to stick your dick. Sighing, you hoist $his belted ass into the air, push $him deeper into the protesting $milkTap.slaveName and squeeze your dick between $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs. You wrap your arms around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk as you fuck $his butt. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. Only once your weight is removed from the squirming milk balloon is $he allowed to pull $himself off of the @@.mediumorchid;resentful $milkTap.slaveName@@ and catch $his breath.
-	<<else>>
-		With a lack of holes to penetrate, you simply wrap your arms around $him and push $him deeper into the protesting $milkTap.slaveName. You bring a hand to <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and lead the other to your <<if $PC.dick == 0>>soaked pussy<<else>>stiff prick<</if>>. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. Only once your weight is removed from the squirming milk balloon is $he allowed to pull $himself off of the @@.mediumorchid;resentful $milkTap.slaveName@@ and catch $his breath.
-	<</if>>
-	$He gives the shaking $milkTap.slaveName an apologetic look before taking a seat. The poor cow isn't used to this yet and @@.gold;is terrified of your willingness@@ to take what you want from your slaves.
-	<<set $milkTap.devotion -= 5, $milkTap.trust -= 5>>
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<set getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<set getSlave($AS).anus = 1>>
-	<</if>>
-
-<<elseif ($milkTap.fetish == "boobs") && ($milkTap.fetishStrength > 60) && ($milkTap.devotion > 20) && (getSlave($AS).devotion < -20)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he is forced to drink from $milkTap.slaveName's breasts. With every thrust into the squirming slave, you push $him into the moaning $milkTap.slaveName forcing even more milk down $his throat. You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much $he loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 well milked breasts, you know _he2'll come out of it and be eagerly begging you for another milking soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen stomach @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.<<if getSlave($AS).anus == 0>> $he @@.mediumorchid;hates you so much more@@ that you @@.lime;broke in $his virgin vagina.@@<</if>>
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he is forced to drink from $milkTap.slaveName's breasts. With every thrust into the squirming slave, you push $him into the moaning $milkTap.slaveName forcing even more milk down $his throat. You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much $he loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 well milked breasts, you know _he2'll come out of it and be eagerly begging you for another milking soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen stomach @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.<<if getSlave($AS).anus == 0>> $he @@.mediumorchid;hates you so much more@@ that you @@.lime;broke in $his virgin anus.@@<</if>>
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can rub your dick between $his huge butt cheeks while $he is forced to drink from $milkTap.slaveName's breasts. With every thrust against the squirming slave, you push $him into the moaning $milkTap.slaveName forcing even more milk down $his throat. You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much $he loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 well milked breasts, you know $he'll come out of it and be eagerly begging you for another milking soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen stomach, and cum soaked back, @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs, for a lack of anything better, while $he is forced to drink from $milkTap.slaveName's breasts. With every thrust against the squirming slave, you push $him into the moaning $milkTap.slaveName forcing even more milk down $his throat. You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much $he loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 well milked breasts, you know _he2'll come out of it and be eagerly begging you for another milking soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen, cum-covered stomach @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.
-	<<else>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he is forced to drink from $milkTap.slaveName's breasts, since $he lacks any better way to please you while you lavish attention on your eager cow. With every thrust against the squirming slave, you push $him into the moaning $milkTap.slaveName forcing even more milk down $his throat. You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much $he loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 well milked breasts, you know _he2'll come out of it and be eagerly begging you for another milking soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen stomach @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.
-	<</if>>
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<set getSlave($AS).vagina = 1>>
-		<<set getSlave($AS).devotion -= 5>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<set getSlave($AS).anus = 1>>
-		<<set getSlave($AS).devotion -= 5>>
-	<</if>>
-	<<set getSlave($AS).devotion -= 5>>
-	<<set getSlave($AS).trust -= 5>>
-	<<set $milkTap.devotion += 4>>
-
-<<elseif (getSlave($AS).devotion <= 20) || ($milkTap.devotion <= 20)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he drinks from $milkTap.slaveName's breasts. With every thrust into the squirming slave, you push $him into the docile $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he drinks from $milkTap.slaveName's breasts. With every thrust into the squirming slave, you push $him into the docile $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can rub your dick between $his huge butt cheeks while $he drinks from $milkTap.slaveName's breasts. With every thrust against the squirming slave, you push $him into the docile $milkTap.slaveName forcing even more milk down $his throat.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs, for a lack of anything better, while $he drinks from $milkTap.slaveName's breasts. With every thrust against the squirming slave, you push $him into the docile $milkTap.slaveName forcing even more milk down $his throat.
-	<<else>>
-		You order <<= getSlave($AS).slaveName>> to position $himself so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he drinks from $milkTap.slaveName's breasts, since $he lacks any better way to please you while you lavish praise on your obedient cow. With every thrust against the squirming slave, you push $him into the docile $milkTap.slaveName forcing even more milk down $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much _he2 loves it groped. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting. Neither slave seems to have enjoyed it, instead opting to just get it over with, though $milkTap.slaveName makes sure to thank <<= getSlave($AS).slaveName>> for lightening _his2 milky breasts.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<set getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<set getSlave($AS).anus = 1>>
-	<</if>>
-
-<<elseif (getSlave($AS).devotion <= 50) || ($milkTap.devotion <= 50)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he drinks from $milkTap.slaveName's breasts. $He submissively obeys. With every thrust into the moaning slave, you push $him into the smiling $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he drinks from $milkTap.slaveName's breasts. $He submissively obeys. With every thrust into the moaning slave, you push $him into the smiling $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can rub your dick between $his huge butt cheeks while $he drinks from $milkTap.slaveName's breasts. $He submissively obeys. With every thrust against the chaste slave, you push $him into the smiling $milkTap.slaveName forcing even more milk down $his throat.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs, for a lack of anything better, while $he drinks from $milkTap.slaveName's breasts. $He submissively obeys. With every thrust against the chaste slave, you push $him into the smiling $milkTap.slaveName forcing even more milk down $his throat.
-	<<else>>
-		You order <<= getSlave($AS).slaveName>> to position $himself so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he drinks from $milkTap.slaveName's breasts, since $he lacks any better way to please you while you lavish attention on your happy cow. With every thrust against the squirming slave, you push $him into the smiling $milkTap.slaveName forcing even more milk down $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple, knowing just how much _he2 gets backed up. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting. Both slaves enjoyed their union, though $milkTap.slaveName even more so thanks to _his2 lighter breasts.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<= getSlave($AS).slaveName>> feels @@.hotpink;closer to you@@ after losing $his virginity to you.
-		<<set getSlave($AS).vagina = 1, getSlave($AS).devotion += 2>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<= getSlave($AS).slaveName>> feels @@.hotpink;closer to you@@ after losing $his anal virginity to you.
-		<<set getSlave($AS).anus = 1, getSlave($AS).devotion += 2>>
-	<</if>>
-
-<<else>>
-	<<= getSlave($AS).slaveName>> eagerly lifts $his ass and jiggles it seductively as $he suckles from the moaning cow.
-	<<if canDoVaginal(getSlave($AS))>>
-		You know that signal, so you hilt yourself in <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> and begin fucking $him against $milkTap.slaveName's tits. With every thrust into the moaning slave, you push $him into the grinning $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You know that signal, so you hilt yourself in <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> and begin fucking $him against $milkTap.slaveName's tits. With every thrust into the moaning slave, you push $him into the grinning $milkTap.slaveName forcing even more milk down $his throat.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You know that signal, but $he isn't allowed to get penetrated, so you settle for sticking your dick between $his huge butt cheeks and fucking $him against $milkTap.slaveName's tits. With every thrust against the moaning slave, you push $him into the grinning $milkTap.slaveName forcing even more milk down $his throat.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You know that signal, but $he isn't allowed to get penetrated, so you settle for sticking your dick between $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs, for a lack of anything better, and fuck $him against $milkTap.slaveName's tits. With every thrust against the moaning slave, you push $him into the grinning $milkTap.slaveName forcing even more milk down $his throat.
-	<<else>>
-		You know that signal, but $he isn't allowed to get fucked, so you reposition $him so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he drinks from $milkTap.slaveName's tits. With every thrust against the moaning slave, you push $him into the grinning $milkTap.slaveName forcing even more milk down $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with milk and place your other hand to $milkTap.slaveName's free nipple to prevent _him2 from feeling left out. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with milk, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with milk, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with milk under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting from $his meal<<if canDoVaginal(getSlave($AS)) || canDoAnal(getSlave($AS))>> and from the pleasure you drove into $him<</if>>. Both slaves @@.hotpink;loved the attention,@@ though $milkTap.slaveName even more so thanks to _his2 lighter breasts.
-	<<set getSlave($AS).devotion += 4, $milkTap.devotion += 4>>
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<= getSlave($AS).slaveName>> got off quite strongly from the growing pressure within $him, @@.hotpink;cementing@@ $his @@.lime;first fucking@@ as something special.
-		<<set getSlave($AS).devotion += 4, getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<= getSlave($AS).slaveName>> got off quite strongly from the growing pressure within $him, @@.hotpink;cementing@@ $his @@.lime;first anal@@ as something special.
-		<<set getSlave($AS).devotion += 4, getSlave($AS).anus = 1>>
-	<</if>>
-
-<</if>>
-
-<<if $milkTap.lactation > 0>>
-	<<set $milkTap.lactationDuration = 2>>
-	<<set $milkTap.boobs -= $milkTap.boobsMilk, $milkTap.boobsMilk = 0>>
-<</if>>
-
-<<else>> /* cum variant */
-<<set getSlave($AS).cumSource = $milkTap.ID>>
-<<if $milkTap.behavioralQuirk == "sinful" || $milkTap.sexualQuirk == "perverted" || $milkTap.fetish == "incest">> /* incest is a planned fetish, don't touch it! */
-	<<set _incestGive = 1>>
-<</if>>
-<<if getSlave($AS).behavioralQuirk == "sinful" || getSlave($AS).sexualQuirk == "perverted" || getSlave($AS).fetish == "incest">>
-	<<set _incestTake = 1>>
-<</if>>
-
-The first necessary step is to prepare the cum slave and $his cock and balls.
-
-<<if $milkTap.fuckdoll > 0>>
-	This is hilariously easy, as you have complete control over how $milkTap.slaveName is posed.
-
-<<elseif $milkTap.fetish == "mindbroken">>
-	This is very easy, as $milkTap.slaveName blankly follows your every will. Combined with _his2 instinct to relieve the building pressure in _his2 loins, _he2 is simple to position.
-
-<<elseif $milkTap.rivalryTarget == getSlave($AS).ID>>
-	This is rather easy, as $milkTap.slaveName wants to <<if canSee($milkTap)>>see<<else>>feel<</if>> <<= getSlave($AS).slaveName>>'s belly swell painfully as $he is forced to suck down _his2 huge loads.
-
-<<elseif $milkTap.relationshipTarget == getSlave($AS).ID>>
-	This is rather easy, as $milkTap.slaveName
-	<<if $milkTap.relationship == 1>>
-		always wanted to get _his2 dick sucked by _his2 friend.
-	<<elseif $milkTap.relationship == 2>>
-		always wanted to get _his2 dick sucked by _his2 best friend.
-	<<elseif $milkTap.relationship == 3>>
-		enjoys getting _his2 dick sucked by _his2 friend with benefits.
-	<<elseif $milkTap.relationship == 4>>
-		loves getting _his2 dick sucked by _his2 lover, something that commonly happens due to _his2 overproduction.
-	<<elseif $milkTap.relationship == 5>>
-		loves getting _his2 dick sucked by _his2 $wife, something that commonly happens due to _his2 overproduction.
-	<</if>>
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName savors the thought of having _his2 dick sucked by _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-	<<else>>
-		This is tough, as $milkTap.slaveName is very uncomfortable having _his2 dick sucked by _his2 <<print relativeTerm($milkTap, getSlave($AS))>>, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName cherishes the sheer lewdity of having _his2 dick sucked by _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-	<<else>>
-		This is tough, as $milkTap.slaveName is rather uncomfortable having _his2 dick sucked by _his2 <<print relativeTerm($milkTap, getSlave($AS))>>, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName savors the thought of having _his2 dick sucked by _his2 own mother.
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName is very uncomfortable having _his2 dick sucked by _his2 own mother, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName cherishes the sheer lewdity of having _his2 dick sucked by _his2 own father.
-	<<else>>
-		This is tough, as $milkTap.slaveName is very uncomfortable having _his2 dick sucked by _his2 own father, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName enjoys the notion of twincest quite a lot.
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName is uncomfortable getting so intimate with _his2 <<print relativeTerm(getSlave($AS), $milkTap)>><<if $milkTap.energy >= 95>>, though as a nymphomaniac, the thought of someone who looks so much like _his2 is a major turn on<<else>> but _he2 can't really complain about getting _his2 overfilled nuts drained<</if>>.
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName is quite eager to get intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName is uncomfortable getting so intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	<<if _incestGive>>
-		This is easy enough, as $milkTap.slaveName is quite eager to get intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>.
-	<<else>>
-		This is moderately tough, as $milkTap.slaveName is uncomfortable getting so intimate with _his2 <<print relativeTerm($milkTap, getSlave($AS))>>, but _he2 can't really complain about getting _his2 overfilled nuts drained.
-	<</if>>
-
-<<elseif ($milkTap.fetish == "cumslut") && ($milkTap.fetishKnown == 1) && ($milkTap.fetishStrength > 60) && ($milkTap.devotion >= -20)>>
-	This is very easy, since $milkTap.slaveName loves blasting loads whenever _he2 can, and it is just a bonus to _him2 that _he2 gets a blowjob in the process.
-
-<<elseif ($milkTap.energy > 95)>>
-	This is very easy, since $milkTap.slaveName is so sexually charged _he2 is practically overflowing at the thought of getting _his2 dick sucked.
-
-<<elseif ($milkTap.devotion > 50)>>
-	Since $milkTap.slaveName is devoted to you, _he2'd allow anyone you want to suck _his2 dick.
-
-<<elseif ($milkTap.devotion > 20)>>
-	Since $milkTap.slaveName is obedient, _he2 appreciates being allowed to have _his2 dick sucked.
-
-<<elseif ($milkTap.devotion >= -20)>>
-	Since $milkTap.slaveName does not resist your will, _he2 should comply reasonably well. If anything, _he2'll at least be thankful for the pleasure and relief.
-
-<<else>>
-	Since $milkTap.slaveName is unlikely to comply willingly, you simply restrain _him2 with _his2 dick exposed and ready to be sucked. To get _him2 going,
-	<<if canDoAnal($milkTap) && $milkTap.prostate > 0>>
-		you circle around behind _him2, <<if $PC.dick == 0>>while donning a strap-on<<else>>teasing your erect cock<</if>>, and force _him2 forward to allow you to insert yourself into $his <<if $milkTap.anus == 0>>virgin <</if>>rear. After a quick and brutal bit of prostrate stimulation, you finish and remove yourself from _him2. Before _he2 has a chance to reclench _his2 anus, you ram an electroshock stimulator in your stead.
-	<<else>>
-		you attach a number of vibrators to _his2 oversized balls and turn them to full power, stirring up _his2 overzealous cum factories.
-	<</if>>
-	_he2 cries in @@.mediumorchid;disgust@@ and @@.gold;fear@@ as _his2 penis twitches from the sensation, begging for unwelcome release.
-	<<set $milkTap.devotion -= 5, $milkTap.trust -= 5>>
-	<<if canDoAnal($milkTap)>>
-		<<if canImpreg($milkTap, $PC)>>
-			<<= knockMeUp($milkTap, 40, 1, -1)>>
-		<</if>>
-		<<if ($milkTap.anus == 0)>>
-			$milkTap.slaveName feels @@.mediumorchid;especially violated@@ having just @@.lime;lost $his anal virginity@@ in such a manner.
-			<<set $milkTap.devotion -= 5, $milkTap.anus = 1>>
-		<</if>>
-		<<set $milkTap.counter.anal++, $analTotal++>>
-	<</if>>
-
-<</if>>
-
-<br><br>
-
-Next, you see to <<= getSlave($AS).slaveName>>.
-
-<<if (isAmputee(getSlave($AS)))>>
-	You tip the limbless $girl face-first into $milkTap.slaveName's dick.
-
-<<elseif tooBigBreasts(getSlave($AS))>>
-	You set $him up so that $his massive breasts pin $him, face-first, to $milkTap.slaveName's dick.
-
-<<elseif $milkTap.fuckdoll > 0>>
-	$He hesitantly brings $his mouth to its precum tipped dick, uncertain about sucking off a doll.
-
-<<elseif getSlave($AS).rivalryTarget == $milkTap.ID>>
-	Knowing $his relationship with $milkTap.slaveName, you feel it best to restrain $him and anchor $him to $milkTap.slaveName's eager cock so $he has no choice but to suck $his way to release.
-
-<<elseif getSlave($AS).relationshipTarget == $milkTap.ID>>
-	This is rather easy, as <<= getSlave($AS).slaveName>>
-	<<if getSlave($AS).relationship == 1>>
-		licks $his lips as $he approaches $his friend's cock.
-	<<elseif getSlave($AS).relationship == 2>>
-		eagerly licks $his lips as $he approaches $his best friend's cock.
-	<<elseif getSlave($AS).relationship == 3>>
-		licks $his lips and smiles as $he approaches $his friend with benefits' cock, knowing well how _his2 <<if canTaste(getSlave($AS))>>cum tastes<<else>>body feels<</if>>.
-	<<elseif getSlave($AS).relationship == 4>>
-		licks $his lips and smiles as $he approaches $his lover's cock. This won't be the first time $he's sucked _his2 dick and swallowed _his2 huge loads.
-	<<elseif getSlave($AS).relationship == 5>>
-		licks $his lips and smiles as $he approaches $his _wife2's cock. This won't be the first time $he's sucked _his2 dick and swallowed _his2 huge loads.
-	<</if>>
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>awkwardly brings $his lips to<</if>> $his mother's cock.
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>awkwardly brings $his lips to<</if>> daddy's cock.
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>awkwardly brings $his lips to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>><<print relativeTerm(getSlave($AS), $milkTap)>>'s cock.
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>awkwardly brings $his lips to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock.
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	$He <<if _incestTake>>readily <</if>>gets in position to suck $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s dick.
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>hesitatingly lowers $himself to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock.
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	$He <<if _incestTake>>eagerly wraps $his lips around<<else>>hesitatingly lowers $himself to<</if>> $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock.
-
-<<elseif (getSlave($AS).fetish == "cumslut") && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).devotion >= -20)>>
-	$He can't wait to wrap $his lips around $milkTap.slaveName's cock and balloon with cum, so $he eagerly approaches the waiting shaft.
-
-<<elseif (getSlave($AS).fetish == "submissive") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-	$He is accustomed to submitting to you, but as a natural submissive $he doesn't have much trouble submitting to $milkTap.slaveName instead.
-
-<<elseif getSlave($AS).devotion < -20>>
-	$He tries to refuse, so you tie $him up, force a mouth spreader into $him, and position $him for $milkTap.slaveName to thrust into.
-
-<<elseif getSlave($AS).devotion <= 20>>
-	$He obeys your orders reluctantly, drawing near $milkTap.slaveName's cock despite $his obvious hesitation to amount of cum that will be gushing into $him.
-
-<<elseif getSlave($AS).devotion <= 50>>
-	$He obeys your orders, drawing near $milkTap.slaveName's cock despite $his slight hesitation at the idea of being filled with cum.
-
-<<else>>
-	$He happily obeys your orders, eagerly <<if canTaste(getSlave($AS))>>tasting<<else>>licking up<</if>> $milkTap.slaveName's beading precum before wrapping $his lips around $milkTap.slaveName's cock and sucking enthusiastically.
-<</if>>
-
-<br><br>
-
-<<if getSlave($AS).preg > 3 && getSlave($AS).pregKnown == 0 && getSlave($AS).inflation > 1>>
-	It becomes abundantly clear that something is wrong with <<= getSlave($AS).slaveName>> as $he struggles to down $his thick meal. Before $his health can be affected further, you pull $him into a medical exam. While most of the tests come back normal, one in particular catches your eye; @@.lime;$he is pregnant<<if getSlave($AS).preg > 10>> and surprisingly far along<</if>>.@@ $he should be able to still handle at least two liters of cum, however.
-	<<run deflate(getSlave($AS))>>
-	<<set getSlave($AS).pregKnown = 1, _pregDiscovery = 1>>
-
-<<elseif $milkTap.fuckdoll > 0>>
-	Slight moaning emanates from the Fuckdoll as <<= getSlave($AS).slaveName>> sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off _his2 cock and settles into _his2 balls for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off _his2 cock and settles into _his2 balls for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off _his2 cock and settles into _his2 balls for a short rest while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-
-<<elseif $milkTap.rivalryTarget == getSlave($AS).ID>>
-	$milkTap.slaveName grins as _his2 rival is forced to suck down loads until $his belly is <<if getSlave($AS).inflation == 3>>nearly bursting with cum. <<= getSlave($AS).slaveName>> struggles against $his bindings until the pressure building in $his overwhelms $him, causing $him to pass out with $milkTap.slaveName's ejaculating cock still stuck in $him. You quickly remove $him from it, making sure $he gets roused from $his stupor by one last blast of cum directly to the face<<elseif getSlave($AS).inflation == 2>>is rounded, jiggling and sloshing with cum. You release $his bindings, allowing $him to flop to the floor. <<if hasAnyArms(getSlave($AS))>>$He gingerly crawls away from $milkTap.slaveName, $his hand cradling $his overfull stomach<<else>>$He rolls onto $his side, groaning with discomfort<</if>><<elseif getSlave($AS).inflation == 1>>bloated with cum. You release $his bindings, allowing $him to flop to the floor. <<if hasAnyArms(getSlave($AS))>>$He gingerly sits up and begins massaging $his full stomach<<else>>$He rolls onto $his back, hiccupping pathetically<</if>><</if>>.
-
-<<elseif $milkTap.relationshipTarget == getSlave($AS).ID>>
-	<<if $milkTap.relationship == 1>>
-		$milkTap.slaveName moans as _his2 friend energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 2>>
-		$milkTap.slaveName moans as _his2 best friend energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his best friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his best friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his best friend's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 3>>
-		$milkTap.slaveName moans lewdly as _his2 friend with benefits energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his friend with benefits' cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his friend with benefits' cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his friend with benefits' cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 4>>
-		$milkTap.slaveName moans lustfully as _his2 lover teases $his dick perfectly with _his2 tongue, savoring it despite commonly being sucked off by <<= getSlave($AS).slaveName>> during their lovemaking. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his lover's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his lover's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his lover's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<<elseif $milkTap.relationship == 5>>
-		$milkTap.slaveName moans lustfully as _his2 $wife teases _his2 dick perfectly with $his tongue, savoring it despite commonly being sucked off by <<= getSlave($AS).slaveName>> during their lovemaking. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his _wife2's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his _wife2's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his _wife2's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-	<</if>>
-
-<<elseif getSlave($AS).mother == $milkTap.ID>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his mother's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his mother's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his mother's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif getSlave($AS).father == $milkTap.ID>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his father's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his father's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his father's cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif $milkTap.mother == getSlave($AS).ID>>
-	$milkTap.slaveName moans lewdly as _his2 mother energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif $milkTap.father == getSlave($AS).ID>>
-	$milkTap.slaveName moans lewdly as _his2 father energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>><<if getSlave($AS).dick > 0 && canAchieveErection(getSlave($AS))>>, $his own stiff prick throbbing against the underside of $his new belly<</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 1>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> sucks _him2 off. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 2>>
-	$milkTap.slaveName moans lewdly as _his2 <<if $milkTap.actualAge >= getSlave($AS).actualAge>>little<<else>>big<</if>> <<print relativeTerm($milkTap, getSlave($AS))>> energetically sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-<<elseif areSisters(getSlave($AS), $milkTap) == 3>>
-	$milkTap.slaveName moans lewdly as _his2 <<print relativeTerm($milkTap, getSlave($AS))>> sucks _his2 dick. You enjoy the show, specifically the sight of <<= getSlave($AS).slaveName>>'s belly steadily growing larger with each orgasm until <<if getSlave($AS).inflation == 3>>$his belly is round and taut, making $him look pregnant. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and rubbing $his gurgling stomach<</if>><<elseif getSlave($AS).inflation == 2>>$his belly is round, jiggling and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his wobbling, gurgling stomach<</if>><<elseif getSlave($AS).inflation == 1>>$his belly is distended and sloshing with cum. $He pops off $his <<print relativeTerm(getSlave($AS), $milkTap)>>'s cock and takes a seat facing the smiling $milkTap.slaveName while hiccupping<<if hasAnyArms(getSlave($AS))>> and teasing $his gurgling stomach<</if>><</if>>.
-
-<<elseif (getSlave($AS).devotion < -20) && ($milkTap.devotion < -20)>>
-	Since you have two restrained and unwilling slaves, though $milkTap.slaveName's twitching penis betrays _him2, you are going to have to take an active role in forcing <<= getSlave($AS).slaveName>> to suck.
-	<<if canDoVaginal(getSlave($AS))>>
-		Moving behind the struggling cocksleeve while<<if $PC.dick == 0>> donning a strap-on<<else>> teasing your erect cock<</if>>, you pull $him into a comfortable position to penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy. Once you are firmly mounted, you reach around, bringing one hand to $his empty stomach and the other to $his exposed throat. As you thrust into $him, you force $him to choke down $milkTap.slaveName's dick, applying pressure to $his throat any time $he attempts to pull away.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		Moving behind the struggling cocksleeve while<<if $PC.dick == 0>> donning a strap-on<<else>> teasing your erect cock<</if>>, you pull $him into a comfortable position to penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>rear. Once you are firmly mounted, you reach around, bringing one hand to $his empty stomach and the other to $his exposed throat. As you thrust into $him, you force $him to choke down $milkTap.slaveName's dick, applying pressure to $his throat any time $he attempts to pull away.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		Moving behind the struggling cocksleeve while teasing your erect cock, you pull $him into a comfortable position to rub your dick between $his huge butt cheeks. Once you are firmly slotted, you reach around, bringing one hand to $his empty stomach and the other to $his exposed throat. As you thrust against $him, you force $him to choke down $milkTap.slaveName's dick, applying pressure to $his throat any time $he attempts to pull away.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		Moving behind the struggling cocksleeve while teasing your erect cock, you pull $him into a comfortable position to fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs, for a lack of anything better. Once you are firmly seated, you reach around, bringing one hand to $his empty stomach and the other to $his exposed throat. As you thrust against $him, you force $him to choke down $milkTap.slaveName's dick, applying pressure to $his throat any time $he attempts to pull away.
-	<<else>>
-		Moving behind the struggling cocksleeve while teasing your erect cock, you find a distinct lack of ways to use $him to get off. <<if $PC.dick != 0>>You settle for rubbing your erection against $his back<<else>>You settle for a vibrating dildo stuck deep into your pussy<</if>>, you'll need both hands to fondle your toy. Once you are positioned, you reach around, bringing one hand to $his empty stomach and the other to $his exposed throat. As you masturbate, you force $him to choke down $milkTap.slaveName's dick, applying pressure to $his throat any time $he attempts to pull away.
-	<</if>>
-	<<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers and the seemingly endless supply of ejaculate from $milkTap.slaveName<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers and the seemingly endless supply of ejaculate from $milkTap.slaveName<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers and the seemingly endless supply of ejaculate from $milkTap.slaveName<</if>>. Standing and releasing $him from $milkTap.slaveName, gives you a lovely sight of <<= getSlave($AS).slaveName>>'s <<if getSlave($AS).inflation == 3>>taut, round belly<<elseif getSlave($AS).inflation == 2>>rounded, jiggling belly<<else>>distended, sloshing belly<</if>> quivering as $he comes down from $his own forced climax<<if $PC.dick != 0 && (canDoAnal(getSlave($AS)) || canDoVaginal(getSlave($AS)))>>, cum leaking from both ends<</if>>.
-	Both slaves @@.mediumorchid;resent@@ what you made them do and @@.gold;fear you@@ as a result.
-	<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 5>>
-	<<set $milkTap.devotion -= 5, $milkTap.trust -= 5>>
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<= getSlave($AS).slaveName>> @@.mediumorchid;especially,@@ having just @@.lime;lost $his virginity@@ in such a demeaning manner.
-		<<set getSlave($AS).devotion -= 5>>
-		<<set getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<= getSlave($AS).slaveName>> @@.mediumorchid;especially,@@ having just @@.lime;lost $his anal virginity@@ in such a demeaning manner.
-		<<set getSlave($AS).devotion -= 5>>
-		<<set getSlave($AS).anus = 1>>
-	<</if>>
-
-<<elseif ($milkTap.devotion < -20)>>
-	Since your sperm tank is restrained, you order the more obedient <<= getSlave($AS).slaveName>> to enjoy $himself with $milkTap.slaveName's dick. As $he teases and licks, you can't help but notice the tantalizing way $he wiggles $his rear.
-	<<if canDoVaginal(getSlave($AS))>>
-		<<if $PC.dick == 0>>Donning a strap-on<<else>>Stroking your stiffening cock<</if>>, you wait for the perfect moment and mount $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy, doggy style.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		<<if $PC.dick == 0>>Donning a strap-on<<else>>Stroking your stiffening cock<</if>>, you wait for the perfect moment and mount $his <<if getSlave($AS).anus == 0>>virgin <</if>>asshole, doggy style.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		Stroking your stiffening cock, you wait for the perfect moment and slip your dick between $his huge butt cheeks.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		Stroking your stiffening cock, you wait for the perfect moment, hoist up $his rear and slip your dick between $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs.
-	<<else>>
-		As you watch $his butt, it becomes clear just how few ways there are to use $him to get off. <<if $PC.dick != 0>>You settle for rubbing your erection against $his back<<else>>You settle for a vibrating dildo stuck deep into your pussy<</if>>, you'll need both hands to fondle your toy.
-	<</if>>
-	You wrap your arms around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with ejaculate. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers<</if>>. Only once your weight is removed from the squirming cum balloon is $he allowed to pull off of the @@.mediumorchid;exhausted $milkTap.slaveName's@@ cock and catch $his breath.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		$His senses were so overwhelmed, $he didn't even notice you @@.lime;broke in $his pussy.@@
-		<<set getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		$His senses were so overwhelmed, $he didn't even notice you @@.lime;broke in $his anus.@@
-		<<set getSlave($AS).anus = 1>>
-	<</if>>
-	$He gives the shaking $milkTap.slaveName an apologetic look before taking a seat. The poor slave isn't used to this yet and @@.gold;is terrified of your willingness@@ to take what you want from your slaves.
-	<<set $milkTap.devotion -= 5, $milkTap.trust -= 5>>
-
-<<elseif ($milkTap.fetish == "cumslut") && ($milkTap.fetishStrength > 60) && ($milkTap.devotion > 20) && (getSlave($AS).devotion < -20)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he is forced to suck $milkTap.slaveName's dick. With every thrust into the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he is forced to suck $milkTap.slaveName's dick. With every thrust into the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can rub your dick between $his huge butt cheeks while $he is forced to suck $milkTap.slaveName's dick. With every thrust against the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You position the restrained <<= getSlave($AS).slaveName>> so that you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs while $he is forced to suck $milkTap.slaveName's dick. With every thrust against the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-	<<else>>
-		You position <<= getSlave($AS).slaveName>> so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he is forced to suck $milkTap.slaveName's dick, since $he lacks any better way to please you. With every thrust against the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with ejaculate and place your other hand to $milkTap.slaveName's swollen testicles, knowing just how much _he2 loves to jettison cum. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers<</if>> and $milkTap.slaveName even more. _He2 is semi-conscious, drooling in @@.hotpink;pleasure and satisfaction,@@ by the time you release the bloated <<= getSlave($AS).slaveName>> from $his harness. Patting _his2 spasming, dribbling cock, you know _he2'll come out of it and be eagerly begging you for another slave to fuck soon. <<= getSlave($AS).slaveName>>, on the other hand, is regarding $his swollen stomach @@.mediumorchid;with disgust@@ and @@.gold;fear@@ of your power over $him.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		$He @@.mediumorchid;hates you so much more@@ that you @@.lime;broke in $his virgin pussy.@@
-		<<set getSlave($AS).vagina = 1, getSlave($AS).devotion -= 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		$He @@.mediumorchid;hates you so much more@@ that you @@.lime;broke in $his virgin anus.@@
-		<<set getSlave($AS).anus = 1, getSlave($AS).devotion -= 1>>
-	<</if>>
-	<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 5>>
-	<<set $milkTap.devotion += 4>>
-
-<<elseif (getSlave($AS).devotion <= 20) || ($milkTap.devotion <= 20)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he sucks $milkTap.slaveName's cock. With every thrust into the squirming slave, you push $milkTap.slaveName's cock deeper down $his throat, giving $milkTap.slaveName's orgasms a straight shot into the moaning slave's gullet.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he sucks $milkTap.slaveName's cock. With every thrust into the squirming slave, you push $milkTap.slaveName's cock deeper down $his throat, giving $milkTap.slaveName's orgasms a straight shot into the moaning slave's gullet.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You order <<= getSlave($AS).slaveName>> to position $his ass so you can rub your dick between $his huge butt cheeks while $he sucks $milkTap.slaveName's cock. With every thrust against the squirming slave, you push $milkTap.slaveName's cock deeper down $his throat, giving $milkTap.slaveName's orgasms a straight shot into the moaning slave's gullet.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to position $his ass so you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs while $he sucks $milkTap.slaveName's cock. With every thrust against the squirming slave, you push $milkTap.slaveName's cock deeper down $his throat, giving $milkTap.slaveName's orgasms a straight shot into the moaning slave's gullet.
-	<<else>>
-		You order <<= getSlave($AS).slaveName>> to position $himself so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he is forced to suck $milkTap.slaveName's dick, since $he lacks any better way to please you. With every thrust against the squirming slave, you force the moaning $milkTap.slaveName's cock deep into $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with ejaculate and place your other hand to $milkTap.slaveName's balls, planning to coax even stronger orgasms out of _him2. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting. Neither slave seems to have truly enjoyed it, instead opting to just get it over with, though $milkTap.slaveName makes sure to thank <<= getSlave($AS).slaveName>> for dealing with $his pent up loads.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<set getSlave($AS).vagina = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<set getSlave($AS).anus = 1>>
-	<</if>>
-
-<<elseif (getSlave($AS).devotion <= 50) || ($milkTap.devotion <= 50)>>
-	<<if canDoVaginal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> while $he sucks $milkTap.slaveName's cock. $He submissively obeys. With every thrust into the moaning slave, you push $milkTap.slaveName's dick deeper down $his throat.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can penetrate $his <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> while $he sucks $milkTap.slaveName's cock. $He submissively obeys. With every thrust into the moaning slave, you push $milkTap.slaveName's dick deeper down $his throat.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can rub your dick between $his huge butt cheeks while $he sucks $milkTap.slaveName's cock. $He submissively obeys. With every thrust against the moaning slave, you push $milkTap.slaveName's dick deeper down $his throat.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You order <<= getSlave($AS).slaveName>> to lift $his ass so you can fuck $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs while $he sucks $milkTap.slaveName's cock. $He submissively obeys. With every thrust against the moaning slave, you push $milkTap.slaveName's dick deeper down $his throat.
-	<<else>>
-		You order <<= getSlave($AS).slaveName>> to position $himself so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he sucks $milkTap.slaveName's cock, since $he lacks any better way to please you. $He submissively obeys. With every thrust against the moaning slave, you push $milkTap.slaveName's dick deeper down $his throat.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with ejaculate and place your other hand to $milkTap.slaveName's balls, knowing just how much _he2 gets backed up. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting. Both slaves enjoyed their union, though $milkTap.slaveName even more so after that many orgasms.
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<= getSlave($AS).slaveName>> feels @@.hotpink;closer to you@@ after losing $his virginity to you.
-		<<set getSlave($AS).vagina = 1, getSlave($AS).devotion += 5>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<= getSlave($AS).slaveName>> feels @@.hotpink;closer to you@@ after losing $his anal virginity to you.
-		<<set getSlave($AS).anus = 1, getSlave($AS).devotion += 5>>
-	<</if>>
-
-<<else>>
-
-	<<= getSlave($AS).slaveName>> eagerly lifts $his ass and jiggles it seductively as $he sucks the moaning slut.
-	<<if canDoVaginal(getSlave($AS))>>
-		You know that signal, so you hilt yourself in <<if getSlave($AS).vagina == 0>>virgin <</if>>pussy <<if $PC.dick == 0>>with a strap-on <</if>> and begin spitroasting $him with $milkTap.slaveName. With every thrust into the moaning slave, every participant comes closer to their own climax.
-		<<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
-	<<elseif canDoAnal(getSlave($AS))>>
-		You know that signal, so you hilt yourself in <<if getSlave($AS).anus == 0>>virgin <</if>>ass <<if $PC.dick == 0>>with a strap-on <</if>> and begin spitroasting $him with $milkTap.slaveName. With every thrust into the moaning slave, every participant comes closer to their own climax.
-		<<set getSlave($AS).counter.anal++, $analTotal++>>
-	<<elseif $PC.dick != 0 && getSlave($AS).butt > 4>>
-		You know that signal, but $he isn't allowed to get penetrated, so you settle for sticking your dick between $his huge butt cheeks and fucking $him along with $milkTap.slaveName. With every thrust against the moaning slave, both you and $milkTap.slaveName come closer to climax.
-	<<elseif $PC.dick != 0 && hasBothLegs(getSlave($AS))>>
-		You know that signal, but $he isn't allowed to get penetrated, so you settle for sticking your dick between $his <<if getSlave($AS).weight > 95>>soft <</if>>thighs and fucking $him along with $milkTap.slaveName. With every thrust against the moaning slave, both you and $milkTap.slaveName come closer to climax.
-	<<else>>
-		You know that signal, but $he isn't allowed to get fucked, so you reposition $him so you can rub your <<if $PC.dick == 0>>clit<<else>>dick<</if>> against $him while $he deepthroats $milkTap.slaveName. With every thrust against the moaning slave, both you and $milkTap.slaveName come closer to climax.
-	<</if>>
-	You wrap an arm around <<= getSlave($AS).slaveName>>'s middle so you may feel $his stomach swell with ejaculate and place your other hand to $milkTap.slaveName's breasts to prevent _him2 from feeling left out from your attention. <<if getSlave($AS).inflation == 3>>You cum multiple times as you feel $his belly slowly round with cum, transform into a jiggling mass, and finally grow taut under your molesting fingers<<elseif getSlave($AS).inflation == 2>>You cum several times as you feel $his belly slowly round with cum, finally transforming into a jiggling mass, under your molesting fingers<<else>>You cum as you feel $his belly slowly round with cum under your molesting fingers<</if>>. When you release $him from under your weight, $he drops to the ground panting from $his meal and from the pleasure you drove into $him. Both slaves @@.hotpink;loved the attention,@@ though $milkTap.slaveName even more so after so much relief.
-	<<set getSlave($AS).devotion += 4>>
-	<<set $milkTap.devotion += 4>>
-	<<if canDoVaginal(getSlave($AS)) && (getSlave($AS).vagina == 0)>>
-		<<= getSlave($AS).slaveName>> got off quite strongly from the growing pressure within $him, @@.hotpink;cementing@@ $his @@.lime;first fucking@@ as something special.
-		<<set getSlave($AS).devotion += 4, getSlave($AS).anus = 1>>
-	<<elseif canDoAnal(getSlave($AS)) && (getSlave($AS).anus == 0)>>
-		<<= getSlave($AS).slaveName>> got off quite strongly from the growing pressure within $him, @@.hotpink;cementing@@ $his @@.lime;first anal@@ as something special.
-		<<set getSlave($AS).devotion += 4, getSlave($AS).anus = 1>>
-	<</if>>
-
-<</if>>
-
-<</if>>
-
-<br><br>
-
-<<if _pregDiscovery == 0>>
-	<<set getSlave($AS).counter.oral++, $milkTap.counter.oral++, $oralTotal += 2>>
-	You help the bloated <<= getSlave($AS).slaveName>> to the couch to recover and, more importantly, keep $his meal down. Only once $he has had several minutes to unwind<<if getSlave($AS).devotion > 10>> and plenty of time to tease you with $his swollen body, do you tell <<else>> do you order<</if>> $him to keep drinking from $milkTap.slaveName so that $he is always filled with <<if getSlave($AS).inflation == 3>>two gallons<<elseif getSlave($AS).inflation == 2>>four liters<<else>>two liters<</if>> of <<= getSlave($AS).inflationType>>. You give $his <<if getSlave($AS).inflation == 3>>taut, firm globe of a belly a pat<<elseif getSlave($AS).inflation == 2>>wobbly, sloshing belly a pat<<else>>distended, sloshing belly a pat<</if>> and send $him on $his way.
-
-	<<if getSlave($AS).inflation == 3>>
-		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility === "permissive")>>$He gingerly leaves your office, massaging $his over-stuffed belly as $he goes<<else>>$His belly is so taut it barely wobbles at all as $he is helped from your office<</if>>. Being filled so full @@.red;surely had negative effects@@ on $his health.
-		<<run healthDamage(getSlave($AS), 1)>>
-	<<elseif getSlave($AS).inflation == 2>>
-		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility === "permissive")>>$He gingerly leaves your office, massaging $his stuffed belly as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>.
-	<<elseif getSlave($AS).inflation == 1>>
-		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility === "permissive")>>$He gingerly leaves your office, massaging $his distended belly as $he goes<<else>>$His belly wobbles as $he is helped from your office<</if>>.
-	<</if>>
-	<br><br>
-	<<if $milkTap.fuckdoll == 0>>
-		Once $he is gone, you see to it that the contented $milkTap.slaveName is helped back to _his2 assignment, but only after _his2 dribbling <<if getSlave($AS).inflationType == "milk">>teats are dealt with<<else>>cock is dealt with<</if>>, causing the waiting servant to gulp nervously at what that may entail.
-	<<else>>
-		Once $he is gone, you see to it that the dribbling Fuckdoll is cleaned up and returned to _his2 proper place, but only after _his2 leaking <<if getSlave($AS).inflationType == "milk">>teats are dealt with<<else>>cock is dealt with<</if>>, causing the waiting servant to gulp nervously at what that may entail.
-	<</if>>
-<<else>>
-	<<if $milkTap.fuckdoll == 0>>
-		With <<= getSlave($AS).slaveName>> unable to continue, you are left with the backed up $milkTap.slaveName to deal with. _He2'll have to figure out some other way to relieve _himself2 as _he2 is helped back to _his2 assignment.
-	<<else>>
-		With <<= getSlave($AS).slaveName>> unable to continue, you are left with the backed up $milkTap.slaveName to deal with. Hopefully _he2 doesn't leak too much as _he2 waits for further use.
-	<</if>>
-<</if>>
-
-<<run SetBellySize(getSlave($AS))>>
-<<set $slaves[$slaveIndices[$milkTap.ID]] = $milkTap>>
-<<set $milkTap = 0>>
diff --git a/src/npc/interaction/fSlaveSelfImpreg.tw b/src/npc/interaction/fSlaveSelfImpreg.tw
index 67eabd3045d52ab302211452b768b550107bcdbe..44e4d18c3d1c5fad35a893dccf5a4a4ac5d9b906 100644
--- a/src/npc/interaction/fSlaveSelfImpreg.tw
+++ b/src/npc/interaction/fSlaveSelfImpreg.tw
@@ -31,7 +31,7 @@
 				and this @@.gold;uniquely degrading@@ violation of $his person only reinforces $his @@.mediumorchid;hatred@@ towards you. The fact that $he is @@.gold;utterly immobile@@ makes a terrifying situation @@.mediumorchid;even worse.@@
 				<<set getSlave($AS).devotion -= 1, getSlave($AS).trust -= 1>>
 			<<else>>
-				and this @@.gold;uniquely degrading@@ violation of $his person only reinforces $his @@.mediumorchid;hatred@@ towards you. $He resists so violently that you must @@.red;physically coerce $him@@ into cooperating with the procedure.
+				and this @@.gold;uniquely degrading@@ violation of $his person only reinforces $his @@.mediumorchid;hatred@@ towards you. $He resists so violently that you must @@.health.dec;physically coerce $him@@ into cooperating with the procedure.
 			<</if>>
 			<<set _coop = false, _enjoy = false>>
 			<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 6>>
diff --git a/src/npc/interaction/fillUpButt.tw b/src/npc/interaction/fillUpButt.tw
index 9a5247d26fb4464256ce375243615a3ea90de9c6..760b1f617c6694fe09710858cc52905d2d3f9479 100644
--- a/src/npc/interaction/fillUpButt.tw
+++ b/src/npc/interaction/fillUpButt.tw
@@ -378,7 +378,7 @@ before shoving the equipment into $his
 <<if _pregDiscovery == 0>>
 	<<set getSlave($AS).inflation += 1>>
 	<<if getSlave($AS).inflation == 3>>
-		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his bloated guts as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>. Being filled so full @@.red;surely had negative effects@@ on $his health.
+		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his bloated guts as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>. Being filled so full @@.health.dec;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
 		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his full guts as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>.
diff --git a/src/npc/interaction/fillUpFace.tw b/src/npc/interaction/fillUpFace.tw
index 6858746d4e7dc198ad7a9341be18a1b18211f1b3..090b91f86f5e48c644978a59c2dce32a9c269e2c 100644
--- a/src/npc/interaction/fillUpFace.tw
+++ b/src/npc/interaction/fillUpFace.tw
@@ -218,7 +218,7 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 <<if _pregDiscovery == 0>>
 	<<set getSlave($AS).inflation += 1>>
 	<<if getSlave($AS).inflation == 3>>
-		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his over-stuffed belly as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>. Being filled so full @@.red;surely had negative effects@@ on $his health.
+		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his over-stuffed belly as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>. Being filled so full @@.health.dec;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
 		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>$He gingerly leaves your office, massaging $his stuffed belly as $he goes<<else>>$His belly wobbles heavily as $he is helped from your office<</if>>.
diff --git a/src/npc/interaction/forceFeeding.tw b/src/npc/interaction/forceFeeding.tw
index cc8ff4412b820b69a2b49c6d26290047b48a6fea..e5f52e0c2f6c85e91b189acc7c546d3161ae70c8 100644
--- a/src/npc/interaction/forceFeeding.tw
+++ b/src/npc/interaction/forceFeeding.tw
@@ -532,7 +532,7 @@ and a little jiggle from $his gut.
 		<<else>>
 			$His belly wobbles heavily as $he is helped from your office.
 		<</if>>
-		Being filled so full @@.red;surely had negative effects@@ on $his health.
+		Being filled so full @@.health.dec;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
 		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>
diff --git a/src/npc/interaction/passage/fSlaveSlaveDickConsummate.tw b/src/npc/interaction/passage/fSlaveSlaveDickConsummate.tw
index 46607f83fade3bb17a52861f4095e162423727a4..d1f4ec87bd309fa326d5bbfc283add80944cc77c 100644
--- a/src/npc/interaction/passage/fSlaveSlaveDickConsummate.tw
+++ b/src/npc/interaction/passage/fSlaveSlaveDickConsummate.tw
@@ -390,7 +390,7 @@ You call $slaverapistx.slaveName into the room.
 			<</if>>
 		<</if>>
 	<</if>> /* closes losing virginity */
-	_He2 begins playing with $him immediately, slapping, pinching and licking $his boobs while bouncing on the meaty shaft. Occasionally _he2 stops, denying <<= getSlave($AS).slaveName>> release by painfully squeezing and smacking the sensitive shaft. By the end of the session <<= getSlave($AS).slaveName>>'s abused, pent-up penis has shot several massive and painful loads into the blissfully satisfied $slaverapistx.slaveName, leaving $him lying on the bed, shaking in horror and @@.red;utter exhaustion,@@ while $slaverapistx.slaveName reaps the opportunity to continue painfully tormenting $him.
+	_He2 begins playing with $him immediately, slapping, pinching and licking $his boobs while bouncing on the meaty shaft. Occasionally _he2 stops, denying <<= getSlave($AS).slaveName>> release by painfully squeezing and smacking the sensitive shaft. By the end of the session <<= getSlave($AS).slaveName>>'s abused, pent-up penis has shot several massive and painful loads into the blissfully satisfied $slaverapistx.slaveName, leaving $him lying on the bed, shaking in horror and @@.health.dec;utter exhaustion,@@ while $slaverapistx.slaveName reaps the opportunity to continue painfully tormenting $him.
 	<<run healthDamage(getSlave($AS), 10)>>
 	<<set getSlave($AS).counter.penetrative += 3, $penetrativeTotal += 3, $slaverapistx.counter.vaginal += 3, $vaginalTotal += 3>>
 
diff --git a/src/npc/slaveStats.tw b/src/npc/slaveStats.tw
index 082d8df8fff47546e86a357c8829a9afe67dcad1..919f6724fc2072b382710ce1ec7a63a6afca2972 100644
--- a/src/npc/slaveStats.tw
+++ b/src/npc/slaveStats.tw
@@ -7,7 +7,7 @@
 	background-color: grey;
 }
 </style>
-<<set _slave  = getSlave($AS)>>
+<<set _slave = getSlave($AS)>>
 <<run App.Utils.setLocalPronouns(_slave)>>
 
 <br>Name: _slave.slaveName,
diff --git a/src/npc/startingGirls/startingGirls.js b/src/npc/startingGirls/startingGirls.js
index ee4968a0688830b7657187e980f48341335bdb19..9e0206d184819d71331bde334ee137b42ca44258 100644
--- a/src/npc/startingGirls/startingGirls.js
+++ b/src/npc/startingGirls/startingGirls.js
@@ -1,11 +1,9 @@
-App.StartingGirls = {};
-
 /** Generate a new slave for the starting girls passage
  * @returns {App.Entity.SlaveState}
  */
 App.StartingGirls.generate = function(params) {
 	let slave = GenerateNewSlave(null, params);
-	setHealth(slave, 0);
+	setHealth(slave, 0, 0, 0, 0, 0);
 	slave.devotion = 0;
 	slave.trust = 0;
 	slave.sexualQuirk = "none";
@@ -13,7 +11,6 @@ App.StartingGirls.generate = function(params) {
 	slave.fetishKnown = 1;
 	slave.canRecruit = 0;
 	slave.weekAcquired = 0;
-	slave.health.tired = 0;
 
 	return slave;
 };
@@ -94,6 +91,11 @@ App.StartingGirls.cleanup = function(slave) {
 	} else if ((slave.vagina > 2 && slave.skill.vaginal <= 10) || (slave.vagina === 0 && slave.skill.vaginal > 30)) {
 		slave.skill.vaginal = 15;
 	}
+
+	slave.prestige = Math.clamp(slave.prestige, 0, 3) || 0;
+	if (slave.prestige === 0) {
+		slave.prestigeDesc = 0;
+	}
 };
 
 /** Apply starting girl PC career bonus
@@ -223,38 +225,37 @@ App.StartingGirls.career = function(slave) {
 	if (V.AgePenalty === 1) {
 		if (slave.actualAge < 16) {
 			text = "Very young careers: ";
-			pullDown = render(setup.veryYoungCareers);
+			pullDown = render(App.Data.Careers.General.veryYoung);
 		} else if (slave.actualAge <= 24) {
 			text = "Young careers: ";
-			pullDown = render(setup.youngCareers);
+			pullDown = render(App.Data.Careers.General.young);
 		} else if (slave.intelligenceImplant >= 15) {
 			text = "Educated careers: ";
-			pullDown = render(setup.educatedCareers);
+			pullDown = render(App.Data.Careers.General.educated);
 		} else {
 			text = "Uneducated careers: ";
-			pullDown = render(setup.uneducatedCareers);
+			pullDown = render(App.Data.Careers.General.uneducated);
 		}
 	} else {
 		if (slave.actualAge < 16) {
 			text = "Very young careers: ";
-			pullDown = render(setup.veryYoungCareers);
+			pullDown = render(App.Data.Careers.General.veryYoung);
 		} else if (slave.intelligenceImplant >= 15) {
 			text = "Educated careers: ";
-			pullDown = render(setup.educatedCareers);
+			pullDown = render(App.Data.Careers.General.educated);
 		} else if (slave.actualAge <= 24) {
 			text = "Young careers: ";
-			pullDown = render(setup.youngCareers);
+			pullDown = render(App.Data.Careers.General.young);
 		} else {
 			text = "Uneducated careers: ";
-			pullDown = render(setup.uneducatedCareers);
+			pullDown = render(App.Data.Careers.General.uneducated);
 		}
 	}
 	function render(options) {
-		let select = document.createElement("SELECT");
+		let select = document.createElement("select");
 		select.classList.add("rajs-list");
 
-		for(let i = 0; i < options.length; i++) {
-			let opt = options[i];
+		for (const opt of options) {
 			let el = document.createElement("option");
 			el.textContent = capFirstChar(opt);
 			el.value = opt;
diff --git a/src/npc/startingGirls/startingGirls.tw b/src/npc/startingGirls/startingGirls.tw
index fe1b351cbfc87d22db6c758828728b1a2537e985..f5de17245bde04d6ca7bde4e2a0a2f24545375f9 100644
--- a/src/npc/startingGirls/startingGirls.tw
+++ b/src/npc/startingGirls/startingGirls.tw
@@ -81,7 +81,7 @@
 				<div class="indent">
 					<<link "Head Girl Prospect" "Starting Girls">>
 						<<set $activeSlave = App.StartingGirls.generate({ minAge: 36, maxAge: 44 })>>
-						<<set $activeSlave.career = setup.HGCareers.random(), $activeSlave.intelligence = 70, $activeSlave.intelligenceImplant = 0>>
+						<<set $activeSlave.career = App.Data.Careers.Leader.HG.random(), $activeSlave.intelligence = 70, $activeSlave.intelligenceImplant = 0>>
 					<</link>>
 					<span class="note">
 						Inexpensive potential to become a great right hand woman
@@ -123,7 +123,9 @@
 			<</link>>
 		<</if>>
 
-		| [[Take control of your arcology|Acquisition]]
+		| <<link "Take control of your arcology" "Acquisition">>
+			<<unset $showSGNationalities>>
+		<</link>>
 	</span>
 	<hr>
 </p>
@@ -370,6 +372,7 @@
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Mental')" id="tab Mental">Mental</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Skills')" id="tab Skills">Skills</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Family', App.StartingGirls.uncommittedFamilyTree(V.activeSlave))" id="tab Family">Family</button>
+	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'body-mods')" id="tab body-mods">Body Mods</button>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Customization')" id="tab Customization">Customization</button>
 	<<if $cash >= _slaveCost>>
 		<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'assignRemove')" id="tab assignRemove">Finalize</button>
@@ -487,12 +490,13 @@
 		<<run _option.addComment(`<span class=warning>${_comment}</span>`)>>
 	<</if>>
 
-	<<run _options.addOption("$His nationality is", "nationality", $activeSlave).showTextBox()>>
+	<<run _options.addOption("$His nationality is", "nationality", $activeSlave).showTextBox()
+	.addValueList(Object.keys(App.Data.SlaveSummary.short.nationality))>>
 
 	<<if $seeRace == 1>>
 		<<run _options.addOption("$His ethnicity is", "race", $activeSlave).showTextBox()
 		.addValueList([["White", "white"], ["Asian", "asian"], ["Latina", "latina"], ["Middle Eastern", "middle eastern"],
-			["Black", "black"], ["Semetic", "semetic"], ["Southern European", "southern european"], ["Indo-Aryan", "indo-aryan"],
+			["Black", "black"], ["Semitic", "semitic"], ["Southern European", "southern european"], ["Indo-Aryan", "indo-aryan"],
 			["Amerindian", "amerindian"], ["Pacific Islander", "pacific islander"], ["Malay", "malay"], ["Mixed Race", "mixed race"]])>>
 	<</if>>
 
@@ -1061,6 +1065,12 @@
 	</div>
 </div>
 
+<div id="body-mods" class="tab-content">
+	<div class="content">
+		<<includeDOM App.UI.bodyModification($activeSlave, true)>>
+	</div>
+</div>
+
 <div id="Customization" class="tab-content">
 	<div class="content">
 
@@ -1180,21 +1190,6 @@
 	<div class="content">
 
 	<<if $cash >= _slaveCost>>
-		<<if $activeSlave.prestige>>
-			<<set $activeSlave.prestige = Math.clamp($activeSlave.prestige,0,3)>>
-		<<else>>
-			<<set $activeSlave.prestigeDesc = 0>>
-		<</if>>
-		<<set $activeSlave.actualAge = Number($activeSlave.actualAge) || 18>>
-		<<set $activeSlave.visualAge = $activeSlave.actualAge>>
-		<<set $activeSlave.physicalAge = $activeSlave.actualAge>>
-		<<set $activeSlave.devotion = Number($activeSlave.devotion) || 0>>
-		<<set $activeSlave.trust = Number($activeSlave.trust) || 0>>
-		<<if $activeSlave.indenture >= 0>>
-			<<set $activeSlave.indenture = Math.clamp($activeSlave.indenture, 26, 208) || 26>>
-		<</if>>
-		<<set $activeSlave.height = Math.clamp($activeSlave.height, 85, 274) || 140>>
-		<<set $activeSlave.boobs = Math.clamp(Math.trunc($activeSlave.boobs/50)*50, 0, 50000) || 200>>
 		<<if $PC.career != "engineer">>
 			<div class="indent">
 				<<link "Add this slave">>
diff --git a/src/npc/surgery/surgery.js b/src/npc/surgery/surgery.js
index 611388f00dd03abff441f1eaf9dee8cc6f010b29..4777bc268a2e2e0c4e56966a2663940ffbacd767 100644
--- a/src/npc/surgery/surgery.js
+++ b/src/npc/surgery/surgery.js
@@ -10,16 +10,17 @@
  * @param {number} [costs] money costs
  * @param {number} [hCosts] health costs
  * @param {string} [surgeryType]
+ * @param {boolean} [cheat]
  * @returns {FC.Medicine.Surgery.Procedure}
  */
-App.Medicine.Surgery.makeOption = function(typeId, label, effect, desc, action, costs, hCosts, surgeryType) {
+App.Medicine.Surgery.makeOption = function(typeId, label, effect, desc, action, costs, hCosts, surgeryType, cheat = false) {
 	return {
 		typeId: typeId,
 		label: label,
 		targetEffect: effect,
 		description: desc,
-		costs: costs,
-		healthCosts: hCosts,
+		costs: (cheat) ? 0 : costs,
+		healthCosts: (cheat) ? 0 : hCosts,
 		action: action,
 		surgeryType: surgeryType
 	};
@@ -72,12 +73,15 @@ App.Medicine.Keys = {
  * Commit procedure, executing its action and subtracting its costs
  * @param {FC.Medicine.Surgery.Procedure} surgery
  * @param {App.Entity.SlaveState} slave
+ * @param {boolean} [cheat]
  */
-App.Medicine.Surgery.commit = function(surgery, slave) {
+App.Medicine.Surgery.commit = function(surgery, slave, cheat = false) {
 	V.surgeryType = surgery.surgeryType;
 	surgery.action(slave);
-	cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
-	surgeryDamage(slave, surgery.healthCosts);
+	if (!cheat) {
+		cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
+		surgeryDamage(slave, surgery.healthCosts, cheat);
+	}
 };
 
 /**
@@ -85,9 +89,10 @@ App.Medicine.Surgery.commit = function(surgery, slave) {
  * @param {string} passage Passage to go after the surgery
  * @param {FC.Medicine.Surgery.Procedure} surgery
  * @param {App.Entity.SlaveState} slave
+ * @param {boolean} [cheat]
  * @returns {string}
  */
-App.Medicine.Surgery.makeLink = function(passage, surgery, slave) {
+App.Medicine.Surgery.makeLink = function(passage, surgery, slave, cheat = false) {
 	if (surgery.action === undefined) {
 		return App.UI.disabledLink(surgery.label, [surgery.description]);
 	}
@@ -107,7 +112,7 @@ App.Medicine.Surgery.makeLink = function(passage, surgery, slave) {
 	}
 
 	return App.UI.link(surgery.label, App.Medicine.Surgery.commit, [surgery, slave], passage,
-		`${capFirstChar(surgery.description)}.\nSurgery costs: ${cashFormat(surgery.costs)}.\nProjected health damage: ${healthCosts()}.`);
+		`${capFirstChar(surgery.description)}.${cheat ? `` : `\nSurgery costs: ${cashFormat(surgery.costs)}.\nProjected health damage: ${healthCosts()}.`}`);
 };
 
 /**
@@ -574,22 +579,23 @@ App.Medicine.Surgery.sizingProcedures = function() {
  * For limbs use removeLimbs()
  * @param {App.Entity.SlaveState} slave
  * @param {string} part
+ * @param {boolean} [cheat]
  */
-globalThis.surgeryAmp = function(slave, part) {
+globalThis.surgeryAmp = function(slave, part, cheat = false) {
 	switch (part) {
 		case "left ear":
 			delete slave.brand["left ear"];
 			slave.earShape = "none";
 			slave.earT = "none";
 			slave.earPiercing = 0;
-			surgeryDamage(slave, 10);
+			surgeryDamage(slave, 10, cheat);
 			break;
 		case "right ear":
 			delete slave.brand["right ear"];
 			slave.earShape = "none";
 			slave.earT = "none";
 			slave.earPiercing = 0;
-			surgeryDamage(slave, 10);
+			surgeryDamage(slave, 10, cheat);
 			break;
 		case "dick":
 			slave.dick = 0;
@@ -599,7 +605,7 @@ globalThis.surgeryAmp = function(slave, part) {
 			slave.dickTat = 0;
 			slave.dickAccessory = "none";
 			slave.chastityPenis = 0;
-			surgeryDamage(slave, 20);
+			surgeryDamage(slave, 20, cheat);
 			break;
 		case "vagina":
 			slave.vagina = -1;
@@ -611,17 +617,17 @@ globalThis.surgeryAmp = function(slave, part) {
 			slave.vaginalAccessory = "none";
 			slave.vaginalAttachment = "none";
 			slave.chastityVagina = 0;
-			surgeryDamage(slave, 20);
+			surgeryDamage(slave, 20, cheat);
 			break;
 		case "horn":
 			slave.horn = "none";
 			slave.hornColor = "none";
-			surgeryDamage(slave, 10);
+			surgeryDamage(slave, 10, cheat);
 			break;
 		case "voicebox":
 			slave.voice = 0;
 			slave.voiceImplant = 0;
-			surgeryDamage(slave, 10);
+			surgeryDamage(slave, 10, cheat);
 			break;
 		case "lips":
 			slave.lips -= slave.lipsImplant;
@@ -727,7 +733,7 @@ globalThis.setEyeColor = function(slave, color, side = "both") {
  * @param {string} sclera
  * @param {string} side
  */
-globalThis.setEyeColorFull = function(slave, iris = "brown", pupil = "circular", sclera = "white", side) {
+globalThis.setEyeColorFull = function(slave, iris = "brown", pupil = "circular", sclera = "white", side = "both") {
 	if (side === "both") {
 		setEyeColorFull(slave, iris, pupil, sclera, "left");
 		setEyeColorFull(slave, iris, pupil, sclera, "right");
@@ -1102,8 +1108,8 @@ globalThis.beginFuckdoll = function(slave) {
 	slave.choosesOwnClothes = 0;
 	slave.clothes = "a Fuckdoll suit";
 	slave.collar = "none";
-	slave.faceAccessory  = "none";
-	slave.mouthAccessory  = "none";
+	slave.faceAccessory = "none";
+	slave.mouthAccessory = "none";
 	if ((!hasAnyLegs(slave)) || (slave.shoes !== "none")) {
 		slave.shoes = "heels";
 	}
diff --git a/src/player/desc/playerBoobs.js b/src/player/desc/playerBoobs.js
index a9d95077f8e05f11674c8b23bacaf4fab43d3cea..fa5cb2fdb349bdb52129f6afbeae115c273e106a 100644
--- a/src/player/desc/playerBoobs.js
+++ b/src/player/desc/playerBoobs.js
@@ -163,7 +163,7 @@ App.Desc.Player.boobs = function() {
 			if (V.PC.boobs >= 1400) {
 				r.push(`You've gotten your top retailored to fit your huge bust.`);
 			} else if (V.PC.boobs >= 1200) {
-				r.push(`Your top strains against your big breasts`);
+				r.push(`Your top strains against your big`);
 				if (V.PC.markings === "freckles") {
 					r.push(`breasts, revealing a peak of freckled cleavage.`);
 				} else if (V.PC.markings === "heavily freckled") {
@@ -294,13 +294,19 @@ App.Desc.Player.boobs = function() {
 		} else if (V.PC.title === 1) {
 			r.push(`Your chest is quite`);
 			if (V.PC.lactation > 0) {
-				frag = ` masculine, though the pair of wet spots forming over your nipples suggest otherwise,`;
+				frag = ` masculine, though the pair of wet spots forming over your nipples suggest otherwise`;
 			} else {
 				frag = ` masculine`;
 			}
 			if (V.PC.markings === "freckles") {
+				if (V.PC.lactation > 0) {
+					frag += `,`;
+				}
 				frag += ` and covered in a light spray of freckles.`;
 			} else if (V.PC.markings === "heavily freckled") {
+				if (V.PC.lactation > 0) {
+					frag += `,`;
+				}
 				frag += ` and covered in dense freckles.`;
 			} else {
 				frag += `.`; // all this frag bs is to make sure there is no space before the period.  Sigh.
@@ -309,17 +315,24 @@ App.Desc.Player.boobs = function() {
 		} else {
 			r.push(`Your chest is`);
 			if (V.PC.lactation > 0) {
-				frag = `non-existent, save for the pair of bulging milk glands beneath your nipples,`;
+				frag = ` non-existent, save for the pair of bulging milk glands beneath your nipples`;
 			} else {
-				frag = `non-existent`;
+				frag = ` non-existent`;
 			}
 			if (V.PC.markings === "freckles") {
-				frag += `and covered in a light spray of freckles.`;
+				if (V.PC.lactation > 0) {
+					frag += `,`;
+				}
+				frag += ` and covered in a light spray of freckles.`;
 			} else if (V.PC.markings === "heavily freckled") {
-				frag += `and covered in dense freckles.`;
+				if (V.PC.lactation > 0) {
+					frag += `,`;
+				}
+				frag += ` and covered in dense freckles.`;
 			} else {
 				frag += `.`;
 			}
+			r.push(frag);
 		}
 	}
 	return r.join(" ");
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index 1cbe58b92443625649cea44c452c4de90faba9eb..cd477ab41500ea2ed3ac0c3b22c217647a179f7b 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -92,19 +92,19 @@ App.Entity.PlayerActionsCountersState = class {
 		this.birthDegenerate = 0;
 		/** how many whoring babies you've had */
 		this.birthClient = 0;
-		/** untracked births */
+		/** how many children you've carried for other arc owners */
 		this.birthArcOwner = 0;
 		/** how many children you've had by sex with citizens (not whoring) */
 		this.birthCitizen = 0;
-		/** how many times you've giving birth to your own selfcest babies */
+		/** how many children you've had with the Sisters */
 		this.birthFutaSis = 0;
-		/** how many units of your cum are stored away for artificially inseminating slaves */
+		/** how many times you've giving birth to your own selfcest babies */
 		this.birthSelf = 0;
 		/** how many designer babies you've produced */
 		this.birthLab = 0;
-		/** how many children you've had with the Sisters */
+		/** untracked births */
 		this.birthOther = 0;
-		/** how many children you've carried for other arc owners */
+		/** how many units of your cum are stored away for artificially inseminating slaves */
 		this.storedCum = 0;
 		/** shared variables */
 		/** amount of milk given */
@@ -289,17 +289,18 @@ App.Entity.PlayerState = class PlayerState {
 		this.pronoun = App.Data.Pronouns.Kind.male;
 		/**
 		 * * career prior to becoming owner
-		 * * "wealth"
-		 * * "capitalist"
-		 * * "mercenary"
-		 * * "slaver"
-		 * * "engineer"
-		 * * "medicine"
-		 * * "celebrity"
-		 * * "escort"
-		 * * "servant"
-		 * * "gang"
-		 * * "BlackHat"
+		 * * (22+)			(14+)					(10+)
+		 * * "wealth"		("trust fund")			("rich kid")
+		 * * "capitalist"	("entrepreneur")		("business kid")
+		 * * "mercenary"	("recruit")				("child soldier")
+		 * * "slaver"		("slave overseer")		("slave tender")
+		 * * "engineer"		("construction")		("worksite helper")
+		 * * "medicine" 	("medical assistant")	("nurse")
+		 * * "celebrity"	("rising star")			("child star")
+		 * * "escort"		("prostitute")			("child prostitute")
+		 * * "servant"		("handmaiden")			("child servant")
+		 * * "gang"			("hoodlum")				("street urchin")
+		 * * "BlackHat"		("hacker")				("script kiddy")
 		 * * "arcology owner"
 		 */
 		this.career = "capitalist";
diff --git a/src/player/js/enslavePlayer.js b/src/player/js/enslavePlayer.js
index 752f46e5a9ebca016f2136b03c0b7927829e2a52..ce10b738752f22bc94d74e930f1ddf98b62a4186 100644
--- a/src/player/js/enslavePlayer.js
+++ b/src/player/js/enslavePlayer.js
@@ -92,7 +92,6 @@ globalThis.convertPlayerToSlave = function(slave, badEnd = "boring") {
 	slave.rules.mobility = "restrictive";
 	slave.pregControl = "none";
 	slave.readyProsthetics = [];
-	slave.death = "";
 	slave.onDiet = 0;
 	slave.haircuts = 0;
 	slave.newGamePlus = 0;
diff --git a/src/player/js/playerJS.js b/src/player/js/playerJS.js
index 278777f61631947c84eed047ddd6a07fe3ca2b6d..a9a5df36f88e508c405884ac3bc9097c44605e4f 100644
--- a/src/player/js/playerJS.js
+++ b/src/player/js/playerJS.js
@@ -637,3 +637,20 @@ globalThis.IncreasePCSkills = function(input, increase = 1) {
 	}
 	return t;
 };
+
+/** Returns if the player is on mandatory bedrest.
+ * @returns {boolean}
+ */
+globalThis.onBedRest = function(actor) {
+	// consider player health and injury in the future!
+	if (!actor) {
+		return null;
+	} else if (!canMove(actor)) {
+		return true;
+	} else if (actor.preg > actor.pregData.normalBirth / 1.33 && actor.womb.find((ft) => ft.genetics.geneticQuirks.polyhydramnios === 2 && ft.age >= 20)) {
+		return true;
+	} else if (actor.bellyPreg >= actor.pregAdaptation * 2200) {
+		return true;
+	}
+	return false;
+};
diff --git a/src/pregmod/FCTV/FCTV.css b/src/pregmod/FCTV/FCTV.css
index 246efe55940bfb3bb14c119d5f46b315e420a59c..f7a3b69f149ad68fc4437c2355e9f3c0d18bc199 100644
--- a/src/pregmod/FCTV/FCTV.css
+++ b/src/pregmod/FCTV/FCTV.css
@@ -1,7 +1,6 @@
 .FCTV {
 	display: float;
 }
-
 .fctv-remote {
 	text-align: center;
 	justify-items: center;
diff --git a/src/pregmod/FCTV/FCTV.js b/src/pregmod/FCTV/FCTV.js
index fdf32b32e601c4ac1cbdfafe5f6e781255357218..b9c32baea5bedba6e475fdb91400275c9f2bade7 100644
--- a/src/pregmod/FCTV/FCTV.js
+++ b/src/pregmod/FCTV/FCTV.js
@@ -37,7 +37,7 @@ globalThis.FCTV = (function() {
 	}
 
 	/**
-	 * checks value of selected channel in relation to an arbitrary number.  Have we watched it more/less/same than i.
+	 * checks value of selected channel in relation to an arbitrary number. Have we watched it more/less/same than i.
 	 * @param {number} selectedChannel
 	 * @param {number} i
 	 * @param {string} operation eq/gt/lt: equals/greater than/less than.
@@ -97,7 +97,7 @@ globalThis.FctvDisplay = function({usedRemote = 0, seeAll = 0, selectedChannel =
 		if (!usedRemote) {
 			p.append(`set FCTV to find a random show. Your larger-than-life screen flashes on, and is soon playing content from the popular streaming service. `);
 			if (V.cheatMode > 0 || V.debugMode > 0 || V.FCTV.remote) {
-				// Create "Use remote" link.  Hide once clicked.
+				// Create "Use remote" link. Hide once clicked.
 				span = document.createElement("span");
 				span.id = "use-remote";
 				span.append(
@@ -328,7 +328,7 @@ globalThis.FctvDisplay = function({usedRemote = 0, seeAll = 0, selectedChannel =
 			// Increment the viewing record for this channel
 			V.FCTV.channel[num(selectedChannel, true)]++;
 
-			// Slave, if needed.  Hosts and market slaves.
+			// Slave, if needed. Hosts and market slaves.
 			let slave;
 			if (channel.episode[epToShow].slaves) {
 				slave = channel.episode[epToShow].slaves[0];
@@ -381,12 +381,12 @@ globalThis.FctvDisplay = function({usedRemote = 0, seeAll = 0, selectedChannel =
 					return -1; // Nothing to watch, fail.
 				}
 
-				if (availableEp > viewedCount) { // If we watched ep 0 last time, our view count will be 1.  Now we can use 1 as our new ep, etc.
+				if (availableEp > viewedCount) { // If we watched ep 0 last time, our view count will be 1. Now we can use 1 as our new ep, etc.
 					epToShow = viewedCount;
 				} else if (channel.loop === true) {
-					// How many times have we been through this series.  Lets say we watched 10 episodes, but there are only 3 uniques [0,1,2].
+					// How many times have we been through this series. Lets say we watched 10 episodes, but there are only 3 uniques [0,1,2].
 					const watchedEntireSeason = Math.trunc(viewedCount / availableEp); // we went through 3 times fully
-					epToShow = viewedCount - (watchedEntireSeason * availableEp); // 10 - 3 seasons (9) is 1. So our last episode was the first, 0 in the array.  And 1 is the next ep!
+					epToShow = viewedCount - (watchedEntireSeason * availableEp); // 10 - 3 seasons (9) is 1. So our last episode was the first, 0 in the array. And 1 is the next ep!
 				} else { // We have seen all the episodes, return a random one
 					epToShow = jsRandom(0, availableEp-1);
 				}
diff --git a/src/pregmod/JobFulfillmentCenterDelivery.tw b/src/pregmod/JobFulfillmentCenterDelivery.tw
index a9f6bb403ae326f1096610d7581635445f8b0a56..94e9a6542cabfad7de0d94e4de17b305c025b5b7 100644
--- a/src/pregmod/JobFulfillmentCenterDelivery.tw
+++ b/src/pregmod/JobFulfillmentCenterDelivery.tw
@@ -2,7 +2,7 @@
 
 <<set $JFC.order = 0, $nextButton = "Continue", $nextLink = "Scheduled Event", $encyclopedia = "Enslaving People">>
 <<set _slave = generateLeadershipSlave($JFC.role, 'Job Fulfillment Center')>>
-<<set _slaveCost = slaveCost(_slave) * 6>> 
+<<set _slaveCost = slaveCost(_slave) * 6>>
 <<setLocalPronouns _slave>>
 <<run App.Utils.setLocalPronouns(_slave)>>
 
diff --git a/src/pregmod/incubatorReport.tw b/src/pregmod/incubatorReport.tw
deleted file mode 100644
index d4c6d6d4bcd0f1537afd2e022fcfa07a36fdb145..0000000000000000000000000000000000000000
--- a/src/pregmod/incubatorReport.tw
+++ /dev/null
@@ -1,829 +0,0 @@
-:: Incubator Report [nobr]
-
-<<set $readySlaves = 0>>
-<<if $incubatorImprintSetting == 0>><<set $incubatorImprintSetting = "trust">><</if>>
-
-<<for _inc = 0; _inc < $tanks.length; _inc++>>
-	<<setLocalPronouns $tanks[_inc]>>
-	<<set $tanks[_inc].birthWeek += 1>>
-	<<if $tanks[_inc].birthWeek >= 52>>
-		<<set $tanks[_inc].birthWeek = 0>>
-		<<if $seeAge == 1>>
-			<<set $tanks[_inc].actualAge++, $tanks[_inc].ovaryAge++>>
-		<</if>>
-	<</if>>
-	<<if $tanks[_inc].growTime > 0>>
-		<<set $tanks[_inc].growTime -= $incubatorUpgradeSpeed>>
-		<br>@@.pink;<<print $tanks[_inc].slaveName>>'s@@ growth is currently being accelerated. $He <<if Math.round($tanks[_inc].growTime/$incubatorUpgradeSpeed) <= 0>>is @@.lime;ready for release.@@ $He will be ejected from $his tank upon your approach<<else>>will be ready for release in about <<print Math.round($tanks[_inc].growTime/$incubatorUpgradeSpeed)>> weeks<</if>>.
-	<<else>>
-		@@.pink;$tanks[_inc].slaveName@@ is @@.lime;ready for release.@@ $He will be ejected from $his tank upon your approach.
-		<<set $readySlaves = 1>>
-	<</if>>
-
-	<<if $incubatorUpgradeWeight == 1>>
-		<br>
-		<<if $incubatorWeightSetting == 1>>
-			<<if $tanks[_inc].weight < 200>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].weight += 70>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].weight += 40>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].weight += 20>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].weight += 10>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].weight += 5>>
-				<</if>>
-			<</if>>
-			The weight monitoring systems are overloading $his intake causing @@.red;rapid weight gain.@@
-		<<elseif $incubatorWeightSetting == 2>>
-			<<if $tanks[_inc].weight > 10>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].weight -= 30>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].weight -= 10>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].weight -= 5>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].weight -= 2>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].weight -= 1>>
-				<</if>>
-				The weight monitoring systems detect $he is overweight and @@.green;decrease $his caloric intake.@@
-			<<elseif $tanks[_inc].weight < -10>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].weight += 30>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].weight += 10>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].weight += 5>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].weight += 2>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].weight += 1>>
-				<</if>>
-				The weight monitoring systems detect $he is underweight and @@.green;increase $his caloric intake.@@
-			<<else>>
-				$He is @@.lime;currently at a healthy weight;@@ efforts will be made to maintain it.
-			<</if>>
-		<<elseif $incubatorWeightSetting == 0>>
-			<<if $tanks[_inc].weight > -100>>
-				$His developing body @@.red;quickly sheds its gained weight.@@
-				<<set $tanks[_inc].weight -= 40>>
-			<</if>>
-		<</if>>
-	<<else>>
-		<<if $tanks[_inc].weight > -100>>
-			$His developing body @@.red;quickly sheds its gained weight.@@
-			<<set $tanks[_inc].weight -= 40>>
-		<</if>>
-	<</if>>
-
-	<<if $incubatorUpgradeMuscles == 1>>
-		<br>
-		<<if $incubatorMusclesSetting == 2>>
-			<<if $tanks[_inc].muscles < 100>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].muscles += 70>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].muscles += 40>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].muscles += 20>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].muscles += 10>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].muscles += 5>>
-				<</if>>
-			<</if>>
-			The strength monitoring systems are overloading $him with steroids causing @@.green;rapid muscle development.@@
-		<<elseif $incubatorMusclesSetting == 1>>
-			<<if $tanks[_inc].muscles > 10>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].muscles -= 30>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].muscles -= 10>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].muscles -= 5>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].muscles -= 2>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].muscles-->>
-				<</if>>
-				The strength monitoring systems detect $he is overly muscular and @@.green;decrease $his steroid dosage.@@
-			<<elseif $tanks[_inc].muscles < -10>>
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].muscles += 30>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].muscles += 10>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].muscles += 5>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].muscles += 2>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].muscles++>>
-				<</if>>
-				The strength monitoring systems detect $he is weak and @@.green;increase $his steroid dosage.@@
-			<<else>>
-				$He has @@.lime;a healthy musculature;@@ efforts will be made to maintain it.
-			<</if>>
-		<<elseif $incubatorMusclesSetting == 0>>
-			<<if $tanks[_inc].muscles > -100>>
-				$His developing body @@.red;quickly loses its gained muscle.@@
-				<<set $tanks[_inc].muscles -= 40>>
-			<</if>>
-		<</if>>
-	<<else>>
-		<<if $tanks[_inc].muscles > -100>>
-			$His developing body @@.red;quickly loses its gained muscle.@@
-			<<set $tanks[_inc].muscles -= 40>>
-		<</if>>
-	<</if>>
-
-	<<if $incubatorUpgradeGrowthStims == 1 && $incubatorGrowthStimsSetting != 0>>
-		<br>
-		<<set _heightLimit = Math.trunc(Math.clamp((Height.mean($tanks[_inc]) * 1.25),0,274))>>
-		<<set _heightLimitAge = Height.forAge($tanks[_inc].height, $tanks[_inc])>>
-		<<if $tanks[_inc].geneticQuirks.dwarfism == 2 && $tanks[_inc].geneticQuirks.gigantism != 2>>
-			<<set _heightLimit = Math.min(_heightLimit, 160)>>
-		<</if>>
-		<<if $tanks[_inc].geneMods.NCS == 1>>
-			/*
-			** NCS should block physical growth beyond that of a toddler, but some players might like
-			** a little more or less. So using $minimumSlaveAge or 8, whichever is lesser.
-			*/
-			<<set _limitAge = Math.min(8, $minimumSlaveAge)>>
-			<<set _heightLimitAge = Height.forAge($tanks[_inc].height, _limitAge, $tanks[_inc].genes)>>
-			<<set _heightLimit = _heightLimitAge>>
-		<<elseif ($tanks[_inc].geneticQuirks.neoteny === 2 && $tanks[_inc].actualAge > 12)>>
-			<<set _heightLimitAge = Height.forAge($tanks[_inc].height, 12, $tanks[_inc].genes)>>
-			<<set _heightLimit = _heightLimitAge>>
-		<</if>>
-		<<if $tanks[_inc].height >= _heightLimit>>
-			The monitoring system detects $his body is not able to support further increases in height, so it carefully regulates stimulant injections to @@.yellow;maintain $his current stature.@@
-			<<set $tanks[_inc].height = _heightLimit>>
-		<<elseif $incubatorGrowthStimsSetting == 2>>
-			<<if $tanks[_inc].geneMods.NCS == 1>>
-				The monitoring system floods $his body with growth stimulants, but $his @@.orange;NCS prevents an increase in $his growth rate.@@
-				<<set $tanks[_inc].height = _heightLimitAge>>
-			<<else>>
-				The monitoring system floods $his body with growth stimulants, causing @@.green;a sharp increase in growth rate.@@
-				<<if $incubatorWeightSetting >= 1 && $incubatorMusclesSetting <= 1 && $incubatorReproductionSetting <= 1>>
-					<<if $incubatorUpgradeSpeed == 52>>
-						<<set $tanks[_inc].height += random(3,6)>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<set $tanks[_inc].height += random(2,5)>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<set $tanks[_inc].height += random(1,4)>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<set $tanks[_inc].height += random(1,3)>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<set $tanks[_inc].height += random(1,2)>>
-					<</if>>
-				<<else>>
-					<<if $incubatorUpgradeSpeed == 52>>
-						<<set $tanks[_inc].height += random(2,5)>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<set $tanks[_inc].height += random(1,4)>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<set $tanks[_inc].height += random(1,3)>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<set $tanks[_inc].height += random(1,2)>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<set $tanks[_inc].height += random(0,1)>>
-					<</if>>
-				<</if>>
-			<</if>>
-		<<elseif $incubatorGrowthStimsSetting == 1>>
-			<<if $tanks[_inc].geneMods.NCS == 1>>
-				The monitoring system detects $he is near the expected height for $his @@.orange;NCS@@ condition, so it carefully regulates stimulant injections to @@.yellow;maintain $his current stature.@@
-				<<set $tanks[_inc].height = _heightLimitAge>>
-			<<elseif $tanks[_inc].height > _heightLimitAge>>
-				The monitoring systems detect $he is near the expected height, so it carefully regulates stimulant injections to @@.yellow;maintain $his current stature.@@
-				<<if random(1,10) == 10>>
-					<<if $incubatorUpgradeSpeed == 52>>
-						<<set $tanks[_inc].height += random(1,4)>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<set $tanks[_inc].height += random(1,3)>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<set $tanks[_inc].height += random(1,2)>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<set $tanks[_inc].height += random(0,1)>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<set $tanks[_inc].height += random(0,1)>>
-					<</if>>
-				<</if>>
-			<<else>>
-				The monitoring systems detect $his body is capable of developing more rapidly and @@.green;increase $his growth stimulant dosage.@@
-				<<if $incubatorUpgradeSpeed == 52>>
-					<<set $tanks[_inc].height += random(1,4)>>
-				<<elseif $incubatorUpgradeSpeed == 18>>
-					<<set $tanks[_inc].height += random(1,3)>>
-				<<elseif $incubatorUpgradeSpeed == 9>>
-					<<set $tanks[_inc].height += random(1,2)>>
-				<<elseif $incubatorUpgradeSpeed == 6>>
-					<<set $tanks[_inc].height += random(0,1)>>
-				<<elseif $incubatorUpgradeSpeed == 5>>
-					<<set $tanks[_inc].height += random(0,1)>>
-				<</if>>
-			<</if>>
-		<</if>>
-		<<set $tanks[_inc].height = Math.clamp($tanks[_inc].height, 0, _heightLimit)>>
-	<<else>>
-		<br>
-		With the growth stimulant injections offline, $his body is left to develop naturally.
-	<</if>>
-
-	<<if $incubatorUpgradeReproduction == 1>>
-		<br>
-		<<set _rearQuirk = $tanks[_inc].geneticQuirks.rearLipedema == 2 ? 2 : 1>>
-		<<if $incubatorReproductionSetting == 2>>
-			$His developing body is being flooded with hormones.
-			<<if $incubatorWeightSetting == 1>>
-				Combined with the abundant food provided to $him, $his body grows rapidly.
-				<<if $tanks[_inc].ovaries == 1>>
-					<<set $tanks[_inc].pubertyXX = 1>>
-					<<if $tanks[_inc].hormoneBalance < 500>>
-						<<set $tanks[_inc].hormoneBalance += 100>>
-					<</if>>
-					<<if $seeHyperPreg == 1>>
-						<<set $tanks[_inc].readyOva = random(25,45)>>
-					<<else>>
-						<<set $tanks[_inc].readyOva = random(3,8)>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess estrogen-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].boobs < 8000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 2000>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 50>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips += 2>>
-						<</if>>
-						<<if $tanks[_inc].butt < 12*_rearQuirk && random(1,100) > 30/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt += 4>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].boobs < 8000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 500>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 50>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 12*_rearQuirk && random(1,100) > 50/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt += 3>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].boobs < 8000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 200>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 60>>
-							The excess estrogen-laced growth hormones @@.green;causes $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 12*_rearQuirk && random(1,100) > 50/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear grow fatter.@@
-							<<set $tanks[_inc].butt += 2>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].boobs < 8000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 100>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 70>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 12*_rearQuirk && random(1,100) > 60/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].boobs < 8000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 100>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 80>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 12*_rearQuirk && random(1,100) > 70/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<</if>>
-				<<elseif $tanks[_inc].balls > 0>>
-					<<set $tanks[_inc].pubertyXY = 1>>
-					<<if $tanks[_inc].hormoneBalance > -500>>
-						<<set $tanks[_inc].hormoneBalance -= 100>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess testosterone-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].balls < 40>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 16>>
-						<</if>>
-						<<if $tanks[_inc].dick < 10 && random(1,100) > 20>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick += 4>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].balls < 40 && random(1,100) > 10>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 9>>
-						<</if>>
-						<<if $tanks[_inc].dick < 10 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick += 3>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].balls < 40 && random(1,100) > 20>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 4>>
-						<</if>>
-						<<if $tanks[_inc].dick < 10 && random(1,100) > 50>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick += 2>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].balls < 40 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 2>>
-						<</if>>
-						<<if $tanks[_inc].dick < 10 && random(1,100) > 70>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].balls < 40 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 10 && random(1,100) > 80>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<</if>>
-				<</if>>
-			<<elseif $incubatorWeightSetting == 2>>
-				Combined with the healthy food provided to $him, $his body grows readily.
-				<<if $tanks[_inc].ovaries == 1>>
-					<<set $tanks[_inc].pubertyXX = 1>>
-					<<if $tanks[_inc].hormoneBalance < 500>>
-						<<set $tanks[_inc].hormoneBalance += 100>>
-					<</if>>
-					<<if $seeHyperPreg == 1>>
-						<<set $tanks[_inc].readyOva = random(15,25)>>
-					<<else>>
-						<<set $tanks[_inc].readyOva = random(2,6)>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess estrogen-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].boobs < 4000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 1000>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 70>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 8*_rearQuirk && random(1,100) > 50/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt += 3>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].boobs < 4000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 500>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 80>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 8*_rearQuirk && random(1,100) > 50/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].boobs < 4000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 200>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 90>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 8*_rearQuirk && random(1,100) > 60/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].boobs < 4000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 100>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 95>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 8*_rearQuirk && random(1,100) > 70/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].boobs < 4000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 100>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 95>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 8*_rearQuirk && random(1,100) > 80/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<</if>>
-				<<elseif $tanks[_inc].balls > 0>>
-					<<set $tanks[_inc].pubertyXY = 1>>
-					<<if $tanks[_inc].hormoneBalance > -500>>
-						<<set $tanks[_inc].hormoneBalance -= 100>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess testosterone-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].balls < 10>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 3>>
-						<</if>>
-						<<if $tanks[_inc].dick < 7 && random(1,100) > 20>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick += 2>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].balls < 10 && random(1,100) > 10>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls += 2>>
-						<</if>>
-						<<if $tanks[_inc].dick < 7 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].balls < 10 && random(1,100) > 20>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 7 && random(1,100) > 50>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].balls < 10 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 7 && random(1,100) > 70>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].balls < 10 && random(1,100) > 30>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 7 && random(1,100) > 80>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<</if>>
-				<</if>>
-			<<else>>
-				Since $his body has little to work with, $his growth is fairly minor.
-				<<if $tanks[_inc].ovaries == 1>>
-					<<set $tanks[_inc].pubertyXX = 1>>
-					<<if $tanks[_inc].hormoneBalance < 500>>
-						<<set $tanks[_inc].hormoneBalance += 100>>
-					<</if>>
-					<<if $seeHyperPreg == 1>>
-						<<set $tanks[_inc].readyOva = random(10,15)>>
-					<<else>>
-						<<set $tanks[_inc].readyOva = random(2,4)>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess estrogen-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].boobs < 2000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 700>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 90>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips += 2>>
-						<</if>>
-						<<if $tanks[_inc].butt < 6*_rearQuirk && random(1,100) > 70/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt += 2>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].boobs < 2000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 200>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 80>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 6*_rearQuirk && random(1,100) > 70/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].boobs < 2000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly balloon $his breasts.@@
-							<<set $tanks[_inc].boobs += 50>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 80>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 6*_rearQuirk && random(1,100) > 90/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].boobs < 2000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly grow $his breasts.@@
-							<<set $tanks[_inc].boobs += 20>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 90>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 6*_rearQuirk && random(1,100) > 90/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].boobs < 2000>>
-							The excess estrogen-laced growth hormones @@.green;rapidly grow $his breasts.@@
-							<<set $tanks[_inc].boobs += 10>>
-						<</if>>
-						<<if $tanks[_inc].hips < 2 && random(1,100) > 95>>
-							The excess estrogen-laced growth hormones @@.green;cause $his hips to widen for childbirth.@@
-							<<set $tanks[_inc].hips++>>
-						<</if>>
-						<<if $tanks[_inc].butt < 6*_rearQuirk && random(1,100) > 90/_rearQuirk>>
-							The excess estrogen-laced growth hormones @@.green;cause $his rear to grow fatter.@@
-							<<set $tanks[_inc].butt++>>
-						<</if>>
-					<</if>>
-				<<elseif $tanks[_inc].balls > 0>>
-					<<set $tanks[_inc].pubertyXY = 1>>
-					<<if $tanks[_inc].hormoneBalance > -500>>
-						<<set $tanks[_inc].hormoneBalance -= 100>>
-					<</if>>
-					<<if $tanks[_inc].geneMods.NCS == 1>>
-						/* NCS blocks hormonal growth of all secondary sexual characteristics */
-						$His @@.orange;NCS blocks all growth@@ despite the excess testosterone-laced growth hormones flooding $his body.
-					<<elseif $incubatorUpgradeSpeed == 52>>
-						<<if $tanks[_inc].balls < 6>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to grow for extra cum production.@@
-							<<set $tanks[_inc].balls += 2>>
-						<</if>>
-						<<if $tanks[_inc].dick < 4 && random(1,100) > 60>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 18>>
-						<<if $tanks[_inc].balls < 6 && random(1,100) > 50>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to grow for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 4 && random(1,100) > 60>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 9>>
-						<<if $tanks[_inc].balls < 6 && random(1,100) > 60>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to balloon for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 4 && random(1,100) > 70>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick += 2>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 6>>
-						<<if $tanks[_inc].balls < 6 && random(1,100) > 70>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to grow for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 4 && random(1,100) > 80>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<<elseif $incubatorUpgradeSpeed == 5>>
-						<<if $tanks[_inc].balls < 6 && random(1,100) > 80>>
-							The excess testosterone-laced growth hormones @@.green;cause $his balls to grow for extra cum production.@@
-							<<set $tanks[_inc].balls++>>
-						<</if>>
-						<<if $tanks[_inc].dick < 4 && random(1,100) > 90>>
-							The excess testosterone-laced growth hormones @@.green;cause $his penis to swell.@@
-							<<set $tanks[_inc].dick++>>
-						<</if>>
-					<</if>>
-				<</if>>
-			<</if>>
-		<<elseif $incubatorReproductionSetting == 1>>
-			$His hormone levels are being carefully managed, @@.green;encouraging early puberty.@@
-			<<if $tanks[_inc].ovaries == 1>>
-				<<set $tanks[_inc].pubertyXX = 1>>
-				<<set $tanks[_inc].hormoneBalance = 250>>
-				<<if $tanks[_inc].geneMods.NCS == 1>>
-					/* NCS blocks hormonal growth of all secondary sexual characteristics */
-					$His @@.orange;NCS blocks growth@@ despite the added estrogen.
-				<<else>>
-					<<if $tanks[_inc].boobs < 400 && random(1,100) > 60>>
-						The added estrogen @@.green;causes $his breasts to swell.@@
-						<<set $tanks[_inc].boobs += 50>>
-					<</if>>
-					<<if $tanks[_inc].hips < 2 && random(1,100) > 90>>
-						The added estrogen @@.green;causes $his hips to widen.@@
-						<<set $tanks[_inc].hips++>>
-					<</if>>
-					<<if $tanks[_inc].butt < 5*_rearQuirk && random(1,100) > 80/_rearQuirk>>
-						The added estrogen @@.green;causes $his butt to grow.@@
-						<<set $tanks[_inc].butt++>>
-					<</if>>
-				<</if>>
-			<<elseif $tanks[_inc].balls > 0>>
-				<<set $tanks[_inc].pubertyXY = 1>>
-				<<set $tanks[_inc].hormoneBalance = -250>>
-				<<if $tanks[_inc].geneMods.NCS == 1>>
-					/* NCS blocks hormonal growth of all secondary sexual characteristics */
-					$His @@.orange;NCS blocks all growth@@ despite the added testosterone.
-				<<else>>
-					<<if $tanks[_inc].balls < 3 && random(1,100) > 80>>
-						The added testosterone @@.green;causes $his balls to swell.@@
-						<<set $tanks[_inc].balls++>>
-					<</if>>
-					<<if $tanks[_inc].dick < 3 && random(1,100) > 60>>
-						The added testosterone @@.green;causes $his penis to grow.@@
-						<<set $tanks[_inc].dick++>>
-					<</if>>
-				<</if>>
-			<</if>>
-		<<else>>
-			<<if $tanks[_inc].hormoneBalance > 100>>
-				<<set $tanks[_inc].hormoneBalance -= 50>>
-			<<elseif $tanks[_inc].hormoneBalance < -100>>
-				<<set $tanks[_inc].hormoneBalance += 50>>
-			<</if>>
-			<<if $tanks[_inc].balls > 0>>
-				<<if $tanks[_inc].balls > 1>>
-					<<set $tanks[_inc].balls -= 5>>
-				<</if>>
-				<<if $tanks[_inc].dick > 1>>
-					<<set $tanks[_inc].dick -= 5>>
-				<</if>>
-				<<if $tanks[_inc].balls <= 0>>
-					<<set $tanks[_inc].balls = 1>>
-				<</if>>
-				<<if $tanks[_inc].dick <= 0>>
-					<<set $tanks[_inc].dick = 1>>
-				<</if>>
-			<</if>>
-			<<if $tanks[_inc].boobs > 0>>
-				<<set $tanks[_inc].boobs -= 500>>
-			<</if>>
-			<<if $tanks[_inc].butt > 0>>
-				<<set $tanks[_inc].butt -= 4>>
-			<</if>>
-		<</if>>
-	<<else>>
-		<<if $tanks[_inc].hormoneBalance > 100>>
-			<<set $tanks[_inc].hormoneBalance -= 50>>
-		<<elseif $tanks[_inc].hormoneBalance < -100>>
-			<<set $tanks[_inc].hormoneBalance += 50>>
-		<</if>>
-		<<if $tanks[_inc].balls > 0>>
-			<<if $tanks[_inc].balls > 1>>
-				<<set $tanks[_inc].balls -= 5>>
-			<</if>>
-			<<if $tanks[_inc].dick > 1>>
-				<<set $tanks[_inc].dick -= 5>>
-			<</if>>
-			<<if $tanks[_inc].balls <= 0>>
-				<<set $tanks[_inc].balls = 1>>
-			<</if>>
-			<<if $tanks[_inc].dick <= 0>>
-				<<set $tanks[_inc].dick = 1>>
-			<</if>>
-		<</if>>
-		<<if $tanks[_inc].boobs > 0>>
-			<<set $tanks[_inc].boobs -= 500>>
-		<</if>>
-		<<if $tanks[_inc].butt > 0>>
-			<<set $tanks[_inc].butt -= 4>>
-		<</if>>
-	<</if>>
-
-	<<if $incubatorReproductionSetting == 2>>
-		<<set $tanks[_inc].energy = 80, $tanks[_inc].need = 100>>
-	<<elseif $incubatorReproductionSetting == 1>>
-		<<set $tanks[_inc].energy = 50, $tanks[_inc].need = 20>>
-	<<else>>
-		<<set $tanks[_inc].energy = 0, $tanks[_inc].need = 0>>
-	<</if>>
-
-	<<if (($incubatorPregAdaptationSetting == 1 && $tanks[_inc].genes == "XX") || ($incubatorPregAdaptationSetting == 2 && $tanks[_inc].genes == "XY") || $incubatorPregAdaptationSetting == 3) && $tanks[_inc].growTime > 0>>
-		<br>
-		The incubator is working on adapting $his abdomen and reproductive organs for future pregnancies.
-
-		<<set _weekAdapt = $tanks[_inc].incubatorPregAdaptationInWeek * $incubatorUpgradeSpeed>>
-		<<if isNaN(_weekAdapt)>> /* NaN check AFTER multiply operation, against it result is critical here. Need to be absolutely sure about this operation, not about just tank[x] property itself. This give me two very unpleasant hours to catch this */
-			<<set $tanks[_inc].incubatorPregAdaptationInWeek = (15000 / 2000 - $tanks[_inc].pregAdaptation) / $tanks[_inc].growTime>>
-		<</if>>
-		<<set _weekAdapt = $tanks[_inc].incubatorPregAdaptationInWeek * $incubatorUpgradeSpeed>> /* Now it's should be fine */
-		<<set _weekAdapt *= 1 + ($incubatorReproductionSetting / 5)>>
-		<<set _weekAdapt *= 1 + ($tanks[_inc].hormoneBalance / 1500)>>
-		/*And now we done*/
-		<<set $tanks[_inc].pregAdaptation += _weekAdapt>>
-		/* here goes side effect from intense and extreme settings: */
-		<<if (random(0, 100) <= ($tanks[_inc].incubatorPregAdaptationPower - 1) * ($incubatorUpgradeSpeed / 2 + 1))>>
-			<<switch random(1, 9)>> /*side effect selection*/
-			<<case 1>>
-				<<set $tanks[_inc].preg = -2>>
-				It caused @@.red;reproductive damage@@ when excessive meddling damaged an organ.
-			<<case 2>>
-				<<if $tanks[_inc].ovaries == 1 || $tanks[_inc].mpreg == 1>>
-					<<set $tanks[_inc].preg = -3>>
-				<</if>>
-				<<if $tanks[_inc].balls > 0>>
-					<<set $tanks[_inc].ballType = "sterile">>
-				<</if>>
-				It caused @@.red;severe reproductive damage@@ when excessive hormones shut down the associated organs.
-			<<case 3>>
-				<<set $tanks[_inc].lactation = 1>>
-				It has @@.orange;triggered a hormonal disorder,@@ causing $his breast glands begin producing milk.
-			<<case 4>>
-				<<set $tanks[_inc].bellySag = 100, $tanks[_inc].bellySagPreg = 100>>
-				It activated @@.red;emergency protocols@@ when overpressure to $his abdominal tissues and skin reached critical levels. $His belly has lost muscle tone and has begun to strongly sag.
-			<<case 5>>
-				<<set $tanks[_inc].health.condition -= 15>>
-				Overzealous prodding caused some @@.red;internal bleeding.@@
-			<<case 6>>
-				<<set $tanks[_inc].weight += 50>>
-				An unexpected shift in hormones spurred @@.red;massive weight gain@@ before it could be corrected.
-			<<case 7>>
-				<<set $tanks[_inc].weight -= 50>>
-				An unexpected shift in hormones spurred @@.red;massive weight loss@@ before it could be corrected.
-			<<case 8>>
-				<<set $tanks[_inc].boobs += 5000>>
-				<<set $tanks[_inc].boobShape = "saggy">>
-				An unexpected shift in hormones encouraged @@.green;explosive breast growth@@ before it could be corrected.
-			<<case 9>>
-				<<set $tanks[_inc].hips = 3>>
-				A malfunction in the skeletal reconstruction software caused it to @@.green;overwiden $his hips.@@
-			<</switch>>
-		<</if>>
-	<</if>>
-
-	<<if $tanks[_inc].preg > 0>>
-		@@.red;The incubator is displaying an alert that $he may be pregnant.@@
-	<</if>>
-
-	<<set $tanks[_inc].weight = Math.clamp($tanks[_inc].weight, -100, 200)>>
-	<<set $tanks[_inc].muscles = Math.clamp($tanks[_inc].muscles, -100, 100)>>
-	<<set $tanks[_inc].dick = Math.clamp($tanks[_inc].dick, 0, 10)>>
-	<<set $tanks[_inc].hips = Math.clamp($tanks[_inc].hips, -2, 2)>>
-	<<set $tanks[_inc].balls = Math.clamp($tanks[_inc].balls, 0, 40)>>
-	<<set $tanks[_inc].boobs = Math.clamp($tanks[_inc].boobs, 25, 30000)>>
-	<<set $tanks[_inc].height = Math.clamp($tanks[_inc].height, 0, 274)>>
-	<<set $tanks[_inc].hormoneBalance = Math.clamp($tanks[_inc].hormoneBalance, -500, 500)>>
-	<<set $tanks[_inc].foreskin = $tanks[_inc].dick>> /*simple, clean way of making sure foreskins and scrotums grow appropriately*/
-	<<set $tanks[_inc].scrotum = $tanks[_inc].balls>> /*if we want dicks/balls to outgrow foreskins/scrotums, this will have to be removed*/
-
-<br><br>
-<</for>>
-/*
-<<if $tanks.length == 0>>
-<<= capFirstChar($incubatorName)>> is currently unused.
-<</if>>
-*/
-
-<br><br>
diff --git a/src/pregmod/managePersonalAffairs.tw b/src/pregmod/managePersonalAffairs.tw
index 91c652644efba7938f41f55555e20a6acfaae30f..fb27d0d6a64b42d2849a66064f2f1098474d4c3f 100644
--- a/src/pregmod/managePersonalAffairs.tw
+++ b/src/pregmod/managePersonalAffairs.tw
@@ -5,7 +5,7 @@
 <<set _ML = $marrying.length>>
 
 <<if $cheatMode == 1>>
-	<div class="cheat-menu">[[Cheat Edit Player|PCCheatMenu][$cheater = 1]]</div>
+	<div class="cheat-menu">[[Cheat Edit Player|PCCheatMenu][$cheater = 1, $backupSlave = clone($PC)]]</div>
 <</if>>
 
 <h1>Personal Affairs</h1>
diff --git a/src/pregmod/pRaped.tw b/src/pregmod/pRaped.tw
index cd7b6c242d5504ed04fbe81985ee0aada748c981..1291412f444c2fe4176a7f5efb87b60ab7ec8488 100644
--- a/src/pregmod/pRaped.tw
+++ b/src/pregmod/pRaped.tw
@@ -266,7 +266,7 @@ While returning from a meeting with a prospective investor, an unfortunate wrong
 			<</if>>
 			<<run cashX(forceNeg(_ContractCost), "slaveTransfer", _rapist)>>
 			<<replace "#result">>
-				You declare $his holes fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if _rapist.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+				You declare $his holes fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if _rapist.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<run repX(500, "event")>>
 				<<set $arcologies[0].prosperity += 2>>
 				<<includeDOM App.UI.newSlaveIntro(_rapist)>>
@@ -313,7 +313,7 @@ While returning from a meeting with a prospective investor, an unfortunate wrong
 				<<set _rapist.behavioralFlaw = "odd">>
 				<<run cashX(forceNeg(_ContractCost), "slaveTransfer", _rapist)>>
 				<<replace "#result">>
-					You want $him to suffer for what $he tried to pull on you. You make sure $he is fully awake as $he is strapped into the autosurgery. You take great pleasure in watching $him struggle as $his limbs are taken, one by one. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ After $he has stabilized, it's off to the penthouse for basic slave induction. You'd like to see $him try and touch you again without arms and legs.
+					You want $him to suffer for what $he tried to pull on you. You make sure $he is fully awake as $he is strapped into the autosurgery. You take great pleasure in watching $him struggle as $his limbs are taken, one by one. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ After $he has stabilized, it's off to the penthouse for basic slave induction. You'd like to see $him try and touch you again without arms and legs.
 				<</replace>>
 			<</link>>
 			<<if _rapist.balls > 0>>
diff --git a/src/pregmod/seDeath.tw b/src/pregmod/seDeath.tw
index 86903576de8c43b5e4098d59949ac7968e7ed1ec..0aa3e98c7c554378bcd5712ee0730a4b896521ec 100644
--- a/src/pregmod/seDeath.tw
+++ b/src/pregmod/seDeath.tw
@@ -1,26 +1,5 @@
 :: SE Death [nobr]
 
-<<set $nextButton = "Continue", $nextLink = "Scheduled Event", _killedSlaves = []>>
-<<for _slave range $slaves>>
-	<<switch _slave.death.toLowerCase()>>
-		<<case "old">>
-			<<DeathOldAge _slave>>
-			<br><br><hr style="margin:0"><br>
-			<<set _killedSlaves.push(_slave)>>
-		<<case "od">>
-			<<DeathOverdose _slave>>
-			<br><br><hr style="margin:0"><br>
-			<<set _killedSlaves.push(_slave)>>
-		<<case "health">>
-			<<DeathUnhealthy _slave>>
-			<br><br><hr style="margin:0"><br>
-			<<set _killedSlaves.push(_slave)>>
-	<</switch>>
-<</for>>
-<<if _killedSlaves.length == 0>>
-	/* if something beats this to the slaves, this should kick the player along instead of leaving them at a blank screen */
-	<<goto "Scheduled Event">>
-<</if>>
-<<for _slave range _killedSlaves>>
-	<<= removeSlave(_slave)>>
-<</for>>
\ No newline at end of file
+<<set $nextButton = "Continue", $nextLink = "Scheduled Event">>
+
+<<includeDOM allDeaths()>>
diff --git a/src/pregmod/sePlayerBirth.tw b/src/pregmod/sePlayerBirth.tw
index 45546f8ccd127ff4f5eef5a412c3b1795fb33236..b71305beb0d60f1b8e5218ad0f37ee5258a34bc8 100644
--- a/src/pregmod/sePlayerBirth.tw
+++ b/src/pregmod/sePlayerBirth.tw
@@ -457,7 +457,7 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 		<<elseif _babiesReduced.length > 1>>
 			_babiesReduced[0] and _babiesReduced[1]'s
 		<<else>>
-			_babiesReduced[0]'s
+			_babiesReduced[0]<<if _babiesReduced[0] != "your own" && _babiesReduced[0] != "designer">>'s<</if>>
 		<</if>>
 		babies into the world.
 
@@ -564,7 +564,7 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 	<</if>>
 
 	<<if _wounded == 1>>
-		Things didn't quite go as planned, leaving you @@.red;weak and wounded.@@ You'll need a couple weeks to recover from the ordeal before you're back on your feet.
+		Things didn't quite go as planned, leaving you @@.health.dec;weak and wounded.@@ You'll need a couple weeks to recover from the ordeal before you're back on your feet.
 		<<run healthDamage($PC, 40)>>
 	<</if>>
 
diff --git a/src/pregmod/slaveOnSlaveFeedingWorkAround.tw b/src/pregmod/slaveOnSlaveFeedingWorkAround.tw
deleted file mode 100644
index e07be1d49ed61a3050e8cb15f02fbd32c9e345bb..0000000000000000000000000000000000000000
--- a/src/pregmod/slaveOnSlaveFeedingWorkAround.tw
+++ /dev/null
@@ -1,284 +0,0 @@
-:: SlaveOnSlaveFeedingWorkAround [nobr]
-
-<<set $nextButton = "Back", $nextLink = "Slave Interact">>
-
-<<set $milkTap = 0, _descM = "milk", _descC = "cum", _eligibilityMilk2 = 0, _eligibilityMilk4 = 0, _eligibilityMilk8 = 0, _eligibilityCum2 = 0, _eligibilityCum4 = 0, _eligibilityCum8 = 0>>
-
-<<for _i = 0; _i < $slaves.length; _i++>> /* milk output */
-	<<if $slaves[_i].lactation > 0>>
-		<<set $slaves[_i].milkOutput = 0>>
-		<<set _milk = milkAmount($slaves[_i])>>
-		<<set _milk = (_milk/14)>>
-		<<set _milk = Math.trunc(_milk)>>
-		<<set $slaves[_i].milkOutput = _milk>>
-	<<else>>
-		<<set _milk = 0>>
-		<<set $slaves[_i].milkOutput = 0>>
-	<</if>>
-<</for>>
-<<for _i = 0; _i < $slaves.length; _i++>> /* cum output */
-	<<if $slaves[_i].balls > 0 && $slaves[_i].dick > 0 && $slaves[_i].chastityPenis != 1>>
-		<<set $slaves[_i].cumOutput = 0>>
-		<<set _cum = cumAmount($slaves[_i])>>
-		<<set _cum = (_cum/7)>>
-		<<set _cum = (_cum/10)>>
-		<<set _cum = Math.trunc(_cum)>>
-		<<set $slaves[_i].cumOutput = _cum>>
-	<<else>>
-		<<set _cum = 0>>
-		<<set $slaves[_i].cumOutput = 0>>
-	<</if>>
-<</for>>
-
-<p class="scene-intro">
-	<<= getSlave($AS).slaveName>> is prepped to drink $his fill; now you must select a slave capable of producing the required amount of milk or ejaculate.
-</p>
-
-<h2>Select an eligible slave to serve as the tap</h2>
-
-<h3>Milk Slaves</h3>
-<table width=90%>
-<tr>
-	<th>
-		2 Liters
-	</th>
-	<th>
-		4 Liters
-	</th>
-	<th>
-		8 Liters
-	</th>
-</tr>
-<tr valign="top">
-	/* 2 Liters */
-	<td>
-		<<for _i = 0; _i < $slaves.length; _i++>>
-			<div>
-				<<if $slaves[_i].milkOutput >= 2>>
-					<<if ($slaves[_i].ID != getSlave($AS).ID) && $slaves[_i].nipples != "fuckable">>
-						<<set _name = SlaveFullName($slaves[_i])>>
-						<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 1, getSlave($AS).inflationType = _descM, getSlave($AS).inflationMethod = 3]]">>
-						<<if relativeTerm(getSlave($AS), $slaves[_i]) != null>>
-							<<print relativeTerm(getSlave($AS), $slaves[_i])>>
-						<</if>>
-						<<if $slaves[_i].relationshipTarget == getSlave($AS).ID>>
-							<<switch $slaves[_i].relationship>>
-							<<case 1>>
-								friends
-							<<case 2>>
-								best friends
-							<<case 3>>
-								friends with benefits
-							<<case 4>>
-								lover
-							<<case 5>>
-								slave $wife
-							<</switch>>
-						<</if>>
-						<<if $slaves[_i].rivalryTarget == getSlave($AS).ID>>
-							<<switch $slaves[_i].relationship>>
-							<<case 1>>
-								dislikes
-							<<case 2>>
-								rival
-							<<case 3>>
-								bitterly hates
-							<</switch>>
-						<</if>>
-						<<set _eligibilityMilk2 = 1>>
-					<</if>>
-				<</if>>
-			</div>
-		<</for>>
-		<<if (_eligibilityMilk2 == 0)>>
-			<div class="note">
-				You have no slaves capable of producing two liters of milk.
-			</div>
-		<</if>>
-	</td>
-
-	<<if getSlave($AS).pregKnown == 0>>
-		/* 4 Liters */
-		<td>
-			<<for _i = 0; _i < $slaves.length; _i++>>
-				<div>
-					<<if $slaves[_i].milkOutput >= 4>>
-						<<if ($slaves[_i].ID != getSlave($AS).ID) && $slaves[_i].nipples != "fuckable">>
-							<<set _name = SlaveFullName($slaves[_i])>>
-							<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 2, getSlave($AS).inflationType = _descM, getSlave($AS).inflationMethod = 3]]">>
-							<<if relativeTerm(getSlave($AS), $slaves[_i]) != null>>
-								<<print relativeTerm(getSlave($AS), $slaves[_i])>>
-							<</if>>
-							<<if $slaves[_i].relationshipTarget == getSlave($AS).ID>>
-								<<switch $slaves[_i].relationship>>
-								<<case 1>>
-									friends
-								<<case 2>>
-									best friends
-								<<case 3>>
-									friends with benefits
-								<<case 4>>
-									lover
-								<<case 5>>
-									slave $wife
-								<</switch>>
-							<</if>>
-							<<if $slaves[_i].rivalryTarget == getSlave($AS).ID>>
-								<<switch $slaves[_i].relationship>>
-								<<case 1>>
-									dislikes
-								<<case 2>>
-									rival
-								<<case 3>>
-									bitterly hates
-								<</switch>>
-							<</if>>
-							<<set _eligibilityMilk4 = 1>>
-						<</if>>
-					<</if>>
-				</div>
-			<</for>>
-			<<if (_eligibilityMilk4 == 0)>>
-				<div class="note">
-					You have no slaves capable of producing four liters of milk.
-				</div>
-			<</if>>
-		</td>
-
-		/* 8 Liters */
-		<td>
-			<<for _i = 0; _i < $slaves.length; _i++>>
-				<div>
-					<<if $slaves[_i].milkOutput >= 8>>
-						<<if ($slaves[_i].ID != getSlave($AS).ID) && $slaves[_i].nipples != "fuckable">>
-							<<set _name = SlaveFullName($slaves[_i])>>
-							<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 3, getSlave($AS).inflationType = _descM, getSlave($AS).inflationMethod = 3]]">>
-							<<if relativeTerm(getSlave($AS), $slaves[_i]) != null>>
-								<<print relativeTerm(getSlave($AS), $slaves[_i])>>
-							<</if>>
-							<<if $slaves[_i].relationshipTarget == getSlave($AS).ID>>
-								<<switch $slaves[_i].relationship>>
-								<<case 1>>
-									friends
-								<<case 2>>
-									best friends
-								<<case 3>>
-									friends with benefits
-								<<case 4>>
-									lover
-								<<case 5>>
-									slave $wife
-								<</switch>>
-							<</if>>
-							<<if $slaves[_i].rivalryTarget == getSlave($AS).ID>>
-								<<switch $slaves[_i].relationship>>
-									<<case 1>>
-										dislikes
-									<<case 2>>
-										rival
-									<<case 3>>
-										bitterly hates
-								<</switch>>
-							<</if>>
-							<<set _eligibilityMilk8 = 1>>
-						<</if>>
-					<</if>>
-				</div>
-			<</for>>
-			<<if (_eligibilityMilk8 == 0)>>
-				<div class="note">
-					You have no slaves capable of producing eight liters of milk.
-				</div>
-			<</if>>
-		</td>
-	<<else>>
-		<td>
-			Due to $his pregnancy, $he is incapable of keeping down more than two liters of milk.
-		</td>
-	<</if>>
-</tr>
-</table>
-
-<h3>Cum Slaves</h3>
-<table width=90%>
-	<tr>
-		<th>
-			2 Liters
-		</th>
-		<th>
-			4 Liters
-		</th>
-		<th>
-			8 Liters
-		</th>
-	</tr>
-
-	<tr valign="top">
-		/* 2 Liters */
-		<td>
-			<<for _i = 0; _i < $slaves.length; _i++>>
-				<div>
-					<<if $slaves[_i].cumOutput >= 2>>
-						<<if ($slaves[_i].ID != getSlave($AS).ID)>>
-							<<set _name = SlaveFullName($slaves[_i])>>
-							<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 1, getSlave($AS).inflationType = _descC, getSlave($AS).inflationMethod = 3]]">>
-							<<set _eligibilityCum2 = 1>>
-						<</if>>
-					<</if>>
-				</div>
-			<</for>>
-			<<if (_eligibilityCum2 == 0)>>
-				<div class="note">
-					You have no slaves capable of producing two liters of cum.
-				</div>
-			<</if>>
-		</td>
-
-		<<if getSlave($AS).pregKnown == 0>>
-			/* 4 Liters */
-			<td>
-				<<for _i = 0; _i < $slaves.length; _i++>>
-					<div>
-						<<if $slaves[_i].cumOutput >= 4>>
-							<<if ($slaves[_i].ID != getSlave($AS).ID)>>
-								<<set _name = SlaveFullName($slaves[_i])>>
-								<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 2, getSlave($AS).inflationType = _descC, getSlave($AS).inflationMethod = 3]]">>
-								<<set _eligibilityCum4 = 1>>
-							<</if>>
-						<</if>>
-					</div>
-				<</for>>
-
-				<<if (_eligibilityCum4 == 0)>>
-					<div class="note">
-						You have no slaves capable of producing four liters of cum.
-					</div>
-				<</if>>
-			</td>
-
-			/* 8 Liters */
-			<td>
-				<<for _i = 0; _i < $slaves.length; _i++>>
-					<div>
-						<<if $slaves[_i].cumOutput >= 8>>
-							<<if ($slaves[_i].ID != getSlave($AS).ID)>>
-								<<set _name = SlaveFullName($slaves[_i])>>
-								<<print "[[_name|FSlaveFeed][$milkTap = $slaves[" + _i + "], getSlave($AS).inflation = 3, getSlave($AS).inflationType = _descC, getSlave($AS).inflationMethod = 3]]">>
-								<<set _eligibilityCum8 = 1>>
-							<</if>>
-						<</if>>
-					</div>
-				<</for>>
-				<<if (_eligibilityCum8 == 0)>>
-					<div class="note">
-						You have no slaves capable of producing eight liters of cum.
-					</div>
-				<</if>>
-			</td>
-		<<else>>
-			<td>
-				Due to $his pregnancy, $he is incapable of keeping down more than two liters of cum.
-			</td>
-		<</if>>
-	</tr>
-</table>
diff --git a/src/pregmod/theBlackMarket.tw b/src/pregmod/theBlackMarket.tw
index 655741be2a54c2e23ae45dda79986aa21977838a..0cfef465f7316a0332d22e86327d0528c68d126e 100644
--- a/src/pregmod/theBlackMarket.tw
+++ b/src/pregmod/theBlackMarket.tw
@@ -63,7 +63,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 75000>>
 								[[Purchase schematics for anal pregnancy|The Black Market][cashX(-75000, "capEx"), $arcologies[0].FSGenderRadicalistResearch = 1, _dump = $merchantFSWares.delete("GenderRadicalistResearch")]] //@@.yellowgreen;<<print cashFormat(75000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(75000)>>@@ for anal pregnancy schematics. "No discounts. No haggling. No price naming. Take it or leave it."
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(75000)>>@@ for anal pregnancy schematics. "No discounts. No haggling. No price naming. Take it or leave it."
 							<</if>>
 						<<else>>
 							You lack the facilities needed to grow organs, so anal pregnancy schematics are currently out of your reach.
@@ -80,7 +80,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 90000>>
 								[[Purchase the recipe for a proven intelligence boosting compound|The Black Market][cashX(-90000, "capEx"), $arcologies[0].FSSlaveProfessionalismResearch = 1, _dump = $merchantFSWares.delete("SlaveProfessionalismResearch")]] //@@.yellowgreen;<<print cashFormat(90000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(90000)>>@@ for a proven intelligence boosting compound. "A cup of tea brewed from this compound makes one smarter. Too bad I don't hand out free samples so people like you can remember where they left their money."
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(90000)>>@@ for a proven intelligence boosting compound. "A cup of tea brewed from this compound makes one smarter. Too bad I don't hand out free samples so people like you can remember where they left their money."
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce drugs of this complexity, so the recipe for a proven intelligence boosting compound is currently unobtainable.
@@ -95,7 +95,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 50000>>
 								[[Purchase designs for immense implants|The Black Market][cashX(-50000, "capEx"), $arcologies[0].FSTransformationFetishistResearch = 1, _dump = $merchantFSWares.delete("TransformationFetishistResearch")]] //@@.yellowgreen;<<print cashFormat(50000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(50000)>>@@ for absurdly huge implant designs. "No big fake boobs for you. How sad it must be to be flat and enslaved by such a poor <<if $PC.title == 1>>Master<<else>>Mistress<</if>>"
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(50000)>>@@ for absurdly huge implant designs. "No big fake boobs for you. How sad it must be to be flat and enslaved by such a poor <<if $PC.title == 1>>Master<<else>>Mistress<</if>>"
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce implants of this caliber, so absurdly huge implant designs are currently unobtainable.
@@ -110,7 +110,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 50000>>
 								[[Purchase formulas for extremely powerful growth drugs|The Black Market][cashX(-50000, "capEx"), $arcologies[0].FSAssetExpansionistResearch = 1, _dump = $merchantFSWares.delete("AssetExpansionistResearch")]] //@@.yellowgreen;<<print cashFormat(50000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(50000)>>@@ for extremely powerful growth drug formulas. "Why'd you even come here if you didn't have the credits to buy anything?"
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(50000)>>@@ for extremely powerful growth drug formulas. "Why'd you even come here if you didn't have the credits to buy anything?"
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce drugs of this complexity, so absurdly powerful implant growth drug formulas are currently unobtainable.
@@ -125,7 +125,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 75000>>
 								[[Purchase formulas for growth reversing drugs|The Black Market][cashX(-75000, "capEx"), $arcologies[0].FSSlimnessEnthusiastResearch = 1, _dump = $merchantFSWares.delete("SlimnessEnthusiastResearch")]] //@@.yellowgreen;<<print cashFormat(75000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(75000)>>@@ for growth reversing drug formulas. "<<if $PC.boobs >= 1000>>Guess you'll be keeping those knockers for now, <<if $PC.title == 0>>Little-Miss-Top-Heavy<<else>>Mr. Busty<</if>><<else>>Guess you don't mind sore backs. Leads to less sore knees, from what I've heard<</if>>."
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(75000)>>@@ for growth reversing drug formulas. "<<if $PC.boobs >= 1000>>Guess you'll be keeping those knockers for now, <<if $PC.title == 0>>Little-Miss-Top-Heavy<<else>>Mr. Busty<</if>><<else>>Guess you don't mind sore backs. Leads to less sore knees, from what I've heard<</if>>."
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce drugs of this complexity, so growth reversing drug formulas are currently unobtainable.
@@ -140,7 +140,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 45000>>
 								[[Purchase formulas for extremely effective anti-aging beauty creams|The Black Market][cashX(-45000, "capEx"), $arcologies[0].FSYouthPreferentialistResearch = 1, _dump = $merchantFSWares.delete("YouthPreferentialistResearch")]] //@@.yellowgreen;<<print cashFormat(45000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(45000)>>@@ for extremely effective anti-aging beauty creams. "<<if $PC.visualAge > 40>>Those wrinkles don't look that bad on you, so don't worry about not being able to afford this<<else>>Just tell them they look young, and, well, don't give them any mirrors. Probably should keep your money troubles from them, too<</if>>."
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(45000)>>@@ for extremely effective anti-aging beauty creams. "<<if $PC.visualAge > 40>>Those wrinkles don't look that bad on you, so don't worry about not being able to afford this<<else>>Just tell them they look young, and, well, don't give them any mirrors. Probably should keep your money troubles from them, too<</if>>."
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce drugs of this complexity, so extremely effective anti-aging beauty creams formulas are currently unobtainable.
@@ -155,7 +155,7 @@ Of all the wonders present, the thing that catches your eye the most is a shady
 							<<if $cash >= 65000>>
 								[[Purchase recipes for highly addictive solid slave food|The Black Market][cashX(-65000, "capEx"), $arcologies[0].FSHedonisticDecadenceResearch = 1, _dump = $merchantFSWares.delete("HedonisticDecadenceResearch")]] //@@.yellowgreen;<<print cashFormat(65000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(65000)>>@@ for highly addictive solid slave food recipes. "I promise you'll not be able to recreate this at home, so if you want to give your begging slave the treat she deserves, buy now! Or, you know, come back with money."
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(65000)>>@@ for highly addictive solid slave food recipes. "I promise you'll not be able to recreate this at home, so if you want to give your begging slave the treat she deserves, buy now! Or, you know, come back with money."
 							<</if>>
 						<<else>>
 							You lack the facilities needed to produce drugs of this complexity, so highly addictive solid slave food recipes are currently unobtainable.
@@ -214,7 +214,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 										[[Purchase childhood fertility induced NCS|The Black Market][cashX(-135000, "capEx"), $arcologies[0].childhoodFertilityInducedNCSResearch = 1, _dump = $merchantFSWares.delete("childFertilityInducedSyndromeX")]] //@@.yellowgreen;<<print cashFormat(135000)>>.@@//<br>
 									<<else>>
 										"_toyDolls _match _noRefunds Or, you know, come back with money."<br>
-										You cannot afford the asking price of @@.red;<<print cashFormat(135000)>>@@ for the Childhood Fertility @@.orange;Induced NCS@@ (genetic engineering and hormonal blend) research recipe.<br>
+										You cannot afford the asking price of @@.cash.dec;<<print cashFormat(135000)>>@@ for the Childhood Fertility @@.orange;Induced NCS@@ (genetic engineering and hormonal blend) research recipe.<br>
 									<</if>>
 									He notices your interest and lets you read the information <<= App.Encyclopedia.Dialog.linkSC("Childhood Fertility Induced NCS", "Childhood Fertility Induced NCS")>>.
 								<<else>>
@@ -235,7 +235,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 									<<if $cash >= 20000>>
 										[[Purchase blueprints for a supportive uterine mesh|The Black Market][cashX(-20000, "capEx"), $UterineRestraintMesh = 1, _dump = $merchantIllegalWares.delete("UterineRestraintMesh")]] //@@.yellowgreen;<<print cashFormat(20000)>>.@@//
 									<<else>>
-										You cannot afford the asking price of @@.red;<<print cashFormat(20000)>>@@ for a supportive uterine mesh.
+										You cannot afford the asking price of @@.cash.dec;<<print cashFormat(20000)>>@@ for a supportive uterine mesh.
 									<</if>>
 									<br>"This is an interesting one... It's designed to prevent any sort of rupturing of the uterus, but, while that idea is great and all, it does jack shit to prevent leaks from elsewhere in the organ. The guy funding the research company was pissed when his slave bloated up like a cum-filled balloon and dropped dead, destroyed most of the development lab. Fortunately, he failed to ruin the best part of it — these blueprints. Now, you're probably wondering what good is something like this, but I've done business with a number of industrial slave farms, and they swear upon its ability to force a girl to carry far more children than physically possible; well, up until their wombs crushed their organs, that is. I supposed it'd work with anything solid, really, if you enjoy sticking things up into slave girls."
 								<<else>>
@@ -261,7 +261,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 									<<if $cash >= 20000>>
 										[[Purchase documents regarding the broodmother implant firmware hack|The Black Market][cashX(-20000, "capEx"), $PGHack = 1, _dump = $merchantIllegalWares.delete("PGHack")]] //@@.yellowgreen;<<print cashFormat(20000)>>.@@//
 									<<else>>
-										You cannot afford the asking price of @@.red;<<print cashFormat(20000)>>@@ for the broodmother implant firmware data.
+										You cannot afford the asking price of @@.cash.dec;<<print cashFormat(20000)>>@@ for the broodmother implant firmware data.
 									<</if>>
 									<br>"I might have something for you, if you're interested. Some time ago a hacker manage to crack the firmware of the publicly available broodmother implant and found how to manipulate it through the radio channel normally used for monitoring. His goal was to take revenge on an arcology owner whose beloved concubine had this implant, but this plan failed as he couldn't hide his hacking attempt; to send something to the implant you need to be very close to it. In fact, you need a special actuator inserted up to the cervix, which just so happens to be included in this deal. It is very much advised to use it from within a surgical suite, in case of complications, such as the sudden activation of the birthing process. But anyway, the hack, if applied appropriately, can interfere with the original logic of the implant, forcing it to release more than one ovum every week. Originally, the hacker made it about a dozen, but after we tweaked his initial code, now it will be just two or three ova. You will get all the required data and schematics for the autosurgery upgrade, but you will need to make and install it on your own."
 								<<else>>
@@ -285,7 +285,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 							<<if $cash >= 120000 && !($arcologies[0].FSRepopulationFocus >= 60)>>
 								[[Purchase documents regarding an incubator pregnancy adaptation module|The Black Market][cashX(-120000, "capEx"), $BlackmarketPregAdaptation = 1, _dump = $merchantIllegalWares.delete("BlackmarketPregAdaptation")]] //@@.yellowgreen;<<print cashFormat(120000)>>.@@//
 							<<elseif $cash < 120000>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(120000)>>@@ for documentation on an incubator pregnancy adaptation module.
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(120000)>>@@ for documentation on an incubator pregnancy adaptation module.
 							<</if>>
 							<br>"I'm not sure about this one, but you might still be interested. Let me just begin with a story... some time ago, there was a man with a very pregnant girl in tow. Not so unusual in these days, but what made it really stand out was the girl's age — she couldn't have been any older then six! And looked ready to birth full sized triplets, no less. To top it all off, she did not look stressed by it at all, which was just amazing, given her size. The man said that he invented some sort of subsystem for those modern incubators to prep the occupant's body in a special way, and that this girl was a test subject. I do not understand much about this technical stuff, but the documentation seems legit enough. If you like, I can sell it to you; no guarantees though, on either the construction or the results."
 							<<if $arcologies[0].FSRepopulationFocus >= 60>>
@@ -315,7 +315,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 							<<if $cash >= 70000>>
 								[[Purchase formulas for elasticity increasing injections|The Black Market][cashX(-70000, "capEx"), $RapidCellGrowthFormula = 1, _dump = $merchantIllegalWares.delete("RapidCellGrowthFormula")]] //@@.yellowgreen;<<print cashFormat(70000)>>.@@//
 							<<else>>
-								You cannot afford the asking price of @@.red;<<print cashFormat(70000)>>@@ for elasticity increasing injections.
+								You cannot afford the asking price of @@.cash.dec;<<print cashFormat(70000)>>@@ for elasticity increasing injections.
 							<</if>>
 							<br>"These injections will loosen up any skin, muscle, organ or whatever living flesh you inject them into. I'm not entirely sure how they work, something about increased cell growth or something. Probably not the safest thing to use, what with it pretty much being cancer in a vial. From what I've gathered, they were originally being developed to use with fillable breast implants. Some rich investor got his rocks off from BE and decided to make his dream a reality. Worked great too, save for the fact that the breasts didn't shrink down when the implant was emptied. Yep, she was left with a big ol' pair of floppy tits after being stretched so much. My take is, if you want to get big, fast, this is the drug for you, but only if you don't care about ever going back."
 						<<else>>
@@ -331,7 +331,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 								<<if $cash >= 50000>>
 									[[Purchase schematics for implants that synchronize ova release|The Black Market][cashX(-50000, "capEx"), $sympatheticOvaries = 1, _dump = $merchantIllegalWares.delete("sympatheticOvaries")]] //@@.yellowgreen;<<print cashFormat(50000)>>.@@//
 								<<else>>
-									You cannot afford the asking price of @@.red;<<print cashFormat(50000)>>@@ for implants that synchronize ova release.
+									You cannot afford the asking price of @@.cash.dec;<<print cashFormat(50000)>>@@ for implants that synchronize ova release.
 								<</if>>
 								<br>"This pair of implants attaches directly to a girl's ovaries and uses signals to communicate with each other. When one releases an egg, the other is spurred to do the same — in other words, guaranteed twins, always. Now, you're probably wondering why I have this for sale and not one of the big names; well what do you think happens when you mix fertility drugs, or hell just a girl prone to twins, and something that effectively doubles egg counts? That's right: she gets really, really pregnant, like, dangerously so. Kind of boring compared to some of the other stuff I get in, to be honest. Though I sometimes wonder what would happen if you got multiple slaves with these implanted and kept them close to each other... Would one ovulating trigger every other implant to do the same?"
 							<<else>>
@@ -353,7 +353,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 								<<if $cash >= 80000>>
 									[[Purchase designs for asexually reproducing ovaries|The Black Market][cashX(-80000, "capEx"), $asexualReproduction = 1, _dump = $merchantIllegalWares.delete("asexualReproduction")]] //@@.yellowgreen;<<print cashFormat(80000)>>.@@//
 								<<else>>
-									You cannot afford the asking price of @@.red;<<print cashFormat(80000)>>@@ for asexually reproducing ovaries.
+									You cannot afford the asking price of @@.cash.dec;<<print cashFormat(80000)>>@@ for asexually reproducing ovaries.
 								<</if>>
 								<br>"Ever wanted kids but were too lazy to even try? Then this is the modification for you! Just replace your slave's existing ovaries with these bad girls and she'll never be without child again! Sure there might be a little bit of inbreeding going on, given that she'd be both the mother and father and all that, and sure she might uncontrollably orgasm herself into a coma from trying to fertilize her own eggs, but think of all the time you'd save from not fucking her! Now, why it was designed that way I couldn't tell you, but from what I hear it's quite the show to watch a girl squirming in constant orgasm as she impregnates herself."
 							<<else>>
@@ -382,7 +382,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 										<br>
 									<</if>>
 								<<else>>
-									You cannot afford the asking price of @@.red;<<print cashFormat(25000)>>@@ for animal ovaries.
+									You cannot afford the asking price of @@.cash.dec;<<print cashFormat(25000)>>@@ for animal ovaries.
 								<</if>>
 								<<if $cash >= 25000>>
 									<<if $animalTesticles == 0>>
@@ -394,7 +394,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 										<br>
 									<</if>>
 								<<else>>
-									You cannot afford the asking price of @@.red;<<print cashFormat(25000)>>@@ for animal testicles.
+									You cannot afford the asking price of @@.cash.dec;<<print cashFormat(25000)>>@@ for animal testicles.
 								<</if>>
 								<<if $cash >= 25000>>
 									<<if $animalMpreg == 0>>
@@ -406,7 +406,7 @@ He gestures to a door in the back of the stall. "The good shit's back there<<if
 										<br>
 									<</if>>
 								<<else>>
-									You cannot afford the asking price of @@.red;<<print cashFormat(25000)>>@@ for animal anal wombs and ovaries.
+									You cannot afford the asking price of @@.cash.dec;<<print cashFormat(25000)>>@@ for animal anal wombs and ovaries.
 								<</if>>
 								/* TODO: flesh this out some more */
 								<br>"Got something real special this week. These are schematics for implanting non-human organs into humans. My supplier told me they came from some military experiments or something — maybe they were trying to make some kind of super soldier. Not my business, though."
diff --git a/src/pregmod/widgets/bodySwapReaction.tw b/src/pregmod/widgets/bodySwapReaction.tw
index b2629c3363d7958ac408770931417b2c5877fe52..0398f3bf685c9f16ffe477f004887ad3763aaa1a 100644
--- a/src/pregmod/widgets/bodySwapReaction.tw
+++ b/src/pregmod/widgets/bodySwapReaction.tw
@@ -16,9 +16,9 @@
 <<set _end = 0>>
 
 <<if $args[0].health.health >= $args[1].health.health+10>>
-	The monitors indicate that $his @@.green;health has improved@@ from $his previous body. Whatever else happens, $he will likely appreciate this.
+	The monitors indicate that $his @@.health.inc;health has improved@@ from $his previous body. Whatever else happens, $he will likely appreciate this.
 <<elseif $args[0].health.health <= $args[1].health.health-10>>
-	The monitors indicate that $his @@.red;health has degraded@@ from $his previous body. Whatever else happens, this will likely upset $him.
+	The monitors indicate that $his @@.health.dec;health has degraded@@ from $his previous body. Whatever else happens, this will likely upset $him.
 <</if>>
 Now you only have to wait for $him to wake up.
 
diff --git a/src/pregmod/widgets/bodyswapWidgets.tw b/src/pregmod/widgets/bodyswapWidgets.tw
index 54c9f0caa16a6ac6d0cd3571d7a3d0a9131260b4..bd5cd97a0e398f7caa1c0675d81c48b203c3fd86 100644
--- a/src/pregmod/widgets/bodyswapWidgets.tw
+++ b/src/pregmod/widgets/bodyswapWidgets.tw
@@ -195,8 +195,8 @@
 	<<set $args[0].dietMilk = $args[1].dietMilk>>
 	<<set $args[0].clothes = $args[1].clothes>>
 	<<set $args[0].collar = $args[1].collar>>
-	<<set $args[0].faceAccessory  = $args[1].faceAccessory>>
-	<<set $args[0].mouthAccessory  = $args[1].mouthAccessory>>
+	<<set $args[0].faceAccessory = $args[1].faceAccessory>>
+	<<set $args[0].mouthAccessory = $args[1].mouthAccessory>>
 	<<set $args[0].shoes = $args[1].shoes>>
 	<<set $args[0].makeup = $args[1].makeup>>
 	<<set $args[0].nails = $args[1].nails>>
diff --git a/src/pregmod/widgets/deathWidgets.tw b/src/pregmod/widgets/deathWidgets.tw
deleted file mode 100644
index 18a1d9dbc5a852ee6bd8e17d4e52630695289d9a..0000000000000000000000000000000000000000
--- a/src/pregmod/widgets/deathWidgets.tw
+++ /dev/null
@@ -1,127 +0,0 @@
-:: death widgets [widget nobr]
-
-<<widget "DeathOldAge">>
-	/* 000-250-006 */
-	<<if $seeImages && $seeReportImages>>
-		<div class="imageRef tinyImg">
-			<<= SlaveArt($args[0], 0, 0)>>
-		</div>
-	<</if>>
-	/* 000-250-006 */
-	<<setLocalPronouns $args[0]>>
-	<<if $args[0].assignment == "be confined in the arcade">>
-		You are notified by $arcadeName staff that one of the cabinets has broken down and will need to be replaced. It would seem ''@@.pink;$args[0].slaveName@@'', the fucktoy encased in it, died <<if $args[0].physicalAge >= 70>>naturally of old age despite<<else>>suddenly, unrelated to<</if>> $his living conditions. $He was a good unit; logs show $he was taking dick up until the very end.
-	<<elseif $args[0].assignment == "work in the dairy" && $dairyRestraintsSetting > 1>>
-		You are notified by $dairyName staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem ''@@.pink;$args[0].slaveName@@'', the cow restrained in it, died <<if $args[0].physicalAge >= 70>>naturally of old age despite<<else>>suddenly, unrelated to<</if>> $his living conditions. $He was a good cow; $he gave milk up until $his death.
-	<<elseif $args[0].fuckdoll > 0>>
-		One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem ''@@.pink;$args[0].slaveName@@'' has died <<if $args[0].physicalAge >= 70>>naturally of old age despite<<else>>suddenly, unrelated to<</if>> $his living conditions. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally.
-	<<else>>
-		''@@.pink;$args[0].slaveName@@'' failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.
-		<<set _deathSeed = random(1,100)>>
-		<<if _deathSeed > 75>>
-			$He is found dead in $his bed, having died sometime during the previous night.
-		<<elseif _deathSeed > 50>>
-			$He is found dead in a stairwell, having had a heart attack while trying to climb it.
-		<<elseif _deathSeed > 25>>
-			$He is found dead in the showers, having slipped sometime earlier.
-		<<else>>
-			<<if App.Utils.hasNonassignmentSex($args[0])>>
-				$He is found dead in the bed of another slave, having died during intercourse. $His lover is not taking it well.
-			<<else>>
-				$He is found dead in the cafeteria, having died during breakfast; $he ruined the day for a number of your slaves.
-			<</if>>
-		<</if>>
-		$args[0].slaveName died at the age of $args[0].actualAge;
-		<<if $args[0].actualAge < 10>>
-			$he lived a tragically short life.
-		<<elseif $args[0].actualAge < 20>>
-			$he died far too young.
-		<<elseif $args[0].actualAge < 30>>
-			$he died in what would be a sex slave's prime.
-		<<elseif $args[0].actualAge < 50>>
-			$he died in $his prime.
-		<<elseif $args[0].actualAge < 65>>
-			$he lived a fair life, good or bad.
-		<<elseif $args[0].actualAge < 90>>
-			$he lived a long life and experienced much during it.
-		<<else>>
-			$he lived a very long life that few get to see.
-		<</if>>
-	<</if>>
-<</widget>>
-
-<<widget "DeathOverdose">>
-	<<setLocalPronouns $args[0]>>
-	/* 000-250-006 */
-	<<if $seeImages && $seeReportImages>>
-		<div class="imageRef tinyImg">
-			<<= SlaveArt($args[0], 0, 0)>>
-		</div>
-	<</if>>
-	/* 000-250-006 */
-	<<if $args[0].assignment == "be confined in the arcade">>
-		You are notified by $arcadeName staff that one of the cabinets has broken down and will need to be replaced. It would seem ''@@.pink;$args[0].slaveName@@'', the fucktoy encased in it, died of an aphrodisiac overdose from the constant aphrodisiac injections. $He was a good unit; logs show $he was taking dick up until the very end.
-	<<elseif $args[0].assignment == "work in the dairy" && $dairyRestraintsSetting > 1>>
-		You are notified by $dairyName staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem ''@@.pink;$args[0].slaveName@@'', the cow restrained in it, died of an aphrodisiac overdose. How $he managed to get them is unknown, but $he was a good cow; $he gave milk up until $his death.
-	<<elseif $args[0].fuckdoll > 0>>
-		One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem ''@@.pink;$args[0].slaveName@@'' has died of an aphrodisiac overdose. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally. It does little to deal with the resulting mess of the orgasm $he died during, however.
-	<<else>>
-		''@@.pink;$args[0].slaveName@@'' failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.
-		$He is found dead in $his bed, having died sometime earlier. Judging by the mess and the expression on $his face, $he died of a heart attack during a particularly intense orgasm bought about by the massive amount of aphrodisiacs in $his system. $args[0].slaveName died at the age of $args[0].actualAge;
-		<<if $args[0].actualAge < 10>>
-			$he lived a tragically short life.
-		<<elseif $args[0].actualAge < 20>>
-			$he died far too young.
-		<<elseif $args[0].actualAge < 30>>
-			$he died in what would be a sex slave's prime.
-		<<elseif $args[0].actualAge < 50>>
-			$he died in $his prime.
-		<<elseif $args[0].actualAge < 65>>
-			$he lived a fair life, good or bad.
-		<<elseif $args[0].actualAge < 90>>
-			$he lived a long life and experienced much during it.
-		<<else>>
-			$he lived a very long life that few get to see.
-		<</if>>
-	<</if>>
-<</widget>>
-
-<<widget "DeathUnhealthy">>
-	/* 000-250-006 */
-	<<if $seeImages && $seeReportImages>>
-		<div class="imageRef tinyImg">
-			<<= SlaveArt($args[0], 0, 0)>>
-		</div>
-	<</if>>
-	/* 000-250-006 */
-	<<setLocalPronouns $args[0]>>
-	<<if $args[0].assignment == "be confined in the arcade">>
-		You are notified by $arcadeName staff that one of the cabinets has broken down and will need to be replaced. It would seem ''@@.pink;$args[0].slaveName@@'', the fucktoy encased in it, died to poor health caused by $his living conditions. $He was a good unit; logs show $he was taking dick up until the very end.
-	<<elseif $args[0].assignment == "work in the dairy" && $dairyRestraintsSetting > 1>>
-		You are notified by $dairyName staff that one of the occupied milkers has ceased producing. Upon inspection, it would seem ''@@.pink;$args[0].slaveName@@'', the cow restrained in it, died to poor health caused by $his living conditions. $He was a good cow; $he gave milk up until $his death.
-	<<elseif $args[0].fuckdoll > 0>>
-		One of your Fuckdolls' monitoring systems alerts you that the slave contained within has died. It would seem ''@@.pink;$args[0].slaveName@@'' has died of general poor health. Thankfully the suit notifies its owner of such things; especially with the rumors of earlier models and necrophilia you hear occasionally. Clean up is easy enough, however.
-	<<else>>
-		''@@.pink;$args[0].slaveName@@'' failed to report in for a routine inspection, something that rarely occurs under your watch. It doesn't take long to track down the wayward slave.
-		$He is found dead in $his bed, having died sometime during the night. $He has been in very poor health lately, so you knew this was a possibility. $args[0].slaveName died at the age of $args[0].actualAge;
-		<<if $args[0].actualAge < 10>>
-			$he lived a tragically short life.
-		<<elseif $args[0].actualAge < 20>>
-			$he died far too young.
-		<<elseif $args[0].actualAge < 30>>
-			$he died in what would be a sex slave's prime.
-		<<elseif $args[0].actualAge < 50>>
-			$he died in $his prime.
-		<<elseif $args[0].actualAge < 65>>
-			$he lived a fair life, good or bad.
-		<<elseif $args[0].actualAge < 90>>
-			$he lived a long life and experienced much during it.
-		<<else>>
-			$he lived a very long life that few get to see.
-		<</if>>
-		<<if ($arcologies[0].FSPaternalist != "unset") && ($args[0].actualAge < 75)>>
-			Allowing a slave to die under your care @@.red;severely damages@@ your image as a caring slaveowner and @@.red;calls into question@@ your paternalistic resolve.
-			<<run FutureSocieties.Change("Paternalist", -10)>>
-		<</if>>
-	<</if>>
-<</widget>>
diff --git a/src/pregmod/widgets/pregmodWidgets.tw b/src/pregmod/widgets/pregmodWidgets.tw
index 54f8aa76a4aedb17a8c8796c27f5df8f848e1d75..afe4adaf36893a46998d5355df75f5cc69172615 100644
--- a/src/pregmod/widgets/pregmodWidgets.tw
+++ b/src/pregmod/widgets/pregmodWidgets.tw
@@ -145,64 +145,3 @@
 	<<set _WifeALisp = lispReplace(_WifeA)>>
 	<<set _WivesALisp = lispReplace(_WivesA)>>
 <</widget>>
-
-<<widget "PlayerRace">>
-	<<replace #ethnicity>>
-		You're $PC.race.
-	<</replace>>
-<</widget>>
-
-<<widget "PlayerSkin">>
-	<<replace #skin>>
-		You have $PC.skin skin.
-	<</replace>>
-<</widget>>
-
-<<widget "PlayerMarkings">>
-	<<replace #markings>>
-		<<if $PC.markings == "none">>
-			Your skin is pure and clear of any freckles.
-		<<elseif $PC.markings == "freckles">>
-			You have some freckles on your cheeks and elsewhere.
-		<<elseif $PC.markings == "heavily freckled">>
-			You have dense freckles on your cheeks and elsewhere.
-		<</if>>
-	<</replace>>
-<</widget>>
-
-<<widget "AgePCEffects">>
-<<switch $PC.actualAge>>
-<<case 3>>
-	<<set $AgeTrainingLowerBoundPC = 18, $AgeTrainingUpperBoundPC = 20, $AgeEffectOnTrainerPricingPC = .1, $AgeEffectOnTrainerEffectivenessPC = .1>>
-<<case 4>>
-	<<set $AgeTrainingLowerBoundPC = 17, $AgeTrainingUpperBoundPC = 19, $AgeEffectOnTrainerPricingPC = .15, $AgeEffectOnTrainerEffectivenessPC = .15>>
-<<case 5>>
-	<<set $AgeTrainingLowerBoundPC = 16, $AgeTrainingUpperBoundPC = 18, $AgeEffectOnTrainerPricingPC = .35, $AgeEffectOnTrainerEffectivenessPC = .35>>
-<<case 6>>
-	<<set $AgeTrainingLowerBoundPC = 15, $AgeTrainingUpperBoundPC = 17, $AgeEffectOnTrainerPricingPC = .55, $AgeEffectOnTrainerEffectivenessPC = .55>>
-<<case 7>>
-	<<set $AgeTrainingLowerBoundPC = 14, $AgeTrainingUpperBoundPC = 16, $AgeEffectOnTrainerPricingPC = .75, $AgeEffectOnTrainerEffectivenessPC = .75>>
-<<case 8>>
-	<<set $AgeTrainingLowerBoundPC = 13, $AgeTrainingUpperBoundPC = 15, $AgeEffectOnTrainerPricingPC = .85, $AgeEffectOnTrainerEffectivenessPC = .85>>
-<<case 9>>
-	<<set $AgeTrainingLowerBoundPC = 12, $AgeTrainingUpperBoundPC = 14, $AgeEffectOnTrainerPricingPC = 1.00, $AgeEffectOnTrainerEffectivenessPC = 1.00>>
-<<case 10>>
-	<<set $AgeTrainingLowerBoundPC = 11, $AgeTrainingUpperBoundPC = 13, $AgeEffectOnTrainerPricingPC = 1.0005, $AgeEffectOnTrainerEffectivenessPC = 1.0005>>
-<<case 11>>
-	<<set $AgeTrainingLowerBoundPC = 10, $AgeTrainingUpperBoundPC = 12, $AgeEffectOnTrainerPricingPC = 1.01, $AgeEffectOnTrainerEffectivenessPC = 1.01>>
-<<case 12>>
-	<<set $AgeTrainingLowerBoundPC = 9, $AgeTrainingUpperBoundPC = 11, $AgeEffectOnTrainerPricingPC = 1.02, $AgeEffectOnTrainerEffectivenessPC = 1.02>>
-<<case 13>>
-	<<set $AgeTrainingLowerBoundPC = 8, $AgeTrainingUpperBoundPC = 10, $AgeEffectOnTrainerPricingPC = 1.03, $AgeEffectOnTrainerEffectivenessPC = 1.03>>
-<<case 14>>
-	<<set $AgeTrainingLowerBoundPC = 7, $AgeTrainingUpperBoundPC = 9, $AgeEffectOnTrainerPricingPC = 1.04, $AgeEffectOnTrainerEffectivenessPC = 1.04>>
-<<case 15>>
-	<<set $AgeTrainingLowerBoundPC = 6, $AgeTrainingUpperBoundPC = 8, $AgeEffectOnTrainerPricingPC = 1.05, $AgeEffectOnTrainerEffectivenessPC = 1.05>>
-<<case 16>>
-	<<set $AgeTrainingLowerBoundPC = 5, $AgeTrainingUpperBoundPC = 7, $AgeEffectOnTrainerPricingPC = 1.06, $AgeEffectOnTrainerEffectivenessPC = 1.06>>
-<<case 17>>
-	<<set $AgeTrainingLowerBoundPC = 4, $AgeTrainingUpperBoundPC = 6, $AgeEffectOnTrainerPricingPC = 1.07, $AgeEffectOnTrainerEffectivenessPC = 1.07>>
-<<case 18>>
-	<<set $AgeTrainingLowerBoundPC = 3, $AgeTrainingUpperBoundPC = 5, $AgeEffectOnTrainerPricingPC = 1.08, $AgeEffectOnTrainerEffectivenessPC = 1.08>>
-<</switch>>
-<</widget>>
diff --git a/src/societies/aztec/slaveSacrifice.tw b/src/societies/aztec/slaveSacrifice.tw
index 62bf5239952cee1d9699d47030458319c4ca90fc..f59ec9596bc542d7d67362ca05574ba53c5b144d 100644
--- a/src/societies/aztec/slaveSacrifice.tw
+++ b/src/societies/aztec/slaveSacrifice.tw
@@ -254,7 +254,7 @@
 		<<set getSlave($AS).boobs += getSlave($AS).boobsMilk>>
 	<</if>>
 	<<run healthDamage(getSlave($AS), 20)>>
-	The penance put $his body through great stress @@.red;which impacted $his health.@@
+	The penance put $his body through great stress @@.health.dec;which impacted $his health.@@
 	<<set getSlave($AS).addict = 5>>
 	<<set getSlave($AS).clothes = "no clothing">>
 
diff --git a/src/uncategorized/PETS.tw b/src/uncategorized/PETS.tw
index 28fb4d5df2b1a4e6ba1286614b76946a34b1af3f..52d8210e3683870bfdab2850cdcd7bfd3ce84c0e 100644
--- a/src/uncategorized/PETS.tw
+++ b/src/uncategorized/PETS.tw
@@ -298,7 +298,7 @@ You decide to knit up care's raveled sleave with a break in the spa. You have yo
 
 <<link "Encourage the use of sleep deprivation as a breaking tool">>
 	<<replace "#result">>
-	You let $activeSlave.slaveName know that as far as you're concerned, sleep is a privilege, not a right. $He grins evilly and grinds $himself into $subSlave.slaveName's mouth harder. The poor prisoner <<if canHear($subSlave)>>heard you, of course<<else>>realizes why $activeSlave.slaveName has increased $his brutality<</if>>, and begins to produce muffled sobs as _he2 realizes what this means for _him2. $activeSlave.slaveName reaches down and caresses _his2 cheek. "There, there," $he croons. "It'<<s>> not rape if you do it in return for <<s>>omething." All the prisoners are all that much more @@.hotpink;broken@@ this week, though sleep deprivation @@.red;isn't healthy.@@
+	You let $activeSlave.slaveName know that as far as you're concerned, sleep is a privilege, not a right. $He grins evilly and grinds $himself into $subSlave.slaveName's mouth harder. The poor prisoner <<if canHear($subSlave)>>heard you, of course<<else>>realizes why $activeSlave.slaveName has increased $his brutality<</if>>, and begins to produce muffled sobs as _he2 realizes what this means for _him2. $activeSlave.slaveName reaches down and caresses _his2 cheek. "There, there," $he croons. "It'<<s>> not rape if you do it in return for <<s>>omething." All the prisoners are all that much more @@.hotpink;broken@@ this week, though sleep deprivation @@.health.dec;isn't healthy.@@
 	<<run $slaves.forEach(function(s) {
 		if (s.assignment == "be confined in the cellblock") {
 			s.devotion += 10;
@@ -309,7 +309,7 @@ You decide to knit up care's raveled sleave with a break in the spa. You have yo
 <</link>>
 <br><<link "Have a quiet word with the Wardeness about prisoners' health">>
 	<<replace "#result">>
-	You let $activeSlave.slaveName finish using $subSlave.slaveName's mouth, turn off the lights in _his2 cell, and then meet $him outside. It won't do to undermine $him in front of the prisoners. You offer a few choice remarks on the value of $his charges to you and the potential effects of sleep deprivation, and point out that $he is a poor Wardeness if $he cannot break slaves without taking measures that may damage their health. $He's clearly filled with remorse, begs your forgiveness, and manages to break slaves just as effectively this week while taking @@.green;better care@@ of their health.
+	You let $activeSlave.slaveName finish using $subSlave.slaveName's mouth, turn off the lights in _his2 cell, and then meet $him outside. It won't do to undermine $him in front of the prisoners. You offer a few choice remarks on the value of $his charges to you and the potential effects of sleep deprivation, and point out that $he is a poor Wardeness if $he cannot break slaves without taking measures that may damage their health. $He's clearly filled with remorse, begs your forgiveness, and manages to break slaves just as effectively this week while taking @@.health.inc;better care@@ of their health.
 	<<run $slaves.forEach(function(s) {
 		if (s.assignment == "be confined in the cellblock") {
 			improveCondition(s, 10);
diff --git a/src/uncategorized/REFI.tw b/src/uncategorized/REFI.tw
index 7621d9b22ad53521e4bae1e51717925b423fe50e..8a4768ddc23e32adaea9607520a876f98c6d2cab 100644
--- a/src/uncategorized/REFI.tw
+++ b/src/uncategorized/REFI.tw
@@ -1403,7 +1403,7 @@ There was a glint of envy <<if canSee($activeSlave)>>in $his eyes when $he saw<<
 			<<if $PC.belly >= 100000>>
 				crushingly
 			<</if>>
-			gravid bulk with $his dick hilted in your needy pussy. You skip all pretense and ride $him long and hard until $he's exhausted and your libido a little lighter. When you finally do release $him from your lust, $he can barely rise <<if hasBothLegs($activeSlave)>>to $his feet<<else>>up<</if>>; you may have ridden $him @@.red;a little too hard.@@ You remind $him that $he was saying something; in response,
+			gravid bulk with $his dick hilted in your needy pussy. You skip all pretense and ride $him long and hard until $he's exhausted and your libido a little lighter. When you finally do release $him from your lust, $he can barely rise <<if hasBothLegs($activeSlave)>>to $his feet<<else>>up<</if>>; you may have ridden $him @@.health.dec;a little too hard.@@ You remind $him that $he was saying something; in response,
 			<<if !canTalk($activeSlave)>>
 				$he pantomimes a pregnant belly, your pregnant belly, and uses gestures to how much $he enjoyed it atop $him.
 			<<else>>
diff --git a/src/uncategorized/REFS.tw b/src/uncategorized/REFS.tw
index 6b7528b473ba6af9c787aea23e057be0546b039c..cc2aa2e7d8ca175dd224566a353389c6c928b128 100644
--- a/src/uncategorized/REFS.tw
+++ b/src/uncategorized/REFS.tw
@@ -111,15 +111,15 @@
 	<<set $activeSlave.clothes = "conservative clothing">>
 
 <<case "knightly duel">>
-    <<set $activeSlaveOneTimeMinAge = 22>>
-    <<set $activeSlaveOneTimeMaxAge = 34>>
+	<<set $activeSlaveOneTimeMinAge = 22>>
+	<<set $activeSlaveOneTimeMaxAge = 34>>
 	<<set $activeSlave = GenerateNewSlave("XY")>>
 	<<set $activeSlave.origin = "$He is a knight. If you have acquired this slave, something has gone wrong.">>
 	<<set $activeSlave.career = "spec ops">>
-    <<set $activeSlave.devotion = random(-80,-60)>>
-    <<set $activeSlave.muscles = random(50,75)>>
-    <<set $activeSlave.skill.combat = 1>>
-    <<set $activeSlave.behavioralFlaw = "malicious", $activeSlave.behavioralQuirk = "none">>
+	<<set $activeSlave.devotion = random(-80,-60)>>
+	<<set $activeSlave.muscles = random(50,75)>>
+	<<set $activeSlave.skill.combat = 1>>
+	<<set $activeSlave.behavioralFlaw = "malicious", $activeSlave.behavioralQuirk = "none">>
 	<<set $activeSlave.trust = random(-30,-20)>>
 	<<set $activeSlave.boobs = 150>>
 	<<set $activeSlave.butt = random(0,1)>>
@@ -172,14 +172,14 @@
 	<<set $activeSlave.career = "a child soldier">>
 	<<set $activeSlave.devotion = 100>>
 	<<set $activeSlave.trust = 100>>
-    <<set $activeSlave.muscles = random(50,75)>>
+	<<set $activeSlave.muscles = random(50,75)>>
 	<<if $activeSlave.weight > 130>>
 		<<set $activeSlave.weight -= 100>>
 		<<set $activeSlave.waist = random(-10,50)>>
 	<</if>>
 	<<set $activeSlave.fetish = "mindbroken">>
 	<<set $activeSlave.fetishKnown = 1>>
-    <<set $activeSlave.skill.combat = 1>>
+	<<set $activeSlave.skill.combat = 1>>
 	<<run configureLimbs($activeSlave, "all", 6)>>
 	<<set $activeSlave.teeth = "pointy">>
 	<<set $activeSlave.earT = "inu">>
diff --git a/src/uncategorized/RESS.tw b/src/uncategorized/RESS.tw
index 6e869d4dd399e6aae743b396b45be81e870863ea..60e715e18a99896249efbcc7b1b9e2fafacd3e87 100644
--- a/src/uncategorized/RESS.tw
+++ b/src/uncategorized/RESS.tw
@@ -4160,7 +4160,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<set $activeSlave.balls = 0, $activeSlave.trust -= 20>>
 		<<run surgeryDamage($activeSlave, 10)>>
 		<<replace "#result">>
-			You bluntly tell $him $he is becoming potent, and that is something you can't allow to roam unchecked amongst your fertile slaves. You drag $him to the remote surgery and strap $him face-down with $his legs spread <<if $activeSlave.belly >= 5000>>$his _belly rounded belly forcing $his rear into the air<</if>>. $He doesn't understand what's happening, since the anesthetics totally deprive $him of any sensation. $He's so drugged and drowsy with @@.red;surgical recovery@@ that it takes a while for $him to figure out what's happened. When $he does, $his poor mind scarcely processes the @@.gold;horror@@ of what's happened. $He numbly carries on, terrified.
+			You bluntly tell $him $he is becoming potent, and that is something you can't allow to roam unchecked amongst your fertile slaves. You drag $him to the remote surgery and strap $him face-down with $his legs spread <<if $activeSlave.belly >= 5000>>$his _belly rounded belly forcing $his rear into the air<</if>>. $He doesn't understand what's happening, since the anesthetics totally deprive $him of any sensation. $He's so drugged and drowsy with @@.health.dec;surgical recovery@@ that it takes a while for $him to figure out what's happened. When $he does, $his poor mind scarcely processes the @@.gold;horror@@ of what's happened. $He numbly carries on, terrified.
 		<</replace>>
 	<</link>>
 <</if>>
@@ -4368,7 +4368,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>>
 					<br><<link "Flog $him">>
 						<<replace "#result2">>
-							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.red;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
+							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.health.dec;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
 							<<set $activeSlave.trust -= 15>>
 							<<run healthDamage($activeSlave, 15)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust += 5; } })>>
@@ -4384,7 +4384,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>> //This option will cost <<print cashFormat($surgeryCost)>>//
 					<br><<link "Castrate $him">>
 						<<replace "#result2">>
-							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
+							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
 							<<set $activeSlave.trust -= 20, $activeSlave.devotion -= 10>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 5; } })>>
 							<<run cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10)>>
@@ -4393,7 +4393,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<if $activeSlave.vagina == -2>>
 						<br><<link "Surgically alter $his gender">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.vagina = 1, $activeSlave.chastityPenis = 0>>
 								<<= VCheck.Vaginal($activeSlave, 1)>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4403,7 +4403,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<else>>
 						<br><<link "Remove $his male genitalia">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.foreskin = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
 								<<run cashX(forceNeg($surgeryCost*2), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20)>>
@@ -4412,7 +4412,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</if>>
 					<br><<link "Deny $him from ever having satisfying sex again">>
 						<<replace "#result2">>
-							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 							<<set $activeSlave.trust -= 50, $activeSlave.devotion -= 30, $activeSlave.dick = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0, $activeSlave.vagina = 0, $activeSlave.ovaries = 0, $activeSlave.clit = 0, $activeSlave.labia = 0, $activeSlave.vaginalAccessory = "none", $activeSlave.vaginalAttachment = "none", $activeSlave.chastityVagina = 0>>
 							<<= VCheck.Anal($activeSlave, 1)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4468,7 +4468,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>>
 					<br><<link "Flog $him">>
 						<<replace "#result2">>
-							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.red;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
+							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.health.dec;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
 							<<set $activeSlave.trust -= 15>>
 							<<run healthDamage($activeSlave, 15)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust += 5; } })>>
@@ -4484,7 +4484,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>> //This option will cost <<print cashFormat($surgeryCost)>>//
 					<br><<link "Castrate $him">>
 						<<replace "#result2">>
-							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
+							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
 							<<set $activeSlave.trust -= 20, $activeSlave.devotion -= 10>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 5; } })>>
 							<<run cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10)>>
@@ -4493,7 +4493,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<if $activeSlave.vagina == -2>>
 						<br><<link "Surgically alter $his gender">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.vagina = 1, $activeSlave.chastityPenis = 0>>
 								<<= VCheck.Vaginal($activeSlave, 1)>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4503,7 +4503,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<else>>
 						<br><<link "Remove $his male genitalia">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.foreskin = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
 								<<run cashX(forceNeg($surgeryCost*2), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20)>>
@@ -4512,7 +4512,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</if>>
 					<br><<link "Deny $him from ever having satisfying sex again">>
 						<<replace "#result2">>
-							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 							<<set $activeSlave.trust -= 50, $activeSlave.devotion -= 30, $activeSlave.dick = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0, $activeSlave.vagina = 0, $activeSlave.ovaries = 0, $activeSlave.clit = 0, $activeSlave.labia = 0, $activeSlave.vaginalAccessory = "none", $activeSlave.vaginalAttachment = "none", $activeSlave.chastityVagina = 0>>
 							<<= VCheck.Anal($activeSlave, 1)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4562,7 +4562,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>>
 					<br><<link "Flog $him">>
 						<<replace "#result2">>
-							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.red;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
+							You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.health.dec;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
 							<<set $activeSlave.trust -= 15>>
 							<<run healthDamage($activeSlave, 15)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust += 5; } })>>
@@ -4578,7 +4578,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</link>> //This option will cost <<print cashFormat($surgeryCost)>>//
 					<br><<link "Castrate $him">>
 						<<replace "#result2">>
-							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
+							As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
 							<<set $activeSlave.trust -= 20, $activeSlave.devotion -= 10>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 5; } })>>
 							<<run cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10)>>
@@ -4587,7 +4587,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<if $activeSlave.vagina == -2>>
 						<br><<link "Surgically alter $his gender">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.vagina = 1, $activeSlave.chastityPenis = 0>>
 								<<= VCheck.Vaginal($activeSlave, 1)>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4597,7 +4597,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<else>>
 						<br><<link "Remove $his male genitalia">>
 							<<replace "#result2">>
-								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
+								You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
 								<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.foreskin = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0>>
 								<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
 								<<run cashX(forceNeg($surgeryCost*2), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20)>>
@@ -4606,7 +4606,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<</if>>
 					<br><<link "Deny $him from ever having satisfying sex again">>
 						<<replace "#result2">>
-							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+							You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 							<<set $activeSlave.trust -= 50, $activeSlave.devotion -= 30, $activeSlave.dick = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0, $activeSlave.vagina = 0, $activeSlave.ovaries = 0, $activeSlave.clit = 0, $activeSlave.labia = 0, $activeSlave.vaginalAccessory = "none", $activeSlave.vaginalAttachment = "none", $activeSlave.chastityVagina = 0>>
 							<<= VCheck.Anal($activeSlave, 1)>>
 							<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4632,7 +4632,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<</link>>
 			<br><<link "Flog $him">>
 				<<replace "#result2">>
-					You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.red;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
+					You bind $his naked body to the wall in preparation for a good beating. Going against one's master is bad, but going against you is even worse. You thoroughly strike $him, showering extra attention to $his crotch, while making sure $he will be in pain for days to come. Such a beating leaves $him @@.health.dec;in agonizing pain@@ and makes a clear example to $him and all your other rebellious slaves that @@.gold;you are not to be trifled with.@@
 					<<set $activeSlave.trust -= 15>>
 					<<run healthDamage($activeSlave, 15)>>
 					<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust += 5; } })>>
@@ -4648,7 +4648,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<</link>> //This option will cost <<print cashFormat($surgeryCost)>>//
 			<br><<link "Castrate $him">>
 				<<replace "#result2">>
-					As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
+					As you pull $his limp body to the remote surgery, you notice $he understands what $he has done and begs you to reconsider your decision; but your mind is set. $He had the balls to try and rape you, and now $he won't. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new life; @@.mediumorchid;$he'll never get hard again@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;mortified by the example.@@
 					<<set $activeSlave.trust -= 20, $activeSlave.devotion -= 10>>
 					<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 5; } })>>
 					<<run cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10)>>
@@ -4657,7 +4657,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<<if $activeSlave.vagina == -2>>
 				<br><<link "Surgically alter $his gender">>
 					<<replace "#result2">>
-						You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+						You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a shemale@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his virgin pussy. You use $him until you are satisfied and toss $him to the floor to think about $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 						<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.vagina = 1, $activeSlave.chastityPenis = 0>>
 						<<= VCheck.Vaginal($activeSlave, 1)>>
 						<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4667,7 +4667,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<<else>>
 				<br><<link "Remove $his male genitalia">>
 					<<replace "#result2">>
-						You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
+						You ask $him if $he enjoyed the last time $he used $his dick and if it was worth crossing you, because it will not happen again. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null@@ and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ Every other rebellious slave is @@.gold;horrified by the example.@@
 						<<set $activeSlave.trust -= 30, $activeSlave.devotion -= 25, $activeSlave.dick = 0, $activeSlave.foreskin = 0, $activeSlave.balls = 0, $activeSlave.scrotum = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0>>
 						<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
 						<<run cashX(forceNeg($surgeryCost*2), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 20)>>
@@ -4676,7 +4676,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<</if>>
 			<br><<link "Deny $him from ever having satisfying sex again">>
 				<<replace "#result2">>
-					You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.red;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
+					You ask $him if $he enjoyed $his last orgasm, because $he won't be getting any more. Restrained as $he is, the most $he can do is cry and beg. Once $he comes to after the surgery, $he faces $his new body; @@.mediumorchid;$he's now a surgical null,@@ though $he retains $his balls, and $he's @@.gold;the only one to blame@@ for $his @@.health.dec;suffering.@@ You waste no time in shoving $him against the wall and forcing your <<if $PC.dick != 0>>dick<<else>>strap-on<</if>> into $his anus. As $his arousal grows, $he realizes you not only took $his dick, but $his prostate as well. $He quickly finds $himself desperate for release but lacking any way to do so. You watch $him squirm in sexual frustration. This will be $his new life. Every other rebellious slave is @@.gold;horrified by the example.@@
 					<<set $activeSlave.trust -= 50, $activeSlave.devotion -= 30, $activeSlave.dick = 0, $activeSlave.prostate = 0, $activeSlave.dickAccessory = "none", $activeSlave.chastityPenis = 0, $activeSlave.vagina = 0, $activeSlave.ovaries = 0, $activeSlave.clit = 0, $activeSlave.labia = 0, $activeSlave.vaginalAccessory = "none", $activeSlave.vaginalAttachment = "none", $activeSlave.chastityVagina = 0>>
 					<<= VCheck.Anal($activeSlave, 1)>>
 					<<set $slaves.forEach(function(s) { if (s.devotion < -50) { s.trust -= 15; } })>>
@@ -4733,7 +4733,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			and gets tackled off of you by _S.Bodyguard.slaveName. After a quick tussle, the amorous cow is restrained and leaking cum on your floor. You sigh to yourself, knowing you have nobody to blame but yourself for this. $He's a breeding bull and you a fertile _womanP. $He only did what $he was conditioned for; the fault is on you for ignoring the warning signs and not taking the proper precautions. _S.Bodyguard.slaveName is visibly disturbed by an assault on you happening within _his2 defensive perimeter and @@.hotpink;vows@@ to not allow it to happen again.
 			<<set _S.Bodyguard.devotion += 2>>
 		<<elseif _S.Concubine && canWalk(_S.Concubine)>>
-			and gets tackled off of you by _S.Concubine.slaveName. After a violent struggle, the amorous cow is restrained and leaking cum on your floor. You sigh to yourself, knowing you have nobody to blame but yourself for this. $He's a breeding bull and you a fertile _womanP. $He only did what $he was conditioned for; the fault is on you for ignoring the warning signs and not taking the proper precautions. _S.Concubine.slaveName is @@.gold;visibly shaken@@ by the assault and was @@.red;badly beaten@@ by the muscular slave during the fight.
+			and gets tackled off of you by _S.Concubine.slaveName. After a violent struggle, the amorous cow is restrained and leaking cum on your floor. You sigh to yourself, knowing you have nobody to blame but yourself for this. $He's a breeding bull and you a fertile _womanP. $He only did what $he was conditioned for; the fault is on you for ignoring the warning signs and not taking the proper precautions. _S.Concubine.slaveName is @@.gold;visibly shaken@@ by the assault and was @@.health.dec;badly beaten@@ by the muscular slave during the fight.
 			<<set _S.Concubine.trust -= 5>>
 			<<run healthDamage(_S.Concubine, 40)>>
 		<<else>>
@@ -5066,7 +5066,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<else>>
 			kid
 		<</if>>
-		draped across you. The meeting is long enough that your interlocutors eventually realize that something unusual is going on, especially once $he wakes up and starts sleepily <<if $PC.dick != 0>>sucking your dick<<if $PC.vagina != -1>> and <</if>><</if>><<if $PC.vagina != -1>>eating you out<</if>>, out of frame. They're @@.red;slightly annoyed,@@ since having sex during meetings is still considered a minor faux pas, but you @@.yellowgreen;close the deal,@@ and $activeSlave.slaveName is @@.hotpink;very pleased@@ that you would rearrange your affairs to make time for <<if $PC.dick != 0>>one of $his blowjobs<<else>>oral sex with $him<</if>>.
+		draped across you. The meeting is long enough that your interlocutors eventually realize that something unusual is going on, especially once $he wakes up and starts sleepily <<if $PC.dick != 0>>sucking your dick<<if $PC.vagina != -1>> and <</if>><</if>><<if $PC.vagina != -1>>eating you out<</if>>, out of frame. They're @@.health.dec;slightly annoyed,@@ since having sex during meetings is still considered a minor faux pas, but you @@.yellowgreen;close the deal,@@ and $activeSlave.slaveName is @@.hotpink;very pleased@@ that you would rearrange your affairs to make time for <<if $PC.dick != 0>>one of $his blowjobs<<else>>oral sex with $him<</if>>.
 		<<run seX($activeSlave, "oral", $PC, "penetrative")>>
 		<<run cashX(_meetingBonus, "event", $activeSlave)>>
 		<<run repX(forceNeg(50), "event", $activeSlave)>>
@@ -6098,7 +6098,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<else>>
 			direct $him to lay on $his side on your desk<<if $activeSlave.belly >= 300000>> with $his _belly belly hanging over the edge<</if>>,
 		<</if>>
-		but is surprised and reassured when $he's penetrated not by a <<if $PC.dick == 0>>strap-on<<else>>turgid<<if $PC.vagina != -1>> futa<</if>> cock<</if>> but by a single gentle finger coated with something healing and cool. The mixed analgesic and anti-inflammatory takes the sharpness off the sore feeling, and will help get $his butt back into fucking shape. @@.mediumaquamarine;$He has become more accepting of anal slavery,@@ and @@.green;$his asshole feels better.@@
+		but is surprised and reassured when $he's penetrated not by a <<if $PC.dick == 0>>strap-on<<else>>turgid<<if $PC.vagina != -1>> futa<</if>> cock<</if>> but by a single gentle finger coated with something healing and cool. The mixed analgesic and anti-inflammatory takes the sharpness off the sore feeling, and will help get $his butt back into fucking shape. @@.mediumaquamarine;$He has become more accepting of anal slavery,@@ and @@.health.inc;$his asshole feels better.@@
 		<<if ($activeSlave.anus > 2)>>Your expert care has @@.orange;allowed $his loose asspussy to recover a little of its natural shape and size.@@<<set $activeSlave.anus -= 1>><</if>>
 		<<set $activeSlave.trust += 4, $activeSlave.minorInjury = 0>>
 	<</replace>>
@@ -7819,7 +7819,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 
 <<link "Check on $him">>
 	<<replace "#result">>
-		Since $he's limbless<<if $activeSlave.pregKnown == 1>> and pregnant<</if>>, $his health is more fragile than most slaves'. You look in on $him, and when $he continues to shiver, you pull the sheets back around $him, tucking $him in in such a way that $he can lie comfortably. In the morning $he doesn't understand why $he's so snug and well-rested, but @@.green;$his health improves with decent sleep.@@
+		Since $he's limbless<<if $activeSlave.pregKnown == 1>> and pregnant<</if>>, $his health is more fragile than most slaves'. You look in on $him, and when $he continues to shiver, you pull the sheets back around $him, tucking $him in in such a way that $he can lie comfortably. In the morning $he doesn't understand why $he's so snug and well-rested, but @@.health.inc;$his health improves with decent sleep.@@
 		<<run improveCondition($activeSlave, 10)>>
 	<</replace>>
 <</link>>
@@ -7831,7 +7831,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <</link>>
 <br><<link "Leave $him be">>
 	<<replace "#result">>
-		In the morning, $his lips are blue and $he's nearly unresponsive. Your other slaves get $him working again, but @@.red;$his health has been damaged.@@
+		In the morning, $his lips are blue and $he's nearly unresponsive. Your other slaves get $him working again, but @@.health.dec;$his health has been damaged.@@
 		<<run healthDamage($activeSlave, 10)>>
 	<</replace>>
 <</link>>
@@ -8431,7 +8431,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<<replace "#result">>
 		$He gets a weekly health exam from the automated systems, which also do their best to monitor $his well-being, but $he does not protest as you take $him to the surgery and give $him a <<if $PC.skill.medicine >= 100>>professional examination. It feels good to put the old skills to use on an attractive patient<<else>>thorough examination<</if>>. There's nothing the matter other than that $he hasn't been 18 for a long time. $He looks a little sad at some of the results, but whenever $he does, you place a hand on $his cheek and give $him a kiss. $He gets the idea.
 		"I under<<s>>tand, <<Master>>. I can <<s>>till <<s>>erve you," $he <<say>>s.
-		You adjust $his diet and exercise a little, which should @@.green;slightly improve@@ $his health<<if $PC.skill.medicine >= 100>>, and prescribe some new supplements that might help $him @@.green;feel $his best@@ all the time<<run improveCondition($activeSlave, 10)>><</if>>. As $he gets up from the chair and makes to resume $his duties, you give $him a light swat across the buttocks. $He squeaks and turns to @@.mediumaquamarine;giggle at you,@@ giving you a broad wink and shaking $his tits a little for you.
+		You adjust $his diet and exercise a little, which should @@.health.inc;slightly improve@@ $his health<<if $PC.skill.medicine >= 100>>, and prescribe some new supplements that might help $him @@.health.inc;feel $his best@@ all the time<<run improveCondition($activeSlave, 10)>><</if>>. As $he gets up from the chair and makes to resume $his duties, you give $him a light swat across the buttocks. $He squeaks and turns to @@.mediumaquamarine;giggle at you,@@ giving you a broad wink and shaking $his tits a little for you.
 		<<set $activeSlave.trust += 4>>
 		<<run improveCondition($activeSlave, 10)>>
 	<</replace>>
@@ -8440,7 +8440,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<br><<link "Give $him an afternoon off for some quality time with your Head Girl">>
 		<<setLocalPronouns _S.HeadGirl 2>>
 		<<replace "#result">>
-			_S.HeadGirl.slaveName understands the situation immediately. _He2 gets _himself2 and $activeSlave.slaveName dressed for a nice, non-sexual 'date' in $clubName, and leads $him out by the hand with a wink over _his2 shoulder to you. Your Head Girl understands just what kind of break from sexual servitude $activeSlave.slaveName really needs. They enjoy a nice meal, take a stroll and talk as friends, and get some inconsequential but relaxing beauty treatments together. They both @@.hotpink;enjoy the relaxation,@@ and $activeSlave.slaveName @@.green;feels much better@@ after the rest, too.
+			_S.HeadGirl.slaveName understands the situation immediately. _He2 gets _himself2 and $activeSlave.slaveName dressed for a nice, non-sexual 'date' in $clubName, and leads $him out by the hand with a wink over _his2 shoulder to you. Your Head Girl understands just what kind of break from sexual servitude $activeSlave.slaveName really needs. They enjoy a nice meal, take a stroll and talk as friends, and get some inconsequential but relaxing beauty treatments together. They both @@.hotpink;enjoy the relaxation,@@ and $activeSlave.slaveName @@.health.inc;feels much better@@ after the rest, too.
 			<<set $activeSlave.devotion += 4>>
 			<<run cashX(forceNeg(500), "event", $activeSlave), improveCondition($activeSlave, 10)>>
 			<<set _S.HeadGirl.devotion += 4>>
@@ -8809,7 +8809,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <</link>>
 <br><<link "Geld $him gradually">>
 	<<replace "#result">>
-		You shove $his unresisting<<if $activeSlave.belly >= 5000>> <<if $activeSlave.bellyPreg >= 3000>>gravid<<else>>swollen<</if>><</if>> body over the couch and seize $his ballsack. When $he feels your tight grip $he spasms and tries to pull away reflexively, but goes limp when $he feels the agony of a warning squeeze. You fasten a tight rubber ring around the base of $his sack, leaving $him writhing on the couch in considerable discomfort. You add leather mittens to $his hands to stop $him from removing the rubber, and then observe that this is a method used to geld livestock. $His tearful begging goes on until you tire of it and put $him out. <<= capFirstChar($assistant.name)>> tracks $his agonized, weeping progress around the arcology for the many hours it takes the lack of blood flow to necessitate a trip to the remote surgery. When that time comes, you make $him beg you to remove $his balls for an hour straight before you do — and $he's so desperate for relief from the pain that $he does it. The experience has left $him @@.red;slightly injured,@@ @@.orange;gelded,@@ @@.red;thoroughly traumatized,@@ and @@.gold;willing to do anything@@ to avoid any more pain.
+		You shove $his unresisting<<if $activeSlave.belly >= 5000>> <<if $activeSlave.bellyPreg >= 3000>>gravid<<else>>swollen<</if>><</if>> body over the couch and seize $his ballsack. When $he feels your tight grip $he spasms and tries to pull away reflexively, but goes limp when $he feels the agony of a warning squeeze. You fasten a tight rubber ring around the base of $his sack, leaving $him writhing on the couch in considerable discomfort. You add leather mittens to $his hands to stop $him from removing the rubber, and then observe that this is a method used to geld livestock. $His tearful begging goes on until you tire of it and put $him out. <<= capFirstChar($assistant.name)>> tracks $his agonized, weeping progress around the arcology for the many hours it takes the lack of blood flow to necessitate a trip to the remote surgery. When that time comes, you make $him beg you to remove $his balls for an hour straight before you do — and $he's so desperate for relief from the pain that $he does it. The experience has left $him @@.health.dec;slightly injured,@@ @@.orange;gelded,@@ @@.red;thoroughly traumatized,@@ and @@.gold;willing to do anything@@ to avoid any more pain.
 		<<set $activeSlave.behavioralFlaw = "odd", $activeSlave.trust -= 20, $activeSlave.balls = 0, $activeSlave.scrotum = 0>>
 		<<run surgeryDamage($activeSlave, 10)>>
 	<</replace>>
@@ -9520,7 +9520,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <</link>>
 <br><<link "Cut $his balls off">>
 	<<replace "#result">>
-		You agree, on the condition that $he be a good little bitch like $he promised. $He thanks you frantically, following you with mixed relief, gratitude, and deep terror as you lead $him to the surgery. It's a medically simple procedure, but $he's @@.red;retained for recovery@@ for some time, a common precaution in your penthouse where the surgery affects an area that might be reinjured by sexual use without a short break for the curatives to take effect. When the medical equipment verifies that $he can be fucked without pain or danger to $his health, you order $him to come back up to your office. $He is a @@.hotpink;very good little bitch,@@
+		You agree, on the condition that $he be a good little bitch like $he promised. $He thanks you frantically, following you with mixed relief, gratitude, and deep terror as you lead $him to the surgery. It's a medically simple procedure, but $he's @@.health.dec;retained for recovery@@ for some time, a common precaution in your penthouse where the surgery affects an area that might be reinjured by sexual use without a short break for the curatives to take effect. When the medical equipment verifies that $he can be fucked without pain or danger to $his health, you order $him to come back up to your office. $He is a @@.hotpink;very good little bitch,@@
 		<<if canDoAnal($activeSlave)>>
 			taking <<if $PC.dick != 0>>a hard buttfuck<<else>>a hard anal fingerfuck<</if>> with apparent enthusiasm and a strong orgasm, though of course $his continued use of a chastity cage conceals almost all the effects.
 			<<= VCheck.Anal($activeSlave, 1)>>
@@ -10401,7 +10401,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 
 <<link "Increase $his assignment to 24 hours a day">>
 	<<replace "#result">>
-		You give $activeSlave.slaveName no indication that $his public use assignment is about to change. Late in the day, when $his shift would normally end, another of your slaves simply maintains $him as usual and then leaves $him be. $activeSlave.slaveName is so deprived of any way to tell time that $he doesn't realize $he's been left for some time. The first indication $he gets that something's different is when liquid food is squirted down $his throat the next morning. When $he's finally stripped out of the suit at the end of the week, $he's pale and wan and $his holes are puffy and red. @@.red;$His health was damaged.@@ However, $his permanent presence became quite noted. @@.green;Your reputation has increased.@@ And when $he's out of the suit, $he instantly begs you to allow $him to do anything to save $himself from more of that. @@.gold;$His fear of you has increased.@@
+		You give $activeSlave.slaveName no indication that $his public use assignment is about to change. Late in the day, when $his shift would normally end, another of your slaves simply maintains $him as usual and then leaves $him be. $activeSlave.slaveName is so deprived of any way to tell time that $he doesn't realize $he's been left for some time. The first indication $he gets that something's different is when liquid food is squirted down $his throat the next morning. When $he's finally stripped out of the suit at the end of the week, $he's pale and wan and $his holes are puffy and red. @@.health.dec;$His health was damaged.@@ However, $his permanent presence became quite noted. @@.green;Your reputation has increased.@@ And when $he's out of the suit, $he instantly begs you to allow $him to do anything to save $himself from more of that. @@.gold;$His fear of you has increased.@@
 	<</replace>>
 	<<run repX(500, "event", $activeSlave), healthDamage($activeSlave, 10)>>
 	<<set $activeSlave.trust -= 10>>
@@ -10674,7 +10674,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 
 <<link "Let $him have labiaplasty to resolve the issue">>
 	<<replace "#result">>
-		When you assent, $he cheers up immediately, looking remarkably happy for a $girl who's just learned $his vagina is about to have a surgical operation performed on it. Then again, having those huge pussylips constantly getting in $his way when $he runs must be extremely uncomfortable, so it's not shocking $he would consider a radical solution to $his problem. When $he exits the remote surgery, $he looks @@.red;sorer than ever,@@ of course, but $he @@.hotpink;smiles gratefully@@ at you the next time $he <<if canSee($activeSlave)>>sees<<else>>meets<</if>> you, and lets you know $he's really looking forward to recovering enough for $him to take $his beloved <<= getWrittenTitle($activeSlave)>> into $his @@.orange;newly streamlined cunt.@@
+		When you assent, $he cheers up immediately, looking remarkably happy for a $girl who's just learned $his vagina is about to have a surgical operation performed on it. Then again, having those huge pussylips constantly getting in $his way when $he runs must be extremely uncomfortable, so it's not shocking $he would consider a radical solution to $his problem. When $he exits the remote surgery, $he looks @@.health.dec;sorer than ever,@@ of course, but $he @@.hotpink;smiles gratefully@@ at you the next time $he <<if canSee($activeSlave)>>sees<<else>>meets<</if>> you, and lets you know $he's really looking forward to recovering enough for $him to take $his beloved <<= getWrittenTitle($activeSlave)>> into $his @@.orange;newly streamlined cunt.@@
 		<<set $activeSlave.devotion += 4, $activeSlave.labia = 0>>
 		<<run surgeryDamage($activeSlave, 10)>>
 	<</replace>>
@@ -11299,7 +11299,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			<<if ($activeSlave.markings == "freckles") || ($activeSlave.markings == "heavily freckled")>>
 				$He proudly shows your other slaves $his breast growth by keeping track of the increasing distance between a couple of chosen freckles on the front of $his boobs.
 			<</if>>
-			The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<if $activeSlave.geneMods.NCS == 0>>
 				<<set $activeSlave.boobs += 200>>
 				<<run healthDamage($activeSlave, 10)>>
@@ -11310,14 +11310,14 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<elseif _injection == "butt">>
 			@@.lime;buttocks have grown,@@ delighting $him.
 			<<set $activeSlave.butt++>>
-			The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "lips">>
-			@@.lime;lips have swelled,@@ delighting $him. The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			@@.lime;lips have swelled,@@ delighting $him. The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<set $activeSlave.lips += 10>>
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "clit">>
-			@@.lime;clit has grown,@@ delighting $him. The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			@@.lime;clit has grown,@@ delighting $him. The dose of growth hormones necessary to cause such rapid tissue changes left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<set $activeSlave.clit++>>
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "dick">>
@@ -11327,7 +11327,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 				As a side effect, $his @@.lime;balls have also swelled,@@ which $he sees as an added benefit.
 				<<set $activeSlave.balls++>>
 			<</if>>
-			The dose of growth hormones and male hormone treatment necessary to cause such rapid change left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			The dose of growth hormones and male hormone treatment necessary to cause such rapid change left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "dickMinus">>
 			@@.orange;dick has shrunk,@@ delighting $him.
@@ -11342,14 +11342,14 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 					<<set $activeSlave.balls-->>
 				<</if>>
 			<</if>>
-			The dose of growth hormones and hormone treatment necessary to cause such rapid tissue reduction left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			The dose of growth hormones and hormone treatment necessary to cause such rapid tissue reduction left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "balls">>
-			@@.lime;testicles have grown,@@ delighting $him. The dose of growth hormones and male hormone treatment necessary to cause such rapid change left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			@@.lime;testicles have grown,@@ delighting $him. The dose of growth hormones and male hormone treatment necessary to cause such rapid change left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<set $activeSlave.balls++>>
 			<<run healthDamage($activeSlave, 10)>>
 		<<elseif _injection == "ballsMinus">>
-			@@.orange;testicles have shrunk,@@ delighting $him. The dose of growth hormones and hormone treatment necessary to cause such rapid tissue reduction left $him feeling @@.red;rather ill,@@ but $he is recovering normally.
+			@@.orange;testicles have shrunk,@@ delighting $him. The dose of growth hormones and hormone treatment necessary to cause such rapid tissue reduction left $him feeling @@.health.dec;rather ill,@@ but $he is recovering normally.
 			<<set $activeSlave.balls-->>
 			<<run healthDamage($activeSlave, 10)>>
 			<<if (($activeSlave.geneMods.NCS == 1) && ($activeSlave.balls > 1) && (random(1,100) > 50))>>
@@ -11745,7 +11745,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 
 <<link "Require $him to self-flagellate">>
 	<<replace "#result">>
-		You tell $him that you will offer $him a method of expiating $his deep and troubling sin. $He looks almost hopeful at this, until you lead $him out onto a balcony and order $him to strip before handing $him a simple length of hempen rope. $He already knows what's coming next, but $he breaks down as $he obeys your orders to kneel. $He sobs abjectly, but you don't even have to give the next order: $he's already flogging the rope against $his own back in the hope of earning a reprieve by obedience. You count the strokes aloud, stopping at one in disapproval of how light $he's hitting $himself. $He flogs $himself harder and harder, screaming a little and crying in pain and fear as you obstinately refuse to say "two." When $he strikes so hard that $he draws a little blood, you finally say the terrible word. When you're finally satisfied with $his self-torture, $he's so exhausted that you carry $his limp, bloody<<if $seePee == 1>>, vaguely urine-scented<</if>><<if $activeSlave.bellyPreg >= 1500>> gravid<</if>> form into the shower, wash $him, and apply curatives, @@.red;though $he's still quite injured.@@ $He wakes to find $himself curled up in your lap, and $he stiffens for a brief moment before @@.hotpink;relaxing against your chest,@@ weeping softly.
+		You tell $him that you will offer $him a method of expiating $his deep and troubling sin. $He looks almost hopeful at this, until you lead $him out onto a balcony and order $him to strip before handing $him a simple length of hempen rope. $He already knows what's coming next, but $he breaks down as $he obeys your orders to kneel. $He sobs abjectly, but you don't even have to give the next order: $he's already flogging the rope against $his own back in the hope of earning a reprieve by obedience. You count the strokes aloud, stopping at one in disapproval of how light $he's hitting $himself. $He flogs $himself harder and harder, screaming a little and crying in pain and fear as you obstinately refuse to say "two." When $he strikes so hard that $he draws a little blood, you finally say the terrible word. When you're finally satisfied with $his self-torture, $he's so exhausted that you carry $his limp, bloody<<if $seePee == 1>>, vaguely urine-scented<</if>><<if $activeSlave.bellyPreg >= 1500>> gravid<</if>> form into the shower, wash $him, and apply curatives, @@.health.dec;though $he's still quite injured.@@ $He wakes to find $himself curled up in your lap, and $he stiffens for a brief moment before @@.hotpink;relaxing against your chest,@@ weeping softly.
 		<<if ($activeSlave.fetish != "submissive") && (random(1,100) > 50)>>
 			<<set $activeSlave.fetish = "submissive", $activeSlave.fetishKnown = 1, $activeSlave.fetishStrength = 10>>
 			$He has come to associate @@.lightcoral;submission@@ with sexual fulfillment.
@@ -13092,7 +13092,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<<replace "#result">>
 		Simple problems require simple solutions — $he'll get fucked in the mouth until $he either gets over $his hang-ups about oral or learns to hide them. You drag the protesting $activeSlave.slaveName out in public, chain $him low so that $his mouth is available, and tell $him that $he'll suck dicks until $he gets through five in a row without grimacing, gagging, or resisting. You have a comfortable chair brought out to you and settle in to watch the show.
 		$activeSlave.slaveName tries, $he really does. But when word gets out as to the conditions of $his enslavement, $his users take a perverse enjoyment in being rougher than usual to evoke the exact reactions $he's trying to avoid. By the third failed streak, you've started to grow bored of the spectacle, but luckily you find entertainment in conversation with those who have already been entertained by poor $activeSlave.slaveName. Before long more chairs have been brought up and an impromptu salon has been set up alongside the blowbang line. By the sixth failed streak, an enterprising citizen has set up a small bar and is serving drinks. By the ninth, you've delegated watching $activeSlave.slaveName to your assistant. You personally break the eleventh streak after $he reached four, to general acclaim from your newfound friends and a toast to your virility.
-		When the fourteenth streak is finally successful, there are serious talks about making these blowbang salons a regular occurrence and some backslapping directed towards you for your innovation in genteel hedonism. While you seriously doubt $activeSlave.slaveName enjoys oral sex any more than $he did at the start of the day, $he's certainly @@.green;learned to keep $his feelings on the matter to $himself.@@ $He did, however, @@.red;have quite a rough time@@ of it<<if $activeSlave.skill.oral <= 30>>, though $he did learn a thing or two about sucking dick.<<= slaveSkillIncrease('oral', $activeSlave, 10)>><<else>>.<</if>> And last of all, you and $activeSlave.slaveName did make @@.green;quite a good impression@@ today, though for widely differing reasons.
+		When the fourteenth streak is finally successful, there are serious talks about making these blowbang salons a regular occurrence and some backslapping directed towards you for your innovation in genteel hedonism. While you seriously doubt $activeSlave.slaveName enjoys oral sex any more than $he did at the start of the day, $he's certainly @@.green;learned to keep $his feelings on the matter to $himself.@@ $He did, however, @@.health.dec;have quite a rough time@@ of it<<if $activeSlave.skill.oral <= 30>>, though $he did learn a thing or two about sucking dick.<<= slaveSkillIncrease('oral', $activeSlave, 10)>><<else>>.<</if>> And last of all, you and $activeSlave.slaveName did make @@.green;quite a good impression@@ today, though for widely differing reasons.
 		<<set $activeSlave.sexualFlaw = "none">>
 		<<run seX($activeSlave, "oral", "public", "penetrative", random(65,80))>>
 		<<run repX(500, "event", $activeSlave), healthDamage($activeSlave, 10)>>
@@ -15570,7 +15570,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<</if>>
 		and $he whimpers and begs for you to reconsider even as $he's strapped onto the operating table.
 		<br><br>
-		By the time $he wakes up from the anesthesia, you've already made the necessary alterations to $his permanent records, which now clearly indicate that $he was born <<print $arcologies[0].FSSubjugationistRace>>, and as such, can be rightfully regarded as the racially inferior fuck-animal $he now is. Of course, your fellow slaveowners and the slave market will be savvy enough to see through the alterations you've forced upon $him, but when you make $him available for a day of use in the public square, all your average citizen knows is that the $girl they are fucking is simply a worthless $arcologies[0].FSSubjugationistRace slut who doesn't deserve much more than a stomach full of cum. The surgery @@.red;negatively effects $his health,@@ but by the end of a long day $he'll likely never forget, $he has gotten a crash course in what it means to be a $arcologies[0].FSSubjugationistRace slave in your arcology, and any evidence that $he was born $activeSlave.race has been efficiently expunged from $his records. <<if ($activeSlave.vagina == 0 || $activeSlave.anus == 0)>>@@.lime;All vestiges of $his virginity have been unceremoniously stripped away@@.<</if>> The experience has @@.hotpink;broken down $his resistance,@@ and your willingness to go to such extremes to subjugate $him @@.gold;greatly increases $his fear of you.@@
+		By the time $he wakes up from the anesthesia, you've already made the necessary alterations to $his permanent records, which now clearly indicate that $he was born <<print $arcologies[0].FSSubjugationistRace>>, and as such, can be rightfully regarded as the racially inferior fuck-animal $he now is. Of course, your fellow slaveowners and the slave market will be savvy enough to see through the alterations you've forced upon $him, but when you make $him available for a day of use in the public square, all your average citizen knows is that the $girl they are fucking is simply a worthless $arcologies[0].FSSubjugationistRace slut who doesn't deserve much more than a stomach full of cum. The surgery @@.health.dec;negatively effects $his health,@@ but by the end of a long day $he'll likely never forget, $he has gotten a crash course in what it means to be a $arcologies[0].FSSubjugationistRace slave in your arcology, and any evidence that $he was born $activeSlave.race has been efficiently expunged from $his records. <<if ($activeSlave.vagina == 0 || $activeSlave.anus == 0)>>@@.lime;All vestiges of $his virginity have been unceremoniously stripped away@@.<</if>> The experience has @@.hotpink;broken down $his resistance,@@ and your willingness to go to such extremes to subjugate $him @@.gold;greatly increases $his fear of you.@@
 		<<set $activeSlave.devotion += 5, $activeSlave.trust -= 10, $activeSlave.anus++>>
 		<<run seX($activeSlave, "anal", "public", "penetrative", 25)>>
 		<<run seX($activeSlave, "oral", "public", "penetrative", 35)>>
@@ -15887,7 +15887,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <<if $activeSlave.lactation < 2>>
 	<<link "Augment $his breast growth with lactation implants">>
 		<<replace "#result">>
-			You announce that you're resolved to continue growing $his breasts, and since $he feels like a cow, plan to help $him fulfill $his image of $himself. $He's experienced enough to know not to question what that means, just to @@.gold;fear the intent behind it.@@ $He's right to worry, as you drag $him to the remote surgery for an impromptu lactation implant installation. When $he comes to, $he immediately realizes $his breasts are larger than ever. As $he brings a hand to each of the full mounds, a moan laced with relief and disdain escapes $his lips; along with a strong gush of milk from $his engorged breasts. $He has been taught a harsh lesson about questioning your will, a lesson $he will be reminded of every time $he has to empty $his ever swelling breasts of their excessive milk. As with all surgery @@.red;$his health has been slightly affected.@@
+			You announce that you're resolved to continue growing $his breasts, and since $he feels like a cow, plan to help $him fulfill $his image of $himself. $He's experienced enough to know not to question what that means, just to @@.gold;fear the intent behind it.@@ $He's right to worry, as you drag $him to the remote surgery for an impromptu lactation implant installation. When $he comes to, $he immediately realizes $his breasts are larger than ever. As $he brings a hand to each of the full mounds, a moan laced with relief and disdain escapes $his lips; along with a strong gush of milk from $his engorged breasts. $He has been taught a harsh lesson about questioning your will, a lesson $he will be reminded of every time $he has to empty $his ever swelling breasts of their excessive milk. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 			<<set $activeSlave.trust -= 5, $activeSlave.lactation = 2>>
 			<<run cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave), surgeryDamage($activeSlave, 10)>>
 		<</replace>>
@@ -15993,7 +15993,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 
 <<link "Give $him the surgery">>
 	<<replace "#result">>
-		You schedule $him for the surgery. Like all invasive procedures, it @@.red;affects $his health,@@ but @@.lime;$he's effectively an anal virgin again.@@ $He @@.mediumaquamarine;trusts you a bit more@@ for granting $his request, and is eager for buttsex that's more interesting than having a hotdog thrown down $his hallway.
+		You schedule $him for the surgery. Like all invasive procedures, it @@.health.dec;affects $his health,@@ but @@.lime;$he's effectively an anal virgin again.@@ $He @@.mediumaquamarine;trusts you a bit more@@ for granting $his request, and is eager for buttsex that's more interesting than having a hotdog thrown down $his hallway.
 		<<set $activeSlave.trust += 3, $activeSlave.anus = 0, cashX(forceNeg($surgeryCost), "slaveSurgery", $activeSlave)>>
 		<<run surgeryDamage($activeSlave, 10)>>
 		<<if $activeSlave.skill.anal > 10>><<set $activeSlave.skill.anal -= 10>><</if>>
@@ -16754,7 +16754,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<else>>
 			$He knows that there are slaves who are treated much more harshly than $he is, and doesn't want to become one of them.
 		<</if>>
-		$He staggers out of your office after $his inspection, doing $his best to ignore $his condition. Over the coming week, $he's continually tortured by $his libido, cruelly entombed as it is in a body which can no longer give it meaningful relief. $He manages to keep it together, mostly, and $his @@.red;sex drive diminishes noticeably.@@
+		$He staggers out of your office after $his inspection, doing $his best to ignore $his condition. Over the coming week, $he's continually tortured by $his libido, cruelly entombed as it is in a body which can no longer give it meaningful relief. $He manages to keep it together, mostly, and $his @@.libido.dec;sex drive diminishes noticeably.@@
 		<<set $activeSlave.energy -= 5>>
 	<</replace>>
 <</link>>
diff --git a/src/uncategorized/RETS.tw b/src/uncategorized/RETS.tw
index 7ae79f08ef5722cbcad3dca74aa76b5cc3e8c684..8e14c7f493a373586d9a3fe2fb04cc08358b5d88 100644
--- a/src/uncategorized/RETS.tw
+++ b/src/uncategorized/RETS.tw
@@ -416,7 +416,7 @@ You pass by the slave quarters during a busy time. Slaves are hurrying back and
 	squashing their boobs together hard so $he can reach despite the mass of soft flesh between them.
 <</if>>
 <br><br>
-"H-hey," $subSlave.slaveName gasps when $activeSlave.slaveName finally breaks the lip lock, but _he2's clearly not that displeased. $activeSlave.slaveName, who has clearly forgotten running into the other slave entirely and now has other things on $his mind, begins to grind against _him2. When $subSlave.slaveName smiles back at the horny <<if $activeSlave.physicalAge > 30>>$woman<<elseif $activeSlave.physicalAge >= 18>>$girl<<elseif $activeSlave.physicalAge >= 18>>teenager<<else>>$loli<</if>> on top of _him2, $activeSlave.slaveName
+"H-hey," $subSlave.slaveName gasps when $activeSlave.slaveName finally breaks the lip lock, but _he2's clearly not that displeased. $activeSlave.slaveName, who has clearly forgotten running into the other slave entirely and now has other things on $his mind, begins to grind against _him2. When $subSlave.slaveName smiles back at the horny <<if $activeSlave.physicalAge > 30>>$woman<<elseif $activeSlave.physicalAge >= 18>>$girl<<elseif $activeSlave.physicalAge >= 13>>teenager<<else>>$loli<</if>> on top of _him2, $activeSlave.slaveName
 <<if canPenetrate($activeSlave)>>
 	<<if !canDoVaginal($subSlave)>>
 		<<if $subSlave.anus == 0 || !canDoAnal($subSlave)>>
diff --git a/src/uncategorized/arcade.tw b/src/uncategorized/arcade.tw
index c4c9d54d5735556b876039b8b9878cf3144dd97f..db8d593a71dc340e0b385a23e7999cf4a47ab44d 100644
--- a/src/uncategorized/arcade.tw
+++ b/src/uncategorized/arcade.tw
@@ -159,12 +159,17 @@
 	<<if $arcadeUpgradeFuckdolls == 1>>
 		equipped to convert inmates into standard Fuckdolls. The converter can be put to work on your order.
 		<div class="choices">
-			[[Turn on conversion|Arcade][$arcadeUpgradeFuckdolls = 2]]
+			[[Turn on conversion|Arcade][$arcadeUpgradeFuckdolls = 2]] | [[Turn on bulk conversion|Arcade][$arcadeUpgradeFuckdolls = 3]]
 		</div>
 	<<elseif $arcadeUpgradeFuckdolls == 2>>
 		equipped to convert inmates into standard Fuckdolls. The converter is currently active and will convert the least popular girl at the end of the week.
 		<div class="choices">
-			[[Turn off conversion|Arcade][$arcadeUpgradeFuckdolls = 1]]
+			[[Turn off conversion|Arcade][$arcadeUpgradeFuckdolls = 1]] | [[Switch to bulk conversion|Arcade][$arcadeUpgradeFuckdolls = 3]]
+		</div>
+	<<elseif $arcadeUpgradeFuckdolls == 3>>
+		equipped to convert inmates into standard Fuckdolls. The converter is currently active and will convert underperforming girls at the end of the week.
+		<div class="choices">
+			[[Turn off conversion|Arcade][$arcadeUpgradeFuckdolls = 1]] | [[Switch to single conversion|Arcade][$arcadeUpgradeFuckdolls = 2]]
 		</div>
 	<<else>>
 		<<set _Tmult2 = Math.trunc(5000*$upgradeMultiplierArcology)>>
diff --git a/src/uncategorized/arcadeReport.tw b/src/uncategorized/arcadeReport.tw
deleted file mode 100644
index 2f2dca07c7e394d40b8a15dba49052161ee1578f..0000000000000000000000000000000000000000
--- a/src/uncategorized/arcadeReport.tw
+++ /dev/null
@@ -1,298 +0,0 @@
-:: Arcade Report [nobr]
-
-<span id="arcade-stats">
-</span>
-
-<<set _slaves = App.Utils.sortedEmployees(App.Entity.facilities.arcade)>>
-<<set _DL = _slaves.length, _SL = $slaves.length, _cockmilked = 0, _milked = 0, _milkProfits = 0, _profits = 0, _oldCash = 0, _boobsImplanted = 0, _prostatesImplanted = 0, _vasectomiesUndone = 0, _mSlave = 0, _bSlave = 0, _pSlave = 0, _cSlave = 0>>
-
-<!-- Statistics gathering -->
-<<set $facility = $facility || {}, $facility.arcade = initFacilityStatistics($facility.arcade)>>
-<<set _arcadeNameCaps = capFirstChar($arcadeName)>>
-
-&nbsp;&nbsp;&nbsp;&nbsp;<<if (_DL > 1)>>''There are _DL inmates confined in $arcadeName.''<<else>>''There is one inmate confined in $arcadeName.''<</if>>
-<<if ($arcologies[0].FSDegradationist > 20)>>
-	<<if $arcologies[0].FSDegradationistLaw == 1>>
-		The tenants located near the arcade consider the facility a normal part of their surroundings, including the crowd of menial slaves waiting their turn spills out into the hallway, 24 hours a day.
-	<<else>>
-		The tenants located near the arcade consider the facility a normal part of their surroundings.
-	<</if>>
-<<elseif ($arcologies[0].FSPaternalist > 20)>>
-	The tenants located near the arcade consider it an @@.red;intolerable@@ establishment.
-<</if>>
-<<if $arcadeDecoration != "standard">>
-	<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_arcadeNameCaps's customers enjoy @@.green;getting off in $arcadeDecoration surroundings.@@
-<</if>>
-
-<<for _slave range _slaves>>
-	<<setLocalPronouns _slave>>
-	<<set $i = $slaveIndices[_slave.ID]>> /% for the included passages %/
-
-	/* Perform facility based rule changes */
-	<<set _slave.clothes = "no clothing">>
-	/* Health */
-	<<if $arcadeUpgradeHealth == 2 && _slave.health.condition < 40>>
-		<<run improveCondition(_slave, 2)>>
-	<<elseif _slave.health.condition > 20>>
-		<<run healthDamage(_slave, 5)>>
-	<<elseif $arcadeUpgradeHealth == 1 && _slave.health.condition < -30>>
-		<<run improveCondition(_slave, 2)>>
-	<<elseif _slave.health.condition > -50>>
-		<<run healthDamage(_slave, 3)>>
-	<<elseif _slave.health.condition > -90>>
-		<<run healthDamage(_slave, 1)>>
-	<</if>>
-	/* Curatives */
-	<<if $arcadeUpgradeHealth == 2>>
-		<<set _slave.curatives = 2>>
-	<<elseif $arcadeUpgradeHealth == 1>>
-		<<set _slave.curatives = 1>>
-	<</if>>
-	/* Stimulation or lack thereof trust/devotion effect */
-	<<if ($arcadeUpgradeInjectors > 0)>>
-		<<if $arcadeUpgradeInjectors == 2>>
-			<<set _slave.aphrodisiacs = 2>>
-		<</if>>
-		<<set _slave.devotion -= 5, _slave.trust -= 10>>
-	<<else>>
-		<<set _slave.trust -= 5>>
-	<</if>>
-	/* Muscles */
-	<<if (_slave.muscles > -100)>>
-		<<set _slave.muscles-->>
-	<</if>>
-
-	<<if $showEWD != 0>>
-		<br><br>
-		/* 000-250-006 */
-		<<if $seeImages && $seeReportImages>>
-			<div class="imageRef tinyImg">
-				<<= SlaveArt(_slave, 0, 0)>>
-			</div>
-		<</if>>
-		/* 000-250-006 */
-		<<includeDOM App.EndWeek.favoriteIcon(_slave)>>
-		<span class='slave-name'><<= SlaveFullName(_slave)>></span>
-		<<if _slave.choosesOwnAssignment == 2>>
-			<<= App.SlaveAssignment.choosesOwnJob(_slave)>>
-		<<else>>
-			is confined in $arcadeName.
-		<</if>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= App.SlaveAssignment.workAGloryHole(_slave)>>
-	<<else>>
-		<<run App.SlaveAssignment.choosesOwnJob(_slave)>>
-		<<run App.SlaveAssignment.workAGloryHole(_slave)>>
-	<</if>>
-	<<if ($arcadeUpgradeCollectors > 0)>>
-		<<if (_slave.vasectomy == 1)>>
-			<<set _slave.vasectomy = 0, _vasectomiesUndone++>>
-			<<run cashX(forceNeg($surgeryCost), "slaveSurgery", _slave), surgeryDamage(_slave, 10)>>
-		<<elseif (_slave.lactation < 2)>>
-			<<set _slave.lactation = 2, _boobsImplanted++>>
-			<<set _bSlave = _slave>>
-			<<run cashX(forceNeg($surgeryCost), "slaveSurgery", _slave), surgeryDamage(_slave, 10)>>
-		<<elseif _slave.prostate == 1>>
-			<<set _slave.prostate = 2, _prostatesImplanted++>>
-			<<set _pSlave = _slave>>
-			<<run cashX(forceNeg($surgeryCost), "slaveSurgery", _slave), surgeryDamage(_slave, 10)>>
-		<<elseif (_slave.lactation > 0) || (_slave.balls > 0)>>
-			<<set _milkResults = App.SlaveAssignment.getMilked(_slave, 1.0)>>
-			<<if $showEWD != 0>>
-				<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= _milkResults.text>>
-			<</if>>
-			<<set _milkProfits += _milkResults.cash>>
-			<<set _growth = 0>>
-			<<set _gigantomastiaMod = _slave.geneticQuirks.gigantomastia == 2 ? (_slave.geneticQuirks.macromastia == 2 ? 3 : 2) : 1>>
-			<<if (_slave.boobs < 2000)>>
-				<<set _growth = 100>>
-			<<elseif (_slave.boobs < 5000*_gigantomastiaMod)>>
-				<<set _growth = 50>>
-			<<elseif (_slave.boobs < 10000*_gigantomastiaMod)>>
-				<<set _growth = 25>>
-			<</if>>
-			<<if (_slave.geneMods.NCS == 1)>>
-				/*
-				** NCS will allow some growth for Arcade milking, but not as much as the Dairy.
-				*/
-				<<set _growth = Math.trunc(_growth/3.5)>>
-			<</if>>
-			<<set _slave.boobs += _growth>>
-			<<if ((_slave.balls > 0) && (_slave.balls < 10) && (random(1,100) > (40 + (10 * (_slave.balls + (2 * _slave.geneMods.NCS))))))>>
-				<<set _slave.balls++>>
-			<</if>>
-			<<if ((_slave.dick > 0) && (_slave.dick < 10) && (random(1,100) > (40 + (10 * _slave.dick + (2 * _slave.geneMods.NCS)))))>>
-				<<set _slave.dick++>>
-			<</if>>
-			<<if (_slave.lactation > 0)>>
-				<<set _milked++>>
-				<<set _mSlave = _slave>>
-			<</if>>
-			<<if (_slave.balls > 0)>>
-				<<set _cockmilked++>>
-				<<set _cSlave = _slave>>
-			<</if>>
-		<</if>>
-	<</if>>
-	<<if (_slave.inflation > 0)>>
-		<<run deflate(_slave)>>
-	<</if>>
-	<<if $showEWD != 0>>
-		<br>&nbsp;&nbsp;&nbsp;
-		<<includeDOM App.SlaveAssignment.rules(_slave)>>
-		<<= App.SlaveAssignment.diet(_slave)>>
-		<<includeDOM App.SlaveAssignment.longTermEffects(_slave)>>
-		<<= App.SlaveAssignment.drugs(_slave)>>
-		<<= App.SlaveAssignment.relationships(_slave)>>
-		<<= App.SlaveAssignment.rivalries(_slave)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;<<= App.SlaveAssignment.devotion(_slave)>>
-	<<else>>
-		<<run App.SlaveAssignment.rules()>>
-		<<run App.SlaveAssignment.diet(_slave)>>
-		<<run App.SlaveAssignment.longTermEffects(_slave)>>
-		<<run App.SlaveAssignment.drugs(_slave)>>
-		<<run App.SlaveAssignment.relationships(_slave)>>
-		<<run App.SlaveAssignment.rivalries(_slave)>>
-		<<run App.SlaveAssignment.devotion(_slave)>>
-	<</if>>
-<</for>>
-
-<<if _DL + $fuckdolls > 0>>
-	<br><br>&nbsp;&nbsp;&nbsp;
-	<<if _milked == 1>>
-		<<setLocalPronouns _mSlave>>
-		One of them is lactating and spends $his time in $arcadeName being simultaneously milked and fucked.
-	<<elseif _milked > 1>>
-		_milked of them are lactating and spend their time in $arcadeName being simultaneously milked and fucked.
-	<</if>>
-
-	<<if _vasectomiesUndone>>
-		<<if _vasectomiesUndone == 1>>
-			One
-		<<else>>
-			_vasectomiesUndone
-		<</if>>
-		of them had severed vas deferens, so they were reattached to allow sperm through, costing @@.red;<<print cashFormat($surgeryCost*_vasectomiesUndone)>>.@@
-	<</if>>
-	<<if _boobsImplanted>>
-		<<if _boobsImplanted == 1>>
-			<<setLocalPronouns _bSlave>>
-			One of them was not lactating, so $he was
-		<<else>>
-			_boobsImplanted of them were not lactating, so they were
-		<</if>>
-		implanted with long-acting lactation inducing drugs, costing @@.red;<<print cashFormat($surgeryCost*_boobsImplanted)>>.@@
-	<</if>>
-	<<if _prostatesImplanted>>
-		<<if _prostatesImplanted == 1>>
-			<<setLocalPronouns _pSlave>>
-			One of them was not producing the maximum possible amount of precum, so $he was
-		<<else>>
-			_prostatesImplanted of them were not producing the maximum possible amount of precum, so they were
-		<</if>>
-		implanted with long-acting prostate stimulation drugs, costing @@.red;<<print cashFormat($surgeryCost*_prostatesImplanted)>>.@@
-	<</if>>
-	<<if _cockmilked == 1>>
-		<<setLocalPronouns _cSlave>>
-		One of them retains testicles and is brutally cockmilked as $he is used.
-	<<elseif _cockmilked > 1>>
-		_cockmilked of them retain testicles and are brutally cockmilked as they are used.
-	<</if>>
-
-	The arcade makes you
-	<<if $policies.publicFuckdolls == 0>>
-		@@.yellowgreen;<<print cashFormat(_profits)>>@@ from selling the inmates' holes
-	<<else>>
-		@@.green;more reputable@@ from freely providing the inmates' holes
-	<</if>>
-	<<if $arcadeUpgradeCollectors > 0 && $policies.publicFuckdolls == 0>>
-		and
-	<</if>>
-	<<if $arcadeUpgradeCollectors > 0>>
-		@@.yellowgreen;<<print cashFormat(_milkProfits)>>@@ from selling the fluids they produced
-	<</if>>
-	this week.
-	<<if ($arcologies[0].FSPaternalist > 20)>>
-		<<run repX(forceNeg(Math.trunc(_profits/20)), "arcade")>>
-	<</if>>
-<</if>>
-
-<<if ($arcadeUpgradeFuckdolls == 2)>>
-	<<set _currentSlave = 0, _Age = -1, _FD = -1, _MB = -1, _Con = -1>>
-	<<for _slave range _slaves>>
-		<<if _slave.sentence == 0>> /* let's not convert slaves we are punishing into Fuckdolls */
-			<<if (_slave.fetish == "mindbroken")>>
-				<<if (_slave.fuckdoll == 0)>>
-					<<if (_slave.physicalAge > 35)>>
-						<<set _Age = $i>>
-						<<break>>
-					<<elseif _FD == -1>>
-						<<set _FD = $i>>
-					<</if>>
-				<<elseif _MB == -1>>
-					<<if _slave.vagina >= 4 || _slave.anus >= 4>>
-						<<set _Con = $i>>
-					<<elseif _slave.physicalAge > 35>>
-						<<set _Age = $i>>
-					<<else>>
-						<<set _MB = $i>>
-					<</if>>
-				<</if>>
-			<<elseif _Con == -1>>
-				<<if _slave.vagina >= 4 || _slave.anus >= 4>>
-					<<set _Con = $i>>
-				<<elseif _slave.physicalAge > 35>>
-					<<set _Age = $i>>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</for>>
-	<<if _FD > -1>>
-		<<set _currentSlave = $slaves[_FD]>>
-	<<elseif _Con > -1>>
-		<<set _currentSlave = $slaves[_Con]>>
-	<<elseif _MB > -1>>
-		<<set _currentSlave = $slaves[_MB]>>
-	<<elseif _Age > -1>>
-		<<set _currentSlave = $slaves[_Age]>>
-	<</if>>
-	<<if _currentSlave != 0>>
-		<<run App.Utils.setLocalPronouns(_currentSlave)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;_currentSlave.slaveName is low-quality merchandise, so $he has been converted into a Fuckdoll.
-		<<= removeSlave(_currentSlave)>>
-		<<if _currentSlave == 0>> /% if not zero then technically there was an error INVALID SLAVE %/
-			<<set $fuckdolls++, _SL-->>
-		<</if>>
-	<<else>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;No slaves have failed quality inspection for Fuckdoll conversion. _arcadeNameCaps will remain overcrowded this week.
-	<</if>>
-<</if>>
-
-<<if _DL > 0>>
-	<!-- Record statistics gathering -->
-	<<script>>
-		var b = State.variables.facility.arcade;
-		b.whoreIncome = 0;
-		b.customers = 0;
-		b.whoreCosts = 0;
-		b.rep = 0;
-		for (var si of b.income.values()) {
-			b.whoreIncome += si.income;
-			b.customers += si.customers;
-			b.whoreCosts += si.cost;
-			b.rep += si.rep;
-		}
-		b.maintenance = State.variables.arcade * State.variables.facilityCost * (0.05 + 0.02 * State.variables.arcadeUpgradeInjectors + 0.05 * State.variables.arcadeUpgradeCollectors);
-		b.totalIncome = b.whoreIncome;
-		b.totalExpenses = b.whoreCosts + b.maintenance;
-		b.profit = b.totalIncome - b.totalExpenses;
-	<</script>>
-
-	<!-- Statistics output -->
-	<<includeDOM App.Facilities.Arcade.Stats(false)>>
-	<<timed 50ms>>
-		<<replace #arcade-stats>>
-			<<includeDOM App.Facilities.Arcade.Stats(true)>>
-		<</replace>>
-	<</timed>>
-	<br><br>
-<</if>>
diff --git a/src/uncategorized/bodyModification.js b/src/uncategorized/bodyModification.js
deleted file mode 100644
index 1fec8a3a5e371f1596f0eb166d239ab22b120d00..0000000000000000000000000000000000000000
--- a/src/uncategorized/bodyModification.js
+++ /dev/null
@@ -1,345 +0,0 @@
-App.Medicine.Modification.Select.brand = function(slave, cheat = false) {
-	const el = new DocumentFragment();
-	let p = document.createElement('p');
-	let div = document.createElement('div');
-	const {his, His, him, He} = getPronouns(slave);
-
-	p.classList.add("indent");
-	for (const brandPlace in slave.brand) {
-		div = document.createElement('div');
-		div.append(`${His} ${brandPlace} is marked with ${slave.brand[brandPlace]}`);
-		if (slave.brand[brandPlace] === V.brandDesign.official) {
-			div.append(`, your `);
-			div.append(App.UI.DOM.passageLink("official brand", "Universal Rules"));
-		}
-		div.append(": ");
-		if (!cheat) {
-			div.append(
-				App.UI.DOM.link(
-					"Remove Brand",
-					() => {
-						V.brandApplied = 0;
-						delete slave.brand[brandPlace];
-						cashX(forceNeg(V.surgeryCost), "slaveSurgery", slave);
-						V.degradation -= 10;
-					},
-					[],
-					"Body Modification"
-				)
-			);
-		} else {
-			div.append(
-				App.UI.DOM.link(
-					"Remove Brand",
-					() => {
-						delete slave.brand[brandPlace];
-						App.Medicine.Modification.Select.brand(slave, cheat); // Refresh display
-					},
-				)
-			);
-		}
-		p.append(div);
-	}
-
-	if (jQuery.isEmptyObject(slave.brand)) {
-		App.UI.DOM.appendNewElement("div", p, `${His} skin is unmarked.`);
-	}
-
-	if (!(Object.values(slave.brand).includes(V.brandDesign.official))) {
-		div = document.createElement('div');
-		div.append(`${He} lacks your `);
-		div.append(App.UI.DOM.passageLink("official brand", "Universal Rules"));
-		div.append(`, "${V.brandDesign.official}."`);
-		p.append(div);
-	}
-
-	el.append(p);
-	p = document.createElement('p');
-	p.classList.add("indent");
-
-
-	div = document.createElement('div');
-	div.append(`Use ''${V.brandDesign.local}'' or choose another brand: `);
-	div.append(symbolOptions("personal"));
-	p.append(div);
-
-	p.append(symbolBlock("dirtyWord"));
-	p.append(symbolBlock("genitalSymbol"));
-	p.append(symbolBlock("silhouettes"));
-	p.append(symbolBlock("FS"));
-
-	div = document.createElement('div');
-	div.classList.add("indent");
-	div.append(`Or design your own: `);
-	div.append(
-		App.UI.DOM.makeTextBox(
-			V.brandDesign.local,
-			v => {
-				V.brandDesign.local = v;
-				App.Medicine.Modification.Select.brand(slave, cheat); // Refresh display
-			},
-		)
-	);
-	p.append(div);
-	el.append(p);
-
-	p = document.createElement('p');
-	p.classList.add("indent");
-	App.UI.DOM.appendNewElement("div", p, "Choose a site for branding: ");
-	const body = slaveBody();
-	p.append(partLinks(body.head));
-	p.append(partLinks(body.torso));
-	p.append(partLinks(body.arms));
-	p.append(partLinks(body.legs));
-
-	div = document.createElement('div');
-	div.classList.add("indent");
-	div.append(`Or a custom site:  `);
-	div.append(
-		App.UI.DOM.makeTextBox(
-			V.brandTarget.local,
-			v => {
-				V.brandTarget.local = v;
-				App.Medicine.Modification.Select.brand(slave, cheat); // Refresh display
-			},
-		)
-	);
-	p.append(div);
-	el.append(p);
-
-	p = document.createElement('p');
-	p.classList.add("indent");
-
-	if (["ankle", "breast", "buttock", "calf", "cheek", "ear", "foot", "hand", "lower arm", "shoulder", "testicle", "thigh", "upper arm", "wrist"].includes(V.brandTarget.local)) {
-		const leftTarget = ("left " + V.brandTarget.local);
-		const rightTarget = ("right " + V.brandTarget.local);
-		if (slave.brand[leftTarget]) {
-			p.append(`${His} ${leftTarget} is already marked with ${slave.brand[leftTarget]}. `);
-		}
-		if (slave.brand[rightTarget]) {
-			p.append(`${His} ${rightTarget} is already marked with ${slave.brand[rightTarget]}. `);
-		}
-		p.append(`Brand ${him} now with ''${V.brandDesign.local}'' on the `); // todo: break out bold
-		let _left;
-		let _right;
-		if (
-			!(["upper arm", "lower arm", "wrist", "hand"].includes(V.brandTarget.local) && getLeftArmID(slave) !== 1) &&
-			!(["thigh", "calf", "ankle", "foot"].includes(V.brandTarget.local) && getLeftLegID(slave) !== 1)
-		) {
-			_left = 1;// make next checks easier
-			if (!cheat) {
-				p.append(
-					App.UI.DOM.link(
-						"left",
-						() => {
-							V.brandApplied = 1;
-							slave.brand[leftTarget] = check(V.brandDesign.local);
-							cashX(forceNeg(V.modCost), "slaveMod", slave);
-							V.degradation += 10;
-						},
-						[],
-						"Body Modification"
-					)
-				);
-			} else {
-				p.append(
-					App.UI.DOM.link(
-						"left",
-						() => {
-							slave.brand[leftTarget] = check(V.brandDesign.local);
-						},
-					)
-				);
-			}
-
-			if (!(["upper arm", "lower arm", "wrist", "hand"].includes(V.brandTarget.local) && getRightArmID(slave) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes(V.brandTarget.local) && getRightLegID(slave) !== 1)) {
-				_right = 1; // make next checks easier
-			}
-			if (_left && _right) {
-				p.append(` ${V.brandTarget.local}, or the `);
-			}
-			if (_right) {
-				if (!cheat) {
-					p.append(
-						App.UI.DOM.link(
-							"right",
-							() => {
-								V.brandApplied = 1;
-								slave.brand[rightTarget] = check(V.brandDesign.local);
-								cashX(forceNeg(V.modCost), "slaveMod", slave);
-								V.degradation += 10;
-							},
-							[],
-							"Body Modification"
-						)
-					);
-				} else {
-					p.append(
-						App.UI.DOM.link(
-							"right",
-							() => {
-								slave.brand[rightTarget] = check(V.brandDesign.local);
-							},
-						)
-					);
-				}
-			}
-			p.append(`? `);
-			if (!_left || !_right) {
-				p.append(` ${V.brandTarget.local}`);
-				App.UI.DOM.appendNewElement("span", p, `Branding will slightly reduce ${his} beauty but may slowly increase your reputation.`, "note");
-			}
-		}
-	} else {
-		if (slave.brand[V.brandTarget.local] === V.brandDesign.local) {
-			p.append(`${He} already has ${V.brandDesign.local} on ${his} ${V.brandTarget.local}.`);
-		} else {
-			if (!cheat) {
-				p.append(
-					App.UI.DOM.link(
-						"Brand",
-						() => {
-							V.brandApplied = 1;
-							slave.brand[V.brandTarget.local] = V.brandDesign.local;
-							cashX(forceNeg(V.modCost), "slaveMod", slave);
-							V.degradation += 10;
-						},
-						[],
-						"Body Modification"
-					)
-				);
-			} else {
-				p.append(
-					App.UI.DOM.link(
-						"Brand",
-						() => {
-							slave.brand[V.brandTarget.local] = V.brandDesign.local;
-						},
-					)
-				);
-			}
-			p.append(` with ${V.brandDesign.local} on the ${V.brandTarget.local}`);
-			if (slave.brand[V.brandTarget.local]) {
-				p.append(`, covering the "${slave.brand[V.brandTarget.local]}" that is already there? `);
-			} else {
-				p.append(`. `);
-			}
-			App.UI.DOM.appendNewElement("span", p, `Branding will slightly reduce ${his} beauty but may slowly increase your reputation.`, "note");
-		}
-	}
-	el.append(p);
-	return jQuery('#brand-selection').empty().append(el);
-
-	function symbolBlock(brandList) {
-		const div = document.createElement('div');
-		div.classList.add("double-choices");
-		div.style.textIndent = "0px";
-		div.append(symbolOptions(brandList));
-		return div;
-	}
-
-	function symbolOptions(brandList) {
-		const list = App.Medicine.Modification.Brands[brandList];
-		const array = [];
-		for (const brand in list) {
-			const frag = new DocumentFragment();
-			if (!cheat && list[brand].hasOwnProperty("requirements")) {
-				if (!list[brand].requirements(slave)) {
-					continue;
-				}
-			}
-			if (brandList === "FS") {
-				App.UI.DOM.appendNewElement("span", frag, "FS ", "note");
-			}
-			frag.append(
-				App.UI.DOM.link(
-					list[brand].displayName,
-					() => {
-						V.brandDesign.local = check(brand);
-						App.Medicine.Modification.Select.brand(slave, cheat); // Refresh display
-					}
-				)
-			);
-			array.push(frag);
-		}
-		return App.UI.DOM.generateLinksStrip(array);
-	}
-
-	function slaveBody() {
-		const body = {};
-		// Sorted head to toe
-		// Head
-		body.head = {};
-		if (slave.earShape !== "none") {
-			body.head.ears = "Ears";
-		}
-		body.head.cheek = "Cheeks";
-		body.head.neck = "Neck";
-
-		// Torso
-		body.torso = {};
-		body.torso.chest = "Chest";
-		body.torso.breast = "Breasts";
-		body.torso.back = "Back";
-		body.torso["lower back"] = "Lower Back";
-		body.torso.belly = "Belly";
-		body.torso["pubic mound"] = "Pubic Mound";
-
-		if (slave.dick > 0) {
-			body.torso.penis = "Penis";
-		}
-		if (slave.balls > 0 && slave.scrotum > 0) {
-			body.torso.testicle = "Testicles";
-		}
-
-		// Arms
-		body.arms = {};
-		body.arms.shoulder = "Shoulders";
-		if (hasAnyNaturalArms(slave)) {
-			body.arms["upper arm"] = "Arm, upper";
-			body.arms["lower arm"] = "Arm, lower";
-			body.arms.wrist = "Wrists";
-			body.arms.hand = "Hands";
-		}
-
-		// Legs
-		body.legs = {};
-		body.legs.buttock = "Buttocks";
-		if (hasAnyNaturalLegs(slave)) {
-			body.legs.thigh = "Thighs";
-			body.legs.calf = "Calves";
-			body.legs.ankle = "Ankles";
-			body.legs.foot = "Feet";
-		}
-		return body;
-	}
-
-	function partLinks(bodyPartObj) {
-		const div = document.createElement("div");
-		div.classList.add("double-choices");
-		div.style.textIndent = "0px";
-		const array = [];
-		for (const bp in bodyPartObj) {
-			array.push(
-				App.UI.DOM.link(
-					bodyPartObj[bp],
-					() => {
-						V.brandTarget.local = check(bp);
-						App.Medicine.Modification.Select.brand(slave, cheat); // Refresh display
-					}
-				)
-			);
-		}
-		div.append(App.UI.DOM.generateLinksStrip(array));
-		return div;
-	}
-
-	function check(brand) {
-		switch(brand) {
-			case "a big helping of your favorite food":
-				return 	"a big helping of " + V.PC.refreshment;
-			default:
-				return brand;
-		}
-	}
-};
diff --git a/src/uncategorized/bodyModification.tw b/src/uncategorized/bodyModification.tw
deleted file mode 100644
index 5cfb1adedfca915ff626ee7dccf7c079b47bddbf..0000000000000000000000000000000000000000
--- a/src/uncategorized/bodyModification.tw
+++ /dev/null
@@ -1,854 +0,0 @@
-:: Body Modification [nobr jump-from-safe]
-
-<<set $nextButton = "Confirm changes", $nextLink = "Slave Interact">>
-<<run App.Utils.setLocalPronouns(getSlave($AS))>>
-<<run Enunciate(getSlave($AS))>>
-
-<<set $encyclopedia = "The Studio">>
-
-<h1>Body Modification Studio</h1>
-<p class="scene-intro">
-	<<= SlaveFullName(getSlave($AS))>> is lying strapped down on the table in your body modification studio. $He is entirely at your mercy.
-
-	<<if $brandApplied || $degradation || $scarApplied || $modReaction>>
-		<<if getSlave($AS).fuckdoll == 0>>
-			<<if canSee(getSlave($AS))>>There's a mirror on the ceiling, so $he can see<<else>>$He can't see, so <<if canHear(getSlave($AS))>>you're careful to describe<<else>>$he must, by $himself, get a feel for<</if>><</if>> $his new appearance.
-		<</if>>
-		<<if $brandApplied>>
-			The smell of burnt flesh hangs in the air. Being branded @@.red;has hurt $his health a little.@@
-			<<run healthDamage(getSlave($AS), 10)>>
-			<<unset $brandApplied>>
-		<</if>>
-		<<if $scarApplied>>
-			<<if $scarTarget.local == "entire body">>
-				<<switch $scarDesign.local>>
-					<<case "burn">>
-						Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the skin with an open flame.
-					<<case "surgical">>
-						<<if $PC.skill.medicine >= 100>>
-							Your medical mastery is perfect, so creating Frankenstein's monster was a deliberate work of art.
-						<<elseif $PC.skill.medicine > 0>>
-							Your medical skills are progressing, and the Frankenstein effect reminds you of your earliest attempts.
-						<<else>>
-							You really slashed away with your knife, but were careful not to allow $him to bleed out.
-						<</if>>
-					<<default>>
-						The best way to apply scarring to the entire body is with a good old fashioned whip. $His body is a mess of crisscrossed lines<<if hasAnyNaturalLimbs(getSlave($AS))>>, and $his <<if getLimbCount(getSlave($AS), 1) > 1>>limbs twisted so violently in their restraints that they too have<<else>>only limb twists so violently in its restraints that it too has<</if>> become scarred<</if>>.
-				<</switch>>
-				No matter how you chose to apply it, scarring so much of $his body has @@.red; hurt $his health.@@
-				<<run healthDamage(getSlave($AS), 20)>>
-			<<else>>
-				<<if getSlave($AS).scar[$scarTarget.local][$scarDesign.local] > 0>>
-					This is not the first time $he was scarred like this.
-				<</if>>
-				<<switch $scarDesign.local>>
-					<<case "whip">>
-						Targeting a single area with a whip is not easy. You set the mood by carefully arranging candles dripping on to a whimpering <<= getSlave($AS).slaveName>>, then got $his attention with a quick
-						<<if canSee(getSlave($AS))>>
-							wave
-						<<elseif canHear(getSlave($AS))>>
-							crack
-						<<else>>
-							tap
-						<</if>>
-						of the whip. One by one, you carefully snuffed out the candles, flicking hot wax as you went. After pausing a moment, you prepared to leave your mark.
-						<<if ["penis", "vagina"].includes($scarTarget.local)>>
-							<<if getSlave($AS).dick > 4 && $seeDicks>>
-								$His dick was large enough that it was not too difficult to hit,
-							<<elseif getSlave($AS).dick > 0 && $seeDicks>>
-								$His dick was a challengingly small target,
-							<<else>>
-								<<if getSlave($AS).clit > 0>>
-									$His clit was a difficult target,
-								<<else>>
-									$His clit was an impossibly tiny target,
-								<</if>>
-							<</if>>
-							but the end was never in doubt. The tip connected with $his most intimate place on the first try, and plunged $him into absolute agony.
-						<<else>>
-							The	end was never in doubt. A few strokes of the whip plunged $him into agony $his body will not allow $him to forget.
-						<</if>>
-					<<case "burn">>
-						Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the <<= getSlave($AS).skin>> skin of $his $scarTarget.local with an open flame.
-					<<case "surgical">>
-						<<if $PC.skill.medicine >= 100>>
-							Your medical mastery is perfect, so creating such a scar was a deliberate act of degradation.
-						<<elseif $PC.skill.medicine > 0>>
-							Your medical skills are progressing, and the sloppy scar reminds you of your earliest attempts.
-						<<else>>
-							You really slashed away at $scarTarget.local with your knife, but were careful not to allow $him to bleed out.
-						<</if>>
-					<<default>>
-						You had no shortage of kinky and medical tools for applying scars. $His <<= getSlave($AS).skin>> $scarTarget.local is bleeding profusely.
-				<</switch>>
-
-				No matter how you chose to apply it, being scarred @@.red; hurt $his health a little.@@
-				<<run healthDamage(getSlave($AS), 10)>>
-			<</if>>
-			Afterwards you seal the wounds with a white medical spray. Infection is no risk to <<= getSlave($AS).slaveName>> thanks to your curatives, but in order to form obvious scar tissue you had to keep air out and delay normal healing as long as possible.
-			<<unset $scarApplied>>
-		<</if>>
-		<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
-			<<if $degradation > 1>>
-				<<if $degradation > 5>>
-					<<if getSlave($AS).devotion <= 50 && getSlave($AS).trust < -50>>
-						$He is appalled by the whorish spectacle you have made of $him. $He @@.gold;fears@@ you all the more for this but is so terrified of you it does not affect $his submission.
-						<<set getSlave($AS).trust -= 10>>
-					<<elseif getSlave($AS).devotion <= 50>>
-						$He is appalled by the whorish spectacle you have made of $him. $He @@.mediumorchid;hates@@ and @@.gold;fears@@ you for this.
-						<<set getSlave($AS).devotion -= 10, getSlave($AS).trust -= 10>>
-					<<else>>
-						$He is shocked by the whorish spectacle you have made of $him. However, $he is so submissive to your will that $he @@.hotpink;accepts@@ that the slave <<if canSee(getSlave($AS))>>in the mirror<<else>>$he pictures<</if>> is who $he is now.
-						<<set getSlave($AS).devotion += 4>>
-					<</if>>
-					<br><br>
-				<<else>>
-					<<if getSlave($AS).devotion < -20 && getSlave($AS).trust < 20>>
-						$He is @@.gold;afraid@@ that $he has been permanently altered against $his will, but is also scared of your reaction to any objection and suppresses $his disgust.
-						<<set getSlave($AS).trust -= 5>>
-					<<elseif getSlave($AS).devotion < -20>>
-						$He is @@.mediumorchid;angry@@ and @@.gold;afraid@@ that $he has been permanently altered against $his will.
-						<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 5>>
-					<<else>>
-						$He is saddened to have been altered against $his will. However, $he realizes that $he is a slave, so $he @@.hotpink;accepts@@ your work.
-						<<set getSlave($AS).devotion += 2>>
-					<</if>>
-				<</if>>
-				<<set $degradation = 0>>
-			<</if>>
-			<<if $modReaction>>
-				<<print $modReaction>>
-				<<unset $modReaction>>
-			<</if>>
-		<</if>>
-	<</if>>
-
-	/* 000-250-006 */
-	<<if $seeImages == 1>>
-		<<if $imageChoice == 1>>
-			<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt(getSlave($AS), 3, 0)>></div>
-		<<else>>
-			<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt(getSlave($AS), 3, 0)>></div>
-		<</if>>
-	<</if>>
-	/* 000-250-006 */
-</p>
-
-/* PIERCINGS */
-
-<<set _piercingCount =(getSlave($AS).earPiercing + getSlave($AS).nosePiercing + getSlave($AS).eyebrowPiercing + getSlave($AS).lipsPiercing + getSlave($AS).tonguePiercing + getSlave($AS).nipplesPiercing + getSlave($AS).areolaePiercing + getSlave($AS).corsetPiercing + getSlave($AS).navelPiercing + getSlave($AS).clitPiercing + getSlave($AS).vaginaPiercing + getSlave($AS).dickPiercing + getSlave($AS).anusPiercing)>>
-
-/*DESCRIPTIONS */
-
-<h2>Piercings</h2>
-
-<div class="indent">
-	<div style="padding-bottom:1em">
-		<<if _piercingCount == 0>>
-			$His smooth <<= getSlave($AS).skin>> skin is completely unpierced.
-		<</if>>
-		<div><<= App.Desc.piercing(getSlave($AS), "ear")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "nose")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "eyebrow")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "lips")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "tongue")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "nipple")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "areolae")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "navel")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "corset")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "clit")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "vagina")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "dick")>></div>
-		<div><<= App.Desc.piercing(getSlave($AS), "anus")>></div>
-
-		<div><<= App.Desc.piercing(getSlave($AS), "chastity")>></div>
-	</div>
-
-	/* Apply piercings */
-
-	<div>
-		Choose piercing style:
-		<<if $piercingLevel != 1>>[[Light|Body Modification][$piercingLevel = 1]]<<else>>Light<</if>>
-		<<if $piercingLevel != 2>>| [[Heavy|Body Modification][$piercingLevel = 2]]<<else>>| Heavy<</if>>
-		<<if $piercingLevel != 3>>| [[Smart|Body Modification][$piercingLevel = 3]]<<else>>| Smart<</if>>
-		<<if $piercingLevel != 0>>| [[Remove|Body Modification][$piercingLevel = 0]]<<else>>| Remove<</if>>
-	</div>
-
-	<div>
-		<<if $piercingLevel == 1>>
-			//Lightly// pierce $his:
-
-			<<link "Entire body">>
-				<<set $modReaction = "">>
-				<<if getSlave($AS).earPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 1)>><</if>>
-				<<if getSlave($AS).nosePiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 1)>><</if>>
-				<<if getSlave($AS).eyebrowPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 1)>><</if>>
-				<<if getSlave($AS).lipsPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 1)>><</if>>
-				<<if getSlave($AS).tonguePiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 1)>><</if>>
-				<<if getSlave($AS).nipples != "fuckable">>
-					<<if getSlave($AS).nipplesPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 1)>><</if>>
-				<</if>>
-				<<if getSlave($AS).areolaePiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 1)>><</if>>
-				<<if getSlave($AS).corsetPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 1)>><</if>>
-				<<if getSlave($AS).navelPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 1)>><</if>>
-				<<if (getSlave($AS).vagina != -1) || (getSlave($AS).dick != 0)>>
-					<<if getSlave($AS).clitPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 1)>><</if>>
-				<</if>>
-				<<if (getSlave($AS).vagina != -1)>>
-					<<if getSlave($AS).vaginaPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 1)>><</if>>
-				<</if>>
-				<<if (getSlave($AS).dick > 0)>>
-					<<if getSlave($AS).dickPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 1)>><</if>>
-				<</if>>
-				<<if getSlave($AS).anusPiercing != 1>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 1)>><</if>>
-				<<goto "Body Modification">>
-			<</link>>
-
-			<<if getSlave($AS).earPiercing != 1>>		| [[Ear|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 1)]]<</if>>
-			<<if getSlave($AS).nosePiercing != 1>>		| [[Nose|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 1)]]<</if>>
-			<<if getSlave($AS).eyebrowPiercing != 1>>	| [[Eyebrow|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 1)]]<</if>>
-			<<if getSlave($AS).lipsPiercing != 1>>		| [[Lips|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 1)]]<</if>>
-			<<if getSlave($AS).tonguePiercing != 1>>	| [[Tongue|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 1)]]<</if>>
-			<<if getSlave($AS).nipples != "fuckable">>
-				<<if getSlave($AS).nipplesPiercing != 1>>	| [[Nipples|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 1)]]<</if>>
-			<</if>>
-			<<if getSlave($AS).areolaePiercing != 1>>	| [[Areolae|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 1)]]<</if>>
-			<<if getSlave($AS).corsetPiercing != 1>>	| [[Corset|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 1)]]<</if>>
-			<<if getSlave($AS).navelPiercing != 1>>		| [[Navel|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 1)]]<</if>>
-			<<if (getSlave($AS).vagina != -1) || (getSlave($AS).dick != 0)>>
-				<<if getSlave($AS).vagina != -1>>
-					<<if getSlave($AS).clitPiercing != 1>>	| [[Clit|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 1)]]<</if>>
-				<<else>>
-					<<if getSlave($AS).clitPiercing != 1>>	| [[Dickhead|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 1)]]<</if>>
-				<</if>>
-			<</if>>
-			<<if (getSlave($AS).vagina != -1)>>
-				<<if getSlave($AS).vaginaPiercing != 1>> | [[Vagina|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 1)]]<</if>>
-			<</if>>
-			<<if (getSlave($AS).dick > 0)>>
-				<<if getSlave($AS).dickPiercing != 1>>	| [[Dick|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 1)]]<</if>>
-			<</if>>
-			<<if getSlave($AS).anusPiercing != 1>>		| [[Anus|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 1)]]<</if>>
-		<<elseif $piercingLevel == 2>>
-			''Heavily'' pierce $his:
-
-			<<link "Entire body">>
-				<<set $modReaction = "">>
-				<<if getSlave($AS).earPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).nosePiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).eyebrowPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).lipsPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).tonguePiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).nipples != "fuckable">>
-					<<if getSlave($AS).nipplesPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 2), $degradation += 1>><</if>>
-				<</if>>
-				<<if getSlave($AS).areolaePiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).corsetPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 2), $degradation += 1>><</if>>
-				<<if getSlave($AS).navelPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 2), $degradation += 1>><</if>>
-				<<if (getSlave($AS).vagina != -1) || (getSlave($AS).dick != 0)>>
-					<<if getSlave($AS).clitPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 2), $degradation += 1>><</if>>
-				<</if>>
-				<<if (getSlave($AS).vagina != -1)>>
-					<<if getSlave($AS).vaginaPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 2), $degradation += 1>><</if>>
-				<</if>>
-				<<if (getSlave($AS).dick > 0)>>
-					<<if getSlave($AS).dickPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 2), $degradation += 1>><</if>>
-				<</if>>
-				<<if getSlave($AS).anusPiercing != 2>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 2), $degradation += 1>><</if>>
-				<<goto "Body Modification">>
-			<</link>>
-
-			<<if getSlave($AS).earPiercing != 2>>		| [[Ear|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).nosePiercing != 2>>		| [[Nose|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).eyebrowPiercing != 2>>	| [[Eyebrow|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).lipsPiercing != 2>>		| [[Lips|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).tonguePiercing != 2>>	| [[Tongue|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).nipples != "fuckable">>
-				<<if getSlave($AS).nipplesPiercing != 2>>	| [[Nipples|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 2),$degradation += 1]]<</if>>
-			<</if>>
-			<<if getSlave($AS).areolaePiercing != 2>>	| [[Areolae|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).corsetPiercing != 2>>	| [[Corset|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 2),$degradation += 1]]<</if>>
-			<<if getSlave($AS).navelPiercing != 2>>		| [[Navel|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 2),$degradation += 1]]<</if>>
-			<<if (getSlave($AS).vagina != -1) || (getSlave($AS).dick != 0)>>
-				<<if getSlave($AS).vagina != -1>>
-					<<if getSlave($AS).clitPiercing != 2>> | [[Clit|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 2),$degradation += 1]]<</if>>
-				<<else>>
-					<<if getSlave($AS).clitPiercing != 2>> | [[Dickhead|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 2),$degradation += 1]]<</if>>
-				<</if>>
-			<</if>>
-			<<if (getSlave($AS).vagina != -1)>>
-				<<if getSlave($AS).vaginaPiercing != 2>> | [[Vagina|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 2),$degradation += 1]]<</if>>
-			<</if>>
-			<<if (getSlave($AS).dick > 0)>>
-				<<if getSlave($AS).dickPiercing != 2>>	| [[Dick|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 2),$degradation += 1]]<</if>>
-			<</if>>
-			<<if getSlave($AS).anusPiercing != 2>>		| [[Anus|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 2),$degradation += 1]]<</if>>
-		<<elseif $piercingLevel == 3>>
-			<<if (getSlave($AS).vagina != -1) || (getSlave($AS).dick != 0)>>
-				<<if getSlave($AS).clitPiercing != 3>>
-					Give $him a [[smart piercing?|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 3),getSlave($AS).clitSetting = "all",$degradation += 1]] //Costs <<print cashFormat($SPcost)>>, unlocks options to mold sexuality//
-				<<else>>
-					$He already has a smart piercing!
-				<</if>>
-			<</if>>
-		<<elseif $piercingLevel == 0>>
-			Remove piercings from:
-
-			/* no dick/vagina checks in 'remove' so stealth piercings can be cleaned. Check only for piercings. */
-			<<link "Everywhere">>
-				<<set $modReaction = "">>
-				<<if getSlave($AS).earPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 0)>><</if>>
-				<<if getSlave($AS).nosePiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 0)>><</if>>
-				<<if getSlave($AS).eyebrowPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 0)>><</if>>
-				<<if getSlave($AS).lipsPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 0)>><</if>>
-				<<if getSlave($AS).tonguePiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 0)>><</if>>
-				<<if getSlave($AS).nipplesPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 0)>><</if>>
-				<<if getSlave($AS).areolaePiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 0)>><</if>>
-				<<if getSlave($AS).corsetPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 0)>><</if>>
-				<<if getSlave($AS).navelPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 0)>><</if>>
-				<<if getSlave($AS).clitPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 0)>><</if>>
-				<<if getSlave($AS).vaginaPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 0)>><</if>>
-				<<if getSlave($AS).dickPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 0)>><</if>>
-				<<if getSlave($AS).anusPiercing > 0>><<set $modReaction += App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 0)>><</if>>
-				<<goto "Body Modification">>
-			<</link>>
-
-			<<if getSlave($AS).earPiercing > 0>>		| [[Ear|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "ear", 0)]]<</if>>
-			<<if getSlave($AS).nosePiercing > 0>>		| [[Nose|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nose", 0)]]<</if>>
-			<<if getSlave($AS).eyebrowPiercing > 0>>	| [[Eyebrow|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "eyebrow", 0)]]<</if>>
-			<<if getSlave($AS).lipsPiercing > 0>>		| [[Lips|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "lips", 0)]]<</if>>
-			<<if getSlave($AS).tonguePiercing > 0>>		| [[Tongue|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "tongue", 0)]]<</if>>
-			<<if getSlave($AS).nipplesPiercing > 0>>	| [[Nipples|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "nipples", 0)]]<</if>>
-			<<if getSlave($AS).areolaePiercing > 0>>	| [[Areolae|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "areolae", 0)]]<</if>>
-			<<if getSlave($AS).corsetPiercing > 0>>		| [[Corset|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "corset", 0)]]<</if>>
-			<<if getSlave($AS).navelPiercing > 0>>		| [[Navel|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "navel", 0)]]<</if>>
-			<<if getSlave($AS).vagina != -1>>
-				<<if getSlave($AS).clitPiercing > 0>>	| [[Clit|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 0)]]<</if>>
-			<<else>>
-				<<if getSlave($AS).clitPiercing > 0>>	| [[Dickhead|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "clit", 0)]]<</if>>
-			<</if>>
-			<<if getSlave($AS).vaginaPiercing > 0>>		| [[Vagina|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "vagina", 0)]]<</if>>
-			<<if getSlave($AS).dickPiercing > 0>>		| [[Dick|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "dick", 0)]]<</if>>
-			<<if getSlave($AS).anusPiercing > 0>>		| [[Anus|Body Modification][$modReaction = App.Medicine.Modification.setPiercing(getSlave($AS), "anus", 0)]]<</if>>
-		<</if>>
-	</div>
-</div> /* indent */
-
-/* TATTOOS */
-
-<h2>Tattoos</h2>
-<div class="indent">
-	<div style="padding-bottom:1em">
-		<<if getSlave($AS).shouldersTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "shoulder")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).lipsTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "lips")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).boobsTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "breast")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).armsTat != 0 && hasAnyNaturalArms(getSlave($AS))>><div><<= App.Desc.tattoo(getSlave($AS), "upper arm")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).backTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "back")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).stampTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "lower back")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).buttTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "buttock")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).vaginaTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "vagina")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).dickTat != 0 && getSlave($AS).dick > 0>><div><<= App.Desc.tattoo(getSlave($AS), "dick")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).anusTat != 0>><div><<= App.Desc.tattoo(getSlave($AS), "anus")>></div><<set _hasTat = 1>><</if>>
-		<<if getSlave($AS).legsTat != 0 && hasAnyNaturalLegs(getSlave($AS))>><div><<= App.Desc.tattoo(getSlave($AS), "thigh")>></div><<set _hasTat = 1>><</if>>
-	</div>
-
-	<div>
-		Choose a tattoo style:
-		/* if/else is here so a "highlighted" option is never a link. */
-		<<if $tattooChoice != "tribal patterns">>[[Tribal patterns|Body Modification][$tattooChoice = "tribal patterns"]]<<else>>Tribal patterns<</if>>
-		<<if $tattooChoice != "flowers">> | [[Flowers|Body Modification][$tattooChoice = "flowers"]]<<else>>| Flowers<</if>>
-		<<if $tattooChoice != "counting">> | [[Counting|Body Modification][$tattooChoice = "counting"]]<<else>>| Counting<</if>>
-		<<if $tattooChoice != "advertisements">> | [[Advertisements|Body Modification][$tattooChoice = "advertisements"]]<<else>>| Advertisements<</if>>
-		<<if $tattooChoice != "rude words">> | [[Rude words|Body Modification][$tattooChoice = "rude words"]]<<else>>| Rude words<</if>>
-		<<if $tattooChoice != "degradation">> | [[Degradation|Body Modification][$tattooChoice = "degradation"]]<<else>>| Degradation<</if>>
-		<<if $tattooChoice != "Asian art">> | [[Asian art|Body Modification][$tattooChoice = "Asian art"]] <<else>>| Asian art<</if>>
-		<<if $tattooChoice != "scenes">> | [[Scenes|Body Modification][$tattooChoice = "scenes"]]<<else>>| Scenes<</if>>
-		<<if $tattooChoice != "bovine patterns">> | [[Bovine|Body Modification][$tattooChoice = "bovine patterns"]]<<else>>| Bovine<</if>>
-		<<if $tattooChoice != "permanent makeup">> | [[Permanent makeup|Body Modification][$tattooChoice = "permanent makeup"]]<<else>>| Permanent makeup<</if>>
-		<<if $tattooChoice != "sacrilege">> | [[Sacrilege|Body Modification][$tattooChoice = "sacrilege"]]<<else>>| Sacrilege<</if>>
-		<<if $tattooChoice != "sacrament">> | [[Sacrament|Body Modification][$tattooChoice = "sacrament"]]<<else>>| Sacrament<</if>>
-		<<if $tattooChoice != "possessive">> | [[Possessive|Body Modification][$tattooChoice = "possessive"]]<<else>>| Possessive<</if>>
-		<<if $tattooChoice != "paternalist">> | [[Paternalist|Body Modification][$tattooChoice = "paternalist"]]<<else>>| Paternalist<</if>>
-
-		<<if getSlave($AS).anusTat == 0>>
-			<<if $tattooChoice != "bleached">>| [[Bleach|Body Modification][$tattooChoice = "bleached"]]<<else>>| Bleach<</if>>
-		<</if>>
-
-		<<if _hasTat == 1>>
-			<<if $tattooChoice !== 0>> | //[[Remove a tattoo|Body Modification][$tattooChoice = 0]]//<<else>>| Remove a tattoo<</if>>
-		<</if>>
-	</div>
-
-	<div>
-		<<if def $tattooChoice>>
-			<<if $tattooChoice == 0>>
-				Clean the ink off of $him:
-			<<elseif $tattooChoice == "counting">>
-				Add tallies of $his sexual exploits to $him:
-			<<elseif $tattooChoice == "bleached">>
-				Bleach $his:
-			<<else>>
-				Add $tattooChoice to $his:
-			<</if>>
-
-			<<if ($tattooChoice != "bleached") && ($tattooChoice != "permanent makeup")>>
-				<<link "Entire body">>
-					<<set _degradationTemp = 0, $modReaction = "">>
-					<<if getSlave($AS).boobsTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "boobs", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if getSlave($AS).buttTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "butt", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if getSlave($AS).vaginaTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "vagina", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if getSlave($AS).dick > 0>>
-						<<if getSlave($AS).dickTat != $tattooChoice && $tattooChoice != "scenes">>
-							<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "dick", $tattooChoice)>>
-							<<set _degradationTemp += 1>>
-						<</if>>
-					<</if>>
-
-					<<if getSlave($AS).lipsTat != $tattooChoice>>
-						<<if $tattooChoice == "scenes">>
-							<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "lips", "permanent makeup")>>
-						<<else>>
-							<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "lips", $tattooChoice)>>
-						<</if>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if ($tattooChoice == "Asian art" || $tattooChoice == "scenes") && getSlave($AS).anusTat == "bleached">>
-						/* leave existing bleached anus alone */
-					<<elseif getSlave($AS).anusTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "anus", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if getSlave($AS).shouldersTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "shoulders", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if getSlave($AS).backTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "back", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if hasAnyNaturalArms(getSlave($AS))>>
-						<<if getSlave($AS).armsTat != $tattooChoice>>
-							<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "arms", $tattooChoice)>>
-							<<set _degradationTemp += 1>>
-						<</if>>
-					<</if>>
-					<<if hasAnyNaturalLegs(getSlave($AS))>>
-						<<if getSlave($AS).legsTat != $tattooChoice>>
-							<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "legs", $tattooChoice)>>
-							<<set _degradationTemp += 1>>
-						<</if>>
-					<</if>>
-
-					<<if getSlave($AS).stampTat != $tattooChoice>>
-						<<set $modReaction += App.Medicine.Modification.setTattoo(getSlave($AS), "stamp", $tattooChoice)>>
-						<<set _degradationTemp += 1>>
-					<</if>>
-
-					<<if !["flowers", "paternalist", "tribal patterns", 0].includes($tattooChoice)>>
-						<<set $degradation += _degradationTemp>>
-					<</if>>
-
-					<<goto "Body Modification">>
-				<</link>>
-			<</if>>
-
-			<<if $tattooChoice == "permanent makeup">>
-				[[Face|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "lips", $tattooChoice)]]
-			<<elseif $tattooChoice == "bleached">>
-				[[Asshole|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "anus", $tattooChoice)]]
-			<<else>>
-				<<if getSlave($AS).shouldersTat != $tattooChoice>> | [[Shoulders|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "shoulders", $tattooChoice)]]<</if>>
-				<<if $tattooChoice != "scenes">>
-					<<if getSlave($AS).lipsTat != $tattooChoice>> | [[Face|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "lips", $tattooChoice)]]<</if>>
-				<</if>>
-				<<if getSlave($AS).boobsTat != $tattooChoice>> | [[Boobs|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "boobs", $tattooChoice)]]<</if>>
-				<<if hasAnyNaturalArms(getSlave($AS))>>
-					<<if getSlave($AS).armsTat != $tattooChoice>> | [[Arms|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "arms", $tattooChoice)]]<</if>>
-				<</if>>
-				<<if getSlave($AS).backTat != $tattooChoice>> | [[Upper back|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "back", $tattooChoice)]]<</if>>
-				<<if getSlave($AS).stampTat != $tattooChoice>> | [[Lower back|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "stamp", $tattooChoice)]]<</if>>
-				<<if getSlave($AS).buttTat != $tattooChoice>> | [[Buttock|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "butt", $tattooChoice)]]<</if>>
-				<<if getSlave($AS).vaginaTat != $tattooChoice>> | [[Vagina|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "vagina", $tattooChoice)]]<</if>>
-				<<if getSlave($AS).dick > 0 && $tattooChoice != "scenes">>
-					<<if getSlave($AS).dickTat != $tattooChoice>> | [[Dick|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "dick", $tattooChoice)]]<</if>>
-				<</if>>
-				<<if ($tattooChoice != "Asian art") && ($tattooChoice != "scenes")>>
-					<<if getSlave($AS).anusTat != $tattooChoice>> | [[Anus|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "anus", $tattooChoice)]]<</if>>
-				<</if>>
-				<<if hasAnyNaturalLegs(getSlave($AS))>>
-					<<if getSlave($AS).legsTat != $tattooChoice>> | [[Legs|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "legs", $tattooChoice)]]<</if>>
-				<</if>>
-			<</if>>
-		<</if>>
-	</div>
-
-	<div>
-		<<if getSlave($AS).belly >= 10000 && getSlave($AS).bellyPreg < 450000 && getSlave($AS).bellyFluid < 5000>>
-			<<if getSlave($AS).bellyTat == 0>>
-				$He has no navel tattoos.
-			<<else>>
-				$His navel is tattooed with <<= getSlave($AS).bellyTat>>.
-			<</if>>
-			<<if getSlave($AS).bellyTat == 0>>
-				<div>
-				[[Heart|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "belly", "a heart")]]
-				| [[Star|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "belly", "a star")]]
-				| [[Butterfly|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "belly", "a butterfly")]]
-				</div>
-			<</if>>
-			<<if getSlave($AS).bellyTat != 0>>
-				//[[Remove tattoos|Body Modification][$modReaction = App.Medicine.Modification.setTattoo(getSlave($AS), "belly", 0)]]//
-			<</if>>
-		<<elseif getSlave($AS).bellyPreg >= 450000>>
-			$His middle is large and taut enough to be a suitable canvas for a navel focused tattoo, but $his brood is too active to permit the needle to do its work.
-		<<elseif getSlave($AS).bellyFluid >= 10000>>
-			$His middle is large and taut enough to be a suitable canvas for a navel focused tattoo, but the pressure applied to $his stomach will likely force $him to release its contents.
-		<<else>>
-			$His middle isn't large enough to be a suitable canvas for a navel focused tattoo.
-		<</if>>
-	</div>
-
-	<div>
-		<<if getSlave($AS).birthsTat > 0>>
-			<<if getSlave($AS).birthsTat > 1>>
-				$He has a series of <<= num(getSlave($AS).birthsTat)>> baby-shaped tattoos adorning $his stomach; one for each successful pregnancy<<if getSlave($AS).pregKnown == 1>> and a temporary one for $his current pregnancy<</if>>.
-			<<else>>
-				$He has a single baby-shaped tattoo<<if getSlave($AS).pregKnown == 1>>, and one temporary one,<</if>> adorning $his stomach.
-			<</if>>
-			//[[Remove tattoos|Body Modification][getSlave($AS).birthsTat = -1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS))]]//
-		<<elseif getSlave($AS).birthsTat == 0>>
-			<<if getSlave($AS).pregKnown == 1>>
-				$He has a single baby-shaped temporary tattoo adorning $his stomach.
-				//[[Remove it|Body Modification][getSlave($AS).birthsTat = -1]]//
-				<<if getSlave($AS).abortionTat > -1>> //This will only remove birth tracking//<</if>>
-			<<else>>
-				$He is scheduled to receive a tattoo each time $he gives birth.
-				//[[Cancel|Body Modification][getSlave($AS).birthsTat = -1]]//
-			<</if>>
-		<<else>>
-			Have $him receive a tattoo each time $he gives birth.
-			//[[Begin keeping track|Body Modification][getSlave($AS).birthsTat = 0]]//
-		<</if>>
-	</div>
-
-	<div>
-		<<if getSlave($AS).abortionTat > 0>>
-			<<if getSlave($AS).abortionTat > 1>>
-				$He has a series of <<= num(getSlave($AS).abortionTat)>> crossed out baby-shaped tattoos<<if getSlave($AS).pregKnown == 1>>, and one uncrossed one,<</if>> adorning $his stomach.
-			<<else>>
-				$He has a single crossed out baby-shaped tattoo<<if getSlave($AS).pregKnown == 1>>, and one uncrossed one,<</if>> adorning $his stomach.
-			<</if>>
-			//[[Remove tattoos|Body Modification][getSlave($AS).abortionTat = -1, cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS))]]//
-		<<elseif getSlave($AS).abortionTat == 0>>
-			<<if getSlave($AS).pregKnown == 1>>
-				$He has a single baby-shaped temporary tattoo adorning $his stomach.
-				//[[Remove it|Body Modification][getSlave($AS).abortionTat = -1]]//
-				<<if getSlave($AS).birthsTat > -1>> //This will only remove abortion tracking//<</if>>
-			<<else>>
-				$He is scheduled to receive a tattoo each time $he gets an abortion or miscarries.
-				//[[Cancel|Body Modification][getSlave($AS).abortionTat = -1]]//
-			<</if>>
-		<<else>>
-			Have $him receive a tattoo for each abortion or miscarriage $he has.
-			//[[Begin keeping track|Body Modification][getSlave($AS).abortionTat = 0]]//
-		<</if>>
-	</div>
-</div>/* indent */
-
-<h3>Custom Tattoos</h3>
-<div class="indent">
-	//Use complete sentences, make a description of it//
-	<div class="grid-2columns-auto">
-		<<if getSlave($AS).lipsTat == 0>>
-			<div>Give $him a custom face tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].lipsTat" $slaves[$slaveIndices[$AS]].lipsTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).shouldersTat == 0>>
-			<div>Give $him a custom shoulder tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].shouldersTat" $slaves[$slaveIndices[$AS]].shouldersTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).armsTat == 0 && hasAnyNaturalArms(getSlave($AS))>>
-			<div>Give $him a custom arm tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].armsTat" $slaves[$slaveIndices[$AS]].armsTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).boobsTat == 0>>
-			<div>Give $him a custom tit tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].boobsTat" $slaves[$slaveIndices[$AS]].boobsTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).backTat == 0>>
-			<div>Give $him a custom back tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].backTat" $slaves[$slaveIndices[$AS]].backTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).stampTat == 0>>
-			<div>Give $him a custom tramp stamp (lower back tattoo):</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].stampTat" $slaves[$slaveIndices[$AS]].stampTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).buttTat == 0>>
-			<div>Give $him a custom butt tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].buttTat" $slaves[$slaveIndices[$AS]].buttTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).dickTat == 0 && getSlave($AS).dick != 0>>
-			<div>Give $him a custom dick tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].dickTat" $slaves[$slaveIndices[$AS]].dickTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).vaginaTat == 0>>
-			<div>Give $him a custom pubic tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].vaginaTat" $slaves[$slaveIndices[$AS]].vaginaTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).anusTat == 0>>
-			<div>Give $him a custom anus tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].anusTat" $slaves[$slaveIndices[$AS]].anusTat "Slave Interact">></div>
-		<</if>>
-
-		<<if getSlave($AS).legsTat == 0 && hasAnyNaturalLegs(getSlave($AS))>>
-			<div>Give $him a custom leg tattoo:</div>
-			<div><<textbox "$slaves[$slaveIndices[$AS]].legsTat" $slaves[$slaveIndices[$AS]].legsTat "Slave Interact">></div>
-		<</if>>
-	</div> /* grid */
-
-	<div>
-		<<if (getSlave($AS).custom.tattoo == "")>>
-			Give $him a custom tattoo: <<textbox "$slaves[$slaveIndices[$AS]].custom.tattoo" $slaves[$slaveIndices[$AS]].custom.tattoo "Slave Interact">>
-		<<else>>
-			$He <<if _hasTat == 1>>also<</if>> has a custom tattoo: <<= getSlave($AS).custom.tattoo>>
-			Change it here: <<textbox "$slaves[$slaveIndices[$AS]].custom.tattoo" $slaves[$slaveIndices[$AS]].custom.tattoo "Slave Interact">>
-		<</if>>
-		<<if getSlave($AS).custom.tattoo != "">>
-			//[[Remove custom tattoo|Body Modification][getSlave($AS).custom.tattoo = "",cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS))]]//
-		<</if>>
-	</div>
-
-</div> /* indent */
-
-/* Branding */
-
-<h2>Branding</h2>
-<span id="brand-selection"></span>
-<script>
-	App.Medicine.Modification.Select.brand(getSlave(V.AS))
-</script>
-
-
-<<if getSlave($AS).breedingMark == 1 && ($propOutcome == 0 || $eugenicsFullControl == 1 || $arcologies[0].FSRestart == "unset")>>
-	<div class="indent">
-	$He has an intricate tattoo on $his lower belly that suggests $he was made to be bred. [[Remove it|Body Modification][getSlave($AS).breedingMark = 0]]
-	</div>
-<</if>>
-
-/* Scars */
-
-<h2>Scars</h2>
-<div class="indent">
-<div style="padding-bottom:1em">
-<<for _scarName, _scar range getSlave($AS).scar>>
-	<div>
-	$His _scarName is marked with <<= App.Desc.expandScarString(getSlave($AS), _scarName)>>:
-	<<capture _scarName>>
-		<<link "Remove Scar">>
-			<<set $scarApplied = 0>>
-			<<run delete getSlave($AS).scar[_scarName]>>
-			<<run cashX(forceNeg($surgeryCost), "slaveSurgery", getSlave($AS))>>
-			<<set $degradation -= 10>>
-			<<goto "Body Modification">>
-		<</link>>
-	<</capture>>
-	</div>
-<</for>>
-<<if (jQuery.isEmptyObject(getSlave($AS).scar))>>
-	<div>
-	$His skin is not scarred.
-	</div>
-<</if>>
-</div>
-
-<div>
-Use ''$scarDesign.local'' or choose another scar:
-
-[[Whip|Body Modification][$scarDesign.local = "whip"]]
-| [[Burns|Body Modification][$scarDesign.local = "burn"]]
-| [[Surgical|Body Modification][$scarDesign.local = "surgical"]]
-| [[Menacing|Body Modification][$scarDesign.local = "menacing"]]
-| [[Exotic|Body Modification][$scarDesign.local = "exotic"]]
-/* Other common scars might be battle scars or c-Section but it makes little sense to include them here */
-</div>
-
-<div>
-Or design your own: <<textbox "$scarDesign.local" $scarDesign.local "Body Modification">>
-</div>
-
-<div>
-Choose a site for scaring:
-
-<<if ["exotic", "menacing"].includes($scarDesign.local)>>
-	[[Cheeks|Body Modification][$scarTarget.local = "cheek"]]
-<<else>>
-	/* Sorted head to toe */
-
-	[[Entire body|Body Modification][$scarTarget.local = "entire body"]]
-
-	/* Head */
-	<<if getSlave($AS).earShape != "none">>
-		| [[Ears|Body Modification][$scarTarget.local = "ear"]]
-	<</if>>
-	| [[Cheeks|Body Modification][$scarTarget.local = "cheek"]]
-	| [[Neck|Body Modification][$scarTarget.local = "neck"]]
-
-	/* Torso */
-	| [[Chest|Body Modification][$scarTarget.local = "chest"]]
-	| [[Breasts|Body Modification][$scarTarget.local = "breast"]]
-	| [[Back|Body Modification][$scarTarget.local = "back"]]
-	| [[Lower Back|Body Modification][$scarTarget.local = "lower back"]]
-	| [[Belly|Body Modification][$scarTarget.local = "belly"]]
-	| [[Pubic Mound|Body Modification][$scarTarget.local = "pubic mound"]]
-	<<if getSlave($AS).dick > 0>>
-		| [[Penis|Body Modification][$scarTarget.local = "penis"]]
-	<</if>>
-	<<if getSlave($AS).balls > 0 && getSlave($AS).scrotum > 0>>
-		| [[Testicles|Body Modification][$scarTarget.local = "testicle"]]
-	<</if>>
-
-	/* Arms*/
-	| [[Shoulders|Body Modification][$scarTarget.local = "shoulder"]]
-	<<if hasAnyNaturalArms(getSlave($AS))>>
-		| [[Arm, upper|Body Modification][$scarTarget.local = "upper arm"]]
-		| [[Arm, lower|Body Modification][$scarTarget.local = "lower arm"]]
-		| [[Wrists|Body Modification][$scarTarget.local = "wrist"]]
-		| [[Hands|Body Modification][$scarTarget.local = "hand"]]
-	<</if>>
-
-	/* Legs */
-	| [[Buttocks|Body Modification][$scarTarget.local = "buttock"]]
-	<<if hasAnyNaturalLegs(getSlave($AS))>>
-		| [[Thighs|Body Modification][$scarTarget.local = "thigh"]]
-		| [[Calves|Body Modification][$scarTarget.local = "calf"]]
-		| [[Ankles|Body Modification][$scarTarget.local = "ankle"]]
-		| [[Feet|Body Modification][$scarTarget.local = "foot"]]
-	<</if>>
-<</if>>
-</div>
-
-<div>
-Or a custom site: <<textbox "$scarTarget.local" $scarTarget.local "Body Modification">>
-</div>
-
-/* Correct some "bad" choices" */
-<<if ["exotic", "menacing"].includes($scarDesign.local)>>
-	<<if $scarTarget.local != "cheek">>
-		<<set $scarTarget.local = "cheek">>
-	<</if>>
-<</if>>
-
-<div>
-<<if ["ankle", "breast", "buttock", "calf", "cheek", "ear", "foot", "hand", "lower arm", "shoulder", "testicle", "thigh", "upper arm", "wrist"].includes($scarTarget.local)>>
-	<<set _leftTarget = ("left " + $scarTarget.local)>>
-	<<set _rightTarget = ("right " + $scarTarget.local)>>
-	<<if getSlave($AS).scar[_leftTarget]>>
-		$His _leftTarget is already marked with <<= App.Desc.expandScarString(getSlave($AS), _leftTarget)>>.
-	<</if>>
-	<<if getSlave($AS).scar[_rightTarget]>>
-		$His _rightTarget is already marked with <<= App.Desc.expandScarString(getSlave($AS), _rightTarget)>>.
-	<</if>>
-	Scar $him now with ''$scarDesign.local'' on the
-	<<set _left = 0, _right = 0>> /* overwrite brand */
-
-	<<if !(["upper arm", "lower arm", "wrist", "hand"].includes($scarTarget.local) && getLeftArmID(getSlave($AS)) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes($scarTarget.local) && getLeftLegID(getSlave($AS)) !== 1)>>
-		<<set _left = 1>> /* make next checks easier */
-		<<link "left">>
-			<<set $scarTarget.local = _leftTarget>>
-			<<set $scarApplied = 1>>
-			<<run App.Medicine.Modification.addScar(getSlave($AS), _leftTarget, $scarDesign.local)>>
-			<<run cashX(forceNeg($modCost), "slaveMod", getSlave($AS))>>
-			<<set $degradation += 10>>
-			<<goto "Body Modification">>
-		<</link>>
-	<</if>>
-	<<if !(["upper arm", "lower arm", "wrist", "hand"].includes($scarTarget.local) && getRightArmID(getSlave($AS)) !== 1) && !(["thigh", "calf", "ankle", "foot"].includes($scarTarget.local) && getRightLegID(getSlave($AS)) !== 1)>>
-		<<set _right = 1>> /* make next checks easier */
-	<</if>>
-	<<if _left && _right>>
-		$scarTarget.local, or the
-	<</if>>
-	<<if _right>>
-		<<link "right">>
-			<<set $scarTarget.local = _rightTarget>>
-			<<set $scarApplied = 1>>
-			<<run App.Medicine.Modification.addScar(getSlave($AS), _rightTarget, $scarDesign.local)>>
-			<<run cashX(forceNeg($modCost), "slaveMod", getSlave($AS))>>
-			<<set $degradation += 10>>
-			<<goto "Body Modification">>
-		<</link>>
-	<</if>><<if !_left || !_right>> $scarTarget.local<</if>>?
-<<else>>
-	<<if getSlave($AS).scar.hasOwnProperty($scarTarget.local)>>
-		<<if getSlave($AS).scar[$scarTarget.local][$scarDesign.local]>>
-			$He already has $scarDesign.local scars on $his $scarTarget.local. You can make it worse.
-		<<else>> /* check how much scarring is on this part */
-			<<set _scarTotalValue = (Object.values(getSlave($AS).scar[$scarTarget.local])).reduce((a, b) => a + b, 0)>>
-			<<if _scarTotalValue>>
-				That would be a new kind of scar to add to the growing collection on $his $scarTarget.local. Life can always be worse for a slave.
-			<</if>>
-		<</if>>
-	<</if>>
-	<<link "Scar">>
-		<<if $scarTarget.local === "entire body" && $scarDesign.local.includes("whip")>>
-			/* Special case for whipping scene, produces two kinds of scars */
-			<<run App.Medicine.Modification.addScourged(getSlave($AS))>>
-		<<else>>
-			/* Normal entire body scarring */
-			<<if $scarTarget.local === "entire body">>
-				<<set _scarArray = ["left breast", "right breast", "back", "lower back", "left buttock", "right buttock"]>>
-				<<if getLeftArmID(getSlave($AS)) === 0>>
-					<<set _scarArray.push("left upper arm")>>
-				<</if>>
-				<<if getRightArmID(getSlave($AS)) === 0>>
-					<<set _scarArray.push("right upper arm")>>
-				<</if>>
-				<<if getLeftLegID(getSlave($AS)) === 0>>
-					<<set _scarArray.push("left thigh")>>
-				<</if>>
-				<<if getRightLegID(getSlave($AS)) === 0>>
-					<<set _scarArray.push("right thigh")>>
-				<</if>>
-			/* Single scar */
-			<<else>>
-				<<set _scarArray = [$scarTarget.local]>>
-			<</if>>
-			<<for _i to 0; _i < _scarArray.length; _i++>>
-				<<run App.Medicine.Modification.addScar(getSlave($AS), _scarArray[_i], $scarDesign.local)>>
-				<<set $degradation += 10>>
-			<</for>>
-		<</if>>
-		<<run cashX(forceNeg($modCost), "slaveMod", getSlave($AS))>>
-		<<set $scarApplied = 1>>
-		<<set $degradation += 10>>
-		<<goto "Body Modification">>
-	<</link>>
-	with $scarDesign.local on the $scarTarget.local<<if getSlave($AS).scar[$scarTarget.local]>>, adding to the scars that are already there?<<else>>.<</if>>
-<</if>>
-</div>
-</div> /* indent */
diff --git a/src/uncategorized/cellblockReport.tw b/src/uncategorized/cellblockReport.tw
deleted file mode 100644
index 8a2d3b4426fdf6360f035987da697c5d79beae08..0000000000000000000000000000000000000000
--- a/src/uncategorized/cellblockReport.tw
+++ /dev/null
@@ -1,308 +0,0 @@
-:: Cellblock Report [nobr]
-
-<<set _slaves = App.Utils.sortedEmployees(App.Entity.facilities.cellblock)>>
-<<set _DL = _slaves.length, _SL = $slaves.length, _brokenSlaves = 0, _idleBonus = 0, _softenedQuirks = 0, _trustMalus = 0, _FLsFetish = 0>>
-
-<<if $cellblockDecoration != "standard">>
-	<<set _devBonus = 1>>
-<<else>>
-	<<set _devBonus = 0>>
-<</if>>
-<<set _cellblockNameCaps = capFirstChar($cellblockName)>>
-<<set $flSex = App.EndWeek.getFLSex(App.Entity.facilities.cellblock)>> /* FIXME: should be local, passed as a parameter to saRules */
-
-<<if (_S.Wardeness)>>
-	<<setLocalPronouns _S.Wardeness>>
-
-	<<if (_S.Wardeness.health.condition < -80)>>
-		<<run improveCondition(_S.Wardeness, 20)>>
-	<<elseif (_S.Wardeness.health.condition < -40)>>
-		<<run improveCondition(_S.Wardeness, 15)>>
-	<<elseif (_S.Wardeness.health.condition < 0)>>
-		<<run improveCondition(_S.Wardeness, 10)>>
-	<<elseif (_S.Wardeness.health.condition < 90)>>
-		<<run improveCondition(_S.Wardeness, 7)>>
-	<</if>>
-	<<if (_S.Wardeness.devotion <= 60)>>
-		<<set _S.Wardeness.devotion += 2>>
-	<</if>>
-	<<set _S.Wardeness.devotion += _devBonus>>
-	<<if _S.Wardeness.trust < 60>>
-		<<set _S.Wardeness.trust += 3>>
-	<</if>>
-	<<if (_S.Wardeness.rules.living != "luxurious")>>
-		<<set _S.Wardeness.rules.living = "luxurious">>
-	<</if>>
-	<<if _S.Wardeness.rules.rest != "restrictive">>
-		<<set _S.Wardeness.rules.rest = "restrictive">>
-	<</if>>
-	<<if _S.Wardeness.fetishStrength <= 95>>
-		<<if _S.Wardeness.fetish != "sadist">>
-			<<if fetishChangeChance(_S.Wardeness) > random(0,100)>>
-				<<set _FLsFetish = 1, _S.Wardeness.fetishKnown = 1, _S.Wardeness.fetish = "sadist">>
-			<</if>>
-		<<elseif _S.Wardeness.fetishKnown == 0>>
-			<<set _FLsFetish = 1, _S.Wardeness.fetishKnown = 1>>
-		<<else>>
-			<<set _FLsFetish = 2, _S.Wardeness.fetishStrength += 4>>
-		<</if>>
-	<</if>>
-	<<if (_S.Wardeness.energy > 95) || (_S.Wardeness.fetish == "sadist")>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-	<</if>>
-	&nbsp;&nbsp;&nbsp;&nbsp;<<= SlaveFullName(_S.Wardeness)>> is serving as the Wardeness.
-	<<if _S.Wardeness.relationship == -3 && _S.Wardeness.devotion > 50>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		As your $wife, $he tries $his best to break the disobedient slaves to your will.
-	<</if>>
-	<<if (_FLsFetish == 1)>>
-		One day $he demands obedience. The next day $he strikes a slave when it isn't given. The next, $he seems more excited than embarrassed when beating a prisoner. Soon, $he's looking for an excuse to punish. $He's @@.pink;become more of a sadist.@@
-	<<elseif (_FLsFetish == 2)>>
-		Being not only allowed but encouraged to get off while punishing prisoners @@.lightsalmon;deepens $his sadism.@@
-	<</if>>
-	<<if setup.wardenessCareers.includes(_S.Wardeness.career)>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		$He has experience with detecting security issues and grinding down potential miscreants from $his life before $he was a slave, making $him more effective.
-	<<elseif _S.Wardeness.skill.wardeness >= $masteredXP>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		$He has experience with detecting security issues and grinding down potential miscreants from working for you, making $him more effective.
-	<<else>>
-		<<set _S.Wardeness.skill.wardeness += random(1,Math.ceil((_S.Wardeness.intelligence+_S.Wardeness.intelligenceImplant)/15) + 8)>>
-	<</if>>
-	<<if _S.Wardeness.fetish == "sadist">>
-		$He uses the prisoners to gratify $his sadism, terrifying them and quickly breaking their resistance.
-	<<elseif _S.Wardeness.energy > 95>>
-		$He is so sexually depraved that $he constantly molests the prisoners, breaking them to a life of servitude.
-	<</if>>
-	<<if _S.Wardeness.sexualFlaw == "malicious" || _S.Wardeness.sexualFlaw == "abusive">>
-		<<set _devBonus++, _trustMalus += 2, _idleBonus += 2>>
-		Sexual abuse of prisoners has become an end for $him, not a means. $He inspires $his prisoners to do everything possible to qualify to get away from $him, even as $he slowly grinds away their interest in sex to do it.
-	<<elseif _S.Wardeness.sexualQuirk == "caring">>
-		<<set _devBonus--, _trustMalus--, _idleBonus-->>
-		$His kindness sometimes shows through $his tough facade, letting prisoners get off easier than they should.
-	<</if>>
-	<<if (_S.Wardeness.dick > 2) && (canPenetrate(_S.Wardeness))>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		$His molestation of the prisoners is more varied and effective because $he has a dick to fuck them with.
-	<</if>>
-	<<if (_S.Wardeness.muscles > 35)>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		$He is strong enough to restrain anyone that tries anything with $his own hands.
-	<</if>>
-	<<if (_S.Wardeness.devotion > 95)>>
-		<<set _devBonus++, _trustMalus++, _idleBonus++>>
-		$His devotion to you is so absolute that $he sees breaking bitches for your service as a noble calling.
-	<</if>>
-	<<for _slave range _slaves>>
-		<<if _S.Wardeness.rivalryTarget == _slave.ID>>
-			$He greatly enjoys breaking $his <<print rivalryTerm(_S.Wardeness)>>, _slave.slaveName's will.
-			<<set _slave.devotion++, _slave.trust -= 3>>
-			<<if random(1,100) > 30>>
-				<<set _S.Wardeness.rivalry++, _slave.rivalry++>>
-			<</if>>
-		<<elseif _S.Wardeness.relationshipTarget == _slave.ID>>
-			$He hates having to break $his <<print relationshipTerm(_S.Wardeness)>>, _slave.slaveName, but $his devotion to you wins out in the end.
-			<<set _slave.devotion++, _slave.trust -= 3>>
-			<<if random(1,100) >= 50>>
-				$His and _slave.slaveName's relationship has been shattered by these events.
-				<<set _S.Wardeness.relationship = 0, _S.Wardeness.relationshipTarget = 0 _slave.relationship = 0, _slave.relationshipTarget = 0>>
-			<</if>>
-		<<elseif areRelated(_S.Wardeness, _slave)>>
-			<<setLocalPronouns _slave 2>>
-			$He shows $his <<print relativeTerm(_S.Wardeness,_slave)>> _slave.slaveName no mercy, making sure _he2 understands _his2 place.
-			<<set _slave.devotion++, _slave.trust-->>
-		<</if>>
-	<</for>>
-	<<if (_DL < $cellblock && !slaveResting(_S.Wardeness))>>
-		<<set _seed = random(1,10)+(($cellblock-_DL)*(random(150,170)+(_idleBonus*10)))>>
-		<<run cashX(_seed, "cellblock", _S.Wardeness)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;Since $he doesn't have enough prisoners to manage to keep $him busy, $he works on citizens' slaves, earning @@.yellowgreen;<<print cashFormat(_seed)>>.@@
-	<</if>>
-	<<if (_DL > 0)>><br><br><</if>>
-<</if>>
-
-<<if (_DL > 0)>>
-	&nbsp;&nbsp;&nbsp;&nbsp;<<if (_DL == 1)>>''One slave is being confined in $cellblockName until they are willing to obey.''<<else>>''_DL slaves are being confined in $cellblockName until they are willing to obey.''<</if>>
-<</if>>
-
-<<if (_S.Wardeness)>>
-	<<set $i = $slaveIndices[$WardenessID]>> /* apply following SA passages to facility leader */
-	<<if $showEWD != 0>>
-		<br><br>
-		/* 000-250-006 */
-		<<if $seeImages && $seeReportImages>>
-		<div class="imageRef tinyImg">
-			<<= SlaveArt(_S.Wardeness, 0, 0)>>
-		</div>
-		<</if>>
-		/* 000-250-006 */
-		<<includeDOM App.EndWeek.favoriteIcon($slaves[$i])>>
-		<span class='slave-name'><<= SlaveFullName($slaves[$i])>></span> is serving as the Wardeness in $cellblockName.
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<= App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
-		<<includeDOM App.SlaveAssignment.rules($slaves[$i])>>
-		<<= App.SlaveAssignment.diet($slaves[$i])>>
-		<<includeDOM App.SlaveAssignment.longTermEffects($slaves[$i])>>
-		<<= App.SlaveAssignment.drugs($slaves[$i])>>
-		<<= App.SlaveAssignment.relationships($slaves[$i])>>
-		<<= App.SlaveAssignment.rivalries($slaves[$i])>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;<<= App.SlaveAssignment.devotion($slaves[$i])>>
-	<<else>>
-		<<run App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
-		<<run App.SlaveAssignment.rules($slaves[$i])>>
-		<<run App.SlaveAssignment.diet($slaves[$i])>>
-		<<run App.SlaveAssignment.longTermEffects($slaves[$i])>>
-		<<run App.SlaveAssignment.drugs($slaves[$i])>>
-		<<run App.SlaveAssignment.relationships($slaves[$i])>>
-		<<run App.SlaveAssignment.rivalries($slaves[$i])>>
-		<<run App.SlaveAssignment.devotion($slaves[$i])>>
-	<</if>>
-<</if>>
-
-<<for _slave range _slaves>>
-	<<set $i = $slaveIndices[_slave.ID]>>
-	<<setLocalPronouns _slave>>
-	<<if (_slave.devotion <= 20)>>
-		<<if (_slave.trust >= -20)>>
-			<<if ((_slave.hears == -1 && _slave.earwear != "hearing aids") || (_slave.hears == 0 && _slave.earwear == "muffling ear plugs") ||(_slave.hears == -2))>>
-				<<set _slave.devotion -= 2, _slave.trust -= 2>>
-			<<else>>
-				<<set _slave.devotion -= 4, _slave.trust -= 4>>
-			<</if>>
-		<<else>>
-			<<set _slave.devotion++>>
-		<</if>>
-	<</if>>
-	<<switch $cellblockDecoration>>
-	<<case "Paternalist">>
-		<<set _slave.rules.living = "normal">>
-		<<if (_slave.inflation > 0)>>
-			<<run deflate(_slave)>>
-		<</if>>
-	<<case "Pastoralist">>
-		<<set _slave.rules.living = "spare">>
-		<<if _slave.inflation == 0>>
-			<<if _slave.pregKnown == 0 && _slave.bellyImplant < 1500>>
-				<<set _slave.inflation = 2, _slave.inflationType = "milk", _slave.inflationMethod = 1>>
-			<<else>>
-				<<set _slave.inflation = 1, _slave.inflationType = "milk", _slave.inflationMethod = 1>>
-			<</if>>
-			<<run SetBellySize(_slave)>>
-		<</if>>
-	<<case "Hedonistic">>
-		<<set _slave.rules.living = "spare">>
-		<<if _slave.weight < 200>>
-			<<if _slave.weightDirection == 1>>
-				<<set _slave.weight += 5>>
-			<<elseif _slave.weightDirection == -1>>
-				<<set _slave.weight += 1>>
-			<<else>>
-				<<set _slave.weight += 3>>
-			<</if>>
-		<</if>>
-		<<if _slave.muscles > -100>>
-			<<set _slave.muscles -= 2>>
-		<</if>>
-		<<if _slave.inflation == 0>>
-			<<if _slave.pregKnown == 0 && _slave.bellyImplant < 1500>>
-				<<set _slave.inflation = 3, _slave.inflationType = "food", _slave.inflationMethod = 1>>
-			<<else>>
-				<<set _slave.inflation = 1, _slave.inflationType = "food", _slave.inflationMethod = 1>>
-			<</if>>
-			<<run SetBellySize(_slave)>>
-		<</if>>
-	<<default>>
-		<<set _slave.rules.living = "spare">>
-		<<if (_slave.inflation > 0)>>
-			<<run deflate(_slave)>>
-		<</if>>
-	<</switch>>
-	<<if $cellblockUpgrade == 1>>
-		<<if (_slave.behavioralFlaw != "none") && (_slave.behavioralQuirk == "none")>>
-			<<run SoftenBehavioralFlaw(_slave)>>
-			<<set _slave.devotion -= 10, _softenedQuirks++>>
-		<<elseif (_slave.sexualFlaw != "none") && (_slave.sexualQuirk == "none")>>
-			<<run SoftenSexualFlaw(_slave)>>
-			<<set _slave.devotion -= 10, _softenedQuirks++>>
-		<</if>>
-	<</if>>
-	<<set _slave.devotion += _devBonus, _slave.trust -= _trustMalus>>
-	<<if (_S.Wardeness) && (_S.Wardeness.sexualFlaw == "malicious") && (_slave.energy >= 2)>>
-		<<set _slave.energy -= 2>>
-	<</if>>
-	<<if (_slave.health.condition < -80)>>
-		<<run improveCondition(_slave, 20)>>
-	<<elseif (_slave.health.condition < -40)>>
-		<<run improveCondition(_slave, 15)>>
-	<<elseif (_slave.health.condition < 0)>>
-		<<run improveCondition(_slave, 10)>>
-	<<elseif (_slave.health.condition < 40)>>
-		<<run improveCondition(_slave, 7)>>
-	<<elseif (_slave.health.condition < 100)>>
-		<<run improveCondition(_slave, 3)>>
-	<</if>>
-	<<if $showEWD != 0>>
-		<br><br>
-		/* 000-250-006 */
-		<<if $seeImages && $seeReportImages>>
-		<div class="imageRef tinyImg">
-			<<= SlaveArt(_slave, 0, 0)>>
-		</div>
-		<</if>>
-		/* 000-250-006 */
-		<<includeDOM App.EndWeek.favoriteIcon(_slave)>>
-		<span class='slave-name'><<= SlaveFullName(_slave)>></span>
-		<<if _slave.choosesOwnAssignment == 2>>
-			<<= App.SlaveAssignment.choosesOwnJob(_slave)>>
-		<<else>>
-			is confined in $cellblockName.
-		<</if>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= App.SlaveAssignment.stayConfined(_slave)>>
-		<br>&nbsp;&nbsp;&nbsp;
-		<<includeDOM App.SlaveAssignment.rules(_slave)>>
-		<<= App.SlaveAssignment.diet(_slave)>>
-		<<includeDOM App.SlaveAssignment.longTermEffects(_slave)>>
-		<<= App.SlaveAssignment.drugs(_slave)>>
-		<<= App.SlaveAssignment.relationships(_slave)>>
-		<<= App.SlaveAssignment.rivalries(_slave)>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;<<= App.SlaveAssignment.devotion(_slave)>>
-	<<else>>
-		<<silently>>
-		<<run App.SlaveAssignment.choosesOwnJob(_slave)>>
-		<<run App.SlaveAssignment.stayConfined(_slave)>>
-		<<run App.SlaveAssignment.rules(_slave)>>
-		<<run App.SlaveAssignment.diet(_slave)>>
-		<<run App.SlaveAssignment.longTermEffects(_slave)>>
-		<<run App.SlaveAssignment.drugs(_slave)>>
-		<<run App.SlaveAssignment.relationships(_slave)>>
-		<<run App.SlaveAssignment.rivalries(_slave)>>
-		<<run App.SlaveAssignment.devotion(_slave)>>
-		<</silently>>
-	<</if>>
-<</for>>
-<<if (_softenedQuirks) || (_brokenSlaves)>>
-	<br><br>&nbsp;&nbsp;&nbsp;&nbsp;
-	<<if (_softenedQuirks > 0)>>
-		_cellblockNameCaps's advanced compliance systems successfully softened
-		<<if (_softenedQuirks == 1)>>
-			one slave's mental flaw into an @@.green;appealing quirk,@@ though the constant correction caused them @@.mediumorchid;considerable anguish.@@
-		<<else>>
-			_softenedQuirks slaves' mental flaws into @@.green;appealing quirks,@@ though the constant correction caused them @@.mediumorchid;considerable anguish.@@
-		<</if>>
-	<</if>>
-	<<if (_brokenSlaves > 0)>>
-		<<if (_brokenSlaves == 1)>>
-			One slave is now willing to @@.hotpink;do as they're told@@ and has been released.
-		<<else>>
-			_brokenSlaves slaves are now willing to @@.hotpink;do as they're told@@ and have been released.
-		<</if>>
-		<<if $cellblockDecoration != "standard">>
-			<br><br>&nbsp;&nbsp;&nbsp;&nbsp;_cellblockNameCaps's $cellblockDecoration atmosphere @@.hotpink;had an impact on them while they were@@ imprisoned.
-		<</if>>
-	<</if>>
-<</if>>
-<<if _DL > 0 || _S.Wardeness>>
-	<br><br>
-<</if>>
diff --git a/src/uncategorized/economics.tw b/src/uncategorized/economics.tw
index cd70fccb641b9edf62dcc3c665636166911b1f7c..48026deca48691c9402016fa920a72563211956a 100644
--- a/src/uncategorized/economics.tw
+++ b/src/uncategorized/economics.tw
@@ -31,8 +31,8 @@
 
 	<<if $secExpEnabled > 0>>
 		<br>
-		<<include "authorityReport">>
-		<<include "securityReport">>
+		<<= App.SecExp.authorityReport()>>
+		<<includeDOM App.SecExp.securityReport()>>
 	<</if>>
 
 	<<includeDOM App.EndWeek.reputation()>>
@@ -95,13 +95,13 @@
 		<<if $secExpEnabled > 0>>
 			<div id="Authority" class="tab-content">
 				<div class="content">
-					<<include "authorityReport">>
+					<<= App.SecExp.authorityReport()>>
 				</div>
 			</div>
 
 			<div id="securityReport" class="tab-content">
 				<div class="content">
-					<<include "securityReport">>
+					<<includeDOM App.SecExp.securityReport()>>
 				</div>
 			</div>
 		<</if>>
@@ -130,4 +130,4 @@
 			document.getElementById("defaultOpen").click();
 		</script>
 	</body>
-<</if>>
\ No newline at end of file
+<</if>>
diff --git a/src/uncategorized/freeRangeDairyAssignmentScene.tw b/src/uncategorized/freeRangeDairyAssignmentScene.tw
index dfe4b26ecf74474f0ddaee85c82510e2c07a7a69..20fadf51b50a5caa875ad2034eb3c45cd4657385 100644
--- a/src/uncategorized/freeRangeDairyAssignmentScene.tw
+++ b/src/uncategorized/freeRangeDairyAssignmentScene.tw
@@ -159,7 +159,7 @@
 		<</if>>
 		<div>
 		The only "furniture" in the stall looks like a dentist's chair. Despite the medical appearance, when $he <<if getSlave($AS).devotion > 90>>eagerly<<elseif getSlave($AS).slaveName < 40>>hesitantly<</if>> gets into the chair it turns out to be quite comfortable.
-		<<if _S.Milkmaid == 0>>
+		<<if $MilkmaidID == 0>>
 			Automated machinery springs to life, preparing $him for milking.
 		<<else>>
 			Your appointed milkmaid _S.Milkmaid.slaveName helps <<= getSlave($AS).slaveName>> with installing the milking devices.
@@ -238,7 +238,7 @@
 
 <p>
 	Sexual stimulation increases product output.
-	<<if _S.Milkmaid == 0 && $dairyStimulatorsSetting == 0>>
+	<<if $MilkmaidID == 0 && $dairyStimulatorsSetting == 0>>
 		Unfortunately, there is neither a milkmaid providing personal assistance nor automatic sodomizers installed in your dairy.<br>
 	<<else>>
 		<<if $MilkmaidID != 0>>
diff --git a/src/uncategorized/fullReport.tw b/src/uncategorized/fullReport.tw
deleted file mode 100644
index 95d64be55e6178b8149b4e3c8b328f271593c277..0000000000000000000000000000000000000000
--- a/src/uncategorized/fullReport.tw
+++ /dev/null
@@ -1,94 +0,0 @@
-:: Full Report [nobr]
-
-/* 000-250-006 */
-<<if $seeImages && $seeReportImages>>
-	<div class="imageRef medImg">
-		<<= SlaveArt($slaves[$i], 2, 0)>>
-	</div>
-<</if>>
-/* 000-250-006 */
-
-<<setLocalPronouns $slaves[$i]>>
-
-<<switch $slaves[$i].assignment>>
-<<case "rest">>
-	<<= App.SlaveAssignment.rest($slaves[$i])>>
-<<case "whore">>
-	<<= App.SlaveAssignment.whore($slaves[$i])>>
-<<case "serve the public">>
-	<<= App.SlaveAssignment.serveThePublic($slaves[$i])>>
-<<case "work a glory hole">>
-	<<= App.SlaveAssignment.workAGloryHole($slaves[$i])>>
-<<case "get milked">>
-	<<set _milkResults = App.SlaveAssignment.getMilked($slaves[$i])>>
-	<<= _milkResults.text>>
-<<case "take classes">>
-	<<= App.SlaveAssignment.takeClasses($slaves[$i])>>
-<<case "please you">>
-	<<= App.SlaveAssignment.pleaseYou($slaves[$i])>>
-<<case "be a subordinate slave">>
-	<<= App.SlaveAssignment.serveYourOtherSlaves($slaves[$i])>>
-<<case "be a servant">>
-	<<= App.SlaveAssignment.servant($slaves[$i])>>
-<<case "stay confined">>
-	<<= App.SlaveAssignment.stayConfined($slaves[$i])>>
-<<case "guard you">>
-	<<= App.SlaveAssignment.guardYou($slaves[$i])>>
-<<case "be your Head Girl">>
-	<<= App.SlaveAssignment.beYourHeadGirl($slaves[$i])>>
-<<case "recruit girls">>
-	<<= App.SlaveAssignment.recruitGirls($slaves[$i])>>
-<<default>>
-	<<= removeJob($slaves[$i], $slaves[$i].assignment)>>
-	<<= App.SlaveAssignment.rest($slaves[$i])>>
-<</switch>>
-
-<<if $servantMilkers == 1 && $slaves[$i].lactation > 0 && $slaves[$i].assignment != "get milked">>
-	$His assignment
-	<<if setup.servantMilkersJobs.includes($slaves[$i].assignment)>>
-		is not strenuous, so $he <<if $slaves[$i].devotion > 20>><<if $slaves[$i].fetish == "boobs">>eagerly <</if>>uses<<elseif $slaves[$i].devotion >= -20>>is required to use<<else>>is forced to use<</if>> the penthouse milkers frequently,
-		<<set _milkResult = App.SlaveAssignment.getMilked($slaves[$i], 0.5)>>
-	<<else>>
-		keeps $him busy, but $he <<if $slaves[$i].devotion > 20>><<if $slaves[$i].fetish == "boobs">>eagerly <</if>>uses<<elseif $slaves[$i].devotion >= -20>>is required to use<<else>>is forced to use<</if>> the penthouse milkers whenever $he can,
-		<<set _milkResult = App.SlaveAssignment.getMilked($slaves[$i], 0.25)>>
-	<</if>>
-	and $he gives <<= _milkResult.milk>> liters of milk over the week, which is sold for @@.yellowgreen;<<print cashFormat(_milkResult.milkSale)>>.@@
-<</if>>
-
-<br>&nbsp;&nbsp;&nbsp;&nbsp;
-<<if $slaves[$i].minorInjury != 0>>
-	$His $slaves[$i].minorInjury will heal by the end of the week.
-<</if>>
-
-<<if $showEWD == 0>>
-	<<includeDOM App.SlaveAssignment.rules($slaves[$i])>>
-	<<run App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-	<<run App.SlaveAssignment.diet($slaves[$i])>>
-	<<run App.SlaveAssignment.longTermEffects($slaves[$i])>>
-	<<run App.SlaveAssignment.drugs($slaves[$i])>>
-	<<run App.SlaveAssignment.relationships($slaves[$i])>>
-	<<run App.SlaveAssignment.rivalries($slaves[$i])>>
-<<else>>
-	<<includeDOM App.SlaveAssignment.rules($slaves[$i])>>
-	<<= App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-	<<= App.SlaveAssignment.diet($slaves[$i])>>
-	<<includeDOM App.SlaveAssignment.longTermEffects($slaves[$i])>>
-	<<= App.SlaveAssignment.drugs($slaves[$i])>>
-	<<= App.SlaveAssignment.relationships($slaves[$i])>>
-	<<= App.SlaveAssignment.rivalries($slaves[$i])>>
-	<br>
-<</if>>
-
-<<if $PC.health.shortDamage < 30 && Array.isArray($personalAttention) && $personalAttention.findIndex(function(s) { return s.ID == $slaves[$i].ID; }) != -1>>
-	<<includeDOM personalAttention($slaves[$i])>>
-<</if>>
-
-<<for _hgtsidx = 0; _hgtsidx < $HGTrainSlavesIDs.length; _hgtsidx++>>
-	<<if $slaves[$i].ID == $HGTrainSlavesIDs[_hgtsidx].ID>>
-		<<set $activeSlave = $slaves[$i], $HGtraining = $HGTrainSlavesIDs[_hgtsidx].training>>
-		<<include "HG Application">>
-		<br>
-	<</if>>
-<</for>>
-
-&nbsp;&nbsp;&nbsp;&nbsp;<<= App.SlaveAssignment.devotion($slaves[$i])>>
diff --git a/src/uncategorized/futureSociety.tw b/src/uncategorized/futureSociety.tw
index 4b4d4ddc5b963b89ed607d88294fa902ad482109..89aa9cd429b4b51c80ce819831ba52b063aec883 100644
--- a/src/uncategorized/futureSociety.tw
+++ b/src/uncategorized/futureSociety.tw
@@ -1767,60 +1767,60 @@ You are spending <<print cashFormat($FSSpending)>> each week to support your soc
 
 <<if $brothel > 0>>
 	<div><<= capFirstChar($brothelName)>> is decorated in $brothelDecoration style.</div>
-	<<SetFacilityDecoration "brothelDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("brothelDecoration")>>
 <</if>>
 
 <<if $club > 0>>
 	<div><<= capFirstChar($clubName)>> is decorated in $clubDecoration style.</div>
-	<<SetFacilityDecoration "clubDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("clubDecoration")>>
 <</if>>
 
 <<if $dairy > 0>>
 	<div><<= capFirstChar($dairyName)>> is decorated in $dairyDecoration style.</div>
-	<<SetFacilityDecoration "dairyDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("dairyDecoration")>>
 <</if>>
 
 <<if $farmyard > 0>>
 	<div><<= capFirstChar($farmyardName)>> is decorated in $farmyardDecoration style.</div>
-	<<SetFacilityDecoration "farmyardDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("farmyardDecoration")>>
 <</if>>
 
 <<if $spa > 0>>
 	<div><<= capFirstChar($spaName)>> is decorated in $spaDecoration style.</div>
-	<<SetFacilityDecoration "spaDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("spaDecoration")>>
 <</if>>
 
 <<if $nursery > 0>>
 	<div><<= capFirstChar($nurseryName)>> is decorated in $nurseryDecoration style.</div>
-	<<SetFacilityDecoration "nurseryDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("nurseryDecoration")>>
 <</if>>
 
 <<if $clinic > 0>>
 	<div><<= capFirstChar($clinicName)>> is decorated in $clinicDecoration style.</div>
-	<<SetFacilityDecoration "clinicDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("clinicDecoration")>>
 <</if>>
 
 <<if $schoolroom > 0>>
 	<div><<= capFirstChar($schoolroomName)>> is decorated in $schoolroomDecoration style.</div>
-	<<SetFacilityDecoration "schoolroomDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("schoolroomDecoration")>>
 <</if>>
 
 <<if $cellblock > 0>>
 	<div><<= capFirstChar($cellblockName)>> is decorated in $cellblockDecoration style.</div>
-	<<SetFacilityDecoration "cellblockDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("cellblockDecoration")>>
 <</if>>
 
 <<if $servantsQuarters > 0>>
 	<div><<= capFirstChar($servantsQuartersName)>> is decorated in $servantsQuartersDecoration style.</div>
-	<<SetFacilityDecoration "servantsQuartersDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("servantsQuartersDecoration")>>
 <</if>>
 
 <<if $arcade > 0>>
 	<div><<= capFirstChar($arcadeName)>> is decorated in $arcadeDecoration style.</div>
-	<<SetFacilityDecoration "arcadeDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("arcadeDecoration")>>
 <</if>>
 
 <<if $masterSuite > 0>>
 	<div><<= capFirstChar($masterSuiteName)>> is decorated in $masterSuiteDecoration style.</div>
-	<<SetFacilityDecoration "masterSuiteDecoration">>
+	<<includeDOM App.UI.facilityRedecoration("masterSuiteDecoration")>>
 <</if>>
diff --git a/src/uncategorized/genericPlotEvents.tw b/src/uncategorized/genericPlotEvents.tw
index 456081630041d23b5462cc126128e54e35b854f4..de9ac5d467ed39ef52f7c829c40becb45dda7afd 100644
--- a/src/uncategorized/genericPlotEvents.tw
+++ b/src/uncategorized/genericPlotEvents.tw
@@ -22,7 +22,7 @@
 <</if>>
 <<run cashX(500*$slaves.length, "event")>>
 
-Early one morning, you hear convulsive dry heaving coming from one of the bathrooms. On investigation, it seems that $slaves[_genPlot].slaveName woke up feeling terribly nauseous. $He's in no danger, but you've hardly checked $him over before more slaves stagger in. Every one of your slaves on curatives has been struck by the mysterious malady and has @@.red;sickened.@@
+Early one morning, you hear convulsive dry heaving coming from one of the bathrooms. On investigation, it seems that $slaves[_genPlot].slaveName woke up feeling terribly nauseous. $He's in no danger, but you've hardly checked $him over before more slaves stagger in. Every one of your slaves on curatives has been struck by the mysterious malady and has @@.health.dec;sickened.@@
 <br><br>
 It doesn't take much investigation before you find other slaveowners reporting the same thing. Elementary detective work fingers a particular drug supplier as the culprit, and before long the unfortunate pharmaceutical concern is drowning under a rain of harsh public comment and harsher private contract warfare. As the day wears on, the poor slaves feel a bit better, but begin to report discomfort in their breasts. Apparently the problem has to do with contamination of the curative production line with A-HGH production reactants.
 <br><br>
@@ -42,7 +42,7 @@ The firm promptly pays @@.yellowgreen;fair compensation@@ for the minor damage t
 	<<goto "Random Nonindividual Event">>
 <</if>>
 
-Early one morning, you hear heaving coming from one of the bathrooms. On investigation, it seems that $slaves[_genPlot].slaveName woke up feeling terribly nauseous. $He's in no danger, but you've hardly checked $him over before more slaves stagger in. Every one of your slaves on breast focused A-HGH has been struck by the mysterious malady and has @@.red;sickened.@@
+Early one morning, you hear heaving coming from one of the bathrooms. On investigation, it seems that $slaves[_genPlot].slaveName woke up feeling terribly nauseous. $He's in no danger, but you've hardly checked $him over before more slaves stagger in. Every one of your slaves on breast focused A-HGH has been struck by the mysterious malady and has @@.health.dec;sickened.@@
 <br><br>
 It doesn't take much investigation before you find other slaveowners reporting the same thing. Elementary detective work fingers a particular drug supplier as the culprit, and before long the unfortunate pharmaceutical concern is drowning under a rain of harsh public comment and harsher private contract warfare. As the day wears on, the poor slaves feel much better, and appear positively glowing. However, their breasts swell slightly and their bellies bulge, pointing to the issue being the contamination of the A-HGH production line with fertility agents.
 <br><br>
diff --git a/src/uncategorized/hgApplication.tw b/src/uncategorized/hgApplication.tw
deleted file mode 100644
index c5b39589583edfcd96b9386303845d1742b8b0b8..0000000000000000000000000000000000000000
--- a/src/uncategorized/hgApplication.tw
+++ /dev/null
@@ -1,441 +0,0 @@
-:: HG Application [nobr]
-
-<<setLocalPronouns _S.HeadGirl>>
-<<setLocalPronouns $activeSlave 2>>
-
-<<if ndef $HGSeverity>>
-	<<set $HGSeverity = 0>>
-<</if>>
-
-<<set $activeSlave.training = Math.clamp($activeSlave.training, 0, 150)>>
-<<set _effectiveness = _S.HeadGirl.actualAge+((_S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant)/3)-(_S.HeadGirl.accent*5)+($HGSeverity*10)+(($activeSlave.intelligence+$activeSlave.intelligenceImplant)/4)-($activeSlave.accent*5)>>
-<<if $AgePenalty == 0>>
-	<<set _effectiveness += Math.max(0,(30-_S.HeadGirl.actualAge))>>
-<</if>>
-<<if _S.HeadGirl.sexualFlaw == "abusive" && $HGSeverity >= 0>>
-	<<set _effectiveness += 10>>
-<</if>>
-<<if $HGtraining != "health">>
-	<<set $activeSlave.health.tired += 25>>
-<</if>>
-
-&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-weight: bold">Your Head Girl</span> <span class='slave-name'>_S.HeadGirl.slaveName</span> notices that <span class='slave-name'>$activeSlave.slaveName</span>
-<<switch $HGtraining>>
-<<case "health">>
-	is unwell.
-<<case "obedience">>
-	is disobedient.
-<<case "paraphilia">>
-	has a paraphilia.
-<<case "flaw">>
-	has an unpleasant flaw worth fixing.
-<<case "soften">>
-	has an unpleasant flaw that $he can try to soften into a unique quirk.
-<<case "oral skill">>
-	isn't as orally skilled as $he is.
-<<case "anal skill">>
-	isn't as skilled at taking anal as $he is.
-<<case "fuck skill">>
-	isn't as skilled at vaginal intercourse as $he is.
-<<case "whore skill">>
-	isn't as skilled at the fine art of sex for money as $he is.
-<<case "entertain skill">>
-	isn't as skilled at entertainment as $he is.
-<</switch>>
-
-<<if $HGtraining == "health">>
-
-$He helps $activeSlave.slaveName however $he can. The tender care has @@.green;improved $activeSlave.slaveName's health.@@
-<<run improveCondition($activeSlave, 10)>>
-<<set $activeSlave.health.tired = Math.clamp($activeSlave.health.tired-10, 0, 1000)>>
-<<set $activeSlave.training = 0>>
-
-<<else>>
-
-<<if _S.HeadGirl.career == "an arcology owner">>
-	$He was once an arcology owner $himself, making $him truly peerless as a Head Girl.
-	<<set _effectiveness += 50>>
-<<elseif setup.HGCareers.includes(_S.HeadGirl.career)>>
-	$He was used to giving commands and being obeyed in $his life before $he was a slave, experience $he can call on now.
-	<<set _effectiveness += 5>>
-<<elseif _S.HeadGirl.skill.headGirl >= $masteredXP>>
-	$He is used to giving commands and being obeyed through experience, rendering $him more effective.
-	<<set _effectiveness += 5>>
-<</if>>
-
-<<if _S.HeadGirl.actualAge > 30>>
-	$His age and experience help $him as $he works with $activeSlave.slaveName.
-<<elseif _S.HeadGirl.actualAge < 25 && $AgePenalty > 0>>
-	$His youth and inexperience impede $him as $he works with $activeSlave.slaveName.
-<</if>>
-
-<<if _S.HeadGirl.accent >= 3>>
-	$He does $his best to give orders in $language, but has to resort to a lot of unseemly gesticulation and shouting, making $him much less effective.
-<<elseif _S.HeadGirl.accent == 2>>
-	$His heavy <<print aNational(_S.HeadGirl.nationality)>> accent impedes $his clarity in $language, making $him somewhat less effective.
-<</if>>
-
-<<if ($HGtraining == "obedience") || ($HGtraining == "flaw") || ($HGtraining == "soften")>>
-	<<if _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
-		<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant > 50>>
-			$He needs $his wits about $him to mold $activeSlave.slaveName, who's quite intelligent _himself2.
-		<<elseif $activeSlave.intelligence+$activeSlave.intelligenceImplant >= -50>>
-			$His intelligence helps $him mold $activeSlave.slaveName.
-		<<else>>
-			Molding $activeSlave.slaveName is very easy, especially for a smart Head Girl like $him.
-		<</if>>
-	<</if>>
-<<else>>
-	<<if _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
-		<<if $activeSlave.intelligence+$activeSlave.intelligenceImplant > 50>>
-			Both slaves are quite intelligent, making $his job much easier.
-		<<elseif $activeSlave.intelligence+$activeSlave.intelligenceImplant >= -50>>
-			$He's quite intelligent and can teach $activeSlave.slaveName well.
-		<<else>>
-			$He needs all $his considerable intelligence to get through to the idiot $he has to teach.
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if $HGSeverity > 0>>
-	Your directives encourage $him to rape any slave that fails in the slightest way, a task $he approaches
-	<<if _S.HeadGirl.sexualFlaw == "abusive">>
-		eagerly, since $he prefers to take $his pleasure without consent.
-	<<elseif _S.HeadGirl.fetish == "sadist">>
-		with sadistic enthusiasm.
-	<<elseif _S.HeadGirl.fetish == "dom">>
-		with confidence.
-	<<elseif _S.HeadGirl.energy > 95>>
-		as a convenient way to
-		<<if (_S.HeadGirl.balls > 0)>>
-			drain $his perpetually overfilled balls into a series of spasming holes.
-		<<else>>
-			sate $his excess sexual appetite.
-		<</if>>
-	<<else>>
-		dutifully.
-	<</if>>
-	<<if $activeSlave.vagina == 0>>
-		In $activeSlave.slaveName's case, $he's careful to limit _his2 sexual abuse to things that won't take the _girl2's virginity. The slave
-	<<elseif $activeSlave.anus == 0>>
-		$He's careful to molest $activeSlave.slaveName in ways that don't involve penetrating _his2 virgin ass. The slave
-	<<else>>
-		$activeSlave.slaveName
-	<</if>>
-	<<if $activeSlave.devotion > 50>>
-		accepts the abuse, believing _he2 deserves to be raped.
-	<<elseif $activeSlave.devotion >= -20>>
-		is @@.gold;frightened@@ by the unceasing abuse.
-		<<set $activeSlave.trust -= 1>>
-	<<else>>
-		@@.mediumorchid;hates@@ and @@.gold;fears@@ the constant abuse.
-		<<set $activeSlave.trust -= 1>>
-		<<set $activeSlave.devotion -= 1>>
-	<</if>>
-	_S.HeadGirl.slaveName makes sure $his victim hates it, every time, and $activeSlave.slaveName can't help but do _his2 very best to avoid this treatment.
-<<elseif $HGSeverity == 0>>
-	You allow $him to use $his own judgment when punishing failures,
-	<<if _S.HeadGirl.sexualFlaw == "abusive">>
-		and $he usually decides in favor of forcing misbehaving slaves to get $him off.
-	<<elseif _S.HeadGirl.fetish == "sadist">>
-		permission the sadist uses for $his own gratification.
-	<<elseif _S.HeadGirl.fetish == "dom">>
-		which the dominant $girl uses confidently.
-	<<elseif _S.HeadGirl.energy > 95>>
-		which $he uses to
-		<<if (_S.HeadGirl.balls > 0)>>
-			force $his subordinates to keep $his balls drained.
-		<<else>>
-			extract the constant sexual favors $his libido demands.
-		<</if>>
-	<<else>>
-		and $he does $his best to be fair.
-	<</if>>
-	$activeSlave.slaveName
-	<<if $activeSlave.devotion > 50>>
-		is @@.mediumaquamarine;grateful@@ that _he2 is punished justly when _he2 makes mistakes.
-		<<set $activeSlave.trust += 1>>
-	<<elseif $activeSlave.devotion >= -20>>
-		does _his2 best to learn quickly and avoid punishment.
-	<<else>>
-		is @@.gold;scared@@ by _his2 inability to get away with the smallest disobedience.
-		<<set $activeSlave.trust -= 1>>
-	<</if>>
-<<else>>
-	You require $him to use respect when punishing; $he does $his best to motivate, but is unable to use real fear as a tool. $activeSlave.slaveName understands this, and @@.mediumaquamarine;trusts@@ that if _he2 fails, the consequences won't be too awful.
-	<<set $activeSlave.trust += 1>>
-<</if>>
-
-<<if $HGtraining == "obedience">>
-
-<<set $activeSlave.training = 0>>
-<<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
-<<if _effectiveness <= 0>>
-	$activeSlave.slaveName is smart enough to complicate things; _he2 manages to outwit $him this week and makes no progress.
-<<else>>
-	<<set $activeSlave.devotion += Math.ceil(_effectiveness/4)>>
-	<<if $activeSlave.devotion < -50>>
-		$He helps watch and restrain the rebellious $activeSlave.slaveName, helping wear _him2 down, @@.gold;breaking $activeSlave.slaveName's rebelliousness.@@
-	<<elseif $activeSlave.devotion < -20>>
-		$He keeps $activeSlave.slaveName constantly aware of _his2 slavery, @@.gold;breaking $activeSlave.slaveName's resistance.@@
-	<<else>>
-		$He acts as another pair of eyes watching $activeSlave.slaveName and metes out punishments, @@.gold;improving $activeSlave.slaveName's servitude.@@
-	<</if>>
-<</if>>
-
-<<elseif $HGtraining == "paraphilia">>
-
-<<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
-<<set $activeSlave.training += _effectiveness>>
-_S.HeadGirl.slaveName does $his best to get $activeSlave.slaveName past it with punishments and rewards,
-<<if $activeSlave.training > 100>>
-	and @@.green;resolves $activeSlave.slaveName's paraphilia.@@
-	<<set $activeSlave.training = 0>>
-	<<set $activeSlave.sexualFlaw = "none">>
-<<else>>
-	and makes partial progress.
-<</if>>
-
-<<elseif $HGtraining == "flaw">>
-
-<<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
-<<set $activeSlave.training += _effectiveness>>
-_S.HeadGirl.slaveName punishes $activeSlave.slaveName whenever $he catches _him2 indulging in _his2 bad habits,
-<<if $activeSlave.training > 100>>
-	and @@.green;fixes $activeSlave.slaveName's flaw.@@
-	<<set $activeSlave.training = 0>>
-	<<if $activeSlave.behavioralFlaw != "none">>
-		<<set $activeSlave.behavioralFlaw = "none">>
-	<<elseif $activeSlave.sexualFlaw != "none">>
-		<<set $activeSlave.sexualFlaw = "none">>
-	<</if>>
-<<else>>
-	and makes partial progress.
-<</if>>
-
-<<elseif $HGtraining == "soften">>
-
-<<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
-<<set $activeSlave.training += _effectiveness>>
-$He punishes $activeSlave.slaveName whenever $he sees _him2 breaking the rules yet does $his best to retain what makes the slave special,
-<<if $activeSlave.training > 150>>
-	<<set $activeSlave.training = 0>>
-	and successfully @@.green;softens $activeSlave.slaveName's flaw.@@
-	<<if $activeSlave.behavioralFlaw != "none">>
-		<<run SoftenBehavioralFlaw($activeSlave)>>
-	<<elseif $activeSlave.sexualFlaw != "none">>
-		<<run SoftenSexualFlaw($activeSlave)>>
-	<</if>>
-<<else>>
-	and makes partial progress.
-<</if>>
-
-<<elseif $HGtraining == "oral skill">>
-
-<<set $activeSlave.training = 0>>
-<<if (_S.HeadGirl.fetish == "cumslut") && (_S.HeadGirl.fetishStrength > 60)>>
-	In spare moments $he teaches $activeSlave.slaveName how to suck cocks, cunts, and assholes. $His enthusiasm for oral sex is infectious. $activeSlave.slaveName's @@.green;oral skills have improved.@@
-	<<= slaveSkillIncrease('oral', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.dick > 0) && canPenetrate(_S.HeadGirl)>>
-	In spare moments $he teaches $activeSlave.slaveName how to suck cocks, cunts, and assholes. Your Head Girl uses $his penis as an effective teaching tool. $activeSlave.slaveName's @@.green;oral skills have improved.@@
-	<<= slaveSkillIncrease('oral', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.clit > 2)>>
-	In spare moments $he teaches $activeSlave.slaveName how to suck cocks, cunts, and assholes. Your Head Girl uses $his pseudophallus-sized clit as an effective teaching tool. $activeSlave.slaveName's @@.green;oral skills have improved.@@
-	<<= slaveSkillIncrease('oral', $activeSlave, random(5,10))>>
-<<else>>
-	In spare moments $he teaches $activeSlave.slaveName how to suck cocks, cunts, and assholes. $activeSlave.slaveName's @@.green;oral skills have improved.@@
-<</if>>
-<<= slaveSkillIncrease('oral', $activeSlave, Math.ceil(_effectiveness/10))>>
-
-<<elseif $HGtraining == "anal skill">>
-
-<<set $activeSlave.training = 0>>
-<<if (_S.HeadGirl.fetish == "buttslut") && (_S.HeadGirl.fetishStrength > 60)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take it up the ass. Your Head Girl's enthusiasm for backdoor loving is infectious. $activeSlave.slaveName's @@.green;anal skills have improved.@@
-	<<= slaveSkillIncrease('anal', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.dick > 0) && canPenetrate(_S.HeadGirl)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a dick up the butt. Your Head Girl uses $his penis as an effective teaching tool. $activeSlave.slaveName's @@.green;anal skills have improved.@@
-	<<= slaveSkillIncrease('anal', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.clit > 2)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a phallus up the butt. Your Head Girl uses $his pseudophallus-sized clit as an effective teaching tool. $activeSlave.slaveName's @@.green;anal skills have improved.@@
-	<<= slaveSkillIncrease('anal', $activeSlave, random(5,10))>>
-<<else>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a dick up the butt. $activeSlave.slaveName's @@.green;anal skills have improved.@@
-<</if>>
-<<= slaveSkillIncrease('anal', $activeSlave, Math.ceil(_effectiveness/10))>>
-
-<<elseif $HGtraining == "fuck skill">>
-
-<<set $activeSlave.training = 0>>
-<<if (_S.HeadGirl.energy > 95)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a dick. Your Head Girl's enthusiasm for sex is infectious. $activeSlave.slaveName's @@.green;vanilla sex skills have improved.@@
-	<<= slaveSkillIncrease('vaginal', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.dick > 0) && canPenetrate(_S.HeadGirl)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a dick. Your Head Girl uses $his penis as an effective teaching tool. $activeSlave.slaveName's @@.green;vanilla sex skills have improved.@@
-	<<= slaveSkillIncrease('vaginal', $activeSlave, random(5,10))>>
-<<elseif (_S.HeadGirl.clit > 2)>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a phallus. Your Head Girl uses $his pseudophallus-sized clit as an effective teaching tool. $activeSlave.slaveName's @@.green;vanilla sex skills have improved.@@
-	<<= slaveSkillIncrease('vaginal', $activeSlave, random(5,10))>>
-<<else>>
-	In spare moments $he teaches $activeSlave.slaveName how to take a dick. $activeSlave.slaveName's @@.green;vanilla sex skills have improved.@@
-<</if>>
-<<= slaveSkillIncrease('vaginal', $activeSlave, Math.ceil(_effectiveness/10))>>
-
-<<elseif $HGtraining == "whore skill">>
-
-<<set $activeSlave.training = 0>>
-In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2. $activeSlave.slaveName's @@.green;whoring skills have improved.@@
-<<= slaveSkillIncrease('whoring', $activeSlave, Math.ceil(_effectiveness/10))>>
-
-<<elseif $HGtraining == "entertain skill">>
-
-<<set $activeSlave.training = 0>>
-<<if (_S.HeadGirl.fetish == "humiliation") && (_S.HeadGirl.fetishStrength > 60)>>
-	In spare moments $he teaches $activeSlave.slaveName how to entertain. Your Head Girl's enthusiasm for public display is infectious. $activeSlave.slaveName's @@.green;entertainment skills have improved.@@
-	<<= slaveSkillIncrease('entertainment', $activeSlave, random(5,10))>>
-<<else>>
-	In spare moments $he teaches $activeSlave.slaveName how to entertain. $activeSlave.slaveName's @@.green;entertainment skills have improved.@@
-<</if>>
-<<= slaveSkillIncrease('entertainment', $activeSlave, Math.ceil(_effectiveness/10))>>
-
-<</if>>
-<</if>>
-
-<<if ($activeSlave.devotion >= -20)>>
-<<if ($activeSlave.attrKnown == 0)>>
-	<<set $activeSlave.attrKnown = 1>>
-	_S.HeadGirl.slaveName also investigates the slave's basic sexual orientation, finding that _he2 is
-	<<if ($activeSlave.attrXY <= 40)>>
-		@@.red;disgusted by men@@
-	<<elseif ($activeSlave.attrXY <= 60)>>
-		indifferent to men
-	<<elseif ($activeSlave.attrXY <= 80)>>
-		@@.green;attracted to men@@
-	<<else>>
-		@@.green;intensely attracted to men@@
-	<</if>>
-	and
-	<<if ($activeSlave.attrXX <= 40)>>
-		@@.red;disgusted by women.@@
-	<<elseif ($activeSlave.attrXX <= 60)>>
-		indifferent to women.
-	<<elseif ($activeSlave.attrXX <= 80)>>
-		@@.green;attracted to women.@@
-	<<else>>
-		@@.green;intensely attracted to women.@@
-	<</if>>
-<</if>>
-<</if>>
-
-<<if $HGPiercings>>
-	<<if $activeSlave.devotion >= -20 && $activeSlave.genes == "XY" && $activeSlave.attrXY <= 35 && $arcologies[0].FSBodyPurist == "unset" && ($arcologies[0].FSPaternalist == "unset" || ($arcologies[0].FSPaternalist != "unset" && SlaveStatsChecker.modScore($activeSlave).total < 13))>>
-		<<set _piercingForbidden = 0>>
-		<<for _i = 0; _i < 1; _i++>>
-			<<if $activeSlave.earPiercing == 0 && $activeSlave.earShape !== "none">>
-				<<if $arcologies[0].FSDegradationist != "unset">>
-					<<set $activeSlave.earPiercing = 2>>
-				<<else>>
-					<<set $activeSlave.earPiercing = 1>>
-				<</if>>
-				<<run RulesDeconfliction($activeSlave)>>
-				<<if $activeSlave.earPiercing != $slaveAfterRA.earPiercing>>
-					<<set _piercingForbidden = 1, $activeSlave.earPiercing = 0>>
-				<<else>>
-					<<if $arcologies[0].FSDegradationist != "unset">>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs to adjust to life as a slave _girl2, so $he has the slave's ears pierced. This will be a constant reminder that _he2 is simply a sexual object here.
-					<<else>>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he has the slave's ears pierced. A little feminine touch can make a big difference.
-					<</if>>
-					<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>>
-					<<break>>
-				<</if>>
-			<</if>>
-			<<if $activeSlave.nosePiercing == 0>>
-				<<if $arcologies[0].FSDegradationist != "unset">>
-					<<set $activeSlave.nosePiercing = 2>>
-				<<else>>
-					<<set $activeSlave.nosePiercing = 1>>
-				<</if>>
-				<<run RulesDeconfliction($activeSlave)>>
-				<<if $activeSlave.nosePiercing != $slaveAfterRA.nosePiercing>>
-					<<set _piercingForbidden = 1, $activeSlave.nosePiercing = 0>>
-				<<else>>
-					<<if $arcologies[0].FSDegradationist != "unset">>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs to adjust to life as a slave _girl2, so $he gives the slave nasal studs and a large septum ring. It should push $activeSlave.slaveName to see _himself2 as a sexual object for others to use.
-					<<else>>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he gives the slave a cute little nose piercing. It should help $activeSlave.slaveName see _himself2 as a bit more feminine.
-					<</if>>
-					<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>>
-					<<break>>
-				<</if>>
-			<</if>>
-			<<if $activeSlave.eyebrowPiercing == 0>>
-				<<if $arcologies[0].FSDegradationist != "unset">>
-					<<set $activeSlave.eyebrowPiercing = 2>>
-				<<else>>
-					<<set $activeSlave.eyebrowPiercing = 1>>
-				<</if>>
-				<<run RulesDeconfliction($activeSlave)>>
-				<<if $activeSlave.eyebrowPiercing != $slaveAfterRA.eyebrowPiercing>>
-					<<set _piercingForbidden = 1, $activeSlave.eyebrowPiercing = 0>>
-				<<else>>
-					<<if $arcologies[0].FSDegradationist != "unset">>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs to adjust to life as a slave _girl2, so $he gives the slave multiple eyebrow piercings. A slutty touch for a slave _girl2 should help _him2 feel a little hungrier for cock.
-					<<else>>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he gives the slave a cute little eyebrow piercing. A slutty touch for a slave _girl2 should help _him2 feel a little hungrier for cock.
-					<</if>>
-					<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>>
-					<<break>>
-				<</if>>
-			<</if>>
-			<<if $activeSlave.lipsPiercing == 0>>
-				<<if $arcologies[0].FSDegradationist != "unset">>
-					<<set $activeSlave.lipsPiercing = 2>>
-				<<else>>
-					<<set $activeSlave.lipsPiercing = 1>>
-				<</if>>
-				<<run RulesDeconfliction($activeSlave)>>
-				<<if $activeSlave.lipsPiercing != $slaveAfterRA.lipsPiercing>>
-					<<set _piercingForbidden = 1, $activeSlave.lipsPiercing = 0>>
-				<<else>>
-					<<if $arcologies[0].FSDegradationist != "unset">>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs to adjust to life as a slave _girl2, so $he has the slave's lower lip pierced. _His2 mouth is for pleasing penises now, so it'll help _him2 if it looks like it.
-					<<else>>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he has the slave's lower lip pierced. _His2 mouth is for pleasing penises now, so it'll help _him2 if it looks like it.
-					<</if>>
-					<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>>
-					<<break>>
-				<</if>>
-			<</if>>
-			<<if $activeSlave.navelPiercing == 0>>
-				<<if $arcologies[0].FSDegradationist != "unset">>
-					<<set $activeSlave.navelPiercing = 2>>
-				<<else>>
-					<<set $activeSlave.navelPiercing = 1>>
-				<</if>>
-				<<run RulesDeconfliction($activeSlave)>>
-				<<if $activeSlave.navelPiercing != $slaveAfterRA.navelPiercing>>
-					<<set _piercingForbidden = 1, $activeSlave.navelPiercing = 0>>
-				<<else>>
-					<<if $arcologies[0].FSDegradationist != "unset">>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he has the slave's navel pierced with a big ring. Whatever _he2 thinks in _his2 mind, _S.HeadGirl.slaveName makes clear to _him2 that _his2 body belongs to you.
-					<<else>>
-						_S.HeadGirl.slaveName knows that $activeSlave.slaveName needs help adjusting to life as a slave _girl2, so $he has the slave's navel pierced. The prettier _his2 lower half looks, the less reluctant _he2 should feel to take it up the butt.
-					<</if>>
-					<<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>>
-					<<break>>
-				<</if>>
-			<</if>>
-			<<break>>
-		<</for>>
-		<<if _piercingForbidden>>
-			<<if $arcologies[0].FSDegradationist != "unset">>
-				_S.HeadGirl.slaveName thinks some piercings might push $activeSlave.slaveName to adjust to life as a slave _girl2, but $he also knows you have rules applied to this slave that forbid it.
-			<<else>>
-				_S.HeadGirl.slaveName thinks some cute piercings might help $activeSlave.slaveName adjust to life as a slave _girl2, but $he also knows you have rules applied to this slave that forbid it.
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<set $slaves[$slaveIndices[$activeSlave.ID]] = $activeSlave>> /* save changes */
diff --git a/src/uncategorized/main.tw b/src/uncategorized/main.tw
index c8f7fb34b21dab882b08c299c00c52a5eae39ca1..f01cb6e089b43c9fe7e529aa1c68ec4009bb1735 100644
--- a/src/uncategorized/main.tw
+++ b/src/uncategorized/main.tw
@@ -17,11 +17,11 @@
 <<run SlaveSort.slaves($slaves)>>
 
 <<if $newModelUI == 1>>
-    <<includeDOM V.building.render()>>
+	<<includeDOM V.building.render()>>
 <</if>>
 
 <<if $seeArcology == 1>>
-    <<includeDOM App.Desc.playerArcology(App.UI.DOM.combineNodes("| ", App.UI.DOM.passageLink("Hide", "Main", () => {V.seeArcology = 0})))>>
+	<<includeDOM App.Desc.playerArcology(App.UI.DOM.combineNodes("| ", App.UI.DOM.passageLink("Hide", "Main", () => {V.seeArcology = 0})))>>
 <</if>>
 
 <<if $seeDesk == 1>>
diff --git a/src/uncategorized/manageArcology.tw b/src/uncategorized/manageArcology.tw
index 7dd1b11b4f6c236bbb8d4b12a237fa7e9f2e7e14..32ece54361bd225264926d55850056df9a320e88 100644
--- a/src/uncategorized/manageArcology.tw
+++ b/src/uncategorized/manageArcology.tw
@@ -133,7 +133,7 @@
 		<<link "Heavy subsidy" "Manage Arcology">>
 			<<set $FCTV.receiver = 1, $FCTV.weekEnabled = $week, $PC.skill.hacking += 1>>
 			<<run FCTV.initChannels()>>
-			<<run cashX(forceNeg(Math.trunc(_heavySub), "capEx")>>
+			<<run cashX(forceNeg(Math.trunc(_heavySub), "capEx"))>>
 			<<run repX(-1500, "capEx")>>
 		<</link>>
 	<<elseif ($FCTV.receiver == 3)>>
diff --git a/src/uncategorized/multiImplant.tw b/src/uncategorized/multiImplant.tw
index 16caaf6cb4bed4282262091aebc1d485c5d9a603..1f09cfea1f951e387c5d6e0ffeb0c686f17d86f9 100644
--- a/src/uncategorized/multiImplant.tw
+++ b/src/uncategorized/multiImplant.tw
@@ -77,7 +77,7 @@ that are ready be sent down.
 	*/
 	<<if getSlave($AS).health.health - (_prostheticCount * 20) < -75>>
 		<br><hr>
-		@@.red;Estimated health impact too great; <<= getSlave($AS).slaveName>> skipped.@@
+		@@.health.dec;Estimated health impact too great; <<= getSlave($AS).slaveName>> skipped.@@
 		<<continue>>
 	<</if>>
 
diff --git a/src/uncategorized/newGamePlus.tw b/src/uncategorized/newGamePlus.tw
index 002b1ddecc9b030e4464f4ece09993e48faf2158..8b8a9636a2a252d4db41189b8d33da08cb396b99 100644
--- a/src/uncategorized/newGamePlus.tw
+++ b/src/uncategorized/newGamePlus.tw
@@ -1,6 +1,6 @@
 :: New Game Plus [nobr]
 
-<<set $ui = "start">> <<unset $SlaveSummaryFiler>>
+<<set $ui = "start">>
 <<if ndef $slavesToImportMax>><<set $slavesToImportMax = 5>><</if>>
 
 <<set _fee = 50000+($slavesToImportMax*10000)>>
@@ -12,7 +12,7 @@ You have decided to start over and will be able to take a few things with you: a
 <br><br>
 
 <<if $cash >= _fee>>
-	You have allocated funds to bring up to $slavesToImportMax slaves with you (or your equivalent) to a new arcology. It will cost @@.yellowgreen;<<print cashFormat(_fee)>>@@ to insure another slave's safe transfer.  You have @@.yellowgreen;<<print cashFormat($cash)>>@@ to spend.
+	You have allocated funds to bring up to $slavesToImportMax slaves with you (or your equivalent) to a new arcology. It will cost @@.yellowgreen;<<print cashFormat(_fee)>>@@ to insure another slave's safe transfer. You have @@.yellowgreen;<<print cashFormat($cash)>>@@ to spend.
 	<br>[[Increase slave import capacity by 1.|New Game Plus][cashX(forceNeg(_fee), "capEx"), $slavesToImportMax++]]
 <<else>>
 	You lack the @@.yellowgreen;<<= cashFormat(_fee)>>@@ needed to bring any more than $slavesToImportMax slaves with you (or your equivalent) to a new arcology.
diff --git a/src/uncategorized/nextWeek.tw b/src/uncategorized/nextWeek.tw
deleted file mode 100644
index 4eed4de160a64d291e08944d483d862562a19854..0000000000000000000000000000000000000000
--- a/src/uncategorized/nextWeek.tw
+++ /dev/null
@@ -1,322 +0,0 @@
-:: Next Week [nobr]
-
-<<set $HackingSkillMultiplier = upgradeMultiplier('hacking')>>
-<<set $upgradeMultiplierArcology = upgradeMultiplier('engineering')>>
-<<set $upgradeMultiplierMedicine = upgradeMultiplier('medicine')>>
-<<set $upgradeMultiplierTrade = upgradeMultiplier('trading')>>
-
-<<if $rivalOwner != 0>>
-	<<set _rival = $arcologies.find(function(s) { return s.rival == 1; })>>
-	<<if def _rival>>
-		<<set $rivalOwner = _rival.prosperity>>
-	<</if>>
-<</if>>
-
-<<if $playerAging != 0>>
-	<<set $PC.birthWeek += 1>>
-	<<if $PC.ovaryAge >= 47 && $PC.ovaries == 1 && ($PC.preg == -1 || $PC.preg == 0)>>
-		<<set $PC.preg = -2>>
-	<</if>>
-	<<if $PC.birthWeek >= 52>>
-		<<set $PC.birthWeek = 0>>
-		<<if $playerAging == 2>>
-			<<set $PC.physicalAge++, $PC.actualAge++, $PC.visualAge++, $PC.ovaryAge++>>
-			<<AgePCEffects>>
-		<</if>>
-	<</if>>
-<</if>>
-<<if $menstruation == 1>>
-<<elseif $PC.geneticQuirks.superfetation == 2 && $PC.womb.length > 0>>
-	<<if $PC.fertPeak == 0>>
-		<<set $PC.fertPeak = 1>>
-	<</if>>
-	<<set $PC.fertPeak-->>
-<<elseif $PC.fertPeak != 0>>
-	<<set $PC.fertPeak = 0>>
-<</if>>
-
-/*Adding random changes to the economy*/
-<<if $difficultySwitch == 1>>
-	<<set _globalEconSeed = random(1,100)>>
-	<<if _globalEconSeed > 98>>
-		<<set $economy += 2>>
-	<<elseif _globalEconSeed > 85>>
-		<<set $economy += 1>>
-	<<elseif _globalEconSeed <= 2>>
-		<<set $economy -= 2>>
-	<<elseif _globalEconSeed <= 25 + $econRate * 10>>
-		<<set $economy -= 1>>
-	<</if>>
-	<<if $economy < 20>>
-		<<set $economy = 20>>
-	<</if>>
-	<<set _localEconSeed = random(1,100)>>
-	<<if $localEcon <= ($economy + $econAdvantage)>>
-		<<if _localEconSeed > 95>>
-			<<set $localEcon += 2>>
-		<<elseif _localEconSeed > 50>>
-			<<set $localEcon += 1>>
-		<<elseif _localEconSeed <= 1>>
-			<<set $localEcon -= 2>>
-		<<elseif _localEconSeed <= 10>>
-			<<set $localEcon -= 1>>
-		<</if>>
-	<<elseif $localEcon <= ($economy + $econAdvantage + 5)>>
-		<<if _localEconSeed > 98>>
-			<<set $localEcon += 2>>
-		<<elseif _localEconSeed > 66>>
-			<<set $localEcon += 1>>
-		<<elseif _localEconSeed <= 2>>
-			<<set $localEcon -= 2>>
-		<<elseif _localEconSeed <= 33>>
-			<<set $localEcon -= 1>>
-		<</if>>
-	<<elseif _localEconSeed > 99>>
-		<<set $localEcon += 2>>
-	<<elseif _localEconSeed > 90>>
-		<<set $localEcon += 1>>
-	<<elseif _localEconSeed <= 5>>
-		<<set $localEcon -= 2>>
-	<<elseif _localEconSeed <= 50>>
-		<<set $localEcon -= 1>>
-	<</if>>
-	<<if $localEcon < 20>>
-		<<set $localEcon = 20>>
-	<</if>>
-
-	<<if $experimental.food == 1>>
-		<<if $localEcon > 100>>
-			<<set $farmyardFoodCost = Math.max(5 / (1 + (Math.trunc(1000-100000/$localEcon)/10)/100), 3.125)>>
-		<<elseif $localEcon == 100>>
-			<<set $farmyardFoodCost = 5 >>
-		<<else>>
-			<<set $farmyardFoodCost = Math.min(5 * (1 + 1.5 * Math.sqrt(Math.trunc(100000/$localEcon-1000)/10)/100), 6.5)>>
-		<</if>>
-	<</if>>
-	<<set $foodCost = Math.trunc(2500/$localEcon)>>
-	<<set $drugsCost = Math.trunc(10000/$localEcon)>>
-	<<set $rulesCost = Math.trunc(10000/$localEcon)>>
-	<<set $modCost = Math.trunc(5000/$localEcon)>>
-	<<set $surgeryCost = Math.trunc(30000/($localEcon * ($PC.career === "medicine" ? 2 : 1)))>>
-<</if>>
-
-<<set $arcologies[0].prosperity = Math.clamp($arcologies[0].prosperity, 1, $AProsperityCap)>>
-
-<<set $averageTrust = 0, $averageDevotion = 0, _slavesContributing = 0, _OldHG = -1, _NewHG = -1, _SL = $slaves.length>>
-<<if $studio == 1>>
-	<<for _genre range App.Porn.getAllGenres()>>
-		<<set $pornStars[_genre.fameVar].p1count = 0>>
-	<</for>>
-<</if>>
-<<for _i = 0; _i < _SL; _i++>>
-	<<if def $slaves[_i].curBabies>>
-		<<run delete $slaves[_i].curBabies>>
-	<</if>>
-	<<run ageSlaveWeeks($slaves[_i])>>
-	<<if $slaves[_i].indenture > 0>>
-		<<set $slaves[_i].indenture -= 1>>
-	<</if>>
-	<<if $slaves[_i].induceLactation > 0>>
-		<<set $slaves[_i].induceLactation-->>
-	<</if>>
-	<<if ($slaves[_i].lactation == 1)>>
-		<<if $slaves[_i].lactationDuration == 1>>
-			<<set $slaves[_i].boobsMilk = 10*$slaves[_i].lactationAdaptation>>
-			<<set $slaves[_i].boobs += $slaves[_i].boobsMilk>>
-		<</if>>
-	<</if>>
-	<<if $menstruation == 1>>
-	<<elseif $slaves[_i].geneticQuirks.superfetation == 2 && $slaves[_i].womb.length > 0>>
-		<<if $slaves[_i].fertPeak == 0>>
-			<<set $slaves[_i].fertPeak = 1>>
-		<</if>>
-		<<set $slaves[_i].fertPeak-->>
-	<<elseif $slaves[_i].fertPeak != 0>>
-		<<set $slaves[_i].fertPeak = 0>>
-	<</if>>
-	<<set $slaves[_i].trust = Number($slaves[_i].trust.toFixed(1)), $slaves[_i].devotion = Number($slaves[_i].devotion.toFixed(1))>>
-	<<set $slaves[_i].oldDevotion = $slaves[_i].devotion, $slaves[_i].oldTrust = $slaves[_i].trust, $slaves[_i].minorInjury = 0>>
-	<<if $slaves[_i].sentence > 1>>
-		<<set $slaves[_i].sentence -= 1>>
-	<<elseif $slaves[_i].sentence == 1>>
-		<<= removeJob($slaves[_i], $slaves[_i].assignment)>>
-	<</if>>
-	<<if $slaves[_i].weekAcquired < 0>>
-		<<set $slaves[_i].weekAcquired = 0>>
-	<</if>>
-	<<if $slaves[_i].relationship == 0>>
-		<<set $slaves[_i].relationshipTarget = 0>>
-	<</if>>
-	<<if $slaves[_i].rivalry == 0>>
-		<<set $slaves[_i].rivalryTarget = 0>>
-	<<elseif $slaves[_i].rivalry < 0>>
-		<<set $slaves[_i].rivalryTarget = 0, $slaves[_i].rivalry = 0>>
-	<</if>>
-	<<if $slaves[_i].vagina < 0>>
-		<<set $slaves[_i].vaginalAccessory = "none", $slaves[_i].chastityVagina = 0, $slaves[_i].vaginaPiercing = 0>>
-	<</if>>
-	<<if $slaves[_i].dick == 0>>
-		<<set $slaves[_i].dickAccessory = "none", $slaves[_i].chastityPenis = 0, $slaves[_i].dickTat = 0, $slaves[_i].dickPiercing = 0>>
-	<</if>>
-	/* I don't trust these */
-	<<if !hasAnyArms($slaves[_i])>>
-		<<set $slaves[_i].armsTat = 0, $slaves[_i].nails = 0, $slaves[_i].armAccessory = "none">>
-	<</if>>
-	<<if !hasAnyLegs($slaves[_i])>>
-		<<set $slaves[_i].heels = 0, $slaves[_i].shoes = "none", $slaves[_i].legAccessory = "none", $slaves[_i].legsTat = 0>>
-	<</if>>
-	/* irregular leptin production weight gain/loss setter */
-	<<if $slaves[_i].geneticQuirks.wGain == 2 && $slaves[_i].geneticQuirks.wLoss == 2>>
-		<<set $slaves[_i].weightDirection = either(-1, 1)>>
-	<<elseif $slaves[_i].geneticQuirks.wGain == 2>>
-		<<set $slaves[_i].weightDirection = 1>>
-	<<elseif $slaves[_i].geneticQuirks.wLoss == 2>>
-		<<set $slaves[_i].weightDirection = -1>>
-	<<else>>
-		<<set $slaves[_i].weightDirection = 0>>
-	<</if>>
-	/% Fix some possible floating point rounding errors, and bring precision to one decimal place. %/
-	<<run SlaveStatClamp($slaves[_i])>>
-	<<set $slaves[_i].energy = Math.clamp($slaves[_i].energy.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].attrXY = Math.clamp($slaves[_i].attrXY.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].attrXX = Math.clamp($slaves[_i].attrXX.toFixed(1), 0, 100)>>
-	<<if $slaves[_i].fetishStrength > 95>>
-		<<set $slaves[_i].fetishStrength = 100>>
-	<<else>>
-		<<set $slaves[_i].fetishStrength = Math.clamp($slaves[_i].fetishStrength.toFixed(1), 0, 100)>>
-	<</if>>
-	<<set $slaves[_i].weight = Math.clamp($slaves[_i].weight.toFixed(1), -100, 200)>>
-	<<set $slaves[_i].butt = Number($slaves[_i].butt.toFixed(1))>>
-	<<set $slaves[_i].muscles = Math.clamp($slaves[_i].muscles.toFixed(1), -100, 100)>>
-	<<set $slaves[_i].face = Math.clamp($slaves[_i].face.toFixed(1), -100, 100)>>
-	<<set $slaves[_i].lips = Math.clamp($slaves[_i].lips.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].skill.oral = Math.clamp($slaves[_i].skill.oral.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].skill.vaginal = Math.clamp($slaves[_i].skill.vaginal.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].skill.anal = Math.clamp($slaves[_i].skill.anal.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].skill.whoring = Math.clamp($slaves[_i].skill.whoring.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].skill.entertainment = Math.clamp($slaves[_i].skill.entertainment.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].lactationAdaptation = Math.clamp($slaves[_i].lactationAdaptation.toFixed(1), 0, 100)>>
-	<<set $slaves[_i].intelligenceImplant = Number($slaves[_i].intelligenceImplant.toFixed(1), 0, 30)>>
-	<<set $slaves[_i].prematureBirth = 0>>
-	<<if ($HGSuiteEquality == 1) && ($HeadGirlID != 0) && ($slaves[_i].devotion > 50)>>
-		<<if ($slaves[_i].assignment == "live with your Head Girl")>>
-			<<set _NewHG = _i>>
-		<<elseif ($slaves[_i].ID == $HeadGirlID)>>
-			<<set _OldHG = _i>>
-		<</if>>
-	<</if>>
-	/* AVERAGE VALUES UPDATE */
-	<<if assignmentVisible($slaves[_i])>>
-		<<set $averageTrust += $slaves[_i].trust, $averageDevotion += $slaves[_i].devotion, _slavesContributing++>>
-	<<elseif ($slaves[_i].assignment != "be confined in the cellblock") && ($slaves[_i].assignment != "be confined in the arcade") && (($slaves[_i].assignment != "work in the dairy") || ($dairyRestraintsSetting < 2)) && $slaves[_i].assignment != "labor in the production line">>
-		<<set $averageTrust += $slaves[_i].trust*0.5, $averageDevotion += $slaves[_i].devotion*0.5, _slavesContributing += 0.5>>
-	<</if>>
-	<<if $studio == 1>>
-		<<set _activeGenres = App.Porn.getAllGenres().filter((g) => $slaves[_i].porn.fame[g.fameVar] > 0)>>
-		<<for _genre range _activeGenres>>
-			<<set $pornStars[_genre.fameVar].p1count++>>
-		<</for>>
-	<</if>>
-	<<if $slaves[_i].choosesOwnAssignment > 0>>
-		<<= assignJob($slaves[_i], "choose her own job")>>
-	<</if>>
-<</for>>
-<<if _slavesContributing != 0>>
-	<<set $averageTrust = $averageTrust/_slavesContributing>>
-	<<set $averageDevotion = $averageDevotion/_slavesContributing>>
-<</if>>
-<<set $enduringTrust = ($averageTrust+($enduringTrust*3))/4>>
-<<set $enduringDevotion = ($averageDevotion+($enduringDevotion*3))/4>>
-
-<<if (_OldHG != -1) && (_NewHG != -1)>>
-	<<set _oldTimeInGrade = $HGTimeInGrade>> /* preserve time in grade during HG swaps */
-	<<if $personalAttention == "HG">>
-		/* keep removeJob from clearing PC HG supporting. */
-		<<set _keepHelpingHG = 1>>
-	<</if>>
-	<<= removeJob($slaves[_NewHG], "live with your Head Girl")>>
-	<<= assignJob($slaves[_OldHG], "live with your Head Girl")>>
-	<<= assignJob($slaves[_NewHG], "be your Head Girl")>>
-	<<set $slaves[_NewHG].diet = "healthy", $HGTimeInGrade = _oldTimeInGrade>>
-	<<if _keepHelpingHG>>
-		<<set $personalAttention = "HG">>
-	<</if>>
-<</if>>
-
-<<set _toSearch = $PC.refreshment.toLowerCase()>>
-<<if _toSearch.indexOf("fertility") != -1>>
-	<<set $PC.forcedFertDrugs = 1>>
-<<elseif $PC.forcedFertDrugs > 0>>
-	<<set $PC.forcedFertDrugs-->>
-<</if>>
-
-<<if $FCTV.receiver > 0>>
-	<<if $FCTV.pcViewership.frequency != -1>>
-		<<set $FCTV.pcViewership.count++>>
-		<<if $FCTV.pcViewership.count >= $FCTV.pcViewership.frequency>>
-			<<set $FCTV.pcViewership.count = 0>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<<set $week++>>
-
-<<if $playerSurgery > 0>>
-	<<set $playerSurgery-->>
-<</if>>
-
-<<if $secExpEnabled > 0>>
-	<<if $foughtThisWeek == 1>>
-		<<set $foughtThisWeek = 0>>
-	<</if>>
-
-	<<if $SecExp.buildings.riotCenter && $SecExp.buildings.riotCenter.sentUnitCooldown > 0>>
-		<<set $SecExp.buildings.riotCenter.sentUnitCooldown-->>
-	<</if>>
-	<<if $SecExp.proclamation.cooldown > 0>>
-		<<set $SecExp.proclamation.cooldown-->>
-	<</if>>
-<</if>>
-
-<<include "Weather">>
-
-<<if $boomerangWeeks>><<set $boomerangWeeks++>><<else>><<set $boomerangSlave = 0>><</if>>
-<<if $traitorWeeks>><<set $traitorWeeks++>><</if>>
-
-<<set $thisWeeksFSWares = $merchantFSWares.randomMany(2)>>
-<<set $thisWeeksIllegalWares = $merchantIllegalWares.randomMany(1)>>
-<<set $prisonCircuitIndex++>><<if $prisonCircuitIndex >= $prisonCircuit.length>><<set $prisonCircuitIndex = 0>><</if>>
-
-<<set $coursed = 0, $prestigeAuctioned = 0, $eliteAuctioned = 0, $shelterSlave = 0, $shelterSlaveBought = 0, $slaveMarketLimit = 10 + ($rep / 1000), $slavesSeen = 0, $slavesSacrificedThisWeek = 0>>
-
-<<if $pit>>
-	<<set $pit.fought = false>>
-<</if>>
-
-/% These are variables that either should be made into _temp vars or should be Zeroed out once done with them instead of here. This can also interfere with debugging or hide NaN's as zeroing things out would clear a NaN. Also could stop from NaN's getting worse? %/
-/% Integer and float variables. No real need to zero them out but doesn't hurt to have them in a known state, though this might mask variables NaN'ing out. Takes up the least amount of Memory besides a "" string. %/
-<<set $i = 0, $j = 0, $averageProsperity = 0, $motherSlave = -1, $daughterSlave = -1, $devMother = -1, $devDaughter = -1, $alphaTwin = -1, $betaTwin = -1, $youngerSister = -1, $olderSister = -1>>
-<<set $boobsID = -1, $boobsInterestTargetID = -1, $buttslutID = -1, $buttslutInterestTargetID = -1, $cumslutID = -1, $cumslutInterestTargetID = -1, $humiliationID = -1, $humiliationInterestTargetID = -1, $sadistID = -1, $sadistInterestTargetID = -1, $masochistID = -1, $masochistInterestTargetID = -1, $domID = -1, $dominantInterestTargetID = -1, $subID = -1, $submissiveInterestTargetID = -1, $shelterGirlID = -1>>
-
-/% Other arrays %/
-<<set $events = [], $RESSevent = [], $RESSTRevent = [], $RETSevent = [], $RECIevent = [], $RecETSevent = [], $REFIevent = [], $REFSevent = [], $PESSevent = [], $PETSevent = [], $FSAcquisitionEvents = [], $FSNonconformistEvents = [], $REAnalCowgirlSubIDs = [], $REButtholeCheckinIDs = [], $recruit = [], $RETasteTestSubIDs = [], $rebelSlaves = [], $REBoobCollisionSubIDs = [], $REIfYouEnjoyItSubIDs = [], $RESadisticDescriptionSubIDs = [], $REShowerForceSubIDs = [], $RECockmilkInterceptionIDs = [], $REInterslaveBeggingIDs = [], $eligibleSlaves = []>>
-
-/% Slave Objects using 0 instead of null. Second most memory eaten up. %/
-<<set $activeSlave = 0, $eventSlave = 0, $subSlave = 0, $milkTap = 0, $relation = 0, $relative = 0, $relative2 = 0>>
-
-/% Slave Objects that never get zeroed so null them here. Second most memory eaten up. %/
-<<set $beforeGingering = null>>
-
-/% Strings Memory varies. %/
-<<set $buyer = "", $desc = "", $event = "", $goto = "", $malefactor = "">>
-/% Done with zeroing out, what should be for the most part Temps %/
-
-<<if $autosave != 0>>
-	<<script>>Save.autosave.save("Week Start Autosave")<</script>>
-<</if>>
-
-<<if $SF.Toggle && $SF.FS.Tension > 100>>
-	<<if ($rep > 17500)>> <<set $rep = 17500>> <</if>>
-<</if>>
-<<set $NaNArray = findNaN()>>
-<<goto "Main">>
diff --git a/src/uncategorized/officeDescription.tw b/src/uncategorized/officeDescription.tw
index ec20b11748e5eae5de8221a82590d1ce2835f3c1..9f5dd957d7bc8723bd29cc7ca79050aeb9872b8f 100644
--- a/src/uncategorized/officeDescription.tw
+++ b/src/uncategorized/officeDescription.tw
@@ -129,7 +129,7 @@
 			<<case "eugenics">>
 				_heA's depicted striking a sexy pose; _heA's so stunning you can't look away.
 			<<case "physical idealist">>
-				_heA's depicted flexing _hisA <<if $arcologies[0].FSPhysicalIdealistStrongFat == 1>>fat veiled <</if>>tremendous musculature intimidatingly.
+				_heA's depicted flexing _hisA tremendous<<if $arcologies[0].FSPhysicalIdealistStrongFat == 1>>, fat-veiled<</if>> musculature intimidatingly.
 			<<case "hedonistic decadence">>
 				_heA's depicted deep throating a banana while groping _hisA large, soft belly.
 			<<case "gender radicalist">>
diff --git a/src/uncategorized/pRivalInitiation.tw b/src/uncategorized/pRivalInitiation.tw
index 576f6a0b0cbc99b4b5913ab6d7cd0369b7f6dbe7..af1698ef86844716998b5449cc9bb272ed4f640c 100644
--- a/src/uncategorized/pRivalInitiation.tw
+++ b/src/uncategorized/pRivalInitiation.tw
@@ -38,7 +38,7 @@ This is a special week, the week of your victory. <<= App.UI.slaveDescriptionDia
 <<if $activeSlave.anus == 0>>
 	<br><<link "Break in $his asshole and then let the public use it">>
 		<<replace "#result">>
-			$activeSlave.slaveName, who has been fairly dignified up to this point, breaks down when $he's placed in stocks with $his ass in the air. $His sobs become screams when, for the first time in $his life, $he feels the burning sensation of a well-lubricated <<if $PC.dick != 0>>cockhead<<else>>strap-on<</if>> forcing its way past $his virgin sphincter. Raping a virgin anus is not a new pleasure for you, but the usual shrieking, struggling and spasming is all the sweeter this time. @@.green;Half the arcology@@ has used $his @@.red;poor injured butthole@@ by the end of the day; $he @@.gold;is learning to fear you,@@ and hates you @@.mediumorchid;even more@@ if possible.
+			$activeSlave.slaveName, who has been fairly dignified up to this point, breaks down when $he's placed in stocks with $his ass in the air. $His sobs become screams when, for the first time in $his life, $he feels the burning sensation of a well-lubricated <<if $PC.dick != 0>>cockhead<<else>>strap-on<</if>> forcing its way past $his virgin sphincter. Raping a virgin anus is not a new pleasure for you, but the usual shrieking, struggling and spasming is all the sweeter this time. @@.green;Half the arcology@@ has used $his @@.health.dec;poor injured butthole@@ by the end of the day; $he @@.gold;is learning to fear you,@@ and hates you @@.mediumorchid;even more@@ if possible.
 			<<set $activeSlave.devotion -= 4, $activeSlave.trust -= 5, $activeSlave.anus = 3>>
 			<<run seX($activeSlave, "anal", "public", "penetrative", 47)>>
 			<<run repX(2500, "event", $activeSlave), healthDamage($activeSlave, 10)>>
diff --git a/src/uncategorized/pUndergroundRailroad.tw b/src/uncategorized/pUndergroundRailroad.tw
index b6d0a8f13f57f22ea07d338444d93f1ee51bdac5..3333aa77fd3cdc08b126516c951ca86cbf1aa895 100644
--- a/src/uncategorized/pUndergroundRailroad.tw
+++ b/src/uncategorized/pUndergroundRailroad.tw
@@ -335,7 +335,7 @@ This is disturbing, to say the least. After close investigation, it appears some
 	<br><<link "Publicly flog $him for treasonous conversation">>
 		<<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Random Nonindividual Event">><<UpdateNextButton>> /* unlock Continue button */
 		<<replace "#result">>
-		You announce that a treasonous slave will be publicly flogged. Treason is understood in the Free Cities to be activity that tends to undermine slavery, and public interest is considerable when $activeSlave.slaveName is dragged out into a public atrium and secured to a post. You do your duty; the one that passes the sentence should swing the lash. The ordeal is long and bloody. The populace understand the necessity of the punishment, though they are @@.red;disturbed@@ that such a thing could happen in your penthouse of all places. The effect on $activeSlave.slaveName's health @@.red;is serious,@@ and $he is @@.gold;terrified of failing you again.@@
+		You announce that a treasonous slave will be publicly flogged. Treason is understood in the Free Cities to be activity that tends to undermine slavery, and public interest is considerable when $activeSlave.slaveName is dragged out into a public atrium and secured to a post. You do your duty; the one that passes the sentence should swing the lash. The ordeal is long and bloody. The populace understand the necessity of the punishment, though they are @@.red;disturbed@@ that such a thing could happen in your penthouse of all places. The effect on $activeSlave.slaveName's health @@.health.dec;is serious,@@ and $he is @@.gold;terrified of failing you again.@@
 		<<set $activeSlave.trust -= 15>>
 		<<run repX(-500, "event", $activeSlave), healthDamage($activeSlave, 30)>>
 		<</replace>>
diff --git a/src/uncategorized/pePitFight.tw b/src/uncategorized/pePitFight.tw
index babb0f886f687e460f41088723813f50a8411245..6a7ca36dc785ae4a59ce0dc8d6fc6e0a9d64622d 100644
--- a/src/uncategorized/pePitFight.tw
+++ b/src/uncategorized/pePitFight.tw
@@ -209,7 +209,7 @@ The umpire announces gravely that the fight is to the death and rings a bell.
 	<<set $activeSlave.counter.pitKills += 1>>
 	<<set $pitKillsTotal += 1>>
 <<elseif _deadliness > (_opponent)>>
-	The combat is long and exhausting. $activeSlave.slaveName and $his opponent are closely matched in terms of skill, so neither takes foolish risks and a protracted, bloody fight results as both slaves take horrible but non-life-threatening cuts. Finally, $activeSlave.slaveName's superior physical condition wins out and $his opponent falls from exhaustion and blood loss. $activeSlave.slaveName stumbles over to open _hisU throat. This victory has @@.green;won you some renown@@ and @@.yellowgreen;a sum of money@@ from each of the spectators, though $activeSlave.slaveName is @@.red;badly hurt.@@
+	The combat is long and exhausting. $activeSlave.slaveName and $his opponent are closely matched in terms of skill, so neither takes foolish risks and a protracted, bloody fight results as both slaves take horrible but non-life-threatening cuts. Finally, $activeSlave.slaveName's superior physical condition wins out and $his opponent falls from exhaustion and blood loss. $activeSlave.slaveName stumbles over to open _hisU throat. This victory has @@.green;won you some renown@@ and @@.yellowgreen;a sum of money@@ from each of the spectators, though $activeSlave.slaveName is @@.health.dec;badly hurt.@@
 	<<run repX(500, "pit", $activeSlave)>>
 	<<run cashX(5000, "pit", $activeSlave)>>
 	<<run healthDamage($activeSlave, 50)>>
@@ -217,10 +217,10 @@ The umpire announces gravely that the fight is to the death and rings a bell.
 	<<set $activeSlave.counter.pitKills += 1>>
 	<<set $pitKillsTotal += 1>>
 <<elseif _deadliness > (_opponent-1)>>
-	The combat is long and exhausting. $activeSlave.slaveName and $his opponent are closely matched in terms of skill, so neither takes foolish risks and a protracted, bloody fight results as both slaves take horrible but non-life-threatening cuts. Finally, $activeSlave.slaveName's inferior physical condition fails $him and $he falls from exhaustion and blood loss. As $he slumps $he catches your eye with a look of apology. $His opponent stumbles over to open $his throat. $activeSlave.slaveName @@.red;has been killed.@@
+	The combat is long and exhausting. $activeSlave.slaveName and $his opponent are closely matched in terms of skill, so neither takes foolish risks and a protracted, bloody fight results as both slaves take horrible but non-life-threatening cuts. Finally, $activeSlave.slaveName's inferior physical condition fails $him and $he falls from exhaustion and blood loss. As $he slumps $he catches your eye with a look of apology. $His opponent stumbles over to open $his throat. $activeSlave.slaveName @@.health.dec;has been killed.@@
 	<<run healthDamage($activeSlave, 1000)>>
 <<else>>
-	$activeSlave.slaveName is outmatched from the start. In the first pass, $his opponent moves beyond $him, opening a long gash in $his thigh as _heU goes. Thus injured, $activeSlave.slaveName fails to block a slash that severs $his Achilles tendon on the other side. On $his knees, $he barely manages to ward off one more blow before the second one opens $his belly. $activeSlave.slaveName @@.red;has been killed.@@
+	$activeSlave.slaveName is outmatched from the start. In the first pass, $his opponent moves beyond $him, opening a long gash in $his thigh as _heU goes. Thus injured, $activeSlave.slaveName fails to block a slash that severs $his Achilles tendon on the other side. On $his knees, $he barely manages to ward off one more blow before the second one opens $his belly. $activeSlave.slaveName @@.health.dec;has been killed.@@
 	<<run healthDamage($activeSlave, 1000)>>
 <</if>>
 
diff --git a/src/uncategorized/penthouseReport.tw b/src/uncategorized/penthouseReport.tw
deleted file mode 100644
index 8119bfbbce3a0627c94ccc60c6b6057dd1b48e86..0000000000000000000000000000000000000000
--- a/src/uncategorized/penthouseReport.tw
+++ /dev/null
@@ -1,44 +0,0 @@
-:: Penthouse Report [nobr]
-
-<<set _SL = $slaves.length>>
-
-<<set _HGSuitSlaves = App.Utils.jobForAssignment(Job.HEADGIRLSUITE).employees()>>
-
-<<for $i = 0; $i < _SL; $i++>>
-	<<if assignmentVisible($slaves[$i])>>
-		<div>
-		<<includeDOM App.EndWeek.favoriteIcon($slaves[$i])>>
-		<span class='slave-name'><<= SlaveFullName($slaves[$i])>></span>
-		<<if $slaves[$i].choosesOwnAssignment == 2>>
-			<<= App.SlaveAssignment.choosesOwnJob($slaves[$i])>>
-			$He
-		<</if>>
-		<<include "Full Report">>
-		<br><br>
-
-		<<if ($slaves[$i].ID == $HeadGirlID) && (_HGSuitSlaves.length > 0)>>
-			/% We found the Head Girl, now let's find her slave %/
-			<<set _iTemp = $i>>
-			<<set _ID = _HGSuitSlaves[0].ID, $i = $slaveIndices[_ID]>>
-
-			<<if $slaves[$i].assignment != "live with your Head Girl">>
-				<br>@@.red;$slaves[$i].slaveName had been assigned to live with your Head Girl, but this week $he was assigned to $slaves[$i].assignment. $He has been released to your penthouse for reassignment.@@
-				<<= removeJob($slaves[$i], "live with your Head Girl")>>
-			<<else>>
-				<span class='slave-name'><<= SlaveFullName($slaves[$i])>></span>
-				<<if $slaves[$i].choosesOwnAssignment == 2>>
-					<<= App.SlaveAssignment.choosesOwnJob($slaves[$i])>>
-					$He
-				<</if>>
-				<<includeDOM App.SlaveAssignment.liveWithHG($slaves[$i])>>
-				<br><br>
-			<</if>>
-
-			<<set $i = _iTemp>>
-		<</if>>
-		<br style="clear: both;"></div>
-	<</if>>
-<</for>>
-
-/* count open spots in facilities after all assignments have been decided for the week */
-<<set $brothelSpots = App.Entity.facilities.brothel.freeSpace, $clubSpots = App.Entity.facilities.club.freeSpace, $dairySpots = App.Entity.facilities.dairy.freeSpace, $servantsQuartersSpots = App.Entity.facilities.servantsQuarters.freeSpace>>
diff --git a/src/uncategorized/personalAttentionSelect.tw b/src/uncategorized/personalAttentionSelect.tw
index f8a3d2dc23a2eb2af5e772b564351269e5282abb..83e8c8043c37fca9aa121d31853b1a5ceea6733b 100644
--- a/src/uncategorized/personalAttentionSelect.tw
+++ b/src/uncategorized/personalAttentionSelect.tw
@@ -198,8 +198,8 @@
 		<<run App.Utils.setLocalPronouns($activeSlave)>>
 
 		You will give <span class='slave-name'><<= SlaveFullName($activeSlave)>></span> your personal attention this week.
-
-		Your training will seek to <<span "training"+_i>><strong><<print $personalAttention[_i].trainingRegimen.replace("her", $his)>></strong><</span>>.
+		<<link "Clear" "Personal Attention Select">> <<set $personalAttention.deleteAt(_i)>> <</link>>
+		<br>Your training will seek to <<span "training"+_i>><strong><<print $personalAttention[_i].trainingRegimen.replace("her", $his)>></strong><</span>>.
 
 		<br>Change training objective:
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
@@ -207,7 +207,8 @@
 			<<link "Break $his will">><<set $personalAttention[_i].trainingRegimen = "break her will">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			| <<link "Use enhanced breaking techniques">><<set $personalAttention[_i].trainingRegimen = "harshly break her will">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<<else>>
-			<<link "Build $his devotion">><<set $personalAttention[_i].trainingRegimen = "build her devotion">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			Current devotion: <<includeDOM App.UI.personalAttentionDevotionText($activeSlave)>>
+			<<link "Build">><<set $personalAttention[_i].trainingRegimen = "build her devotion">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
 
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
@@ -219,17 +220,19 @@
 
 		<<if ($activeSlave.behavioralFlaw != "none")>>
 			<br>&nbsp;&nbsp;&nbsp;&nbsp;
-			<<link "Remove $his behavioral flaw">><<set $personalAttention[_i].trainingRegimen = "fix her behavioral flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			Current behavioral flaw: <span class='red'><<= capFirstChar($activeSlave.behavioralFlaw)>>.</span>
+			<<link "Remove">><<set $personalAttention[_i].trainingRegimen = "fix her behavioral flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			<<if ($activeSlave.devotion < -20)>>
 				| //$He must be broken before $his flaws can be softened//
 			<<else>>
-				| <<link "Soften $his behavioral flaw">><<set $personalAttention[_i].trainingRegimen = "soften her behavioral flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+				| <<link "Soften">><<set $personalAttention[_i].trainingRegimen = "soften her behavioral flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			<</if>>
 		<</if>>
 
 		<<if ($activeSlave.sexualFlaw != "none")>>
 			<br>&nbsp;&nbsp;&nbsp;&nbsp;
-			<<link "Remove $his sexual flaw">><<set $personalAttention[_i].trainingRegimen = "fix her sexual flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			Current sexual flaw: <span class='red'><<= capFirstChar($activeSlave.sexualFlaw)>>.</span>
+			<<link "Remove">><<set $personalAttention[_i].trainingRegimen = "fix her sexual flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			<<if ($activeSlave.devotion < -20)>>
 				<<if ($activeSlave.behavioralFlaw == "none")>>
 				| //$He must be broken before $his flaws can be softened//
@@ -237,12 +240,14 @@
 			<<elseif ["abusive", "anal addict", "attention whore", "breast growth", "breeder", "cum addict", "malicious", "neglectful", "self hating"].includes($activeSlave.sexualFlaw)>>
 				| //Paraphilias cannot be softened//
 			<<else>>
-				| <<link "Soften $his sexual flaw">><<set $personalAttention[_i].trainingRegimen = "soften her sexual flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+				| <<link "Soften">><<set $personalAttention[_i].trainingRegimen = "soften her sexual flaw">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			<</if>>
 		<</if>>
 
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		<<if $activeSlave.skill.anal >= 100 && $activeSlave.skill.oral >= 100 && $activeSlave.skill.whoring >= 30 && $activeSlave.skill.entertainment >= 30>>
+		<<if ($activeSlave.devotion <= 20) && ($activeSlave.trust >= -20)>>
+			//$He's too disobedient to learn sex skills//
+		<<elseif $activeSlave.skill.anal >= 100 && $activeSlave.skill.oral >= 100 && $activeSlave.skill.whoring >= 30 && $activeSlave.skill.entertainment >= 30>>
 			<<if $activeSlave.skill.vaginal >= 100 && $activeSlave.vagina > -1>>
 				//$He knows all the skills you can teach//
 			<<elseif $activeSlave.dick == 0 && $activeSlave.scrotum == 0 && $activeSlave.vagina == -1>>
@@ -251,22 +256,21 @@
 				//$He knows all the skills you can teach a gelded slave//
 			<<elseif $activeSlave.dick > 0 && $activeSlave.boobs > 300 && $activeSlave.vagina == -1>>
 				//$He knows all the skills you can teach a shemale slave//
-			<<elseif ($activeSlave.devotion <= 20) && ($activeSlave.trust >= -20)>>
-				//$He's too disobedient to learn sex skills//
 			<<else>>
 				<<link "Teach $him">><<set $personalAttention[_i].trainingRegimen = "learn skills">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 			<</if>>
-		<<elseif ($activeSlave.devotion <= 20) && ($activeSlave.trust >= -20)>>
-			//$He's too disobedient to learn sex skills//
 		<<else>>
 			<<link "Teach $him">><<set $personalAttention[_i].trainingRegimen = "learn skills">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
 
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
+		Current health: <<includeDOM App.UI.personalAttentionHealthText($activeSlave)>>
 		<<link "Care for $him">><<set $personalAttention[_i].trainingRegimen = "look after her">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		Induce a flaw:
+		//Inducing flaws is difficult and bad for slaves' obedience.//
+		<br>&nbsp;&nbsp;&nbsp;&nbsp;
+		Induce a behavioral flaw:
 		<<if $activeSlave.behavioralQuirk != "confident" && $activeSlave.behavioralFlaw != "arrogant">>
 			<<link "Arrogance">><<set $personalAttention[_i].trainingRegimen = "induce arrogance">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
@@ -294,8 +298,10 @@
 		<<if $activeSlave.behavioralQuirk != "advocate" && $activeSlave.behavioralFlaw != "liberated">>
 			| <<link "Liberation">><<set $personalAttention[_i].trainingRegimen = "induce liberation">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
+		<br>&nbsp;&nbsp;&nbsp;&nbsp;
+		Induce a sexual flaw:
 		<<if $activeSlave.sexualQuirk != "gagfuck queen" && $activeSlave.sexualFlaw != "hates oral">>
-			| <<link "Hatred of oral">><<set $personalAttention[_i].trainingRegimen = "induce hatred of oral">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<<link "Hatred of oral">><<set $personalAttention[_i].trainingRegimen = "induce hatred of oral">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
 		<<if $activeSlave.sexualQuirk != "painal queen" && $activeSlave.sexualFlaw != "hates anal">>
 			| <<link "Hatred of anal">><<set $personalAttention[_i].trainingRegimen = "induce hatred of anal">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
@@ -322,38 +328,39 @@
 			| <<link "Judgment">><<set $personalAttention[_i].trainingRegimen = "induce judgement">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
 		<</if>>
 		<<if $activeSlave.fetishStrength > 95>>
-		<<if $activeSlave.fetish == "cumslut" && $activeSlave.sexualFlaw != "cum addict">>
-			| <<link "Cum addiction">><<set $personalAttention[_i].trainingRegimen = "induce cum addiction">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "buttslut" && $activeSlave.sexualFlaw != "anal addict">>
-			| <<link "Anal addiction">><<set $personalAttention[_i].trainingRegimen = "induce anal addiction">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "humiliation" && $activeSlave.sexualFlaw != "attention whore">>
-			| <<link "Attention whoring">><<set $personalAttention[_i].trainingRegimen = "induce attention whoring">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "boobs" && $activeSlave.sexualFlaw != "breast growth">>
-			| <<link "Breast growth obsession">><<set $personalAttention[_i].trainingRegimen = "induce breast growth obsession">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "dom" && $activeSlave.sexualFlaw != "abusive">>
-			| <<link "Abusiveness">><<set $personalAttention[_i].trainingRegimen = "induce abusiveness">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "sadist" && $activeSlave.sexualFlaw != "malicious">>
-			| <<link "Maliciousness">><<set $personalAttention[_i].trainingRegimen = "induce maliciousness">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "masochist" && $activeSlave.sexualFlaw != "self hatred">>
-			| <<link "Self hatred">><<set $personalAttention[_i].trainingRegimen = "induce self hatred">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "submissive" && $activeSlave.sexualFlaw != "neglectful">>
-			| <<link "Sexual self neglect">><<set $personalAttention[_i].trainingRegimen = "induce sexual self neglect">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
-		<<if $activeSlave.fetish == "pregnancy" && $activeSlave.sexualFlaw != "breeder">>
-			| <<link "Breeding obsession">><<set $personalAttention[_i].trainingRegimen = "induce breeding obsession">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
-		<</if>>
+			<br>&nbsp;&nbsp;&nbsp;&nbsp;
+			Induce a paraphilia:
+			<<if $activeSlave.fetish == "cumslut" && $activeSlave.sexualFlaw != "cum addict">>
+				| <<link "Cum addiction">><<set $personalAttention[_i].trainingRegimen = "induce cum addiction">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "buttslut" && $activeSlave.sexualFlaw != "anal addict">>
+				| <<link "Anal addiction">><<set $personalAttention[_i].trainingRegimen = "induce anal addiction">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "humiliation" && $activeSlave.sexualFlaw != "attention whore">>
+				| <<link "Attention whoring">><<set $personalAttention[_i].trainingRegimen = "induce attention whoring">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "boobs" && $activeSlave.sexualFlaw != "breast growth">>
+				| <<link "Breast growth obsession">><<set $personalAttention[_i].trainingRegimen = "induce breast growth obsession">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "dom" && $activeSlave.sexualFlaw != "abusive">>
+				| <<link "Abusiveness">><<set $personalAttention[_i].trainingRegimen = "induce abusiveness">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "sadist" && $activeSlave.sexualFlaw != "malicious">>
+				| <<link "Maliciousness">><<set $personalAttention[_i].trainingRegimen = "induce maliciousness">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "masochist" && $activeSlave.sexualFlaw != "self hatred">>
+				| <<link "Self hatred">><<set $personalAttention[_i].trainingRegimen = "induce self hatred">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "submissive" && $activeSlave.sexualFlaw != "neglectful">>
+				| <<link "Sexual self neglect">><<set $personalAttention[_i].trainingRegimen = "induce sexual self neglect">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
+			<<if $activeSlave.fetish == "pregnancy" && $activeSlave.sexualFlaw != "breeder">>
+				| <<link "Breeding obsession">><<set $personalAttention[_i].trainingRegimen = "induce breeding obsession">><<replace `"#training"+_i`>><strong>$personalAttention[_i].trainingRegimen</strong><</replace>><</link>>
+			<</if>>
 		<<else>>
-			| //Paraphilias can only be induced from a strong fetish//
+			<br>&nbsp;&nbsp;&nbsp;&nbsp;
+			//Paraphilias can only be induced from a strong fetish//
 		<</if>>
-		<br>&nbsp;&nbsp;&nbsp;&nbsp;//Inducing flaws is difficult and bad for slaves' obedience.//
-
 		<</capture>> /* $activeSlave, _i */
 	<</for>>
 <</if>> /* CLOSES NO SLAVE SELECTED */
@@ -362,4 +369,4 @@
 <<includeDOM App.UI.SlaveList.slaveSelectionList(
 		s => assignmentVisible(s) && s.fuckdoll === 0,
 		s => App.UI.DOM.link(SlaveFullName(s), (id) => {App.UI.selectSlaveForPersonalAttention(id); }, s.ID)
-	)>>
+	)>>
\ No newline at end of file
diff --git a/src/uncategorized/randomNonindividualEvent.tw b/src/uncategorized/randomNonindividualEvent.tw
index 1a682c7d10f77d68ebcf31080eda6aaa1e753d8e..d26b4c7e3e194899ea7d3f3977efe8389a61610b 100644
--- a/src/uncategorized/randomNonindividualEvent.tw
+++ b/src/uncategorized/randomNonindividualEvent.tw
@@ -1384,7 +1384,7 @@
 	<<if $arcologies[0].FSNeoImperialist > (random(25,100) || $cheatMode+24) && $arcologies[0].FSNeoImperialistLaw2 == 1>>
 		<<set $REFSevent.push("baron demand")>>
 	<</if>>
-	<<if $arcologies[0].FSNeoImperialist > (random(25,100) || $cheatMode+24) && $arcologies[0].FSNeoImperialistLaw2 == 1  && $arcologies[0].FSNeoImperialistLaw1 == 1 && $policies.regularParties == 1>>
+	<<if $arcologies[0].FSNeoImperialist > (random(25,100) || $cheatMode+24) && $arcologies[0].FSNeoImperialistLaw2 == 1 && $arcologies[0].FSNeoImperialistLaw1 == 1 && $policies.regularParties == 1>>
 		<<set $REFSevent.push("feast")>>
 	<</if>>
 	<<if $arcologies[0].FSNeoImperialist > (random(25,100) || $cheatMode+24)>>
diff --git a/src/uncategorized/reBrothelFunction.tw b/src/uncategorized/reBrothelFunction.tw
index 0fe31d2cc2a160efac15a4f71a882db6ee22bac5..16030c05da0fdc3d5861c422670030290d06346e 100644
--- a/src/uncategorized/reBrothelFunction.tw
+++ b/src/uncategorized/reBrothelFunction.tw
@@ -56,7 +56,7 @@ The last of these requests comes in the form of a letter, stamped with the heral
 <</link>> //This will earn you <<print cashFormat(2000)>>//
 <br><<link "Welcome the boy into manhood in Free Cities fashion">>
 	<<replace "#result">>
-	Though he was but a boy yesterday, none can say that this newly made man entered his manhood with anything less than a sterling display of enthusiasm and virility, though he is somewhat lacking in general technique. Nevertheless, any man able to fuck his way through an entire brothel of sex slaves and back again is clearly capable of bearing the mantle of citizenry in $arcologies[0].name. The story of a boy entering manhood in such a spectacular manner spreads rapidly and reflects well in the court of @@.green;public opinion,@@ with many citizens recalling their own passage past the age of majority. However, a lifetime of indulgence and spoiling have rendered this new citizen unable to understand the concept of being refused — not that your slaves could refuse him, in any case. His rough treatment has left your poor slave whores @@.red;battered@@ by his brutally selfish lovemaking.
+	Though he was but a boy yesterday, none can say that this newly made man entered his manhood with anything less than a sterling display of enthusiasm and virility, though he is somewhat lacking in general technique. Nevertheless, any man able to fuck his way through an entire brothel of sex slaves and back again is clearly capable of bearing the mantle of citizenry in $arcologies[0].name. The story of a boy entering manhood in such a spectacular manner spreads rapidly and reflects well in the court of @@.green;public opinion,@@ with many citizens recalling their own passage past the age of majority. However, a lifetime of indulgence and spoiling have rendered this new citizen unable to understand the concept of being refused — not that your slaves could refuse him, in any case. His rough treatment has left your poor slave whores @@.health.dec;battered@@ by his brutally selfish lovemaking.
 	<<for $i = 0; $i < $slaves.length; $i++>>
 		<<if $slaves[$i].assignment == "work in the brothel">>
 			<<run healthDamage($slaves[$i], 5)>>
diff --git a/src/uncategorized/reBusyBrothel.tw b/src/uncategorized/reBusyBrothel.tw
index 6859fbdfdd3c31ecf16f0843339ebb2824f69161..ca08fe39153ea1a10e38de0d7b6f86a61c2a6cfb 100644
--- a/src/uncategorized/reBusyBrothel.tw
+++ b/src/uncategorized/reBusyBrothel.tw
@@ -40,7 +40,7 @@ Of course, $brothelName is the best establishment of its kind in the arcology. C
 <</link>> //This will cost <<print cashFormat(2000)>>//
 <br><<link "Eliminate prices for one morning to promote the brothel">>
 	<<replace "#result">>
-		The news that sex will be free at the brothel travels like wildfire. Security measures are necessary to control the throng that spends the entire day entering and leaving the brothel, though as the day goes on the crowds thin. By midmorning, all the holes on offer are so fucked out that only those who fetishize that sort of thing stick around. The brothel is a real seminal sewer by noon, and it smells like it. Nevertheless, free sex is a short route to @@.green;public approval,@@ though you do miss a morning's fees. The poor slave whores are @@.red;fairly battered@@ by so much wear and tear in so little time.
+		The news that sex will be free at the brothel travels like wildfire. Security measures are necessary to control the throng that spends the entire day entering and leaving the brothel, though as the day goes on the crowds thin. By midmorning, all the holes on offer are so fucked out that only those who fetishize that sort of thing stick around. The brothel is a real seminal sewer by noon, and it smells like it. Nevertheless, free sex is a short route to @@.green;public approval,@@ though you do miss a morning's fees. The poor slave whores are @@.health.dec;fairly battered@@ by so much wear and tear in so little time.
 		<<run App.Entity.facilities.brothel.employees().forEach(s => {
 			healthDamage(s, 10);
 			if (canDoVaginal(s)) {
diff --git a/src/uncategorized/reBusyDairy.tw b/src/uncategorized/reBusyDairy.tw
index 515278d877c5b90c3bac894ec5178831705f8efa..621fc9fdb0b8db655ddbfd08881607bf90121bd1 100644
--- a/src/uncategorized/reBusyDairy.tw
+++ b/src/uncategorized/reBusyDairy.tw
@@ -18,14 +18,11 @@ Even with high doses of modern drugs, human cows simply do not produce a very hi
 	<<for _slave range App.Utils.jobForAssignment(Job.DAIRY).employees()>>
 		<<set _slave.devotion += 4>>
 		<<if canDoVaginal(_slave)>>
-			<<set _slave.counter.vaginal += 1>>
-			<<set $vaginalTotal += 1>>
+			<<run actX(_slave, "vaginal")>>
 		<<elseif canDoAnal(_slave)>>
-			<<set _slave.counter.anal += 1>>
-			<<set $analTotal += 1>>
+			<<run actX(_slave, "anal")>>
 		<</if>>
-		<<set _slave.counter.oral += 1>>
-		<<set $oralTotal += 1>>
+		<<run actX(_slave, "oral")>>
 	<</for>>
 	<<run cashX(-1000, "event")>>
 	<</replace>>
@@ -39,7 +36,7 @@ Even with high doses of modern drugs, human cows simply do not produce a very hi
 		<</replace>>
 	<</link>> //This will cost <<print cashFormat(2000)>>//
 <</if>>
-<<if ($cumSlaves >= 5) && ($activeSlave.fetish != "cumslut")>>
+<<if $cumSlaves >= 5>>
 	<br><<link "Cum in, milk out">>
 		<<replace "#result">>
 		As a promotional gimmick, you announce with considerable fanfare a special, experimental brand of milk, available at the normal price for a short time only. The milk will be unique in that it will be from cows fed mostly on slaves' cum; the cockmilked slaves will in turn be given as much milk as possible to produce a 'pure slave product,' recursively. The sad realities of nutrition stop it from being much more than a marketing ploy, but it's certainly a @@.green;successful@@ attempt to spark discussion.
@@ -48,4 +45,4 @@ Even with high doses of modern drugs, human cows simply do not produce a very hi
 		<</replace>>
 	<</link>> //This will cost <<print cashFormat(1000)>>//
 <</if>>
-</span>
+</span>
\ No newline at end of file
diff --git a/src/uncategorized/reFSAcquisition.tw b/src/uncategorized/reFSAcquisition.tw
index 1f9abef4f4737f4080951247f8eb507f48ec0d08..80d81b60a50e09099b67eafb3770d8548f21fbb0 100644
--- a/src/uncategorized/reFSAcquisition.tw
+++ b/src/uncategorized/reFSAcquisition.tw
@@ -178,7 +178,7 @@
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave("XX")>>
 <<set $activeSlave.origin = "$He was sold to you as a way of disposing of an inconveniently pregnant young $woman.">>
-<<set $activeSlave.career = setup.youngCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.young.random()>>
 <<run generateSalonModifications($activeSlave)>>
 <<run setHealth($activeSlave, jsRandom(-20, 20), undefined, undefined, 0, 10)>>
 <<set $activeSlave.devotion = random(-45,-25)>>
@@ -223,7 +223,7 @@
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave()>>
 <<set $activeSlave.origin = "$He was voluntarily enslaved after $he decided that your paternalistic arcology was a better place for advancement than the old world.">>
-<<set $activeSlave.career = setup.educatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.educated.random()>>
 <<run setHealth($activeSlave, jsRandom(40, 60), undefined, undefined, 0, 0)>>
 <<set $activeSlave.devotion = random(25,45)>>
 <<set $activeSlave.trust = random(25,45)>>
@@ -578,7 +578,7 @@
 <<set $activeSlave.lips = 15>>
 <<set $activeSlave.devotion = random(25,45)>>
 <<set $activeSlave.trust = random(25,45)>>
-<<set $activeSlave.career = setup.educatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.educated.random()>>
 <<run setHealth($activeSlave, jsRandom(20, 40), undefined, undefined, 0, 0)>>
 <<set $activeSlave.intelligence = random(51,95)>>
 <<set $activeSlave.intelligenceImplant = 15>>
@@ -775,7 +775,7 @@
 <<set $activeSlave.lips = 0>>
 <<set $activeSlave.devotion = random(25,45)>>
 <<set $activeSlave.trust = random(25,45)>>
-<<set $activeSlave.career = setup.educatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.educated.random()>>
 <<run setHealth($activeSlave, jsRandom(20, 40), undefined, undefined, 0, 0)>>
 <<set $activeSlave.intelligence = random(51,95)>>
 <<set $activeSlave.intelligenceImplant = 15>>
@@ -866,7 +866,7 @@
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave()>>
 <<set $activeSlave.origin = "$He was voluntarily enslaved after $he decided that your arcology was the best place for $him to get the steroids that $he'd allowed to define $his life.">>
-<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 <<run generateSalonModifications($activeSlave)>>
 <<run setHealth($activeSlave, jsRandom(20, 40), undefined, undefined, 0, 20)>>
 <<set $activeSlave.muscles = 100>>
@@ -1023,7 +1023,7 @@
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave("XX")>>
 <<set $activeSlave.origin = "$He sold $himself to you in the hopes that $his body would help keep humanity alive.">>
-<<set $activeSlave.career = setup.youngCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.young.random()>>
 <<run generateSalonModifications($activeSlave)>>
 <<run setHealth($activeSlave, jsRandom(40, 60), undefined, undefined, 0, 0)>>
 <<set $activeSlave.devotion = random(25,35)>>
@@ -1556,7 +1556,7 @@ The call comes in from an old world university. The caller is a young $woman who
 
 You receive so many messages, as a noted titan of the new Free Cities world, that $assistant.name has to be quite draconian in culling them. _HeA lets only the most important through to you. One category of message that always gets through regardless of content, though, is requests for voluntary enslavement. As the new world takes shape, they've become less rare than they once were.
 <br><br>
-The call comes in from a private old world apartment. The caller is a $woman who speaks in a gruff and serious tone, explaining that $he was a soldier in an Old World military who served with distinction for years before being dishonorably discharged due to some incident that $he refuses to talk about when you ask. $He says that there's no room left for tired dogs in the crumbling economy of the old world for those without money, and $his only real options are to submit to menial servitude or open slavery.  $He says that $he's heard that your Imperial society values warriors and honor, and that $he'd prefer living somewhere where $his skills are worth something - even as a slave. At your request, $he sends over a dossier containing $his personal data.
+The call comes in from a private old world apartment. The caller is a $woman who speaks in a gruff and serious tone, explaining that $he was a soldier in an Old World military who served with distinction for years before being dishonorably discharged due to some incident that $he refuses to talk about when you ask. $He says that there's no room left for tired dogs in the crumbling economy of the old world for those without money, and $his only real options are to submit to menial servitude or open slavery. $He says that $he's heard that your Imperial society values warriors and honor, and that $he'd prefer living somewhere where $his skills are worth something - even as a slave. At your request, $he sends over a dossier containing $his personal data.
 
 <<case "Aztec Revivalist">>
 
diff --git a/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw b/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
index 21f03484a15aae29eaf841c716c3c17802c55ac4..545c7062d0c29fedfaec04e40fd2251496104592 100644
--- a/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
+++ b/src/uncategorized/reFSEgyptianRevivalistAcquisition.tw
@@ -25,11 +25,11 @@
 <<set $activeSlave.relationshipTarget = _secondSlave.ID>>
 
 <<if (_secondSlave.actualAge <= 22)>>
-	<<set _secondSlave.career = setup.youngCareers.random()>>
+	<<set _secondSlave.career = App.Data.Careers.General.young.random()>>
 <<elseif (_secondSlave.intelligenceImplant >= 15)>>
-	<<set _secondSlave.career = setup.educatedCareers.random()>>
+	<<set _secondSlave.career = App.Data.Careers.General.educated.random()>>
 <<else>>
-	<<set _secondSlave.career = setup.uneducatedCareers.random()>>
+	<<set _secondSlave.career = App.Data.Careers.General.uneducated.random()>>
 <</if>>
 
 /* they've been fucking, obviously, so no virginity */
@@ -70,7 +70,7 @@ This call is coming from a public kiosk, which is usually an indication that the
 <br><br>
 <span id="result">
 <<if $cash >= _contractCost>>
-	[[Enslave the pair|Bulk Slave Intro][$market = {newSlaves: _newSlaves}, $market.newSlaves.forEach((s) => cashX(forceNeg(_contractCost/$market.newSlaves.length), "slaveTransfer", s))]]
+	[[Enslave the pair|Bulk Slave Intro][$market.newSlaves = _newSlaves, $market.newSlaves.forEach((s) => cashX(forceNeg(_contractCost/$market.newSlaves.length), "slaveTransfer", s))]]
 <<else>>
 	//You lack the necessary funds to enslave them.//
 <</if>>
diff --git a/src/uncategorized/reFormerAbolitionist.tw b/src/uncategorized/reFormerAbolitionist.tw
index e242f0504b5482717bc26ff50690e77cd998b30f..873c6fb7c4d6c5d03c067765b1290bf6a2f9f42d 100644
--- a/src/uncategorized/reFormerAbolitionist.tw
+++ b/src/uncategorized/reFormerAbolitionist.tw
@@ -29,7 +29,7 @@ This is a rare opportunity. While the mob is quick to pat itself on the back for
 <span id="result">
 <br><<link "Just capitalize on $his popularity by renting out $his mouth">>
 	<<replace "#result">>
-		You fasten $activeSlave.slaveName in a kneeling position in the center of your club, secured by shackles around $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> — purely decorative, since $he's so devoted $he'd perform $his role in this if you just hinted it would please you if $he did. In front of $him, you place a sign: "Fuck the mouth that preached abolitionism, <<print cashFormat(5)>>." In a few moments, the morning crowd will begin to arrive, and you have no doubt that $activeSlave.slaveName will be very, very popular. And $he is. Even with an extra dose of curatives and a check-up every night, the strain of a week of dicks and a diet of cum @@.red;has taken a toll on $his health.@@ But even after you pay to have the area that $activeSlave.slaveName worked thoroughly cleaned, you have made @@.yellowgreen;a tidy profit.@@
+		You fasten $activeSlave.slaveName in a kneeling position in the center of your club, secured by shackles around $his <<if hasAnyArms($activeSlave)>>wrist<<if hasBothArms($activeSlave)>>s<</if>><<if hasAnyLegs($activeSlave)>> and <</if>><</if>><<if hasAnyLegs($activeSlave)>>ankle<<if hasBothLegs($activeSlave)>>s<</if>><</if>> — purely decorative, since $he's so devoted $he'd perform $his role in this if you just hinted it would please you if $he did. In front of $him, you place a sign: "Fuck the mouth that preached abolitionism, <<print cashFormat(5)>>." In a few moments, the morning crowd will begin to arrive, and you have no doubt that $activeSlave.slaveName will be very, very popular. And $he is. Even with an extra dose of curatives and a check-up every night, the strain of a week of dicks and a diet of cum @@.health.dec;has taken a toll on $his health.@@ But even after you pay to have the area that $activeSlave.slaveName worked thoroughly cleaned, you have made @@.yellowgreen;a tidy profit.@@
 		<<run healthDamage($activeSlave, 10)>>
 		<<set $activeSlave.counter.oral += random(410,440)>>
 		<<run cashX(2000, "event", $activeSlave)>>
diff --git a/src/uncategorized/reMalefactor.tw b/src/uncategorized/reMalefactor.tw
index 86f47476fa740b633cc768498790cffc5cb8ade6..e0294b2f9f011ab29081b7fcf43a23a5375981c7 100644
--- a/src/uncategorized/reMalefactor.tw
+++ b/src/uncategorized/reMalefactor.tw
@@ -70,7 +70,7 @@
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave("XX")>>
 <<set $activeSlave.origin = "You sentenced $him to enslavement as a punishment for smuggling slaves within $his body.">>
-<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 <<set $activeSlave.devotion = random(-75,-50)>>
 <<set $activeSlave.trust = random(-75,-60)>>
 <<set $activeSlave.oldDevotion = $activeSlave.devotion>>
@@ -304,7 +304,7 @@
 <<set $fixedRace = _realRace>>
 <<set $activeSlave = GenerateNewSlave()>>
 <<set $activeSlave.origin = "You sentenced $him to enslavement as a punishment for defying local racial segregation laws.">>
-<<set $activeSlave.career = setup.educatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.educated.random()>>
 <<set $activeSlave.devotion = random(-75,-50)>>
 <<set $activeSlave.trust = random(-75,-50)>>
 <<set $activeSlave.oldDevotion = $activeSlave.devotion>>
@@ -434,26 +434,26 @@ $He is easily taken into custody and $his contraband confiscated once the jeerin
 			<<replace "#result">>
 				<<switch $malefactor>>
 				<<case "addict">>
-					You declare $his holes fair game for the entire arcology, since $he must somehow repay $his debt to society. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology, since $he must somehow repay $his debt to society. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "escapee">>
-					You declare $his holes fair game for the entire arcology, so that all free citizens can own $him for a moment, if they wish. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology, so that all free citizens can own $him for a moment, if they wish. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "orphanloli">>
-					You declare $his holes fair game for the entire arcology, so that all free citizens can own $him for a moment, if they wish. $He spends a torturous day in the stocks before being hauled in for enslavement, @@.red;completely broken@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology, so that all free citizens can own $him for a moment, if they wish. $He spends a torturous day in the stocks before being hauled in for enslavement, @@.health.dec;completely broken@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 					<<set $activeSlave.fetish = "mindbroken">>
 				<<case "anchorBaby">>
-					You declare $his holes fair game for the entire arcology; since $he wants to produce future slaves, your arcology might as well have the privilege of fathering them. $He spends a torturous day in the stocks after $his children are taken to a slave orphanage before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology; since $he wants to produce future slaves, your arcology might as well have the privilege of fathering them. $He spends a torturous day in the stocks after $his children are taken to a slave orphanage before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "businesswoman">>
-					You declare $his holes fair game for the entire arcology, in payment for $his crimes. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology, in payment for $his crimes. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "whore">>
-					You declare $his holes fair game for the entire arcology, to repay $his thefts. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology, to repay $his thefts. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "rapist">>
-					You declare $his holes fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "mule">>
-					You declare $his holes fair game for the entire arcology as punishment for trying to smuggle in a load instead of taking one. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology as punishment for trying to smuggle in a load instead of taking one. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<<case "liberator">>
-					Slaveownership is the cornerstone of the society you're building in your arcology, and this $woman attempted to undermine it. $His holes are thus fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun,@@ and small slaveholders are @@.green;encouraged to do business with your arcology@@ after word of the incident gets around.
+					Slaveownership is the cornerstone of the society you're building in your arcology, and this $woman attempted to undermine it. $His holes are thus fair game for the entire arcology. $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun,@@ and small slaveholders are @@.green;encouraged to do business with your arcology@@ after word of the incident gets around.
 				<<case "passfail">>
-					You declare $his holes fair game for the entire arcology; after all, $he did want to be around lots of _fakeRace people, didn't $he? $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.red;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
+					You declare $his holes fair game for the entire arcology; after all, $he did want to be around lots of _fakeRace people, didn't $he? $He spends a torturous day in the stocks before being hauled in for enslavement, somewhat @@.health.dec;the worse for wear@@ and @@.red;acting oddly@@ due to $his ordeal, bruises all over $his body, cum leaking from $his @@.lime;loosened@@ anus<<if $activeSlave.vagina > -1>> and @@.lime;fucked-out@@ pussy<</if>>. The public @@.green;enjoys the fun.@@
 				<</switch>>
 				<<run repX(500, "event", $activeSlave)>>
 				<<set $arcologies[0].prosperity += 2>>
@@ -527,25 +527,25 @@ $He is easily taken into custody and $his contraband confiscated once the jeerin
 				<<replace "#result">>
 					<<switch $malefactor>>
 					<<case "addict">>
-						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs for a fix until $he realizes what's coming, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
+						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs for a fix until $he realizes what's coming, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
 					<<case "escapee">>
-						An example must be made. If $he escaped once with $his tendons clipped, $he must be rather talented at it. It remains to be seen if $he can manage to escape with neither arms nor legs, so you announce $his sentence to the crowd and send $his unconscious form for reduction. Of course, @@.red;$his health is affected.@@ The public @@.green;approves of this harshness,@@ believing that you have taken strong measures against escapees.
+						An example must be made. If $he escaped once with $his tendons clipped, $he must be rather talented at it. It remains to be seen if $he can manage to escape with neither arms nor legs, so you announce $his sentence to the crowd and send $his unconscious form for reduction. Of course, @@.health.dec;$his health is affected.@@ The public @@.green;approves of this harshness,@@ believing that you have taken strong measures against escapees.
 					<<case "orphanloli">>
-						An example must be made. If $he thinks $he can escape captivity and steal to survive, let's see $him try without arms and legs, so you announce $his sentence to the crowd and send $his unconscious form for reduction. Of course, @@.red;$his health is affected.@@ The public @@.green;approves of this harshness,@@ believing that you have taken strong measures against escapees.
+						An example must be made. If $he thinks $he can escape captivity and steal to survive, let's see $him try without arms and legs, so you announce $his sentence to the crowd and send $his unconscious form for reduction. Of course, @@.health.dec;$his health is affected.@@ The public @@.green;approves of this harshness,@@ believing that you have taken strong measures against escapees.
 					<<case "anchorBaby">>
-						An example must be made. Potential immigrants must understand that attempting to circumvent the process will result in severe fines and proportional punishment when they are incapable of paying them. The protesting mother is stripped, both of clothing and children, and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly attempt to smuggle things into your arcology without the ability to move.
+						An example must be made. Potential immigrants must understand that attempting to circumvent the process will result in severe fines and proportional punishment when they are incapable of paying them. The protesting mother is stripped, both of clothing and children, and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly attempt to smuggle things into your arcology without the ability to move.
 					<<case "businesswoman">>
-						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
+						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
 					<<case "whore">>
-						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly attempt to steal anything without hands.
+						An example must be made. Free people must understand that criminals who commit outrages against them will be severely punished. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly attempt to steal anything without hands.
 					<<case "rapist">>
-						An example must be made. Rape is rare in your arcology, since citizens are so generously provided with means of sexual outlet. When the price of renting a slave that's impossible to rape is as low as it is, sexual assault of a free woman is doubly unforgivable. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since even if $he cheers up and ever manages an erection again, $he's not likely to rape anyone without arms or legs.
+						An example must be made. Rape is rare in your arcology, since citizens are so generously provided with means of sexual outlet. When the price of renting a slave that's impossible to rape is as low as it is, sexual assault of a free woman is doubly unforgivable. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since even if $he cheers up and ever manages an erection again, $he's not likely to rape anyone without arms or legs.
 					<<case "mule">>
-						An example must be made. With the quantity of drugs consumed in the arcology, quality is everything and attempting to undermine that is unforgivable. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly haul contraband into your arcology without the ability to move $himself.
+						An example must be made. With the quantity of drugs consumed in the arcology, quality is everything and attempting to undermine that is unforgivable. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he can hardly haul contraband into your arcology without the ability to move $himself.
 					<<case "liberator">>
-						An example must be made. Slaveownership is the cornerstone of the society you're building in your arcology, and this $woman attempted to undermine it. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he will scarcely be able to liberate anyone without arms or legs.
+						An example must be made. Slaveownership is the cornerstone of the society you're building in your arcology, and this $woman attempted to undermine it. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness,@@ since $he will scarcely be able to liberate anyone without arms or legs.
 					<<case "passfail">>
-						An example must be made. The citizens of your arcology must be assured that the racially inferior will not try to rise above their station, no matter their deceptions. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.red;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
+						An example must be made. The citizens of your arcology must be assured that the racially inferior will not try to rise above their station, no matter their deceptions. The protesting malefactor is stripped and stuffed into your remote surgery on public video feed. $He begs and pleads until $he sees $his doom in the form of the surgical machinery, at which point $he switches to fighting vainly to escape. Of course, @@.health.dec;$his health is affected@@ and the horrible experience has left $him @@.red;acting oddly.@@ Then it's off to the penthouse for basic slave induction. The public @@.green;approves of this harshness.@@
 					<</switch>>
 					<<run repX(500, "event", $activeSlave)>>
 					<<includeDOM App.UI.newSlaveIntro($activeSlave)>>
diff --git a/src/uncategorized/reRecruit.tw b/src/uncategorized/reRecruit.tw
index 0c3d813e840f97cabf5bc309c1c12fd85028fb85..6471b3ee17887054d2b92abfdc65047e10eb6a94 100644
--- a/src/uncategorized/reRecruit.tw
+++ b/src/uncategorized/reRecruit.tw
@@ -1496,7 +1496,7 @@
 <<run setHealth($activeSlave, jsRandom(-70, -60), undefined, undefined, undefined, 0)>>
 <<set $activeSlave.intelligence = random(-50,0)>>
 <<set $activeSlave.intelligenceImplant = 0>>
-<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 <<set $activeSlave.counter.birthsTotal = 2>>
 
 <<case "spoiled daughter">>
@@ -1700,9 +1700,9 @@
 <<set $activeSlave.lactation = 1>>
 <<set $activeSlave.lactationDuration = 2>>
 <<if $activeSlave.actualAge < 13>>
-	<<set $activeSlave.career = setup.veryYoungCareers.random()>>
+	<<set $activeSlave.career = App.Data.Careers.General.veryYoung.random()>>
 <<else>>
-	<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+	<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 <</if>>
 <<set $activeSlave.vagina = 3>>
 <<set $activeSlave.anus = 0>>
@@ -1836,7 +1836,7 @@
 <<set $activeSlaveOneTimeMaxAge = 24>>
 <<set $oneTimeDisableDisability = 1>>
 <<set $activeSlave = GenerateNewSlave("XX")>>
-<<set $activeSlave.career = setup.bodyguardCareers.random()>>
+<<set $activeSlave.career = App.Data.Careers.Leader.bodyguard.random()>>
 <<set $activeSlave.origin = "$He is an unsuccessful cyborg experiment that was set free.">>
 <<set $activeSlave.devotion = random(0,20)>>
 <<set $activeSlave.trust = random(0,20)>>
diff --git a/src/uncategorized/reStandardPunishment.tw b/src/uncategorized/reStandardPunishment.tw
index 1feee7e3d8c74ae03d816e5b915d578a8b35050d..a78564babdb0afbff0d40f2072158af08421f322 100644
--- a/src/uncategorized/reStandardPunishment.tw
+++ b/src/uncategorized/reStandardPunishment.tw
@@ -191,7 +191,7 @@
 		$His smart piercing makes the next step easy. $He gets three vibrators in total:
 	<</if>>
 	the second <<if $activeSlave.vagina > 0>>comfortably fills $his pussy<<elseif $activeSlave.vagina > 0>>rests against $his virgin pussy without breaking $his virtue<<else>>is a nice little bullet vibe for $his soft perineum<</if>>, and the third <<if $activeSlave.anus > 0>>is, of course, a vibrating plug for $his asspussy<<else>>rests against $his unfucked rosebud without penetrating it<</if>>.
-	Then you walk away, setting the vibrators to bring $him to the edge of orgasm and keep $him there. The effect is almost instant, and as you go $he begins to writhe, <<if canTalk($activeSlave)>>desperately calling after you to beg for mercy<<elseif $activeSlave.voice != 0>>moaning desperately<<else>>panting mutely<</if>>. When you finally come back and let $him down, $he's unsteady on $his feet and @@.hotpink;very submissive,@@ though a bit @@.red;burned out@@ on stimulation.
+	Then you walk away, setting the vibrators to bring $him to the edge of orgasm and keep $him there. The effect is almost instant, and as you go $he begins to writhe, <<if canTalk($activeSlave)>>desperately calling after you to beg for mercy<<elseif $activeSlave.voice != 0>>moaning desperately<<else>>panting mutely<</if>>. When you finally come back and let $him down, $he's unsteady on $his feet and @@.hotpink;very submissive,@@ though a bit @@.libido.dec;burned out@@ on stimulation.
 	<<set $activeSlave.devotion += 6>>
 	<<set $activeSlave.energy -= 1>>
 	<</replace>>
@@ -216,7 +216,7 @@
 	<<else>>
 		tight hole. $His poor little anus doesn't want to let you in, but you force yourself in anyway, making the slave wriggle desperately beneath you as $he feels the invading phallus force $his anal ring to accommodate its girth.
 	<</if>>
-	You tell $him that many slaves enjoy anal, and that if $he's a good $desc, $he can too, but that it's against the rules for $him to show reluctance to be assfucked. The slave below you is too discomfited by anal pain to respond coherently, but $he gets the message. $He'll be @@.hotpink;better about submitting@@ in the future, but due to the connection between sex and punishment $he'll be @@.red;a little less eager for sex,@@ especially anal.
+	You tell $him that many slaves enjoy anal, and that if $he's a good $desc, $he can too, but that it's against the rules for $him to show reluctance to be assfucked. The slave below you is too discomfited by anal pain to respond coherently, but $he gets the message. $He'll be @@.hotpink;better about submitting@@ in the future, but due to the connection between sex and punishment $he'll be @@.libido.dec;a little less eager for sex,@@ especially anal.
 	<<set $activeSlave.counter.anal += 1>>
 	<<set $analTotal += 1>>
 	<<set $activeSlave.devotion += 4>>
@@ -236,7 +236,7 @@
 	<<else>>
 		poor tight pussy. $He's tight, and the initial penetration was uncomfortable for $him, but you make it worse by treating $his cunt like a veteran whore's. $He begins to struggle a little as you hammer $him.
 	<</if>>
-	You tell $him that many slaves enjoy getting fucked, and that if $he's a good $desc, $he can too, but that it's against the rules for $him to show reluctance to be penetrated. The slave below you is too discomfited to respond coherently, but $he gets the message. $He'll be @@.hotpink;better about submitting@@ in the future, but due to the connection between sex and punishment $he'll be @@.red;a little less eager for sex,@@ especially vaginal.
+	You tell $him that many slaves enjoy getting fucked, and that if $he's a good $desc, $he can too, but that it's against the rules for $him to show reluctance to be penetrated. The slave below you is too discomfited to respond coherently, but $he gets the message. $He'll be @@.hotpink;better about submitting@@ in the future, but due to the connection between sex and punishment $he'll be @@.libido.dec;a little less eager for sex,@@ especially vaginal.
 	<<set $activeSlave.counter.vaginal += 1>>
 	<<set $vaginalTotal += 1>>
 	<<set $activeSlave.devotion += 4>>
@@ -256,7 +256,7 @@
 	<<else>>
 		gagging throat, enjoying the sensations of $his lips and tongue writhing against you as $he desperately tries to accommodate the intruder.
 	<</if>>
-	$He begins to struggle a little, so you snake a hand down behind $his head to hold $him in place. When you're done, $he's an inelegant mess, coughing and gasping for air. <<if canTalk($activeSlave)>>Once $he's got $his breath back, $he tearfully<<else>>Even as $he pants, $he uses shaky gestures to beg forgiveness, and $he even<</if>> promises to be less chatty. $He'll be @@.hotpink;more punctual@@ in the future, but due to the connection between sex and punishment $he'll be @@.red;a little less eager for sex,@@ especially oral.
+	$He begins to struggle a little, so you snake a hand down behind $his head to hold $him in place. When you're done, $he's an inelegant mess, coughing and gasping for air. <<if canTalk($activeSlave)>>Once $he's got $his breath back, $he tearfully<<else>>Even as $he pants, $he uses shaky gestures to beg forgiveness, and $he even<</if>> promises to be less chatty. $He'll be @@.hotpink;more punctual@@ in the future, but due to the connection between sex and punishment $he'll be @@.libido.dec;a little less eager for sex,@@ especially oral.
 	<<set $activeSlave.counter.oral += 1>>
 	<<set $oralTotal += 1>>
 	<<set $activeSlave.devotion += 4>>
@@ -318,7 +318,7 @@
 		<<set $activeSlave.counter.oral += 1>>
 		<<set $oralTotal += 1>>
 	<</if>>
-	The horny slave getting oral enjoys the oral attention, especially once $activeSlave.slaveName starts to groan with discomfort. You discard $him once everyone except $him has gotten off. $He'll be @@.hotpink;more submissive@@ to sexual demands from now on, though $he'll be @@.red;slightly less interested in sex@@ for $his own pleasure.
+	The horny slave getting oral enjoys the oral attention, especially once $activeSlave.slaveName starts to groan with discomfort. You discard $him once everyone except $him has gotten off. $He'll be @@.hotpink;more submissive@@ to sexual demands from now on, though $he'll be @@.libido.dec;slightly less interested in sex@@ for $his own pleasure.
 	<<set $activeSlave.devotion += 4>>
 	<<set $activeSlave.energy -= 2>>
 	<</replace>>
diff --git a/src/uncategorized/recETS.tw b/src/uncategorized/recETS.tw
index a078ae5348a54e61a1fddcc2433a2fb1f61468bd..8775b046f523c996cfc71e22ee1a39fa9e554507 100644
--- a/src/uncategorized/recETS.tw
+++ b/src/uncategorized/recETS.tw
@@ -74,7 +74,7 @@
 
 			<<set $activeSlave = GenerateNewSlave("XX")>>
 			<<set $activeSlave.origin = "$He sold $himself into slavery to escape addiction debts.">>
-			<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+			<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 			<<set $activeSlave.devotion = random(-15,15)>>
 			<<set $activeSlave.trust = random(-15,15)>>
 			<<set $activeSlave.oldDevotion = $activeSlave.devotion>>
@@ -702,7 +702,7 @@
 	<<switch $RecETSevent>>
 		<<case "addict mother daughter">>
 			<<set $activeSlave.origin = "You tricked $his mother into selling $him into slavery to clear addiction debts.">>
-			<<set $activeSlave.career = setup.uneducatedCareers.random()>>
+			<<set $activeSlave.career = App.Data.Careers.General.uneducated.random()>>
 			<<set $activeSlave.slaveName = $activeSlave.birthName>>
 			<<set $activeSlave.ID = $activeSlave.ID + 1000>>
 			<<set $activeSlave.devotion += 10>>
diff --git a/src/uncategorized/resEndowment.tw b/src/uncategorized/resEndowment.tw
index 5618653f503cafe07811eca5d0ec36e0ca448322..4ee11846653f60e98589cd2e39651c86f5f25dba 100644
--- a/src/uncategorized/resEndowment.tw
+++ b/src/uncategorized/resEndowment.tw
@@ -45,7 +45,7 @@
 		<<default>> /* TSS */
 			provide good quality slaves at competitive prices."
 	<</switch>>
-	
+
 	Getting down to the business advantages, he adds that "donors receive considerable price advantage on future _SCH.nickname," which seems to translate into a discount of around 20% once the sales language is stripped off it. You point out that's bordering on pointlessness to you, given your ability to purchase almost anything you wish; he hesitates, but then admits that "as our foremost supporter" you could use an endowment to guide school policy to an extent.
 <</if>>
 
diff --git a/src/uncategorized/scheduledEvent.tw b/src/uncategorized/scheduledEvent.tw
index c6c20a114c04db2dc145fade46b0c10002d9457c..e38e8183f4f89876d51dd7b422cf87b325e4e268 100644
--- a/src/uncategorized/scheduledEvent.tw
+++ b/src/uncategorized/scheduledEvent.tw
@@ -76,7 +76,7 @@
 <</if>>
 
 <<if $secExpEnabled > 0 && $SecExp.settings.battle.enabled > 0>>
-	<<include "attackGenerator">>
+	<<= App.SecExp.generator.attack()>>
 <</if>>
 
 <<if $rivalOwner == -1>>
@@ -111,8 +111,7 @@
 <<elseif ($burstee != 0)>>
 	<<set $burst = 0>>
 	<<goto "SE Burst">>
-<<elseif ($slaveDeath != 0)>>
-	<<set $slaveDeath = 0>>
+<<elseif ($slaveDeath.size)>>
 	<<goto "SE Death">>
 <<elseif ($birthee != 0)>>
 	<<goto "SE Birth">>
diff --git a/src/uncategorized/seCoursing.tw b/src/uncategorized/seCoursing.tw
index 26264529f57be7735434b816da0899292a6e0e0d..2f5b71138c73727fbb1e5e527cf73e59655b1386 100644
--- a/src/uncategorized/seCoursing.tw
+++ b/src/uncategorized/seCoursing.tw
@@ -141,7 +141,7 @@ You lead your lurcher _activeLurcher.slaveName out on a leash, naked just like t
 	<<set $activeSlave.skill.anal = 0>>
 	<<set $activeSlave.skill.whoring = 0>>
 	<<set $activeSlave.weight = random(-20,100)>>
-	<<set $activeSlave.career = setup.youngCareers.random()>>
+	<<set $activeSlave.career = App.Data.Careers.General.young.random()>>
 <<elseif $origin == "housewife">>
 	<<set $activeSlaveOneTimeMinAge = 35>>
 	<<set $activeSlaveOneTimeMaxAge = 39>>
@@ -419,7 +419,7 @@ You lead your lurcher _activeLurcher.slaveName out on a leash, naked just like t
 <</if>>
 <<if $activeSlave.boobs >= 2000>>
 	_He2 has absurd tits that will destroy any chance _he2 has of getting away.
-	<<set $hareSpeed -= 1>>
+	<<set $hareSpeed -= 2>>
 <<elseif $activeSlave.boobs >= 800>>
 	_He2 has huge boobs that will make it painful for _him2 to run hard.
 	<<set $hareSpeed -= 1>>
diff --git a/src/uncategorized/seNicaeaAnnouncement.tw b/src/uncategorized/seNicaeaAnnouncement.tw
index 157f943d79e7e79f77c31971dd6505195dfec7d9..fe5d66491b20ab1b800fb329c0ccd3960e71edfa 100644
--- a/src/uncategorized/seNicaeaAnnouncement.tw
+++ b/src/uncategorized/seNicaeaAnnouncement.tw
@@ -3,7 +3,7 @@
 <<if $plot == 1>><<set $nextLink = "Nonrandom Event">><<else>><<set $nextLink = "Random Nonindividual Event">><</if>>
 <<set $nextButton = "Continue">>
 <<set $nicaea.announced = 1, $nicaea.preparation = 1, $nicaea.involvement = 0, $nicaea.power = 1>>
-<<set $nicaea.name = "Council of " + setup.ArcologyNamesChattelReligionist.random()>>
+<<set $nicaea.name = "Council of " + App.Data.ArcologyNames.ChattelReligionist.random()>>
 
 <<if $PC.title == 1>><<set _advocate = "male">><<elseif $PC.dick != 0>><<set _advocate = "futa">><<else>><<set _advocate = "female">><</if>>
 
diff --git a/src/uncategorized/sellSlave.tw b/src/uncategorized/sellSlave.tw
index 7815c4bff9489255ed98cc53fbf9519a6d348b86..b27ed6ddbe2c6389988d89206a5b7f75a1f16b0b 100644
--- a/src/uncategorized/sellSlave.tw
+++ b/src/uncategorized/sellSlave.tw
@@ -257,52 +257,52 @@ A reputable slave appraiser arrives promptly to inspect $him and certify $his qu
 <</if>>
 
 <<if getSlave($AS).career != 0>>
-	<<if setup.bodyguardCareers.includes(getSlave($AS).career)>>
+	<<if App.Data.Careers.Leader.bodyguard.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Bodyguard; that's valuable.
-	<<elseif setup.wardenessCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.wardeness.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Wardeness; that's valuable.
-	<<elseif setup.attendantCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.attendant.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Attendant; that's valuable.
-	<<elseif setup.matronCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.matron.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Matron; that's valuable.
-	<<elseif setup.schoolteacherCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.schoolteacher.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Schoolteacher; that's valuable.
-	<<elseif setup.stewardessCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.stewardess.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Stewardess; that's valuable.
-	<<elseif setup.milkmaidCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.milkmaid.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Milkmaid; that's valuable.
-	<<elseif setup.farmerCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.farmer.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Farmer; that's valuable.
-	<<elseif setup.madamCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.madam.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Madam; that's valuable.
-	<<elseif setup.DJCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.DJ.includes(getSlave($AS).career)>>
 		$His background would help make $him a good DJ; that's valuable.
-	<<elseif setup.HGCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.HG.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Head Girl; that's valuable.
-	<<elseif setup.recruiterCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.recruiter.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Recruiter; that's valuable.
-	<<elseif setup.matronCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.matron.includes(getSlave($AS).career)>>
 		$His background would help make $him a good Matron; that's valuable.
-	<<elseif setup.entertainmentCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.General.entertainment.includes(getSlave($AS).career)>>
 		$His background should help $his flirting a little.
-	<<elseif setup.whoreCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.General.whore.includes(getSlave($AS).career)>>
 		$His background should help $his fucking a little.
-	<<elseif setup.gratefulCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.General.grateful.includes(getSlave($AS).career)>>
 		$His background should make $him a bit more trusting.
-	<<elseif setup.menialCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.General.menial.includes(getSlave($AS).career)>>
 		$His background should make $him a bit more tractable.
-	<<elseif setup.servantCareers.includes(getSlave($AS).career)>>
+	<<elseif App.Data.Careers.Leader.servant.includes(getSlave($AS).career)>>
 		$His background should make $him a good servant.
 	<</if>>
 <</if>>
 <<if ($week-getSlave($AS).weekAcquired >= 20) && (getSlave($AS).skill.entertainment >= 100)>>
-	<<if setup.entertainmentCareers.includes(getSlave($AS).career)>>
+	<<if App.Data.Careers.General.entertainment.includes(getSlave($AS).career)>>
 	<<else>>
 		$He's gotten enough experience as a slave entertainer that $he has the added value of a $girl with a history in entertainment from before $he was a slave.
 	<</if>>
 <</if>>
 <<if (getSlave($AS).counter.oral + getSlave($AS).counter.anal + getSlave($AS).counter.vaginal + getSlave($AS).counter.mammary + getSlave($AS).counter.penetrative > 1000)>>
-	<<if setup.whoreCareers.includes(getSlave($AS).career)>>
+	<<if App.Data.Careers.General.whore.includes(getSlave($AS).career)>>
 	<<else>>
 		I see $he's sexually very experienced; that counts as a stand-in for the usual bonus value from having been a sex worker before enslavement.
 	<</if>>
diff --git a/src/uncategorized/surgeryDegradation.tw b/src/uncategorized/surgeryDegradation.tw
index 60868fbf1ca0592e2a693a12819181c61787cab3..59f024937921d11149d4655d1d8595328a0f584b 100644
--- a/src/uncategorized/surgeryDegradation.tw
+++ b/src/uncategorized/surgeryDegradation.tw
@@ -30,11 +30,11 @@
 <<set _slaveDevotion = getSlave($AS).devotion>>
 
 <<if (getSlave($AS).health.condition < random(-100,-80)) && !["body hair removal", "braces", "chem castrate", "eyebrow removal", "hair removal", "insemination", "removeBraces"].includes($surgeryType)>>
-	<<= getSlave($AS).slaveName>> @@.red;has died from complications of surgery.@@
+	<<= getSlave($AS).slaveName>> @@.health.dec;has died from complications of surgery.@@
 	<<= removeSlave(getSlave($AS))>>
 	<<set $nextLink = "Main">>
 <<elseif $surgeryType == "breastShapePreservation" && ((getSlave($AS).health.condition-(getSlave($AS).boobs/1000)) < random(-100,-80))>>
-	<<= getSlave($AS).slaveName>>'s mesh implantation @@.red;has gone wrong, resulting in a mastectomy!@@
+	<<= getSlave($AS).slaveName>>'s mesh implantation @@.health.dec;has gone wrong, resulting in a mastectomy!@@
 	<<if (getSlave($AS).boobs >= 7000)>>
 		<<if (getSlave($AS).areolae > 2)>>
 			The emergency mastectomy also @@.orange;slightly reduces $his massive areolae.@@
@@ -48,24 +48,24 @@
 			<<set getSlave($AS).nipples = "huge">>
 		<</if>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			As with all invasive surgery @@.red;$his health has been affected.@@
+			As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<elseif (getSlave($AS).sexualFlaw == "breast growth")>>
-			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. The immense bust $he managed to grow has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his enormous breasts, and now that they are gone, $he has nothing to live for. @@.red;Your apparent theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. The immense bust $he managed to grow has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his enormous breasts, and now that they are gone, $he has nothing to live for. @@.red;Your apparent theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 			<<set getSlave($AS).trust = -50, getSlave($AS).devotion = -50>>
 			<<set getSlave($AS).fetish = "mindbroken">>
 			<<set getSlave($AS).fetishStrength = 10>>
 			<<set getSlave($AS).sexualQuirk = "none", getSlave($AS).behavioralQuirk = "none", getSlave($AS).behavioralFlaw = "none", getSlave($AS).sexualFlaw = "none">>
 		<<elseif (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).devotion <= 20)>>
-			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent, immense bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his enormous breasts, and they were apparently swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves, even though it was your intent to preserve them.
+			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent, immense bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his enormous breasts, and they were apparently swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves, even though it was your intent to preserve them.
 			<<set getSlave($AS).trust -= 40, getSlave($AS).devotion -= 20>>
 		<<elseif (getSlave($AS).devotion > 50)>>
-			<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form, completely unaware this wasn't your intent. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs@@ and @@.mediumaquamarine;thankful@@ that you'd consider $his health, well being and ability to fuck. As with all invasive surgery @@.red;$his health has been affected.@@
+			<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form, completely unaware this wasn't your intent. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs@@ and @@.mediumaquamarine;thankful@@ that you'd consider $his health, well being and ability to fuck. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 			<<set getSlave($AS).devotion += 4, getSlave($AS).trust += 4>>
 		<<elseif (getSlave($AS).devotion >= -20)>>
-			<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts with appreciation<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but both your and $him expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed the literal weight off $his chest.
+			<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts with appreciation<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but both your and $him expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed the literal weight off $his chest.
 			<<set getSlave($AS).trust += 5>>
 		<<else>>
-			<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with relief<<else>>The sudden lack of weight on $his chest fills $him with relief<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but $he breathes easier without the immense weight hanging from $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the immense weight hanging from $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every supposed whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $his chest@@ and @@.mediumaquamarine;is thankful for your consideration of $his health,@@ though it may be short lived. As with all invasive surgery @@.red;$his health has been affected.@@
+			<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with relief<<else>>The sudden lack of weight on $his chest fills $him with relief<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but $he breathes easier without the immense weight hanging from $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the immense weight hanging from $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every supposed whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $his chest@@ and @@.mediumaquamarine;is thankful for your consideration of $his health,@@ though it may be short lived. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 			<<set getSlave($AS).trust += 10, getSlave($AS).devotion += 5>>
 		<</if>>
 	<<else>>
@@ -81,24 +81,24 @@
 			<<set getSlave($AS).nipples = "huge">>
 		<</if>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			As with all invasive surgery @@.red;$his health has been affected.@@
+			As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<elseif (getSlave($AS).sexualFlaw == "breast growth")>>
-			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once glorious bust has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his huge breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once glorious bust has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his huge breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 			<<set getSlave($AS).trust = -50, getSlave($AS).devotion = -50>>
 			<<set getSlave($AS).fetish = "mindbroken">>
 			<<set getSlave($AS).fetishStrength = 10>>
 			<<set getSlave($AS).sexualQuirk = "none", getSlave($AS).behavioralQuirk = "none", getSlave($AS).behavioralFlaw = "none", getSlave($AS).sexualFlaw = "none">>
 		<<elseif (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).devotion <= 20)>>
-			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his huge breasts, and they were apparently swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves, even though it was your intent to preserve them.
+			<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his huge breasts, and they were apparently swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves, even though it was your intent to preserve them.
 			<<set getSlave($AS).trust -= 40, getSlave($AS).devotion -= 20>>
 		<<elseif (getSlave($AS).devotion > 50)>>
-			<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form, completely unaware this wasn't your intent. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+			<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form, completely unaware this wasn't your intent. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion >= -20)>>
-			<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but both your and $him expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but both your and $him expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 5>>
 		<<else>>
-			<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this apparent surgical theft as a cruel imposition.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this apparent surgical theft as a cruel imposition.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<</if>>
 	<</if>>
@@ -222,7 +222,7 @@ As the remote surgery's long recovery cycle completes,
 	<<set getSlave($AS).sexualFlaw = "none", getSlave($AS).behavioralFlaw = "none">>
 
 <<case "unblind">>
-	The eye surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he processes $his new vision. Seeing the world as it is is a gift that those who do not need it cannot properly understand.
+	The eye surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he processes $his new vision. Seeing the world as it is is a gift that those who do not need it cannot properly understand.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 25, getSlave($AS).trust += 25>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -236,7 +236,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "blind">>
-	The laser eye surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this darkness is $his new reality.
+	The laser eye surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this darkness is $his new reality.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to weep, not understanding why it's necessary that $he be unable to see. After a short cry that trails off into a few sniffles, $he carries on.
@@ -250,7 +250,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "remove eyes">>
-	Surgery doesn't take long, but since it was invasive there are @@.red;moderate health consequences.@@ As anesthesia wears off $he tries to open $his eyes and finds $he is unable to.
+	Surgery doesn't take long, but since it was invasive there are @@.health.dec;moderate health consequences.@@ As anesthesia wears off $he tries to open $his eyes and finds $he is unable to.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if getBestVision(getSlave($AS)) > 0>>
 			<<if (getSlave($AS).devotion > 50)>>
@@ -275,7 +275,7 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "ocular implant">>
 	<<if getBestVision(getSlave($AS)) > 0>>
-		The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $his new eyes deliver nearly overwhelming amount of visual information. Seeing the world as it is is a gift that those who do not need it cannot properly understand.
+		The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $his new eyes deliver nearly overwhelming amount of visual information. Seeing the world as it is is a gift that those who do not need it cannot properly understand.
 		<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 			<<set getSlave($AS).devotion += 25, getSlave($AS).trust += 25>>
 			<<if (getSlave($AS).devotion > 50)>>
@@ -288,7 +288,7 @@ As the remote surgery's long recovery cycle completes,
 			<</if>>
 		<</if>>
 	<<else>>
-		The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. When $he is allowed to open $his eyes the amount of visual information makes $him reel.
+		The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. When $he is allowed to open $his eyes the amount of visual information makes $him reel.
 		<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 			<<if (getSlave($AS).devotion > 50)>>
 				$He is @@.hotpink;grateful@@ for $his improved vision, and knowing how much you invested in $him makes $him @@.mediumaquamarine;trust you more@@ as well.
@@ -304,10 +304,10 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "newEyes">>
-	The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $he notices nothing has changed; though the next time $he looks in the mirror, $he'll see a pair of familiar <<= App.Desc.eyesColor(getSlave($AS))>> peering back at $him.
+	The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as $he is allowed to open $his eyes and look around, $he notices nothing has changed; though the next time $he looks in the mirror, $he'll see a pair of familiar <<= App.Desc.eyesColor(getSlave($AS))>> peering back at $him.
 
 <<case "undeafen">>
-	The inner ear surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $his head tilts towards any source of sound with manic speed as $he processes $his new hearing. Hearing the world as it is is a gift that those who do not need it cannot properly understand.
+	The inner ear surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $his head tilts towards any source of sound with manic speed as $he processes $his new hearing. Hearing the world as it is is a gift that those who do not need it cannot properly understand.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 25, getSlave($AS).trust += 25>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -321,7 +321,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "deafen">>
-	The inner ear surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as the bandages around $his ears are removed, $he begins to twist and turn $his head frantically, not immediately understanding that this silence is $his new reality.
+	The inner ear surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as the bandages around $his ears are removed, $he begins to twist and turn $his head frantically, not immediately understanding that this silence is $his new reality.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to weep, not understanding why it's necessary that $he be unable to hear. After a short cry that trails off into a few sniffles, $he carries on.
@@ -336,7 +336,7 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "cochlear implant">>
 	<<if getSlave($AS).hears <= -2>>
-		The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $his head tilts towards any source of sound with manic speed as $he processes a nearly overwhelming amount of auditory information. Hearing the world as it is is a gift that those who do not need it cannot properly understand.
+		The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $his head tilts towards any source of sound with manic speed as $he processes a nearly overwhelming amount of auditory information. Hearing the world as it is is a gift that those who do not need it cannot properly understand.
 		<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 			<<set getSlave($AS).devotion += 25, getSlave($AS).trust += 25>>
 			<<if (getSlave($AS).devotion > 50)>>
@@ -349,7 +349,7 @@ As the remote surgery's long recovery cycle completes,
 			<</if>>
 		<</if>>
 	<<else>>
-		The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. When the bandages around $his ears are removed the amount of auditory information makes $him reel.
+		The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. When the bandages around $his ears are removed the amount of auditory information makes $him reel.
 		<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 			<<if (getSlave($AS).devotion > 50)>>
 				$He is @@.hotpink;grateful,@@ for $his improved hearing, and knowing how much you invested in $him makes $him @@.mediumaquamarine;trust you more@@ as well.
@@ -367,54 +367,54 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "earMinor">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his altered ears. The modification surgery is brief, with @@.red;nothing more than minor health effects.@@
+		$He shows little reaction to $his altered ears. The modification surgery is brief, with @@.health.dec;nothing more than minor health effects.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new ears, <<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified, <</if>><<if hasAnyArms(getSlave($AS))>>they're still a bit sore, but $he reaches up to feel them gently,<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $He seems to think $his new ears are @@.hotpink;cute.@@ The modification surgery is brief, with @@.red;nothing more than minor health effects.@@
+		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new ears, <<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified, <</if>><<if hasAnyArms(getSlave($AS))>>they're still a bit sore, but $he reaches up to feel them gently,<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $He seems to think $his new ears are @@.hotpink;cute.@@ The modification surgery is brief, with @@.health.dec;nothing more than minor health effects.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new ears skeptically.<<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>>$He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his ears reshaped. The modification surgery is brief, with @@.red;nothing more than minor health effects.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new ears skeptically.<<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>>$He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his ears reshaped. The modification surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new ears with disdain.<<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently, as if to confirm it's not some trick.<</if>> For now, @@.mediumorchid;$he seems to view $his altered ears as a cruel imposition.@@ The modification surgery is brief, with @@.red;nothing more than minor health effects.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new ears with disdain.<<else>>$He can't see but it's clear from the dull ache in $his ears that they have been modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently, as if to confirm it's not some trick.<</if>> For now, @@.mediumorchid;$he seems to view $his altered ears as a cruel imposition.@@ The modification surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "earMajor">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new ears and they twitch in response, surprising $him, <<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified, <</if>><<if hasAnyArms(getSlave($AS))>>they're still a bit sore, but $he reaches up to feel them gently<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will, for now $he seems to think $his new ears are @@.hotpink;cute.@@ Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new ears and they twitch in response, surprising $him, <<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified, <</if>><<if hasAnyArms(getSlave($AS))>>they're still a bit sore, but $he reaches up to feel them gently<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will, for now $he seems to think $his new ears are @@.hotpink;cute.@@ Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new ears disapprovingly and they twitch slightly in response.<<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>>$His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will. $He's come to terms with the fact that $he's a slave, but $he is still somewhat angry that you would go this far. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new ears disapprovingly and they twitch slightly in response.<<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>>$His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will. $He's come to terms with the fact that $he's a slave, but $he is still somewhat angry that you would go this far. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new ears with disgust and they twitch slightly in response.<<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>> $His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will. For now, @@.mediumorchid;$he seems to view $his altered ears as a cruel imposition.@@ Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new ears with disgust and they twitch slightly in response.<<else>>$He can't see but it's clear from the dull ache that $his ears have been extensively modified.<</if>><<if hasAnyArms(getSlave($AS))>>$He's still a bit sore, but $he reaches up to feel them gently.<</if>> $His new ears seem to respond to $his emotional state, with time and mild electro-stimulation $he will learn to control $his new ear muscles so $he can move them at will. For now, @@.mediumorchid;$he seems to view $his altered ears as a cruel imposition.@@ Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "earRestore">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>/*Will expand in future*/
-		$He is delighted to have $his ears back. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		$He is delighted to have $his ears back. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 	<</if>>
 
 <<case "earGone">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		$He shows little reaction to $his altered ears. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He is horrified that you would disfigure $him but ultimately accepts it since $he is devoted to you. Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@
+		$He is horrified that you would disfigure $him but ultimately accepts it since $he is devoted to you. Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He is horrified that you would disfigure $him, $he's come to terms with the fact that $he's a slave, but $he is still @@.mediumorchid;angry that you would go this far.@@ Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He is horrified that you would disfigure $him, $he's come to terms with the fact that $he's a slave, but $he is still @@.mediumorchid;angry that you would go this far.@@ Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<<else>>
-		$He is horrified that you would disfigure $him and begins to weep openly as soon as $he discovers the loss.@@.mediumorchid;$He seems to consider the loss as a cruel theft.@@ Since the surgery was fairly invasive, @@.red;$his health has been greatly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		$He is horrified that you would disfigure $him and begins to weep openly as soon as $he discovers the loss.@@.mediumorchid;$He seems to consider the loss as a cruel theft.@@ Since the surgery was fairly invasive, @@.health.dec;$his health has been greatly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 
 <<case "newEars">>
-	The implant surgery is @@.red;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $he
+	The implant surgery is @@.health.dec;invasive@@ and $he spends some time in the autosurgery recovering. As soon as the bandages around $his ears are removed, $he
 	<<if getSlave($AS).fetish == "mindbroken">>
 		returns to $his normal activities, none the wiser.
 	<<else>>
@@ -422,7 +422,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "desmell">>
-	The nasal surgery is brief, with @@.red;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any impairment to $his sense of smell, and so must wait to discover the change when $he's released much later on.
+	The nasal surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any impairment to $his sense of smell, and so must wait to discover the change when $he's released much later on.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to cry, not understanding why it's necessary that $he be unable to smell. After a few sniffles, $he carries on.
@@ -436,7 +436,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "resmell">>
-	The nasal surgery is brief, with @@.red;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any improvement to $his sense of smell, and so must wait to discover the change when $he's released much later on.
+	The nasal surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any improvement to $his sense of smell, and so must wait to discover the change when $he's released much later on.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 15, getSlave($AS).trust += 15>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -449,7 +449,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "detaste">>
-	The oral surgery is brief, with @@.red;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any impairment to $his sense of taste, and so must wait to discover the change when $he's released much later on.
+	The oral surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any impairment to $his sense of taste, and so must wait to discover the change when $he's released much later on.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to cry, not understanding why it's necessary that $he be unable to taste. After a few sniffles, $he carries on.
@@ -463,7 +463,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "retaste">>
-	The oral surgery is brief, with @@.red;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any improvement to $his sense of taste, and so must wait to discover the change when $he's released much later on.
+	The oral surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ In the sterile environment of the autosurgery, $he's unable to notice any improvement to $his sense of taste, and so must wait to discover the change when $he's released much later on.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 15, getSlave($AS).trust += 15>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -477,30 +477,30 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "horn">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his new head ornamentation. As with all invasive surgery @@.red;$his health has been affected.@@
+		$He shows little reaction to $his new head ornamentation. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>>, <<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to feel their shape,<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $He seems to think $his new horns are @@.hotpink;cute.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He looks in the mirror and turns $his head side to side admiring $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>>, <<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to feel their shape,<</if>> $he turns to you with a smile, tilting $his head at various angles to show them off. $He seems to think $his new horns are @@.hotpink;cute.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>> disapprovingly<<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to tug on them gently,<</if>>$He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having horns attached to $his head. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>> disapprovingly<<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to tug on them gently,<</if>>$He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having horns attached to $his head. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>> with disgust.<<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, and probably not for the better, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to tug on them, as if to confirm it's not some trick.<</if>> For now, @@.mediumorchid;$he seems to view $his head ornamentation as a cruel imposition.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new <<= getSlave($AS).hornColor>> <<= getSlave($AS).horn>> with disgust.<<else>>$He can't see but it's clear from the dull ache that something has been done to $his head, and probably not for the better, <</if>><<if hasAnyArms(getSlave($AS))>>the base is still a bit sore, but $he reaches up to tug on them, as if to confirm it's not some trick.<</if>> For now, @@.mediumorchid;$he seems to view $his head ornamentation as a cruel imposition.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "hornGone">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to the removal of $his head ornamentation. As with all invasive surgery @@.red;$his health has been affected.@@
+		$He shows little reaction to the removal of $his head ornamentation. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He is a little sad $his head ornaments are gone but since $he is attentive to your will it doesn't have a great impact on $his mental state. As with all invasive surgery @@.red;$his health has been affected.@@
+		$He is a little sad $his head ornaments are gone but since $he is attentive to your will it doesn't have a great impact on $his mental state. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<else>>
-		$He is glad to be rid of the horns but any happiness is tempered by $his general dissatisfaction of being treated as your surgical plaything. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He is glad to be rid of the horns but any happiness is tempered by $his general dissatisfaction of being treated as your surgical plaything. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<</if>>
 
 <<case "tailInterface">>
-	Implanting a tail socket and interfacing it with $his spinal column is delicate and invasive procedure @@.red;$his health has been greatly affected.@@
+	Implanting a tail socket and interfacing it with $his spinal column is delicate and invasive procedure @@.health.dec;$his health has been greatly affected.@@
 
 <<case "boobs">>
 	<<if (getSlave($AS).areolae < 2) && (random(1,100) > 70)>>
@@ -523,36 +523,36 @@ As the remote surgery's long recovery cycle completes,
 		<<set getSlave($AS).boobShape = "normal">>
 	<</if>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to the new weight on $his chest. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He shows little reaction to the new weight on $his chest. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new assets.<<else>>$He's barely out of the surgery before $he's rubbing $his new assets against anything $he can reach.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new assets.<<else>>$He's barely out of the surgery before $he's rubbing $his new assets against anything $he can reach.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his new breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too much, but $he wiggles $himself a little to make them bounce for you.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his new breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too much, but $he wiggles $himself a little to make them bounce for you.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new breasts<<else>>$He shifts them<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new breasts<<else>>$He shifts them<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new breasts<<else>>The new weight on $his chest fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view these fake breasts as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new breasts<<else>>The new weight on $his chest fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view these fake breasts as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "preg">>
 	$He leaves the surgery with a certain warmth in $his lower abdomen, $he knows that $he has been impregnated.
 	<<if getSlave($AS).fetish == "mindbroken">>
-		As with all surgery @@.red;$his health has been slightly affected.@@
+		As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He is @@.hotpink; filled with joy@@ about being swollen with life and gleefully rubs $his soon to be huge belly. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He is @@.hotpink; filled with joy@@ about being swollen with life and gleefully rubs $his soon to be huge belly. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He's @@.hotpink;grateful@@ that you think $his offspring are valuable, and a little nervous about how $he'll perform as a breeder. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He's @@.hotpink;grateful@@ that you think $his offspring are valuable, and a little nervous about how $he'll perform as a breeder. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and how big $he will get.
+		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and how big $he will get.
 		<<set getSlave($AS).trust -= 10>>
 	<<else>>
-		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you have forced $him to be a broodmother. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his rapidly filling womb.
+		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you have forced $him to be a broodmother. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his rapidly filling womb.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 15>>
 	<</if>>
 	<<if $PC.dick != 0>>
@@ -571,7 +571,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "preg1hack">>
-	The hacking process is brief, being little more than inserting the actuator into $his vagina, and leaves $him with @@.red;nothing more than minor health effects@@ from the altered implant functions. $He leaves the surgery without any specific feeling, but $he knows that something has been done to $his implant.
+	The hacking process is brief, being little more than inserting the actuator into $his vagina, and leaves $him with @@.health.dec;nothing more than minor health effects@@ from the altered implant functions. $He leaves the surgery without any specific feeling, but $he knows that something has been done to $his implant.
 	<<set getSlave($AS).broodmotherFetuses = either(2, 2, 2, 2, 3, 3, 4)>> /*My testing shows that 2 or 3 relatively safe for generic adult slave with effective curatives or clinic, 4 - high risk of bursting. So there is a catch with it.*/
 	<<if getSlave($AS).fetish == "mindbroken">>
 		/*nothing*/
@@ -593,18 +593,18 @@ As the remote surgery's long recovery cycle completes,
 <<case "pregRemove">>
 	$He leaves the surgery with a certain soreness and minor pain in $his lower abdomen, $he knows that $his days as broodmother are finished.
 	<<if getSlave($AS).fetish == "mindbroken">>
-		As with all surgery @@.red;$his health has been slightly affected.@@
+		As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He is @@.red; filled with despair@@ about missing being swollen with life and rubs $his flat belly with sorrow. Only one fact slightly soothes $him and allows $him to remain sane — at least $he will not become infertile and still can get pregnant naturally. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He is @@.red; filled with despair@@ about missing being swollen with life and rubs $his flat belly with sorrow. Only one fact slightly soothes $him and allows $him to remain sane — at least $he will not become infertile and still can get pregnant naturally. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 30>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He's @@.hotpink;grateful@@ that you allowed $his body to be free of constant pregnancy stress, and a little nervous about if you will appreciate $him enough without such dedication. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He's @@.hotpink;grateful@@ that you allowed $his body to be free of constant pregnancy stress, and a little nervous about if you will appreciate $him enough without such dedication. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10>>
 	<<else>>
-		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you can change $his body so radically just at your will. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his now empty womb.
+		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you can change $his body so radically just at your will. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his now empty womb.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 15>>
 	<</if>>
 
@@ -654,87 +654,87 @@ As the remote surgery's long recovery cycle completes,
 <<case "freshOvaries">>
 	<<if getSlave($AS).ovaryAge >= 45 && getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if ((getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)) || getSlave($AS).origin == "$He sold $himself to you in the hope of someday bearing children.">>
-			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant once more. $He is @@.hotpink;filled with joy@@ whenever $he thinks about the fact that $he'll have the chance to feel a life growing within $him again. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant once more. $He is @@.hotpink;filled with joy@@ whenever $he thinks about the fact that $he'll have the chance to feel a life growing within $him again. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 5>>
 		<<elseif (getSlave($AS).devotion > 50)>>
-			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has the chance to get pregnant again. $He's @@.hotpink;grateful@@ that you think $him worthy of extending $his fertility, and even a little nervous about how $he'll perform as a mother that has tasted menopause. As with all surgery @@.red;$his health has been slightly affected.@@
+			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has the chance to get pregnant again. $He's @@.hotpink;grateful@@ that you think $him worthy of extending $his fertility, and even a little nervous about how $he'll perform as a mother that has tasted menopause. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion >= -20)>>
-			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated once more. $He understands the realities of $his life as a slave, but didn't expect to have $his waning fertility renewed. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated once more. $He understands the realities of $his life as a slave, but didn't expect to have $his waning fertility renewed. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 5>>
 		<<else>>
-			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to believe you have forced fertility upon $him again. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he'll be forced to carry children. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			$He leaves the surgery with nothing but a nonspecific ache, yet a familiar warmth, in $his lower abdomen, but $he knows enough about surgery and sex slaves to believe you have forced fertility upon $him again. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he'll be forced to carry children. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 		<</if>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen; it won't be clear to $him that menopause is not a concern for now. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen; it won't be clear to $him that menopause is not a concern for now. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<</if>>
 
 <<case "ovaImplant added" "ovaImplant removed">>
 	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower abdomen.
 	<<if (getSlave($AS).devotion > 50)>>
-		$He's @@.hotpink;grateful@@ that you think $him worthy of surgically modifying. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He's @@.hotpink;grateful@@ that you think $him worthy of surgically modifying. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He understands the realities of $his life as a slave, but didn't expect to undergo a mysterious procedure like this. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He understands the realities of $his life as a slave, but didn't expect to undergo a mysterious procedure like this. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at what you could have possibly done to $him. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at what you could have possibly done to $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "mpreg">>
 	$He leaves the surgery with a certain fullness in $his lower abdomen, $he knows that, despite lacking female reproductive organs, $he can now become pregnant.
 	<<if getSlave($AS).fetish == "mindbroken">>
-		As with all surgery @@.red;$his health has been slightly affected.@@
+		As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He is @@.hotpink; filled with joy@@ about the possibility of becoming pregnant and gleefully rubs $his softer belly. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He is @@.hotpink; filled with joy@@ about the possibility of becoming pregnant and gleefully rubs $his softer belly. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He's @@.hotpink;grateful@@ that you think $his offspring are valuable enough to give $him this gift, and a little nervous about how $he'll perform as a mother. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He's @@.hotpink;grateful@@ that you think $his offspring are valuable enough to give $him this gift, and a little nervous about how $he'll perform as a mother. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his inevitable pregnancy.
+		$He understands the realities of $his life as a slave, so it isn't much of a shock. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his inevitable pregnancy.
 		<<set getSlave($AS).trust -= 10>>
 	<<else>>
-		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you have forced $him to become fertile in such an unnatural way. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his inevitable pregnancy.
+		$He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ that you have forced $him to become fertile in such an unnatural way. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and $his inevitable pregnancy.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 15>>
 	<</if>>
 
 <<case "mpreg removed">>
-	$He notices quickly that $his stomach is slightly flatter than before. $He ponders this change for a moment, unsure of what to think of this occurrence. As with all surgery @@.red;$his health has been slightly affected.@@
+	$He notices quickly that $his stomach is slightly flatter than before. $He ponders this change for a moment, unsure of what to think of this occurrence. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "nippleCunts">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows no reaction to $his altered nipples. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He shows no reaction to $his altered nipples. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's experimentally probing $his new nipples despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new nipples against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's experimentally probing $his new nipples despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new nipples against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He runs a finger into $his new nipples experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel the new nipples capping $his tits turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his nipples.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He runs a finger into $his new nipples experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel the new nipples capping $his tits turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his nipples.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new nipples skeptically<<else>>The cool air flowing over $his new nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his nipples reshaped; $he may reconsider once $he realizes just what they are now capable of. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new nipples skeptically<<else>>The cool air flowing over $his new nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his nipples reshaped; $he may reconsider once $he realizes just what they are now capable of. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new nipples<<else>>The feel of the air running over $his new nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim; a notion that is likely only going to strengthen once $he realizes just how fuckable $his tits have become. For now, @@.mediumorchid;$he seems to view $his altered nipples as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new nipples<<else>>The feel of the air running over $his new nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim; a notion that is likely only going to strengthen once $he realizes just how fuckable $his tits have become. For now, @@.mediumorchid;$he seems to view $his altered nipples as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "areolae">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little reaction to $his altered nipples. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He shows little reaction to $his altered nipples. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new nipples despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new nipples against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new nipples despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new nipples against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He runs a finger over $his new nipples experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel the new nipples capping $his tits turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his nipples.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He runs a finger over $his new nipples experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel the new nipples capping $his tits turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his nipples.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new nipples skeptically<<else>>The cool air flowing over $his new nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his nipples reshaped. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new nipples skeptically<<else>>The cool air flowing over $his new nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his nipples reshaped. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new nipples<<else>>The feel of the air running over $his new nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view $his altered nipples as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new nipples<<else>>The feel of the air running over $his new nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view $his altered nipples as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
@@ -751,15 +751,15 @@ As the remote surgery's long recovery cycle completes,
 		<<set getSlave($AS).nipples = "huge">>
 	<</if>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little awareness that $his breasts are smaller. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He shows little awareness that $his breasts are smaller. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, sleeker breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his smaller breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, sleeker breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his smaller breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, smaller breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find them substantially less bouncy<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, smaller breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find them substantially less bouncy<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical theft as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical theft as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10>>
 		<<set getSlave($AS).devotion -= 5>>
 	<</if>>
@@ -781,24 +781,24 @@ As the remote surgery's long recovery cycle completes,
 		<<set getSlave($AS).nipples = "huge">>
 	<</if>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little awareness that $his breasts are gone, despite such a massive change. As with all invasive surgery @@.red;$his health has been affected.@@
+		$He shows little awareness that $his breasts are gone, despite such a massive change. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).sexualFlaw == "breast growth")>>
-		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. The immense bust $he managed to grow has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his enormous breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. The immense bust $he managed to grow has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his enormous breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).trust = -50, getSlave($AS).devotion = -50>>
 		<<set getSlave($AS).fetish = "mindbroken">>
 		<<set getSlave($AS).fetishStrength = 10>>
 		<<set getSlave($AS).sexualQuirk = "none", getSlave($AS).behavioralQuirk = "none", getSlave($AS).behavioralFlaw = "none", getSlave($AS).sexualFlaw = "none">>
 	<<elseif (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).devotion <= 20)>>
-		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent, immense bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his enormous breasts, and they were swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves.
+		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent, immense bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his enormous breasts, and they were swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves.
 		<<set getSlave($AS).trust -= 40, getSlave($AS).devotion -= 20>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs@@ and @@.mediumaquamarine;thankful@@ that you'd consider $his health, well being and ability to fuck. As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs@@ and @@.mediumaquamarine;thankful@@ that you'd consider $his health, well being and ability to fuck. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4, getSlave($AS).trust += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts with appreciation<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed the literal weight off $his chest.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts with appreciation<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed the literal weight off $his chest.
 		<<set getSlave($AS).trust += 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with relief<<else>>The sudden lack of weight on $his chest fills $him with relief<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but $he breathes easier without the immense weight hanging from $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the immense weight hanging from $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $his chest@@ and @@.mediumaquamarine;is thankful for your consideration of $his health.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with relief<<else>>The sudden lack of weight on $his chest fills $him with relief<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but $he breathes easier without the immense weight hanging from $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the immense weight hanging from $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $his chest@@ and @@.mediumaquamarine;is thankful for your consideration of $his health.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).trust += 10, getSlave($AS).devotion += 5>>
 	<</if>>
 
@@ -819,80 +819,80 @@ As the remote surgery's long recovery cycle completes,
 		<<set getSlave($AS).nipples = "huge">>
 	<</if>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He shows little awareness that $his breasts are gone. As with all invasive surgery @@.red;$his health has been affected.@@
+		$He shows little awareness that $his breasts are gone. As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).sexualFlaw == "breast growth")>>
-		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once glorious bust has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his huge breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once glorious bust has been all but stripped from $him. $His face fills with disbelief as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with disbelief as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$His <<if !hasBothArms(getSlave($AS))>>hands immediately dart<<else>>hand immediately darts<</if>> to grope $his tits, but $he only ends up grabbing air. $His face twitches, $his mind unable to comprehend why this has happened to $him. $His <<if !hasBothArms(getSlave($AS))>>hand falls to $his side<<else>>hands fall to $his sides<</if>> as $his will breaks.<<else>> $He tries to squirm, and finds $he is no longer pinned by $his tits. $His face twitches, $his mind unable to comprehend why this has happened to $him. $He sobs once as $his will to go on breaks apart.<</if>> $He loved $his huge breasts, and now that they are gone, $he has nothing to live for. @@.red;Your theft of $his obsession has broken $his mind.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).trust = -50, getSlave($AS).devotion = -50>>
 		<<set getSlave($AS).fetish = "mindbroken">>
 		<<set getSlave($AS).fetishStrength = 10>>
 		<<set getSlave($AS).sexualQuirk = "none", getSlave($AS).behavioralQuirk = "none", getSlave($AS).behavioralFlaw = "none", getSlave($AS).sexualFlaw = "none">>
 	<<elseif (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).devotion <= 20)>>
-		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his huge breasts, and they were swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves.
+		<<if canSee(getSlave($AS))>>$He can hardly believe what $he is seeing. $His once magnificent bust has been all but stripped from $him. $His face fills with resentment as $his flatness dawns on $him<<else>>$He immediately notices the lack of an immense weight hanging off $his chest. $His face fills with resentment as $his flatness dawns on $him<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He loved $his huge breasts, and they were swiped from off $his chest by the person $he was just beginning to entrust $himself to. @@.mediumorchid;$He sees this as a betrayal by you.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ that you may chose to steal something else $he loves.
 		<<set getSlave($AS).trust -= 40, getSlave($AS).devotion -= 20>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He hefts $his new, tiny breasts experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his tiny breasts move and turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his boobs.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, tiny breasts skeptically<<else>>$He attempts to sway $his big tits experimentally, only to find $his chest barely moves at all<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical theft as a cruel imposition.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes the sudden lack of $his former breasts with resentment<<else>>The sudden lack of weight on $his chest fills $him with resentment<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical theft as a cruel imposition.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "breastShapePreservation">>
-	$He notices almost immediately the immense soreness in $his breasts. $He can't find anything off about them, but $he knows you did something to them. As with all surgery @@.red;$his health has been slightly affected.@@
+	$He notices almost immediately the immense soreness in $his breasts. $He can't find anything off about them, but $he knows you did something to them. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "breastLift">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		The changes to $his breasts are lost on $him. As with all surgery @@.red;$his health has been slightly affected.@@
+		The changes to $his breasts are lost on $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new, perkier breasts despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new, perkier breasts against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new, perkier breasts despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new, perkier breasts against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He runs $his hand<<if hasBothArms(getSlave($AS))>>s<</if>> over $his perkier breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his perkier tits move before $he turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's thrilled that you firmed up $his breasts.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He runs $his hand<<if hasBothArms(getSlave($AS))>>s<</if>> over $his perkier breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his perkier tits move before $he turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's thrilled that you firmed up $his breasts.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, perkier breasts skeptically<<else>>The cool air flowing over the new location of $his nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, but $he is @@.hotpink;quite happy@@ you'd take the time to make $his breasts prettier. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and that this may be only the beginning of extensive breast surgery.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, perkier breasts skeptically<<else>>The cool air flowing over the new location of $his nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, but $he is @@.hotpink;quite happy@@ you'd take the time to make $his breasts prettier. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body and that this may be only the beginning of extensive breast surgery.
 		<<set getSlave($AS).devotion += 3, getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, perkier breasts<<else>>The feel of the air running over the new location of $his nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. Oddly, @@.hotpink;$he seems to like that $his breasts are no longer saggy@@ despite $his immediate reaction. As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body and fears that this is only the beginning of $his breast surgeries.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, perkier breasts<<else>>The feel of the air running over the new location of $his nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. Oddly, @@.hotpink;$he seems to like that $his breasts are no longer saggy@@ despite $his immediate reaction. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body and fears that this is only the beginning of $his breast surgeries.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion += 2>>
 	<</if>>
 
 <<case "breastReconstruction">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		The changes to $his breasts are lost on $him. As with all surgery @@.red;$his health has been slightly affected.@@
+		The changes to $his breasts are lost on $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "boobs") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new, <<print getSlave($AS).boobShape>> breasts despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new, <<print getSlave($AS).boobShape>> breasts against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He's barely out of the surgery before $he's playing with $his new, <<print getSlave($AS).boobShape>> breasts despite the pain.<<else>>$He's barely out of the surgery before $he's rubbing $his new, <<print getSlave($AS).boobShape>> breasts against anything $he can reach, despite the pain.<</if>> $He's @@.hotpink;deliriously happy@@ with your changes to what $he thinks of as $his primary sexual organs, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He runs $his hand<<if hasBothArms(getSlave($AS))>>s<</if>> over $his <<print getSlave($AS).boobShape>> breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his <<print getSlave($AS).boobShape>> tits move before $he turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his breasts.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He runs $his hand<<if hasBothArms(getSlave($AS))>>s<</if>> over $his <<print getSlave($AS).boobShape>> breasts experimentally and turns to you with a smile to show them off. $He's still sore, so $he doesn't touch them much, but $he turns from side to side to let you see them from all angles.<<else>>$He bounces a little to feel $his <<print getSlave($AS).boobShape>> tits move before $he turns $his torso to you with a smile to show them off. $He's still sore, so $he doesn't move too violently, but $he wiggles $himself a little to show off.<</if>> @@.hotpink;$He's happy with your changes to $his breasts.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, <<print getSlave($AS).boobShape>> breasts skeptically<<else>>The cool air flowing over the new location of $his nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his breasts reshaped. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, <<print getSlave($AS).boobShape>> breasts skeptically<<else>>The cool air flowing over the new location of $his nipples draws a skeptical expression to $his face<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them.<<else>>$He's still sore, so $he keeps $his body still.<</if>> $He's come to terms with the fact that $he's a slave, so $he isn't much affected mentally despite the surprise of having $his breasts reshaped. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new, <<print getSlave($AS).boobShape>> breasts<<else>>The feel of the air running over the new location of $his nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view $his altered breasts as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new, <<print getSlave($AS).boobShape>> breasts<<else>>The feel of the air running over the new location of $his nipples fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his body still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view $his altered breasts as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10>>
 	<</if>>
 
 <<case "lactation">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He notices almost immediately that $his breasts feel fuller, gasping as milk begins to leak from $his nipples. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He notices almost immediately that $his breasts feel fuller, gasping as milk begins to leak from $his nipples. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
 		<<if hasAnyArms(getSlave($AS))>>
-			$He hefts $his swollen breasts experimentally and turns to you with a smile to show them off. 
+			$He hefts $his swollen breasts experimentally and turns to you with a smile to show them off.
 			<<if getSlave($AS).lactation == 1>>
 				$His milk begins to flow as $he does, but instead of a slight dribble, it keeps coming.
 			<<else>>
-				As $he does, a drop of milk drips from a nipple and $he gasps in surprise. 
+				As $he does, a drop of milk drips from a nipple and $he gasps in surprise.
 			<</if>>
 			$He's shocked, but after <<if canTaste(getSlave($AS))>>tasting<<else>>licking up<</if>> $his own milk experimentally $he <<if canSee(getSlave($AS))>>looks<<else>>smiles<</if>> at you shyly and gently teases some more milk out of $himself. The resulting stream of cream is bountiful and $he giggles happily.
 		<<else>>
 			As you carry $him out of the surgery, droplets of milk begin to bud from $his nipples, and $he giggles giddily.
 		<</if>>
-		@@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		@@.hotpink;$He's happy with your changes to $his boobs.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
 		<<if hasAnyArms(getSlave($AS))>>
@@ -909,7 +909,7 @@ As the remote surgery's long recovery cycle completes,
 			<</if>>
 			a few experimental pokes and rubs at $himself $he seems to understand that $he's a lactation slave now, and that's how it is.
 		<<else>>
-			As you carry $him out of the surgery, droplets of milk begin to bud from $his 
+			As you carry $him out of the surgery, droplets of milk begin to bud from $his
 			<<if getSlave($AS).lactation == 1>>
 				nipples; $he was already lactating, but it never just flowed freely like this.
 			<<else>>
@@ -917,23 +917,23 @@ As the remote surgery's long recovery cycle completes,
 			<</if>>
 			$He's shocked, but $he seems to understand that $he's a lactation slave now, and that's how it is.
 		<</if>>
-		$He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
 		<<if canSee(getSlave($AS))>>$He eyes $his swollen breasts<<else>>As $he feels the fullness in $his breasts, $his face fills<</if>> with resentment. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim.
 		<<if getSlave($AS).lactation == 1>>
-			As milk begins to bead at $his nipples, $he breaks down in rage and unhappiness, dripping bitter tears as the flow steadily increases past what $he was used to. 
+			As milk begins to bead at $his nipples, $he breaks down in rage and unhappiness, dripping bitter tears as the flow steadily increases past what $he was used to.
 		<<else>>
 			When $he finally figures out $he's lactating, $he breaks down in rage and unhappiness, dripping milk and bitter tears.
 		<</if>>
-		For now, @@.mediumorchid;$he seems to view being a lactation slave as a cruel hardship.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		For now, @@.mediumorchid;$he seems to view being a lactation slave as a cruel hardship.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 	/* Done here to allow for natural lactation into artificial lactation text */
 	<<set getSlave($AS).lactation = 2>>
 
 <<case "endlac">>
-	$He notices almost immediately that the soreness that used to tell $him $he needed to be milked has gone. $He bounces $his breasts idly; it looks like $he doesn't know what to think about having $his lactation dry up. As with all surgery @@.red;$his health has been slightly affected.@@
+	$He notices almost immediately that the soreness that used to tell $him $he needed to be milked has gone. $He bounces $his breasts idly; it looks like $he doesn't know what to think about having $his lactation dry up. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<if getSlave($AS).assignment == "get milked" || getSlave($AS).assignment == "work in the dairy">>
 		@@.yellow;$His assignment has defaulted to rest.@@
 		<<= removeJob(getSlave($AS), getSlave($AS).assignment)>>
@@ -954,16 +954,16 @@ As the remote surgery's long recovery cycle completes,
 			<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 		<</if>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "endprecum">>
-	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $His generous production of precum won't tail off for some time, until the slow-release drugs are completely flushed from $his system. Even then, the only real change for $him will be a little less inconvenience. As with all surgery @@.red;$his health has been slightly affected.@@
+	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $His generous production of precum won't tail off for some time, until the slow-release drugs are completely flushed from $his system. Even then, the only real change for $him will be a little less inconvenience. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "vasectomy">>
-	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $He likely won't realize what happened<<if getSlave($AS).ballType == "sterile">> given that $he couldn't get girls pregnant in the first place<<else>>, but may piece things together when $he realizes the girls $he fucks never get pregnant<</if>>. As with all surgery @@.red;$his health has been slightly affected.@@
+	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $He likely won't realize what happened<<if getSlave($AS).ballType == "sterile">> given that $he couldn't get girls pregnant in the first place<<else>>, but may piece things together when $he realizes the girls $he fucks never get pregnant<</if>>. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "vasectomy undo">>
-	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $He likely won't ever realize what happened <<if getSlave($AS).ballType == "sterile">>since $his balls don't work in the first place<<else>>until $he gets a girl pregnant<</if>>. As with all surgery @@.red;$his health has been slightly affected.@@
+	$His groin is a little sore, and $he examines it closely, but $he can't find much difference. $He likely won't ever realize what happened <<if getSlave($AS).ballType == "sterile">>since $his balls don't work in the first place<<else>>until $he gets a girl pregnant<</if>>. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "chem castrate">>
 	$He knows something has been done to $his testicles, but $he can't feel anything off about them. Since the surgery was nothing more than a simple injection, $he has been spared any pain.
@@ -982,20 +982,20 @@ As the remote surgery's long recovery cycle completes,
 			<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 		<</if>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "endejac">>
-	$His groin is a little sore, and $he examines it closely, but $he can't find much difference other than the swelling in $his crotch has gone down. $He'll realize later when $his next ejaculation is rather underwhelming from what $he has become accustomed to. As with all surgery @@.red;$his health has been slightly affected.@@
+	$His groin is a little sore, and $he examines it closely, but $he can't find much difference other than the swelling in $his crotch has gone down. $He'll realize later when $his next ejaculation is rather underwhelming from what $he has become accustomed to. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "scarRemov">>
-	Even though removing scars is a trivial process, @@.red;$his health has been slightly affected.@@
+	Even though removing scars is a trivial process, @@.health.dec;$his health has been slightly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		When $he exits the room, $he feels that most of $his skin is numb, and $he is @@.hotpink;happy@@ when $he finds $his scars have been removed.
 		<<set getSlave($AS).devotion += 2>>
 	<</if>>
 
 <<case "scarFear">>
-	Even though creating an artificial scar is a trivial process, @@.red;$his health has been slightly affected.@@
+	Even though creating an artificial scar is a trivial process, @@.health.dec;$his health has been slightly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		When $he exits the room, $he feels that $his face is numb,
 		<<if getSlave($AS).ID == $BodyguardID>>
@@ -1014,7 +1014,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "scarExo">>
-	Even though creating an artificial scar is a trivial process, @@.red;$his health has been slightly affected.@@
+	Even though creating an artificial scar is a trivial process, @@.health.dec;$his health has been slightly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		When $he exits the room, $he feels that $his face is numb,
 		<<if getSlave($AS).devotion > 50>>
@@ -1032,39 +1032,39 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "butt">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He doesn't notice that $his butt has gotten larger. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He doesn't notice that $his butt has gotten larger. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "buttslut") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He gently flexes $his sore buttocks with a sigh of pleasure. $He's @@.hotpink;deliriously happy@@ to have a bigger butt, since $he confidently expects that this will mean more cocks being shoved up $his asshole. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He gently flexes $his sore buttocks with a sigh of pleasure. $He's @@.hotpink;deliriously happy@@ to have a bigger butt, since $he confidently expects that this will mean more cocks being shoved up $his asshole. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He rubs $his new butt experimentally and turns to you with a smile to show it off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his ass.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He rubs $his new butt experimentally and turns to you with a smile to show it off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his ass.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>$He shifts $his new butt<</if>> skeptically. $He's still sore, so $he doesn't touch it. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>$He shifts $his new butt<</if>> skeptically. $He's still sore, so $he doesn't touch it. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>The new weight in $his backside fills $him<</if>> with resentment. $He's still sore, so $he doesn't touch them, but $he glares daggers. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this fake ass as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>The new weight in $his backside fills $him<</if>> with resentment. $He's still sore, so $he doesn't touch them, but $he glares daggers. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this fake ass as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "buttLoss">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He doesn't notice that $his butt has gotten smaller. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He doesn't notice that $his butt has gotten smaller. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He <<if canSee(getSlave($AS))>>twists to view<<else>>$He jiggles<</if>> $his new, sleeker derrière and turns to you with a smile and a flirty little roll of $his hips. $He's still sore, so $he doesn't bounce $his tighter buttocks for you, but $he seems happy all the same. @@.hotpink;$He's happy with your changes to $his buttocks.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He <<if canSee(getSlave($AS))>>twists to view<<else>>$He jiggles<</if>> $his new, sleeker derrière and turns to you with a smile and a flirty little roll of $his hips. $He's still sore, so $he doesn't bounce $his tighter buttocks for you, but $he seems happy all the same. @@.hotpink;$He's happy with your changes to $his buttocks.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>twists to view<<else>>jiggles<</if>> $his new, sleeker derrière skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>twists to view<<else>>jiggles<</if>> $his new, sleeker derrière skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He shifts $his diminished ass with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical ass theft as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He shifts $his diminished ass with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this surgical ass theft as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "face">>
 	<<= faceIncrease(getSlave($AS), 20)>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He doesn't notice the improvements to $his face, but $he's not the one looking at it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He doesn't notice the improvements to $his face, but $he's not the one looking at it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
 		$He
 		<<if canSee(getSlave($AS))>>
@@ -1072,7 +1072,7 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			listens closely to the description of $his new face. $He
 		<</if>>
-		hopes you'll like $his new face better and use $him more frequently as a result. @@.hotpink;$He's happy with your changes to $his face.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		hopes you'll like $his new face better and use $him more frequently as a result. @@.hotpink;$He's happy with your changes to $his face.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
 		$He
@@ -1081,7 +1081,7 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			listens closely to the description of $his new face. $He
 		<</if>>
-		hopes you'll like $his new face better and treat $him more kindly as a result. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		hopes you'll like $his new face better and treat $him more kindly as a result. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
 		$He
@@ -1090,13 +1090,13 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			listens closely to the description of $his new face,
 		<</if>>
-		which $he hates, though $he hopes you'll like $his new face better and punish $him less cruelly as a result. For now, @@.mediumorchid;$he seems to view this fake face as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		which $he hates, though $he hopes you'll like $his new face better and punish $him less cruelly as a result. For now, @@.mediumorchid;$he seems to view this fake face as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "age">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He doesn't notice the improvements to $his face, but $he's not the one looking at it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He doesn't notice the improvements to $his face, but $he's not the one looking at it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
 		$He
 		<<if canSee(getSlave($AS))>>
@@ -1104,18 +1104,18 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			listens closely to the description of $his new younger self with approval.
 		<</if>>
-		$He doesn't recognize $his youthful yet artificial appearance yet, but $he resolves to back $his new appearance up with youthful energy and cheer. @@.hotpink;$He's very happy you used surgery to reverse $his apparent aging.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He doesn't recognize $his youthful yet artificial appearance yet, but $he resolves to back $his new appearance up with youthful energy and cheer. @@.hotpink;$He's very happy you used surgery to reverse $his apparent aging.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>examines youthful yet artificial appearance in the mirror<<else>>listens to the description of $his youthful yet artificial appearance<</if>> with hesitation. The cosmetic surgery is extremely well done, and $he eventually decides that being subjected to surgery without $his consent is @@.hotpink;acceptable,@@ if it's being used to make $him look so young and pretty. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He <<if canSee(getSlave($AS))>>examines youthful yet artificial appearance in the mirror<<else>>listens to the description of $his youthful yet artificial appearance<</if>> with hesitation. The cosmetic surgery is extremely well done, and $he eventually decides that being subjected to surgery without $his consent is @@.hotpink;acceptable,@@ if it's being used to make $him look so young and pretty. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<else>>
-		$He <<if canSee(getSlave($AS))>>examines $his youthful yet artificial appearance in the mirror<<else>>listens to the description of $his youthful yet artificial appearance<</if>> with confusion. The cosmetic surgery is extremely well done, and $he eventually decides that being subjected to surgery without $his consent isn't worth fighting against, if it's being used to make $him look younger. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>examines $his youthful yet artificial appearance in the mirror<<else>>listens to the description of $his youthful yet artificial appearance<</if>> with confusion. The cosmetic surgery is extremely well done, and $he eventually decides that being subjected to surgery without $his consent isn't worth fighting against, if it's being used to make $him look younger. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<</if>>
 
 <<case "eyeFix">>
-	The laser eye surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he processes $his newly clear vision. Seeing the world as it is without glasses or contact lenses is a gift that those who do not need it cannot properly understand.
+	The laser eye surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he processes $his newly clear vision. Seeing the world as it is without glasses or contact lenses is a gift that those who do not need it cannot properly understand.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 5, getSlave($AS).trust += 5>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -1128,7 +1128,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "eyeBlur">>
-	The laser eye surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this blurred world is $his new reality.
+	The laser eye surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this blurred world is $his new reality.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to weep, not understanding why it's necessary that $he not be able to see clearly. After a short cry that trails off into a few sniffles, $he carries on.
@@ -1142,7 +1142,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "earFix">>
-	The inner ear surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he identifies each and every newly clear sound. Hearing the world as it is without hearing aids is a gift that those who do not need it cannot properly understand.
+	The inner ear surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $his gaze flicks from object to object with manic speed as $he identifies each and every newly clear sound. Hearing the world as it is without hearing aids is a gift that those who do not need it cannot properly understand.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<set getSlave($AS).devotion += 5, getSlave($AS).trust += 5>>
 		<<if (getSlave($AS).devotion > 50)>>
@@ -1155,7 +1155,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "earMuffle">>
-	The inner ear surgery is brief, with @@.red;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this muffled world is $his new reality.
+	The inner ear surgery is brief, with @@.health.dec;nothing more than minor health effects.@@ As soon as $he is allowed to open $his eyes and look around, $he begins to glance back and forth frantically, not immediately understanding that this muffled world is $his new reality.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			When $he finally figures it out, $he begins to weep, not understanding why it's necessary that $he not be able to hear clearly. After a short cry that trails off into a few sniffles, $he carries on.
@@ -1175,41 +1175,41 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			$He can't discern the changes to $his race and likely never will.
 		<</if>>
-		As with all invasive surgery @@.red;$his health has been affected.@@
+		As with all invasive surgery @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with approval. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, and<<else>>$He doesn't recognize $himself quite yet, but<</if>> $he hopes you'll like $his new appearance better and use $him more frequently as a result. @@.hotpink;$He's happy with your changes to $his racial appearance.@@ As with all invasive surgery @@.red;$his health has been affected.@@
+		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with approval. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, and<<else>>$He doesn't recognize $himself quite yet, but<</if>> $he hopes you'll like $his new appearance better and use $him more frequently as a result. @@.hotpink;$He's happy with your changes to $his racial appearance.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with hesitation. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, and<<else>>$He doesn't recognize $himself quite yet, but<</if>> $he hopes you'll like $his new appearance better and treat $him more kindly as a result. As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with hesitation. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, and<<else>>$He doesn't recognize $himself quite yet, but<</if>> $he hopes you'll like $his new appearance better and treat $him more kindly as a result. As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with revulsion. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, which $he loves, and<<else>>$He doesn't recognize $himself quite yet, which $he hates, though<</if>> $he hopes you'll like $his new appearance better and punish $him less cruelly as a result. For now, @@.mediumorchid;$he seems to view this <<if (getSlave($AS).race != getSlave($AS).origRace)>>fake<</if>> racial appearance as a cruel imposition.@@ As with all invasive surgery @@.red;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>examines $his new self in the mirror<<elseif canHear(getSlave($AS))>>listens to the description of $his new self<<else>>gets a feel for the changes to $his body<</if>> with revulsion. <<if (getSlave($AS).race == getSlave($AS).origRace)>>$He recognizes $himself as $he was, which $he loves, and<<else>>$He doesn't recognize $himself quite yet, which $he hates, though<</if>> $he hopes you'll like $his new appearance better and punish $him less cruelly as a result. For now, @@.mediumorchid;$he seems to view this <<if (getSlave($AS).race != getSlave($AS).origRace)>>fake<</if>> racial appearance as a cruel imposition.@@ As with all invasive surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "lips">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He vaguely realizes $his mouth doesn't move as well as it used to. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He vaguely realizes $his mouth doesn't move as well as it used to. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).fetish == "cumslut") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He licks $his new lips experimentally but doesn't lose much time before turning to you with $his mouth open and ready. $He's still sore, so $he's careful, but $he runs $his wet tongue over $his lips, already panting at the thought of sucking dick. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ @@.hotpink;$He's happy with your changes to $his lips,@@ so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He licks $his new lips experimentally but doesn't lose much time before turning to you with $his mouth open and ready. $He's still sore, so $he's careful, but $he runs $his wet tongue over $his lips, already panting at the thought of sucking dick. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ @@.hotpink;$He's happy with your changes to $his lips,@@ so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 4>>
 		<<if getSlave($AS).skill.oral > 10>>
 			<<set getSlave($AS).skill.oral -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He puckers $his new lips experimentally and turns to you with a smile to show them off. $He's still sore, so $he's careful as $he blows you an awkward kiss. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ @@.hotpink;$He's happy with your changes to $his lips.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He puckers $his new lips experimentally and turns to you with a smile to show them off. $He's still sore, so $he's careful as $he blows you an awkward kiss. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ @@.hotpink;$He's happy with your changes to $his lips.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 		<<if getSlave($AS).skill.oral > 10>>
 			<<set getSlave($AS).skill.oral -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>eyes<<else>>puckers<</if>> $his new lips skeptically. $He's still sore, so $he doesn't touch them. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally, @@.red;but if $he had much in the way of oral skills, they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>eyes<<else>>puckers<</if>> $his new lips skeptically. $He's still sore, so $he doesn't touch them. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally, @@.red;but if $he had much in the way of oral skills, they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<if getSlave($AS).skill.oral > 10>>
 			<<set getSlave($AS).skill.oral -= 10>>
 		<</if>>
 	<<else>>
-		$He <<if canSee(getSlave($AS))>>eyes<<else>>puckers<</if>> $his new lips with resentment. $He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ For now, @@.mediumorchid;$he seems to view these fake lips as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>eyes<<else>>puckers<</if>> $his new lips with resentment. $He's still sore, so $he doesn't touch them, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. If $he had much in the way of oral skills, @@.red;they've likely suffered.@@ For now, @@.mediumorchid;$he seems to view these fake lips as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10>>
 		<<set getSlave($AS).devotion -= 5>>
 		<<if getSlave($AS).skill.oral > 10>>
@@ -1219,18 +1219,18 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "liposuction">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He notices that $he is quite lighter than $he used to be. As with all surgeries, @@.red;$his health has been affected.@@
+		$He notices that $he is quite lighter than $he used to be. As with all surgeries, @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).behavioralFlaw == "anorexic")>>
-		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ As with all surgeries, @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ As with all surgeries, @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. $He's still sore, so $he doesn't bend or flirt, but $he turns around to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his body.@@ As with all surgery @@.red;$his health has been affected.@@
+		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. $He's still sore, so $he doesn't bend or flirt, but $he turns around to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his body.@@ As with all surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>eyes $his new thin figure<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>eyes $his new thin figure<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new thin figure<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this model figure as a cruel imposition.@@ As with all surgery @@.red;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new thin figure<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this model figure as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 	<<if getSlave($AS).weight > 190 && getSlave($AS).bellySag < 10>>
@@ -1247,7 +1247,7 @@ As the remote surgery's long recovery cycle completes,
 	<<set getSlave($AS).boobs = Math.clamp(getSlave($AS).boobs, 0, 50000)>>
 	<<set getSlave($AS).butt = Math.clamp(getSlave($AS).butt, 0, 20)>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He notices that $his weight is slightly off, almost as if it were someplace else. As with all surgeries, @@.red;$his health has been affected.@@
+		$He notices that $his weight is slightly off, almost as if it were someplace else. As with all surgeries, @@.health.dec;$his health has been affected.@@
 	<<elseif (getSlave($AS).behavioralFlaw == "anorexic")>>
 		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off.
 		As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@
@@ -1273,7 +1273,7 @@ As the remote surgery's long recovery cycle completes,
 				$He rubs $his new butt experimentally and turns to you to show it off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see it from all angles. $He isn't too keen on the idea of $his fat being moved to $his butt instead of removed completely, but $he accepts that it isn't hanging from $his front any longer.
 			<</if>>
 		<</if>>
-		As with all surgeries, @@.red;$his health has been affected.@@
+		As with all surgeries, @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
 		<<if canSee(getSlave($AS))>>$He looks over $his new thin figure experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. $He's still sore, so $he doesn't bend or flirt, but $he turns around to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his body.@@
@@ -1299,7 +1299,7 @@ As the remote surgery's long recovery cycle completes,
 				$He rubs $his new butt experimentally and turns to you with a smile to show it off. $He's still sore, so $he doesn't bounce or squeeze, but $he turns from side to side to let you see it from all angles.
 			<</if>>
 		<</if>>
-		As with all surgery @@.red;$his health has been affected.@@
+		As with all surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
 		<<if $boobFat > 0>>
@@ -1314,7 +1314,7 @@ As the remote surgery's long recovery cycle completes,
 		<<if $buttFat > 0>>
 			<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>$He shifts $his new butt<</if>> skeptically. $He's still sore, so $he doesn't touch it.
 		<</if>>
-		$He <<if canSee(getSlave($AS))>>eyes $his new thin figure<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>eyes $his new thin figure<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
 		<<if $boobFat > 0>>
@@ -1329,7 +1329,7 @@ As the remote surgery's long recovery cycle completes,
 		<<if $buttFat > 0>>
 			<<if canSee(getSlave($AS))>>$He eyes $his new butt<<else>>The new weight in $his backside fills $him<</if>> with resentment.
 		<</if>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new thin figure<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this model figure as a cruel imposition.@@ As with all surgery @@.red;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new thin figure<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this model figure as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 5>>
 	<</if>>
 	<<if getSlave($AS).weight > 190 && getSlave($AS).bellySag < 10>>
@@ -1343,48 +1343,48 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "lipo">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		While $he notices how sore $his waist is, $he fails to find the reason why. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		While $he notices how sore $his waist is, $he fails to find the reason why. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).behavioralFlaw == "anorexic")>>
-		<<if canSee(getSlave($AS))>>$He looks over $his new waist experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		<<if canSee(getSlave($AS))>>$He looks over $his new waist experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if canSee(getSlave($AS))>>$He looks over $his new waist experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. $He's still sore, so $he doesn't bend or flirt, but $he turns around to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his waist.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if canSee(getSlave($AS))>>$He looks over $his new waist experimentally<<else>>$He shifts $his weight experimentally<</if>> and turns to you with a smile to show it off. $He's still sore, so $he doesn't bend or flirt, but $he turns around to let you see it from all angles. @@.hotpink;$He's happy with your changes to $his waist.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He <<if canSee(getSlave($AS))>>eyes $his new waist<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He <<if canSee(getSlave($AS))>>eyes $his new waist<<else>>shifts $his weight<</if>> skeptically. $He's still sore, so $he doesn't bend or touch $himself. $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his new waist<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this fake figure as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his new waist<<else>>How light $he feels fills $him<</if>> with resentment. $He's still sore, so $he doesn't bend or touch $himself, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>. $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this fake figure as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "ribs">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause is $his new extremely narrow waist. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause is $his new extremely narrow waist. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).behavioralFlaw == "anorexic")>>
-		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, and when $he <<if canSee(getSlave($AS))>>looks at you, you see awe in $his eyes<<else>>turns to face you, you see awe on $his face<</if>>. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, and when $he <<if canSee(getSlave($AS))>>looks at you, you see awe in $his eyes<<else>>turns to face you, you see awe on $his face<</if>>. As an anorexic @@.hotpink;$he thinks you have brought $him closer to the true ideal.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, and when $he <<if canSee(getSlave($AS))>>looks at you, you see awe and fear in $his eyes<<else>>turns to you, you see awe and fear on $his face<</if>>. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, and when $he <<if canSee(getSlave($AS))>>looks at you, you see awe and fear in $his eyes<<else>>turns to you, you see awe and fear on $his face<</if>>. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, wobbles a little, gasps with pain at the soreness as $he does, and then manages to hold $himself upright and still as $he sobs. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He's desperately stiff and sore. It takes a good while for $him to figure out what has happened, but eventually $his careful investigations discern that the cause of $his extremely narrow waist is that $his ribcage has been shortened. $He gasps with shock, wobbles a little, gasps with pain at the soreness as $he does, and then manages to hold $himself upright and still as $he sobs. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "mtf">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Surprisingly, $he already realized while exiting that $his genitalia was completely reshaped. The reasons why are lost to $him, though. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Surprisingly, $he already realized while exiting that $his genitalia was completely reshaped. The reasons why are lost to $him, though. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, and $his face is a strange mix of hope, happiness, satisfaction, and the tiniest tinge of soul-crushing sadness as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that has replaced $his penis. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, and $his face is a strange mix of hope, happiness, satisfaction, and the tiniest tinge of soul-crushing sadness as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that has replaced $his penis. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that has replaced $his penis. Eventually $his shoulders slump and $he tries to carry on. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that has replaced $his penis. Eventually $his shoulders slump and $he tries to carry on. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<<else>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, but expectations and the reality are two different things. $He's beside $himself, weeping gently. $He absently moves a hand to where $his penis would have been and snatches it away with a sob. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. As a shemale slave $he knew this day might come, but expectations and the reality are two different things. $He's beside $himself, weeping gently. $He absently moves a hand to where $his penis would have been and snatches it away with a sob. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 	<<if (getSlave($AS).assignment == "get milked" || getSlave($AS).assignment == "work in the dairy") && getSlave($AS).lactation == 0>>
@@ -1394,43 +1394,43 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "ntf">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Surprisingly, $he already realized while exiting that $his genitalia was completely reshaped. The reasons why are lost to $him, though. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Surprisingly, $he already realized while exiting that $his genitalia was completely reshaped. The reasons why are lost to $him, though. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He can only be happy that $he <<if getSlave($AS).genes == "XX">>once again has a vagina and now has a means of release again<<else>>now has a hole other than $his anus to pleasure $himself with<</if>>. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He can only be happy that $he <<if getSlave($AS).genes == "XX">>once again has a vagina and now has a means of release again<<else>>now has a hole other than $his anus to pleasure $himself with<</if>>. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He didn't expect <<if getSlave($AS).genes == "XX">>to ever have a pussy again<<else>>to be given a hole other than $his anus<</if>>, and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that now adorns $his formerly featureless crotch. Eventually $his shoulders slump and $he tries to carry on knowing that this is just another hole for your amusement, not $hers. @@.mediumorchid;$He will struggle with feelings of confusion over just what your plans for $him are.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He didn't expect <<if getSlave($AS).genes == "XX">>to ever have a pussy again<<else>>to be given a hole other than $his anus<</if>>, and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the vagina that now adorns $his formerly featureless crotch. Eventually $his shoulders slump and $he tries to carry on knowing that this is just another hole for your amusement, not $hers. @@.mediumorchid;$He will struggle with feelings of confusion over just what your plans for $him are.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<<else>>
-		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He didn't expect <<if getSlave($AS).genes == "XX">>to ever have a pussy again<<else>>to be given a hole other than $his anus<</if>>, but expectations and the reality are two different things. $He's beside $himself, weeping gently. $He absently moves a hand to where there used to be nothing and snatches it away with a sob, knowing this was done for yourself, not $him. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his genitalia was completely reshaped. $He didn't expect <<if getSlave($AS).genes == "XX">>to ever have a pussy again<<else>>to be given a hole other than $his anus<</if>>, but expectations and the reality are two different things. $He's beside $himself, weeping gently. $He absently moves a hand to where there used to be nothing and snatches it away with a sob, knowing this was done for yourself, not $him. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 
 <<case "herm">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Surprisingly, $he already realized while exiting that $his genitalia has been heavily augmented. The reasons why are lost to $him, though. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Surprisingly, $he already realized while exiting that $his genitalia has been heavily augmented. The reasons why are lost to $him, though. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He already realized while exiting that $his genitalia has been heavily augmented. $He's almost beside $himself with joy: $he now has three holes for $his beloved <<= getWrittenTitle(getSlave($AS))>> to fuck, and _heP even allowed $him to retain $his dick! @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He already realized while exiting that $his genitalia has been heavily augmented. $He's almost beside $himself with joy: $he now has three holes for $his beloved <<= getWrittenTitle(getSlave($AS))>> to fuck, and _heP even allowed $him to retain $his dick! @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He already realized while exiting that $his genitalia has been heavily augmented. $His efforts to cope with this radical development are palpable. $He retains $his penis, to $his considerable surprise; and $he also has a pussy. $He hardly knows what to think or who to be. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He already realized while exiting that $his genitalia has been heavily augmented. $His efforts to cope with this radical development are palpable. $He retains $his penis, to $his considerable surprise; and $he also has a pussy. $He hardly knows what to think or who to be. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<else>>
-		$He already realized while exiting that $his genitalia has been heavily augmented. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ $He is not as angry as $he might otherwise have been, since you left $him $his dick, but $he has a hole where there was none before. $He views it with hatred and revulsion. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He already realized while exiting that $his genitalia has been heavily augmented. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ $He is not as angry as $he might otherwise have been, since you left $him $his dick, but $he has a hole where there was none before. $He views it with hatred and revulsion. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "geld">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Surprisingly, $he already realized while exiting that $his scrotum was gone. The reasons why are lost to $him, though. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Surprisingly, $he already realized while exiting that $his scrotum was gone. The reasons why are lost to $him, though. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he already realized while exiting that $his scrotum was gone. $He was already accepting of $his role as an effectively female slave, a receptacle for cock, and $he now understands that you no longer think it will ever again be necessary for $him to cum. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he already realized while exiting that $his scrotum was gone. $He was already accepting of $his role as an effectively female slave, a receptacle for cock, and $he now understands that you no longer think it will ever again be necessary for $him to cum. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he already realized while exiting that $his scrotum was gone. $He was already struggling to accept $his role as a female slave, a receptacle for cock, and $he now understands it better. $He has not yet accepted that $he will never again fuck, though, that $his future is nothing but getting fucked. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his scrotum was gone. $He was already struggling to accept $his role as a female slave, a receptacle for cock, and $he now understands it better. $He has not yet accepted that $he will never again fuck, though, that $his future is nothing but getting fucked. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<<else>>
-		Of course, $he already realized while exiting that $his scrotum was gone. $He cannot accept that it is no longer $his role to fuck holes, but rather to provide a hole for others to fuck.<<if hasAnyArms(getSlave($AS))>> $He absently moves a hand to where $his balls would have been and snatches it away with a sob.<</if>> @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
+		Of course, $he already realized while exiting that $his scrotum was gone. $He cannot accept that it is no longer $his role to fuck holes, but rather to provide a hole for others to fuck.<<if hasAnyArms(getSlave($AS))>> $He absently moves a hand to where $his balls would have been and snatches it away with a sob.<</if>> @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 	<<if (getSlave($AS).assignment == "get milked" || getSlave($AS).assignment == "work in the dairy") && getSlave($AS).lactation == 0>>
@@ -1439,7 +1439,7 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 
 <<case "prostate">>
-	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower parts. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower parts. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			<<if getSlave($AS).anus == 0>>
@@ -1510,34 +1510,34 @@ As the remote surgery's long recovery cycle completes,
 			<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 		<</if>>
 	<</if>>
-	Naturally, @@.red;$his health has been greatly affected@@ by such serious surgery.
+	Naturally, @@.health.dec;$his health has been greatly affected@@ by such serious surgery.
 	<<set getSlave($AS).chastityPenis = 0>>
 	<<set getSlave($AS).dickAccessory = "none">>
 
 <<case "relocate">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. Since the surgery was invasive, @@.red;$his health has been seriously affected.@@
+		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. Since the surgery was invasive, @@.health.dec;$his health has been seriously affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. However, $he was able to follow enough of the surgery to understand that $he still has $his balls, even if they're no longer visible. $He's eager to <<if canSee(getSlave($AS))>>see, and when $he's recovered enough, $he's pleased by the sight<<else>>feel, and when $he's recovered enough, $he's pleased by the sensation<</if>> of the smooth skin <<if getSlave($AS).dick > 0>>beneath $his still-functional cock<<else>>where $his testicles used to be<</if>>. @@.hotpink;$He has become more submissive due to your reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been seriously affected.@@
+		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. However, $he was able to follow enough of the surgery to understand that $he still has $his balls, even if they're no longer visible. $He's eager to <<if canSee(getSlave($AS))>>see, and when $he's recovered enough, $he's pleased by the sight<<else>>feel, and when $he's recovered enough, $he's pleased by the sensation<</if>> of the smooth skin <<if getSlave($AS).dick > 0>>beneath $his still-functional cock<<else>>where $his testicles used to be<</if>>. @@.hotpink;$He has become more submissive due to your reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been seriously affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. However, $he was able to follow enough of the surgery to understand that $he still has $his balls, even if they're no longer visible. $He's relieved $he hasn't been gelded, but $he's still quite horrified. @@.mediumorchid;$He blames you for changing $him so radically.@@ Since the surgery was invasive, @@.red;$his health has been seriously affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. However, $he was able to follow enough of the surgery to understand that $he still has $his balls, even if they're no longer visible. $He's relieved $he hasn't been gelded, but $he's still quite horrified. @@.mediumorchid;$He blames you for changing $him so radically.@@ Since the surgery was invasive, @@.health.dec;$his health has been seriously affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<<else>>
-		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. $He spends a long time in terror, wondering whether $he's been gelded, but $he eventually realizes that $his balls have merely been relocated. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been seriously affected.@@ $He is @@.gold;very afraid@@ of your total power over $his body.
+		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. $He spends a long time in terror, wondering whether $he's been gelded, but $he eventually realizes that $his balls have merely been relocated. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been seriously affected.@@ $He is @@.gold;very afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 
 <<case "scrotalTuck">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. Since the surgery was invasive, @@.red;$his health has been seriously affected.@@
+		When $he begins surgical recovery, $his groin hurts quite a lot, and $he can't quite tell what's going on down there. Since the surgery was invasive, @@.health.dec;$his health has been seriously affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, @@.hotpink;$he's very happy you'd take an interest in $his balls,@@ and looks forward to discovering how it moves during sex. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, @@.hotpink;$he's very happy you'd take an interest in $his balls,@@ and looks forward to discovering how it moves during sex. @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his genitals. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his genitals. @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own genitals. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his testicles. $He examines them carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his balls fit $his scrotum perfectly, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own genitals. @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion -= 5>>
 	<</if>>
 
@@ -1573,17 +1573,17 @@ As the remote surgery's long recovery cycle completes,
 	<<else>>
 		Of course, $he already realized while exiting that $his genitalia has been radically simplified. As a herm slave $he knew this day might come,
 		<<if (getSlave($AS).devotion > 50)>>
-			and $his face is a strange mix of hope, happiness, satisfaction, and the tiniest tinge of soul-crushing sadness as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the cock that is all that remains of $his once-unique hermaphroditic genitalia. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+			and $his face is a strange mix of hope, happiness, satisfaction, and the tiniest tinge of soul-crushing sadness as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the cock that is all that remains of $his once-unique hermaphroditic genitalia. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion > 20)>>
-			and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the cock that is all that remains of $his once-unique hermaphroditic genitalia. Eventually $his shoulders slump and $he tries to carry on. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+			and $his face is a strange mix of fear, hope, and resignation as $he <<if canSee(getSlave($AS))>>views<<else>>feels<</if>> the cock that is all that remains of $his once-unique hermaphroditic genitalia. Eventually $his shoulders slump and $he tries to carry on. @@.mediumorchid;$He will struggle with feelings of confusion and loss.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<<else>>
 			but expectations and the reality are two different things. $He's beside $himself, weeping gently.<<if hasAnyArms(getSlave($AS))>> $He absently moves a hand to where $his pussy would have been and snatches it away with a sob.<</if>> @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 		<</if>>
 	<</if>>
-	Naturally, @@.red;$his health has been greatly affected@@ by such serious surgery.
+	Naturally, @@.health.dec;$his health has been greatly affected@@ by such serious surgery.
 	<<set getSlave($AS).chastityVagina = 0>>
 	<<set getSlave($AS).vaginalAccessory = "none", getSlave($AS).vaginalAttachment = "none">>
 	<<if getSlave($AS).cervixImplant == 1>>
@@ -1640,45 +1640,45 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "teeth">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand $his teeth are now fake. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand $his teeth are now fake. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, but figures out the idea quickly enough by experimentally <<if hasAnyArms(getSlave($AS))>>sucking on two fingers.<<else>>simulating oral sex with $his own tongue.<</if>> @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, but figures out the idea quickly enough by experimentally <<if hasAnyArms(getSlave($AS))>>sucking on two fingers.<<else>>simulating oral sex with $his own tongue.<</if>> @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, gags, spits out the prosthetics, <<if canSee(getSlave($AS))>>looks at $himself in the mirror<<else>>runs $his tongue across $his empty gums<</if>>, and sobs. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he is wearing prosthetic teeth that $he can remove. $He gasps with shock, gags, spits out the prosthetics, <<if canSee(getSlave($AS))>>looks at $himself in the mirror<<else>>runs $his tongue across $his empty gums<</if>>, and sobs. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "sharp">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He seems doubtful, but then works up $his courage and bares them <<if canSee(getSlave($AS))>>at a mirror<<else>> so you may see<</if>>. $He menaces <<if canSee(getSlave($AS))>>$himself<</if>> for a while before laughing quietly. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He seems doubtful, but then works up $his courage and bares them <<if canSee(getSlave($AS))>>at a mirror<<else>> so you may see<</if>>. $He menaces <<if canSee(getSlave($AS))>>$himself<</if>> for a while before laughing quietly. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, accidentally gets $his tongue in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, accidentally gets $his tongue in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "fangs">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a pair of fangs. $He seems doubtful, but then works up $his courage and bares them <<if canSee(getSlave($AS))>>at a mirror<<else>> so you may see<</if>>. $He menaces <<if canSee(getSlave($AS))>>$himself<</if>> for a while before laughing quietly. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a pair of fangs. $He seems doubtful, but then works up $his courage and bares them <<if canSee(getSlave($AS))>>at a mirror<<else>> so you may see<</if>>. $He menaces <<if canSee(getSlave($AS))>>$himself<</if>> for a while before laughing quietly. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has teeth fit for a carnivore. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<else>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a pair of fangs. $He gasps with shock, accidentally gets $his lip in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a pair of fangs. $He gasps with shock, accidentally gets $his lip in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "fang">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw, yet fails to understand that $his teeth have changed shape. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
 		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a fang. $He seems doubtful,
 		<<if getSlave($AS).lips <= 50>>
@@ -1686,28 +1686,28 @@ As the remote surgery's long recovery cycle completes,
 		<<else>>
 			especially when he tries to close $his mouth only to find $his fat lip keeps meeting the tip of $his tooth.
 		<</if>>
-		Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a fang. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a fang. $He gasps with shock, but eventually $his shoulders slump and $he tries to carry on. $He isn't much affected mentally. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 	<<else>>
-		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a fang $He gasps with shock, accidentally gets $his lip in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his jaw. It takes a good while for $him to figure out what has happened, but eventually $he gets enough sensation in $his mouth to realize that $he now has a fang $He gasps with shock, accidentally gets $his lip in the way in $his anguish, bites $himself, and starts gagging and spitting blood. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "oral">>
-	$He clearly feels quite normal except for a vague ache around $his jaw. $He <<if canSee(getSlave($AS))>>checks $himself in the mirror<<else>>runs $his tongue over $his teeth<</if>> repeatedly before accepting that nothing too unusual has happened, and that $he now appears to have a fully functional set of normal teeth. Though $he's surprised that nothing outlandish has been done, $he carries on as usual. Since the surgery was moderately invasive, @@.red;$his health has been somewhat affected.@@
+	$He clearly feels quite normal except for a vague ache around $his jaw. $He <<if canSee(getSlave($AS))>>checks $himself in the mirror<<else>>runs $his tongue over $his teeth<</if>> repeatedly before accepting that nothing too unusual has happened, and that $he now appears to have a fully functional set of normal teeth. Though $he's surprised that nothing outlandish has been done, $he carries on as usual. Since the surgery was moderately invasive, @@.health.dec;$his health has been somewhat affected.@@
 
 <<case "mute">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He simply keeps trying for some time before accepting this is how things are. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He simply keeps trying for some time before accepting this is how things are. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, but manages to control $himself at length and begins to think. <<if canSee(getSlave($AS))>>Seeing<<else>>Finding<</if>> you, $he tries to ask you for instruction and realizes $he cannot. <<if hasBothLegs(getSlave($AS))>>After more thought, $he simply gets on $his knees facing away from you and spreads $his buttocks to offer $himself to you.<<else>>After more thought, $he simply rolls over facing away from you and winks $his asshole to offer $himself to you.<</if>> @@.hotpink;$He has become more submissive due to your radical reshaping of $his being.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, but manages to control $himself at length and begins to think. <<if canSee(getSlave($AS))>>Seeing<<else>>Finding<</if>> you, $he tries to ask you for instruction and realizes $he cannot. <<if hasBothLegs(getSlave($AS))>>After more thought, $he simply gets on $his knees facing away from you and spreads $his buttocks to offer $himself to you.<<else>>After more thought, $he simply rolls over facing away from you and winks $his asshole to offer $himself to you.<</if>> @@.hotpink;$He has become more submissive due to your radical reshaping of $his being.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, trying to scream or protest but only managing to cry. Eventually $he pulls $himself together. @@.mediumorchid;$He will struggle with $his medically created disability.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, trying to scream or protest but only managing to cry. Eventually $he pulls $himself together. @@.mediumorchid;$He will struggle with $his medically created disability.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<<else>>
-		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, trying to scream or protest but only managing to cry. As long as $he remains unbent to your will, $he will have a constant, inescapable reminder of who and what you are. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
+		$He clearly feels quite normal except for a vague ache around $his larynx. It takes a good while for $him to figure out what has happened, but eventually $he tries to speak and makes no sound. $He panics, trying to scream or protest but only managing to cry. As long as $he remains unbent to your will, $he will have a constant, inescapable reminder of who and what you are. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 50>>
 	<</if>>
 
@@ -1737,7 +1737,7 @@ As the remote surgery's long recovery cycle completes,
 			<<set getSlave($AS).devotion -= 5>>
 		<</if>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "restoreVoice">>
 	<<if getSlave($AS).fetish == "mindbroken">>
@@ -1752,31 +1752,31 @@ As the remote surgery's long recovery cycle completes,
 		The autosurgery instructed $him in no uncertain terms not to speak during recovery, so $he knows that $his voice has been restored. When you come in to check on $him late in the process, though, $he <<if canSee(getSlave($AS))>>stares at<<else>>faces<</if>> you for a long moment before turning away from you, towards the wall, and beginning to cry quietly. It seems $he's surprised that you would do this for $him, and does not understand how to deal with the situation. In any case, @@.hotpink;$he hates you far less@@ than $he once did.
 		<<set getSlave($AS).devotion += 10>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "voice">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		The autosurgery instructed $him in no uncertain terms not to speak during recovery, but $he fails to recall that. $He doesn't notice anything different. As with all surgery @@.red;$his health has been slightly affected.@@
+		The autosurgery instructed $him in no uncertain terms not to speak during recovery, but $he fails to recall that. $He doesn't notice anything different. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>>higher and more girly than it was before, and $he laughs at $himself as $he gets used to it. @@.hotpink;$He has become more submissive,@@ since this helps $him to think of $himself as a <<if getSlave($AS).voice == 3>>bimbo slut<<else>>real woman<</if>>. As with all surgery @@.red;$his health has been slightly affected.@@
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>>higher and more girly than it was before, and $he laughs at $himself as $he gets used to it. @@.hotpink;$He has become more submissive,@@ since this helps $him to think of $himself as a <<if getSlave($AS).voice == 3>>bimbo slut<<else>>real woman<</if>>. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>> higher and more girly than it was before, and $he laughs grimly at $himself as $he gets used to it. $He carries on regardless, accepting it as one more thing to learn to accept. As with all surgery @@.red;$his health has been slightly affected.@@
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>> higher and more girly than it was before, and $he laughs grimly at $himself as $he gets used to it. $He carries on regardless, accepting it as one more thing to learn to accept. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>> higher and more girly than it was before. For now, @@.mediumorchid;$he feels this <<if getSlave($AS).voice == 3>>bimbo<<else>>feminine<</if>> voice is not $hers, a cruel mockery.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 3>>far <</if>> higher and more girly than it was before. For now, @@.mediumorchid;$he feels this <<if getSlave($AS).voice == 3>>bimbo<<else>>feminine<</if>> voice is not $hers, a cruel mockery.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "voice2">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		The autosurgery instructed $him in no uncertain terms not to speak during recovery, but $he fails to recall that. $He doesn't notice anything different. As with all surgery @@.red;$his health has been slightly affected.@@
+		The autosurgery instructed $him in no uncertain terms not to speak during recovery, but $he fails to recall that. $He doesn't notice anything different. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before, and $he laughs at $himself as $he gets used to it. @@.hotpink;$He has become more dominant,@@ since this allows $him to think of $himself as <<if getSlave($AS).voice == 1>>an action movie hero<<elseif getSlave($AS).voice == 2>>a mature career woman<</if>>. As with all surgery @@.red;$his health has been slightly affected.@@
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before, and $he laughs at $himself as $he gets used to it. @@.hotpink;$He has become more dominant,@@ since this allows $him to think of $himself as <<if getSlave($AS).voice == 1>>an action movie hero<<elseif getSlave($AS).voice == 2>>a mature career woman<</if>>. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before, and $he laughs grimly at $himself as $he gets used to it. $He carries on regardless, accepting it as one more thing to learn to accept. As with all surgery @@.red;$his health has been slightly affected.@@
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before, and $he laughs grimly at $himself as $he gets used to it. $He carries on regardless, accepting it as one more thing to learn to accept. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before. For now, @@.mediumorchid;$he feels this <<if getSlave($AS).voice == 1>>gravelly<<elseif getSlave($AS).voice == 2>>subdued<</if>> voice is not $hers, a cruel mockery.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but it gains strength gradually. It comes out <<if getSlave($AS).voice == 1>>far <</if>> lower and more manly than it was before. For now, @@.mediumorchid;$he feels this <<if getSlave($AS).voice == 1>>gravelly<<elseif getSlave($AS).voice == 2>>subdued<</if>> voice is not $hers, a cruel mockery.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 	<</if>>
 
@@ -1786,159 +1786,159 @@ As the remote surgery's long recovery cycle completes,
 	<<else>>
 		Before surgery, $he was warned repeatedly not to try talking for a while, and $he obeys. When $he finally does, $his voice is raspy and weak, but as it gradually gains strength $he notices that it sounds more natural and human than $his old electrolarynx.
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "height">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery very slowly, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy for some time. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very slowly, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy for some time. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, but $he's happy with $his new body. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, but $he's happy with $his new body. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, but $he's accepted $his new body. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, but $he's accepted $his new body. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, and $he refuses to accept that this new body is really what $he's been condemned to. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He exits the surgery very cautiously, since $he's terribly sore and isn't yet used to the novel lengths of the legs $he walks on. $He'll be clumsy and hesitant for some time, and $he refuses to accept that this new body is really what $he's been condemned to. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "circumcision">>
 	<<if (getSlave($AS).dick > 0)>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. Since it is still there, $he doesn't understand what changed. Circumcision of an adult is not a trivial procedure, so @@.red;$his health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. Since it is still there, $he doesn't understand what changed. Circumcision of an adult is not a trivial procedure, so @@.health.dec;$his health has been slightly affected.@@
 		<<elseif (getSlave($AS).devotion > 50)>>
-			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, @@.hotpink;$he's very happy you'd take an interest in $his penis,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave. Circumcision of an adult is not a trivial procedure, so @@.red;$his health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, @@.hotpink;$he's very happy you'd take an interest in $his penis,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave. Circumcision of an adult is not a trivial procedure, so @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion > 20)>>
-			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his member. Circumcision of an adult is not a trivial procedure, so @@.red;$his health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his member. Circumcision of an adult is not a trivial procedure, so @@.health.dec;$his health has been slightly affected.@@
 		<<else>>
-			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own penis. Circumcision of an adult is not a trivial procedure, so @@.red;$his health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own penis. Circumcision of an adult is not a trivial procedure, so @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion -= 5>>
 		<</if>>
 	<<else>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			$He exits the surgery gingerly, since $he can feel that something was done to $his crotch. Since everything is still there, $he doesn't understand what changed. @@.red;$His health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that something was done to $his crotch. Since everything is still there, $he doesn't understand what changed. @@.health.dec;$his health has been slightly affected.@@
 		<<elseif (getSlave($AS).devotion > 50)>>
-			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, @@.hotpink;$he's very happy you'd take an interest in $his sex,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave. @@.red;$His health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, @@.hotpink;$he's very happy you'd take an interest in $his sex,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave. @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion > 20)>>
-			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind minor surgical alteration. @@.red;$His health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind minor surgical alteration. @@.health.dec;$his health has been slightly affected.@@
 		<<else>>
-			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his sex. @@.red;$His health has been slightly affected.@@
+			$He exits the surgery gingerly, since $he can feel that $his crotch was operated on. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $he's been circumcised, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his sex. @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion -= 5>>
 		<</if>>
 	<</if>>
 
 <<case "foreskinTuck">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. Since it is still there, $he doesn't understand what changed. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. Since it is still there, $he doesn't understand what changed. @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, @@.hotpink;$he's very happy you'd take an interest in $his penis,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave.<<if canAchieveErection(getSlave($AS))>> $He also can't wait <<if canSee(getSlave($AS))>>for the show during<<else>>to feel<</if>> $his next erection.<</if>> @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, @@.hotpink;$he's very happy you'd take an interest in $his penis,@@ and looks forward to having a slightly easier time keeping $himself clean through $his life as a sex slave.<<if canAchieveErection(getSlave($AS))>> $He also can't wait <<if canSee(getSlave($AS))>>for the show during<<else>>to feel<</if>> $his next erection.<</if>> @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his member. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, $he's flooded with relief. $He's so elated that $he hasn't been severely damaged that $he doesn't mind your taking surgical control of $his member. @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own penis. @@.red;$His health has been slightly affected.@@
+		$He exits the surgery gingerly, since $he can feel that something was done to $his dick. $He examines it carefully <<if canSee(getSlave($AS))>>in the mirror<<else>>by feeling alone<</if>>. Realizing that $his foreskin fits comfortably, $he's flooded with relief, since $he was afraid that you'd done something far more terrible. Once $he has time to think about it, though, $he's somewhat @@.mediumorchid;resentful,@@ since $he naturally feels a certain proprietary interest in $his own penis. @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "hips">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery very slowly, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very slowly, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, but $he's happy with $his new hips. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, but $he's happy with $his new hips. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, but $he's accepted $his new hips. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, but $he's accepted $his new hips. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, and $he refuses to accept that you are both willing and able to reshape $him down to $his very bones. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He exits the surgery very cautiously, since $he's still in considerable pain and isn't yet used to the way $his pelvis now fits together with the rest of $his body. $He'll be clumsy and hesitant for some time, and $he refuses to accept that you are both willing and able to reshape $him down to $his very bones. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "shoulders">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, but $he's happy with $his new shoulders. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, but $he's happy with $his new shoulders. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, but $he's accepted $his new shoulders. $He isn't much affected mentally. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, but $he's accepted $his new shoulders. $He isn't much affected mentally. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<else>>
-		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, and $he refuses to accept that you are both willing and able to reshape $him down to $his very bones. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He exits the surgery gingerly, since the changes of proportion in $his upper body have seriously affected $his sense of balance, albeit temporarily. $He'll be clumsy and hesitant for some time, and $he refuses to accept that you are both willing and able to reshape $him down to $his very bones. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "heels">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. A whorish pair of heels are slipped onto $him and $he is forced to $his feet to show that $he can indeed walk comfortably while wearing them. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. A whorish pair of heels are slipped onto $him and $he is forced to $his feet to show that $he can indeed walk comfortably while wearing them. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him and eagerly puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. $He struts back and forth experimentally and then offers <<if canSee(getSlave($AS))>>the mirror<<else>>you<</if>> $his asshole to check to see that $his ability to take a standing buttfuck hasn't been affected. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him and eagerly puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. $He struts back and forth experimentally and then offers <<if canSee(getSlave($AS))>>the mirror<<else>>you<</if>> $his asshole to check to see that $his ability to take a standing buttfuck hasn't been affected. @@.hotpink;$He has become more submissive due to your radical reshaping of $his body.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 20)>>
-		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him and obediently puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. After a while $he realizes that $he'll never walk without looking like a stripper again, but $he accepts it. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him and obediently puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. After a while $he realizes that $he'll never walk without looking like a stripper again, but $he accepts it. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him, but obstinately ignores them. After some time, $he realizes that the situation isn't changing and puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. $His <<if canSee(getSlave($AS))>>eyes are<<else>>face is<</if>> filled with hatred when $he <<if canSee(getSlave($AS))>>sees $his new and permanent appearance in the mirror<<else>>discovers this is the only way $he'll be able to stand now<</if>>. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He exits the surgery on all fours, unable to stand due to the changes to $his legs. $He finds a whorish pair of heels waiting for $him, but obstinately ignores them. After some time, $he realizes that the situation isn't changing and puts them on, gingerly standing to find that $he can indeed walk comfortably while wearing them. $His <<if canSee(getSlave($AS))>>eyes are<<else>>face is<</if>> filled with hatred when $he <<if canSee(getSlave($AS))>>sees $his new and permanent appearance in the mirror<<else>>discovers this is the only way $he'll be able to stand now<</if>>. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "heelsRestoration">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He obviously confused, but will get used to it soon enough. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He obviously confused, but will get used to it soon enough. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He's grinning like a fool the whole time. @@.hotpink;$He has become more obedient due to gratitude.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He's grinning like a fool the whole time. @@.hotpink;$He has become more obedient due to gratitude.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<else>>
-		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He obviously confused, finding it surprising that you would go to the expense and trouble of repairing $him. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He exits the surgery on $his own two feet. $He's still sore, but the modern surgery is fast and effective, and $he can use $his restored legs immediately. $He gingerly stands on one foot, then the other; and even takes a few little hops. $He obviously confused, finding it surprising that you would go to the expense and trouble of repairing $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<</if>>
 
 <<case "ampA1">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		You're there to help $him with the door as $he exits the surgery. $He smiles dumbly at your generosity, already seeming to forget $he ever had another arm. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		You're there to help $him with the door as $he exits the surgery. $He smiles dumbly at your generosity, already seeming to forget $he ever had another arm. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		You're there to help $him with the door as $he exits the surgery. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a short, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the stump that was once $his arm, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		You're there to help $him with the door as $he exits the surgery. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a short, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the stump that was once $his arm, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		You're there to help $him with the door as $he exits the surgery. Despite $his obedience, $he cries softly the whole time, the stump at $his shoulder occasionally moving as $he reflexively tries to grab at $himself and $his surroundings. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;immensely afraid@@ of your total power over $his body.
+		You're there to help $him with the door as $he exits the surgery. Despite $his obedience, $he cries softly the whole time, the stump at $his shoulder occasionally moving as $he reflexively tries to grab at $himself and $his surroundings. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;immensely afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 8, getSlave($AS).devotion -= 8>>
 	<<else>>
-		You're there to help $him with the door as $he exits the surgery. You had a good idea what $his reaction would be, so you've made sure to bind $his other arm<<if (hasAnyLegs(getSlave($AS)))>> and $his leg<<if (hasBothLegs(getSlave($AS)))>>s<</if>><</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>shoulder stump twitches pathetically with $his desperate efforts to move $his missing arm, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;incredibly and intensely terrified@@ of your total power over $his body.
+		You're there to help $him with the door as $he exits the surgery. You had a good idea what $his reaction would be, so you've made sure to bind $his other arm<<if (hasAnyLegs(getSlave($AS)))>> and $his leg<<if (hasBothLegs(getSlave($AS)))>>s<</if>><</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>shoulder stump twitches pathetically with $his desperate efforts to move $his missing arm, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;incredibly and intensely terrified@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<</if>>
 
 <<case "ampA2">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		You're there to open the door for $him as $he exits the surgery. $He smiles dumbly at your generosity, already seeming to forget the sensation of touching. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		You're there to open the door for $him as $he exits the surgery. $He smiles dumbly at your generosity, already seeming to forget the sensation of touching. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		You're there to open the door for $him as $he exits the surgery. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a short, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the two stumps that were once $his arms, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		You're there to open the door for $him as $he exits the surgery. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a short, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the two stumps that were once $his arms, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		You're there to open the door for $him as $he exits the surgery. Despite $his obedience, $he cries softly the whole time, $his shoulder stumps occasionally moving as $he reflexively tries to grab at $himself and $his surroundings. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;extremely afraid@@ of your total power over $his body.
+		You're there to open the door for $him as $he exits the surgery. Despite $his obedience, $he cries softly the whole time, $his shoulder stumps occasionally moving as $he reflexively tries to grab at $himself and $his surroundings. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;extremely afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 15>>
 	<<else>>
-		You're there to open the door for $him as $he exits the surgery. You had a good idea what $his reaction would be, so you've made sure to bind $his leg<<if (hasBothLegs(getSlave($AS)))>>s<</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>shoulder stumps twitch pathetically with $his desperate efforts to move $his arms, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;utterly and entirely terrified@@ of your total power over $his body.
+		You're there to open the door for $him as $he exits the surgery. You had a good idea what $his reaction would be, so you've made sure to bind $his leg<<if (hasBothLegs(getSlave($AS)))>>s<</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>shoulder stumps twitch pathetically with $his desperate efforts to move $his arms, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;utterly and entirely terrified@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 40, getSlave($AS).devotion -= 40>>
 	<</if>>
 
 <<case "ampL1">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. $He leans into you the entire time, already seeming to forget $he ever had another leg. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. $He leans into you the entire time, already seeming to forget $he ever had another leg. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. $He knows what a slave's life is, but $he did not really expect that it would involve this. After a brief, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the stump that was once $his leg, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. $He knows what a slave's life is, but $he did not really expect that it would involve this. After a brief, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the stump that was once $his leg, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. Despite $his obedience, $he cries softly the whole time, the stump at $his hip occasionally moving as $he reflexively tries to use it somehow. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;immensely afraid@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery by $himself; you had to walk alongside $him. Despite $his obedience, $he cries softly the whole time, the stump at $his hip occasionally moving as $he reflexively tries to use it somehow. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;immensely afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<<else>>
-		Of course, $he could not walk out of the surgery by $himself; you had to carry $him. You had a good idea what $his reaction would be, so you've made sure to bind $his other leg<<if (hasAnyArms(getSlave($AS)))>> and $his arm<<if (hasBothArms(getSlave($AS)))>>s<</if>><</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>leg stump twitches pathetically with $his desperate efforts to move $his missing leg, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;incredibly and intensely terrified@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery by $himself; you had to carry $him. You had a good idea what $his reaction would be, so you've made sure to bind $his other leg<<if (hasAnyArms(getSlave($AS)))>> and $his arm<<if (hasBothArms(getSlave($AS)))>>s<</if>><</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>leg stump twitches pathetically with $his desperate efforts to move $his missing leg, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;incredibly and intensely terrified@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 15, getSlave($AS).devotion -= 15>>
 	<</if>>
 
 <<case "ampL2">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Of course, $he could not walk out of the surgery; you carried $him. $He holds onto you the entire time, already seeming to forget the sensation of walking. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He holds onto you the entire time, already seeming to forget the sensation of walking. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would involve this. After a brief, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the two stumps that were once $his legs, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would involve this. After a brief, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> the two stumps that were once $his legs, $he squares $his shoulders and resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, $his hip stumps occasionally moving as $he reflexively tries to stand up and walk around. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;extremely afraid@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, $his hip stumps occasionally moving as $he reflexively tries to stand up and walk around. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;extremely afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<<else>>
-		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so you've made sure to bind $his arm<<if (hasBothArms(getSlave($AS)))>>s<</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>hip stumps twitch pathetically with $his desperate efforts to move $his legs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;utterly and entirely terrified@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so you've made sure to bind $his arm<<if (hasBothArms(getSlave($AS)))>>s<</if>> to prevent $him from trying to attack you, and <<if (getSlave($AS).teeth == "removable")>>remove $his teeth<<else>>muzzle $him<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>hip stumps twitch pathetically with $his desperate efforts to move $his legs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;utterly and entirely terrified@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 30, getSlave($AS).devotion -= 30>>
 	<</if>>
 
@@ -1955,14 +1955,14 @@ As the remote surgery's long recovery cycle completes,
 	/*
 <<case "bla">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time, trying to move the arms and legs $he now lacks. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time, trying to move the arms and legs $he now lacks. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a long, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> $his limbless torso, $he squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. After a long, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> $his limbless torso, $he squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he will never do again. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he will never do again. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;cripplingly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 20, getSlave($AS).devotion -= 20>>
 	<<else>>
-		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so <<if (getSlave($AS).teeth == "removable")>>you removed $his teeth<<else>>$he's muzzled<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>stumps twitch pathetically with $his desperate efforts to move $his limbs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terrified to the point of insanity@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so <<if (getSlave($AS).teeth == "removable")>>you removed $his teeth<<else>>$he's muzzled<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>stumps twitch pathetically with $his desperate efforts to move $his limbs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terrified to the point of insanity@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 50, getSlave($AS).devotion -= 50>>
 	<</if>>
 
@@ -1979,14 +1979,14 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "amp2">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time trying to move the arms and legs $he now lacks, never noticing the ports in $his stumps. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time trying to move the arms and legs $he now lacks, never noticing the ports in $his stumps. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. Anesthesia has yet to completely wear off so it takes $him a while to notice ports in $his stumps. When $he does, $he visibly relaxes, realizing $he may yet walk again. $He squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect that it would ever come to this for $him. Anesthesia has yet to completely wear off so it takes $him a while to notice ports in $his stumps. When $he does, $he visibly relaxes, realizing $he may yet walk again. $He squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he will never do again. $He slightly calms down when $he <<if canSee(getSlave($AS))>>sees<<else>>feels<</if>> ports installed in $his stumps and realizes $his immobility is not permanent. Even so, $he is still @@.gold;terrified@@ of your total power over $his body. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he will never do again. $He slightly calms down when $he <<if canSee(getSlave($AS))>>sees<<else>>feels<</if>> ports installed in $his stumps and realizes $his immobility is not permanent. Even so, $he is still @@.gold;terrified@@ of your total power over $his body. @@.mediumorchid;$He will struggle greatly with $his medically created disability.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<<else>>
-		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so <<if (getSlave($AS).teeth == "removable")>>you removed $his teeth<<else>>$he's muzzled<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>stumps twitch pathetically with $his desperate efforts to move $his limbs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.red;$his health has been greatly affected.@@ $He is @@.gold;terrified to the point of insanity@@ of your total power over $his body.
+		Of course, $he could not walk out of the surgery; you carried $him. You had a good idea what $his reaction would be, so <<if (getSlave($AS).teeth == "removable")>>you removed $his teeth<<else>>$he's muzzled<</if>> to prevent $him from trying to bite. $He sobs convulsively, and $his <<if canSee(getSlave($AS))>>eyes dart desperately from side to side through $his tears, hopelessly imploring the mirror to show $him something other than this<<else>>stumps twitch pathetically with $his desperate efforts to move $his limbs, to prove there is something other than this<</if>>. Anything other than this. @@.mediumorchid;The surgical invasion has filled $him with horror and anger.@@ Since the surgery was invasive, @@.health.dec;$his health has been greatly affected.@@ $He is @@.gold;terrified to the point of insanity@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 50, getSlave($AS).devotion -= 50>>
 	<</if>>
 
@@ -1997,7 +1997,7 @@ As the remote surgery's long recovery cycle completes,
 	*/
 
 <<case "PLimb interface1">>
-	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. Recovery will be @@.red;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve impulse detectors constituted major surgery.
+	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. Recovery will be @@.health.dec;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve impulse detectors constituted major surgery.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if getSlave($AS).devotion > 20>>
 			Nevertheless, $he's @@.hotpink;overwhelmed with gratitude,@@ and thanks you profusely the first chance $he gets. $He follows the acclimation program diligently, doing $his best to learn how to be a good slave despite, or sometimes even because of, $his disability. $He @@.mediumaquamarine;places more trust in you,@@ too, since you obviously have $his best interests at heart.
@@ -2016,7 +2016,7 @@ As the remote surgery's long recovery cycle completes,
 	<<set $nextLink = "Remote Surgery">>
 
 <<case "PLimb interface2">>
-	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. $His stumps twitch slightly as the software begins configuring. Recovery will be @@.red;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve bridges constituted major surgery.
+	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. $His stumps twitch slightly as the software begins configuring. Recovery will be @@.health.dec;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve bridges constituted major surgery.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if getSlave($AS).devotion > 20>>
 			Nevertheless, $he's @@.hotpink;overwhelmed with gratitude,@@ and thanks you profusely the first chance $he gets. $He follows the acclimation program diligently, doing $his best to learn how to be a good slave despite, or sometimes even because of, $his disability. $He @@.mediumaquamarine;places more trust in you,@@ too, since you obviously have $his best interests at heart.
@@ -2035,7 +2035,7 @@ As the remote surgery's long recovery cycle completes,
 	<<set $nextLink = "Remote Surgery">>
 
 <<case "PLimb interface3">>
-	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. $His stumps twitch slightly as the software begins configuring. Since $he already had anchors installed in previous surgery this procedure was less invasive and thus @@.red;$his health has been only slightly affected.@@
+	When $he is carried out of surgery $he <<if canSee(getSlave($AS))>>cranes $his neck to better see the ports<<else>>wiggles $his stumps trying to feel the ports<</if>> installed in $his stumps. $His stumps twitch slightly as the software begins configuring. Since $he already had anchors installed in previous surgery this procedure was less invasive and thus @@.health.dec;$his health has been only slightly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if getSlave($AS).devotion > 20>>
 			$He is @@.hotpink;overjoyed@@ when $he finds out this upgrade will allow $him to //feel// with $his limbs again and thanks you profusely the first chance $he gets. $He @@.mediumaquamarine;places more trust in you,@@ too, since you obviously have $his best interests at heart.
@@ -2055,7 +2055,7 @@ As the remote surgery's long recovery cycle completes,
 
 /* This was moved to prostheticsConfig.tw
 <<case "basicPLimbs">>
-	$He exits the surgery hesitantly, the software of $his prosthetic limbs and the wetware of $his brain working together to figure out how to walk together under the tutelage of a prescribed tutorial. Recovery will be @@.red;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve impulse detectors constituted major surgery.
+	$He exits the surgery hesitantly, the software of $his prosthetic limbs and the wetware of $his brain working together to figure out how to walk together under the tutelage of a prescribed tutorial. Recovery will be @@.health.dec;significant,@@ since the surgical implantation of anchor points for the limbs themselves and the installation of nerve impulse detectors constituted major surgery.
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if getSlave($AS).devotion > 20>>
 			Nevertheless, $he's @@.hotpink;overwhelmed with gratitude,@@ and thanks you profusely the first chance $he gets. $He follows the acclimation program diligently, doing $his best to learn how to be a good slave despite, or sometimes even because of, $his artificial arms and legs. $He @@.mediumaquamarine;places more trust in you,@@ too, since you obviously have $his best interests at heart.
@@ -2131,83 +2131,83 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "removeLimbs">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time trying to move the arms and legs that $he used to have. As with all surgery @@.red;$his health has been slightly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He squirms the entire time trying to move the arms and legs that $he used to have. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect $his artificial limbs would be removed again so suddenly. After a long, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> $his limbless torso, $he squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. As $he was already amputated, there was no lasting effect. As with all surgery @@.red;$his health has been slightly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. $He knows what a slave's life is, but $he did not really expect $his artificial limbs would be removed again so suddenly. After a long, silent <<if canSee(getSlave($AS))>>stare at<<else>>consideration of<</if>> $his limbless torso, $he squares $his shoulders and visibly resolves to carry on being a good slave as best $he can. As $he was already amputated, there was no lasting effect. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 20)>>
-		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he had to learn to do again. Your removal of $his prosthetic limbs has caused @@.mediumorchid;$his devotion to drop@@ and @@.gold;$his trust to drop.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. Despite $his obedience, $he cries softly the whole time, shoulder and hip stumps moving occasionally as $he reflexively tries to stand — to walk — to touch $himself — to gesture — all things that $he had to learn to do again. Your removal of $his prosthetic limbs has caused @@.mediumorchid;$his devotion to drop@@ and @@.gold;$his trust to drop.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<<else>>
-		Of course, $he could not walk out of the surgery; you carried $him. It seems that $his mistrust of you was well founded and this removal of $his artificial limbs has caused @@.mediumorchid;$his devotion to drop@@ and @@.gold;$his trust to drop.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		Of course, $he could not walk out of the surgery; you carried $him. It seems that $his mistrust of you was well founded and this removal of $his artificial limbs has caused @@.mediumorchid;$his devotion to drop@@ and @@.gold;$his trust to drop.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 */
 
 <<case "ster">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to know that $he'll never have a child now. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to know that $he'll never have a child now. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have a child. $He is @@.mediumorchid;filled with grief@@ whenever $he thinks about the fact that feeling a life growing within $him is now, finally and forever, beyond $him. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have a child. $He is @@.mediumorchid;filled with grief@@ whenever $he thinks about the fact that feeling a life growing within $him is now, finally and forever, beyond $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 20>>
 	<<elseif (getSlave($AS).energy > 95)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have to worry about a pussyfuck getting $him pregnant again. As far as $he's concerned, easier, less worrisome vaginal intercourse is a @@.hotpink;good thing;@@ one less thing to keep in mind when a guy blows his load inside $him. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have to worry about a pussyfuck getting $him pregnant again. As far as $he's concerned, easier, less worrisome vaginal intercourse is a @@.hotpink;good thing;@@ one less thing to keep in mind when a guy blows his load inside $him. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have to worry about a pregnancy, ever again. $He has many rules and duties to worry about as a slave, so $he's @@.hotpink;grateful@@ that $he has one less thing to worry about as $he serves you. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never have to worry about a pregnancy, ever again. $He has many rules and duties to worry about as a slave, so $he's @@.hotpink;grateful@@ that $he has one less thing to worry about as $he serves you. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never get pregnant. $He understands the realities of $his life as a slave, and that a traditional life with a child was never going to happen for $him anyway, so it isn't much of a shock. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never get pregnant. $He understands the realities of $his life as a slave, and that a traditional life with a child was never going to happen for $him anyway, so it isn't much of a shock. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never get pregnant. $He does not understand the realities of $his life as a slave at a core level, so despite the fact that $he was never going to have a family, fertile or not, $he's @@.mediumorchid;resentful@@ of the surgical invasion. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he'll never get pregnant. $He does not understand the realities of $his life as a slave at a core level, so despite the fact that $he was never going to have a family, fertile or not, $he's @@.mediumorchid;resentful@@ of the surgical invasion. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "fert">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance to get pregnant. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance to get pregnant. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif ((getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)) || getSlave($AS).origin == "$He sold $himself to you in the hope of someday bearing children.">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant. $He is @@.hotpink;filled with joy@@ whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant. $He is @@.hotpink;filled with joy@@ whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He's so pleased that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant. $He's @@.hotpink;grateful@@ that you think $him worthy of breeding, and even a little nervous about how $he'll perform as a breeder. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he now has a chance to get pregnant. $He's @@.hotpink;grateful@@ that you think $him worthy of breeding, and even a little nervous about how $he'll perform as a breeder. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated. $He understands the realities of $his life as a slave, and that being bred was a possibility, so it isn't much of a shock. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated. $He understands the realities of $his life as a slave, and that being bred was a possibility, so it isn't much of a shock. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he'll be bred. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to know that it means $he may now be impregnated. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he'll be bred. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "addOvaries">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance to get pregnant. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance to get pregnant. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif ((getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)) || getSlave($AS).origin == "$He sold $himself to you in the hope of someday bearing children.">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he now has a chance to get pregnant. $He is filled with joy whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He now places @@.mediumaquamarine;total faith@@ in your plans for $his body and @@.hotpink;adores you@@ for giving $him a gift $he once thought impossible. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he now has a chance to get pregnant. $He is filled with joy whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He now places @@.mediumaquamarine;total faith@@ in your plans for $his body and @@.hotpink;adores you@@ for giving $him a gift $he once thought impossible. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 10, getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "asexualReproOvaries">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he is now self-impregnating. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he is now self-impregnating. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to suspect that something has critically changed about $his reproductive system. $He is @@.mediumaquamarine;pleased@@ that you would go out of your way to alter $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to suspect that something has critically changed about $his reproductive system. $He is @@.mediumaquamarine;pleased@@ that you would go out of your way to alter $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to shudder at the thought of just what could have possibly been put into $him. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to shudder at the thought of just what could have possibly been put into $him. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to dread the discovery of what now resides inside $him. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to dread the discovery of what now resides inside $him. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 	<<if isFertile(getSlave($AS)) && getSlave($AS).preg == 0>>
@@ -2217,36 +2217,36 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "addAnimalOvaries">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance of carrying an animal baby to term. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but lacks the mental faculties to realize that $he now has a chance of carrying an animal baby to term. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif ((getSlave($AS).fetish == "pregnancy") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)) || getSlave($AS).origin == "$He sold $himself to you in the hope of someday bearing children.">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he now has a chance to get pregnant. $He is filled with joy whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He now places @@.mediumaquamarine;total faith@@ in your plans for $his body and @@.hotpink;adores you@@ for giving $him a gift $he once thought impossible. $He doesn't realize exactly what the surgery entailed, however — the happiness $he felt at first will most likely be replaced with horror once $he realizes the babies in $his womb are not human. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he now has a chance to get pregnant. $He is filled with joy whenever $he thinks about the fact that feeling a life growing within $him is now, at last, a possibility. $He now places @@.mediumaquamarine;total faith@@ in your plans for $his body and @@.hotpink;adores you@@ for giving $him a gift $he once thought impossible. $He doesn't realize exactly what the surgery entailed, however — the happiness $he felt at first will most likely be replaced with horror once $he realizes the babies in $his womb are not human. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 10, getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he has managed to catch enough slave rumors about what the organ farm can do to know that it means $he is now internally indistinguishable from a natural, unaltered woman — at least, that's what $he believes. Little does $he know that $he is now capable of carrying animal babies in $his womb. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "addTesticles">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.red;$his health has been slightly affected.@@
+		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
 		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the results. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future.
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.red;$his health has been slightly affected.@@
+		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.red;$his health has been slightly affected.@@ $He doesn't have to know exactly what you did, though, to be @@.gold;mortified@@ and @@.mediumorchid;infuriated@@ by your messing around inside $his body. $He hasn't yet learned to accept that you control $him completely, down to the arrangement and even presence of $his internal organs.
+		It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a nonspecific ache in $his abdomen. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He doesn't have to know exactly what you did, though, to be @@.gold;mortified@@ and @@.mediumorchid;infuriated@@ by your messing around inside $his body. $He hasn't yet learned to accept that you control $him completely, down to the arrangement and even presence of $his internal organs.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 10>>
 	<</if>>
 
 <<case "addProstate">>
-	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower parts. As with all surgery @@.red;$his health has been slightly affected.@@
+	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower parts. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<if getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0>>
 		<<if (getSlave($AS).devotion > 50)>>
 			<<if getSlave($AS).anus == 0>>
@@ -2276,44 +2276,44 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "addDick">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he strains to focus on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $His eyes open wide as $he takes in the new member between $his legs. Suddenly, it twitches for the first time. The mixed pain of the terribly sore area and pleasure of new sensations floods $him, and $he jerks against the surgical restraints, gasping for breath. @@.red;$His health has been severely affected@@ by the intrusive surgery.
+		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he strains to focus on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $His eyes open wide as $he takes in the new member between $his legs. Suddenly, it twitches for the first time. The mixed pain of the terribly sore area and pleasure of new sensations floods $him, and $he jerks against the surgical restraints, gasping for breath. @@.health.dec;$his health has been severely affected@@ by the intrusive surgery.
 	<<elseif (getSlave($AS).devotion > 50)>>
-		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he strains to focus on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His eyes open wide as $he takes in the new member between $his legs. Suddenly, it twitches for the first time. The mixed pain of the terribly sore area and pleasure of new sensations floods $him, and $he jerks against the surgical restraints, gasping for breath. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. @@.red;$His health has been severely affected@@ by the intrusive surgery.
+		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he strains to focus on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His eyes open wide as $he takes in the new member between $his legs. Suddenly, it twitches for the first time. The mixed pain of the terribly sore area and pleasure of new sensations floods $him, and $he jerks against the surgical restraints, gasping for breath. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. @@.health.dec;$his health has been severely affected@@ by the intrusive surgery.
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he focuses on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $He slowly becomes aware of the soft new member resting between $his legs. $He has a penis. The thought process is visible on $his face as the fact sinks in. $He has a penis! Unsure what to make of this unbelievable development, $he <<if canSee(getSlave($AS))>>turns $his gaze away from the mirror and<</if>> shuts $his eyes tightly. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. @@.red;$His health has been severely affected@@ by the intrusive surgery.
+		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he focuses on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $He slowly becomes aware of the soft new member resting between $his legs. $He has a penis. The thought process is visible on $his face as the fact sinks in. $He has a penis! Unsure what to make of this unbelievable development, $he <<if canSee(getSlave($AS))>>turns $his gaze away from the mirror and<</if>> shuts $his eyes tightly. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. @@.health.dec;$his health has been severely affected@@ by the intrusive surgery.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he focuses on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $He slowly becomes aware of the soft new member resting between $his legs. $He has a penis. The thought process is visible on $his face as the fact sinks in. $He has a penis! <<if getSlave($AS).voice == 0>>$He tries to scream, but as a mute all $he can manage is pathetic, silent thrashing against the surgical restraints.<<else>>A huge scream rises out of $him, going on and on, until $he pauses to gasp for breath so $he can go on screaming.<</if>> $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. @@.red;$His health has been severely affected@@ by the intrusive surgery.
+		The surgery is invasive, and $he spends some time in the autosurgery, slowly recovering. As feeling slowly returns to the lower half of $his body and $his thoughts coalesce against the dissipating fog of surgical pharmacology, $he focuses on $his groin<<if canSee(getSlave($AS))>>, visible in a ceiling mirror above $him<</if>>. $He slowly becomes aware of the soft new member resting between $his legs. $He has a penis. The thought process is visible on $his face as the fact sinks in. $He has a penis! <<if getSlave($AS).voice == 0>>$He tries to scream, but as a mute all $he can manage is pathetic, silent thrashing against the surgical restraints.<<else>>A huge scream rises out of $him, going on and on, until $he pauses to gasp for breath so $he can go on screaming.<</if>> $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. @@.health.dec;$his health has been severely affected@@ by the intrusive surgery.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "addBalls">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he struggles to understand what just happened. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he struggles to understand what just happened. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he begins to giggle. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he begins to giggle. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $He cannot quite feel what has happened to $him yet, but $he does not have long to wait. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, the rush of new hormones clashes with $his already roiling emotions, and $he begins to sob. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $He cannot quite feel what has happened to $him yet, but $he does not have long to wait. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, the rush of new hormones clashes with $his already roiling emotions, and $he begins to sob. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		The autosurgery allows $him to recover for a while. Once it's safe, though, it's time to test the function of $his new organs by seeing if they permit $him to achieve erection. The surgery slowly inserts a dildo into $his anus; the slave is so fuzzy from the surgery and accompanying drugs that it takes a while for the machine assfuck to register. Gradually, though, $his new dick becomes hard. Horrified, $he cannot take $his <<if canSee(getSlave($AS))>>eyes off $his own reflection in the ceiling mirror<<else>>mind off $his soft cock<</if>> as it bobs and waves with the sodomy. A delayed reaction sets in as the soreness of surgical recovery competes with the stimulation: <<if getSlave($AS).voice == 0>>$he tries to scream, but only manages to gasp repeatedly<<else>>$he howls with pain and terror<</if>> as the dildo forces a weak prostate orgasm. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.red;$his health has been slightly affected.@@
+		The autosurgery allows $him to recover for a while. Once it's safe, though, it's time to test the function of $his new organs by seeing if they permit $him to achieve erection. The surgery slowly inserts a dildo into $his anus; the slave is so fuzzy from the surgery and accompanying drugs that it takes a while for the machine assfuck to register. Gradually, though, $his new dick becomes hard. Horrified, $he cannot take $his <<if canSee(getSlave($AS))>>eyes off $his own reflection in the ceiling mirror<<else>>mind off $his soft cock<</if>> as it bobs and waves with the sodomy. A delayed reaction sets in as the soreness of surgical recovery competes with the stimulation: <<if getSlave($AS).voice == 0>>$he tries to scream, but only manages to gasp repeatedly<<else>>$he howls with pain and terror<</if>> as the dildo forces a weak prostate orgasm. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
 /* TODO: this will need a rewrite */
 <<case "addAnimalBalls">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he struggles to understand what just happened. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he struggles to understand what just happened. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he begins to giggle. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. As a devoted slave, $he knew the essentials of the surgery before it was performed, so $he's excited to <<if canSee(getSlave($AS))>>see<<else>>feel<</if>> the result. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, $he begins to giggle. $He is @@.mediumaquamarine;tremendously impressed@@ that you would devote such immense resources to altering $his body, and is more willing than ever to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 5, getSlave($AS).devotion += 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $He cannot quite feel what has happened to $him yet, but $he does not have long to wait. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, the rush of new hormones clashes with $his already roiling emotions, and $he begins to sob. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He lies back in the surgical chair<<if canSee(getSlave($AS))>>, gazing at $himself in the ceiling mirror<</if>> as the fog of anesthetics lifts and feeling returns to $his lower half. $He cannot quite feel what has happened to $him yet, but $he does not have long to wait. $His new testicles are small, and $his scrotum is scarcely visible under $his dick, but its impact becomes immediately clear. $He slowly achieves an excruciatingly painful erection, panting with the pain as $his very sore member becomes hard. The terrible overstimulation brings a spurt of cum jetting out of $his cockhead. As the agony melts away, the rush of new hormones clashes with $his already roiling emotions, and $he begins to sob. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, so much so that $he is now more willing to @@.hotpink;submit to your plans@@ for $his future. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion += 5>>
 	<<else>>
-		The autosurgery allows $him to recover for a while. Once it's safe, though, it's time to test the function of $his new organs by seeing if they permit $him to achieve erection. The surgery slowly inserts a dildo into $his anus; the slave is so fuzzy from the surgery and accompanying drugs that it takes a while for the machine assfuck to register. Gradually, though, $his new dick becomes hard. Horrified, $he cannot take $his <<if canSee(getSlave($AS))>>eyes off $his own reflection in the ceiling mirror<<else>>mind off $his soft cock<</if>> as it bobs and waves with the sodomy. A delayed reaction sets in as the soreness of surgical recovery competes with the stimulation: <<if getSlave($AS).voice == 0>>$he tries to scream, but only manages to gasp repeatedly<<else>>$he howls with pain and terror<</if>> as the dildo forces a weak prostate orgasm. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.red;$his health has been slightly affected.@@
+		The autosurgery allows $him to recover for a while. Once it's safe, though, it's time to test the function of $his new organs by seeing if they permit $him to achieve erection. The surgery slowly inserts a dildo into $his anus; the slave is so fuzzy from the surgery and accompanying drugs that it takes a while for the machine assfuck to register. Gradually, though, $his new dick becomes hard. Horrified, $he cannot take $his <<if canSee(getSlave($AS))>>eyes off $his own reflection in the ceiling mirror<<else>>mind off $his soft cock<</if>> as it bobs and waves with the sodomy. A delayed reaction sets in as the soreness of surgical recovery competes with the stimulation: <<if getSlave($AS).voice == 0>>$he tries to scream, but only manages to gasp repeatedly<<else>>$he howls with pain and terror<</if>> as the dildo forces a weak prostate orgasm. $He is @@.gold;terrified@@ of your apparently untrammeled power over $his body, and @@.mediumorchid;furious@@ at $his lack of control over $his own person. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
@@ -2331,7 +2331,7 @@ As the remote surgery's long recovery cycle completes,
 		shocked and appalled to find that $he has a ballsack. $He <<if canSee(getSlave($AS))>>stares at $his newly visible<<else>>feels $his new<</if>> balls in horror; it's obvious $he thought they were out of sight, and out of mind. $He checks $himself over carefully, and then bursts into tears, seemingly feeling that even if $he has a ballsack, $his cock is still a slave's bitchclit, and $his asshole is still a slut's asspussy. $He's @@.gold;frightened of your ability to modify $him,@@ and @@.mediumorchid;resentful@@ of how you use it.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "addForeskin">>
 	<<if getSlave($AS).dick > 0>>
@@ -2363,31 +2363,31 @@ As the remote surgery's long recovery cycle completes,
 			<<set getSlave($AS).trust -= 5>>
 		<</if>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "anus">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with a terribly sore rear end and little desire to mess with it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore rear end and little desire to mess with it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).fetish == "buttslut") && (getSlave($AS).fetishStrength > 60) && (getSlave($AS).fetishKnown == 1)>>
-		$He leaves the surgery with a terribly sore rear end. $He is @@.hotpink;filled with joy@@ at the prospect of having a tight butt all over again, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore rear end. $He is @@.hotpink;filled with joy@@ at the prospect of having a tight butt all over again, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 		<<if getSlave($AS).skill.anal > 10>>
 			<<set getSlave($AS).skill.anal -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with a terribly sore rear end. $He's a bit anxious at the prospect of the pain having to get back to a loose asshole the hard way, but $he's @@.hotpink;happy@@ that you think $him worth the effort of surgical improvement. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore rear end. $He's a bit anxious at the prospect of the pain having to get back to a loose asshole the hard way, but $he's @@.hotpink;happy@@ that you think $him worth the effort of surgical improvement. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 		<<if getSlave($AS).skill.anal > 10>>
 			<<set getSlave($AS).skill.anal -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with a terribly sore rear end. $He knows $he'll have to endure the pain of being fucked in a tight asshole again soon enough, but trepidation is nothing new for $him. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with a terribly sore rear end. $He knows $he'll have to endure the pain of being fucked in a tight asshole again soon enough, but trepidation is nothing new for $him. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<if getSlave($AS).skill.anal > 10>>
 			<<set getSlave($AS).skill.anal -= 10>>
 		<</if>>
 	<<else>>
-		$He leaves the surgery with a terribly sore rear end. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his rear end, and knows that this means that $he'll have to take the pain of sodomy in a tight ass all over again. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
+		$He leaves the surgery with a terribly sore rear end. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his rear end, and knows that this means that $he'll have to take the pain of sodomy in a tight ass all over again. If $he had much in the way of anal skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<<if getSlave($AS).skill.anal > 10>>
 			<<set getSlave($AS).skill.anal -= 10>>
@@ -2396,88 +2396,88 @@ As the remote surgery's long recovery cycle completes,
 
 <<case "labiaplasty">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).energy > 95)>>
-		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $he's already anticipating recovering enough to feel your cockhead pressing past $his new lips, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $he's already anticipating recovering enough to feel your cockhead pressing past $his new lips, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4>>
 		<<set getSlave($AS).devotion += 10>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement, and a little excited at the idea that $he now has what you consider good-looking pussylips. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement, and a little excited at the idea that $he now has what you consider good-looking pussylips. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
+		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
 		<<set getSlave($AS).trust -= 10>>
 		<<set getSlave($AS).devotion -= 5>>
 	<</if>>
 
 <<case "clitoral reduction">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).energy > 95)>>
-		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $his sensitivity has been reduced, @@.red;reducing $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $his sensitivity has been reduced, @@.libido.dec;reducing $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 		<<set getSlave($AS).energy -= 5>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement, although $his sensitivity has been reduced, @@.red;reducing $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement, although $his sensitivity has been reduced, @@.libido.dec;reducing $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 		<<set getSlave($AS).energy -= 5>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. The reduced sensitivity has @@.red;noticeably reduced $his sex drive.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. The reduced sensitivity has @@.libido.dec;noticeably reduced $his sex drive.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<set getSlave($AS).energy -= 10>>
 	<<else>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today and @@.red;$his sex drive takes a heavy hit..@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
+		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today and @@.libido.dec;$his sex drive takes a heavy hit..@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<<set getSlave($AS).energy -= 20>>
 	<</if>>
 
 <<case "clitoral enlargement">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).energy > 95)>>
-		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $his sensitivity has been increased, @@.green;raising $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the thought that $his pussy will now be more appealing to $his <<= getWrittenTitle(getSlave($AS))>>; $his sensitivity has been increased, @@.libido.inc;raising $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 		<<set getSlave($AS).energy += 1>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement; $his sensitivity has been increased, @@.green;raising $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $his pussy worth the effort of surgical improvement; $his sensitivity has been increased, @@.libido.inc;raising $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 		<<set getSlave($AS).energy += 1>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. $His sensitivity has been increased, @@.green;raising $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. $His sensitivity has been increased, @@.libido.inc;raising $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<set getSlave($AS).energy += 1>>
 	<<else>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. $His sensitivity has been increased, @@.green;raising $his sex drive slightly.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
+		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. $His sensitivity has been increased, @@.libido.inc;raising $his sex drive slightly.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<<set getSlave($AS).energy += 1>>
 	<</if>>
 
 <<case "vagina">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).energy > 95)>>
-		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the prospect of having a tight cunt again, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He is @@.hotpink;filled with joy@@ at the prospect of having a tight cunt again, so much so that $he now @@.mediumaquamarine;trusts@@ your plans for $his body. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).trust += 4, getSlave($AS).devotion += 10>>
 		<<if getSlave($AS).skill.vaginal > 10>>
 			<<set getSlave($AS).skill.vaginal -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $him worth the effort of surgical improvement, and a little excited to feel like $he's a girl again. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with a terribly sore pussy. $He's @@.hotpink;happy@@ that you think $him worth the effort of surgical improvement, and a little excited to feel like $he's a girl again. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 		<<if getSlave($AS).skill.vaginal > 10>>
 			<<set getSlave($AS).skill.vaginal -= 10>>
 		<</if>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with a terribly sore pussy. $He's somewhat revolted by the surgery, but as far as $he's concerned, the short recovery period will mean a time during which $he can be sure no one will use $his pussy. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<if getSlave($AS).skill.vaginal > 10>>
 			<<set getSlave($AS).skill.vaginal -= 10>>
 		<</if>>
 	<<else>>
-		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
+		$He leaves the surgery with a terribly sore pussy. $He's @@.mediumorchid;horrified@@ at surgical alteration of $his womanhood; this is probably more of an invasion than $he could readily imagine before today. If $he had much in the way of vanilla sex skills, @@.red;they've likely suffered.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;terribly afraid@@ of your total power over $his most intimate parts.
 		<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<<if getSlave($AS).skill.vaginal > 10>>
 			<<set getSlave($AS).skill.vaginal -= 10>>
@@ -2487,32 +2487,32 @@ As the remote surgery's long recovery cycle completes,
 <<case "bellyscar">>
 	$He leaves the surgery with a soreness on $his lower abdomen,
 	<<if getSlave($AS).fetish == "mindbroken">>
-		and finds $his c-section scar has been surgically removed. As with all surgery @@.red;$his health has been slightly affected.@@
+		and finds $his c-section scar has been surgically removed. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		and is @@.hotpink;pleased@@ to find $his c-section scar has been surgically removed. $He's happy that $he'll be able to show off $his <<if getSlave($AS).weight > 95>>big soft belly<<elseif getSlave($AS).weight > 30>>soft belly<<elseif getSlave($AS).muscles > 30>>muscular belly<<elseif getSlave($AS).muscles > 5>>firm, ripped belly <<else>>firm, flat belly<</if>> without being self conscious about $his scar. As with all surgery @@.red;$his health has been slightly affected.@@
+		and is @@.hotpink;pleased@@ to find $his c-section scar has been surgically removed. $He's happy that $he'll be able to show off $his <<if getSlave($AS).weight > 95>>big soft belly<<elseif getSlave($AS).weight > 30>>soft belly<<elseif getSlave($AS).muscles > 30>>muscular belly<<elseif getSlave($AS).muscles > 5>>firm, ripped belly <<else>>firm, flat belly<</if>> without being self conscious about $his scar. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 2>>
 	<</if>>
 
 <<case "tummyTuck">>
 	$He leaves the surgery with a soreness on $his lower abdomen,
 	<<if getSlave($AS).fetish == "mindbroken">>
-		and finds $his belly isn't so saggy anymore. As with all surgery @@.red;$his health has been slightly affected.@@
+		and finds $his belly isn't so saggy anymore. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<else>>
-		and is @@.hotpink;pleased@@ to find it's no longer saggy. $He's happy that $he'll be able to show off $his <<if getSlave($AS).weight > 95>>big soft belly<<if getSlave($AS).weight > 130>>, for awhile at least,<</if>><<elseif getSlave($AS).weight > 30>>soft belly<<elseif getSlave($AS).muscles > 30>>muscular belly<<elseif getSlave($AS).muscles > 5>>once again firm, ripped belly <<else>>once again firm, flat belly<</if>> without being self conscious about it. As with all surgery @@.red;$his health has been affected.@@
+		and is @@.hotpink;pleased@@ to find it's no longer saggy. $He's happy that $he'll be able to show off $his <<if getSlave($AS).weight > 95>>big soft belly<<if getSlave($AS).weight > 130>>, for awhile at least,<</if>><<elseif getSlave($AS).weight > 30>>soft belly<<elseif getSlave($AS).muscles > 30>>muscular belly<<elseif getSlave($AS).muscles > 5>>once again firm, ripped belly <<else>>once again firm, flat belly<</if>> without being self conscious about it. As with all surgery @@.health.dec;$his health has been affected.@@
 		<<set getSlave($AS).devotion += 2>>
 	<</if>>
 
 <<case "bellyIn">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen that $he cannot figure out the source of. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen that $he cannot figure out the source of. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his womb. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his womb. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache and slight weight in $his lower abdomen, and as such, knows you put something into $his womb. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his womb. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache and slight weight in $his lower abdomen, and as such, knows you put something into $his womb. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his womb. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to believe that $he has been impregnated. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been bred. Even after what has been implanted into $his womb is explained to $him, $he is no less defiant; though $he is relieved that $he isn't pregnant. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, but $he knows enough about surgery and sex slaves to believe that $he has been impregnated. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been bred. Even after what has been implanted into $his womb is explained to $him, $he is no less defiant; though $he is relieved that $he isn't pregnant. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 
@@ -2525,37 +2525,37 @@ As the remote surgery's long recovery cycle completes,
 		<<set _cervixPumpLocation = "rectum">>
 	<</if>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation that $he cannot figure out the source of. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation that $he cannot figure out the source of. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, and as such, knows you put something into $his _cervixPumpLocation. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As with all surgery @@.red;$his health has been slightly affected.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, and as such, knows you put something into $his _cervixPumpLocation. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, and as such, knows you put something into $his _cervixPumpLocation. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his _cervixPumpLocation. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, and as such, knows you put something into $his _cervixPumpLocation. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his _cervixPumpLocation. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
 		<<if $surgeryType == "cervixPump">>
-			$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, but $he knows enough about surgery and sex slaves to believe that $he has modified in $his most private place. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been subject of such modifications. Even after what has been implanted into $his womb is explained to $him, $he is no less defiant; though $he is relieved that it isn't some permanent mutilation, $he only feels dirty and humiliated when $he thinks of vaginal sex slowly rounding $his middle. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, but $he knows enough about surgery and sex slaves to believe that $he has modified in $his most private place. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been subject of such modifications. Even after what has been implanted into $his womb is explained to $him, $he is no less defiant; though $he is relieved that it isn't some permanent mutilation, $he only feels dirty and humiliated when $he thinks of vaginal sex slowly rounding $his middle. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<else>>
-			$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, but $he knows enough about surgery and sex slaves to believe that $he has modified in $his backdoor. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been subject of such modifications. Even after what has been implanted into $his rear hole is explained to $him, $he is no less defiant; though $he is relieved that it isn't some permanent mutilation, $he only feels dirty and humiliated when $he thinks of anal sex slowly rounding $his middle. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen and _cervixPumpAcheLocation, but $he knows enough about surgery and sex slaves to believe that $he has modified in $his backdoor. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at the potential that $he's been subject of such modifications. Even after what has been implanted into $his rear hole is explained to $him, $he is no less defiant; though $he is relieved that it isn't some permanent mutilation, $he only feels dirty and humiliated when $he thinks of anal sex slowly rounding $his middle. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<</if>>
 		<<set getSlave($AS).trust -= 5, getSlave($AS).devotion -= 5>>
 	<</if>>
 	<<if $surgeryType == "cervixPumpA" && getSlave($AS).mpreg == 1>>
-		$His existing anal womb made the surgery @@.green;slightly easier.@@
+		$His existing anal womb made the surgery @@.health.inc;slightly easier.@@
 		<<run improveCondition(getSlave($AS), 10)>>
 	<</if>>
 
 <<case "bellyInMale">>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen that $he cannot figure out the source of. As $his surgery was very invasive @@.red;$his health has been greatly impacted.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen that $he cannot figure out the source of. As $his surgery was very invasive @@.health.dec;$his health has been greatly impacted.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his body. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As $his surgery was very invasive @@.red;$his health has been greatly impacted.@@
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his body. $He is @@.hotpink;curious@@ about the details of the implant, and eagerly awaits to see the end result. As $his surgery was very invasive @@.health.dec;$his health has been greatly impacted.@@
 		<<set getSlave($AS).devotion += 4>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		$He leaves the surgery with nothing but a nonspecific ache and slight weight in $his lower abdomen, and as such, knows you put something into $his body. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his middle. As $his surgery was very invasive @@.red;$his health has been greatly impacted.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache and slight weight in $his lower abdomen, and as such, knows you put something into $his body. $He understands the realities of $his life as a slave, but $he is still surprised at what now resides in $his middle. As $his surgery was very invasive @@.health.dec;$his health has been greatly impacted.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 	<<else>>
-		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his body. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at your treatment of $his body. As $his surgery was very invasive @@.red;$his health has been greatly impacted.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+		$He leaves the surgery with nothing but a nonspecific ache in $his lower abdomen, and as such, knows you put something into $his body. $He does not understand the realities of $his life as a slave at a core level, so $he's @@.mediumorchid;terrified and angry@@ at your treatment of $his body. As $his surgery was very invasive @@.health.dec;$his health has been greatly impacted.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 		<<set getSlave($AS).trust -= 5>>
 		<<set getSlave($AS).devotion -= 5>>
 	<</if>>
@@ -2567,28 +2567,28 @@ As the remote surgery's long recovery cycle completes,
 	<</if>>
 	<<if getSlave($AS).bellyPain == 1>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>$He shifts $his expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He doesn't understand why it has gotten larger either. As with all surgery @@.red;$his health has been slightly affected.@@
+			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>$He shifts $his expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He doesn't understand why it has gotten larger either. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<elseif (getSlave($AS).devotion > 50)>>
-			<<if hasAnyArms(getSlave($AS))>>$He runs $his fingers over $his expanded belly experimentally and turns to you with a smile to show it off. $He's still sore, so $he doesn't bounce or stretch, but $he turns from side to side to let you see it from all angles.<<else>>$He bounces a little to feel the weight in $his expanded middle and turns $his torso to you with a smile to show it off. $He's still sore, so $he doesn't move too much, but $he wiggles $himself a little to make it move for you.<</if>> @@.hotpink;$He's happy with your changes to $his body.@@ As with all surgery @@.red;$his health has been slightly affected.@@
+			<<if hasAnyArms(getSlave($AS))>>$He runs $his fingers over $his expanded belly experimentally and turns to you with a smile to show it off. $He's still sore, so $he doesn't bounce or stretch, but $he turns from side to side to let you see it from all angles.<<else>>$He bounces a little to feel the weight in $his expanded middle and turns $his torso to you with a smile to show it off. $He's still sore, so $he doesn't move too much, but $he wiggles $himself a little to make it move for you.<</if>> @@.hotpink;$He's happy with your changes to $his body.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@
 			<<set getSlave($AS).devotion += 4>>
 		<<elseif (getSlave($AS).devotion >= -20)>>
-			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>$He shifts $his expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>$He shifts $his expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 5>>
 		<<else>>
-			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>The new weight on $his midriff fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this rounded belly as a cruel imposition.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes $his expanded belly<<else>>The new weight on $his midriff fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this rounded belly as a cruel imposition.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 10, getSlave($AS).devotion -= 5>>
 		<</if>>
 	<<elseif getSlave($AS).bellyPain == 2>>
 		<<if getSlave($AS).fetish == "mindbroken">>
-			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>$He shifts $his painfully expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's extremely sore, so $he doesn't touch it.<<else>>$He's still extremely sore, so $he keeps $his torso still.<</if>> $He can't understand how it has grown so much so fast. Since $his body has been pushed beyond its limits, @@.red;$his health has been notably affected.@@
+			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>$He shifts $his painfully expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's extremely sore, so $he doesn't touch it.<<else>>$He's still extremely sore, so $he keeps $his torso still.<</if>> $He can't understand how it has grown so much so fast. Since $his body has been pushed beyond its limits, @@.health.dec;$his health has been notably affected.@@
 		<<elseif (getSlave($AS).devotion > 50)>>
-			<<if hasAnyArms(getSlave($AS))>>$He gingerly runs $his fingers over $his painfully expanded belly and turns to you to show it off. $He's extremely sore, so $he doesn't bounce or stretch, but $he tries to turn from side to side to let you see it from all angles.<<else>>$He is so sore, $he doesn't dare move the weight in $his expanded middle, but $he turns $his torso to you to show it off. $He stomachs the pain and wiggles $himself a little to make it move for you.<</if>> @@.hotpink;$He's certain $he'll be happy with your changes to $his body@@ once the pain subsides. Since $his body has been pushed beyond its limits, @@.red;$his health has been notably affected.@@
+			<<if hasAnyArms(getSlave($AS))>>$He gingerly runs $his fingers over $his painfully expanded belly and turns to you to show it off. $He's extremely sore, so $he doesn't bounce or stretch, but $he tries to turn from side to side to let you see it from all angles.<<else>>$He is so sore, $he doesn't dare move the weight in $his expanded middle, but $he turns $his torso to you to show it off. $He stomachs the pain and wiggles $himself a little to make it move for you.<</if>> @@.hotpink;$He's certain $he'll be happy with your changes to $his body@@ once the pain subsides. Since $his body has been pushed beyond its limits, @@.health.dec;$his health has been notably affected.@@
 			<<set getSlave($AS).devotion += 2>>
 		<<elseif (getSlave($AS).devotion >= -20)>>
-			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>$He shifts $his painfully expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's extremely sore, so $he doesn't touch it.<<else>>$He's still extremely sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. Since $his body has been pushed beyond its limits, @@.red;$his health has been notably affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>$He shifts $his painfully expanded belly<</if>> skeptically. <<if hasAnyArms(getSlave($AS))>>$He's extremely sore, so $he doesn't touch it.<<else>>$He's still extremely sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, so $he expected something like this when $he was sent to the surgery. $He isn't much affected mentally. Since $his body has been pushed beyond its limits, @@.health.dec;$his health has been notably affected.@@ $He is @@.gold;sensibly fearful@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 7>>
 		<<else>>
-			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>The new, painful weight on $his midriff fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still extremely sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still extremely sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this rounded belly as a terribly cruel imposition.@@ Since $his body has been pushed beyond its limits, @@.red;$his health has been notably affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+			<<if canSee(getSlave($AS))>>$He eyes $his painfully expanded belly<<else>>The new, painful weight on $his midriff fills $him<</if>> with resentment. <<if hasAnyArms(getSlave($AS))>>$He's still extremely sore, so $he doesn't touch it, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<<else>>$He's still extremely sore, so $he keeps $his torso still, but <<if canSee(getSlave($AS))>>$he glares daggers<<else>>$his face contorts with distaste<</if>>.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.mediumorchid;$he seems to view this rounded belly as a terribly cruel imposition.@@ Since $his body has been pushed beyond its limits, @@.health.dec;$his health has been notably affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 			<<set getSlave($AS).trust -= 12, getSlave($AS).devotion -= 7>>
 		<</if>>
 	<</if>>
@@ -2596,21 +2596,21 @@ As the remote surgery's long recovery cycle completes,
 <<case "bellyDown">>
 	<<if getSlave($AS).bellyImplant < 0>><<set getSlave($AS).bellyImplant = 0>><</if>><<run SetBellySize(getSlave($AS))>>
 	<<if getSlave($AS).fetish == "mindbroken">>
-		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with appreciation<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He can't why it is smaller now. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with appreciation<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He can't why it is smaller now. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 	<<elseif (getSlave($AS).devotion > 50)>>
-		<<if hasAnyArms(getSlave($AS))>>$He hefts $his lighter belly experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or stretch, but $he turns from side to side to let you see it from all angles.<<else>>$He bounces a little to feel the change in weight within $his middle and turns $his torso to you with a smile to show is off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his body@@ and @@.mediumaquamarine;trusts@@ that your looking out for $his ability to serve you. As with all surgery @@.red;$his health has been slightly affected.@@
+		<<if hasAnyArms(getSlave($AS))>>$He hefts $his lighter belly experimentally and turns to you with a smile to show off $his new, slimmer form. $He's still sore, so $he doesn't bounce or stretch, but $he turns from side to side to let you see it from all angles.<<else>>$He bounces a little to feel the change in weight within $his middle and turns $his torso to you with a smile to show is off. $He's still sore, so $he doesn't bounce too much.<</if>> @@.hotpink;$He's happy with your changes to $his body@@ and @@.mediumaquamarine;trusts@@ that your looking out for $his ability to serve you. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 		<<set getSlave($AS).devotion += 3, getSlave($AS).trust += 3>>
 	<<elseif (getSlave($AS).devotion >= -20)>>
-		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with appreciation<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.red;$his health has been slightly affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed some weight off $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with appreciation<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it.<<else>>$He's still sore, so $he keeps $his torso still.<</if>> $He's come to terms with the fact that $he's a slave, but $he expected something other than this when $he was sent to the surgery. $He isn't much affected mentally. As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is @@.mediumaquamarine;thankful@@ that you removed some weight off $his body.
 		<<set getSlave($AS).trust += 2>>
 	<<else>>
-		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with relief<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but $he breathes easier without the extra weight in $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the extra weight in $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $him.@@ As with all surgery @@.red;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
+		<<if canSee(getSlave($AS))>>$He eyes $his lighter belly with relief<<else>>$He attempts to shift $himself, only to find $his middle is not as heavy as before<</if>>. <<if hasAnyArms(getSlave($AS))>>$He's still sore, so $he doesn't touch it, but $he breathes easier without the extra weight in $him.<<else>>$He's still sore, so $he keeps $his torso still, but $he breathes easier without the extra weight in $him.<</if>> $He still thinks of $himself as a person, so $he isn't used to the idea of being surgically altered to suit your every whim. For now, @@.hotpink;$he seems appreciative of this literal weight lifted from $him.@@ As with all surgery @@.health.dec;$his health has been slightly affected.@@ $He is now @@.gold;terribly afraid@@ of your total power over $his body.
 		<<set getSlave($AS).devotion += 1, getSlave($AS).trust -= 10>>
 	<</if>>
 
 <<case "bellyOut">>
 	<<run SetBellySize(getSlave($AS))>>
-	$He notices almost immediately that the weight in $his middle is gone. $He shifts $his torso idly; it looks like $he doesn't know what to think about having $his belly implant removed. As with all surgery @@.red;$his health has been slightly affected.@@
+	$He notices almost immediately that the weight in $his middle is gone. $He shifts $his torso idly; it looks like $he doesn't know what to think about having $his belly implant removed. As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "body hair removal">>
 	<<if getSlave($AS).underArmHStyle != "hairless">><<set getSlave($AS).underArmHStyle = "bald">><</if>>
@@ -2749,16 +2749,16 @@ As the remote surgery's long recovery cycle completes,
 			$he fails to realize $he now has pubic hair.
 		<</if>>
 	<</if>>
-	As with all surgery @@.red;$his health has been slightly affected.@@
+	As with all surgery @@.health.dec;$his health has been slightly affected.@@
 
 <<case "womb">>
-	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower belly. As with all invasive surgery @@.red;$his health has been greatly affected.@@
+	It's not immediately apparent to $him what kind of surgery $he received, since all $he's left with is a terrible nonspecific ache in $his lower belly. As with all invasive surgery @@.health.dec;$his health has been greatly affected.@@
 
 <<case "elasticity treatment" "immortality treatment" "gene treatment">>
-	The procedure spans the week, with $him spending every other day in the surgery room for a series of 4 sets of injections. A few hours after each session, $he feels terribly ill. $He doesn't quite understand what it's about, just that $he feels pretty bad. The process involves <<if ($PC.skill.medicine >= 100)>>you<<else>>the remote surgeon<</if>> injecting the serum across $his entire body, every few <<if $showInches == 2>>inches<<else>>centimeters<</if>>, leaving small needle marks that fade out within minutes. Despite not leaving a lasting evidence, the process is very invasive work, and leaves $him @@.red;feeling weak and tired.@@
+	The procedure spans the week, with $him spending every other day in the surgery room for a series of 4 sets of injections. A few hours after each session, $he feels terribly ill. $He doesn't quite understand what it's about, just that $he feels pretty bad. The process involves <<if ($PC.skill.medicine >= 100)>>you<<else>>the remote surgeon<</if>> injecting the serum across $his entire body, every few <<if $showInches == 2>>inches<<else>>centimeters<</if>>, leaving small needle marks that fade out within minutes. Despite not leaving a lasting evidence, the process is very invasive work, and leaves $him @@.health.dec;feeling weak and tired.@@
 
 <<case "retrograde virus injection NCS">>
-	The procedure spans the week, with $him spending every other day in the surgery room for a series of 4 sets of injections. A few hours after each session, $he feels terribly ill. $He doesn't quite understand what it's about, just that $he feels pretty bad. The process involves <<if ($PC.skill.medicine >= 100)>>you<<else>>the remote surgeon<</if>> injecting the serum across $his body entire body, every few <<if $showInches == 2>>inches<<else>>centimeters<</if>>, leaving small needle marks that fade out within minutes. Despite not leaving a lasting evidence, the process is very invasive work, and leaves $him @@.red;feeling weak and tired.@@<br><br>
+	The procedure spans the week, with $him spending every other day in the surgery room for a series of 4 sets of injections. A few hours after each session, $he feels terribly ill. $He doesn't quite understand what it's about, just that $he feels pretty bad. The process involves <<if ($PC.skill.medicine >= 100)>>you<<else>>the remote surgeon<</if>> injecting the serum across $his body entire body, every few <<if $showInches == 2>>inches<<else>>centimeters<</if>>, leaving small needle marks that fade out within minutes. Despite not leaving a lasting evidence, the process is very invasive work, and leaves $him @@.health.dec;feeling weak and tired.@@<br><br>
 	/*
 		Generate the changes, into variables to set contexts.
 	*/
@@ -2936,7 +2936,7 @@ As the remote surgery's long recovery cycle completes,
 <<if ($PC.skill.medicine >= 100) && !["basicPLimbs", "beautyPLimbs", "combatPLimbs", "cyberPLimbs", "sexPLimbs"].includes($surgeryType)>>
 	<br><br>
 	<<if !["body hair removal", "braces", "chem castrate", "eyebrow removal", "hair removal", "insemination", "removeBraces"].includes($surgeryType)>>
-		Since you @@.springgreen;performed the surgery yourself,@@ and you do an artist's work, $his health is @@.green;less affected@@ by the surgery than it would have been if you'd paid some hack to do it remotely.
+		Since you @@.springgreen;performed the surgery yourself,@@ and you do an artist's work, $his health is @@.health.inc;less affected@@ by the surgery than it would have been if you'd paid some hack to do it remotely.
 		<<run improveCondition(getSlave($AS), 5)>>
 	<</if>>
 	<<if (getSlave($AS).fetish != "mindbroken" && getSlave($AS).fuckdoll == 0)>>
diff --git a/src/uncategorized/weather.tw b/src/uncategorized/weather.tw
deleted file mode 100644
index 1e8378086e27383b832ab4171595c266b7169923..0000000000000000000000000000000000000000
--- a/src/uncategorized/weather.tw
+++ /dev/null
@@ -1,653 +0,0 @@
-:: Weather [nobr]
-
-<<if $weatherRemaining == 0>>
-<<if $continent == "Africa">>
-<<set $weatherType = either(1, 1, 1, 1, 1, 2, 3, 4, 4, 5, 6, 6)>>
-<<elseif $continent == "Asia">>
-<<set $weatherType = either(1, 2, 2, 2, 2, 3, 4, 4, 5, 6)>>
-<<elseif $continent == "Australia">>
-<<set $weatherType = either(1, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6)>>
-<<elseif $continent == "Western Europe">>
-<<set $weatherType = either(1, 2, 2, 3, 4, 5, 5, 6)>>
-<<elseif $continent == "Central Europe">>
-<<set $weatherType = either(1, 2, 3, 4, 5, 5, 5, 6)>>
-<<elseif $continent == "Scandinavia">>
-<<set $weatherType = either(1, 2, 3, 4, 5, 5, 5, 5, 5, 6)>>
-<<elseif $continent == "Eastern Europe">>
-<<set $weatherType = either(1, 2, 3, 4, 4, 5, 5, 5, 6)>>
-<<elseif $continent == "Southern Europe">>
-<<set $weatherType = either(1, 1, 1, 2, 2, 3, 4, 4, 5, 6)>>
-<<elseif $continent == "Brazil">>
-<<set $weatherType = either(1, 1, 2, 3, 3, 3, 4, 4, 5, 6)>>
-<<elseif $continent == "Japan">>
-<<set $weatherType = either(1, 2, 2, 2, 3, 4, 4, 5, 6, 6)>>
-<<elseif $continent == "Middle East">>
-<<set $weatherType = either(1, 1, 1, 1, 1, 2, 3, 4, 4, 5, 6)>>
-<<elseif $continent == "North America">>
-<<set $weatherType = either(1, 2, 3, 3, 4, 5, 6, 6, 6)>>
-<<elseif $continent == "South America">>
-<<set $weatherType = either(1, 1, 2, 3, 3, 3, 4, 4, 5, 6)>>
-<<elseif $terrain === "oceanic">>
-<<set $weatherType = either(1, 2, 2, 2, 2, 2, 3, 4, 5, 6)>>
-<</if>>
-<<set $weatherRemaining = random (3,6)>>
-<</if>>
-
-<<if $weatherLastWeek == 0>>
-<<set $weatherLastWeek = 1>>
-<</if>>
-
-<<set _seed = random(1,10)>>
-<<if $weatherType == 1>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.hotnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.hotlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.hotheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.hotextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<<elseif $weatherType == 2>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.windynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.windylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.windyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.windyextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<<elseif $weatherType == 3>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.smokynice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.smokylight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.smokyheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.smokyextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<<elseif $weatherType == 4>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.toxicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.toxiclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.toxicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.toxicextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<<elseif $weatherType == 5>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.coldnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.coldlight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.coldheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.coldextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<<elseif $weatherType == 6>>
-<<if $week < 25>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 1>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 2>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 50 && $weatherLastWeek == 3>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 1>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 2>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 3>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week < 75 && $weatherLastWeek == 3>>
-	<<if _seed > 5>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 1>>
-	<<if _seed > 8>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 2>>
-	<<if _seed > 7>>
-		<<set $weatherToday = App.Data.weather.tectonicnice.random()>>
-		<<set $weatherLastWeek = 1>>
-	<<elseif _seed > 5>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 3>>
-	<<if _seed > 6>>
-		<<set $weatherToday = App.Data.weather.tectoniclight.random()>>
-		<<set $weatherLastWeek = 2>>
-	<<elseif _seed > 2>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<<elseif $week > 75 && $weatherLastWeek == 4>>
-	<<if _seed > 4>>
-		<<set $weatherToday = App.Data.weather.tectonicheavy.random()>>
-		<<set $weatherLastWeek = 3>>
-	<<else>>
-		<<set $weatherToday = App.Data.weather.tectonicextreme.random()>>
-		<<set $weatherLastWeek = 4>>
-	<</if>>
-<</if>>
-<</if>>
\ No newline at end of file
diff --git a/src/utility/miscWidgets.tw b/src/utility/miscWidgets.tw
index a9c54dfe57853bfe833779b173f9a80644b17c62..3ee42debfdba5273e1f0aafac0487f0ab3151a00 100644
--- a/src/utility/miscWidgets.tw
+++ b/src/utility/miscWidgets.tw
@@ -143,91 +143,3 @@ Allows for dynamic updating of the next button in the storyCaption (left side-ba
 <</switch>>
 <</capture>>
 <</widget>>
-
-/* Called as <<SetFacilityDecoration "facilityDecoration">>, will generate a clickable list of potential decorations, quotes are needed to pass facilityDecoration as reference - DO NOT INCLUDE $ PREFIX! */
-<<widget "SetFacilityDecoration">>
-<<if ($arcologies[0].FSSupremacist >= 20) && (State.variables[$args[0]] != "Supremacist")>>
-	<div class="indent">[[Supremacist redecoration|Future Society][State.variables[$args[0]] = "Supremacist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSSubjugationist >= 20) && (State.variables[$args[0]] != "Subjugationist")>>
-	<div class="indent">[[Subjugationist redecoration|Future Society][State.variables[$args[0]] = "Subjugationist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSRepopulationFocus >= 20) && (State.variables[$args[0]] != "Repopulation Focus")>>
-	<div class="indent">[[Repopulation Focus redecoration|Future Society][State.variables[$args[0]] = "Repopulation Focus", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSRestart >= 20) && (State.variables[$args[0]] != "Eugenics")>>
-	<div class="indent">[[Eugenics redecoration|Future Society][State.variables[$args[0]] = "Eugenics", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSGenderRadicalist >= 20) && (State.variables[$args[0]] != "Gender Radicalist")>>
-	<div class="indent">[[Gender Radicalist redecoration|Future Society][State.variables[$args[0]] = "Gender Radicalist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSGenderFundamentalist >= 20) && (State.variables[$args[0]] != "Gender Fundamentalist")>>
-	<div class="indent">[[Gender Fundamentalist redecoration|Future Society][State.variables[$args[0]] = "Gender Fundamentalist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSPaternalist >= 20) && (State.variables[$args[0]] != "Paternalist")>>
-	<div class="indent">[[Paternalist redecoration|Future Society][State.variables[$args[0]] = "Paternalist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSIntellectualDependency >= 20) && (State.variables[$args[0]] != "Intellectual Dependency")>>
-	<div class="indent">[[Intellectual Dependency redecoration|Future Society][State.variables[$args[0]] = "Intellectual Dependency", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSSlaveProfessionalism >= 20) && (State.variables[$args[0]] != "Slave Professionalism")>>
-	<div class="indent">[[Slave Professionalism redecoration|Future Society][State.variables[$args[0]] = "Slave Professionalism", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSBodyPurist >= 20) && (State.variables[$args[0]] != "Body Purist")>>
-	<div class="indent">[[Body Purist redecoration|Future Society][State.variables[$args[0]] = "Body Purist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSTransformationFetishist >= 20) && (State.variables[$args[0]] != "Transformation Fetishist")>>
-	<div class="indent">[[Transformation Fetishist redecoration|Future Society][State.variables[$args[0]] = "Transformation Fetishist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSYouthPreferentialist >= 20) && (State.variables[$args[0]] != "Youth Preferentialist")>>
-	<div class="indent">[[Youth Preferentialist redecoration|Future Society][State.variables[$args[0]] = "Youth Preferentialist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSMaturityPreferentialist >= 20) && (State.variables[$args[0]] != "Maturity Preferentialist")>>
-	<div class="indent">[[Maturity Preferentialist redecoration|Future Society][State.variables[$args[0]] = "Maturity Preferentialist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSPetiteAdmiration >= 20) && (State.variables[$args[0]] != "Petite Admiration")>>
-	<div class="indent">[[Petite Admiration redecoration|Future Society][State.variables[$args[0]] = "Petite Admiration", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSStatuesqueGlorification >= 20) && (State.variables[$args[0]] != "Statuesque Glorification")>>
-	<div class="indent">[[Statuesque Glorification redecoration|Future Society][State.variables[$args[0]] = "Statuesque Glorification", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSSlimnessEnthusiast >= 20) && (State.variables[$args[0]] != "Slimness Enthusiast")>>
-	<div class="indent">[[Slimness Enthusiast redecoration|Future Society][State.variables[$args[0]] = "Slimness Enthusiast", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSAssetExpansionist >= 20) && (State.variables[$args[0]] != "Asset Expansionist")>>
-	<div class="indent">[[Asset Expansionist redecoration|Future Society][State.variables[$args[0]] = "Asset Expansionist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSPastoralist >= 20) && (State.variables[$args[0]] != "Pastoralist")>>
-	<div class="indent">[[Pastoralist redecoration|Future Society][State.variables[$args[0]] = "Pastoralist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSPhysicalIdealist >= 20) && (State.variables[$args[0]] != "Physical Idealist")>>
-	<div class="indent">[[Physical Idealist redecoration|Future Society][State.variables[$args[0]] = "Physical Idealist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSHedonisticDecadence >= 20) && (State.variables[$args[0]] != "Hedonistic")>>
-	<div class="indent">[[Hedonistic redecoration|Future Society][State.variables[$args[0]] = "Hedonistic", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSChattelReligionist >= 20) && (State.variables[$args[0]] != "Chattel Religionist")>>
-	<div class="indent">[[Chattel Religionist redecoration|Future Society][State.variables[$args[0]] = "Chattel Religionist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSDegradationist >= 20) && (State.variables[$args[0]] != "Degradationist")>>
-	<div class="indent">[[Degradationist redecoration|Future Society][State.variables[$args[0]] = "Degradationist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if ($arcologies[0].FSRomanRevivalist >= 20) && (State.variables[$args[0]] != "Roman Revivalist")>>
-	<div class="indent">[[Roman Revivalist redecoration|Future Society][State.variables[$args[0]] = "Roman Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSAztecRevivalist >= 20) && (State.variables[$args[0]] != "Aztec Revivalist")>>
-	<div class="indent">[[Aztec Revivalist redecoration|Future Society][State.variables[$args[0]] = "Aztec Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSEgyptianRevivalist >= 20) && (State.variables[$args[0]] != "Egyptian Revivalist")>>
-	<div class="indent">[[Egyptian Revivalist redecoration|Future Society][State.variables[$args[0]] = "Egyptian Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSEdoRevivalist >= 20) && (State.variables[$args[0]] != "Edo Revivalist")>>
-	<div class="indent">[[Edo Revivalist redecoration|Future Society][State.variables[$args[0]] = "Edo Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSArabianRevivalist >= 20) && (State.variables[$args[0]] != "Arabian Revivalist")>>
-	<div class="indent">[[Arabian Revivalist redecoration|Future Society][State.variables[$args[0]] = "Arabian Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSChineseRevivalist >= 20) && (State.variables[$args[0]] != "Chinese Revivalist")>>
-	<div class="indent">[[Chinese Revivalist redecoration|Future Society][State.variables[$args[0]] = "Chinese Revivalist", cashX(-5000, "capEx")]]</div>
-<<elseif ($arcologies[0].FSNeoImperialist >= 20) && (State.variables[$args[0]] != "Neo Imperialist")>>
-	<div class="indent">[[Neo-Imperialist redecoration|Future Society][State.variables[$args[0]] = "Neo Imperialist", cashX(-5000, "capEx")]]</div>
-<</if>>
-<<if (State.variables[$args[0]] != "standard")>>
-	<div class="indent">[[Remove all decorations|Future Society][State.variables[$args[0]] = "standard"]]</div>
-<</if>>
-<</widget>>
diff --git a/submodules/sugarcube-2 b/submodules/sugarcube-2
index c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f..563752841c740a7ffeb921f4e7250172d7b497d3 160000
--- a/submodules/sugarcube-2
+++ b/submodules/sugarcube-2
@@ -1 +1 @@
-Subproject commit c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f
+Subproject commit 563752841c740a7ffeb921f4e7250172d7b497d3
diff --git a/themes/light/options.css b/themes/light/options.css
index 88e25e7e362d0ee0669b6fb4c471dcd7a7e7c8e7..419ff6ed92790d70bcd8ce5fddc1a4419d519621 100644
--- a/themes/light/options.css
+++ b/themes/light/options.css
@@ -1,3 +1,3 @@
-div.options-group span.comment {
+div.options-group .comment {
     color: #575757;
 }