diff --git a/.gitmodules b/.gitmodules
index 326cc637e01baabb2e1677639376dff4036bb402..8b747cd99da8c852987d800c57179329d9010220 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "submodules/sugarcube-2"]
 	path = submodules/sugarcube-2
 	url = https://gitgud.io/Arkerthan/sugarcube-2.git
+	branch = fc
diff --git a/Changelog.txt b/Changelog.txt
index 95aa0077e1dc0dce2dedbfbcade50c709b077a5b..986c9fcc64406364fc60d62e49bfcac74551d003 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,5 +1,29 @@
 Pregmod
 
+0.10.7.1-3.6.x
+
+	8/01/2020
+
+	2
+	-sugarcube updated to 2.33.1
+	-completion of RECI cleanup
+	-fixes
+
+	7/31/2020
+
+	1
+	-added The Utopian Orphanage slave school
+	-sugarcube updated to 2.33.0
+
+	7/30/2020
+
+	0
+	-revised mobility tracking
+	-overhauled inbreeding effects
+	-farmyard development progressed
+	-continued code cleanup
+	-fixes
+
 0.10.7.1-3.5.x
 
 	7/06/2020
diff --git a/devNotes/sugarcube stuff/sugarcube-fc-changes.patch b/devNotes/sugarcube stuff/sugarcube-fc-changes.patch
index 7489b32a6596755c9452eae660017f12f87bb4ec..5956801c0326edd78ebbcf6b53fb6213da1ba61f 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 bd4421f..1a64744 100644
+index 955b617..acd80f5 100644
 --- a/build.js
 +++ b/build.js
 @@ -30,6 +30,7 @@ const CONFIG = {
@@ -325,25 +325,25 @@ index 75408a3..3993e1e 100644
  	}
  
 diff --git a/src/macros/macrocontext.js b/src/macros/macrocontext.js
-index addd272..8b9758c 100644
+index bd211cd..712e9e1 100644
 --- a/src/macros/macrocontext.js
 +++ b/src/macros/macrocontext.js
-@@ -272,8 +272,8 @@ var MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -277,8 +277,8 @@ var MacroContext = (() => { // eslint-disable-line no-unused-vars, no-var
  			this._debugViewEnabled = false;
  		}
  
 -		error(message, source) {
--			return throwError(this._output, `<<${this.name}>>: ${message}`, source ? source : this.source);
+-			return throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source);
 +		error(message, source, stack) {
-+			return throwError(this._output, `<<${this.name}>>: ${message}`, source ? source : this.source, stack);
++			return throwError(this._output, `<<${this.displayName}>>: ${message}`, source ? source : this.source, stack);
  		}
  	}
  
 diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js
-index 3608880..b5cbf1d 100644
+index cbfc9d4..6e04094 100644
 --- a/src/macros/macrolib.js
 +++ b/src/macros/macrolib.js
-@@ -89,7 +89,7 @@
+@@ -90,7 +90,7 @@
  				Scripting.evalJavaScript(this.args.full);
  			}
  			catch (ex) {
@@ -352,7 +352,7 @@ index 3608880..b5cbf1d 100644
  			}
  
  			// Custom debug view setup.
-@@ -347,7 +347,7 @@
+@@ -351,7 +351,7 @@
  				}
  			}
  			catch (ex) {
@@ -361,7 +361,7 @@ index 3608880..b5cbf1d 100644
  			}
  		}
  	});
-@@ -487,7 +487,7 @@
+@@ -784,7 +784,7 @@
  				}
  			}
  			catch (ex) {
@@ -384,7 +384,7 @@ index bc8b018..df7285c 100644
  
  		/*
 diff --git a/src/passage.js b/src/passage.js
-index aa5c2f2..15d4221 100644
+index 73959ec..b299722 100644
 --- a/src/passage.js
 +++ b/src/passage.js
 @@ -222,8 +222,10 @@ var Passage = (() => { // eslint-disable-line no-unused-vars, no-var
@@ -401,10 +401,10 @@ index aa5c2f2..15d4221 100644
  			return frag;
  		}
 diff --git a/src/save.js b/src/save.js
-index 5ac0446..a1191d4 100644
+index b6cca52..adbf7f1 100644
 --- a/src/save.js
 +++ b/src/save.js
-@@ -30,12 +30,66 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -38,12 +38,66 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return false;
  		}
  
@@ -474,7 +474,7 @@ index 5ac0446..a1191d4 100644
  			saves = {
  				autosave : null,
  				slots    : saves
-@@ -45,6 +99,7 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -53,6 +107,7 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		/* /legacy */
  
  		// Handle the author changing the number of save slots.
@@ -482,7 +482,7 @@ index 5ac0446..a1191d4 100644
  		if (Config.saves.slots !== saves.slots.length) {
  			if (Config.saves.slots < saves.slots.length) {
  				// Attempt to decrease the number of slots; this will only compact
-@@ -69,10 +124,11 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -77,10 +132,11 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  
  			updated = true;
  		}
@@ -495,7 +495,7 @@ index 5ac0446..a1191d4 100644
  			updated = true;
  		}
  
-@@ -90,29 +146,39 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -98,29 +154,39 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		/* /legacy */
  
  		// If the saves object was updated, then update the store.
@@ -542,7 +542,7 @@ index 5ac0446..a1191d4 100644
  		return true;
  	}
  
-@@ -120,7 +186,6 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -128,7 +194,6 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		return autosaveOk() || slotsOk();
  	}
  
@@ -550,7 +550,7 @@ index 5ac0446..a1191d4 100644
  	/*******************************************************************************************************************
  		Autosave Functions.
  	*******************************************************************************************************************/
-@@ -129,28 +194,21 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -137,28 +202,21 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  	}
  
  	function autosaveHas() {
@@ -584,7 +584,7 @@ index 5ac0446..a1191d4 100644
  	}
  
  	function autosaveSave(title, metadata) {
-@@ -158,25 +216,38 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -166,25 +224,38 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return false;
  		}
  
@@ -606,11 +606,11 @@ index 5ac0446..a1191d4 100644
  			supplemental.metadata = metadata;
  		}
  
--		saves.autosave = _marshal(supplemental);
+-		saves.autosave = _marshal(supplemental, { type : Type.Autosave });
 -
 -		return _savesObjSave(saves);
 +		try {
-+			return storage.set('autosave', _marshal(supplemental), false);
++			return storage.set('autosave', _marshal(supplemental, { type : Type.Autosave }), false);
 +		} catch (ex) {
 +			index.autosave = null;
 +			indexSave(index);
@@ -630,7 +630,7 @@ index 5ac0446..a1191d4 100644
  	}
  
  
-@@ -196,14 +267,15 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -204,14 +275,15 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return 0;
  		}
  
@@ -650,7 +650,7 @@ index 5ac0446..a1191d4 100644
  		return count;
  	}
  
-@@ -216,9 +288,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -224,9 +296,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return false;
  		}
  
@@ -662,7 +662,7 @@ index 5ac0446..a1191d4 100644
  			return false;
  		}
  
-@@ -226,31 +298,19 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -234,31 +306,19 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  	}
  
  	function slotsGet(slot) {
@@ -700,7 +700,7 @@ index 5ac0446..a1191d4 100644
  	}
  
  	function slotsSave(slot, title, metadata) {
-@@ -269,24 +329,37 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -277,24 +337,37 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return false;
  		}
  
@@ -731,11 +731,11 @@ index 5ac0446..a1191d4 100644
  			supplemental.metadata = metadata;
  		}
  
--		saves.slots[slot] = _marshal(supplemental);
+-		saves.slots[slot] = _marshal(supplemental, { type : Type.Slot });
 -
 -		return _savesObjSave(saves);
 +		try {
-+			return storage.set(`slot${slot}`, _marshal(supplemental));
++			return storage.set(`slot${slot}`, _marshal(supplemental, { type : Type.Slot }));
 +		} catch (ex) {
 +			index.slots[slot] = null;
 +			indexSave(index);
@@ -745,7 +745,7 @@ index 5ac0446..a1191d4 100644
  	}
  
  	function slotsDelete(slot) {
-@@ -294,21 +367,23 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -302,21 +375,23 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			return false;
  		}
  
@@ -774,18 +774,18 @@ index 5ac0446..a1191d4 100644
  		if (typeof Config.saves.isAllowed === 'function' && !Config.saves.isAllowed()) {
  			if (Dialog.isOpen()) {
  				$(document).one(':dialogclosed', () => UI.alert(L10n.get('savesDisallowed')));
-@@ -352,7 +427,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -360,7 +435,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		const baseName     = filename == null ? Story.domId : legalizeName(filename); // lazy equality for null
  		const saveName     = `${baseName}-${datestamp()}.save`;
  		const supplemental = metadata == null ? {} : { metadata }; // lazy equality for null
--		const saveObj      = LZString.compressToBase64(JSON.stringify(_marshal(supplemental)));
+-		const saveObj      = LZString.compressToBase64(JSON.stringify(_marshal(supplemental, { type : Type.Disk })));
 +		const saveObj      = LZString.compressToBase64(JSON.stringify(
-+			marshaledSave == null ? _marshal(supplemental) : marshaledSave
++			marshaledSave == null ? _marshal(supplemental, { type : Type.Disk }) : marshaledSave
 +		));
  		saveAs(new Blob([saveObj], { type : 'text/plain;charset=UTF-8' }), saveName);
  	}
  
-@@ -426,10 +503,17 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -434,10 +511,17 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		return saveObj.metadata;
  	}
  
@@ -804,7 +804,7 @@ index 5ac0446..a1191d4 100644
  	function _appendSlots(array, num) {
  		for (let i = 0; i < num; ++i) {
  			array.push(null);
-@@ -438,31 +522,8 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -446,31 +530,8 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  		return array;
  	}
  
@@ -837,7 +837,7 @@ index 5ac0446..a1191d4 100644
  			return false;
  		}
  
-@@ -615,9 +676,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
+@@ -623,9 +684,9 @@ var Save = (() => { // eslint-disable-line no-unused-vars, no-var
  			Save Functions.
  		*/
  		init  : { value : savesInit },
@@ -849,7 +849,7 @@ index 5ac0446..a1191d4 100644
  		/*
  			Autosave Functions.
 diff --git a/src/state.js b/src/state.js
-index 5ce7c55..69a73a3 100644
+index 184c92b..d445630 100644
 --- a/src/state.js
 +++ b/src/state.js
 @@ -104,7 +104,7 @@ var State = (() => { // eslint-disable-line no-unused-vars, no-var
@@ -882,7 +882,7 @@ index 5ce7c55..69a73a3 100644
  		for (let i = 1, iend = historyArr.length; i < iend; ++i) {
  			delta.push(Diff.diff(historyArr[i - 1], historyArr[i]));
 diff --git a/src/ui.js b/src/ui.js
-index 44fb8fa..df9cb63 100644
+index 22fd8ae..97fe8b3 100644
 --- a/src/ui.js
 +++ b/src/ui.js
 @@ -252,7 +252,7 @@ var UI = (() => { // eslint-disable-line no-unused-vars, no-var
diff --git a/devTools/tweeGo/storyFormats/sugarcube-2/format.js b/devTools/tweeGo/storyFormats/sugarcube-2/format.js
index 63600741ce163df340daf481f5d21d4fa94e749a..e230a079e3bb3f73e1a5785aafd0948885eafc32 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.31.1","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.31.1): 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/master/classList.js */\nif(\"document\" in self){if(!(\"classList\" in document.createElement(\"_\"))){(function(j){\"use strict\";if(!(\"Element\" in j)){return}var a=\"classList\",f=\"prototype\",m=j.Element[f],b=Object,k=String[f].trim||function(){return this.replace(/^\\s+|\\s+$/g,\"\")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p<o;p++){if(p in this&&this[p]===q){return p}}return -1},n=function(o,p){this.name=o;this.code=DOMException[o];this.message=p},g=function(p,o){if(o===\"\"){throw new n(\"SYNTAX_ERR\",\"An invalid or illegal string was specified\")}if(/\\s/.test(o)){throw new n(\"INVALID_CHARACTER_ERR\",\"String contains an invalid character\")}return c.call(p,o)},d=function(s){var r=k.call(s.getAttribute(\"class\")||\"\"),q=r?r.split(/\\s+/):[],p=0,o=q.length;for(;p<o;p++){this.push(q[p])}this._updateClassName=function(){s.setAttribute(\"class\",this.toString())}},e=d[f]=[],i=function(){return new d(this)};n[f]=Error[f];e.item=function(o){return this[o]||null};e.contains=function(o){o+=\"\";return g(this,o)!==-1};e.add=function(){var s=arguments,r=0,p=s.length,q,o=false;do{q=s[r]+\"\";if(g(this,q)===-1){this.push(q);o=true}}while(++r<p);if(o){this._updateClassName()}};e.remove=function(){var t=arguments,s=0,p=t.length,r,o=false,q;do{r=t[s]+\"\";q=g(this,r);while(q!==-1){this.splice(q,1);o=true;q=g(this,r)}}while(++s<p);if(o){this._updateClassName()}};e.toggle=function(p,q){p+=\"\";var o=this.contains(p),r=o?q!==true&&\"remove\":q!==false&&\"add\";if(r){this[r](p)}if(q===true||q===false){return q}else{return !o}};e.toString=function(){return this.join(\" \")};if(b.defineProperty){var l={get:i,enumerable:true,configurable:true};try{b.defineProperty(m,a,l)}catch(h){if(h.number===-2146823252){l.enumerable=false;b.defineProperty(m,a,l)}}}else{if(b[f].__defineGetter__){m.__defineGetter__(a,i)}}}(self))}else{(function(){var b=document.createElement(\"_\");b.classList.add(\"c1\",\"c2\");if(!b.classList.contains(\"c2\")){var c=function(e){var d=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(h){var g,f=arguments.length;for(g=0;g<f;g++){h=arguments[g];d.call(this,h)}}};c(\"add\");c(\"remove\")}b.classList.toggle(\"c3\",false);if(b.classList.contains(\"c3\")){var a=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(d,e){if(1 in arguments&&!this.contains(d)===!e){return e}else{return a.call(this,d)}}}b=null}())}};\n/*!\n * https://github.com/es-shims/es5-shim\n * @license es5-shim Copyright 2009-2015 by contributors, MIT License\n * see https://github.com/es-shims/es5-shim/blob/v4.5.13/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=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\\u180e\\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//# sourceMappingURL=es5-shim.map\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//# sourceMappingURL=es6-shim.map\n/*! jQuery v3.4.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=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,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},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.4.1\",k=function(e,t){return new k.fn.init(e,t)},p=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;function d(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)}k.fn=k.prototype={jquery:f,constructor:k,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=k.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return k.each(this,e)},map:function(n){return this.pushStack(k.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)},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},k.extend=k.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&&(k.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||k.isPlainObject(n)?n:{},i=!1,a[t]=k.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},k.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){b(e,{nonce:t&&t.nonce})},each:function(e,t){var n,r=0;if(d(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},trim:function(e){return null==e?\"\":(e+\"\").replace(p,\"\")},makeArray:function(e,t){var n=t||[];return null!=e&&(d(Object(e))?k.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(d(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.apply([],a)},guid:1,support:y}),\"function\"==typeof Symbol&&(k.fn[Symbol.iterator]=t[Symbol.iterator]),k.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){n[\"[object \"+t+\"]\"]=t.toLowerCase()});var h=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,k=\"sizzle\"+1*new Date,m=n.document,S=0,r=0,p=ue(),x=ue(),N=ue(),A=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=\"(?:\\\\\\\\.|[\\\\w-]|[^\\0-\\\\xa0])+\",W=\"\\\\[\"+M+\"*(\"+I+\")(?:\"+M+\"*([*^$|!~]?=)\"+M+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+I+\"))|)\"+M+\"*\\\\]\",$=\":(\"+I+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+W+\")*)|.*)\\\\)|)\",F=new RegExp(M+\"+\",\"g\"),B=new RegExp(\"^\"+M+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+M+\"+$\",\"g\"),_=new RegExp(\"^\"+M+\"*,\"+M+\"*\"),z=new RegExp(\"^\"+M+\"*([>+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp($),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+$),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-f]{1,6}\"+M+\"?|(\"+M+\")|.)\",\"ig\"),ne=function(e,t,n){var r=\"0x\"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|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(m.childNodes),m.childNodes),t[m.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&&((e?e.ownerDocument||e:m)!==C&&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&&!A[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=k),o=(l=h(t)).length;while(o--)l[o]=\"#\"+s+\" \"+xe(l[o]);c=l.join(\",\"),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute(\"id\")}}}return g(t.replace(B,\"$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[k]=!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:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),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=k,!C.getElementsByName||!C.getElementsByName(k).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){a.appendChild(e).innerHTML=\"<a id='\"+k+\"'></a><select id='\"+k+\"-\\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~=\"+k+\"-]\").length||v.push(\"~=\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+k+\"+*\").length||v.push(\".#.+[+~]\")}),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(\"!=\",$)}),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===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,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]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[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){A(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=p[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&p(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(F,\" \")+\" \").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)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&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]=[S,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&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[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[S,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[k]?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(B,\"$1\"));return s[k]?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=[S,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[k]||(e[k]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===S&&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[k]&&(v=Ce(v)),y&&!y[k]&&(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))[k]){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(B,\"$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(B,\" \")}),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=N[e+\" \"];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[k]?i.push(a):o.push(a);(a=N(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=S+=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&&(S=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&&(S=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=k.split(\"\").sort(D).join(\"\")===k,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);k.find=h,k.expr=h.selectors,k.expr[\":\"]=k.expr.pseudos,k.uniqueSort=k.unique=h.uniqueSort,k.text=h.getText,k.isXMLDoc=h.isXML,k.contains=h.contains,k.escapeSelector=h.escape;var T=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&k(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},N=k.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var D=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?k.grep(e,function(e){return-1<i.call(n,e)!==r}):k.filter(n,e,r)}k.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?k.find.matchesSelector(r,e)?[r]:[]:k.find.matches(e,k.grep(t,function(e){return 1===e.nodeType}))},k.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(k(e).filter(function(){for(t=0;t<r;t++)if(k.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)k.find(e,i[t],n);return 1<r?k.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,\"string\"==typeof e&&N.test(e)?k(e):e||[],!1).length}});var q,L=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:L.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 k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.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(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(k.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a=\"string\"!=typeof e&&k(e);if(!N.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&&k.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?k.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?i.call(k(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(k.uniqueSort(k.merge(this.get(),k(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),k.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return T(e,\"parentNode\")},parentsUntil:function(e,t,n){return T(e,\"parentNode\",n)},next:function(e){return P(e,\"nextSibling\")},prev:function(e){return P(e,\"previousSibling\")},nextAll:function(e){return T(e,\"nextSibling\")},prevAll:function(e){return T(e,\"previousSibling\")},nextUntil:function(e,t,n){return T(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return T(e,\"previousSibling\",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return\"undefined\"!=typeof e.contentDocument?e.contentDocument:(A(e,\"template\")&&(e=e.content||e),k.merge([],e.childNodes))}},function(r,i){k.fn[r]=function(e,t){var n=k.map(this,i,e);return\"Until\"!==r.slice(-5)&&(t=e),t&&\"string\"==typeof t&&(n=k.filter(t,n)),1<this.length&&(O[r]||k.uniqueSort(n),H.test(r)&&n.reverse()),this.pushStack(n)}});var R=/[^\\x20\\t\\r\\n\\f]+/g;function M(e){return e}function I(e){throw e}function W(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])}}k.Callbacks=function(r){var e,n;r=\"string\"==typeof r?(e=r,n={},k.each(e.match(R)||[],function(e,t){n[t]=!0}),n):k.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){k.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 k.each(arguments,function(e,t){var n;while(-1<(n=k.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<k.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},k.extend({Deferred:function(e){var o=[[\"notify\",\"progress\",k.Callbacks(\"memory\"),k.Callbacks(\"memory\"),2],[\"resolve\",\"done\",k.Callbacks(\"once memory\"),k.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",k.Callbacks(\"once memory\"),k.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 k.Deferred(function(r){k.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,M,s),l(u,o,I,s)):(u++,t.call(e,l(u,o,M,s),l(u,o,I,s),l(u,o,M,o.notifyWith))):(a!==M&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){k.Deferred.exceptionHook&&k.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==I&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(k.Deferred.getStackHook&&(t.stackTrace=k.Deferred.getStackHook()),C.setTimeout(t))}}return k.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:M,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:M)),o[2][3].add(l(0,e,m(n)?n:I))}).promise()},promise:function(e){return null!=e?k.extend(e,a):a}},s={};return k.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=k.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&&(W(e,o.done(a(t)).resolve,o.reject,!n),\"pending\"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)W(i[t],a(t),o.reject);return o.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;k.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&$.test(e.name)&&C.console.warn(\"jQuery.Deferred exception: \"+e.message,e.stack,t)},k.readyException=function(e){C.setTimeout(function(){throw e})};var F=k.Deferred();function B(){E.removeEventListener(\"DOMContentLoaded\",B),C.removeEventListener(\"load\",B),k.ready()}k.fn.ready=function(e){return F.then(e)[\"catch\"](function(e){k.readyException(e)}),this},k.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--k.readyWait:k.isReady)||(k.isReady=!0)!==e&&0<--k.readyWait||F.resolveWith(E,[k])}}),k.ready.then=F.then,\"complete\"===E.readyState||\"loading\"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(k.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(k(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},z=/^-ms-/,U=/-([a-z])/g;function X(e,t){return t.toUpperCase()}function V(e){return e.replace(z,\"ms-\").replace(U,X)}var G=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Y(){this.expando=k.expando+Y.uid++}Y.uid=1,Y.prototype={cache:function(e){var t=e[this.expando];return t||(t={},G(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[V(t)]=n;else for(r in t)i[V(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][V(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(V):(t=V(t))in r?[t]:t.match(R)||[]).length;while(n--)delete r[t[n]]}(void 0===t||k.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&&!k.isEmptyObject(t)}};var Q=new Y,J=new Y,K=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,Z=/[A-Z]/g;function ee(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(Z,\"-$&\").toLowerCase(),\"string\"==typeof(n=e.getAttribute(r))){try{n=\"true\"===(i=n)||\"false\"!==i&&(\"null\"===i?null:i===+i+\"\"?+i:K.test(i)?JSON.parse(i):i)}catch(e){}J.set(e,t,n)}else n=void 0;return n}k.extend({hasData:function(e){return J.hasData(e)||Q.hasData(e)},data:function(e,t,n){return J.access(e,t,n)},removeData:function(e,t){J.remove(e,t)},_data:function(e,t,n){return Q.access(e,t,n)},_removeData:function(e,t){Q.remove(e,t)}}),k.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=J.get(o),1===o.nodeType&&!Q.get(o,\"hasDataAttrs\"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf(\"data-\")&&(r=V(r.slice(5)),ee(o,r,i[r]));Q.set(o,\"hasDataAttrs\",!0)}return i}return\"object\"==typeof n?this.each(function(){J.set(this,n)}):_(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=J.get(o,n))?t:void 0!==(t=ee(o,n))?t:void 0;this.each(function(){J.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),k.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=Q.get(e,t),n&&(!r||Array.isArray(n)?r=Q.access(e,t,k.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=k.queue(e,t),r=n.length,i=n.shift(),o=k._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,function(){k.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return Q.get(e,n)||Q.access(e,n,{empty:k.Callbacks(\"once memory\").add(function(){Q.remove(e,[t+\"queue\",n])})})}}),k.fn.extend({queue:function(t,n){var e=2;return\"string\"!=typeof t&&(n=t,t=\"fx\",e--),arguments.length<e?k.queue(this[0],t):void 0===n?this:this.each(function(){var e=k.queue(this,t,n);k._queueHooks(this,t),\"fx\"===t&&\"inprogress\"!==e[0]&&k.dequeue(this,t)})},dequeue:function(e){return this.each(function(){k.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,i=k.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=Q.get(o[a],e+\"queueHooks\"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var te=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,ne=new RegExp(\"^(?:([+-])=|)(\"+te+\")([a-z%]*)$\",\"i\"),re=[\"Top\",\"Right\",\"Bottom\",\"Left\"],ie=E.documentElement,oe=function(e){return k.contains(e.ownerDocument,e)},ae={composed:!0};ie.getRootNode&&(oe=function(e){return k.contains(e.ownerDocument,e)||e.getRootNode(ae)===e.ownerDocument});var se=function(e,t){return\"none\"===(e=t||e).style.display||\"\"===e.style.display&&oe(e)&&\"none\"===k.css(e,\"display\")},ue=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];for(o in i=n.apply(e,r||[]),t)e.style[o]=a[o];return i};function le(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return k.css(e,t,\"\")},u=s(),l=n&&n[3]||(k.cssNumber[t]?\"\":\"px\"),c=e.nodeType&&(k.cssNumber[t]||\"px\"!==l&&+u)&&ne.exec(k.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)k.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,k.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 ce={};function fe(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]=Q.get(r,\"display\")||null,l[c]||(r.style.display=\"\")),\"\"===r.style.display&&se(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ce[s])||(o=a.body.appendChild(a.createElement(s)),u=k.css(o,\"display\"),o.parentNode.removeChild(o),\"none\"===u&&(u=\"block\"),ce[s]=u)))):\"none\"!==n&&(l[c]=\"none\",Q.set(r,\"display\",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}k.fn.extend({show:function(){return fe(this,!0)},hide:function(){return fe(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){se(this)?k(this).show():k(this).hide()})}});var pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i,ge={option:[1,\"<select multiple='multiple'>\",\"</select>\"],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)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Q.set(e[n],\"globalEval\",!t||Q.get(t[n],\"globalEval\"))}ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;var me,xe,be=/<|&#?\\w+;/;function we(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))k.merge(p,o.nodeType?[o]:o);else if(be.test(o)){a=a||f.appendChild(t.createElement(\"div\")),s=(de.exec(o)||[\"\",\"\"])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+k.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;k.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<k.inArray(o,r))i&&i.push(o);else if(l=oe(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}me=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(xe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),xe.setAttribute(\"checked\",\"checked\"),xe.setAttribute(\"name\",\"t\"),me.appendChild(xe),y.checkClone=me.cloneNode(!0).cloneNode(!0).lastChild.checked,me.innerHTML=\"<textarea>x</textarea>\",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==(\"focus\"===t)}function Ae(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)Ae(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=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return\"undefined\"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||\"\").match(R)||[\"\"]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.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),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||\"\").match(R)||[\"\"]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){f=k.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)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,\"events\")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){a=k.event.handlers.call(this,s,l),t=0;while((i=a[t++])&&!s.isPropagationStopped()){s.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!s.isImmediatePropagationStopped())s.rnamespace&&!1!==o.namespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((k.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,s),s.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<k(i,this).index(l):k.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(k.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[k.expando]?e:new k.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&De(t,\"click\",ke),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,\"input\")&&De(t,\"click\"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,\"input\")&&Q.get(t,\"click\")||A(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},k.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},k.Event=function(e,t){if(!(this instanceof k.Event))return new k.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?ke:Se,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&&k.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[k.expando]=!0},k.Event.prototype={constructor:k.Event,isDefaultPrevented:Se,isPropagationStopped:Se,isImmediatePropagationStopped:Se,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=ke,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=ke,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=ke,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},k.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&&Te.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Ce.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},k.event.addProp),k.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){k.event.special[e]={setup:function(){return De(this,e,Ne),!1},trigger:function(){return De(this,e),!0},delegateType:t}}),k.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,i){k.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||k.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),k.fn.extend({on:function(e,t,n,r){return Ae(this,e,t,n,r)},one:function(e,t,n,r){return Ae(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,k(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=Se),this.each(function(){k.event.remove(this,e,n,t)})}});var je=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)[^>]*)\\/>/gi,qe=/<script|<style|<link/i,Le=/checked\\s*(?:[^=]|=\\s*.checked.)/i,He=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;function Oe(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&k(e).children(\"tbody\")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function Re(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n<r;n++)k.event.add(t,i,l[i][n]);J.hasData(e)&&(s=J.access(e),u=k.extend({},s),J.set(t,u))}}function Ie(n,r,i,o){r=g.apply([],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&&Le.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Ie(t,r,i,o)});if(f&&(t=(e=we(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=k.map(ve(e,\"script\"),Pe)).length;c<f;c++)u=e,c!==p&&(u=k.clone(u,!0,!0),s&&k.merge(a,ve(u,\"script\"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,k.map(a,Re),c=0;c<s;c++)u=a[c],he.test(u.type||\"\")&&!Q.access(u,\"globalEval\")&&k.contains(l,u)&&(u.src&&\"module\"!==(u.type||\"\").toLowerCase()?k._evalUrl&&!u.noModule&&k._evalUrl(u.src,{nonce:u.nonce||u.getAttribute(\"nonce\")}):b(u.textContent.replace(He,\"\"),u,l))}return n}function We(e,t,n){for(var r,i=t?k.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||k.cleanData(ve(r)),r.parentNode&&(n&&oe(r)&&ye(ve(r,\"script\")),r.parentNode.removeChild(r));return e}k.extend({htmlPrefilter:function(e){return e.replace(je,\"<$1></$2>\")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.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++)Me(o[r],a[r]);else Me(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=k.event.special,o=0;void 0!==(n=e[o]);o++)if(G(n)){if(t=n[Q.expando]){if(t.events)for(r in t.events)i[r]?k.event.remove(n,r):k.removeEvent(n,r,t.handle);n[Q.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),k.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return _(this,function(e){return void 0===e?k.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 Ie(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)})},prepend:function(){return Ie(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Ie(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&&(k.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 k.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&&!qe.test(e)&&!ge[(de.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=k.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(k.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 Ie(this,arguments,function(e){var t=this.parentNode;k.inArray(this,n)<0&&(k.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),k.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,a){k.fn[e]=function(e){for(var t,n=[],r=k(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),k(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var $e=new RegExp(\"^(\"+te+\")(?!px)[a-z%]+$\",\"i\"),Fe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Be=new RegExp(re.join(\"|\"),\"i\");function _e(e,t,n){var r,i,o,a,s=e.style;return(n=n||Fe(e))&&(\"\"!==(a=n.getPropertyValue(t)||n[t])||oe(e)||(a=k.style(e,t)),!y.pixelBoxStyles()&&$e.test(a)&&Be.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 ze(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(u){s.style.cssText=\"position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0\",u.style.cssText=\"position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%\",ie.appendChild(s).appendChild(u);var e=C.getComputedStyle(u);n=\"1%\"!==e.top,a=12===t(e.marginLeft),u.style.right=\"60%\",o=36===t(e.right),r=36===t(e.width),u.style.position=\"absolute\",i=12===t(u.offsetWidth/3),ie.removeChild(s),u=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s=E.createElement(\"div\"),u=E.createElement(\"div\");u.style&&(u.style.backgroundClip=\"content-box\",u.cloneNode(!0).style.backgroundClip=\"\",y.clearCloneStyle=\"content-box\"===u.style.backgroundClip,k.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),a},scrollboxSize:function(){return e(),i}}))}();var Ue=[\"Webkit\",\"Moz\",\"ms\"],Xe=E.createElement(\"div\").style,Ve={};function Ge(e){var t=k.cssProps[e]||Ve[e];return t||(e in Xe?e:Ve[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Ue.length;while(n--)if((e=Ue[n]+t)in Xe)return e}(e)||e)}var Ye=/^(none|table(?!-c[ea]).+)/,Qe=/^--/,Je={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Ke={letterSpacing:\"0\",fontWeight:\"400\"};function Ze(e,t,n){var r=ne.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function et(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+=k.css(e,n+re[a],!0,i)),r?(\"content\"===n&&(u-=k.css(e,\"padding\"+re[a],!0,i)),\"margin\"!==n&&(u-=k.css(e,\"border\"+re[a]+\"Width\",!0,i))):(u+=k.css(e,\"padding\"+re[a],!0,i),\"padding\"!==n?u+=k.css(e,\"border\"+re[a]+\"Width\",!0,i):s+=k.css(e,\"border\"+re[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 tt(e,t,n){var r=Fe(e),i=(!y.boxSizingReliable()||n)&&\"border-box\"===k.css(e,\"boxSizing\",!1,r),o=i,a=_e(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if($e.test(a)){if(!n)return a;a=\"auto\"}return(!y.boxSizingReliable()&&i||\"auto\"===a||!parseFloat(a)&&\"inline\"===k.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===k.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+et(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function nt(e,t,n,r,i){return new nt.prototype.init(e,t,n,r,i)}k.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=_e(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=V(t),u=Qe.test(t),l=e.style;if(u||(t=Ge(s)),a=k.cssHooks[t]||k.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=ne.exec(n))&&i[1]&&(n=le(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(k.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=V(t);return Qe.test(t)||(t=Ge(s)),(a=k.cssHooks[t]||k.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=_e(e,t,r)),\"normal\"===i&&t in Ke&&(i=Ke[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),k.each([\"height\",\"width\"],function(e,u){k.cssHooks[u]={get:function(e,t,n){if(t)return!Ye.test(k.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?tt(e,u,n):ue(e,Je,function(){return tt(e,u,n)})},set:function(e,t,n){var r,i=Fe(e),o=!y.scrollboxSize()&&\"absolute\"===i.position,a=(o||n)&&\"border-box\"===k.css(e,\"boxSizing\",!1,i),s=n?et(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e[\"offset\"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-et(e,u,\"border\",!1,i)-.5)),s&&(r=ne.exec(t))&&\"px\"!==(r[3]||\"px\")&&(e.style[u]=t,t=k.css(e,u)),Ze(0,t,s)}}}),k.cssHooks.marginLeft=ze(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(_e(e,\"marginLeft\"))||e.getBoundingClientRect().left-ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+\"px\"}),k.each({margin:\"\",padding:\"\",border:\"Width\"},function(i,o){k.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r=\"string\"==typeof e?e.split(\" \"):[e];t<4;t++)n[i+re[t]+o]=r[t]||r[t-2]||r[0];return n}},\"margin\"!==i&&(k.cssHooks[i+o].set=Ze)}),k.fn.extend({css:function(e,t){return _(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Fe(e),i=t.length;a<i;a++)o[t[a]]=k.css(e,t[a],!1,r);return o}return void 0!==n?k.style(e,t,n):k.css(e,t)},e,t,1<arguments.length)}}),((k.Tween=nt).prototype={constructor:nt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||k.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(k.cssNumber[n]?\"\":\"px\")},cur:function(){var e=nt.propHooks[this.prop];return e&&e.get?e.get(this):nt.propHooks._default.get(this)},run:function(e){var t,n=nt.propHooks[this.prop];return this.options.duration?this.pos=t=k.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):nt.propHooks._default.set(this),this}}).init.prototype=nt.prototype,(nt.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=k.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){k.fx.step[e.prop]?k.fx.step[e.prop](e):1!==e.elem.nodeType||!k.cssHooks[e.prop]&&null==e.elem.style[Ge(e.prop)]?e.elem[e.prop]=e.now:k.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=nt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},k.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},k.fx=nt.prototype.init,k.fx.step={};var rt,it,ot,at,st=/^(?:toggle|show|hide)$/,ut=/queueHooks$/;function lt(){it&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(lt):C.setTimeout(lt,k.fx.interval),k.fx.tick())}function ct(){return C.setTimeout(function(){rt=void 0}),rt=Date.now()}function ft(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=re[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function pt(e,t,n){for(var r,i=(dt.tweeners[t]||[]).concat(dt.tweeners[\"*\"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function dt(o,e,t){var n,a,r=0,i=dt.prefilters.length,s=k.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=rt||ct(),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:k.extend({},e),opts:k.extend(!0,{specialEasing:{},easing:k.easing._default},t),originalProperties:e,originalOptions:t,startTime:rt||ct(),duration:t.duration,tweens:[],createTween:function(e,t){var n=k.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=V(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=k.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=dt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(k._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return k.map(c,pt,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),k.fx.timer(k.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}k.Animation=k.extend(dt,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return le(n.elem,e,ne.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=[\"*\"]):e=e.match(R);for(var n,r=0,i=e.length;r<i;r++)n=e[r],dt.tweeners[n]=dt.tweeners[n]||[],dt.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&&se(e),v=Q.get(e,\"fxshow\");for(r in n.queue||(null==(a=k._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--,k.queue(e,\"fx\").length||a.empty.fire()})})),t)if(i=t[r],st.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]||k.style(e,r)}if((u=!k.isEmptyObject(t))||!k.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Q.get(e,\"display\")),\"none\"===(c=k.css(e,\"display\"))&&(l?c=l:(fe([e],!0),l=e.style.display||l,c=k.css(e,\"display\"),fe([e]))),(\"inline\"===c||\"inline-block\"===c&&null!=l)&&\"none\"===k.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=Q.access(e,\"fxshow\",{display:l}),o&&(v.hidden=!g),g&&fe([e],!0),p.done(function(){for(r in g||fe([e]),Q.remove(e,\"fxshow\"),d)k.style(e,r,d[r])})),u=pt(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?dt.prefilters.unshift(e):dt.prefilters.push(e)}}),k.speed=function(e,t,n){var r=e&&\"object\"==typeof e?k.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return k.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in k.fx.speeds?r.duration=k.fx.speeds[r.duration]:r.duration=k.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&&k.dequeue(this,r.queue)},r},k.fn.extend({fadeTo:function(e,t,n,r){return this.filter(se).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=k.isEmptyObject(t),o=k.speed(e,n,r),a=function(){var e=dt(this,k.extend({},t),o);(i||Q.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&&!1!==i&&this.queue(i||\"fx\",[]),this.each(function(){var e=!0,t=null!=i&&i+\"queueHooks\",n=k.timers,r=Q.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&ut.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||k.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||\"fx\"),this.each(function(){var e,t=Q.get(this),n=t[a+\"queue\"],r=t[a+\"queueHooks\"],i=k.timers,o=n?n.length:0;for(t.finish=!0,k.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})}}),k.each([\"toggle\",\"show\",\"hide\"],function(e,r){var i=k.fn[r];k.fn[r]=function(e,t,n){return null==e||\"boolean\"==typeof e?i.apply(this,arguments):this.animate(ft(r,!0),e,t,n)}}),k.each({slideDown:ft(\"show\"),slideUp:ft(\"hide\"),slideToggle:ft(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,r){k.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),k.timers=[],k.fx.tick=function(){var e,t=0,n=k.timers;for(rt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||k.fx.stop(),rt=void 0},k.fx.timer=function(e){k.timers.push(e),k.fx.start()},k.fx.interval=13,k.fx.start=function(){it||(it=!0,lt())},k.fx.stop=function(){it=null},k.fx.speeds={slow:600,fast:200,_default:400},k.fn.delay=function(r,e){return r=k.fx&&k.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)}})},ot=E.createElement(\"input\"),at=E.createElement(\"select\").appendChild(E.createElement(\"option\")),ot.type=\"checkbox\",y.checkOn=\"\"!==ot.value,y.optSelected=at.selected,(ot=E.createElement(\"input\")).value=\"t\",ot.type=\"radio\",y.radioValue=\"t\"===ot.value;var ht,gt=k.expr.attrHandle;k.fn.extend({attr:function(e,t){return _(this,k.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){k.removeAttr(this,e)})}}),k.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return\"undefined\"==typeof e.getAttribute?k.prop(e,t,n):(1===o&&k.isXMLDoc(e)||(i=k.attrHooks[t.toLowerCase()]||(k.expr.match.bool.test(t)?ht:void 0)),void 0!==n?null===n?void k.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=k.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(R);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ht={set:function(e,t,n){return!1===t?k.removeAttr(e,n):e.setAttribute(n,n),n}},k.each(k.expr.match.bool.source.match(/\\w+/g),function(e,t){var a=gt[t]||k.find.attr;gt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=gt[o],gt[o]=r,r=null!=a(e,t,n)?o:null,gt[o]=i),r}});var vt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;function mt(e){return(e.match(R)||[]).join(\" \")}function xt(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function bt(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(R)||[]}k.fn.extend({prop:function(e,t){return _(this,k.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[k.propFix[e]||e]})}}),k.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&k.isXMLDoc(e)||(t=k.propFix[t]||t,i=k.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=k.find.attr(e,\"tabindex\");return t?parseInt(t,10):vt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),y.optSelected||(k.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)}}),k.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){k.propFix[this.toLowerCase()]=this}),k.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){k(this).addClass(t.call(this,e,xt(this)))});if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&\" \"+mt(i)+\" \"){a=0;while(o=e[a++])r.indexOf(\" \"+o+\" \")<0&&(r+=o+\" \");i!==(s=mt(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){k(this).removeClass(t.call(this,e,xt(this)))});if(!arguments.length)return this.attr(\"class\",\"\");if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&\" \"+mt(i)+\" \"){a=0;while(o=e[a++])while(-1<r.indexOf(\" \"+o+\" \"))r=r.replace(\" \"+o+\" \",\" \");i!==(s=mt(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){k(this).toggleClass(i.call(this,e,xt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=k(this),r=bt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&\"boolean\"!==o||((e=xt(this))&&Q.set(this,\"__className__\",e),this.setAttribute&&this.setAttribute(\"class\",e||!1===i?\"\":Q.get(this,\"__className__\")||\"\"))})},hasClass:function(e){var t,n,r=0;t=\" \"+e+\" \";while(n=this[r++])if(1===n.nodeType&&-1<(\" \"+mt(xt(n))+\" \").indexOf(t))return!0;return!1}});var wt=/\\r/g;k.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,k(this).val()):n)?t=\"\":\"number\"==typeof t?t+=\"\":Array.isArray(t)&&(t=k.map(t,function(e){return null==e?\"\":e+\"\"})),(r=k.valHooks[this.type]||k.valHooks[this.nodeName.toLowerCase()])&&\"set\"in r&&void 0!==r.set(this,t,\"value\")||(this.value=t))})):t?(r=k.valHooks[t.type]||k.valHooks[t.nodeName.toLowerCase()])&&\"get\"in r&&void 0!==(e=r.get(t,\"value\"))?e:\"string\"==typeof(e=t.value)?e.replace(wt,\"\"):null==e?\"\":e:void 0}}),k.extend({valHooks:{option:{get:function(e){var t=k.find.attr(e,\"value\");return null!=t?t:mt(k.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=k(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=k.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<k.inArray(k.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),k.each([\"radio\",\"checkbox\"],function(){k.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<k.inArray(k(e).val(),t)}},y.checkOn||(k.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}),y.focusin=\"onfocusin\"in C;var Tt=/^(?:focusinfocus|focusoutblur)$/,Ct=function(e){e.stopPropagation()};k.extend(k.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&&!Tt.test(d+k.event.triggered)&&(-1<d.indexOf(\".\")&&(d=(h=d.split(\".\")).shift(),h.sort()),u=d.indexOf(\":\")<0&&\"on\"+d,(e=e[k.expando]?e:new k.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]:k.makeArray(t,[e]),c=k.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,Tt.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=(Q.get(o,\"events\")||{})[e.type]&&Q.get(o,\"handle\"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&G(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)||!G(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),k.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Ct),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Ct),k.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=k.extend(new k.Event,n,{type:e,isSimulated:!0});k.event.trigger(r,null,t)}}),k.fn.extend({trigger:function(e,t){return this.each(function(){k.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return k.event.trigger(e,t,n,!0)}}),y.focusin||k.each({focus:\"focusin\",blur:\"focusout\"},function(n,r){var i=function(e){k.event.simulate(r,e.target,k.event.fix(e))};k.event.special[r]={setup:function(){var e=this.ownerDocument||this,t=Q.access(e,r);t||e.addEventListener(n,i,!0),Q.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this,t=Q.access(e,r)-1;t?Q.access(e,r,t):(e.removeEventListener(n,i,!0),Q.remove(e,r))}}});var Et=C.location,kt=Date.now(),St=/\\?/;k.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||k.error(\"Invalid XML: \"+e),t};var Nt=/\\[\\]$/,At=/\\r?\\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;function qt(n,e,r,i){var t;if(Array.isArray(e))k.each(e,function(e,t){r||Nt.test(n)?i(n,t):qt(n+\"[\"+(\"object\"==typeof t&&null!=t?e:\"\")+\"]\",t,r,i)});else if(r||\"object\"!==w(e))i(n,e);else for(t in e)qt(n+\"[\"+t+\"]\",e[t],r,i)}k.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&&!k.isPlainObject(e))k.each(e,function(){i(this.name,this.value)});else for(n in e)qt(n,e[n],t,i);return r.join(\"&\")},k.fn.extend({serialize:function(){return k.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=k.prop(this,\"elements\");return e?k.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!k(this).is(\":disabled\")&&jt.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=k(this).val();return null==n?null:Array.isArray(n)?k.map(n,function(e){return{name:t.name,value:e.replace(At,\"\\r\\n\")}}):{name:t.name,value:n.replace(At,\"\\r\\n\")}}).get()}});var Lt=/%20/g,Ht=/#.*$/,Ot=/([?&])_=[^&]*/,Pt=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Rt=/^(?:GET|HEAD)$/,Mt=/^\\/\\//,It={},Wt={},$t=\"*/\".concat(\"*\"),Ft=E.createElement(\"a\");function Bt(o){return function(e,t){\"string\"!=typeof e&&(t=e,e=\"*\");var n,r=0,i=e.toLowerCase().match(R)||[];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 _t(t,i,o,a){var s={},u=t===Wt;function l(e){var r;return s[e]=!0,k.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 zt(e,t){var n,r,i=k.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&k.extend(!0,e,r),e}Ft.href=Et.href,k.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":$t,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\":k.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,k.ajaxSettings),t):zt(k.ajaxSettings,e)},ajaxPrefilter:Bt(It),ajaxTransport:Bt(Wt),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=k.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?k(y):k.event,x=k.Deferred(),b=k.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=Pt.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||Et.href)+\"\").replace(Mt,Et.protocol+\"//\"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||\"*\").toLowerCase().match(R)||[\"\"],null==v.crossDomain){r=E.createElement(\"a\");try{r.href=v.url,r.href=r.href,v.crossDomain=Ft.protocol+\"//\"+Ft.host!=r.protocol+\"//\"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&\"string\"!=typeof v.data&&(v.data=k.param(v.data,v.traditional)),_t(It,v,t,T),h)return T;for(i in(g=k.event&&v.global)&&0==k.active++&&k.event.trigger(\"ajaxStart\"),v.type=v.type.toUpperCase(),v.hasContent=!Rt.test(v.type),f=v.url.replace(Ht,\"\"),v.hasContent?v.data&&v.processData&&0===(v.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(v.data=v.data.replace(Lt,\"+\")):(o=v.url.slice(f.length),v.data&&(v.processData||\"string\"==typeof v.data)&&(f+=(St.test(f)?\"&\":\"?\")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Ot,\"$1\"),o=(St.test(f)?\"&\":\"?\")+\"_=\"+kt+++o),v.url=f+o),v.ifModified&&(k.lastModified[f]&&T.setRequestHeader(\"If-Modified-Since\",k.lastModified[f]),k.etag[f]&&T.setRequestHeader(\"If-None-Match\",k.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]?\", \"+$t+\"; 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=_t(Wt,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)),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\"))&&(k.lastModified[f]=u),(u=T.getResponseHeader(\"etag\"))&&(k.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]),--k.active||k.event.trigger(\"ajaxStop\")))}return T},getJSON:function(e,t,n){return k.get(e,t,n,\"json\")},getScript:function(e,t){return k.get(e,void 0,t,\"script\")}}),k.each([\"get\",\"post\"],function(e,i){k[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),k.ajax(k.extend({url:e,type:i,dataType:r,data:t,success:n},k.isPlainObject(e)&&e))}}),k._evalUrl=function(e,t){return k.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){k.globalEval(e,t)}})},k.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=k(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){k(this).wrapInner(n.call(this,e))}):this.each(function(){var e=k(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){k(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){k(this).replaceWith(this.childNodes)}),this}}),k.expr.pseudos.hidden=function(e){return!k.expr.pseudos.visible(e)},k.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},k.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},Xt=k.ajaxSettings.xhr();y.cors=!!Xt&&\"withCredentials\"in Xt,y.ajax=Xt=!!Xt,k.ajaxTransport(function(i){var o,a;if(y.cors||Xt&&!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(Ut[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()}}}),k.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),k.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 k.globalEval(e),e}}}),k.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")}),k.ajaxTransport(\"script\",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=k(\"<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 Vt,Gt=[],Yt=/(=)\\?(?=&|$)|\\?\\?/;k.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=Gt.pop()||k.expando+\"_\"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Yt.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(Yt,\"$1\"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||k.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?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"<form></form><form></form>\",2===Vt.childNodes.length),k.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=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1<s&&(r=mt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&\"object\"==typeof t&&(i=\"POST\"),0<a.length&&k.ajax({url:e,type:i||\"GET\",dataType:\"html\",data:t}).done(function(e){o=arguments,a.html(r?k(\"<div>\").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,\"position\"),c=k(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=k.css(e,\"top\"),u=k.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,k.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):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.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\"===k.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\"===k.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,\"borderTopWidth\",!0),i.left+=k.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-k.css(r,\"marginTop\",!0),left:t.left-i.left-k.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===k.css(e,\"position\"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;k.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)}}),k.each([\"top\",\"left\"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+\"px\":t})}),k.each({Height:\"height\",Width:\"width\"},function(a,s){k.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){k.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?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.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){k.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}}),k.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),k.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)}}),k.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||k.guid++,i},k.holdReady=function(e){e?k.readyWait++:k.ready(!0)},k.isArray=Array.isArray,k.parseJSON=JSON.parse,k.nodeName=A,k.isFunction=m,k.isWindow=x,k.camelCase=V,k.type=w,k.now=Date.now,k.isNumeric=function(e){var t=k.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return k});var Qt=C.jQuery,Jt=C.$;return k.noConflict=function(e){return C.$===k&&(C.$=Jt),e&&C.jQuery===k&&(C.jQuery=Qt),k},e||(C.jQuery=C.$=k),k});\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.0\n * JavaScript is all like \"You images are done yet or what?\"\n * MIT License\n */\n!function(t,e){\"function\"==typeof define&&define.amd?define(\"ev-emitter/ev-emitter\",e):\"object\"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}(this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||[];return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){\"use strict\";\"function\"==typeof define&&define.amd?define([\"ev-emitter/ev-emitter\"],function(i){return e(t,i)}):\"object\"==typeof module&&module.exports?module.exports=e(t,require(\"ev-emitter\")):t.imagesLoaded=e(t,t.EvEmitter)}(window,function(t,e){function i(t,e){for(var i in e)t[i]=e[i];return t}function n(t){var e=[];if(Array.isArray(t))e=t;else if(\"number\"==typeof t.length)for(var i=0;i<t.length;i++)e.push(t[i]);else e.push(t);return e}function o(t,e,r){return this instanceof o?(\"string\"==typeof t&&(t=document.querySelectorAll(t)),this.elements=n(t),this.options=i({},this.options),\"function\"==typeof e?r=e:i(this.options,e),r&&this.on(\"always\",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(function(){this.check()}.bind(this))):new o(t,e,r)}function r(t){this.img=t}function s(t,e){this.url=t,this.element=e,this.img=new Image}var h=t.jQuery,a=t.console;o.prototype=Object.create(e.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(t){\"IMG\"==t.nodeName&&this.addImage(t),this.options.background===!0&&this.addElementBackgroundImages(t);var e=t.nodeType;if(e&&d[e]){for(var i=t.querySelectorAll(\"img\"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if(\"string\"==typeof this.options.background){var r=t.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var d={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(t){var e=getComputedStyle(t);if(e)for(var i=/url\\((['\"])?(.*?)\\1\\)/gi,n=i.exec(e.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,t),n=i.exec(e.backgroundImage)}},o.prototype.addImage=function(t){var e=new r(t);this.images.push(e)},o.prototype.addBackground=function(t,e){var i=new s(t,e);this.images.push(i)},o.prototype.check=function(){function t(t,i,n){setTimeout(function(){e.progress(t,i,n)})}var e=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(e){e.once(\"progress\",t),e.check()}):void this.complete()},o.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent(\"progress\",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log(\"progress: \"+i,t,e)},o.prototype.complete=function(){var t=this.hasAnyBroken?\"fail\":\"done\";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent(\"always\",[this]),this.jqDeferred){var e=this.hasAnyBroken?\"reject\":\"resolve\";this.jqDeferred[e](this)}},r.prototype=Object.create(e.prototype),r.prototype.check=function(){var t=this.getIsImageComplete();return t?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&&void 0!==this.img.naturalWidth},r.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent(\"progress\",[this,this.img,e])},r.prototype.handleEvent=function(t){var e=\"on\"+t.type;this[e]&&this[e](t)},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 t=this.getIsImageComplete();t&&(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(t,e){this.isLoaded=t,this.emitEvent(\"progress\",[this,this.element,e])},o.makeJQueryPlugin=function(e){e=e||t.jQuery,e&&(h=e,h.fn.imagesLoaded=function(t,e){var i=new o(this,t,e);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\";\n}\n[data-icon-after]:after {\n\tcontent: \"\\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.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</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: 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   = Object.freeze({\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   = Object.freeze({\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\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\tescape       : { value : utilEscape },\n\t\tunescape     : { value : utilUnescape },\n\t\tcharAndPosAt : { value : utilCharAndPosAt },\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\n\t\t/*\n\t\t\tTime Functions.\n\t\t*/\n\t\tnow : { value : utilNow },\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/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 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\t/* none */\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\t/* none */\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\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\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, Patterns, 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(name) {\n\t\tconst varData = _parseVariableChain(name);\n\n\t\tif (varData === null) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst pNames = varData.names;\n\t\tlet retVal = varData.store;\n\n\t\tfor (let i = 0, iend = pNames.length; i < iend; ++i) {\n\t\t\tif (typeof retVal[pNames[i]] === 'undefined') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tretVal = retVal[pNames[i]];\n\t\t}\n\n\t\treturn retVal;\n\t}\n\n\t/*\n\t\tSets the value of the given story/temporary variable.\n\t*/\n\tfunction variableSet(name, value) {\n\t\tconst varData = _parseVariableChain(name);\n\n\t\tif (varData === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst pNames  = varData.names;\n\t\tconst varName = pNames.pop();\n\t\tlet baseObj = varData.store;\n\n\t\tfor (let i = 0, iend = pNames.length; i < iend; ++i) {\n\t\t\tif (typeof baseObj[pNames[i]] === 'undefined') {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbaseObj = baseObj[pNames[i]];\n\t\t}\n\n\t\tbaseObj[varName] = value;\n\t\treturn true;\n\t}\n\n\t/*\n\t\tReturns the property name chain of the given story/temporary variable,\n\t\twhich may be of arbitrary complexity.\n\t*/\n\tconst _parseVarRegExp = new RegExp(`^(?:${Patterns.variableSigil}(${Patterns.identifier})|\\\\.(${Patterns.identifier})|\\\\[(?:(?:\"((?:\\\\\\\\.|[^\"\\\\\\\\])+)\")|(?:'((?:\\\\\\\\.|[^'\\\\\\\\])+)')|(${Patterns.variableSigil}${Patterns.identifierFirstChar}.*)|(\\\\d+))\\\\])`);\n\tfunction _parseVariableChain(varText) {\n\t\tconst retVal = {\n\t\t\tstore : varText[0] === '$' ? State.variables : State.temporary,\n\t\t\tnames : []\n\t\t};\n\t\tlet text  = varText;\n\t\tlet match;\n\n\t\twhile ((match = _parseVarRegExp.exec(text)) !== null) {\n\t\t\t// Remove full match from text.\n\t\t\ttext = text.slice(match[0].length);\n\n\t\t\t// Base variable.\n\t\t\tif (match[1]) {\n\t\t\t\tretVal.names.push(match[1]);\n\t\t\t}\n\n\t\t\t// Dot property.\n\t\t\telse if (match[2]) {\n\t\t\t\tretVal.names.push(match[2]);\n\t\t\t}\n\n\t\t\t// Square-bracketed property (double quoted).\n\t\t\telse if (match[3]) {\n\t\t\t\tretVal.names.push(match[3]);\n\t\t\t}\n\n\t\t\t// Square-bracketed property (single quoted).\n\t\t\telse if (match[4]) {\n\t\t\t\tretVal.names.push(match[4]);\n\t\t\t}\n\n\t\t\t// Square-bracketed property (embedded variable).\n\t\t\telse if (match[5]) {\n\t\t\t\tretVal.names.push(variableGet(match[5]));\n\t\t\t}\n\n\t\t\t// Square-bracketed property (numeric index).\n\t\t\telse if (match[6]) {\n\t\t\t\tretVal.names.push(Number(match[6]));\n\t\t\t}\n\t\t}\n\n\t\treturn text === '' ? retVal : null;\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 parseMap = Object.freeze({\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 varTest = new RegExp(`^${Patterns.variable}`);\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/*\n\t\t\t\t\tBarewords.\n\t\t\t\t*/\n\t\t\t\tif (match[5]) {\n\t\t\t\t\tlet token = match[5];\n\n\t\t\t\t\t/*\n\t\t\t\t\t\tIf the token is simply a dollar-sign or underscore, then it's either\n\t\t\t\t\t\tjust the raw character or, probably, a function alias, so skip it.\n\t\t\t\t\t*/\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/*\n\t\t\t\t\t\tIf the token is a story $variable or temporary _variable, reset it\n\t\t\t\t\t\tto just its sigil—for later mapping.\n\t\t\t\t\t*/\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/*\n\t\t\t\t\t\tIf the token is `is`, check to see if it's followed by `not`, if so,\n\t\t\t\t\t\tconvert them into the `isnot` operator.\n\n\t\t\t\t\t\tNOTE: This is a safety feature, since `$a is not $b` probably sounds\n\t\t\t\t\t\treasonable to most users.\n\t\t\t\t\t*/\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 part  = code.slice(start);\n\n\t\t\t\t\t\tif (/^\\s+not\\b/.test(part)) {\n\t\t\t\t\t\t\tcode = code.splice(start, part.search(/\\S/));\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/*\n\t\t\t\t\t\tIf the finalized token has a mapping, replace it within the code string\n\t\t\t\t\t\twith its counterpart.\n\n\t\t\t\t\t\tNOTE: We must use `parseMap.hasOwnProperty(token)` here, rather than\n\t\t\t\t\t\tsimply using something like `parseMap[token]`, otherwise tokens which\n\t\t\t\t\t\tmatch properties from the prototype chain will cause shenanigans.\n\t\t\t\t\t*/\n\t\t\t\t\tif (parseMap.hasOwnProperty(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\tparseMap[token] // replacement string\n\t\t\t\t\t\t);\n\t\t\t\t\t\tparseRe.lastIndex += parseMap[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) {\n\t\treturn (function (code, output) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, String(code), output);\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) {\n\t\treturn (function (code, output) {\n\t\t\treturn eval(code);\n\t\t}).call(output ? { output } : null, parse(String(code)), output);\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 (macro.hasOwnProperty('tags')) {\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 (macro.hasOwnProperty('_MACRO_API')) {\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 ${macro.hasOwnProperty('handler') ? 'is not a function' : 'does not exist'}`,\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\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\t--opened;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\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 (macro.hasOwnProperty('skipArgs')) {\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 (macro.hasOwnProperty('skipArg0')) {\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, clone, 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, deep) {\n\t\tif (Array.isArray(name)) {\n\t\t\tname.forEach(name => macrosAdd(name, def, deep));\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_macros[name] = deep ? clone(def) : def;\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] = deep ? clone(_macros[def]) : _macros[def];\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\n\t\t\t/* legacy */\n\t\t\t/*\n\t\t\t\tSince `macrosGet()` may return legacy macros, we have to add a flag to (modern)\n\t\t\t\tAPI macros, so that the macro formatter will know how to call the macro.\n\t\t\t*/\n\t\t\t_macros[name]._MACRO_API = true;\n\t\t\t/* /legacy */\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 (_macros[name].hasOwnProperty('tags')) {\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 (_macros[name].hasOwnProperty('tags')) {\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\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 : 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.name,\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.name}>>: ${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, Patterns, Scripting, SimpleAudio, State, Story,\n\t       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\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 varRe = new RegExp(`(${Patterns.variable})`,'g');\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 = varRe.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\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 re = new RegExp(\n\t\t\t\t`State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})`,\n\t\t\t\t'g'\n\t\t\t);\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.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\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 re       = new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g');\n\t\t\tlet match;\n\n\t\t\twhile ((match = re.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\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 re       = new RegExp(`State\\\\.variables\\\\.(${Patterns.identifier})`, 'g');\n\t\t\tlet match;\n\t\t\tlet needStore = false;\n\n\t\t\twhile ((match = re.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[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\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\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 (/^\\s*if\\b/i.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&& /[^!=&^|<>*/%+-]=[^=>]/.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\t_hasRangeRe : new RegExp(`^\\\\S${Patterns.anyChar}*?\\\\s+range\\\\s+\\\\S${Patterns.anyChar}*?$`),\n\t\t_rangeRe    : new RegExp(`^(?:State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s*,\\\\s*)?State\\\\.(variables|temporary)\\\\.(${Patterns.identifier})\\\\s+range\\\\s+(\\\\S${Patterns.anyChar}*?)$`),\n\t\t_3PartRe    : /^([^;]*?)\\s*;\\s*([^;]*?)\\s*;\\s*([^;]*?)$/,\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 (/^\\S+\\s+in\\s+\\S+/i.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 (/^\\S+\\s+of\\s+\\S+/i.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._3PartRe);\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\t_handleFor(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\t_handleForRange(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\t_toRangeList(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\tif (this.args.length > 3 && this.args[3] === 'checked') {\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tState.setVar(varName, uncheckValue);\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\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 && /^(?:transition|t8n)$/.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<<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\tif (this.args.length > 2 && this.args[2] === 'checked') {\n\t\t\t\tel.checked = true;\n\t\t\t\tState.setVar(varName, checkValue);\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<<textbox>>\n\t*/\n\tMacro.add('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 varId        = Util.slugify(varName);\n\t\t\tconst defaultValue = this.args[1];\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/*\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     : '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, 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, 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/*\n\t\t\t\tSet the variable and input element to the default value.\n\t\t\t*/\n\t\t\tState.setVar(varName, defaultValue);\n\t\t\tel.value = defaultValue;\n\n\t\t\t/*\n\t\t\t\tAutofocus the input 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\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 && /^(?:transition|t8n)$/.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\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 && /^(?:transition|t8n)$/.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\tconst turnId = State.turns;\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 the timer if the turn IDs do not match.\n\t\t\t\tif (turnId !== State.turns) {\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\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 && /^(?:transition|t8n)$/.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\tconst turnId = State.turns;\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\tif (turnId !== State.turns) {\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.on('click.dialog-close', '.ui-close', { closeFn }, dialogClose)\n\t\t\t.on('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\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\t_hideOutlines(); // initially hide outlines\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.escape(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 (!node.hasChildNodes()) {\n\t\t\t\treturn '';\n\t\t\t}\n\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 (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, storage */\n\nvar Save = (() => { // eslint-disable-line no-unused-vars, no-var\n\t'use strict';\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), 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));\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) : 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)));\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) {\n\t\tif (DEBUG) { console.log('[Save/_marshal()]'); }\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);\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\t(new 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('Alert', '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\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, Save.slots.save)\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\t() => Save.export()\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\t// case 'symbol':\n\t\t// \treturn `Symbol\\u202F\"${String(value).slice(7, -1)}\"`;\n\n\t\tcase 'function':\n\t\t\t// return JSON.stringify(value.toString());\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      : 31,\n\tpatch      : 1,\n\tprerelease : null,\n\tbuild      : 49,\n\tdate       : new Date(\"2020-06-11T20:35:17.305Z\"),\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.1","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.1): 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 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\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// Push our typing handler onto the queue.\n\t\t\tTempState.macroTypeQueue.push(() => {\n\t\t\t\tconst $wrapper = jQuery(document.createElement(elTag))\n\t\t\t\t\t.addClass(className);\n\n\t\t\t\t// Add the user ID, if any.\n\t\t\t\tif (elId) {\n\t\t\t\t\t$wrapper.attr('id', elId);\n\t\t\t\t}\n\n\t\t\t\t// Add the user class(es), if any.\n\t\t\t\tif (elClass) {\n\t\t\t\t\t$wrapper.addClass(elClass);\n\t\t\t\t}\n\n\t\t\t\tnew Wikifier($wrapper, contents);\n\n\t\t\t\tconst passage = State.passage;\n\n\t\t\t\t// Skip typing if….\n\t\t\t\tif (\n\t\t\t\t\t// …we've visited the passage before.\n\t\t\t\t\t!Config.macros.typeVisitedPassages\n\t\t\t\t\t&& State.passages.slice(0, -1).some(title => title === passage)\n\n\t\t\t\t\t// …there were any content errors.\n\t\t\t\t\t|| $wrapper.find('.error').length > 0\n\t\t\t\t) {\n\t\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t// Run the next typing handler in the queue, if any.\n\t\t\t\t\tif (TempState.macroTypeQueue.length > 0) {\n\t\t\t\t\t\tTempState.macroTypeQueue.first()();\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Create a new `NodeTyper` instance for the wrapper's contents and\n\t\t\t\t// replace the target with the typing wrapper.\n\t\t\t\tconst typer = new NodeTyper({\n\t\t\t\t\ttargetNode : $wrapper.get(0),\n\t\t\t\t\tclassNames : cursor === 'none' ? null : `${className}-cursor`\n\t\t\t\t});\n\t\t\t\t$target.replaceWith($wrapper);\n\n\t\t\t\t// Set up event IDs.\n\t\t\t\tconst typingCompleteId = ':typingcomplete';\n\t\t\t\tconst typingStartId    = ':typingstart';\n\t\t\t\tconst typingStopId     = ':typingstop';\n\t\t\t\tconst keydownAndNS     = `keydown${namespace}`;\n\t\t\t\tconst typingStopAndNS  = `${typingStopId}${namespace}`;\n\n\t\t\t\t// Set up handlers for spacebar aborting and continuations.\n\t\t\t\t$(document)\n\t\t\t\t\t.off(keydownAndNS)\n\t\t\t\t\t.on(keydownAndNS, ev => {\n\t\t\t\t\t\t// Finish typing if the player aborts via the skip key.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tUtil.scrubEventKey(ev.key) === skipKey\n\t\t\t\t\t\t\t&& (ev.target === document.body || ev.target === document.documentElement)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\t\t$(document).off(keydownAndNS);\n\t\t\t\t\t\t\ttyper.finish();\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.one(typingStopAndNS, () => {\n\t\t\t\t\t\t// Fire the typing complete event and return, if the queue is empty.\n\t\t\t\t\t\tif (TempState.macroTypeQueue.length === 0) {\n\t\t\t\t\t\t\tjQuery.event.trigger(typingCompleteId);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Run the next typing handler in the queue.\n\t\t\t\t\t\tTempState.macroTypeQueue.first()();\n\t\t\t\t\t});\n\n\t\t\t\t// Set up the typing interval and start/stop event firing.\n\t\t\t\tconst typeNode = function typeNode() {\n\t\t\t\t\t// Fire the typing start event.\n\t\t\t\t\t$wrapper.trigger(typingStartId);\n\n\t\t\t\t\tconst typeNodeId = setInterval(() => {\n\t\t\t\t\t\t// Stop typing if….\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t// …we've navigated away.\n\t\t\t\t\t\t\tState.passage !== passage\n\n\t\t\t\t\t\t\t// …we're done typing.\n\t\t\t\t\t\t\t|| !typer.type()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tclearInterval(typeNodeId);\n\n\t\t\t\t\t\t\t// Remove this handler from the queue.\n\t\t\t\t\t\t\tTempState.macroTypeQueue.shift();\n\n\t\t\t\t\t\t\t// Fire the typing stop event.\n\t\t\t\t\t\t\t$wrapper.trigger(typingStopId);\n\n\t\t\t\t\t\t\t// Add the done class to the wrapper.\n\t\t\t\t\t\t\t$wrapper.addClass(`${className}-done`);\n\n\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\tif (cursor === 'keep') {\n\t\t\t\t\t\t\t\t$wrapper.addClass(`${className}-cursor`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}, speed);\n\t\t\t\t};\n\n\t\t\t\t// Kick off typing the node.\n\t\t\t\tif (start) {\n\t\t\t\t\tsetTimeout(typeNode, start);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\ttypeNode();\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()());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tTempState.macroTypeQueue.first()();\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\tconst turnId = State.turns;\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 the timer if the turn IDs do not match.\n\t\t\t\tif (turnId !== State.turns) {\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\tconst turnId = State.turns;\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\tif (turnId !== State.turns) {\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\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\t_hideOutlines(); // initially hide outlines\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\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, Save.slots.save)\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\t() => Save.export()\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      : 1,\n\tprerelease : null,\n\tbuild      : 52,\n\tdate       : new Date(\"2020-08-01T12:25:33.326Z\"),\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/js/002-config/fc-js-init.js b/js/002-config/fc-js-init.js
index b2f6656f91a1bfb95e24d6aa2b023b25cdcd3a5e..4c6933ee222c41cddc34066d37e09388f5b78d5c 100644
--- a/js/002-config/fc-js-init.js
+++ b/js/002-config/fc-js-init.js
@@ -54,6 +54,7 @@ App.Medicine.Modification.Brands = {};
 App.Medicine.Modification.Select = {};
 App.Medicine.OrganFarm = {};
 App.Medicine.OrganFarm.Organs = {};
+App.Medicine.Salon = {};
 App.Medicine.Surgery = {};
 App.RA = {};
 App.Reminders = {};
diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index 77854e982ad0a54bd4b81558fee78ffe1129e132..34642f8503a2c312e79a5b4a84e83284e7afa150 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -320,10 +320,20 @@ App.Data.resetOnNGPlus = {
 		oralAppeal: 0,
 		vaginalAppeal: 0,
 		analAppeal: 0,
-		sexualOpeness: 0
+		sexualOpeness: 0,
+		bestialityOpeness: 0
 	},
 
-	FCTV: {},
+	FCTV: {
+		receiver: -1,
+		channel: {},
+		pcViewership: {
+			count: 0,
+			frequency: 4,
+		},
+		remote: 0,
+		weekEnabled: 0
+	},
 	assistant: {},
 	targetArcology: {fs: "New"},
 	readySlaves: 0,
@@ -367,9 +377,6 @@ App.Data.resetOnNGPlus = {
 	attackThisWeek: 0,
 	lastAttackWeeks: 0,
 	lastRebellionWeeks: 0,
-	hasFoughtOnce: 0,
-	hasFoughtMajorBattleOnce: 0,
-	hasRebelledOnce: 0,
 	majorBattle: 0,
 	PCvictoryStreak: 0,
 	PClossStreak: 0,
@@ -447,7 +454,6 @@ App.Data.resetOnNGPlus = {
 	smilingManProgress: 0,
 	investedFunds: 0,
 	relationshipLM: 0,
-	smilingManWeek: 0,
 	globalCrisisWeeks: 0,
 	smilingManFate: 4,
 
@@ -559,9 +565,12 @@ App.Data.resetOnNGPlus = {
 	eliteFailTimer: 0,
 	nurseryGrowthStimsSetting: 0,
 	MadamIgnoresFlaws: 0,
+	MadamNoSex: 0,
 	farmyardBreeding: 0,
+	farmyardRestraints: 0,
 	farmyardShows: 0,
 	DJignoresFlaws: 0,
+	DJnoSex: 0,
 	slaveFightingBG: 0,
 	HGSlaveSuccess: 0,
 	unMadam: 0,
@@ -768,10 +777,10 @@ App.Data.resetOnNGPlus = {
 	masterSuiteNameCaps: "The Master Suite",
 
 	// Nursery Subsection
-	nursery: 0,						/* counts the number of children the nursery can support */
+	nursery: 0,							/* counts the number of children the nursery can support */
 	nurseryNannies: 0,					/* counts the number of nannies the nursery can support */
 	nurseryBabies: 0,					/* counts the number of children currently in the nursery */
-	MatronInfluence: 0,				/* check for whether the children are influenced by the Matron */
+	MatronInfluence: 0,					/* check for whether the children are influenced by the Matron */
 	nannyInfluence: 0,					/* check for whether the children are influenced by the nannies */
 	nurseryDecoration: "standard",
 	nurseryWeight: 0,
@@ -790,25 +799,72 @@ App.Data.resetOnNGPlus = {
 	sortNurseryList: "Unsorted",
 	targetAgeNursery: 18,
 
-	// Farmyard Subsection %/
+	// Farmyard Subsection
 	farmyard: 0,
 	farmyardShowgirls: [],			/* array of farmhands putting on shows */
 	farmyardFarmers: [],			/* array of farmhands farming */
 	farmMenials: 0,
 	farmMenialsSpace: 0,
 	farmyardDecoration: "standard",
-	farmyardUpgrade: {
-		pump: 0, fertilizer: 0, hydroponics: 0, machinery: 0, seeds: 0
+	farmyardUpgrades: {
+		pump: 0,
+		fertilizer: 0,
+		hydroponics: 0,
+		machinery: 0,
+		seeds: 0,
 	},
 	farmyardCrops: 0,
-	farmyardStable: 0,
+	farmyardStables: 0,
 	farmyardKennels: 0,
 	farmyardCages: 0,
 	activeCanine: 0,
 	activeHooved: 0,
 	activeFeline: 0,
 	animalsBought: {
-		canines: 0, hooved: 0, felines: 0, labradorRetrievers: 0, germanShepherds: 0, goldenRetrievers: 0, frenchBulldogs: 0, bulldogs: 0, beagles: 0, poodles: 0, rottweilers: 0, yorkshireTerriers: 0, siberianHuskies: 0, horses: 0, bulls: 0, pigs: 0, siameses: 0, persians: 0, maineCoons: 0, ragdolls: 0, bengals: 0, abbysinians: 0, birmans: 0, orientalShorthairs: 0, sphynxes: 0, russianBlues: 0, wolves: 0, foxes: 0, jackals: 0, dingos: 0, zebras: 0, cougars: 0, jaguars: 0, pumas: 0, lynx: 0, leopards: 0, lions: 0, tigers: 0
+		canines: 0,
+		felines: 0,
+		hooved: 0,
+
+		beagles: 0,
+		bulldogs: 0,
+		frenchBulldogs: 0,
+		germanShepherds: 0,
+		goldenRetrievers: 0,
+		labradorRetrievers: 0,
+		poodles: 0,
+		rottweilers: 0,
+		siberianHuskies: 0,
+		yorkshireTerriers: 0,
+
+		bulls: 0,
+		horses: 0,
+		pigs: 0,
+
+		abbysinians: 0,
+		bengals: 0,
+		birmans: 0,
+		maineCoons: 0,
+		orientalShorthairs: 0,
+		persians: 0,
+		ragdolls: 0,
+		russianBlues: 0,
+		siameses: 0,
+		sphynxes: 0,
+
+		dingos: 0,
+		foxes: 0,
+		jackals: 0,
+		wolves: 0,
+
+		zebras: 0,
+
+		cougars: 0,
+		jaguars: 0,
+		leopards: 0,
+		lions: 0,
+		lynx: 0,
+		pumas: 0,
+		tigers: 0,
 	},
 	canines: [],
 	hooved: [],
@@ -990,6 +1046,9 @@ App.Data.resetOnNGPlus = {
 	TSS: {
 		schoolUpgrade: 0, schoolPresent: 0, schoolProsperity: 0, subsidize: 0, schoolAnnexed: 0, studentsBought: 0, schoolSale: 0
 	},
+	TUO: {
+		schoolUpgrade: 0, schoolPresent: 0, schoolProsperity: 0, subsidize: 0, schoolAnnexed: 0, studentsBought: 0, schoolSale: 0
+	},
 	GRI: {
 		schoolUpgrade: 0, schoolPresent: 0, schoolProsperity: 0, subsidize: 0, schoolAnnexed: 0, studentsBought: 0, schoolSale: 0
 	},
@@ -1023,39 +1082,31 @@ App.Data.resetOnNGPlus = {
 	weddingPlanned: 0,
 	/** @type {string|Array<{ID:number, trainingRegimen:string}>} */
 	personalAttention: "sex",
-	/** @type {FC.SlaveStateOrZero}  */
-	HeadGirl: 0,
+	HeadGirlID: 0,
 	HGTimeInGrade: 0,
 	HGEnergy: 0,
 	HGCum: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Recruiter: 0,
+	RecruiterID: 0,
 	recruiterTarget: "desperate whores",
 	recruiterProgress: 0,
 	recruiterIdleRule: "number",
 	recruiterIdleNumber: 20,
 	recruiterIOUs: 0,
 	bodyguardTrains: 1,
-	/** @type {FC.SlaveStateOrZero} */
-	Bodyguard: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Madam: 0,
+	BodyguardID: 0,
+	MadamID: 0,
 	djID: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Milkmaid: 0,
+	MilkmaidID: 0,
 	milkmaidImpregnates: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Farmer: 0,
+	FarmerID: 0,
 	StewardessID: 0,
 	stewardessImpregnates: 0,
 	SchoolteacherID: 0,
 	AttendantID: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Matron: 0,
+	MatronID: 0,
 	NurseID: 0,
 	WardenessID: 0,
-	/** @type {FC.SlaveStateOrZero} */
-	Concubine: 0,
+	ConcubineID: 0,
 
 	justiceEvents: ["slave deal", "slave training", "majority deal", "indenture deal", "virginity deal", "breeding deal"], /* not in setupVars because we remove events from this array as they occur */
 	prisonCircuit: ["low tier criminals", "gangs and smugglers", "white collar", "military prison"],
diff --git a/js/003-data/miscData.js b/js/003-data/miscData.js
index 90610f5a58d21fbc6250685ace9cd24b6822d33e..f31f5ea9adff46fcb15115b8a03fcb24aa1389b6 100644
--- a/js/003-data/miscData.js
+++ b/js/003-data/miscData.js
@@ -237,7 +237,20 @@ App.Data.misc = {
 			fetusSize: [5000, 11000, 19000, 30000, 45000, 60000, 60000],
 			fetusRate: [4, 4, 4, 4, 4, 4, 4, 4],
 			sizeType: 2
-		}
+		},
+
+		insect: {
+			type: "insect",
+			normalOvaMin: 50,
+			normalOvaMax: 500,
+			normalBirth: 2,
+			minLiveBirth: 1,
+			drugsEffect: 0.0,
+			fetusWeek: [0, 1, 2, 3, 4, 99999],
+			fetusSize: [20, 350, 400, 800, 5000, 5000],
+			fetusRate: [4, 4, 4, 4, 4, 4],
+			sizeType: 2
+		},
 
 	},
 	/* Source data for canine include CTR (not head to toe size), so CTR do not apply anywhere, always 1.*/
@@ -1544,7 +1557,7 @@ App.Data.misc = {
 	/* 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"],
 
-	highHeels: ["boots", "extreme heels", "extreme platform heels", "heels", "platform heels", "pumps"],
+	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"],
diff --git a/js/003-data/policiesData.js b/js/003-data/policiesData.js
index ef65cc82ec9746c896fd5832379c0fe96928f40b..7690a24648db95d15bb5a31978d8980cefb01b63 100644
--- a/js/003-data/policiesData.js
+++ b/js/003-data/policiesData.js
@@ -319,6 +319,14 @@ App.Data.Policies.Selection = {
 				requirements: function() { return (V.arcologies[0].FSEgyptianRevivalist === "unset"); },
 			}
 		],
+		"policies.bestialityOpeness": [
+			{
+				title: "Bestiality Acceptance",
+				text: "you will use your personal influence to spur acceptance of bestiality.",
+				activatedText: "you are using your personal influence to spur the acceptance of bestiality.",
+				requirements: function() { return (V.seeBestiality === 1); },
+			}
+		]
 	},
 	PopulationPolicies: {
 		"policies.proRefugees": [
@@ -491,7 +499,7 @@ App.Data.Policies.Selection = {
 					const el = new DocumentFragment();
 					if (V.LurcherID !== 0) {
 						el.append(`Your current lurcher is `);
-						el.append(App.UI.DOM.slaveDescriptionDialog(getSlave(V.LurcherID)));
+						el.append(App.UI.DOM.slaveDescriptionDialog(S.Lurcher));
 					}
 					return el;
 				}
@@ -547,6 +555,22 @@ App.Data.Policies.Selection = {
 				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
 			}
 		],
+		"TUO.subsidize": [
+			{
+				title: "The Utopian Orphanage 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.",
+				requirements: function() { return (V.TUO.schoolProsperity < 10 && V.TUO.schoolPresent === 1); },
+				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
+			},
+			{
+				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.",
+				requirements: function() { return (V.TUO.schoolPresent === 1); },
+				get note() { return `Will cost ${cashFormat(1000)} weekly to maintain; does not cost reputation to start`; },
+			}
+		],
 		"GRI.subsidize": [
 			{
 				title: "The Growth Research Institute subsidy",
@@ -1690,8 +1714,8 @@ App.Data.Policies.Selection = {
 				titleClass: "lime",
 				text: "your Head Girl will be given legal status as your Consort.",
 				get activatedText() {
-					if (V.HeadGirl.ID) {
-						const {his} = getPronouns(getSlave(V.HeadGirl.ID));
+					if (S.HeadGirl) {
+						const {his} = getPronouns(S.HeadGirl);
 						return `your Head Girl holds legal status as your Consort, increasing ${his} prominence.`;
 					} else {
 						return `though you do not currently have a Head Girl, they would hold legal status as your Consort, increasing their prominence.`;
@@ -1712,7 +1736,7 @@ App.Data.Policies.Selection = {
 				note: `Will help advance Edo Revivalism`
 			}
 		],
-		"arcologies[0].FSEdoRevivalist >= 90": [
+		"arcologies[0].FSEdoRevivalistLaw": [
 			{
 				title: "Cultural Insularity",
 				titleClass: "lime",
@@ -1759,16 +1783,16 @@ App.Data.Policies.Selection = {
 				title: "Slave Administrator Enabling Law",
 				titleClass: "lime",
 				get text() {
-					if (V.HeadGirl.ID) {
-						const {him} = getPronouns(getSlave(V.HeadGirl.ID));
+					if (S.HeadGirl) {
+						const {him} = getPronouns(S.HeadGirl);
 						return `will afford your Head Girl considerable legal power, allowing ${him} to accomplish even more slave training.`;
 					} else {
 						return `will afford a Head Girl considerable legal power, allowing them to accomplish even more slave training.`;
 					}
 				},
 				get activatedText() {
-					if (V.HeadGirl.ID) {
-						const {him} = getPronouns(getSlave(V.HeadGirl.ID));
+					if (S.HeadGirl) {
+						const {him} = getPronouns(S.HeadGirl);
 						return `affords your Head Girl considerable legal power, allowing ${him} to accomplish even more slave training.`;
 					} else {
 						return `affords a Head Girl considerable legal power, allowing them to accomplish even more slave training.`;
@@ -1876,7 +1900,7 @@ App.Data.Policies.Selection = {
 				requirements: function() { return (V.arcologies[0].FSRestartDecoration >= 100 && V.rep >= 5000); },
 				onImplementation: function() {
 					repX(-4000, "policies");
-					Engine.play("Breeder Proposal");
+					SugarCube.Engine.play("Breeder Proposal");
 				},
 				hide: {ifActivated: 1}, // Repeal is not possible.
 				note: `Will greatly damage your reputation for even proposing`
diff --git a/js/003-data/slaveMods.js b/js/003-data/slaveMods.js
index 781a17b19982bf75c7c2b558769c65df06a4ac3c..b5d8e78164b5f2378e5cb699a66c5dbab78dfc3a 100644
--- a/js/003-data/slaveMods.js
+++ b/js/003-data/slaveMods.js
@@ -191,3 +191,208 @@ App.Medicine.Modification.Brands = {
 		},
 	}
 };
+
+
+App.Medicine.Modification.Color = {
+	Primary: [
+		{value: "auburn"},
+		{value: "black"},
+		{value: "blazing red"},
+		{value: "blonde"},
+		{value: "blue-violet"},
+		{value: "blue"},
+		{value: "brown"},
+		{value: "burgundy"},
+		{value: "chestnut"},
+		{value: "chocolate brown"},
+		{value: "copper"},
+		{value: "dark blue"},
+		{value: "dark brown"},
+		{value: "dark orchid"},
+		{value: "deep red"},
+		{value: "ginger"},
+		{value: "golden"},
+		{value: "green-yellow"},
+		{value: "green"},
+		{value: "grey"},
+		{value: "hazel"},
+		{value: "jet black"},
+		{value: "neon blue"},
+		{value: "neon green"},
+		{value: "neon pink"},
+		{value: "pink"},
+		{value: "platinum blonde"},
+		{value: "purple"},
+		{value: "red"},
+		{value: "sea green"},
+		{value: "silver"},
+		{value: "strawberry-blonde"},
+		{value: "white"},
+	],
+	Secondary: [
+		{
+			title: "None",
+			value: ""
+		},
+		{
+			title: "Black",
+			value: " with black highlights"
+		},
+		{
+			title: "Blazing red",
+			value: " with blazing red highlights"
+		},
+		{
+			title: "Blonde",
+			value: " with blonde highlights"
+		},
+		{
+			title: "Grey",
+			value: " with grey highlights"
+		},
+		{
+			title: "Neon blue",
+			value: " with neon blue highlights"
+		},
+		{
+			title: "Neon green",
+			value: " with neon green highlights"
+		},
+		{
+			title: "Neon pink",
+			value: " with neon pink highlights"
+		},
+		{
+			title: "Rainbow",
+			value: " with rainbow highlights"
+		},
+		{
+			title: "Silver",
+			value: " with silver highlights"
+		},
+		{
+			title: "White",
+			value: " with white highlights"
+		},
+	]
+};
+
+App.Medicine.Modification.hairStyles = {
+	Normal: [
+		{
+			title: "Afro",
+			value: "afro"
+		},
+		{
+			title: "Braided",
+			value: "braided"
+		},
+		{
+			title: "Cornrows",
+			value: "cornrows"
+		},
+		{
+			title: "Curled",
+			value: "curled"
+		},
+		{
+			title: "Dreadlocks",
+			value: "dreadlocks"
+		},
+		{
+			title: "Eary",
+			value: "eary"
+		},
+		{
+			title: "In a bun",
+			value: "bun"
+		},
+		{
+			title: "In a messy bun",
+			value: "messy bun"
+		},
+		{
+			title: "In a ponytail",
+			value: "ponytail"
+		},
+		{
+			title: "In tails",
+			value: "tails"
+		},
+		{
+			title: "Luxurious",
+			value: "luxurious"
+		},
+		{
+			title: "Messy",
+			value: "messy"
+		},
+		{
+			title: "Neat",
+			value: "neat"
+		},
+		{
+			title: "Permed",
+			value: "permed"
+		},
+		{
+			title: "Shaved sides",
+			value: "strip"
+		},
+		{
+			title: "Up",
+			value: "up"
+		},
+	],
+	Cut: [
+		{
+			title: "Shaved",
+			value: "shaved",
+			hLength: 0
+		},
+		{
+			title: "Trimmed short",
+			value: "Salon",
+			hLength: 10
+		},
+		{
+			title: "Buzzcut",
+			value: "buzzcut",
+			hLength: 1
+		},
+	],
+	Length: [
+		{
+			title: "Very short",
+			hLength: 5
+		},
+		{
+			title: "Short",
+			hLength: 10
+		},
+		{
+			title: "Shoulder length",
+			hLength: 30
+		},
+		{
+			title: "Long",
+			hLength: 60
+		},
+		{
+			title: "Very long",
+			hLength: 100
+		},
+		{
+			title: "Apply hair growth stimulating treatment",
+			hLength: 0,
+			requirements: function(slave) { return (slave.hLength === 1); }
+		},
+		{
+			title: "Apply extensions",
+			onApplication: function(slave) { slave.hLength += 10; },
+			requirements: function(slave) { return (!slave.hLength === 1 && slave.hLength < 150); }
+		},
+	]
+};
+
+
diff --git a/slave variables documentation - Pregmod.txt b/slave variables documentation - Pregmod.txt
index e5db8973851d24928b25034ddc4d665ec0ebb965..b6d2f7d04d96dad84685d95ef6a49eab2bf6a3bc 100644
--- a/slave variables documentation - Pregmod.txt	
+++ b/slave variables documentation - Pregmod.txt	
@@ -2934,10 +2934,13 @@ shoes:
 may accept strings, use at own risk
 "none"
 "heels"
+"platform heels"
 "pumps"
 "extreme heels"
+"extreme platform heels"
 "boots"
 "flats"
+"platform shoes"
 
 vaginalAccessory:
 
diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js
index 95c56fcc0df620841bf4063ade116cdcc98f4d10..0b8dee56f7365bf3b9d9e30a7400ade70a4f33bd 100644
--- a/src/002-config/fc-version.js
+++ b/src/002-config/fc-version.js
@@ -1,6 +1,6 @@
 App.Version = {
 	base: "0.10.7.1", // The vanilla version the mod is based off of, this should never be changed.
-	pmod: "3.5.4",
+	pmod: "3.6.2",
 	commitHash: null,
-	release: 1076
+	release: 1080
 };
diff --git a/src/002-config/mousetrapConfig.js b/src/002-config/mousetrapConfig.js
index ffcfcb7b0d3da9e458b5d6d55e630b34349bf52a..c4cb6d42a4f62d533527eed93af1a96652318c55 100644
--- a/src/002-config/mousetrapConfig.js
+++ b/src/002-config/mousetrapConfig.js
@@ -238,6 +238,7 @@ App.UI.Hotkeys = (function() {
 			// discard obsolete hotkeys
 			if (actions[saveKey]) {
 				actions[saveKey].combinations = save[saveKey];
+				addBinding(saveKey, save[saveKey]);
 			}
 		}
 	}
diff --git a/src/003-assets/CSS/table.css b/src/003-assets/CSS/table.css
index 2df00a8ec002373692e4e2ee1bcdf43ce5f314f7..d71878e44483a7243eb66aa161bf63a4a53419a2 100644
--- a/src/003-assets/CSS/table.css
+++ b/src/003-assets/CSS/table.css
@@ -21,6 +21,24 @@ table.budget {
 	padding: 5px;
 	font-family: "monospace";
 }
-table.budget tr.colored {
+table.budget .colored {
 	background-color: #001700;
 }
+
+table.budget .accordion:not(.closed) .cash {
+	color: gray;
+}
+
+table.budget .accordion > :first-child {
+	position: relative;
+}
+
+table.budget .accordion > :first-child::before {
+	font-family: "tme-fa-icons";
+	position: absolute;
+	left: 10px;
+	content: "\e81c";
+}
+table.budget .accordion.closed > :first-child::before {
+	content: "\e81e";
+}
diff --git a/src/004-base/organFarmBase.js b/src/004-base/organFarmBase.js
index b6e2c6a7e2627f9f63c7158e6ca6686452bc2ccf..4e8e6e50ba1fb0323e53379d2bc3133575695090 100644
--- a/src/004-base/organFarmBase.js
+++ b/src/004-base/organFarmBase.js
@@ -2,7 +2,7 @@ App.Medicine.OrganFarm.Organ = class {
 	/**
 	 * @param {string} type - unique type of organ, used as key
 	 * @param {string} name - display name
-	 * @param {string} tooltip - full sentence, uncapitalized and unpunctuated
+	 * @param {string|function(App.Entity.SlaveState):string} tooltip - full sentence, uncapitalized and unpunctuated
 	 * @param {number} cost - how much it costs to grow the organ
 	 * @param {number} time - how long it takes to grow the organ (without upgrades)
 	 * @param {function(App.Entity.SlaveState):boolean} canGrow
diff --git a/src/004-base/specialSlavesProxy.js b/src/004-base/specialSlavesProxy.js
index 92622b4efbf40a734577d4532cc4fc371868ca8d..66e3e91824dea7f78409d0df536133ff9f5050c5 100644
--- a/src/004-base/specialSlavesProxy.js
+++ b/src/004-base/specialSlavesProxy.js
@@ -6,37 +6,37 @@ App.SpecialSlavesProxy = class SpecialSlavesProxy {
 		return slaveStateById(V.AttendantID);
 	}
 	get Bodyguard() {
-		return V.Bodyguard;
+		return slaveStateById(V.BodyguardID);
 	}
 	get Concubine() {
-		return V.Concubine;
+		return slaveStateById(V.ConcubineID);
 	}
 	get DJ() {
 		return slaveStateById(V.djID);
 	}
 	get Farmer() {
-		return V.Farmer;
+		return slaveStateById(V.FarmerID);
 	}
 	get HeadGirl() {
-		return V.HeadGirl;
+		return slaveStateById(V.HeadGirlID);
 	}
 	get Lurcher() {
 		return slaveStateById(V.LurcherID);
 	}
 	get Madam() {
-		return V.Madam;
+		return slaveStateById(V.MadamID);
 	}
 	get Matron() {
-		return V.Matron;
+		return slaveStateById(V.MatronID);
 	}
 	get Milkmaid() {
-		return V.Milkmaid;
+		return slaveStateById(V.MilkmaidID);
 	}
 	get Nurse() {
 		return slaveStateById(V.NurseID);
 	}
 	get Recruiter() {
-		return V.Recruiter;
+		return slaveStateById(V.RecruiterID);
 	}
 	get Schoolteacher() {
 		return slaveStateById(V.SchoolteacherID);
diff --git a/src/Mods/DinnerParty/dinnerPartyExecution.tw b/src/Mods/DinnerParty/dinnerPartyExecution.tw
index 1631d9f9448e86c493a2cc214ea3ede756059fb2..ca9fecf951f41adfe350b9100f929c18fdee8838 100644
--- a/src/Mods/DinnerParty/dinnerPartyExecution.tw
+++ b/src/Mods/DinnerParty/dinnerPartyExecution.tw
@@ -419,24 +419,24 @@
 		<</for>>
 
 		/% Head Girl abuse %/
-		<<if ($HeadGirl != 0) && ($activeSlave.assignment != "be your Head Girl")>>
-			<<setLocalPronouns $HeadGirl 2>>
+		<<if ($HeadGirlID != 0) && ($activeSlave.assignment != "be your Head Girl")>>
+			<<setLocalPronouns _S.HeadGirl 2>>
 			<br><br>
 			You notice a group of guests corner your Head Girl. The group consists of some very powerful people. Most of them arcology owners themselves, some of them have more powerful arcologies than yours.
-			$HeadGirl.slaveName tries to slip away, but _he2 was grabbed at the neck by the leader of the pack. He pulls _him2 down to the ground forcefully.
-			$HeadGirl.slaveName knows better than to resist, _he2 is still a slave despite _his2 position as your Head Girl. A slave does not raise their hand against a Master, even one that's not _his2 own. The leader rips off _his2 dress, exposing _his2 bare chest. Your Head Girl is frozen in terror when _he2 is lifted onto the hastily cleared dining hall table by several of the men. _His2 eyes desperately search for you. You are the only one who can stop this. All that's required is a single word from you.
-			$HeadGirl.slaveName's eyes meets yours; the look of relief on _his2 face is soon replaced by the sad realization that you will not intervene.
-			_His2 mouth just barely forms the word "<<= WrittenMaster($HeadGirl)>>" as you shake your head, so _he2 closes _his2 mouth and resigns $himself to _his2 fate.
+			_S.HeadGirl.slaveName tries to slip away, but _he2 was grabbed at the neck by the leader of the pack. He pulls _him2 down to the ground forcefully.
+			_S.HeadGirl.slaveName knows better than to resist, _he2 is still a slave despite _his2 position as your Head Girl. A slave does not raise their hand against a Master, even one that's not _his2 own. The leader rips off _his2 dress, exposing _his2 bare chest. Your Head Girl is frozen in terror when _he2 is lifted onto the hastily cleared dining hall table by several of the men. _His2 eyes desperately search for you. You are the only one who can stop this. All that's required is a single word from you.
+			_S.HeadGirl.slaveName's eyes meets yours; the look of relief on _his2 face is soon replaced by the sad realization that you will not intervene.
+			_His2 mouth just barely forms the word "<<= WrittenMaster(_S.HeadGirl)>>" as you shake your head, so _he2 closes _his2 mouth and resigns $himself to _his2 fate.
 			<br>
-			Your Head Girl is placed across the corner of the dining hall table face-down, _his2 arm<<if hasBothArms($HeadGirl)>>s<</if>> and leg<<if hasBothLegs($HeadGirl)>>s<</if>> held down by four men. _He2 finds a hand on _his2 ass, groping roughly, then another hand on the other cheek, roughly parting _his2 globes to get better access.
+			Your Head Girl is placed across the corner of the dining hall table face-down, _his2 arm<<if hasBothArms(_S.HeadGirl)>>s<</if>> and leg<<if hasBothLegs(_S.HeadGirl)>>s<</if>> held down by four men. _He2 finds a hand on _his2 ass, groping roughly, then another hand on the other cheek, roughly parting _his2 globes to get better access.
 			/% Pussy Check %/
-			<<if ($HeadGirl.vagina >= 0) && canDoVaginal($HeadGirl)>>
+			<<if (_S.HeadGirl.vagina >= 0) && canDoVaginal(_S.HeadGirl)>>
 				A finger traces along _his2 slit, finding the source of that moisture and pressing its way in. Another finger was added and the hand began to finger fuck _him2 roughly. After a few seconds a third finger was added and then a fourth, stretching _his2 pussy.
-				$HeadGirl.slaveName screams loudly as the entire hand was shoved up _his2 cunt without warning.
+				_S.HeadGirl.slaveName screams loudly as the entire hand was shoved up _his2 cunt without warning.
 				_His2 pussy has @@.lime;loosened.@@
-				<<set $HeadGirl.vagina += 1>>
+				<<set _S.HeadGirl.vagina += 1>>
 			<</if>>
-			One man moves to the head of $HeadGirl.slaveName and shoved his cock into _his2 mouth, fucking _his2 throat roughly. He buries his cock deep down _his2 throat all the way to his balls.
+			One man moves to the head of _S.HeadGirl.slaveName and shoved his cock into _his2 mouth, fucking _his2 throat roughly. He buries his cock deep down _his2 throat all the way to his balls.
 			He places both hands around _his2 neck and squeezes with every stroke. Your Head Girl's face turns red and _he2 gags uncontrollably. _His2 eyes roll back as another man shoves his hard cock in _his2 asshole, sodomizing _him2 with no mercy. As your Head Girl makes squeaking noises in pain, several guests pick up forks and jab it at _his2 butt and _his2 back, chanting "More meat... More meat..." and "Squeal, piggy, squeal." You slump over your chair at the head of the table. Gulping down your drink, you suppress the urge to order your security drones to open fire on those men. You know they would never dare to harm your Head Girl, not permanently, at least not anything your remote surgery couldn't fix. It's not as if you haven't done worse to your slaves, but the message is clear; the attack on your Head Girl was meant as disrespect and an insult towards you. Raping your Head Girl is like raping you. You stare at the group of men gang raping your Head Girl at the end of your own dining table, burning their @@.yellow;names and their faces@@ into your memory as they take turns fucking _him2. They make use of _his2 every hole. When they are done, _he2 is left on the floor, battered and bruised, covered in cum and blood from superficial wounds.
 			The leader of the pack pulls out his half erect cock and
 			<<if $seePee == 1>>
@@ -456,8 +456,8 @@
 			<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.
 			<br>
-			<<run healthDamage($HeadGirl, 20)>>
-			<<set $HeadGirl.devotion -= 20>>
+			<<run healthDamage(_S.HeadGirl, 20)>>
+			<<set _S.HeadGirl.devotion -= 20>>
 			<<set _generateEnemies = 1>>
 		<</if>>
 	<</if>>
@@ -578,4 +578,4 @@
 		<<set $MOD_enemyList.push($MOD_enemy)>>
 	<</for>>
 <</if>>
-*/
\ No newline at end of file
+*/
diff --git a/src/Mods/DinnerParty/dinnerPartyPreparations.tw b/src/Mods/DinnerParty/dinnerPartyPreparations.tw
index 777778de6c05068b7cf04155c168d347dea2d0a0..33faa2cdb6eaf260ddce23e59e7be448951ff2a4 100644
--- a/src/Mods/DinnerParty/dinnerPartyPreparations.tw
+++ b/src/Mods/DinnerParty/dinnerPartyPreparations.tw
@@ -29,13 +29,13 @@ Your assistant will take care of the invitations and all the arrangements; all y
 
 __Select Your Meat:__
 <br><br>
-<<= App.UI.SlaveList.slaveSelectionList(
+<<includeDOM App.UI.SlaveList.slaveSelectionList(
 		s => assignmentVisible(s) && s.fuckdoll === 0,
 		App.UI.SlaveList.SlaveInteract.stdInteract,
 		null,
-		(s, i) => {
+		(s) => {
 			const p = getPronouns(s);
 			return App.UI.DOM.passageLink(`Make ${p.him} the main course`, "Dinner Party Execution",
-				() => { App.Utils.setActiveSlaveByIndex(i); })
+				() => { $activeSlave = s; })
 		}
 	)>>
diff --git a/src/Mods/SecExp/SecExpBackwardCompatibility.tw b/src/Mods/SecExp/SecExpBackwardCompatibility.tw
index 969ce8f11c87a486d9c69e9762075d730c1e3fcd..eefe11c4748acb9f7c0356d2957f26820970a08d 100644
--- a/src/Mods/SecExp/SecExpBackwardCompatibility.tw
+++ b/src/Mods/SecExp/SecExpBackwardCompatibility.tw
@@ -4,276 +4,36 @@
 
 /* base stats */
 <<run App.SecExp.Check.general()>>
-<<if ndef $attackType>>
-	<<set $attackType = "none">>
-<</if>>
-<<if ndef $attackThisWeek>>
-	<<set $attackThisWeek = 0>>
-<</if>>
-<<if ndef $lastAttackWeeks>>
-	<<set $lastAttackWeeks = 0>>
-<</if>>
 <<set $lastRebellionWeeks = Number($lastRebellionWeeks) || 0>>
-<<if ndef $hasFoughtOnce>>
-	<<set $hasFoughtOnce = 0>>
-<</if>>
-<<if ndef $hasFoughtMajorBattleOnce>>
-	<<set $hasFoughtMajorBattleOnce = 0>>
-<</if>>
-<<if ndef $hasRebelledOnce>>
-	<<set $hasRebelledOnce = 0>>
-<</if>>
-<<if ndef $majorBattle>>
-	<<set $majorBattle = 0>>
-<</if>>
-<<if ndef $PCvictoryStreak>>
-	<<set $PCvictoryStreak = 0>>
-<</if>>
-<<if ndef $PClossStreak>>
-	<<set $PClossStreak = 0>>
-<</if>>
-<<if ndef $wasToggledBefore>>
-	<<set $wasToggledBefore = 0>>
-<</if>>
-<<if ndef $foughtThisWeek>>
-	<<set $foughtThisWeek = 0>>
-<</if>>
 
-/* edicts */
-<<if ndef $alternativeRents>>
-	<<set $alternativeRents = 0>>
-<</if>>
-<<if ndef $enslavementRights>>
-	<<set $enslavementRights = 0>>
-<</if>>
-<<if ndef $securityExemption>>
-	<<set $securityExemption = 0>>
-<</if>>
-<<if ndef $sellData>>
-	<<set $sellData = 0>>
-<</if>>
-<<if ndef $propCampaignBoost>>
-	<<set $propCampaignBoost = 0>>
-<</if>>
-<<if ndef $slaveWatch>>
-	<<set $slaveWatch = 0>>
-<</if>>
-<<if ndef $subsidyChurch>>
-	<<set $subsidyChurch = 0>>
-<</if>>
-<<if ndef $limitImmigration>>
-	<<set $limitImmigration = 0>>
-<</if>>
-<<if ndef $openBorders>>
-	<<set $openBorders = 0>>
-<</if>>
-<<if ndef $slavesOfficers>>
-	<<set $slavesOfficers = 0>>
-<</if>>
-<<if ndef $martialSchool>>
-	<<set $martialSchool = 0>>
-<</if>>
-<<if ndef $discountMercenaries>>
-	<<set $discountMercenaries = 0>>
-<</if>>
-<<if ndef $militiaFounded>>
-	<<set $militiaFounded = 0>>
-<</if>>
-<<if ndef $recruitVolunteers>>
-	<<set $recruitVolunteers = 0>>
-<</if>>
-<<if ndef $conscription>>
-	<<set $conscription = 0>>
-<</if>>
-<<if ndef $militaryService>>
-	<<set $militaryService = 0>>
-<</if>>
-<<if ndef $militarizedSociety>>
-	<<set $militarizedSociety = 0>>
-<</if>>
-<<if ndef $militaryExemption>>
-	<<set $militaryExemption = 0>>
-<</if>>
-<<if ndef $lowerRquirements>>
-	<<set $lowerRquirements = 0>>
-<</if>>
-<<if ndef $noSubhumansInArmy>>
-	<<set $noSubhumansInArmy = 0>>
-<</if>>
-<<if ndef $pregExemption>>
-	<<set $pregExemption = 0>>
-<</if>>
-<<if ndef $eliteOfficers>>
-	<<set $eliteOfficers = 0>>
-<</if>>
-<<if ndef $liveTargets>>
-	<<set $liveTargets = 0>>
-<</if>>
-<<if ndef $legionTradition>>
-	<<set $legionTradition = 0>>
-<</if>>
-<<if ndef $eagleWarriors>>
-	<<set $eagleWarriors = 0>>
-<</if>>
-<<if ndef $ronin>>
-	<<set $ronin = 0>>
-<</if>>
-<<if ndef $sunTzu>>
-	<<set $sunTzu = 0>>
-<</if>>
-<<if ndef $mamluks>>
-	<<set $mamluks = 0>>
-<</if>>
-<<if ndef $pharaonTradition>>
-	<<set $pharaonTradition = 0>>
-<</if>>
-<<if ndef $weaponsLaw>>
-	<<set $weaponsLaw = 3>>
-<</if>>
-<<if ndef $soldierWages>>
-	<<set $soldierWages = 1>>
-<</if>>
-<<if ndef $militiaSoldierPrivilege>>
-	<<set $militiaSoldierPrivilege = 0>>
-<</if>>
-<<if ndef $slaveSoldierPrivilege>>
-	<<set $slaveSoldierPrivilege = 0>>
-<</if>>
-<<if ndef $mercSoldierPrivilege>>
-	<<set $mercSoldierPrivilege = 0>>
-<</if>>
-<<if ndef $tradeLegalAid>>
-	<<set $tradeLegalAid = 0>>
-<</if>>
-<<if ndef $taxTrade>>
-	<<set $taxTrade = 0>>
-<</if>>
-
-/* buildings */
-<<if ndef $secHQ>>
-	<<set $secHQ = 0>>
-<</if>>
-<<if ndef $secMenials>>
-	<<set $secMenials = $secHelots || 0>>
-	<<unset $secHelots>>
-<</if>>
-<<if ndef $secUpgrades >>
-	<<set $secUpgrades = {
-	nanoCams: 0,
-	cyberBots: 0,
-	eyeScan: 0,
-	cryptoAnalyzer: 0,
-	coldstorage: 0}>>
-<</if>>
-<<if ndef $crimeUpgrades>>
-	<<set $crimeUpgrades = {
-	autoTrial: 0,
-	autoArchive: 0,
-	worldProfiler: 0,
-	advForensic: 0}>>
-<</if>>
-<<if ndef $intelUpgrades>>
-	<<set $intelUpgrades = {
-	sensors: 0,
-	radar: 0,
-	signalIntercept: 0}>>
-<</if>>
-<<if ndef $readinessUpgrades>>
-	<<set $readinessUpgrades = {
-	earlyWarn: 0,
-	rapidPlatforms: 0,
-	pathways: 0,
-	rapidVehicles: 0}>>
-<</if>>
-<<if ndef $riotCenter>>
-	<<set $riotCenter = 0>>
-<</if>>
-<<if ndef $riotUpgrades>>
-	<<set $riotUpgrades = {
-	freeMedia: 0,
-	rapidUnit: 0,
-	rapidUnitSpeed: 0}>>
-<</if>>
-<<if ndef $fort>>
-	<<set $fort = {
-	reactor: 0,
-	waterway: 0,
-	assistant: 0}>>
-<</if>>
-<<if ndef $sentUnitCooldown>>
-	<<set $sentUnitCooldown = 0>>
-<</if>>
-<<if ndef $advancedRiotEquip>>
-	<<set $advancedRiotEquip = 0>>
-<</if>>
-<<if ndef $brainImplant>>
-	<<set $brainImplant = -1>>
-<</if>>
-<<if ndef $brainImplantProject>>
-	<<set $brainImplantProject = 0>>
-<</if>>
-<<if def $weapHelots>>
-	<<set $weapMenials = $weapHelots || 0>>
-	<<unset $weapHelots>>
-<</if>>
-<<if ndef $weapManu>>
-	<<set $weapManu = 0>>
-<</if>>
-<<if ndef $weapProductivity>>
-	<<set $weapProductivity = 1>>
-<</if>>
-<<if ndef $weapLab>>
-	<<set $weapLab = 1>>
-<</if>>
-<<if ndef $baseUpgradeTime>>
-	<<set $baseUpgradeTime = 10>>
-<</if>>
-<<if ndef $weapUpgrades>>
-	<<set $weapUpgrades = []>>
-<</if>>
-<<if ndef $currentUpgrade>>
-	<<set $currentUpgrade = {
-	ID: 0,
-	name: " ",
-	unit: 0,
-	type: 0,
-	time: 0,
-	value: 0}>>
-<<else>>
-	<<if ndef $currentUpgrade.ID>>
-		<<if $currentUpgrade.name == "magnetic based ballistic weaponry">>
-			<<set $currentUpgrade.ID = 0>>
-		<<elseif $currentUpgrade.name == "ceramo-metallic alloys">>
-			<<set $currentUpgrade.ID = 1>>
-		<<elseif $currentUpgrade.name == "rapid action stimulants">>
-			<<set $currentUpgrade.ID = 2>>
-		<<elseif $currentUpgrade.name == "fast response neural stimulant">>
-			<<set $currentUpgrade.ID = 3>>
-		<<elseif $currentUpgrade.name == "universal cyber enhancements">>
-			<<set $currentUpgrade.ID = 4>>
-		<<elseif $currentUpgrade.name == "remote neural links">>
-			<<set $currentUpgrade.ID = 5>>
-		<<elseif $currentUpgrade.name == "combined training regimens with the special force">>
-			<<set $currentUpgrade.ID = 6>>
-		<<elseif $currentUpgrade.name == "a variant of the stimulant cocktail that the special force created">>
-			<<set $currentUpgrade.ID = 7>>
-		<<elseif $currentUpgrade.name == "a mesh network based off the custom network of the special force">>
-			<<set $currentUpgrade.ID = 8>>
-		<<elseif $currentUpgrade.name == "dynamic battle aware AI">>
-			<<set $currentUpgrade.ID = -1>>
-		<<elseif $currentUpgrade.name == "adaptive armored frames">>
-			<<set $currentUpgrade.ID = -2>>
-		<<elseif $currentUpgrade.name == "advanced synthetic alloys">>
-			<<set $currentUpgrade.ID = -3>>
-		<</if>>
-	<</if>>
-<</if>>
-<<if ndef $droneUpgrades>>
-	<<set $droneUpgrades = {
-	attack: 0,
-	defense: 0,
-	hp:0}>>
-<<elseif $droneUpgrades == null>>
+<<if ndef $currentUpgrade.ID>>
+	<<if $currentUpgrade.name == "magnetic based ballistic weaponry">>
+		<<set $currentUpgrade.ID = 0>>
+	<<elseif $currentUpgrade.name == "ceramo-metallic alloys">>
+		<<set $currentUpgrade.ID = 1>>
+	<<elseif $currentUpgrade.name == "rapid action stimulants">>
+		<<set $currentUpgrade.ID = 2>>
+	<<elseif $currentUpgrade.name == "fast response neural stimulant">>
+		<<set $currentUpgrade.ID = 3>>
+	<<elseif $currentUpgrade.name == "universal cyber enhancements">>
+		<<set $currentUpgrade.ID = 4>>
+	<<elseif $currentUpgrade.name == "remote neural links">>
+		<<set $currentUpgrade.ID = 5>>
+	<<elseif $currentUpgrade.name == "combined training regimens with the special force">>
+		<<set $currentUpgrade.ID = 6>>
+	<<elseif $currentUpgrade.name == "a variant of the stimulant cocktail that the special force created">>
+		<<set $currentUpgrade.ID = 7>>
+	<<elseif $currentUpgrade.name == "a mesh network based off the custom network of the special force">>
+		<<set $currentUpgrade.ID = 8>>
+	<<elseif $currentUpgrade.name == "dynamic battle aware AI">>
+		<<set $currentUpgrade.ID = -1>>
+	<<elseif $currentUpgrade.name == "adaptive armored frames">>
+		<<set $currentUpgrade.ID = -2>>
+	<<elseif $currentUpgrade.name == "advanced synthetic alloys">>
+		<<set $currentUpgrade.ID = -3>>
+	<</if>>
+<</if>>
+<<if $droneUpgrades == null>>
 	<<set $droneUpgrades = {
 	attack: 0,
 	defense: 0,
@@ -296,13 +56,7 @@
 		<<set $droneUpgrades.hp = 0>>
 	<</if>>
 <</if>>
-<<if ndef $humanUpgrade>>
-	<<set $humanUpgrade = {
-	attack: 0,
-	defense: 0,
-	hp:0,
-	morale: 0}>>
-<<elseif $humanUpgrade == null>>
+<<if $humanUpgrade == null>>
 	<<set $humanUpgrade = {
 	attack: 0,
 	defense: 0,
@@ -330,265 +84,11 @@
 		<<set $humanUpgrade.morale = 0>>
 	<</if>>
 <</if>>
-<<if ndef $sellTo>>
-	<<set $sellTo = {
-	citizen: 1,
-	raiders: 1,
-	oldWorld: 1,
-	FC: 1}>>
-<</if>>
-<<if ndef $completedUpgrades>>
-	<<set $completedUpgrades = []>>
-<</if>>
-<<if ndef $transportHub>>
-	<<set $transportHub = 0>>
-<</if>>
-<<if ndef $airport>>
-	<<set $airport = 1>>
-<</if>>
-<<if ndef $railway>>
-	<<set $railway = 1>>
-<</if>>
-<<if ndef $docks>>
-	<<set $docks = 1>>
-<</if>>
-<<if ndef $hubSecurity>>
-	<<set $hubSecurity = 1>>
-<</if>>
-
-/* events */
-<<if ndef $smilingManProgress>>
-	<<set $smilingManProgress = 0>>
-<</if>>
-<<if ndef $investedFunds>>
-	<<set $investedFunds = 0>>
-<</if>>
-<<if ndef $relationshipLM>>
-	<<set $relationshipLM = 0>>
-<</if>>
-<<if ndef $smilingManWeek>>
-	<<set $smilingManWeek = 0>>
-<</if>>
-<<if ndef $globalCrisisWeeks>>
-	<<set $globalCrisisWeeks = 0>>
-<</if>>
-<<if ndef $smilingManFate>>
-	<<set $smilingManFate = 4>>
-<</if>>
-
-/* rebellions */
-<<if ndef $tension>>
-	<<set $tension = 0>>
-<</if>>
-<<if ndef $slaveProgress>>
-	<<set $slaveProgress = 0>>
-<</if>>
-<<if ndef $citizenProgress>>
-	<<set $citizenProgress = 0>>
-<</if>>
-<<if ndef $slaveRebellionEventFires>>
-	<<set $slaveRebellionEventFires = 0>>
-<</if>>
-<<if ndef $citizenRebellionEventFires>>
-	<<set $citizenRebellionEventFires = 0>>
-<</if>>
-<<if ndef $slaveRebellion>>
-	<<set $slaveRebellion = 0>>
-<</if>>
-<<if ndef $citizenRebellion>>
-	<<set $citizenRebellion = 0>>
-<</if>>
-<<if ndef $engageRule>>
-	<<set $engageRule = 0>>
-<</if>>
-<<if ndef $irregulars>>
-	<<set $irregulars = 0>>
-<</if>>
-<<if ndef $repairTime>>
-	<<set $repairTime = 3>>
-<</if>>
-<<if ndef $arcRepairTime>>
-	<<set $arcRepairTime = 0>>
-<</if>>
-<<if ndef $garrison>>
-	<<set $garrison = {
-	penthouse: 0,
-	reactor: 0,
-	assistant: 0,
-	waterway: 0,
-	reactorTime: 0,
-	assistantTime: 0,
-	waterwayTime: 0}>>
-<<else>>
-	<<if ndef $garrison.penthouse>>
-		<<set $garrison.penthouse = 0>>
-	<</if>>
-	<<if ndef $garrison.reactor>>
-		<<set $garrison.reactor = 0>>
-	<</if>>
-	<<if ndef $garrison.assistant>>
-		<<set $garrison.assistant = 0>>
-	<</if>>
-	<<if ndef $garrison.waterway>>
-		<<set $garrison.waterway = 0>>
-	<</if>>
-	<<if ndef $garrison.reactorTime>>
-		<<set $garrison.reactorTime = 0>>
-	<</if>>
-	<<if ndef $garrison.assistantTime>>
-		<<set $garrison.assistantTime = 0>>
-	<</if>>
-	<<if ndef $garrison.waterwayTime>>
-		<<set $garrison.waterwayTime = 0>>
-	<</if>>
-<</if>>
-<<if ndef $rebellionsCount>>
-	<<set $rebellionsCount = 0>>
-<</if>>
-<<if ndef $PCrebWon>>
-	<<set $PCrebWon = 0>>
-<</if>>
-<<if ndef $PCrebLoss>>
-	<<set $PCrebLoss = 0>>
-<</if>>
-
-/* armed forces stats */
-<<if ndef $targetUnit>>
-	<<set $targetUnit = 0>>
-<</if>>
-<<if ndef $targetIndex>>
-	<<set $targetIndex = 0>>
-<</if>>
-<<if ndef $secBotsCost>>
-	<<set $secBotsCost = 500>>
-<</if>>
-<<if ndef $secBotsUpgradeCost>>
-	<<set $secBotsUpgradeCost = 250>>
-<</if>>
-<<if ndef $equipUpgradeCost>>
-	<<set $equipUpgradeCost = 250>>
-<</if>>
-<<if ndef $maxTroops>>
-	<<set $maxTroops = 30>>
-<</if>>
-<<if ndef $militiaFounded>>
-	<<set $militiaFounded = 0>>
-<</if>>
-<<if ndef $militiaFreeManpower>>
-	<<set $militiaFreeManpower = 0>>
-<</if>>
-<<if ndef $militiaTotalCasualties>>
-	<<set $militiaTotalCasualties = 0>>
-<</if>>
-<<if ndef $slavesOfficers>>
-	<<set $slavesOfficers = 0>>
-<</if>>
-<<if ndef $slavesTotalCasualties>>
-	<<set $slavesTotalCasualties = 0>>
-<</if>>
-<<if ndef $slavesMaxTroops>>
-	<<set $slavesMaxTroops = 30>>
-<</if>>
-<<if ndef $mercFreeManpower>>
-	<<set $mercFreeManpower = 0>>
-<</if>>
-<<if ndef $mercTotalCasualties>>
-	<<set $mercTotalCasualties = 0>>
-<</if>>
-<<if ndef $createdSlavesUnits>>
-	<<set $createdSlavesUnits = 0>>
-<</if>>
-<<if ndef $createdMilitiaUnits>>
-	<<set $createdMilitiaUnits = 0>>
-<</if>>
-<<if ndef $createdMercUnits>>
-	<<set $createdMercUnits = 0>>
-<</if>>
-
-/* battle relevant vars */
-<<if ndef $slaveVictories>>
-	<<set $slaveVictories = []>>
-<</if>>
-<<if ndef $totalKills>>
-	<<set $totalKills = 0>>
-<</if>>
-<<if ndef $battlesCount>>
-	<<set $battlesCount = 0>>
-<</if>>
-<<if ndef $majorBattlesCount>>
-	<<set $majorBattlesCount = 0>>
-<</if>>
-<<if ndef $chosenTactic>>
-	<<set $chosenTactic = "none">>
-<</if>>
-<<if ndef $leadingTroops>>
-	<<set $leadingTroops = "none">>
-<</if>>
-<<if ndef $attackTroops>>
-	<<set $attackTroops = 0>>
-<</if>>
-<<if ndef $attackEquip>>
-	<<set $attackEquip = 0>>
-<</if>>
-<<if ndef $battleTerrain>>
-	<<set $battleTerrain = "none">>
-<</if>>
-<<if ndef $maxTurns>>
-	<<set $maxTurns = 10>>
-<</if>>
-<<if ndef $battleResult>>
-	<<set $battleResult = 4>>				/* sets $battleResult value outside accepted range (-3,3) to avoid evaluation problems */
-<</if>>
-<<if ndef $losses>>
-	<<set $losses = 0>>
-<</if>>
-<<if ndef $enemyLosses>>
-	<<set $enemyLosses = 0>>
-<</if>>
-<<if ndef $battleTurns>>
-	<<set $battleTurns = 0>>
-<</if>>
-<<if ndef $tacticsSuccessful>>
-	<<set $tacticsSuccessful = 0>>
-<</if>>
-<<if ndef $leaderWounded>>
-	<<set $leaderWounded = 0>>
-<</if>>
-<<if ndef $gainedCombat>>
-	<<set $gainedCombat = 0>>
-<</if>>
-<<if ndef $gainedWarfare>>
-	<<set $gainedWarfare = 0>>
-<</if>>
-<<if ndef $SFIntervention>>
-	<<set $SFIntervention = 0>>
-<</if>>
-<<if ndef $rebellingID>>
-	<<set $rebellingID = []>>
-<</if>>
-<<if ndef $saveValid>>
-	<<set $saveValid = 0>>
-<</if>>
-<<if ndef $lastSelection>>
-	<<set $lastSelection = []>>
-<</if>>
 
 /* SFanon additions */
-<<if ndef $SFSupportLevel>>
-	<<set $SFSupportLevel = 0>>
-<</if>>
 <<if ndef $secUpgrades.coldstorage>>
 	<<set $secUpgrades.coldstorage = 0>>
 <</if>>
-<<if ndef $SFGear>>
-	<<set $SFGear = 0>>
-<</if>>
-<<if ndef $SavedLeader>>
-	<<set $SavedLeader = $leadingTroops>>
-<</if>>
-<<if ndef $SavedSFI>>
-	<<set $SavedSFI = $SFIntervention>>
-<</if>>
 
 /* init merc manpower if it wasn't done already */
 <<if $wasToggledBefore == 0>>
@@ -599,6 +99,17 @@
 	<</if>>
 <</if>>
 
+/* if we had the old one-time battle flags, make sure the counters are set to at least one */
+<<if $battlesCount == 0 && $hasFoughtOnce == 1>>
+	<<set $battlesCount = 1>>
+<</if>>
+<<if $majorBattlesCount == 0 && $hasFoughtMajorBattleOnce == 1>>
+	<<set $majorBattlesCount = 1>>
+<</if>>
+<<if $rebellionsCount == 0 && $hasRebelledOnce == 1>>
+	<<set $rebellionsCount = 1>>
+<</if>>
+
 /* recalculation widgets */
 <<fixBrokenUnits>>
 <<fixBrokenStats>>
diff --git a/src/Mods/SecExp/attackGenerator.tw b/src/Mods/SecExp/attackGenerator.tw
index 9c9e19961b4a23ef23de86c3dd159cc95354e8bf..5a5736aba9c0f02229820b02da2034bbf72738aa 100644
--- a/src/Mods/SecExp/attackGenerator.tw
+++ b/src/Mods/SecExp/attackGenerator.tw
@@ -16,7 +16,7 @@
 	<<else>>
 		<<set _attackChance = 20>>
 	<</if>>
-	<<if $hasFoughtOnce == 1>>
+	<<if $battlesCount > 0>>
 		<<set _attackChance = 25>>
 	<</if>>
 	<<if $lastAttackWeeks >= 10>>
diff --git a/src/Mods/SecExp/attackHandler.tw b/src/Mods/SecExp/attackHandler.tw
index 11ee23f4ac78843b3b83180baea117a2aeb30e55..220a0dbae4dff4bb4fac64c14cc6874cc9e324b3 100644
--- a/src/Mods/SecExp/attackHandler.tw
+++ b/src/Mods/SecExp/attackHandler.tw
@@ -45,7 +45,6 @@
 	<<if $majorBattle == 0>>
 		<<set _turns = $maxTurns>>
 	<</if>>
-	<<set _turn = 0>>
 	<<set _attack = 0>>
 	<<set _defense = 0>>
 	<<set _morale = 0>>
@@ -65,8 +64,6 @@
 	<<set _mercMod = 1>>
 	<<set _enemyMod = 1>>
 	<<set _SFMod = 1>>
-	<<set _expBonus = 0>>
-	<<set _loyaltyBonus = 0>>
 	<<set _armyMod = 0>>
 
 	/* major battle */
@@ -214,97 +211,99 @@
 			<<set _tacChance += 0.30>>
 		<</if>>
 	<<elseif $leadingTroops == "bodyguard">>
-		<<if $Bodyguard.devotion < -20>>
+		<<if _S.Bodyguard.devotion < -20>>
 			<<set _slaveMod -= 0.15>>
-		<<elseif $Bodyguard.devotion > 50>>
+		<<elseif _S.Bodyguard.devotion > 50>>
 			<<set _slaveMod += 0.15>>
 		<</if>>
-		<<if ($rep < 10000 && $SecExp.core.authority < 10000) || $Bodyguard.prestige < 1>>
+		<<if ($rep < 10000 && $SecExp.core.authority < 10000) || _S.Bodyguard.prestige < 1>>
 			<<set _militiaMod -= 0.15>>
 			<<set _mercMod -= 0.15>>
 			<<set _SFMod -= 0.15>>
-		<<elseif $Bodyguard.prestige >= 2>>
+		<<elseif _S.Bodyguard.prestige >= 2>>
 			<<set _militiaMod += 0.10>>
 			<<set _mercMod += 0.10>>
 			<<set _SFMod += 0.10>>
 		<</if>>
-		<<if (setup.bodyguardCareers.includes($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 95>>
+		<<set _BGCareerGivesBonus = setup.bodyguardCareers.includes(_S.Bodyguard.career) || setup.HGCareers.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>>
 			<<set _defMod += 0.25>>
 			<<set _tacChance += 0.50>>
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 95>>
+		<<elseif _BGTotalIntelligence > 95>>
 			<<set _atkMod += 0.20>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.35>>
-		<<elseif (setup.bodyguardCareers.includes($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 50>>
+		<<elseif _BGCareerGivesBonus && _BGTotalIntelligence > 50>>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 50>>
+		<<elseif _BGTotalIntelligence > 50>>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif (setup.bodyguardCareers.includes($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 15>>
+		<<elseif _BGCareerGivesBonus && _BGTotalIntelligence > 15>>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.15>>
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 15>>
+		<<elseif _BGTotalIntelligence > 15>>
 			<<set _atkMod += 0.5>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif !(setup.bodyguardCareers.includes($Bodyguard.career) && setup.HGCareers.includes($Bodyguard.career) && setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) || $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -50>>
+		<<elseif !_BGCareerGivesBonus || _BGTotalIntelligence < -50>>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.30>>
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -50>>
+		<<elseif _BGTotalIntelligence < -50>>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.25>>
-		<<elseif !(setup.bodyguardCareers.includes($Bodyguard.career) && setup.HGCareers.includes($Bodyguard.career) && setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) || $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -15>>
+		<<elseif !_BGCareerGivesBonus || _BGTotalIntelligence < -15>>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -15>>
+		<<elseif _BGTotalIntelligence < -15>>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.15>>
 		<</if>>
 			/* does she get wounded? */
-		<<if $Bodyguard.skill.combat == 1>>
+		<<if _S.Bodyguard.skill.combat == 1>>
 			<<set _woundChance -= 2>>
 		<</if>>
-		<<set _woundChance -= 0.25 * (getLimbCount($Bodyguard, 105))>>
-		<<if $Bodyguard.health.condition >= 50>>
+		<<set _woundChance -= 0.25 * (getLimbCount(_S.Bodyguard, 105))>>
+		<<if _S.Bodyguard.health.condition >= 50>>
 			<<set _woundChance -= 1>>
 		<</if>>
-		<<if $Bodyguard.weight > 130>>
+		<<if _S.Bodyguard.weight > 130>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.muscles < -30>>
+		<<if _S.Bodyguard.muscles < -30>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if getBestVision($Bodyguard) === 0>>
+		<<if getBestVision(_S.Bodyguard) === 0>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.heels == 1>>
+		<<if _S.Bodyguard.heels == 1>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.boobs >= 1400>>
+		<<if _S.Bodyguard.boobs >= 1400>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.butt >= 6>>
+		<<if _S.Bodyguard.butt >= 6>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.belly >= 10000>>
+		<<if _S.Bodyguard.belly >= 10000>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.dick >= 8>>
+		<<if _S.Bodyguard.dick >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.balls >= 8>>
+		<<if _S.Bodyguard.balls >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -95>>
+		<<if _BGTotalIntelligence < -95>>
 			<<set _woundChance += 1>>
 		<</if>>
 		<<if random(1,100) <= _woundChance>>
@@ -316,103 +315,102 @@
 			<<set _enemyMod += 0.2>>
 		<</if>>
 		/* 60% chance of getting combat skill if not already have it */
-		<<if $Bodyguard.skill.combat == 0 && random(1,100) <= 60>>
+		<<if _S.Bodyguard.skill.combat == 0 && random(1,100) <= 60>>
 			<<set $gainedCombat = 1>>
-			<<set _i = $slaveIndices[$Bodyguard.ID]>>
-			<<set $slaves[_i].skill.combat = 1>>
+			<<set _S.Bodyguard.skill.combat = 1>>
 		<</if>>
 	<<elseif $leadingTroops == "headGirl">>
-		<<if $HeadGirl.devotion < -20>>
+		<<if _S.HeadGirl.devotion < -20>>
 			<<set _slaveMod -= 0.15>>
-		<<elseif $HeadGirl.devotion > 51>>
+		<<elseif _S.HeadGirl.devotion > 51>>
 			<<set _slaveMod += 0.15>>
 		<</if>>
-		<<if ($rep < 10000 && $SecExp.core.authority < 10000) || $HeadGirl.prestige < 1>>
+		<<if ($rep < 10000 && $SecExp.core.authority < 10000) || _S.HeadGirl.prestige < 1>>
 			<<set _militiaMod -= 0.15>>
 			<<set _mercMod -= 0.15>>
 			<<set _SFMod -= 0.15>>
-		<<elseif $HeadGirl.prestige >= 2>>
+		<<elseif _S.HeadGirl.prestige >= 2>>
 			<<set _militiaMod += 0.10>>
 			<<set _mercMod += 0.10>>
 			<<set _SFMod += 0.10>>
 		<</if>>
-		<<if (setup.bodyguardCareers.includes($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 95>>
+		<<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>>
 			<<set _atkMod += 0.25>>
 			<<set _defMod += 0.25>>
 			<<set _tacChance += 0.50>>
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 95>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 95>>
 			<<set _atkMod += 0.20>>
 			<<set _defMod += 0.15>>
 			<<set _tacChance += 0.35>>
-		<<elseif (setup.bodyguardCareers.includes($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+		<<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>>
 			<<set _atkMod += 0.15>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.25>>
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50>>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.10>>
 			<<set _tacChance += 0.20>>
-		<<elseif (setup.bodyguardCareers.includes($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 15>>
+		<<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>>
 			<<set _atkMod += 0.10>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.15>>
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 15>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
 			<<set _atkMod += 0.05>>
 			<<set _defMod += 0.05>>
 			<<set _tacChance += 0.10>>
-		<<elseif !(setup.bodyguardCareers.includes($HeadGirl.career) && setup.HGCareers.includes($HeadGirl.career) && setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) || $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -50>>
+		<<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>>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.15>>
 			<<set _tacChance -= 0.30>>
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -50>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -50>>
 			<<set _atkMod -= 0.15>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.25>>
-		<<elseif !(setup.bodyguardCareers.includes($HeadGirl.career) && setup.HGCareers.includes($HeadGirl.career) && setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) || $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -15>>
+		<<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>>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.10>>
 			<<set _tacChance -= 0.20>>
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -15>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -15>>
 			<<set _atkMod -= 0.10>>
 			<<set _defMod -= 0.05>>
 			<<set _tacChance -= 0.15>>
 		<</if>>
 		/* does she get wounded? */
-		<<if $HeadGirl.skill.combat == 1>>
+		<<if _S.HeadGirl.skill.combat == 1>>
 			<<set _woundChance -= 3>>
 		<</if>>
-		<<set _woundChance -= 0.25 * (getLimbCount($HeadGirl, 105))>>
-		<<if $HeadGirl.health.condition >= 50>>
+		<<set _woundChance -= 0.25 * (getLimbCount(_S.HeadGirl, 105))>>
+		<<if _S.HeadGirl.health.condition >= 50>>
 			<<set _woundChance -= 2>>
 		<</if>>
-		<<if $HeadGirl.weight > 130>>
+		<<if _S.HeadGirl.weight > 130>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.muscles < -30>>
+		<<if _S.HeadGirl.muscles < -30>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if getBestVision($HeadGirl) === 0>>
+		<<if getBestVision(_S.HeadGirl) === 0>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.heels == 1>>
+		<<if _S.HeadGirl.heels == 1>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.boobs >= 1400>>
+		<<if _S.HeadGirl.boobs >= 1400>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.butt >= 6>>
+		<<if _S.HeadGirl.butt >= 6>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.belly >= 10000>>
+		<<if _S.HeadGirl.belly >= 10000>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.dick >= 8>>
+		<<if _S.HeadGirl.dick >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.balls >= 8>>
+		<<if _S.HeadGirl.balls >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -95>>
+		<<if _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant < -95>>
 			<<set _woundChance += 1>>
 		<</if>>
 		<<if random(1,100) <= _woundChance>>
@@ -424,10 +422,9 @@
 			<<set _enemyMod += 0.2>>
 		<</if>>
 		/* 60% chance of getting combat skill if not already have it */
-		<<if $HeadGirl.skill.combat == 0 && random(1,100) <= 60>>
+		<<if _S.HeadGirl.skill.combat == 0 && random(1,100) <= 60>>
 			<<set $gainedCombat = 1>>
-			<<set _i = $slaveIndices[$HeadGirl.ID]>>
-			<<set $slaves[_i].skill.combat = 1>>
+			<<set _S.HeadGirl.skill.combat = 1>>
 		<</if>>
 	<<elseif $leadingTroops == "citizen">>
 		<<if $arcologies[0].FSDegradationist == "unset" && $arcologies[0].FSPaternalist == "unset">>
diff --git a/src/Mods/SecExp/attackOptions.tw b/src/Mods/SecExp/attackOptions.tw
index 15b05eaa48593f489b86ba7c8addfa0983d4a8be..747dc74f7dbb43fff01f2c86ebb2872dfb9666d8 100644
--- a/src/Mods/SecExp/attackOptions.tw
+++ b/src/Mods/SecExp/attackOptions.tw
@@ -19,8 +19,8 @@
 <</if>>
 <hr>
 <<if $majorBattle == 0>>
-	<<if $hasFoughtOnce == 1>>
-		The ominous message dominates the screens of your office, <<print $assistant.name>> quickly gathers all information available to prepare for battle.
+	<<if $battlesCount > 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 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.
@@ -63,86 +63,49 @@
 		Due to their great wealth, Free Cities inevitably become tasty morsels for anyone able to field armed men. Considering the particular needs of arcologies their supply lines tend to be delicate lifelines, often preyed upon by those who stand to gain from the free city downfall.
 	<</if>>
 <<else>>
-	<<if $hasFoughtMajorBattleOnce == 1>>
-		The ominous message dominates the screens of your office, <<print $assistant.name>> quickly gathers all information available to prepare for battle.
-		<<if $attackType == "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.
-			<<else>>
-				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">>
-			<<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.
-			<<else>>
-				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 low numbers.
-		<<elseif $attackType == "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.
-			<<else>>
-				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">>
-			<<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.
-			<<else>>
-				Some of your citizens saw the 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.
-			<</if>>
-			<br>
-			Old world expeditions are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.
-		<</if>>
+	<<if $majorBattlesCount > 0>>
+		The ominous message dominates the screens of your office, and <<print $assistant.name>> quickly gathers all information available to prepare for the major battle ahead.
 	<<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.
 		<br>
 		<br>
-		<<if $attackType == "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.
-			<<else>>
-				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">>
-			<<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.
-			<<else>>
-				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">>
-			<<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.
-			<<else>>
-				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">>
-			<<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.
-			<<else>>
-				Some of your citizens saw the 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.
-			<</if>>
-			<br>
-			Old world expeditions are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.
+	<</if>>
+	<<if $attackType == "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.
+		<<else>>
+			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">>
+		<<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.
+		<<else>>
+			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">>
+		<<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.
+		<<else>>
+			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">>
+		<<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.
+		<<else>>
+			Some of your citizens saw the 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.
+		<</if>>
+		<br>
+		Old world expeditions are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.
 	<</if>>
 <</if>>
 <br><br>
@@ -202,9 +165,9 @@ __Battle Plan__:
 <<case "assistant">>
 	<<set _leader = "$assistant.name will">>
 <<case "bodyguard">>
-	<<set _leader = "$Bodyguard.slaveName will">>
+	<<set _leader = "_S.Bodyguard.slaveName will">>
 <<case "headGirl">>
-	<<set _leader = "$HeadGirl.slaveName will">>
+	<<set _leader = "_S.HeadGirl.slaveName will">>
 <<case "citizen">>
 	<<set _leader = "The citizens' militia commander will">>
 <<case "mercenary">>
@@ -227,19 +190,19 @@ __Battle Plan__:
 		<<set _leader = "$assistant.name will">>
 		<<replace "#leader">><strong><<print _leader>></strong><</replace>>
 	<</link>>
-	<<if $Bodyguard != 0 && $slavesOfficers == 1>>
+	<<if $BodyguardID != 0 && $slavesOfficers == 1>>
 		|
 		<<link "Let your bodyguard lead your troops">>
 			<<set $leadingTroops = "bodyguard">>
-			<<set _leader = "$Bodyguard.slaveName will">>
+			<<set _leader = "_S.Bodyguard.slaveName will">>
 			<<replace "#leader">><strong><<print _leader>></strong><</replace>>
 		<</link>>
 	<</if>>
-	<<if $HeadGirl != 0 && $slavesOfficers == 1>>
+	<<if $HeadGirlID != 0 && $slavesOfficers == 1>>
 		|
 		<<link "Let your Head Girl lead your troops">>
 			<<set $leadingTroops = "headGirl">>
-			<<set _leader = "$HeadGirl.slaveName will">>
+			<<set _leader = "_S.HeadGirl.slaveName will">>
 			<<replace "#leader">><strong><<print _leader>></strong><</replace>>
 		<</link>>
 	<</if>>
@@ -562,9 +525,6 @@ For this battle you choose to follow <span id="tactic"><strong><<print $chosenTa
 <<if App.SecExp.conflict.deployedUnits() > 0>>
 	<<link "Send your orders" "attackHandler">>
 		<<set $battleResult = 4>>						/* sets $battleResult value outside accepted range to avoid evaluation problems */
-		<<if $hasFoughtOnce == 0>>
-			<<set $hasFoughtOnce = 1>>
-		<</if>>
 		<<set $foughtThisWeek = 1>>
 	<</link>>
 <<else>>
@@ -573,9 +533,6 @@ For this battle you choose to follow <span id="tactic"><strong><<print $chosenTa
 <br>
 <<link "Surrender" "attackReport">>
 	<<set $battleResult = -1>>
-	<<if $hasFoughtOnce == 0>>
-		<<set $hasFoughtOnce = 1>>
-	<</if>>
 	<<set $foughtThisWeek = 1>>
 <</link>>
 <br>
@@ -585,9 +542,6 @@ For this battle you choose to follow <span id="tactic"><strong><<print $chosenTa
 /* calculates bribe cost */
 <<link "Attempt to bribe" "attackHandler">>
 	<<set $battleResult = 1>>
-	<<if $hasFoughtOnce == 0>>
-		<<set $hasFoughtOnce = 1>>
-	<</if>>
 	<<set $foughtThisWeek = 1>>
 <</link>>
 <br>&nbsp;&nbsp;&nbsp;&nbsp;//Will cost around <<print cashFormat(Math.round($bribeCost * (1 + either(-1,1) * random(2) * 0.1)))>> (estimate).//
diff --git a/src/Mods/SecExp/attackReport.tw b/src/Mods/SecExp/attackReport.tw
index e73de6208a3b6a89bfacdd5c89995bdd80a8f086..9d7c3568b09decad3164b39edbca55cb929c4b90 100644
--- a/src/Mods/SecExp/attackReport.tw
+++ b/src/Mods/SecExp/attackReport.tw
@@ -854,16 +854,16 @@
 			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">>
-		<<setLocalPronouns $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.conflict.deployedUnits('slaves') >= 1>>
-			<<if $Bodyguard.devotion < -20>>
+			<<if _S.Bodyguard.devotion < -20>>
 				$His low devotion has a negative impact on the morale of your slave soldiers.
-			<<elseif $Bodyguard.devotion > 51>>
+			<<elseif _S.Bodyguard.devotion > 51>>
 				$His devotion to you has a positive impact on the morale of your slave soldiers, proud to be led by one of their own.
 			<</if>>
 		<</if>>
-		<<if _oldRep < 10000 && _oldAuth < 10000 || $Bodyguard.prestige < 1>>
+		<<if _oldRep < 10000 && _oldAuth < 10000 || _S.Bodyguard.prestige < 1>>
 			<<if App.SecExp.conflict.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.conflict.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
 				<<if $SF.Toggle && $SF.Active >= 1 && App.SecExp.conflict.deployedUnits('mercs') == 1 && $SFIntervention>>
@@ -882,7 +882,7 @@
 			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
 				Your soldiers <<if App.SecExp.conflict.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<</if>>
-		<<elseif $Bodyguard.prestige >= 2>>
+		<<elseif _S.Bodyguard.prestige >= 2>>
 			<<if App.SecExp.conflict.deployedUnits('militia') >= 1>>
 				Your
 				<<if App.SecExp.conflict.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
@@ -921,25 +921,27 @@
 				Your soldiers <<if App.SecExp.conflict.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($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 95>>
+		<<set _BGCareerGivesBonus = setup.bodyguardCareers.includes(_S.Bodyguard.career) || setup.HGCareers.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.
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 95>>
+		<<elseif _BGTotalIntelligence > 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($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 50>>
+		<<elseif _BGCareerGivesBonus && _BGTotalIntelligence > 50>>
 			Having both the experience and the intelligence, $he performs admirably as your commander. $His competence greatly increases the efficiency of your troops.
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 50>>
+		<<elseif _BGTotalIntelligence > 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($Bodyguard.career) || setup.HGCareers.includes($Bodyguard.career) || setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) && $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 15>>
+		<<elseif _BGCareerGivesBonus && _BGTotalIntelligence > 15>>
 			Thanks to $his experience, $he is a decent commander, competently guiding your troops through the battle.
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant > 15>>
+		<<elseif _BGTotalIntelligence > 15>>
 			Lacking experience, $his performance as a commander is rather forgettable.
-		<<elseif !(setup.bodyguardCareers.includes($Bodyguard.career) && setup.HGCareers.includes($Bodyguard.career) && setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) || $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -50>>
+		<<elseif !_BGCareerGivesBonus || _BGTotalIntelligence < -50>>
 			Despite the experience $he accumulated during $his past career, $his very low intelligence is a great disadvantage for your troops.
-		<<elseif $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -50>>
+		<<elseif _BGTotalIntelligence < -50>>
 			Without experience and low intelligence, $he performs horribly as a commander, greatly affecting your troops.
-		<<elseif !(setup.bodyguardCareers.includes($Bodyguard.career) && setup.HGCareers.includes($Bodyguard.career) && setup.secExCombatPrestige.includes($Bodyguard.prestigeDesc)) || $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -15>>
+		<<elseif !_BGCareerGivesBonus || _BGTotalIntelligence < -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 $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -15>>
+		<<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>>
@@ -947,7 +949,7 @@
 		<</if>>
 		<<if $leaderWounded == 1>>
 			Unfortunately, @@.red;$he sustained major injuries.@@
-			<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Bodyguard.ID))>>
+			<<set _woundType = App.SecExp.inflictBattleWound(_S.Bodyguard)>>
 			<<if _woundType == "voice">>
 				A stray bullet hit $his neck. While $he fortunately avoided fatal hemorrhaging, $his vocal cords were irreparably damaged.
 			<<elseif _woundType == "eyes">>
@@ -964,19 +966,18 @@
 		<<if $battleResult == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
 			<<set _found = 0>>
 			<<for _i = 0; _i < $slaveVictories.length; _i++>>
-				<<if $slaveVictories[_i].ID == $Bodyguard.ID>>
+				<<if $slaveVictories[_i].ID == $BodyguardID>>
 					<<set $slaveVictories[_i].victories++>>
 					<<set _found = 1>>
 					<<if $slaveVictories[_i].victories >= 10>>
-						<<if $Bodyguard.prestige < 3>>
-							<<set _j = $slaveIndices[$Bodyguard.ID]>>
-							<<set $slaves[_j].prestige++>>
-							<<if $slaves[_j].prestige == 1>>
-								<<set $slaves[_j].prestigeDesc = "$He is well known for being a great commander.">>
-							<<elseif $slaves[_j].prestige == 2>>
-								<<set $slaves[_j].prestigeDesc = "$He is famous for being an incredible commander.">>
-							<<elseif $slaves[_j].prestige == 3>>
-								<<set $slaves[_j].prestigeDesc = "$He is known as a legendary commander all over the world.">>
+						<<if _S.Bodyguard.prestige < 3>>
+							<<set _S.Bodyguard.prestige++>>
+							<<if _S.Bodyguard.prestige == 1>>
+								<<set _S.Bodyguard.prestigeDesc = "$He is well known for being a great commander.">>
+							<<elseif _S.Bodyguard.prestige == 2>>
+								<<set _S.Bodyguard.prestigeDesc = "$He is famous for being an incredible commander.">>
+							<<elseif _S.Bodyguard.prestige == 3>>
+								<<set _S.Bodyguard.prestigeDesc = "$He is known as a legendary commander all over the world.">>
 							<</if>>
 							<<set $slaveVictories[_i].victories = 0>>
 							$He brought your army to victory so many times that $his @@.green;prestige has increased.@@
@@ -986,21 +987,21 @@
 				<</if>>
 			<</for>>
 			<<if _found == 0>>
-				<<set _newSlave = {ID: $Bodyguard.ID, victories: 0}>>
+				<<set _newSlave = {ID: $BodyguardID, victories: 0}>>
 				<<set $slaveVictories.push(_newSlave)>>
 			<</if>>
 		<</if>>
 	<<elseif $leadingTroops == "headGirl">>
-		<<setLocalPronouns $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.conflict.deployedUnits('slaves') >= 1>>
-			<<if $HeadGirl.devotion < -20>>
+			<<if _S.HeadGirl.devotion < -20>>
 				$His low devotion has a negative impact on the morale of your slave soldiers.
-			<<elseif $HeadGirl.devotion > 51>>
+			<<elseif _S.HeadGirl.devotion > 51>>
 				$His devotion to you has a positive impact on the morale of your slave soldiers, proud to be led by one of their own.
 			<</if>>
 		<</if>>
-		<<if _oldRep < 10000 && _oldAuth < 10000 || $HeadGirl.prestige < 1>>
+		<<if _oldRep < 10000 && _oldAuth < 10000 || _S.HeadGirl.prestige < 1>>
 			<<if App.SecExp.conflict.deployedUnits('militia') >= 1>>
 				Your volunteers <<if App.SecExp.conflict.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a
 				<<if App.SecExp.conflict.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
@@ -1019,7 +1020,7 @@
 			<<elseif $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
 				Your soldiers <<if App.SecExp.conflict.deployedUnits('slaves') >= 1>>however<</if>> are not enthusiastic to have a slave as a commander.
 			<</if>>
-		<<elseif $HeadGirl.prestige >= 2>>
+		<<elseif _S.HeadGirl.prestige >= 2>>
 			<<if App.SecExp.conflict.deployedUnits('militia') >= 1>>
 				Your
 				<<if App.SecExp.conflict.deployedUnits('mercs') == 1 && $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
@@ -1058,25 +1059,25 @@
 				Your soldiers <<if App.SecExp.conflict.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($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 95>>
+		<<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>>
 			With $his experience and $his great intellect, $he is able to exploits the smallest of tactical advantages, making your troops greatly effective.
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 95>>
+		<<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($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+		<<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>>
 			Having both the experience and the intelligence, $he performs admirably as your commander. $His competence greatly increases the efficiency of your troops.
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+		<<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($HeadGirl.career) || setup.HGCareers.includes($HeadGirl.career) || setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) && $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 15>>
+		<<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>>
 			Thanks to $his experience, $he is a decent commander, competently guiding your troops through the battle.
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 15>>
+		<<elseif _S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 15>>
 			Lacking experience $his performance as a commander is rather forgettable.
-		<<elseif !(setup.bodyguardCareers.includes($HeadGirl.career) && setup.HGCareers.includes($HeadGirl.career) && setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) || $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -50>>
+		<<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>>
 			Despite the experience $he accumulated during $his past career, $his very low intelligence is a great disadvantage for your troops.
-		<<elseif $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -50>>
+		<<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($HeadGirl.career) && setup.HGCareers.includes($HeadGirl.career) && setup.secExCombatPrestige.includes($HeadGirl.prestigeDesc)) || $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -15>>
+		<<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>>
 			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 $HeadGirl.intelligence+$HeadGirl.intelligenceImplant < -15>>
+		<<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>>
@@ -1084,7 +1085,7 @@
 		<</if>>
 		<<if $leaderWounded == 1>>
 			Unfortunately, @@.red;$he sustained major injuries.@@
-			<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Bodyguard.ID))>>
+			<<set _woundType = App.SecExp.inflictBattleWound(_S.Bodyguard)>>
 			<<if _woundType == "voice">>
 				A stray bullet hit $his neck. While $he fortunately avoided fatal hemorrhaging, $his vocal cords were irreparably damaged.
 			<<elseif _woundType == "eyes">>
@@ -1101,19 +1102,18 @@
 		<<if $battleResult == 3 && $SecExp.settings.battle.allowSlavePrestige == 1>>
 			<<set _found = 0>>
 			<<for _i = 0; _i < $slaveVictories.length; _i++>>
-				<<if $slaveVictories[_i].ID == $HeadGirl.ID>>
+				<<if $slaveVictories[_i].ID == $HeadGirlID>>
 					<<set $slaveVictories[_i].victories++>>
 					<<set _found = 1>>
 					<<if $slaveVictories[_i].victories >= 10>>
-						<<if $HeadGirl.prestige < 3>>
-							<<set _j = $slaveIndices[$HeadGirl.ID]>>
-							<<set $slaves[_j].prestige++>>
-							<<if $slaves[_j].prestige == 1>>
-								<<set $slaves[_j].prestigeDesc = "$He is well known for being a great commander.">>
-							<<elseif $slaves[_j].prestige == 2>>
-								<<set $slaves[_j].prestigeDesc = "$He is famous for being an incredible commander.">>
-							<<elseif $slaves[_j].prestige == 3>>
-								<<set $slaves[_j].prestigeDesc = "$He is known as a legendary commander all over the world.">>
+						<<if _S.HeadGirl.prestige < 3>>
+							<<set _S.HeadGirl.prestige++>>
+							<<if _S.HeadGirl.prestige == 1>>
+								<<set _S.HeadGirl.prestigeDesc = "$He is well known for being a great commander.">>
+							<<elseif _S.HeadGirl.prestige == 2>>
+								<<set _S.HeadGirl.prestigeDesc = "$He is famous for being an incredible commander.">>
+							<<elseif _S.HeadGirl.prestige == 3>>
+								<<set _S.HeadGirl.prestigeDesc = "$He is known as a legendary commander all over the world.">>
 							<</if>>
 							<<set $slaveVictories[_i].victories = 0>>
 							$He brought your army to victory so many times that $his @@.green;prestige has increased.@@
@@ -1123,7 +1123,7 @@
 				<</if>>
 			<</for>>
 			<<if _found == 0>>
-				<<set _newSlave = {ID: $HeadGirl.ID, victories: 0}>>
+				<<set _newSlave = {ID: $HeadGirlID, victories: 0}>>
 				<<set $slaveVictories.push(_newSlave)>>
 			<</if>>
 		<</if>>
@@ -1528,4 +1528,4 @@
 <</for>>
 <<for _i = 0; _i < $mercUnits.length; _i++>>
 	<<set $mercUnits[_i].isDeployed = 0>>
-<</for>>
\ No newline at end of file
+<</for>>
diff --git a/src/Mods/SecExp/buildings/propagandaHub.tw b/src/Mods/SecExp/buildings/propagandaHub.tw
index 2d147704b537648d320f6f847c241c28ef25af7a..a2d306128a40497d382c550b4156c663d54d2251 100644
--- a/src/Mods/SecExp/buildings/propagandaHub.tw
+++ b/src/Mods/SecExp/buildings/propagandaHub.tw
@@ -14,15 +14,15 @@ Propaganda Hub
 
 The propaganda hub is a surprisingly inconspicuous building, dimly lit from the outside. You are well aware however of its role and its importance. You confidently enter its unassuming doorway and calmly review the work being done here.
 
-<<if $Recruiter != 0>>
-	<<setLocalPronouns $Recruiter>>
+<<if $RecruiterID != 0>>
+	<<setLocalPronouns _S.Recruiter>>
 	<br><br>
 	<<if $SecExp.buildings.propHub.recuriterOffice == 0>>
-		<<link "Give <span class='slave-name'><<= SlaveFullName($Recruiter)>></span> an office.""propagandaHub">>
+		<<link "Give <span class='slave-name'><<= SlaveFullName(_S.Recruiter)>></span> an office.""propagandaHub">>
 			<<set $SecExp.buildings.propHub.recuriterOffice = 1>>
 		<</link>>
 	<<else>>
-		<<link "Remove <span class='slave-name'><<= SlaveFullName($Recruiter)>></span> from $his office.""propagandaHub">>
+		<<link "Remove <span class='slave-name'><<= SlaveFullName(_S.Recruiter)>></span> from $his office.""propagandaHub">>
 			<<set $SecExp.buildings.propHub.recuriterOffice = 0>>
 		<</link>>
 	<</if>>
diff --git a/src/Mods/SecExp/edicts.tw b/src/Mods/SecExp/edicts.tw
index 90b55e3985ccceb09f7bd0870ac54ae400c9088e..efeebcc527bdeaa05c20c6e5440b99f232f58745 100644
--- a/src/Mods/SecExp/edicts.tw
+++ b/src/Mods/SecExp/edicts.tw
@@ -94,7 +94,7 @@
 	<br>''All weapons allowed:'' residents are allowed to buy, sell and keep all kind of weapons in the arcology.
 <</if>>
 
-<<if $hasFoughtOnce == 1>>
+<<if $battlesCount > 0 || $rebellionsCount > 0>>
 	<br><br>__Defense Force:__
 	<<if $soldierWages == 0>>
 		<br>''Low wages for soldiers:'' wages for soldiers are set to a low level compared to market standards.
@@ -427,7 +427,7 @@
 	<br>&nbsp;&nbsp;&nbsp;&nbsp;//Will cost some authority each week, but rebellions will be poorly armed.//
 <</if>>
 
-<<if $hasFoughtOnce == 1 || $mercenaries > 0>>
+<<if $battlesCount > 0 || $rebellionsCount > 0 || $mercenaries > 0>>
 	<br><br>__Defense Force__:
 
 	<<if $soldierWages == 0>>
diff --git a/src/Mods/SecExp/js/secExp.js b/src/Mods/SecExp/js/secExp.js
index ad1c54f0f63d5e5437efeb7c2cc63440a87d4aa0..67defdae1fa91fa53edc3d464493094c751bcb80 100644
--- a/src/Mods/SecExp/js/secExp.js
+++ b/src/Mods/SecExp/js/secExp.js
@@ -169,9 +169,9 @@ App.SecExp.conflict = (function() {
 				militiaC++;
 			}
 
-			militiaC += V.militiaUnits.filter((u) => u.active === 1 && V.loyalID.includes(u.ID)).length;
-			slavesC += V.slaveUnits.filter((u) => u.active === 1 && V.loyalID.includes(u.ID)).length;
-			mercsC += V.mercUnits.filter((u) => u.active === 1 && V.loyalID.includes(u.ID)).length;
+			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;
 		}
 
 		if(input === '') {
@@ -220,7 +220,7 @@ App.SecExp.conflict = (function() {
 			if (V.secBots.active === 1) {
 				troops += V.secBots.troops;
 			}
-			countHumanTroops((u) => u.active === 1 && V.loyalID.includes(u.ID));
+			countHumanTroops((u) => u.active === 1 && !V.rebellingID.includes(u.ID));
 			if (V.SF.Toggle && V.SF.Active >= 1) {
 				troops += App.SecExp.troopsFromSF();
 			}
@@ -370,246 +370,7 @@ App.SecExp.Check = (function() {
 	};
 
 	function general() {
-		if (jsDef(V.secExp)) {
-			if (V.secExpEnabled !== 1) {
-				V.secExpEnabled = V.secExp;
-			}
-			delete V.secExp;
-		}
-		if (typeof V.secExpEnabled !== "number") {
-			V.secExpEnabled = 0;
-		}
-		V.SecExp = V.SecExp || SecExpBase();
-		V.SecExp.settings = V.SecExp.settings || {};
-		delete V.SecExp.settings.show;
-
-		delete V.SecExp.army;
-
-		if (V.secExpEnabled > 0) {
-			Object.assign(V.secBots, {
-				active: V.secBots.active || V.arcologyUpgrade.drones > 0 ? 1 : 0,
-				ID: -1,
-				isDeployed: V.secBots.isDeployed || 0,
-				troops: Math.max(V.secBots.troops, V.arcologyUpgrade.drones > 0 ? 30 : 0),
-				maxTroops: Math.max(V.secBots.maxTroops, V.arcologyUpgrade.drones > 0 ? 30 : 0)
-			});
-
-			V.SecExp.core = V.SecExp.core || {};
-
-			V.SecExp.core.trade = V.SecExp.core.trade || 0;
-			if (passage() === "Acquisition" || V.SecExp.core.trade === 0) {
-				let init = jsRandom(20, 30);
-				if (V.terrain === "urban") {
-					init += jsRandom(10, 10);
-				} else if (V.terrain === "ravine") {
-					init -= jsRandom(5, 5);
-				}
-				if (["BlackHat", "capitalist", "celebrity", "wealth"].includes(V.PC.career)) {
-					init += jsRandom(5, 5);
-				} else if (["escort", "gang", "servant"].includes(V.PC.career)) {
-					init -= jsRandom(5, 5);
-				}
-				V.SecExp.core.trade = init;
-			}
-			if (jsDef(V.trade)) {
-				V.SecExp.core.trade = V.trade;
-			}
-
-			V.SecExp.core.authority = V.SecExp.core.authority || 0;
-			if (jsDef(V.authority)) {
-				V.SecExp.core.authority = V.authority;
-			}
-
-			V.SecExp.security = V.SecExp.security || {};
-			V.SecExp.security.cap = V.SecExp.security.cap || 100;
-			if (jsDef(V.security)) {
-				V.SecExp.security.cap = V.security;
-			}
-			V.SecExp.core.crimeLow = V.SecExp.core.crimeLow || 30;
-			if (jsDef(V.crime)) {
-				V.SecExp.core.crimeLow = V.crime;
-			}
-
-			V.SecExp.settings.difficulty = V.SecExp.settings.difficulty || 1;
-			if (jsDef(V.difficulty)) {
-				V.SecExp.settings.difficulty = V.difficulty;
-			}
-
-			V.SecExp.settings.battle = V.SecExp.settings.battle || {};
-			if (!jsDef(V.SecExp.settings.battle.enabled)) {
-				V.SecExp.settings.battle.enabled = 1;
-			}
-			if (jsDef(V.battlesEnabled)) {
-				V.SecExp.settings.battle.enabled = V.battlesEnabled;
-			}
-			delete V.SecExp.battle;
-
-			V.SecExp.settings.battle.major = V.SecExp.settings.battle.major || {};
-			V.SecExp.settings.battle.frequency = V.SecExp.settings.battle.frequency || 1;
-			if (jsDef(V.battleFrequency)) {
-				V.SecExp.settings.battle.frequency = V.battleFrequency;
-			}
-			V.SecExp.settings.battle.force = V.SecExp.settings.battle.force || 0;
-			if (jsDef(V.forceBattle)) {
-				V.SecExp.settings.battle.force = V.forceBattle;
-			}
-
-			if (jsDef(V.readiness)) {
-				if(V.readiness === 10) {
-					V.sectionInFirebase = 1;
-				}
-			}
-
-			V.SecExp.settings.unitDescriptions = V.SecExp.settings.unitDescriptions || 0;
-
-			if (!jsDef(V.SecExp.settings.battle.allowSlavePrestige)) {
-				V.SecExp.settings.battle.allowSlavePrestige = 1;
-			}
-			if (jsDef(V.allowPrestigeFromBattles)) {
-				V.SecExp.settings.battle.allowSlavePrestige = V.allowPrestigeFromBattles;
-			}
-
-			V.SecExp.settings.battle.major.enabled = V.SecExp.settings.battle.major.enabled || 0;
-			if (jsDef(V.majorBattlesEnabled)) {
-				V.SecExp.settings.battle.major.enabled = V.majorBattlesEnabled;
-			}
-
-			if (!jsDef(V.SecExp.settings.battle.major.gameOver)) {
-				V.SecExp.settings.battle.major.gameOver = 1;
-			}
-			if (jsDef(V.majorBattleGameOver)) {
-				V.SecExp.settings.battle.major.gameOver = V.majorBattleGameOver;
-			}
-			V.SecExp.settings.battle.major.force = V.SecExp.settings.battle.major.force || 0;
-			if (jsDef(V.forceMajorBattle)) {
-				V.SecExp.settings.battle.major.force = V.forceMajorBattle;
-			}
-			V.SecExp.settings.battle.major.mult = V.SecExp.settings.battle.major.mult || 1;
-
-			V.SecExp.settings.rebellion = V.SecExp.settings.rebellion || {};
-			if (!jsDef(V.SecExp.settings.rebellion.enabled)) {
-				V.SecExp.settings.rebellion.enabled = 1;
-			}
-			if (jsDef(V.rebellionsEnabled)) {
-				V.SecExp.settings.rebellion.enabled = V.rebellionsEnabled;
-			}
-
-			V.SecExp.settings.rebellion.force = V.SecExp.settings.rebellion.force || 0;
-			if (jsDef(V.forceRebellion)) {
-				V.SecExp.settings.rebellion.force = V.forceRebellion;
-			}
-			if (!jsDef(V.SecExp.settings.rebellion.gameOver)) {
-				V.SecExp.settings.rebellion.gameOver = 1;
-			}
-			if (jsDef(V.rebellionGameOver)) {
-				V.SecExp.settings.rebellion.gameOver = V.rebellionGameOver;
-			}
-
-			V.SecExp.settings.rebellion.speed = V.SecExp.settings.rebellion.speed || 1;
-			if (jsDef(V.rebellionSpeed)) {
-				V.SecExp.settings.rebellion.speed = V.rebellionSpeed;
-			}
-
-			if (V.SecExp.settings.battle.enabled + V.SecExp.settings.rebellion.enabled > 0) {
-				V.SecExp.settings.showStats = V.SecExp.settings.showStats || 0;
-				if (jsDef(V.showBattleStatistics)) {
-					V.SecExp.settings.showStats = V.showBattleStatistics;
-				}
-			}
-
-			V.SecExp.buildings = V.SecExp.buildings || {};
-			V.SecExp.buildings.propHub = V.SecExp.buildings.propHub || {};
-			V.SecExp.buildings.propHub.active = V.SecExp.buildings.propHub.active || 0;
-			if (V.SecExp.buildings.pr === null) {
-				delete V.SecExp.buildings.pr;
-			}
-			if (jsDef(V.SecExp.buildings.pr)) {
-				V.SecExp.buildings.propHub = V.SecExp.buildings.pr;
-				delete V.SecExp.buildings.pr;
-			}
-			if (jsDef(V.propHub)) {
-				V.SecExp.buildings.propHub.active = V.propHub;
-			}
-
-			if (V.SecExp.buildings.propHub.active > 0) {
-				V.SecExp.buildings.propHub.recuriterOffice = V.SecExp.buildings.propHub.recuriterOffice || 0;
-				V.SecExp.buildings.propHub.campaign = V.SecExp.buildings.propHub.campaign || 0;
-				if (jsDef(V.propCampaign)) {
-					V.SecExp.buildings.propHub.campaign = V.propCampaign;
-				}
-
-				V.SecExp.buildings.propHub.miniTruth = V.SecExp.buildings.propHub.miniTruth || 0;
-				if (jsDef(V.miniTruth)) {
-					V.SecExp.buildings.propHub.miniTruth = V.miniTruth;
-				}
-
-				V.SecExp.buildings.propHub.secretService = V.SecExp.buildings.propHub.secretService || 0;
-				if (jsDef(V.secretService)) {
-					V.SecExp.buildings.propHub.secretService = V.secretService;
-				}
-				if (jsDef(V.SecExp.buildings.propHub.SS)) {
-					V.SecExp.buildings.propHub.secretService = V.SecExp.buildings.propHub.SS;
-					delete V.SecExp.buildings.propHub.SS;
-				}
-
-				if (V.SecExp.buildings.propHub.campaign >= 1) {
-					V.SecExp.buildings.propHub.focus = V.SecExp.buildings.propHub.focus || "social engineering";
-					if (jsDef(V.propFocus) && V.propFocus !== "none") {
-						V.SecExp.buildings.propHub.focus = V.propFocus;
-					}
-				}
-
-				if (jsDef(V.RecuriterOffice)) {
-					V.recuriterOffice = V.RecuriterOffice;
-				}
-				const vars = ['recuriterOffice', 'fakeNews', 'controlLeaks', 'marketInfiltration', 'blackOps'];
-				for(let i = 0; i < vars.length; i++) {
-					if (jsDef(V[vars[i]]) && V[vars[i]] > 0) {
-						V.SecExp.buildings.propHub[vars[i]] = V[vars[i]];
-						delete V[vars[i]];
-					} else {
-						V.SecExp.buildings.propHub[vars[i]] = V.SecExp.buildings.propHub[vars[i]] || 0;
-					}
-				}
-			}
-
-			V.SecExp.buildings.barracks = V.SecExp.buildings.barracks || {};
-			V.SecExp.buildings.barracks.active = V.SecExp.buildings.barracks.active || 0;
-			if (jsDef(V.secBarracks)) {
-				V.SecExp.buildings.barracks.active = V.secBarracks;
-			}
-
-			if (V.SecExp.buildings.barracks.active > 0) {
-				V.SecExp.buildings.barracks.upgrades = V.SecExp.buildings.barracks.upgrades || {};
-				V.SecExp.buildings.barracks.upgrades.size = V.SecExp.buildings.barracks.upgrades.size || 0;
-				V.SecExp.buildings.barracks.upgrades.luxury = V.SecExp.buildings.barracks.upgrades.luxury || 0;
-				V.SecExp.buildings.barracks.upgrades.training = V.SecExp.buildings.barracks.upgrades.training || 0;
-				V.SecExp.buildings.barracks.upgrades.loyaltyMod = V.SecExp.buildings.barracks.upgrades.loyaltyMod || 0;
-				if (jsDef(V.secBarracksUpgrades)) {
-					V.SecExp.buildings.barracks.upgrades = V.secBarracksUpgrades;
-				}
-			}
-
-			V.SecExp.proclamation = V.SecExp.proclamation || {};
-			V.SecExp.proclamation.cooldown = V.SecExp.proclamation.cooldown || 0;
-			if (jsDef(V.proclamationsCooldown)) {
-				V.SecExp.proclamation.cooldown = V.proclamationsCooldown;
-			}
-			V.SecExp.proclamation.currency = V.SecExp.proclamation.currency || "";
-			if (jsDef(V.proclamationCurrency)) {
-				V.SecExp.proclamation.currency = V.proclamationCurrency;
-			}
-			V.SecExp.proclamation.type = V.SecExp.proclamation.type || "crime";
-			if (jsDef(V.proclamationType)) {
-				if (V.proclamationType !== "none") {
-					V.SecExp.proclamation.type = V.proclamationType;
-				}
-			}
-		}
-
-		if (jsDef(V.SecExp.core)) {
-			delete V.SecExp.core.crimeCap;
-		}
+		App.SecExp.generalBC();
 	} // Closes general check function.
 
 	function secRestPoint() {
diff --git a/src/Mods/SecExp/js/secExpBC.js b/src/Mods/SecExp/js/secExpBC.js
new file mode 100644
index 0000000000000000000000000000000000000000..efbc3f89b41104e62e9f9cb74b4c3a268f8dcdf7
--- /dev/null
+++ b/src/Mods/SecExp/js/secExpBC.js
@@ -0,0 +1,243 @@
+// @ts-nocheck
+App.SecExp.generalBC = function (){
+	if (jsDef(V.secExp)) {
+		if (V.secExpEnabled !== 1) {
+			V.secExpEnabled = V.secExp;
+		}
+		delete V.secExp;
+	}
+	if (typeof V.secExpEnabled !== "number") {
+		V.secExpEnabled = 0;
+	}
+	V.SecExp = V.SecExp || SecExpBase();
+	V.SecExp.settings = V.SecExp.settings || {};
+	delete V.SecExp.settings.show;
+
+	delete V.SecExp.army;
+
+	if (V.secExpEnabled > 0) {
+		Object.assign(V.secBots, {
+			active: V.secBots.active || V.arcologyUpgrade.drones > 0 ? 1 : 0,
+			ID: -1,
+			isDeployed: V.secBots.isDeployed || 0,
+			troops: Math.max(V.secBots.troops, V.arcologyUpgrade.drones > 0 ? 30 : 0),
+			maxTroops: Math.max(V.secBots.maxTroops, V.arcologyUpgrade.drones > 0 ? 30 : 0)
+		});
+
+		V.SecExp.core = V.SecExp.core || {};
+
+		V.SecExp.core.trade = V.SecExp.core.trade || 0;
+		if (passage() === "Acquisition" || V.SecExp.core.trade === 0) {
+			let init = jsRandom(20, 30);
+			if (V.terrain === "urban") {
+				init += jsRandom(10, 10);
+			} else if (V.terrain === "ravine") {
+				init -= jsRandom(5, 5);
+			}
+			if (["BlackHat", "capitalist", "celebrity", "wealth"].includes(V.PC.career)) {
+				init += jsRandom(5, 5);
+			} else if (["escort", "gang", "servant"].includes(V.PC.career)) {
+				init -= jsRandom(5, 5);
+			}
+			V.SecExp.core.trade = init;
+		}
+		if (jsDef(V.trade)) {
+			V.SecExp.core.trade = V.trade;
+		}
+
+		V.SecExp.core.authority = V.SecExp.core.authority || 0;
+		if (jsDef(V.authority)) {
+			V.SecExp.core.authority = V.authority;
+		}
+
+		V.SecExp.security = V.SecExp.security || {};
+		V.SecExp.security.cap = V.SecExp.security.cap || 100;
+		if (jsDef(V.security)) {
+			V.SecExp.security.cap = V.security;
+		}
+		V.SecExp.core.crimeLow = V.SecExp.core.crimeLow || 30;
+		if (jsDef(V.crime)) {
+			V.SecExp.core.crimeLow = V.crime;
+		}
+
+		V.SecExp.settings.difficulty = V.SecExp.settings.difficulty || 1;
+		if (jsDef(V.difficulty)) {
+			V.SecExp.settings.difficulty = V.difficulty;
+		}
+
+		V.SecExp.settings.battle = V.SecExp.settings.battle || {};
+		if (!jsDef(V.SecExp.settings.battle.enabled)) {
+			V.SecExp.settings.battle.enabled = 1;
+		}
+		if (jsDef(V.battlesEnabled)) {
+			V.SecExp.settings.battle.enabled = V.battlesEnabled;
+		}
+		delete V.SecExp.battle;
+
+		V.SecExp.settings.battle.major = V.SecExp.settings.battle.major || {};
+		V.SecExp.settings.battle.frequency = V.SecExp.settings.battle.frequency || 1;
+		if (jsDef(V.battleFrequency)) {
+			V.SecExp.settings.battle.frequency = V.battleFrequency;
+		}
+		V.SecExp.settings.battle.force = V.SecExp.settings.battle.force || 0;
+		if (jsDef(V.forceBattle)) {
+			V.SecExp.settings.battle.force = V.forceBattle;
+		}
+
+		if (jsDef(V.readiness)) {
+			if(V.readiness === 10) {
+				V.sectionInFirebase = 1;
+			}
+		}
+
+		V.SecExp.settings.unitDescriptions = V.SecExp.settings.unitDescriptions || 0;
+
+		if (!jsDef(V.SecExp.settings.battle.allowSlavePrestige)) {
+			V.SecExp.settings.battle.allowSlavePrestige = 1;
+		}
+		if (jsDef(V.allowPrestigeFromBattles)) {
+			V.SecExp.settings.battle.allowSlavePrestige = V.allowPrestigeFromBattles;
+		}
+
+		V.SecExp.settings.battle.major.enabled = V.SecExp.settings.battle.major.enabled || 0;
+		if (jsDef(V.majorBattlesEnabled)) {
+			V.SecExp.settings.battle.major.enabled = V.majorBattlesEnabled;
+		}
+
+		if (!jsDef(V.SecExp.settings.battle.major.gameOver)) {
+			V.SecExp.settings.battle.major.gameOver = 1;
+		}
+		if (jsDef(V.majorBattleGameOver)) {
+			V.SecExp.settings.battle.major.gameOver = V.majorBattleGameOver;
+		}
+		V.SecExp.settings.battle.major.force = V.SecExp.settings.battle.major.force || 0;
+		if (jsDef(V.forceMajorBattle)) {
+			V.SecExp.settings.battle.major.force = V.forceMajorBattle;
+		}
+		V.SecExp.settings.battle.major.mult = V.SecExp.settings.battle.major.mult || 1;
+
+		V.SecExp.settings.rebellion = V.SecExp.settings.rebellion || {};
+		if (!jsDef(V.SecExp.settings.rebellion.enabled)) {
+			V.SecExp.settings.rebellion.enabled = 1;
+		}
+		if (jsDef(V.rebellionsEnabled)) {
+			V.SecExp.settings.rebellion.enabled = V.rebellionsEnabled;
+		}
+
+		V.SecExp.settings.rebellion.force = V.SecExp.settings.rebellion.force || 0;
+		if (jsDef(V.forceRebellion)) {
+			V.SecExp.settings.rebellion.force = V.forceRebellion;
+		}
+		if (!jsDef(V.SecExp.settings.rebellion.gameOver)) {
+			V.SecExp.settings.rebellion.gameOver = 1;
+		}
+		if (jsDef(V.rebellionGameOver)) {
+			V.SecExp.settings.rebellion.gameOver = V.rebellionGameOver;
+		}
+
+		V.SecExp.settings.rebellion.speed = V.SecExp.settings.rebellion.speed || 1;
+		if (jsDef(V.rebellionSpeed)) {
+			V.SecExp.settings.rebellion.speed = V.rebellionSpeed;
+		}
+
+		if (V.SecExp.settings.battle.enabled + V.SecExp.settings.rebellion.enabled > 0) {
+			V.SecExp.settings.showStats = V.SecExp.settings.showStats || 0;
+			if (jsDef(V.showBattleStatistics)) {
+				V.SecExp.settings.showStats = V.showBattleStatistics;
+			}
+		}
+
+		V.SecExp.buildings = V.SecExp.buildings || {};
+		V.SecExp.buildings.propHub = V.SecExp.buildings.propHub || {};
+		V.SecExp.buildings.propHub.active = V.SecExp.buildings.propHub.active || 0;
+		if (V.SecExp.buildings.pr === null) {
+			delete V.SecExp.buildings.pr;
+		}
+		if (jsDef(V.SecExp.buildings.pr)) {
+			V.SecExp.buildings.propHub = V.SecExp.buildings.pr;
+			delete V.SecExp.buildings.pr;
+		}
+		if (jsDef(V.propHub)) {
+			V.SecExp.buildings.propHub.active = V.propHub;
+		}
+
+		if (V.SecExp.buildings.propHub.active > 0) {
+			V.SecExp.buildings.propHub.recuriterOffice = V.SecExp.buildings.propHub.recuriterOffice || 0;
+			V.SecExp.buildings.propHub.campaign = V.SecExp.buildings.propHub.campaign || 0;
+			if (jsDef(V.propCampaign)) {
+				V.SecExp.buildings.propHub.campaign = V.propCampaign;
+			}
+
+			V.SecExp.buildings.propHub.miniTruth = V.SecExp.buildings.propHub.miniTruth || 0;
+			if (jsDef(V.miniTruth)) {
+				V.SecExp.buildings.propHub.miniTruth = V.miniTruth;
+			}
+
+			V.SecExp.buildings.propHub.secretService = V.SecExp.buildings.propHub.secretService || 0;
+			if (jsDef(V.secretService)) {
+				V.SecExp.buildings.propHub.secretService = V.secretService;
+			}
+			if (jsDef(V.SecExp.buildings.propHub.SS)) {
+				V.SecExp.buildings.propHub.secretService = V.SecExp.buildings.propHub.SS;
+				delete V.SecExp.buildings.propHub.SS;
+			}
+
+			if (V.SecExp.buildings.propHub.campaign >= 1) {
+				V.SecExp.buildings.propHub.focus = V.SecExp.buildings.propHub.focus || "social engineering";
+				if (jsDef(V.propFocus) && V.propFocus !== "none") {
+					V.SecExp.buildings.propHub.focus = V.propFocus;
+				}
+			}
+
+			if (jsDef(V.RecuriterOffice)) {
+				V.recuriterOffice = V.RecuriterOffice;
+			}
+			const vars = ['recuriterOffice', 'fakeNews', 'controlLeaks', 'marketInfiltration', 'blackOps'];
+			for(let i = 0; i < vars.length; i++) {
+				if (jsDef(V[vars[i]]) && V[vars[i]] > 0) {
+					V.SecExp.buildings.propHub[vars[i]] = V[vars[i]];
+					delete V[vars[i]];
+				} else {
+					V.SecExp.buildings.propHub[vars[i]] = V.SecExp.buildings.propHub[vars[i]] || 0;
+				}
+			}
+		}
+
+		V.SecExp.buildings.barracks = V.SecExp.buildings.barracks || {};
+		V.SecExp.buildings.barracks.active = V.SecExp.buildings.barracks.active || 0;
+		if (jsDef(V.secBarracks)) {
+			V.SecExp.buildings.barracks.active = V.secBarracks;
+		}
+
+		if (V.SecExp.buildings.barracks.active > 0) {
+			V.SecExp.buildings.barracks.upgrades = V.SecExp.buildings.barracks.upgrades || {};
+			V.SecExp.buildings.barracks.upgrades.size = V.SecExp.buildings.barracks.upgrades.size || 0;
+			V.SecExp.buildings.barracks.upgrades.luxury = V.SecExp.buildings.barracks.upgrades.luxury || 0;
+			V.SecExp.buildings.barracks.upgrades.training = V.SecExp.buildings.barracks.upgrades.training || 0;
+			V.SecExp.buildings.barracks.upgrades.loyaltyMod = V.SecExp.buildings.barracks.upgrades.loyaltyMod || 0;
+			if (jsDef(V.secBarracksUpgrades)) {
+				V.SecExp.buildings.barracks.upgrades = V.secBarracksUpgrades;
+			}
+		}
+
+		V.SecExp.proclamation = V.SecExp.proclamation || {};
+		V.SecExp.proclamation.cooldown = V.SecExp.proclamation.cooldown || 0;
+		if (jsDef(V.proclamationsCooldown)) {
+			V.SecExp.proclamation.cooldown = V.proclamationsCooldown;
+		}
+		V.SecExp.proclamation.currency = V.SecExp.proclamation.currency || "";
+		if (jsDef(V.proclamationCurrency)) {
+			V.SecExp.proclamation.currency = V.proclamationCurrency;
+		}
+		V.SecExp.proclamation.type = V.SecExp.proclamation.type || "crime";
+		if (jsDef(V.proclamationType)) {
+			if (V.proclamationType !== "none") {
+				V.SecExp.proclamation.type = V.proclamationType;
+			}
+		}
+	}
+
+	if (jsDef(V.SecExp.core)) {
+		delete V.SecExp.core.crimeCap;
+	}
+};
diff --git a/src/Mods/SecExp/rebellionGenerator.tw b/src/Mods/SecExp/rebellionGenerator.tw
index cb17b7b64985b00b84490a19aa7287f10395511f..9fce42c4b92fd5fca715e3f9301994541c381da8 100644
--- a/src/Mods/SecExp/rebellionGenerator.tw
+++ b/src/Mods/SecExp/rebellionGenerator.tw
@@ -288,19 +288,13 @@
 <</if>>
 
 /* resetting ID list */
-<<unset $rebellingID>>
 <<set $rebellingID = []>>
-<<unset $loyalID>>
-<<set $loyalID = []>>
 
 /* if a rebellion fires determine amount of rebels and rebelling units */
 <<if $slaveRebellion == 1>>
 	<<set $engageRule = 0>>
 	<<set $lastRebellionWeeks = 0>>
 	<<set $leadingTroops = "assistant">>
-	<<if $hasRebelledOnce == 0>>
-		<<set $hasRebelledOnce = 1>>
-	<</if>>
 	/* 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)>>
@@ -323,9 +317,6 @@
 				<<set $rebellingID.push($slaveUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($slaveUnits[_i].ID))>>
-			<<set $loyalID.push($slaveUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<for _i = 0; _i < $militiaUnits.length; _i++>>
 		<<if $militiaUnits[_i].loyalty < 10>>
@@ -341,9 +332,6 @@
 				<<set $rebellingID.push($militiaUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($militiaUnits[_i].ID))>>
-			<<set $loyalID.push($militiaUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<for _i = 0; _i < $mercUnits.length; _i++>>
 		<<if $mercUnits[_i].loyalty < 10>>
@@ -359,17 +347,11 @@
 				<<set $rebellingID.push($mercUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($mercUnits[_i].ID))>>
-			<<set $loyalID.push($mercUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<set $attackEquip = Math.clamp($weaponsLaw + random(-2,1),0,4)>>
 <<elseif $citizenRebellion == 1>>
 	<<set $engageRule = 0>>
 	<<set $lastRebellionWeeks = 0>>
-	<<if $hasRebelledOnce == 0>>
-		<<set $hasRebelledOnce = 1>>
-	<</if>>
 	<<set $leadingTroops = "assistant">>
 	/* calc how many citizens participate */
 	<<set _authFactor = Math.clamp(1 - ($SecExp.core.authority / 20000),0.4,0.6)>>
@@ -393,9 +375,6 @@
 				<<set $rebellingID.push($militiaUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($militiaUnits[_i].ID))>>
-			<<set $loyalID.push($militiaUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<for _i = 0; _i < $slaveUnits.length; _i++>>
 		<<if $slaveUnits[_i].loyalty < 10>>
@@ -411,9 +390,6 @@
 				<<set $rebellingID.push($slaveUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($slaveUnits[_i].ID))>>
-			<<set $loyalID.push($slaveUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<for _i = 0; _i < $mercUnits.length; _i++>>
 		<<if $mercUnits[_i].loyalty < 10>>
@@ -429,9 +405,6 @@
 				<<set $rebellingID.push($mercUnits[_i].ID)>>
 			<</if>>
 		<</if>>
-		<<if !($rebellingID.includes($mercUnits[_i].ID))>>
-			<<set $loyalID.push($mercUnits[_i].ID)>>
-		<</if>>
 	<</for>>
 	<<set $attackEquip = Math.clamp($weaponsLaw + random(-1,1),0,4)>>
 <<else>>
diff --git a/src/Mods/SecExp/rebellionHandler.tw b/src/Mods/SecExp/rebellionHandler.tw
index 2682d53b923997f71f86cae2c302738f0a01d76b..7260fe6d976c7fd7bb1395d1ef8266861cbab184 100644
--- a/src/Mods/SecExp/rebellionHandler.tw
+++ b/src/Mods/SecExp/rebellionHandler.tw
@@ -2,7 +2,6 @@
 
 <<set $nextButton = " ", $nextLink = "attackReport", $encyclopedia = "Battles">>
 
-<<set _turn = 0>>
 <<set _turns = $maxTurns * 2>>
 <<set _attack = 0>>
 <<set _defense = 0>>
@@ -15,9 +14,6 @@
 <<set _enemyHp = 0>>
 <<set _enemyBaseHp = 0>>
 <<set _woundChance = 5>>								/* leader has a base chance of 5% to get wounded */
-<<set _tacChance = 0.5>>								/* by default tactics have a 50% chance of succeeding */
-<<set _expBonus = 0>>
-<<set _loyaltyBonus = 0>>
 <<set _irregularMod = 0>>
 <<set _armyMod = 0>>
 
@@ -59,7 +55,7 @@
 <</if>>
 
 <<for _i = 0; _i < $militiaUnits.length; _i++>>
-	<<if $militiaUnits[_i].active == 1 && $loyalID.includes($militiaUnits[_i].ID)>>
+	<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Militia", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -67,7 +63,7 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $slaveUnits.length; _i++>>
-	<<if $slaveUnits[_i].active == 1 && $loyalID.includes($slaveUnits[_i].ID)>>
+	<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Slaves", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
@@ -75,7 +71,7 @@
 	<</if>>
 <</for>>
 <<for _i = 0; _i < $mercUnits.length; _i++>>
-	<<if $mercUnits[_i].active == 1 && $loyalID.includes($mercUnits[_i].ID)>>
+	<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
 		<<set _unit = App.SecExp.getUnit("Mercs", _i)>>
 		<<set _attack += _unit.attack>>
 		<<set _defense += _unit.defense>>
diff --git a/src/Mods/SecExp/rebellionOptions.tw b/src/Mods/SecExp/rebellionOptions.tw
index 8e7da9e0b9fff123a19a2ac5890864954295d233..834b91f0092fa0900d5fd2d34cbff62192e1183f 100644
--- a/src/Mods/SecExp/rebellionOptions.tw
+++ b/src/Mods/SecExp/rebellionOptions.tw
@@ -63,14 +63,15 @@
 		betrayed you and joined the insurrection.
 	<</if>>
 	<<set _count = 0>>
-	<<if $loyalID.length > 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 && ($loyalID.includes($militiaUnits[_i].ID))>>
+			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$militiaUnits[_i].platoonName,
 				<<else>>
 					$militiaUnits[_i].platoonName
@@ -78,9 +79,9 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && ($loyalID.includes($slaveUnits[_i].ID))>>
+			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$slaveUnits[_i].platoonName,
 				<<else>>
 					$slaveUnits[_i].platoonName
@@ -88,16 +89,16 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && ($loyalID.includes($mercUnits[_i].ID))>>
+			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<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>>
+		<<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>>
@@ -159,13 +160,14 @@
 	<</if>>
 	<br>
 	<<set _count = 0>>
-	<<if $loyalID.length > 0>>
+	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $rebellingID.length>>
+	<<if _loyalUnits > 0>>
 		<br>
 		<<if $arcologyUpgrade.drones == 1>>Your security drones,<</if>>
 		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && ($loyalID.includes($militiaUnits[_i].ID))>>
+			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$militiaUnits[_i].platoonName,
 				<<else>>
 					$militiaUnits[_i].platoonName
@@ -173,9 +175,9 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && ($loyalID.includes($slaveUnits[_i].ID))>>
+			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$slaveUnits[_i].platoonName,
 				<<else>>
 					$slaveUnits[_i].platoonName
@@ -183,9 +185,9 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && ($loyalID.includes($mercUnits[_i].ID))>>
+			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$mercUnits[_i].platoonName,
 				<<else>>
 					$mercUnits[_i].platoonName
@@ -289,16 +291,10 @@ We can dedicate some of our forces to the protection of the vital parts of the a
 <br>
 <<link "Proceed" "rebellionHandler">>
 	<<set $battleResult = 4>>						/* sets $battleResult value outside accepted range to avoid evaluation problems */
-	<<if $hasFoughtOnce == 0>>
-		<<set $hasFoughtOnce = 1>>
-	<</if>>
 	<<set $foughtThisWeek = 1>>
 <</link>>
 <br>
 <<link "Surrender" "rebellionHandler">>
 	<<set $battleResult = -1>>
-	<<if $hasFoughtOnce == 0>>
-		<<set $hasFoughtOnce = 1>>
-	<</if>>
 	<<set $foughtThisWeek = 1>>
 <</link>>
diff --git a/src/Mods/SecExp/rebellionReport.tw b/src/Mods/SecExp/rebellionReport.tw
index d6977359a9802640750bdd68027c7636ee5d028a..a32f7242c41b313ada87e19aea5f4efd40e981ad 100644
--- a/src/Mods/SecExp/rebellionReport.tw
+++ b/src/Mods/SecExp/rebellionReport.tw
@@ -518,12 +518,12 @@
 <<else>>
 	The garrison assigned to the facility housing $assistant.name's mainframe prevented any sabotage attempt.
 <</if>>
-<<if $garrison.penthouse == 1 && $Bodyguard != 0>>
-	<<setLocalPronouns $Bodyguard 2>>
+<<if $garrison.penthouse == 1 && $BodyguardID != 0>>
+	<<setLocalPronouns _S.Bodyguard 2>>
 	The garrison assigned to the penthouse together with your loyal Bodyguard stopped all assaults against your penthouse with ease.
-<<elseif $Bodyguard != 0>>
+<<elseif $BodyguardID != 0>>
 	<<if random(1,100) <= 75>>
-		During the fighting a group of slaves assaulted the penthouse. Your Bodyguard, $Bodyguard.slaveName, stood strong against the furious attack.
+		During the fighting a group of slaves assaulted the penthouse. Your Bodyguard, _S.Bodyguard.slaveName, stood strong against the furious attack.
 		<<set _woundChance = 0>>
 		<<if $PC.career == "mercenary" || $PC.career == "gang">>
 			<<set _woundChance -= 5>>
@@ -560,50 +560,50 @@
 		<<else>>
 			Fortunately you managed to avoid injury.
 		<</if>>
-		<<if $Concubine != 0>>
-			<<setLocalPronouns $Concubine>>
+		<<if _S.Concubine>>
+			<<setLocalPronouns _S.Concubine>>
 			<<set _woundChance = 0>>
-			<<if $Concubine.skill.combat == 1>>
+			<<if _S.Concubine.skill.combat == 1>>
 				<<set _woundChance -= 2>>
 			<</if>>
-			<<set _woundChance -= 0.25 * (getLimbCount($Concubine, 105))>>
-			<<if $Concubine.health.condition >= 50>>
+			<<set _woundChance -= 0.25 * (getLimbCount(_S.Concubine, 105))>>
+			<<if _S.Concubine.health.condition >= 50>>
 				<<set _woundChance -= 1>>
 			<</if>>
-			<<if $Concubine.weight > 130>>
+			<<if _S.Concubine.weight > 130>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.muscles < -30>>
+			<<if _S.Concubine.muscles < -30>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if getBestVision($Concubine) === 0>>
+			<<if getBestVision(_S.Concubine) === 0>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.heels == 1>>
+			<<if _S.Concubine.heels == 1>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.boobs >= 1400>>
+			<<if _S.Concubine.boobs >= 1400>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.butt >= 6>>
+			<<if _S.Concubine.butt >= 6>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.belly >= 10000>>
+			<<if _S.Concubine.belly >= 10000>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.dick >= 8>>
+			<<if _S.Concubine.dick >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.balls >= 8>>
+			<<if _S.Concubine.balls >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.intelligence+$Concubine.intelligenceImplant < -95>>
+			<<if _S.Concubine.intelligence+_S.Concubine.intelligenceImplant < -95>>
 				<<set _woundChance += 1>>
 			<</if>>
 			<<set _woundChance *= random(2,4)>>
 			<<if random(1,100) <= _woundChance>>
 				Your Concubine was unfortunately caught in the crossfire and
-				<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Concubine.ID))>>
+				<<set _woundType = App.SecExp.inflictBattleWound(_S.Concubine)>>
 				<<if _woundType == "voice">>
 					a splinter pierced $his throat, severing $his vocal cords.
 				<<elseif _woundType == "eyes">>
@@ -617,49 +617,49 @@
 				<</if>>
 			<</if>>
 		<</if>>
-		<<setLocalPronouns $Bodyguard>>
+		<<setLocalPronouns _S.Bodyguard>>
 		<<set _woundChance = 0>>
-		<<if $Bodyguard.skill.combat == 1>>
+		<<if _S.Bodyguard.skill.combat == 1>>
 			<<set _woundChance -= 2>>
 		<</if>>
-		<<set _woundChance -= 0.25 * (getLimbCount($Bodyguard, 105))>>
-		<<if $Bodyguard.health.condition >= 50>>
+		<<set _woundChance -= 0.25 * (getLimbCount(_S.Bodyguard, 105))>>
+		<<if _S.Bodyguard.health.condition >= 50>>
 			<<set _woundChance -= 1>>
 		<</if>>
-		<<if $Bodyguard.weight > 130>>
+		<<if _S.Bodyguard.weight > 130>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.muscles < -30>>
+		<<if _S.Bodyguard.muscles < -30>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if getBestVision($Bodyguard) === 0>>
+		<<if getBestVision(_S.Bodyguard) === 0>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.heels == 1>>
+		<<if _S.Bodyguard.heels == 1>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.boobs >= 1400>>
+		<<if _S.Bodyguard.boobs >= 1400>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.butt >= 6>>
+		<<if _S.Bodyguard.butt >= 6>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.belly >= 10000>>
+		<<if _S.Bodyguard.belly >= 10000>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.dick >= 8>>
+		<<if _S.Bodyguard.dick >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.balls >= 8>>
+		<<if _S.Bodyguard.balls >= 8>>
 			<<set _woundChance += 1>>
 		<</if>>
-		<<if $Bodyguard.intelligence+$Bodyguard.intelligenceImplant < -95>>
+		<<if _S.Bodyguard.intelligence+_S.Bodyguard.intelligenceImplant < -95>>
 			<<set _woundChance += 1>>
 		<</if>>
 		<<set _woundChance *= random(2,4)>>
 		<<if random(1,100) <= _woundChance>>
 			During one of the assaults your Bodyguard was hit.
-			<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Bodyguard.ID))>>
+			<<set _woundType = App.SecExp.inflictBattleWound(_S.Bodyguard)>>
 			<<if _woundType == "voice">>
 				A splinter pierced $his throat, severing $his vocal cords.
 			<<elseif _woundType == "eyes">>
@@ -718,50 +718,50 @@
 		<<else>>
 			Fortunately you managed to avoid injury.
 		<</if>>
-		<<if $Concubine != 0>>
-			<<setLocalPronouns $Concubine>>
+		<<if _S.Concubine>>
+			<<setLocalPronouns _S.Concubine>>
 			<<set _woundChance = 0>>
-			<<if $Concubine.skill.combat == 1>>
+			<<if _S.Concubine.skill.combat == 1>>
 				<<set _woundChance -= 2>>
 			<</if>>
-			<<set _woundChance -= 0.25 * (getLimbCount($Concubine, 105))>>
-			<<if $Concubine.health.condition >= 50>>
+			<<set _woundChance -= 0.25 * (getLimbCount(_S.Concubine, 105))>>
+			<<if _S.Concubine.health.condition >= 50>>
 				<<set _woundChance -= 1>>
 			<</if>>
-			<<if $Concubine.weight > 130>>
+			<<if _S.Concubine.weight > 130>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.muscles < -30>>
+			<<if _S.Concubine.muscles < -30>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if getBestVision($Concubine) === 0>>
+			<<if getBestVision(_S.Concubine) === 0>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.heels == 1>>
+			<<if _S.Concubine.heels == 1>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.boobs >= 1400>>
+			<<if _S.Concubine.boobs >= 1400>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.butt >= 6>>
+			<<if _S.Concubine.butt >= 6>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.belly >= 10000>>
+			<<if _S.Concubine.belly >= 10000>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.dick >= 8>>
+			<<if _S.Concubine.dick >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.balls >= 8>>
+			<<if _S.Concubine.balls >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.intelligence+$Concubine.intelligenceImplant < -95>>
+			<<if _S.Concubine.intelligence+_S.Concubine.intelligenceImplant < -95>>
 				<<set _woundChance += 1>>
 			<</if>>
 			<<set _woundChance *= random(2,4)>>
 			<<if random(1,100) <= _woundChance>>
 				Your Concubine was unfortunately caught in the crossfire and
-				<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Concubine.ID))>>
+				<<set _woundType = App.SecExp.inflictBattleWound(_S.Concubine)>>
 				<<if _woundType == "voice">>
 					a splinter pierced $his throat, severing $his vocal cords.
 				<<elseif _woundType == "eyes">>
@@ -821,50 +821,50 @@
 		<<else>>
 			Fortunately you managed to avoid injury.
 		<</if>>
-		<<if $Concubine != 0>>
-			<<setLocalPronouns $Concubine>>
+		<<if _S.Concubine>>
+			<<setLocalPronouns _S.Concubine>>
 			<<set _woundChance = 0>>
-			<<if $Concubine.skill.combat == 1>>
+			<<if _S.Concubine.skill.combat == 1>>
 				<<set _woundChance -= 2>>
 			<</if>>
-			<<set _woundChance -= 0.25 * (getLimbCount($Concubine, 105))>>
-			<<if $Concubine.health.condition >= 50>>
+			<<set _woundChance -= 0.25 * (getLimbCount(_S.Concubine, 105))>>
+			<<if _S.Concubine.health.condition >= 50>>
 				<<set _woundChance -= 1>>
 			<</if>>
-			<<if $Concubine.weight > 130>>
+			<<if _S.Concubine.weight > 130>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.muscles < -30>>
+			<<if _S.Concubine.muscles < -30>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if getBestVision($Concubine) === 0>>
+			<<if getBestVision(_S.Concubine) === 0>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.heels == 1>>
+			<<if _S.Concubine.heels == 1>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.boobs >= 1400>>
+			<<if _S.Concubine.boobs >= 1400>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.butt >= 6>>
+			<<if _S.Concubine.butt >= 6>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.belly >= 10000>>
+			<<if _S.Concubine.belly >= 10000>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.dick >= 8>>
+			<<if _S.Concubine.dick >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.balls >= 8>>
+			<<if _S.Concubine.balls >= 8>>
 				<<set _woundChance += 1>>
 			<</if>>
-			<<if $Concubine.intelligence+$Concubine.intelligenceImplant < -95>>
+			<<if _S.Concubine.intelligence+_S.Concubine.intelligenceImplant < -95>>
 				<<set _woundChance += 1>>
 			<</if>>
 			<<set _woundChance *= random(2,4)>>
 			<<if random(1,100) <= _woundChance>>
 				Your Concubine was unfortunately caught in the crossfire and
-				<<set _woundType = App.SecExp.inflictBattleWound(getSlave($Concubine.ID))>>
+				<<set _woundType = App.SecExp.inflictBattleWound(_S.Concubine)>>
 				<<if _woundType == "voice">>
 					a splinter pierced $his throat, severing $his vocal cords.
 				<<elseif _woundType == "eyes">>
diff --git a/src/Mods/SecExp/secExpSmilingMan.tw b/src/Mods/SecExp/secExpSmilingMan.tw
index a153b6de275c59cd7de9db843a3ccbf5c0cea4a6..3ff49f92c06eb4a57b3df451c91b5473c0c75e1f 100644
--- a/src/Mods/SecExp/secExpSmilingMan.tw
+++ b/src/Mods/SecExp/secExpSmilingMan.tw
@@ -161,7 +161,6 @@
 	</p>
 <<elseif $smilingManProgress == 2>>
 	<<set $fcnn.push("...my money safe the old-fashioned way: I store it all underneath my mattress...")>>
-	<<set $smilingManWeek = $week>>
 	<p style="margin-bottom: 2em">
 		When $assistant.name violently wakes you up, _hisA worried expression can mean only one thing: the Smiling Man had been back. "We were anonymously sent a link to a new website: it's a very simple site, no visuals, no text; only a countdown ticking away. It will reach zero this evening." your assistant says.
 		This is troubling, yet somewhat exciting. The Smiling Man never failed to cause damage, but his ego had gotten the best of him this time — having time to prepare before their attack will give you a chance to find them. For the rest of the day you do your best to plan, prepare and focus.
diff --git a/src/Mods/SecExp/securityReport.tw b/src/Mods/SecExp/securityReport.tw
index 5b3783426b744e2a6a6a3ae2f096301f60d91484..3b81c9965501d4983a2b362f1ffe6fa47de16084 100644
--- a/src/Mods/SecExp/securityReport.tw
+++ b/src/Mods/SecExp/securityReport.tw
@@ -150,13 +150,13 @@
 	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>>
-<<if $lastAttackWeeks < 3 && $hasFoughtOnce == 1>>
+<<if $lastAttackWeeks < 3 && $battlesCount > 0>>
 	The recent attack has a negative effect on the security of the arcology.
 	<<set _secGrowth -= 1>>
-<<elseif $lastAttackWeeks < 5 && $hasFoughtOnce == 1>>
+<<elseif $lastAttackWeeks < 5 && $battlesCount > 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 $hasFoughtOnce == 1>>
+<<elseif $battlesCount > 0>>
 	The arcology has not been attacked in a while, which has a positive effect on security.
 	<<set _secGrowth += 0.5>>
 <</if>>
@@ -360,19 +360,19 @@ Due to the deterioration of the old world countries, organized crime focuses mor
 	<</if>>
 	<<if $SecExp.buildings.propHub.active > 0>>
 		<<if $SecExp.buildings.propHub.campaign >= 1 && $SecExp.buildings.propHub.focus == "recruitment">>
-			<<if $SecExp.buildings.propHub.recuriterOffice === 0 || $Recruiter == 0>>
+			<<if $SecExp.buildings.propHub.recuriterOffice === 0 || $RecruiterID == 0>>
 				<<if $propCampaignBoost == 1>>
 					<<set _recruitsMultiplier *= 1.1>>
 				<<else>>
 					<<set _recruitsMultiplier *= 1.05>>
 				<</if>>
-			<<elseif $SecExp.buildings.propHub.recuriterOffice > 0 && $Recruiter > 0>>
-				<<setLocalPronouns $Recruiter>>
-				<span class='slave-name'><<= SlaveFullName($Recruiter)>></span> is able to further boost your militia recruitment campaign from $his PR hub office.
+			<<elseif $SecExp.buildings.propHub.recuriterOffice > 0 && $RecruiterID > 0>>
+				<<setLocalPronouns _S.Recruiter>>
+				<span class='slave-name'><<= SlaveFullName(_S.Recruiter)>></span> is able to further boost your militia recruitment campaign from $his PR hub office.
 				<<if $propCampaignBoost == 1>>
-					<<set _recruitsMultiplier *= 1.2 + (($Recruiter.intelligence+$Recruiter.intelligenceImplant)/650)>>
+					<<set _recruitsMultiplier *= 1.2 + ((_S.Recruiter.intelligence+_S.Recruiter.intelligenceImplant)/650)>>
 				<<else>>
-					<<set _recruitsMultiplier *= 1.15 + (($Recruiter.intelligence+$Recruiter.intelligenceImplant)/650)>>
+					<<set _recruitsMultiplier *= 1.15 + ((_S.Recruiter.intelligence+_S.Recruiter.intelligenceImplant)/650)>>
 				<</if>>
 			<</if>>
 		<</if>>
diff --git a/src/Mods/SecExp/tradeReport.tw b/src/Mods/SecExp/tradeReport.tw
index d27deed58f3ad89f9816723319a9dd079ac1730a..c27ede3daa09ae66db50ebf184ff50794148325f 100644
--- a/src/Mods/SecExp/tradeReport.tw
+++ b/src/Mods/SecExp/tradeReport.tw
@@ -17,17 +17,17 @@
 <</if>>
 
 <<set _tradeChange = 0>>
-<<if $lastAttackWeeks < 2 && $hasFoughtOnce == 1>>
+<<if $lastAttackWeeks < 2 && $battlesCount > 0>>
 	The recent attack has a negative effect on the trade of the arcology.
 	<<set _tradeChange -= 1>>
-<<elseif $lastAttackWeeks < 4 && $hasFoughtOnce == 1>>
+<<elseif $lastAttackWeeks < 4 && $battlesCount > 0>>
 	While some time has passed, the last attack still has a negative effect on the commercial activity of the arcology.
 	<<set _tradeChange -= 0.5>>
 <</if>>
-<<if $lastRebellionWeeks < 2 && $hasRebelledOnce == 1>>
+<<if $lastRebellionWeeks < 2 && $rebellionsCount > 0>>
 	The recent rebellion has a negative effect on the trade of the arcology.
 	<<set _tradeChange -= 1>>
-<<elseif $lastRebellionWeeks < 4 && $hasRebelledOnce == 1>>
+<<elseif $lastRebellionWeeks < 4 && $rebellionsCount > 0>>
 	While some time has passed, the last rebellion still has a negative effect on the commercial activity of the arcology.
 	<<set _tradeChange -= 0.5>>
 <</if>>
diff --git a/src/Mods/SecExp/unitsRebellionReport.tw b/src/Mods/SecExp/unitsRebellionReport.tw
index 73534c482ef5add94aecacca3146bb4e3437b63c..e4586ae34de5519c12191c561df2cac703ce7879 100644
--- a/src/Mods/SecExp/unitsRebellionReport.tw
+++ b/src/Mods/SecExp/unitsRebellionReport.tw
@@ -12,13 +12,14 @@
 		<br>$SF.Lower, <<print num($SF.ArmySize)>> strong, was called to join the battle: no casualties suffered.
 	<</if>>
 	<<set _count = 0>>
-	<<if $loyalID.length > 0>>
+	<<set _loyalUnits = $militiaUnits.length + $slaveUnits.length + $mercUnits.length - $rebellingID.length>>
+	<<if _loyalUnits > 0>>
 		<br>
 		<<for _i = 0; _i < $militiaUnits.length; _i++>>
-			<<if $militiaUnits[_i].active == 1 && ($loyalID.includes($militiaUnits[_i].ID))>>
+			<<if $militiaUnits[_i].active == 1 && (!$rebellingID.includes($militiaUnits[_i].ID))>>
 				<<set $militiaUnits[_i].battlesFought++>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$militiaUnits[_i].platoonName,
 				<<else>>
 					$militiaUnits[_i].platoonName
@@ -26,10 +27,10 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $slaveUnits.length; _i++>>
-			<<if $slaveUnits[_i].active == 1 && ($loyalID.includes($slaveUnits[_i].ID))>>
+			<<if $slaveUnits[_i].active == 1 && (!$rebellingID.includes($slaveUnits[_i].ID))>>
 				<<set $slaveUnits[_i].battlesFought++>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$slaveUnits[_i].platoonName,
 				<<else>>
 					$slaveUnits[_i].platoonName
@@ -37,10 +38,10 @@
 			<</if>>
 		<</for>>
 		<<for _i = 0; _i < $mercUnits.length; _i++>>
-			<<if $mercUnits[_i].active == 1 && ($loyalID.includes($mercUnits[_i].ID))>>
+			<<if $mercUnits[_i].active == 1 && (!$rebellingID.includes($mercUnits[_i].ID))>>
 				<<set $mercUnits[_i].battlesFought++>>
 				<<set _count++>>
-				<<if _count < $loyalID.length>>
+				<<if _count < _loyalUnits>>
 					$mercUnits[_i].platoonName,
 				<<else>>
 					$mercUnits[_i].platoonName
@@ -176,7 +177,7 @@
 	<</if>>
 	<<if App.SecExp.conflict.deployedUnits('militia') >= 1>>
 		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && $loyalID.includes($militiaUnits[_j].ID)>>
+			<<if $militiaUnits[_j].active == 1 && !$rebellingID.includes($militiaUnits[_j].ID)>>
 				<br><br>
 				<<set $militiaUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
@@ -218,7 +219,7 @@
 	<<if App.SecExp.conflict.deployedUnits('slaves') >= 1>>
 		<<set _med = 0>>
 		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && $loyalID.includes($slaveUnits[_j].ID)>>
+			<<if $slaveUnits[_j].active == 1 && !$rebellingID.includes($slaveUnits[_j].ID)>>
 				<br><br>
 				<<set $slaveUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
@@ -263,7 +264,7 @@
 	<</if>>
 	<<if App.SecExp.conflict.deployedUnits('mercs') >= 1>>
 		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && $loyalID.includes($mercUnits[_j].ID)>>
+			<<if $mercUnits[_j].active == 1 && !$rebellingID.includes($mercUnits[_j].ID)>>
 				<br><br>
 				<<set $mercUnits[_j].battlesFought++>>
 				<<set _loss = _lossesList.pluck()>>
diff --git a/src/Mods/SecExp/widgets/miscSecExpWidgets.tw b/src/Mods/SecExp/widgets/miscSecExpWidgets.tw
index 77e99c5102c43d78eabfc7a00e534ff7145c178d..868c0d95573227608e32957f8c686ff7a994426d 100644
--- a/src/Mods/SecExp/widgets/miscSecExpWidgets.tw
+++ b/src/Mods/SecExp/widgets/miscSecExpWidgets.tw
@@ -127,9 +127,6 @@
 	<<if !Number.isInteger($militiaFreeManpower)>>
 		<<set $militiaFreeManpower = 0>>
 	<</if>>
-	<<if !Number.isInteger($battlesCount)>>
-		<<set $battlesCount = 0>>
-	<</if>>
 <</widget>>
 
 <<widget "replenishAllUnits">>
diff --git a/src/Mods/SpecialForce/Firebase.tw b/src/Mods/SpecialForce/Firebase.tw
index 0c6badff365ef2eba86ec341c323161047f8e5af..e7f1bab1f28d5cfae3f170ac13b42f67d306ea73 100644
--- a/src/Mods/SpecialForce/Firebase.tw
+++ b/src/Mods/SpecialForce/Firebase.tw
@@ -33,7 +33,7 @@
 			<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'Upgrades')" id="tab Upgrades">Upgrades</button>
 		<</if>>
 		<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'Actions')" id="tab Actions">Actions</button>
-		<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'FS')" id="tab FS">Future Socities</button>
+		<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'FS')" id="tab FS">Future Societies</button>
 	</div>
 
 	<div id="Upgrades" class="tabcontent">
diff --git a/src/Mods/SpecialForce/SpecialForce.js b/src/Mods/SpecialForce/SpecialForce.js
index dc53b2c38a1a24b3924d8d15b79f41dc01696d13..88ca2598303e22be61244f454caadede97a07118 100644
--- a/src/Mods/SpecialForce/SpecialForce.js
+++ b/src/Mods/SpecialForce/SpecialForce.js
@@ -215,359 +215,6 @@ App.SF.Init = function() {
 	// V.arcologies[0].SFRaid = 1; V.arcologies[0].SFRaidTarget = -1;
 };
 
-/* no-usedOnce */
-App.SF.BC = function() {
-	function InitClean() {
-		delete V.SFMODToggle;
-		delete V.securityForceActive;
-		delete V.securityForceCreate;
-		delete V.securityForceEventSeen;
-	}
-
-	function MainClean() {
-		delete V.securityForceActive;
-		delete V.securityForceRecruit;
-		delete V.securityForceTrade;
-		delete V.securityForceBooty;
-		delete V.securityForceIncome;
-		delete V.securityForceMissionEfficiency;
-		delete V.securityForceProfitable;
-		delete V.TierTwoUnlock;
-		delete V.securityForceDepravity;
-		delete V.SFAO;
-		delete V.securityForceUpgradeTokenReset;
-		delete V.securityForceUpgradeToken;
-		delete V.securityForceGiftToken;
-		delete V.securityForceRulesOfEngagement;
-		delete V.securityForceFocus;
-		delete V.securityForceAccountability;
-		delete V.securityForceName;
-		delete V.SubsidyActive;
-		delete V.securityForceSubsidyActive;
-	}
-
-	function ColonelClean() {
-		delete V.ColonelCore;
-		delete V.securityForceColonelToken;
-		delete V.securityForceColonelSexed;
-		delete V.ColonelRelationship;
-		delete V.securityForceSexedColonelToken;
-	}
-
-	function TradeShowClean() {
-		delete V.OverallTradeShowAttendance;
-		delete V.CurrentTradeShowAttendance;
-		delete V.TradeShowIncome;
-		delete V.TotalTradeShowIncome;
-		delete V.TradeShowHelots;
-		delete V.TotalTradeShowHelots;
-	}
-
-	function UnitsClean() {
-		delete V.securityForceInfantryPower;
-		delete V.securityForceArcologyUpgrades;
-		delete V.securityForceVehiclePower;
-		delete V.securityForceDronePower;
-		delete V.securityForceStimulantPower;
-		delete V.securityForceHeavyBattleTank;
-		delete V.securityForceAircraftPower;
-		delete V.securityForceSpacePlanePower;
-		delete V.securityForceAC130;
-		delete V.securityForceSatellitePower;
-		delete V.securityForceGiantRobot;
-		delete V.securityForceMissileSilo;
-		delete V.securityForceAircraftCarrier;
-		delete V.securityForceSubmarine;
-		delete V.securityForceHeavyAmphibiousTransport;
-		delete V.securityForcePersonnel;
-		delete V.securityForceFortressZeppelin;
-		delete V.securityForceHeavyTransport;
-	}
-
-	if (typeof V.SF !== "object") {
-		if (V.securityForceEventSeen < 1) {
-			V.securityForceActive = -1;
-		} else {
-			V.securityForceActive = 2;
-		}
-		V.SF = {Toggle: V.SFMODToggle, Active: V.securityForceActive};
-		InitClean();
-		if (V.securityForceName === undefined) {
-			V.securityForceName = "the special force";
-		}
-		if (V.SF.Active >= 1) {
-			Object.assign(V.SF, {
-				Depravity: V.securityForceDepravity,
-				Upgrade: V.securityForceUpgradeToken,
-				Gift: V.securityForceGiftToken,
-				UC: {Assign: V.SF.SpecOps, Lock: V.SF.SpecOpsLock},
-				ROE: V.securityForceRulesOfEngagement,
-				Target: V.securityForceFocus,
-				Regs: V.securityForceAccountability,
-				Lower: V.securityForceName,
-			});
-			MainClean();
-
-			if (V.ColonelCore === undefined) {
-				V.ColonelCore = "";
-			}
-			if (V.ColonelDiscussion === undefined) {
-				V.ColonelDiscussion = 0;
-			}
-			if (V.ColonelSexed === undefined) {
-				V.ColonelSexed = 0;
-			}
-			V.SF.Colonel = {
-				Core: V.ColonelCore,
-				Talk: V.securityForceColonelToken,
-				Fun: V.securityForceColonelSexed,
-				Status: V.ColonelRelationship
-			};
-			ColonelClean();
-
-			if (V.TradeShowIncome === undefined) {
-				V.TradeShowIncome = 0;
-			}
-			if (V.TotalTradeShowIncome === undefined) {
-				V.TotalTradeShowIncome = 0;
-			}
-			if (V.TradeShowHelots === undefined) {
-				V.TradeShowHelots = 0;
-			}
-			if (V.TotalTradeShowHelots === undefined) {
-				V.TotalTradeShowHelots = 0;
-			}
-			V.SF.MercCon = {
-				History: V.OverallTradeShowAttendance,
-				CanAttend: V.CurrentTradeShowAttendance,
-				Income: V.TradeShowIncome,
-				Revenue: V.TotalTradeShowIncome,
-				Menials: V.TradeShowHelots,
-				TotalMenials: V.TotalTradeShowHelots,
-				Mercs: 0,
-				TotalMercs: 0
-			};
-			TradeShowClean();
-			if (V.SF.MercCon.History > 0) {
-				V.SF.MercCon.CanAttend = 1;
-			}
-
-			if (V.securityForceHeavyBattleTank === undefined) {
-				V.securityForceHeavyBattleTank = 0;
-			}
-			if (V.securityForceSpacePlanePower === undefined) {
-				V.securityForceSpacePlanePower = 0;
-			}
-			if (V.securityForceAC130 === undefined) {
-				V.securityForceAC130 = 0;
-			}
-			if (V.securityForceSatellitePower === undefined) {
-				V.securityForceSatellitePower = 0;
-			}
-			if (V.securityForceGiantRobot === undefined) {
-				V.securityForceGiantRobot = 0;
-			}
-			if (V.securityForceMissileSilo === undefined) {
-				V.securityForceMissileSilo = 0;
-			}
-			if (V.securityForceAircraftCarrier === undefined) {
-				V.securityForceAircraftCarrier = 0;
-			}
-			if (V.securityForceSubmarine === undefined) {
-				V.securityForceSubmarine = 0;
-			}
-			if (V.securityForceHeavyAmphibiousTransport === undefined) {
-				V.securityForceHeavyAmphibiousTransport = 0;
-			}
-
-			V.SF.ArmySize = V.securityForcePersonnel;
-
-			V.SF.SatLaunched = 0;
-
-			V.SF.Squad = {
-				Armoury: V.securityForceInfantryPower,
-				Firebase: V.securityForceArcologyUpgrades,
-				AV: V.securityForceVehiclePower,
-				TV: V.securityForceVehiclePower,
-				Drones: V.securityForceDronePower,
-				Drugs: V.securityForceStimulantPower,
-				PGT: V.securityForceHeavyBattleTank,
-				AA: V.securityForceAircraftPower,
-				TA: V.securityForceAircraftPower,
-				SpacePlane: V.securityForceSpacePlanePower,
-				GunS: V.securityForceAC130,
-				Satellite: V.securityForceSatellitePower,
-				GiantRobot: V.securityForceGiantRobot,
-				MissileSilo: V.securityForceMissileSilo,
-				AircraftCarrier: V.securityForceAircraftCarrier,
-				Sub: V.securityForceSubmarine,
-				HAT: V.securityForceHeavyAmphibiousTransport
-			};
-			UnitsClean();
-		} else {
-			App.SF.Init();
-		}
-	} else if (typeof V.SF === "object") {
-		V.SF.FS = V.SF.FS || {};
-		V.SF.FS.Tension = V.SF.FS.Tension || -1;
-
-		if (V.SF.Toggle && V.SF.Active >= 1) {
-			V.SF.UC = V.SF.UC || {};
-			V.SF.Depravity = V.SF.Depravity || 0;
-			V.SF.Upgrade = V.SF.Upgrade || 0;
-			V.SF.Gift = V.SF.Gift || 0;
-			V.SF.UC.Assign = V.SF.UC.Assign || 0;
-			V.SF.UC.Lock = V.SF.UC.Lock || 0;
-			V.SF.ROE = V.SF.ROE || "hold";
-			V.SF.Target = V.SF.Target || "recruit";
-			V.SF.Regs = V.SF.Regs || "strict";
-			V.SF.Lower = V.SF.Lower || "the special force";
-
-			V.SF.ArmySize = V.SF.ArmySize || 40;
-
-			V.SF.Squad = V.SF.Squad || {};
-			for (let i = 0; i < App.SF.upgrades.list('all').length; i++) {
-				V.SF.Squad[App.SF.upgrades.list('all')[i]] = V.SF.Squad[App.SF.upgrades.list('all')[i]] || 0;
-			}
-
-			V.SF.SatLaunched = V.SF.SatLaunched || 0;
-
-			V.SF.Colonel = V.SF.Colonel || {};
-			V.SF.Colonel.Core = V.SF.Colonel.Core || "";
-			V.SF.Colonel.Talk = V.SF.Colonel.Talk || 0;
-			V.SF.Colonel.Fun = V.SF.Colonel.Fun || 0;
-			V.SF.Colonel.Status = V.SF.Colonel.Status || 0;
-
-			V.SF.MercCon = V.SF.MercCon || {};
-			V.SF.MercCon.History = V.SF.MercCon.History || 0;
-			V.SF.MercCon.CanAttend = V.SF.MercCon.CanAttend || 0;
-			if (V.SF.MercCon.History >= 1) {
-				V.SF.MercCon.CanAttend = -1;
-			}
-			V.SF.MercCon.Income = V.SF.MercCon.Income || 0;
-			V.SF.MercCon.Revenue = V.SF.MercCon.Revenue || 0;
-			V.SF.MercCon.Mercs = V.SF.MercCon.Mercs || 0;
-			V.SF.MercCon.Menials = V.SF.MercCon.Menials || 0;
-			V.SF.MercCon.TotalMenials = V.SF.MercCon.TotalMenials || 0;
-			V.SF.MercCon.TotalMercs = V.SF.MercCon.TotalMercs || 0;
-
-			if (typeof V.SF.Squad.Satellite === "object") {
-				if (V.SF.Squad.Satellite.InOrbit > 0) {
-					V.SF.SatLaunched = V.SF.Squad.Satellite.InOrbit; delete V.SF.Squad.Satellite.InOrbit;
-				}
-				V.SF.Squad.Satellite = V.SF.Squad.Satellite.lv;
-			}
-
-			delete V.SF.FS.upgrade; delete V.SF.UC.num;
-
-			if (jsDef(V.choice)) {
-				V.SF.Gift = V.choice;
-			}
-
-			if (V.Tour !== undefined) {
-				V.SF.tour = V.Tour || 0;
-			}
-
-			if (V.SF.Squad.Troops) {
-				V.SF.ArmySize = V.SF.Squad.Troops;
-				delete V.SF.Squad.Troops;
-			}
-
-			if (V.SF.MercCon !== undefined) {
-				if( V.SF.MercCon.View !== undefined) {
-					delete V.SF.MercCon.View;
-				}
-				if (V.SF.MercCon.Helots !== undefined) {
-					V.SF.MercCon.Menials = V.SF.MercCon.Helots;
-					delete V.SF.MercCon.Helots;
-				}
-				if (V.SF.MercCon.TotalHelots !== undefined) {
-					V.SF.MercCon.TotalMenials = V.SF.MercCon.TotalHelots;
-					delete V.SF.MercCon.TotalHelots;
-				}
-			}
-
-			if (V.SF.SpecOps !== undefined && V.SF.SpecOpsLock !== undefined) {
-				V.SF.UC = {Assign: V.SF.SpecOps, Lock: V.SF.SpecOpsLock};
-			}
-
-			if (V.SFUC !== undefined) {
-				V.SF.UC.num = V.SFUC || 0;
-			}
-
-			if (V.SpecOpsLock !== undefined) {
-				V.SF.SpecOpsLock = V.SpecOpsLock;
-			}
-
-			if (V.SF.U !== undefined) {
-				V.SF.Upgrade = V.SF.U || 0;
-			}
-
-			if (V.SF.WG !== undefined) {
-				V.SF.Gift = V.SF.WG || 0;
-			}
-
-			if (V.SF.Depravity < 0) {
-				V.SF.Depravity = 0;
-			}
-
-			if (V.SFUnit !== undefined) {
-				if (V.SFUnit.AT !== undefined) {
-					V.SFUnitTA = 0;
-				}
-				if (V.SFTradeShow !== undefined) {
-					V.SF.MercCon = V.SFTradeShow;
-				}
-				delete V.SFTradeShow;
-				if (V.SFColonel !== undefined) {
-					V.SF.Colonel = V.SFColonel;
-				}
-
-				if (V.SF.Squad !== undefined && V.SF.Squad.Satellite !== undefined && V.SatLaunched !== undefined) {
-					V.SF.Squad.Sat = {lv: V.SF.Squad.Satellite, InOrbit: V.SatLaunched};
-					V.SF.Squad.Satellite = V.SF.Squad.Sat;
-					delete V.SF.Squad.Sat;
-					delete V.SatLaunched;
-				}
-			}
-		} // closes: V.SF.Toggle && V.SF.Active >= 1
-	}
-
-	delete V.Tour; delete V.SFColonel; delete V.SFUnit;
-	delete V.SF.tour; delete V.SF.Caps; delete V.SF.Size; delete V.choice;
-	delete V.SF.Units; delete V.SpecOpsLock; delete V.SF.U; delete V.SF.WG;
-	delete V.SF.Subsidy; delete V.SF.SpecOps; delete V.SF.SpecOpsLock; delete V.SFUC;
-
-	if (V.SF.BadOutcome !== undefined) {
-		delete V.SF.BadOutcome;
-	}
-	if (V.arcologies[0].SFRaid !== undefined) {
-		delete V.arcologies[0].SFRaid;
-	}
-	if (V.arcologies[0].SFRaidTarget !== undefined) {
-		delete V.arcologies[0].SFRaidTarget;
-	}
-	if (V.SF.Facility !== undefined) {
-		delete V.SF.Facility;
-	}
-
-	if (V.SF.MWU !== undefined) {
-		delete V.SF.MWU;
-	}
-	if (V.SF.Bonus !== undefined) {
-		delete V.SF.Bonus;
-	}
-
-	InitClean();
-	MainClean();
-	ColonelClean();
-	TradeShowClean();
-	UnitsClean();
-
-	if (V.week < 72 && V.SF.Active !== -1) { V.SF.Active = -1; }
-};
-/* usedOnce */
-
 App.SF.fsIntegration = function(input = 'Menu', textDisplay = 100) {
 	const Revivalisms = ['Arabian_Revivalism', 'Aztec_Revivalism', 'Chinese_Revivalism', 'Edo_Revivalism', 'Egyptian_Revivalism', 'Multiculturalism', 'Roman_Revivalism'];
 	let FS_OPTIONS = ['Asset_Expansionism', 'Body_Purism', 'Chattel_Religionism', 'Degradationism', 'Eugenics', 'Gender_radicalism', 'Gender_traditionalism', 'Hedonistic_Decadence', 'Maturity_Preferentialism', 'Paternalism', 'Physical_Idealism', 'Repopulation', 'Slave_Pastoralism', 'Slimness_Enthusiasm', 'Transformation_Fetishism', 'Youth_Preferentialism'];
diff --git a/src/Mods/SpecialForce/SpecialForceBC.js b/src/Mods/SpecialForce/SpecialForceBC.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b848c1b539e874ff337b2cf52eee7749d799c8d
--- /dev/null
+++ b/src/Mods/SpecialForce/SpecialForceBC.js
@@ -0,0 +1,353 @@
+// @ts-nocheck
+/* no-usedOnce */
+App.SF.BC = function() {
+	function InitClean() {
+		delete V.SFMODToggle;
+		delete V.securityForceActive;
+		delete V.securityForceCreate;
+		delete V.securityForceEventSeen;
+	}
+
+	function MainClean() {
+		delete V.securityForceActive;
+		delete V.securityForceRecruit;
+		delete V.securityForceTrade;
+		delete V.securityForceBooty;
+		delete V.securityForceIncome;
+		delete V.securityForceMissionEfficiency;
+		delete V.securityForceProfitable;
+		delete V.TierTwoUnlock;
+		delete V.securityForceDepravity;
+		delete V.SFAO;
+		delete V.securityForceUpgradeTokenReset;
+		delete V.securityForceUpgradeToken;
+		delete V.securityForceGiftToken;
+		delete V.securityForceRulesOfEngagement;
+		delete V.securityForceFocus;
+		delete V.securityForceAccountability;
+		delete V.securityForceName;
+		delete V.SubsidyActive;
+		delete V.securityForceSubsidyActive;
+	}
+
+	function ColonelClean() {
+		delete V.ColonelCore;
+		delete V.securityForceColonelToken;
+		delete V.securityForceColonelSexed;
+		delete V.ColonelRelationship;
+		delete V.securityForceSexedColonelToken;
+	}
+
+	function TradeShowClean() {
+		delete V.OverallTradeShowAttendance;
+		delete V.CurrentTradeShowAttendance;
+		delete V.TradeShowIncome;
+		delete V.TotalTradeShowIncome;
+		delete V.TradeShowHelots;
+		delete V.TotalTradeShowHelots;
+	}
+
+	function UnitsClean() {
+		delete V.securityForceInfantryPower;
+		delete V.securityForceArcologyUpgrades;
+		delete V.securityForceVehiclePower;
+		delete V.securityForceDronePower;
+		delete V.securityForceStimulantPower;
+		delete V.securityForceHeavyBattleTank;
+		delete V.securityForceAircraftPower;
+		delete V.securityForceSpacePlanePower;
+		delete V.securityForceAC130;
+		delete V.securityForceSatellitePower;
+		delete V.securityForceGiantRobot;
+		delete V.securityForceMissileSilo;
+		delete V.securityForceAircraftCarrier;
+		delete V.securityForceSubmarine;
+		delete V.securityForceHeavyAmphibiousTransport;
+		delete V.securityForcePersonnel;
+		delete V.securityForceFortressZeppelin;
+		delete V.securityForceHeavyTransport;
+	}
+
+	if (typeof V.SF !== "object") {
+		if (V.securityForceEventSeen < 1) {
+			V.securityForceActive = -1;
+		} else {
+			V.securityForceActive = 2;
+		}
+		V.SF = {Toggle: V.SFMODToggle, Active: V.securityForceActive};
+		InitClean();
+		if (V.securityForceName === undefined) {
+			V.securityForceName = "the special force";
+		}
+		if (V.SF.Active >= 1) {
+			Object.assign(V.SF, {
+				Depravity: V.securityForceDepravity,
+				Upgrade: V.securityForceUpgradeToken,
+				Gift: V.securityForceGiftToken,
+				UC: {Assign: V.SF.SpecOps, Lock: V.SF.SpecOpsLock},
+				ROE: V.securityForceRulesOfEngagement,
+				Target: V.securityForceFocus,
+				Regs: V.securityForceAccountability,
+				Lower: V.securityForceName,
+			});
+			MainClean();
+
+			if (V.ColonelCore === undefined) {
+				V.ColonelCore = "";
+			}
+			if (V.ColonelDiscussion === undefined) {
+				V.ColonelDiscussion = 0;
+			}
+			if (V.ColonelSexed === undefined) {
+				V.ColonelSexed = 0;
+			}
+			V.SF.Colonel = {
+				Core: V.ColonelCore,
+				Talk: V.securityForceColonelToken,
+				Fun: V.securityForceColonelSexed,
+				Status: V.ColonelRelationship
+			};
+			ColonelClean();
+
+			if (V.TradeShowIncome === undefined) {
+				V.TradeShowIncome = 0;
+			}
+			if (V.TotalTradeShowIncome === undefined) {
+				V.TotalTradeShowIncome = 0;
+			}
+			if (V.TradeShowHelots === undefined) {
+				V.TradeShowHelots = 0;
+			}
+			if (V.TotalTradeShowHelots === undefined) {
+				V.TotalTradeShowHelots = 0;
+			}
+			V.SF.MercCon = {
+				History: V.OverallTradeShowAttendance,
+				CanAttend: V.CurrentTradeShowAttendance,
+				Income: V.TradeShowIncome,
+				Revenue: V.TotalTradeShowIncome,
+				Menials: V.TradeShowHelots,
+				TotalMenials: V.TotalTradeShowHelots,
+				Mercs: 0,
+				TotalMercs: 0
+			};
+			TradeShowClean();
+			if (V.SF.MercCon.History > 0) {
+				V.SF.MercCon.CanAttend = 1;
+			}
+
+			if (V.securityForceHeavyBattleTank === undefined) {
+				V.securityForceHeavyBattleTank = 0;
+			}
+			if (V.securityForceSpacePlanePower === undefined) {
+				V.securityForceSpacePlanePower = 0;
+			}
+			if (V.securityForceAC130 === undefined) {
+				V.securityForceAC130 = 0;
+			}
+			if (V.securityForceSatellitePower === undefined) {
+				V.securityForceSatellitePower = 0;
+			}
+			if (V.securityForceGiantRobot === undefined) {
+				V.securityForceGiantRobot = 0;
+			}
+			if (V.securityForceMissileSilo === undefined) {
+				V.securityForceMissileSilo = 0;
+			}
+			if (V.securityForceAircraftCarrier === undefined) {
+				V.securityForceAircraftCarrier = 0;
+			}
+			if (V.securityForceSubmarine === undefined) {
+				V.securityForceSubmarine = 0;
+			}
+			if (V.securityForceHeavyAmphibiousTransport === undefined) {
+				V.securityForceHeavyAmphibiousTransport = 0;
+			}
+
+			V.SF.ArmySize = V.securityForcePersonnel;
+
+			V.SF.SatLaunched = 0;
+
+			V.SF.Squad = {
+				Armoury: V.securityForceInfantryPower,
+				Firebase: V.securityForceArcologyUpgrades,
+				AV: V.securityForceVehiclePower,
+				TV: V.securityForceVehiclePower,
+				Drones: V.securityForceDronePower,
+				Drugs: V.securityForceStimulantPower,
+				PGT: V.securityForceHeavyBattleTank,
+				AA: V.securityForceAircraftPower,
+				TA: V.securityForceAircraftPower,
+				SpacePlane: V.securityForceSpacePlanePower,
+				GunS: V.securityForceAC130,
+				Satellite: V.securityForceSatellitePower,
+				GiantRobot: V.securityForceGiantRobot,
+				MissileSilo: V.securityForceMissileSilo,
+				AircraftCarrier: V.securityForceAircraftCarrier,
+				Sub: V.securityForceSubmarine,
+				HAT: V.securityForceHeavyAmphibiousTransport
+			};
+			UnitsClean();
+		} else {
+			App.SF.Init();
+		}
+	} else if (typeof V.SF === "object") {
+		V.SF.FS = V.SF.FS || {};
+		V.SF.FS.Tension = V.SF.FS.Tension || -1;
+
+		if (V.SF.Toggle && V.SF.Active >= 1) {
+			V.SF.UC = V.SF.UC || {};
+			V.SF.Depravity = V.SF.Depravity || 0;
+			V.SF.Upgrade = V.SF.Upgrade || 0;
+			V.SF.Gift = V.SF.Gift || 0;
+			V.SF.UC.Assign = V.SF.UC.Assign || 0;
+			V.SF.UC.Lock = V.SF.UC.Lock || 0;
+			V.SF.ROE = V.SF.ROE || "hold";
+			V.SF.Target = V.SF.Target || "recruit";
+			V.SF.Regs = V.SF.Regs || "strict";
+			V.SF.Lower = V.SF.Lower || "the special force";
+
+			V.SF.ArmySize = V.SF.ArmySize || 40;
+
+			V.SF.Squad = V.SF.Squad || {};
+			for (let i = 0; i < App.SF.upgrades.list('all').length; i++) {
+				V.SF.Squad[App.SF.upgrades.list('all')[i]] = V.SF.Squad[App.SF.upgrades.list('all')[i]] || 0;
+			}
+
+			V.SF.SatLaunched = V.SF.SatLaunched || 0;
+
+			V.SF.Colonel = V.SF.Colonel || {};
+			V.SF.Colonel.Core = V.SF.Colonel.Core || "";
+			V.SF.Colonel.Talk = V.SF.Colonel.Talk || 0;
+			V.SF.Colonel.Fun = V.SF.Colonel.Fun || 0;
+			V.SF.Colonel.Status = V.SF.Colonel.Status || 0;
+
+			V.SF.MercCon = V.SF.MercCon || {};
+			V.SF.MercCon.History = V.SF.MercCon.History || 0;
+			V.SF.MercCon.CanAttend = V.SF.MercCon.CanAttend || 0;
+			if (V.SF.MercCon.History >= 1) {
+				V.SF.MercCon.CanAttend = -1;
+			}
+			V.SF.MercCon.Income = V.SF.MercCon.Income || 0;
+			V.SF.MercCon.Revenue = V.SF.MercCon.Revenue || 0;
+			V.SF.MercCon.Mercs = V.SF.MercCon.Mercs || 0;
+			V.SF.MercCon.Menials = V.SF.MercCon.Menials || 0;
+			V.SF.MercCon.TotalMenials = V.SF.MercCon.TotalMenials || 0;
+			V.SF.MercCon.TotalMercs = V.SF.MercCon.TotalMercs || 0;
+
+			if (typeof V.SF.Squad.Satellite === "object") {
+				if (V.SF.Squad.Satellite.InOrbit > 0) {
+					V.SF.SatLaunched = V.SF.Squad.Satellite.InOrbit; delete V.SF.Squad.Satellite.InOrbit;
+				}
+				V.SF.Squad.Satellite = V.SF.Squad.Satellite.lv;
+			}
+
+			delete V.SF.FS.upgrade; delete V.SF.UC.num;
+
+			if (jsDef(V.choice)) {
+				V.SF.Gift = V.choice;
+			}
+
+			if (V.Tour !== undefined) {
+				V.SF.tour = V.Tour || 0;
+			}
+
+			if (V.SF.Squad.Troops) {
+				V.SF.ArmySize = V.SF.Squad.Troops;
+				delete V.SF.Squad.Troops;
+			}
+
+			if (V.SF.MercCon !== undefined) {
+				if( V.SF.MercCon.View !== undefined) {
+					delete V.SF.MercCon.View;
+				}
+				if (V.SF.MercCon.Helots !== undefined) {
+					V.SF.MercCon.Menials = V.SF.MercCon.Helots;
+					delete V.SF.MercCon.Helots;
+				}
+				if (V.SF.MercCon.TotalHelots !== undefined) {
+					V.SF.MercCon.TotalMenials = V.SF.MercCon.TotalHelots;
+					delete V.SF.MercCon.TotalHelots;
+				}
+			}
+
+			if (V.SF.SpecOps !== undefined && V.SF.SpecOpsLock !== undefined) {
+				V.SF.UC = {Assign: V.SF.SpecOps, Lock: V.SF.SpecOpsLock};
+			}
+
+			if (V.SFUC !== undefined) {
+				V.SF.UC.num = V.SFUC || 0;
+			}
+
+			if (V.SpecOpsLock !== undefined) {
+				V.SF.SpecOpsLock = V.SpecOpsLock;
+			}
+
+			if (V.SF.U !== undefined) {
+				V.SF.Upgrade = V.SF.U || 0;
+			}
+
+			if (V.SF.WG !== undefined) {
+				V.SF.Gift = V.SF.WG || 0;
+			}
+
+			if (V.SF.Depravity < 0) {
+				V.SF.Depravity = 0;
+			}
+
+			if (V.SFUnit !== undefined) {
+				if (V.SFUnit.AT !== undefined) {
+					V.SFUnitTA = 0;
+				}
+				if (V.SFTradeShow !== undefined) {
+					V.SF.MercCon = V.SFTradeShow;
+				}
+				delete V.SFTradeShow;
+				if (V.SFColonel !== undefined) {
+					V.SF.Colonel = V.SFColonel;
+				}
+
+				if (V.SF.Squad !== undefined && V.SF.Squad.Satellite !== undefined && V.SatLaunched !== undefined) {
+					V.SF.Squad.Sat = {lv: V.SF.Squad.Satellite, InOrbit: V.SatLaunched};
+					V.SF.Squad.Satellite = V.SF.Squad.Sat;
+					delete V.SF.Squad.Sat;
+					delete V.SatLaunched;
+				}
+			}
+		} // closes: V.SF.Toggle && V.SF.Active >= 1
+	}
+
+	delete V.Tour; delete V.SFColonel; delete V.SFUnit;
+	delete V.SF.tour; delete V.SF.Caps; delete V.SF.Size; delete V.choice;
+	delete V.SF.Units; delete V.SpecOpsLock; delete V.SF.U; delete V.SF.WG;
+	delete V.SF.Subsidy; delete V.SF.SpecOps; delete V.SF.SpecOpsLock; delete V.SFUC;
+
+	if (V.SF.BadOutcome !== undefined) {
+		delete V.SF.BadOutcome;
+	}
+	if (V.arcologies[0].SFRaid !== undefined) {
+		delete V.arcologies[0].SFRaid;
+	}
+	if (V.arcologies[0].SFRaidTarget !== undefined) {
+		delete V.arcologies[0].SFRaidTarget;
+	}
+	if (V.SF.Facility !== undefined) {
+		delete V.SF.Facility;
+	}
+
+	if (V.SF.MWU !== undefined) {
+		delete V.SF.MWU;
+	}
+	if (V.SF.Bonus !== undefined) {
+		delete V.SF.Bonus;
+	}
+
+	InitClean();
+	MainClean();
+	ColonelClean();
+	TradeShowClean();
+	UnitsClean();
+
+	if (V.week < 72 && V.SF.Active !== -1) { V.SF.Active = -1; }
+};
+/* usedOnce */
diff --git a/src/Mods/SpecialForce/WeeklyChoices.tw b/src/Mods/SpecialForce/WeeklyChoices.tw
index 8f5291b74c888b550e073325231c90fdb486edc4..c4d22063754f2c5e5f5d8eddf6e8ddfe24c7a929 100644
--- a/src/Mods/SpecialForce/WeeklyChoices.tw
+++ b/src/Mods/SpecialForce/WeeklyChoices.tw
@@ -61,21 +61,21 @@
 			<<set $SF.Colonel.Talk = 1, $SF.Colonel.Status += 2, $SF.FS.Tension -= _colonelTalkTensionRuction>>
 			<br><br>You ask The Colonel if she would like to stretch her legs up on the surface. It doesn't take much effort for her to agree.
 			<<if $PC.skill.warfare >= 100 && $PC.career == "mercenary">>
-				Your mastery of wet work and prior experience in a PMC satisfies The Colonel that between you<<if $Bodyguard != 0>>, $Bodyguard.slaveName,<</if>> and her, there should be little threat to walking around the arcology. Being able to see and interact with the arcology owner directly maintains the false idea that you're just like one of them while also giving them an increased opportunity to try gaining your favor.
+				Your mastery of wet work and prior experience in a PMC satisfies The Colonel that between you<<if _S.Bodyguard>>, _S.Bodyguard.slaveName,<</if>> and her, there should be little threat to walking around the arcology. Being able to see and interact with the arcology owner directly maintains the false idea that you're just like one of them while also giving them an increased opportunity to try gaining your favor.
 				<<run repX(10, "specialForces")>>
 				<<run cashX(_EnvCash2, "specialForces")>>
 			<<elseif $PC.skill.warfare >= 100>>
-				Your mastery of wet work satisfies The Colonel that you only need two soldiers <<if $Bodyguard != 0>> plus $Bodyguard.slaveName<</if>> to walk safely around the arcology. Being able to see and interact with the arcology owner directly maintains the false idea that you're just like one of them while also giving them an increased opportunity to try gaining your favor.
+				Your mastery of wet work satisfies The Colonel that you only need two soldiers <<if _S.Bodyguard>> plus _S.Bodyguard.slaveName<</if>> to walk safely around the arcology. Being able to see and interact with the arcology owner directly maintains the false idea that you're just like one of them while also giving them an increased opportunity to try gaining your favor.
 				<<run repX(5, "specialForces")>>
 				<<run cashX(_EnvCash3, "specialForces")>>
 			<<elseif $PC.skill.warfare >= 60>>
-				With some expertise in warfare, The Colonel believes <<if $Bodyguard != 0>>with $Bodyguard.slaveName <</if>>you only need a squad of armed soldiers for a walk through the arcology.
+				With some expertise in warfare, The Colonel believes <<if _S.Bodyguard>>with _S.Bodyguard.slaveName <</if>>you only need a squad of armed soldiers for a walk through the arcology.
 			<<elseif $PC.skill.warfare >= 30>>
-				As you have some skill in warfare, The Colonel believes<<if $Bodyguard != 0>> with $Bodyguard.slaveName<</if>> you only need two full squads of armed soldiers for a walk around the arcology.
+				As you have some skill in warfare, The Colonel believes<<if _S.Bodyguard>> with _S.Bodyguard.slaveName<</if>> you only need two full squads of armed soldiers for a walk around the arcology.
 			<<elseif $PC.skill.warfare >= 10>>
-				Your minor skill in warfare convinces The Colonel that <<if $Bodyguard != 0>>in addition to $Bodyguard.slaveName, <</if>>you need two full squads of armed soldiers and an armored car escort for a simple walk around the arcology.
+				Your minor skill in warfare convinces The Colonel that <<if _S.Bodyguard>>in addition to _S.Bodyguard.slaveName, <</if>>you need two full squads of armed soldiers and an armored car escort for a simple walk around the arcology.
 			<<else>>
-				Your complete lack of combat skill convinces The Colonel that <<if $Bodyguard != 0>>in addition to $Bodyguard.slaveName, <</if>>you need two full squads of armed soldiers, an armored car escort, and a sniper overwatch for a simple walk around the arcology.
+				Your complete lack of combat skill convinces The Colonel that <<if _S.Bodyguard>>in addition to _S.Bodyguard.slaveName, <</if>>you need two full squads of armed soldiers, an armored car escort, and a sniper overwatch for a simple walk around the arcology.
 			<</if>>
 
 			<br>As you make your way through the arcology you stop at a
diff --git a/src/arcologyBuilding/manufacturing.js b/src/arcologyBuilding/manufacturing.js
index 82b17cfa7871d3e633f7a21330118da2e455dbdb..e9dc8a2cd5b84a566de5ac2e307c7ed88c4ee5c6 100644
--- a/src/arcologyBuilding/manufacturing.js
+++ b/src/arcologyBuilding/manufacturing.js
@@ -202,9 +202,7 @@ App.Arcology.Cell.Manufacturing = class extends App.Arcology.Cell.BaseCell {
 						() => {
 							V.farmyard = 5;
 							thisCell.type = "Farmyard";
-						}, cost, "and will incur upkeep costs",
-						// this only exists for the farmyard, remove feature once that is out of alpha
-						App.UI.DOM.makeElement("span", "Alpha Content!", "warning")
+						}, cost, "and will incur upkeep costs"
 					));
 				}
 			}
diff --git a/src/arcologyBuilding/shops.js b/src/arcologyBuilding/shops.js
index 5f14f7eefd0b1411ea0d20f7d4e0c3a8a08f5cb1..d5ab18b5d190ce9d7328eb117fc1709b686668a4 100644
--- a/src/arcologyBuilding/shops.js
+++ b/src/arcologyBuilding/shops.js
@@ -311,6 +311,7 @@ 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"
@@ -321,23 +322,20 @@ 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 (this.type !== "Shops") {
-		const currentFSStyle = this.type.replace(/\s+/g, '');
-		// <</if>>
-
 		if (A.FSSubjugationist !== "unset") {
 			if (V.FSPromenade.Subjugationist === 0) {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Subjugationist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Subjugationist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Subjugationist";
 					}, cost
 				));
@@ -349,8 +347,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Supremacist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Supremacist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Supremacist";
 					}, cost
 				));
@@ -362,8 +360,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Gender Radicalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.GenderRadicalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Gender Radicalist";
 					}, cost
 				));
@@ -373,8 +371,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Gender Fundamentalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.GenderFundamentalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Gender Fundamentalist";
 					}, cost
 				));
@@ -386,8 +384,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Paternalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Paternalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Paternalist";
 					}, cost
 				));
@@ -397,8 +395,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Degradationist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Degradationist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Degradationist";
 					}, cost
 				));
@@ -410,8 +408,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Intellectual Dependency establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.IntellectualDependency = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Intellectual Dependency";
 					}, cost
 				));
@@ -421,8 +419,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Slave Professionalism establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.SlaveProfessionalism = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Slave Professionalism";
 					}, cost
 				));
@@ -434,8 +432,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Body Purist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.BodyPurist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Body Purist";
 					}, cost
 				));
@@ -445,8 +443,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Transformation Fetishist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.TransformationFetishist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Transformation Fetishist";
 					}, cost
 				));
@@ -458,8 +456,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Youth Preferentialist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.YouthPreferentialist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Youth Preferentialist";
 					}, cost
 				));
@@ -469,8 +467,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Maturity Preferentialist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.MaturityPreferentialist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Maturity Preferentialist";
 					}, cost
 				));
@@ -482,8 +480,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Petite Admiration establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.PetiteAdmiration = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Petite Admiration";
 					}, cost
 				));
@@ -493,8 +491,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Statuesque Glorification establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.StatuesqueGlorification = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Statuesque Glorification";
 					}, cost
 				));
@@ -506,8 +504,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Slimness Enthusiast establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.SlimnessEnthusiast = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Slimness Enthusiast";
 					}, cost
 				));
@@ -517,8 +515,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Asset Expansionist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.AssetExpansionist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Asset Expansionist";
 					}, cost
 				));
@@ -529,9 +527,9 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 			if (V.FSPromenade.Pastoralist === 0) {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Pastoralist establishments.",
-					() => {
-						V.FSPromenade.Pastoralist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
+					() => {;
+						this._clearFsStyle();
+						V.FSPromenade.Pastoralist = 1
 						this.type = "Pastoralist";
 					}, cost
 				));
@@ -543,8 +541,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Physical Idealist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.PhysicalIdealist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Physical Idealist";
 					}, cost
 				));
@@ -554,8 +552,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Hedonistic establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Hedonism = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Hedonism";
 					}, cost
 				));
@@ -567,8 +565,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Repopulationist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Repopulationist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Repopulationist";
 					}, cost
 				));
@@ -578,8 +576,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Eugenics establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.Eugenics = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Eugenics";
 					}, cost
 				));
@@ -591,8 +589,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Chattel Religionist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.ChattelReligionist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Chattel Religionist";
 					}, cost
 				));
@@ -604,8 +602,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Roman Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.RomanRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Roman Revivalist";
 					}, cost
 				));
@@ -615,8 +613,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Aztec Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.AztecRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Aztec Revivalist";
 					}, cost
 				));
@@ -626,8 +624,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Egyptian Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.EgyptianRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Egyptian Revivalist";
 					}, cost
 				));
@@ -637,8 +635,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Edo Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.EdoRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Edo Revivalist";
 					}, cost
 				));
@@ -648,8 +646,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Arabian Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.ArabianRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Arabian Revivalist";
 					}, cost
 				));
@@ -659,8 +657,8 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 				fragment.append(this._makeUpgrade(
 					"Upgrade this sector to appeal to Chinese Revivalist establishments.",
 					() => {
+						this._clearFsStyle();
 						V.FSPromenade.ChineseRevivalist = 1;
-						V.FSPromenade[currentFSStyle] = 0;
 						this.type = "Chinese Revivalist";
 					}, cost
 				));
@@ -671,7 +669,7 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 			fragment.append(this._makeUpgrade(
 				"Return this sector to standard outlets",
 				() => {
-					V.FSPromenade[currentFSStyle] = 0;
+					this._clearFsStyle();
 					this.type = "Shops";
 				}, cost
 			));
@@ -680,6 +678,13 @@ 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/cheats/PCCheatMenuCheatDatatypeCleanup.tw b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
index 5f78ca87af81bba2e840704827e8dc9ee8fc1027..005c0e3a00652c292e405f25d89be27537382be0 100644
--- a/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
+++ b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw
@@ -71,6 +71,7 @@
 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_id(-1)>>
 <<run PCDatatypeCleanup()>>
 <<set $upgradeMultiplierArcology = upgradeMultiplier('engineering')>>
 <<set $upgradeMultiplierMedicine = upgradeMultiplier('medicine')>>
diff --git a/src/cheats/mod_EditArcologyCheat.tw b/src/cheats/mod_EditArcologyCheat.tw
index 16eae1a020d3abc38649c51f1bd3b2166c4fa99d..b1eec155dd20cc655db78d447918412781498bec 100644
--- a/src/cheats/mod_EditArcologyCheat.tw
+++ b/src/cheats/mod_EditArcologyCheat.tw
@@ -202,6 +202,20 @@ Custom: <<textbox "$language" $language "MOD_Edit Arcology Cheat">>
 	<br>TSS Failed: ''$TSS.schoolAnnexed'' |
 	<<radiobutton "$TSS.schoolAnnexed" 0>> 0
 	| <<radiobutton "$TSS.schoolAnnexed" 1>> 1 (Failed)
+	
+	<br>TUO Upgrades: ''$TUO.schoolUpgrade'' |
+	<<radiobutton "$TUO.schoolUpgrade" 0>> 0
+	| <<radiobutton "$TUO.schoolUpgrade" 1>> 1
+	| <<radiobutton "$TUO.schoolUpgrade" 2>> 2
+
+	<br>TUO Moved to Arcology: ''$TUO.schoolPresent'' |
+	<<radiobutton "$TUO.schoolPresent" 0>> 0
+	| <<radiobutton "$TUO.schoolPresent" 1>> 1 (Moved)
+
+	<br>TUO Prosperity: <<textbox "$TUO.schoolProsperity" $TUO.schoolProsperity>>
+	<br>TUO Failed: ''$TUO.schoolAnnexed'' |
+	<<radiobutton "$TUO.schoolAnnexed" 0>> 0
+	| <<radiobutton "$TUO.schoolAnnexed" 1>> 1 (Failed)
 
 	<br><br>''The Growth Research Institute:''
 	<br>GRI Students Bought: <<textbox "$GRI.studentsBought" $GRI.studentsBought>>
diff --git a/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw b/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw
index 9ac0b8c1f7773a1ede8b558f13872d319296675a..283f230b8ca21b397bc7728936812e07caf97b93 100644
--- a/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw
+++ b/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw
@@ -327,3 +327,4 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the
 <<if def _escn>>
 	<<set $cribs[_escn] = clone($activeSlave)>>
 <</if>>
+<<run ibc.recalculate_coeff_id($activeSlave.ID)>>
diff --git a/src/cheats/mod_EditSlaveCheat.tw b/src/cheats/mod_EditSlaveCheat.tw
index 3ece6b32dae265ec7fbb3d01c04316340cab7bc0..0b39e745e74003940fcdae98070a18c0ff4e0a59 100644
--- a/src/cheats/mod_EditSlaveCheat.tw
+++ b/src/cheats/mod_EditSlaveCheat.tw
@@ -2,7 +2,6 @@
 
 <<set $nextButton = "Continue">>
 <<set $nextLink = "MOD_Edit Slave Cheat Datatype Cleanup">>
-<<set $oldName = $activeSlave.slaveName, $oldSurname = $activeSlave.slaveSurname>>
 <<set $tempSlave = clone($activeSlave)>>
 <<setLocalPronouns $tempSlave>>
 
diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
index 259d748bfbaa74c177b5e2c72ed4ca88aa5e50b6..015bbf3689f12cfe7aa5def560e7729c99d46c73 100644
--- a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
+++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw
@@ -71,3 +71,4 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the
 <<if def _escdc>>
 	<<set $slaves[_escdc] = $activeSlave>>
 <</if>>
+<<run ibc.recalculate_coeff_id($activeSlave.ID)>>
diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
index 3dd6e0e84971868e55ea3238e7bfc1ec30e46916..043d863a43e53d02bd68d7404ded8b01fa599212 100644
--- a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
+++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw
@@ -230,3 +230,4 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the
 <<if def _escn>>
 	<<set $slaves[_escn] = clone($activeSlave)>>
 <</if>>
+<<run ibc.recalculate_coeff_id($activeSlave.ID)>>
diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index 64ed155782408677cfc8c1b7f75ffe62b9be8d78..e595875d8c85665ffc837754889a2a6187195cef 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -302,8 +302,8 @@ App.Update.globalVariables = function(node) {
 	// Farmyard Subsection
 	const animalsBought = ["abbysinians", "beagles", "bengals", "birmans", "bulldogs", "bulls", "canines", "cougars", "dingos", "felines", "foxes", "frenchBulldogs", "germanShepherds", "goldenRetrievers", "hooved", "horses", "jackals", "jaguars", "labradorRetrievers", "leopards", "lions", "lynx", "maineCoons", "orientalShorthairs", "persians", "pigs", "poodles", "pumas", "ragdolls", "rottweilers", "russianBlues", "siameses", "siberianHuskies", "sphynxes", "tigers", "wolves", "yorkshireTerriers", "zebras"];
 	animalsBought.forEach(function(species) { V.animalsBought[species] = V.animalsBought[species] || 0; });
-	if (typeof V.farmyardUpgrade !== "object") {
-		V.farmyardUpgrade = {
+	if (typeof V.farmyardUpgrades !== "object") {
+		V.farmyardUpgrades = {
 			pump: 0, fertilizer: 0, hydroponics: 0, machinery: 0, seeds: 0
 		};
 	}
@@ -914,7 +914,7 @@ App.Update.globalVariables = function(node) {
 		}
 	}
 
-	policies.BC();
+	App.Update.policies();
 
 	// Player Arcology
 	{
@@ -923,7 +923,7 @@ App.Update.globalVariables = function(node) {
 		} else {
 			assistant.object();
 		}
-		FCTV.manage();
+		App.Update.FCTV();
 		if (jQuery.isEmptyObject(V.FSPromenade)) {
 			V.FSPromenade = {
 				Subjugationist: 0,
@@ -1258,6 +1258,7 @@ App.Update.slaveRecords = function(node) {
 					child.spermY = normalRandInt(50, 5);
 				}
 				App.Facilities.Nursery.InfantDatatypeCleanup(child);
+				child.inbreedingCoeff = ibc.coeff(child);
 			} else {
 				App.Update.Slave(child);
 				App.Entity.Utils.SlaveDataSchemeCleanup(child, true);
@@ -1280,6 +1281,17 @@ App.Update.slaveRecords = function(node) {
 };
 
 App.Update.genePoolRecords = function(node) {
+	Object.values(V.missingTable).forEach(s => {
+		if (!jsDef(s.mother))
+			s.mother = 0;
+		if (!jsDef(s.father))
+			s.father = 0;
+		if (!jsDef(s.inbreedingCoeff))
+			s.inbreedingCoeff = 0;
+	});
+
+	let ib_coeff = ibc.coeff_slaves(V.genePool);
+	V.genePool.forEach(g => {g.inbreedingCoeff = ib_coeff[g.ID]});
 	V.slaveIndices = slaves2indices(); // we're going to need to compare to active slaves, if they exist
 
 	for (let bci = 0; bci < V.genePool.length; bci++) {
diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js
index 60a17047a0e5579de773a24678db928f63fc925a..0d0904d701c4157c1a62fdb908e5ed0adc4e1f60 100644
--- a/src/data/backwardsCompatibility/datatypeCleanup.js
+++ b/src/data/backwardsCompatibility/datatypeCleanup.js
@@ -1489,6 +1489,8 @@ globalThis.ArcologyDatatypeCleanup = function() {
 
 	V.TSS.studentsBought = Math.max(+V.TSS.studentsBought, 0) || 0;
 	V.TSS.schoolProsperity = Math.clamp(+V.TSS.schoolProsperity, -10, 10) || 0;
+	V.TUO.studentsBought = Math.max(+V.TSS.studentsBought, 0) || 0;
+	V.TUO.schoolProsperity = Math.clamp(+V.TSS.schoolProsperity, -10, 10) || 0;
 	V.GRI.studentsBought = Math.max(+V.GRI.studentsBought, 0) || 0;
 	V.GRI.schoolProsperity = Math.clamp(+V.GRI.schoolProsperity, -10, 10) || 0;
 	V.SCP.studentsBought = Math.max(+V.SCP.studentsBought, 0) || 0;
@@ -1521,7 +1523,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 
 	/**
 	 * @param {function(App.Entity.SlaveState):boolean} predicate
-	 * @returns {number} ID of the first matched slave or 0 if no match was founds
+	 * @returns {number} ID of the first matched slave or 0 if no match was found
 	 */
 	function findSlaveId(predicate) {
 		const s = V.slaves.find(predicate);
@@ -1547,8 +1549,8 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		HeadGirlSuiteDatatypeCleanup();
 		NurseryDatatypeCleanup();
 
-		V.Recruiter = V.slaves.find(s => s.assignment === Job.RECRUITER) || 0;
-		V.Bodyguard = V.slaves.find(s => s.assignment === Job.BODYGUARD) || 0;
+		V.RecruiterID = findSlaveId(s => s.assignment === Job.RECRUITER);
+		V.BodyguardID = findSlaveId(s => s.assignment === Job.BODYGUARD);
 
 		replaceSlaveStateWithId("Lurcher");
 		replaceSlaveStateWithId("Stud");
@@ -1607,7 +1609,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		}
 		V.brothelBoost.eligible = Math.clamp(+V.brothelBoost.eligible, 0, 10) || 0;
 		/* madam */
-		V.Madam = V.slaves.find(s => s.assignment === Job.MADAM) || 0;
+		V.MadamID = findSlaveId(s => s.assignment === Job.MADAM);
 		V.MadamIgnoresFlaws = Math.clamp(+V.MadamIgnoresFlaws, 0, 1) || 0;
 	}
 
@@ -1637,7 +1639,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		V.bioreactorsXY = Math.max(+V.bioreactorsXY, 0) || 0;
 		V.bioreactorsBarren = Math.max(+V.bioreactorsBarren, 0) || 0;
 		/* milkmaid */
-		V.Milkmaid = V.slaves.find(s => s.assignment === Job.MILKMAID) || 0;
+		V.MilkmaidID = findSlaveId(s => s.assignment === Job.MILKMAID);
 		V.milkmaidImpregnates = Math.clamp(+V.milkmaidImpregnates, 0, 1) || 0;
 	}
 
@@ -1646,7 +1648,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		V.farmyardBreeding = Math.clamp(+V.farmyardBreeding, 0, 1) || 0;
 		V.farmyardShows = Math.clamp(+V.farmyardShows, 0, 1) || 0;
 		/* farmer */
-		V.Farmer = V.slaves.find(s => s.assignment === Job.FARMER) || 0;
+		V.FarmerID = findSlaveId(s => s.assignment === Job.FARMER);
 	}
 
 	function ClubDatatypeCleanup() {
@@ -1735,12 +1737,12 @@ globalThis.FacilityDatatypeCleanup = (function() {
 		V.masterSuitePregnancyFertilityDrugs = Math.clamp(+V.masterSuitePregnancyFertilityDrugs, 0, 1) || 0;
 		V.masterSuiteHyperPregnancy = Math.clamp(+V.masterSuiteHyperPregnancy, 0, 1) || 0;
 		/* concubine */
-		V.Concubine = V.slaves.find(s => s.assignment === Job.CONCUBINE) || 0;
+		V.ConcubineID = findSlaveId(s => s.assignment === Job.CONCUBINE);
 	}
 
 	function HeadGirlSuiteDatatypeCleanup() {
 		/* headgirl */
-		V.HeadGirl = V.slaves.find(s => s.assignment === Job.HEADGIRL) || 0;
+		V.HeadGirlID = findSlaveId(s => s.assignment === Job.HEADGIRL);
 		V.HGSuiteEquality = Math.clamp(+V.HGSuiteEquality, 0, 1) || 0;
 		if (V.HGSuiteSurgery !== 0) {
 			V.HGSuiteSurgery = 1;
@@ -1755,7 +1757,7 @@ globalThis.FacilityDatatypeCleanup = (function() {
 
 	function NurseryDatatypeCleanup() {
 		/* matron */
-		V.Matron = V.slaves.find(s => s.assignment === Job.MATRON) || 0;
+		V.MatronID = findSlaveId(s => s.assignment === Job.MATRON);
 	}
 })();
 
diff --git a/src/data/backwardsCompatibility/policiesBC.js b/src/data/backwardsCompatibility/policiesBC.js
new file mode 100644
index 0000000000000000000000000000000000000000..861bd621fd7562681ca91ba7aedaac0713191f97
--- /dev/null
+++ b/src/data/backwardsCompatibility/policiesBC.js
@@ -0,0 +1,85 @@
+// @ts-nocheck
+App.Update.policies = function() {
+	function convertMain(variable, pro, anti) {
+		if (V[pro]) {
+			V.policies[variable] = 1;
+		} else if (V[anti]) {
+			V.policies[variable] = -1;
+		}
+	}
+
+	function convertRetirement(variable, retireType, amountRequired) {
+		if (V[retireType] && V[amountRequired]) {
+			V.policies.retirement[variable] = V[amountRequired];
+		}
+	}
+
+	if (V.releaseID < 1069) {
+		V.policies.childProtectionAct = V.childProtectionAct;
+		V.policies.culturalOpenness = V.CulturalOpenness;
+		V.policies.sexualOpeness = V.sexualOpeness;
+		V.policies.proRefugees = V.ProRefugees;
+		V.policies.publicFuckdolls = V.publicFuckdolls;
+
+		V.policies.proRecruitment = V.ProRecruitment;
+		V.policies.cash4Babies = V.Cash4Babies;
+		V.policies.regularParties = V.RegularParties;
+		V.policies.publicPA = V.PAPublic;
+		V.policies.coursingAssociation = V.CoursingAssociation;
+
+		V.policies.raidingMercenaries = V.RaidingMercenaries;
+		V.policies.mixedMarriage = V.MixedMarriage;
+		V.policies.goodImageCampaign = V.goodImageCampaign;
+		V.policies.alwaysSubsidizeRep = V.alwaysSubsidizeRep;
+		V.policies.alwaysSubsidizeGrowth = V.alwaysSubsidizeGrowth;
+
+		convertMain('immmigrationCash', 'ProImmigrationCash', 'AntiImmigrationCash');
+		convertMain('immmigrationRep', 'ProImmigrationRep', 'AntiImmigrationRep');
+		convertMain('enslavementCash', 'ProEnslavementCash', 'AntiEnslavementCash');
+		convertMain('enslavementRep', 'ProEnslavementRep', 'AntiEnslavementRep');
+		convertMain('cashForRep', 'CashForRep', 'RepForCash');
+
+		convertMain('oralAppeal', 'OralEncouragement', 'OralDiscouragement');
+		convertMain('vaginalAppeal', 'VaginalEncouragement', 'VaginalDiscouragement');
+		convertMain('analAppeal', 'AnalEncouragement', 'AnalDiscouragement');
+
+		convertRetirement('sex', 'SexMilestoneRetirement', 'retirementSex');
+		convertRetirement('milk', 'MilkMilestoneRetirement', 'retirementMilk');
+		convertRetirement('cum', 'CumMilestoneRetirement', 'retirementCum');
+		convertRetirement('births', 'BirthsMilestoneRetirement', 'retirementBirths');
+		convertRetirement('kills', 'KillsMilestoneRetirement', 'retirementKills');
+
+		if (V.BioreactorRetirement) {
+			V.policies.retirement.fate = "bioreactor";
+		} else if (V.ArcadeRetirement) {
+			V.policies.retirement.fate = "arcade";
+		} else if (V.CitizenRetirement) {
+			V.policies.retirement.fate = "citizen";
+		}
+
+		V.policies.retirement.menial2Citizen = V.citizenRetirementMenials;
+		V.policies.retirement.customAgePolicy = V.policies.retirement.customAgePolicySet || V.CustomRetirementAgePolicy;
+		V.policies.retirement.physicalAgePolicy = V.PhysicalRetirementAgePolicy;
+
+		V.policies.SMR.basicSMR = V.BasicSMR;
+		V.policies.SMR.healthInspectionSMR = V.HealthInspectionSMR;
+		V.policies.SMR.educationSMR = V.EducationSMR;
+		V.policies.SMR.frigiditySMR = V.FrigiditySMR;
+
+		V.policies.SMR.weightSMR = V.BasicWeightSMR;
+		V.policies.SMR.honestySMR = V.HonestySMR;
+
+		V.policies.SMR.beauty.basicSMR = V.BasicBeautySMR;
+		V.policies.SMR.beauty.qualitySMR = V.QualityBeautySMR;
+
+		V.policies.SMR.height.basicSMR = V.BasicHeightSMR;
+		V.policies.SMR.height.advancedSMR = V.AdvancedHeightSMR;
+
+		V.policies.SMR.intelligence.basicSMR = V.BasicIntelligenceSMR;
+		V.policies.SMR.intelligence.qualitySMR = V.QualityIntelligenceSMR;
+
+		V.policies.SMR.eugenics.faceSMR = V.FaceEugenicsSMR;
+		V.policies.SMR.eugenics.heightSMR = V.HeightEugenicsSMR;
+		V.policies.SMR.eugenics.intelligenceSMR = V.IntelligenceEugenicsSMR;
+	}
+};
diff --git a/src/data/backwardsCompatibility/updateSlaveObject.js b/src/data/backwardsCompatibility/updateSlaveObject.js
index aecfcbe7fa6955dd6bcd935feeccb640287555e0..af4d2a0f912cce2c041bb8905211d3c6ec82020f 100644
--- a/src/data/backwardsCompatibility/updateSlaveObject.js
+++ b/src/data/backwardsCompatibility/updateSlaveObject.js
@@ -1026,4 +1026,14 @@ App.Update.Slave = function(slave, genepool = false) {
 			}
 		}
 	}
+
+	if (!jsDef(slave.inbreedingCoeff)) {
+		slave.inbreedingCoeff = ibc.coeff(slave);
+		slave.womb.forEach(f => {
+			// Use null as the ID, since fetuses are missing it
+			f.genetics.inbreedingCoeff = ibc.coeff(
+				{ID: null, mother: f.genetics.mother, father: f.genetics.father}
+			);
+		});
+	}
 };
diff --git a/src/data/newGamePlus.js b/src/data/newGamePlus.js
index f5a569ea612662e0a39f428dcde935087e67530a..bc771401033f017391fc9c69d2c3dc11f1b9476f 100644
--- a/src/data/newGamePlus.js
+++ b/src/data/newGamePlus.js
@@ -69,18 +69,77 @@ App.Data.NewGamePlus = (function() {
 
 		const ngUpdateMissingTable = function(missingTable) {
 			const newTable = {};
+			let needed = [];
 
 			(V.slaves || [])
 				.forEach(s => ([s.pregSource + NGPOffset, s.mother + NGPOffset, s.father + NGPOffset]
 					.filter(i => (i in missingTable))
 					.forEach(i => {
-						newTable[i - NGPOffset] = missingTable[i];
-						newTable[i - NGPOffset].ID -= NGPOffset;
+						if (needed.indexOf(i) === -1)
+							needed.push(i);
 					})));
+			(V.slaves || []).forEach(s => (s.womb
+				.forEach(f => ([f.fatherID, f.genetics.father, f.genetics.mother]
+					.filter(i => (i in missingTable))
+					.forEach(i => {
+						if (needed.indexOf(i) === -1)
+							needed.push(i);
+					})))));
+
+			while (needed.length > 0) {
+				let i = needed.shift();
+				let s = missingTable[i];
+				newTable[i - NGPOffset] = s;
+				s.ID -= NGPOffset;
+				if (s.mother in missingTable) {
+					s.mother -= NGPOffset;
+					if (!(s.mother in newTable) && needed.indexOf(s.mother + NGPOffset) === -1)
+						needed.push(s.mother + NGPOffset);
+				} 
+				if (s.father in missingTable) {
+					s.father -= NGPOffset;
+					if (!(s.father in newTable) && needed.indexOf(s.father + NGPOffset) === -1)
+						needed.push(s.father + NGPOffset);
+				}
+			}
 
 			return newTable;
 		};
 
+		if (typeof V.missingTable !== undefined) {
+			let oldMissingParentID = Math.min(-10000, ...Object.keys(V.missingTable)) - 1;
+			V.slaves.filter(s => (s.assignment !== Job.IMPORTED)).forEach(s => {
+				V.missingTable[oldMissingParentID] = {
+					slaveName: s.slaveName,
+					slaveSurname: s.slaveSurname,
+					fullName: SlaveFullName(s),
+					dick: s.dick,
+					vagina: s.vagina,
+					ID: oldMissingParentID,
+					mother: s.mother,
+					father: s.father,
+					inbreedingCoeff: s.inbreedingCoeff
+				};
+				Object.values(V.missingTable).forEach(so => {
+					if (so.mother === s.ID)
+						so.mother = oldMissingParentID;
+					if (so.father === s.ID)
+						so.father = oldMissingParentID;
+				});
+				V.slaves.concat([V.PC]).forEach(so => {
+					if (so.mother === s.ID)
+						so.mother = oldMissingParentID;
+					if (so.father === s.ID)
+						so.father = oldMissingParentID;
+					if (so.assignment === Job.IMPORTED || so.ID === -1) {
+						WombChangeID(so, s.ID, oldMissingParentID);
+						WombChangeGeneID(so, s.ID, oldMissingParentID);
+					}
+				});
+				oldMissingParentID--;
+			});
+		}
+
 		V.slaves.deleteWith((s) => s.assignment !== Job.IMPORTED);
 
 		for (let slave of V.slaves) {
@@ -117,11 +176,7 @@ App.Data.NewGamePlus = (function() {
 			slave.relationshipTarget = slaveOrZero(slave.relationshipTarget);
 		}
 		V.genePool = ngUpdateGenePool(V.genePool);
-		if (typeof V.missingTable === undefined || V.showMissingSlaves === false) {
-			V.missingTable = {};
-		} else {
-			V.missingTable = ngUpdateMissingTable(V.missingTable);
-		}
+		V.missingTable = ngUpdateMissingTable(V.missingTable);
 		let validRelationship = (s) => (s.relationshipTarget !== 0 && getSlave(s.relationshipTarget).relationshipTarget === s.ID);
 		for (let slave of V.slaves) {
 			if ((slave.relationship < 0 && V.freshPC === 1) || (slave.relationship > 0 && !validRelationship(slave))) {
diff --git a/src/descriptions/arcologyDescription.js b/src/descriptions/arcologyDescription.js
index a4b2880469355e543e0d9b8dcf6c8f70050b8557..12ef6da75cb1446e4732db60814a6c5fdaeb4773 100644
--- a/src/descriptions/arcologyDescription.js
+++ b/src/descriptions/arcologyDescription.js
@@ -134,8 +134,12 @@ App.Desc.playerArcology = function(lastElement) {
 		if (A.FSAssetExpansionistDecoration === 100) {
 			buffer.push(`The slaves' breasts are unrealistically huge, almost as large as the rest of their bodies.`);
 		}
-		if (A.FSRepopulationFocusDecoration === 100) {
-			buffer.push(`The slaves are heavily pregnant${V.PC.vagina !== -1 ? `, as is the central${V.PC.dick !== 0 ? " futanari" : " female"}` : ""} figure.`);
+		if (A.FSRepopulationFocusDecoration === 100) { /* Can this be made one line? */
+			if (V.PC.vagina !== -1) {
+				buffer.push(`The slaves are heavily pregnant, as is the central ${V.PC.dick !== 0 ? "futanari" : "female"} figure.`);
+			} else {
+				buffer.push(`The slaves are heavily pregnant.`);
+			}
 		}
 		if (A.FSRestartDecoration === 100) {
 			buffer.push(`The central`);
diff --git a/src/descriptions/familySummaries.js b/src/descriptions/familySummaries.js
index 5db6e4def0c6fbc5cd77dd2e2f9e8758cd4d9c45..f962d0a076090a3469a5eaaa5bc7c7ab0ccff19d 100644
--- a/src/descriptions/familySummaries.js
+++ b/src/descriptions/familySummaries.js
@@ -490,6 +490,20 @@ App.Desc.family = (function() {
 			r.push(`${He} has ${numberWithPlural(slave.sisters, "sister")} and ${numberWithPlural(slave.daughters, "daughter")}.`);
 		}
 
+		if (V.inbreeding && slave.inbreedingCoeff > 0) {
+			r.push(`${He} is`);
+			if (slave.inbreedingCoeff >= 0.5)
+				r.push("extremely");
+			else if (slave.inbreedingCoeff >= 0.25)
+				r.push("very");
+			else if (slave.inbreedingCoeff >= 0.125);
+			else if (slave.inbreedingCoeff >= 0.0625)
+				r.push("somewhat");
+			else
+				r.push("slightly");
+			r.push(`inbred, with a CoI of ${slave.inbreedingCoeff}.`);
+		}
+
 		return r.join(" ");
 	}
 
@@ -675,6 +689,20 @@ App.Desc.family = (function() {
 			r.push(`<br>You have ${numberWithPlural(V.PC.sisters, "sister")} and ${numberWithPlural(V.PC.daughters, "daughter")}.`);
 		}
 
+		if (V.inbreeding && V.PC.inbreedingCoeff > 0) {
+			r.push(`You are`);
+			if (V.PC.inbreedingCoeff >= 0.5)
+				r.push("extremely");
+			else if (V.PC.inbreedingCoeff >= 0.25)
+				r.push("very");
+			else if (V.PC.inbreedingCoeff >= 0.125); // No adjective in this case
+			else if (V.PC.inbreedingCoeff >= 0.0625)
+				r.push("somewhat");
+			else
+				r.push("slightly");
+			r.push(`inbred, with a CoI of ${V.PC.inbreedingCoeff}.`);
+		}
+
 		return r.join(" ");
 	}
 
diff --git a/src/endWeek/endWeek.js b/src/endWeek/endWeek.js
index e905a4a5fd2eb3dd55c60f8510de9c84504744c6..f9ea6808a0cc99304682871c05e38d6f3cacb1d2 100644
--- a/src/endWeek/endWeek.js
+++ b/src/endWeek/endWeek.js
@@ -286,18 +286,10 @@ globalThis.endWeek = (function() {
 		V.HGEnergy = 0;
 		V.HGCum = 0;
 		V.HGSlaveSuccess = 0;
-		V.HeadGirl = 0;
-		V.Recruiter = 0;
-		V.Madam = 0;
 		V.unMadam = 0;
 		V.madamCashBonus = 0;
 		V.whorePriceAdjustment = {};
 		V.unDJ = 0;
-		V.Milkmaid = 0;
-		V.Farmer = 0;
-		V.Concubine = 0;
-		V.Matron = 0;
-		V.Bodyguard = 0;
 		V.StudID = 0;
 		V.StudCum = 0;
 		V.fuckSlaves = 0;
diff --git a/src/endWeek/healthFunctions.js b/src/endWeek/healthFunctions.js
index 6b013a041e06b7b387094a7ae8ce2d69bae0d93e..f4361019e9018f716ff6bdca16cc58f19dee718f 100644
--- a/src/endWeek/healthFunctions.js
+++ b/src/endWeek/healthFunctions.js
@@ -340,7 +340,7 @@ globalThis.willWorkToDeath = function(slave) {
 		return true;
 	} else if (slave.fetish === "mindbroken") {
 		return true;
-	} else if (V.Madam !== 0 && (slave.assignment === Job.BROTHEL || (slave.assignment === Job.WHORE && V.brothel > 0 && V.universalRulesFacilityWork === 1 && V.brothelSpots > 0))) {
+	} else if (V.MadamID !== 0 && (slave.assignment === Job.BROTHEL || (slave.assignment === Job.WHORE && V.brothel > 0 && V.universalRulesFacilityWork === 1 && V.brothelSpots > 0))) {
 		return true;
 	} else if (S.DJ && (slave.assignment === Job.CLUB || (slave.assignment === Job.PUBLIC && V.club > 0 && V.universalRulesFacilityWork === 1 && V.clubSpots > 0))) {
 		return true;
diff --git a/src/endWeek/minorInjuryResponse.js b/src/endWeek/minorInjuryResponse.js
index 4b61210ecddc785fbac1043e8d2253a514efbc5b..4abe972651182c5550315914117d07cc869d816a 100644
--- a/src/endWeek/minorInjuryResponse.js
+++ b/src/endWeek/minorInjuryResponse.js
@@ -10,22 +10,22 @@ globalThis.minorInjuryResponse = function(slave) {
 	let trackedCategory = "";
 
 	switch (slave.assignment) {
-		case window.Job.WHORE:
+		case Job.WHORE:
 			trackedCategory = "slaveAssignmentWhore";
 			break;
-		case window.Job.MADAM:
+		case Job.MADAM:
 			trackedCategory = "slaveAssignmentMadam";
 			break;
-		case window.Job.BROTHEL:
+		case Job.BROTHEL:
 			trackedCategory = "slaveAssignmentBrothel";
 			break;
-		case window.Job.PUBLIC:
+		case Job.PUBLIC:
 			trackedCategory = "slaveAssignmentPublic";
 			break;
-		case window.Job.DJ:
+		case Job.DJ:
 			trackedCategory = "slaveAssignmentDj";
 			break;
-		case window.Job.CLUB:
+		case Job.CLUB:
 			trackedCategory = "slaveAssignmentClub";
 			break;
 		default:
diff --git a/src/endWeek/reports/childrenReport.js b/src/endWeek/reports/childrenReport.js
index 5dc0bf6c3f5ea815a138b2e52014f64e7ca4e9ee..9b7517a753796630b4ffffbcdeb9d8566d3308ac 100644
--- a/src/endWeek/reports/childrenReport.js
+++ b/src/endWeek/reports/childrenReport.js
@@ -6,7 +6,7 @@ App.Facilities.Nursery.childrenReport = function childrenReport() {
 	"use strict";
 
 	const
-		Matron = V.Matron,
+		Matron = S.Matron,
 		NL = App.Entity.facilities.nursery.employeesIDs().size,
 		CL = V.cribs.length;
 
diff --git a/src/endWeek/saBeYourHeadGirl.js b/src/endWeek/saBeYourHeadGirl.js
index 4c8c5b992b3445546339a1bb1bd1276e33e98de0..c8778af1a32b93520259ba549c387a42680e601f 100644
--- a/src/endWeek/saBeYourHeadGirl.js
+++ b/src/endWeek/saBeYourHeadGirl.js
@@ -67,20 +67,21 @@ App.SlaveAssignment.beYourHeadGirl = (function() {
 			slave.trust += 1;
 			App.SlaveAssignment.pleaseYou(slave);
 			if (slave.relationship > 4) {
-				if (slave.relationshipTarget === V.Concubine.ID) {
-					if (areRelated(V.Concubine, slave)) {
-						const concubinePronouns = getPronouns(V.Concubine);
-						if (V.Concubine.mother === slave.ID || V.Concubine.father === slave.ID) {
+				if (slave.relationshipTarget === V.ConcubineID) {
+					const concubine = S.Concubine;
+					if (areRelated(concubine, slave)) {
+						const concubinePronouns = getPronouns(concubine);
+						if (concubine.mother === slave.ID || concubine.father === slave.ID) {
 							r.push(`Your Concubine is your Consort's ${concubinePronouns.daughter}-${concubinePronouns.wife}. Truly, ${arcology.name} looks up to you <span class="green">as children to a father.</span>`);
 							repX(1000, "headGirl", slave);
-						} else if (slave.mother === V.Concubine.ID) {
+						} else if (slave.mother === V.ConcubineID) {
 							r.push(`Your Concubine is your Consort's mother-${concubinePronouns.wife}. Truly, ${arcology.name} looks up to you <span class="green">as children to a father.</span>`);
 							repX(1000, "headGirl", slave);
-						} else if (slave.father === V.Concubine.ID) {
+						} else if (slave.father === V.ConcubineID) {
 							r.push(`Your Concubine is your Consort's father-${concubinePronouns.wife}. Truly, ${arcology.name} looks up to you <span class="green">as children to a father.</span>`);
 							repX(1000, "headGirl", slave);
 						} else {
-							switch (areSisters(slave, V.Concubine)) {
+							switch (areSisters(slave, concubine)) {
 								case 1:
 									r.push(`Your Concubine is your Consort's twin-${concubinePronouns.wife}. Truly, ${arcology.name} looks up to you <span class="green">as children to a father.</span>`);
 									repX(1000, "headGirl", slave);
diff --git a/src/endWeek/saChoosesOwnClothes.js b/src/endWeek/saChoosesOwnClothes.js
index 8461bfc531bcaede0e9f50d46fbe08f2f87f722e..f59942c5d265bb168672a53b52526c62d52b1b84 100644
--- a/src/endWeek/saChoosesOwnClothes.js
+++ b/src/endWeek/saChoosesOwnClothes.js
@@ -1032,7 +1032,7 @@ App.SlaveAssignment.choosesOwnClothes = (function() {
 
 		if (slave.fetish === "mindbroken") {
 			if (hasBothLegs(slave) && slave.heels === 1) {
-				shoes.push({text: `${He} finds ${he} can inexplicably walk if ${he} wears heels; a daily lesson for ${him}, as ${he} forgets shortly after leaving.`, shoes: jsEither(["boots", "extreme heels", "heels", "pumps"])});
+				shoes.push({text: `${He} finds ${he} can inexplicably walk if ${he} wears heels; a daily lesson for ${him}, as ${he} forgets shortly after leaving.`, shoes: jsEither(["boots", "extreme heels", "heels"])});
 			}
 			if (V.arcologies[0].FSIntellectualDependencyLawBeauty === 1 || V.arcologies[0].FSStatuesqueGlorification > 0) {
 				shoes.push({text: `A fleeting thought of heels crosses ${his} mind, so ${he} grabs the nearest pair.`, shoes: jsEither(["extreme heels", "heels", "pumps"])});
diff --git a/src/endWeek/saClothes.js b/src/endWeek/saClothes.js
index 39e21d396acf790e065deaebe4f49dd805989812..3ba0331829ad501fd5dfb6ecdd4cbee127db4798 100644
--- a/src/endWeek/saClothes.js
+++ b/src/endWeek/saClothes.js
@@ -867,21 +867,6 @@ App.SlaveAssignment.clothes = (function() {
 				if (slave.shoes === "pumps") {
 					r.push(`While ${he} can just barely stand with ${his} heeled pumps, ${he} is incapable of taking a step in them.`);
 				}
-				if (slave.devotion >= -20 && slave.fetish === "submissive" && slave.fetishKnown === 1) {
-					r.push(`${He} <span class="hotpink">loves being forced</span> to crawl around like some kind of animal.`);
-					slave.devotion += 1;
-				} else if (slave.devotion > 50) {
-					r.push(`${He} accepts being forced to crawl around on`);
-					if (hasAllLimbs(slave)) {
-						r.push(`all fours.`);
-					} else {
-						r.push(`the ground.`);
-					}
-				} else {
-					r.push(`${He} <span class="mediumorchid">resents being forced</span> to crawl around like some kind of animal, but it keeps ${him} properly <span class="gold">afraid.</span>`);
-					slave.devotion -= 2;
-					slave.trust -= 4;
-				}
 			}
 		}
 	}
diff --git a/src/endWeek/saGetMilked.js b/src/endWeek/saGetMilked.js
index 818d51a7d9e0e3560e87bed6949803d105e3f8b4..0679e1ea546d1de289c14f736479a137963fab13 100644
--- a/src/endWeek/saGetMilked.js
+++ b/src/endWeek/saGetMilked.js
@@ -119,14 +119,14 @@ App.SlaveAssignment.getMilked = (function() {
 					r += ` Since there's extra space in ${V.dairyName}, ${he} spends most of ${his} milkings there.`;
 					V.dairySpots -= 1; // Would this need to be pulled for statistics gathering?
 				}
-				if (V.Milkmaid !== 0) {
-					r += ` While there, ${he} gets the benefit of ${V.Milkmaid.slaveName}'s `;
-					if (V.Milkmaid.physicalAge < 21) {
+				if (V.MilkmaidID !== 0) {
+					r += ` While there, ${he} gets the benefit of ${S.Milkmaid.slaveName}'s `;
+					if (S.Milkmaid.physicalAge < 21) {
 						r += `youthful energy`;
 					} else {
 						r += `care`;
 					}
-					if (V.Milkmaid.skill.oral >= 100) {
+					if (S.Milkmaid.skill.oral >= 100) {
 						r += ` and talented tongue`;
 					}
 					r += `.`;
@@ -589,10 +589,10 @@ App.SlaveAssignment.getMilked = (function() {
 		/* Dairy rework cum half here */
 		if (slave.assignment === Job.DAIRY) {
 			if (V.dairyStimulatorsUpgrade !== 1) {
-				if (V.Milkmaid !== 0) {
-					if (V.Milkmaid.dick > 4 && canAchieveErection(V.Milkmaid)) {
-						const milkmaidPronouns = getPronouns(V.Milkmaid);
-						r += ` ${V.Milkmaid.slaveName} sometimes stands in for the machines, which is a polite way of saying ${milkmaidPronouns.he} sometimes fucks ${slave.slaveName}'s ass to help ${him} cum.`;
+				if (V.MilkmaidID !== 0) {
+					if (S.Milkmaid.dick > 4 && canAchieveErection(S.Milkmaid)) {
+						const milkmaidPronouns = getPronouns(S.Milkmaid);
+						r += ` ${S.Milkmaid.slaveName} sometimes stands in for the machines, which is a polite way of saying ${milkmaidPronouns.he} sometimes fucks ${slave.slaveName}'s ass to help ${him} cum.`;
 					}
 				}
 			}
@@ -629,8 +629,8 @@ App.SlaveAssignment.getMilked = (function() {
 				} else if (slave.chem > 100) {
 					cum *= ((600 - slave.chem) / 600);
 				}
-			} else if (V.Milkmaid !== 0) {
-				if (V.Milkmaid.dick > 4 && canAchieveErection(V.Milkmaid)) {
+			} else if (V.MilkmaidID !== 0) {
+				if (S.Milkmaid.dick > 4 && canAchieveErection(S.Milkmaid)) {
 					cum *= 1.2;
 				}
 			}
diff --git a/src/endWeek/saGuardYou.js b/src/endWeek/saGuardYou.js
index c4e5f84ec8d0edda580657e5e361f6e3f5e114dc..41040672cef920539ae2fadd5a3ce2b78f159423 100644
--- a/src/endWeek/saGuardYou.js
+++ b/src/endWeek/saGuardYou.js
@@ -282,19 +282,19 @@ App.SlaveAssignment.guardYou = (function() {
 					}
 				}
 				if (!candidate) {
-					if (V.HeadGirl !== 0 && V.HeadGirl.skill.combat === 0 && bodyguardSuccessorEligible(V.HeadGirl)) {
-						r.push(`${He} does ${his} best to train ${V.HeadGirl.slaveName} whenever ${he} can, hoping that your Head Girl can be made capable of stepping into ${his} place.`);
-						candidate = V.HeadGirl;
+					if (V.HeadGirlID !== 0 && S.HeadGirl.skill.combat === 0 && bodyguardSuccessorEligible(S.HeadGirl)) {
+						r.push(`${He} does ${his} best to train ${S.HeadGirl.slaveName} whenever ${he} can, hoping that your Head Girl can be made capable of stepping into ${his} place.`);
+						candidate = S.HeadGirl;
 					}
 				}
 				if (!candidate) {
-					if (V.Concubine !== 0 && V.Concubine.skill.combat === 0 && V.Concubine.devotion > 50 && V.Concubine.muscles >= -30 && V.Concubine.weight < 150 && V.Concubine.butt < 10 && V.Concubine.boobs < 25000 && V.Concubine.belly < 5000 && V.Concubine.balls < 20 && V.Concubine.dick < 15 && V.Concubine.fuckdoll === 0 && V.Concubine.fetish !== "mindbroken" && canWalk(V.Concubine)) {
-						r.push(`${He} does ${his} best to train ${V.Concubine.slaveName} whenever ${he} can, hoping that your Concubine can be made capable of stepping into ${his} place.`);
-						if (V.Concubine.boobs >= 8000 || V.Concubine.butt >= 10 || V.Concubine.belly >= 5000 || V.Concubine.balls >= 10 || V.Concubine.dick >= 10 || V.Concubine.muscles < 0 || V.Concubine.weight >= 100) {
+					if (V.ConcubineID !== 0 && S.Concubine.skill.combat === 0 && S.Concubine.devotion > 50 && S.Concubine.muscles >= -30 && S.Concubine.weight < 150 && S.Concubine.butt < 10 && S.Concubine.boobs < 25000 && S.Concubine.belly < 5000 && S.Concubine.balls < 20 && S.Concubine.dick < 15 && S.Concubine.fuckdoll === 0 && S.Concubine.fetish !== "mindbroken" && canWalk(S.Concubine)) {
+						r.push(`${He} does ${his} best to train ${S.Concubine.slaveName} whenever ${he} can, hoping that your Concubine can be made capable of stepping into ${his} place.`);
+						if (S.Concubine.boobs >= 8000 || S.Concubine.butt >= 10 || S.Concubine.belly >= 5000 || S.Concubine.balls >= 10 || S.Concubine.dick >= 10 || S.Concubine.muscles < 0 || S.Concubine.weight >= 100) {
 							r.push(`${His} body is poorly suited for combat, but ${he} can learn to work around it with enough effort.`);
 							flawedTrainee = jsRandom(0, 50);
 						}
-						candidate = V.Concubine;
+						candidate = S.Concubine;
 					}
 				}
 				if (!candidate) {
diff --git a/src/endWeek/saHormonesEffects.js b/src/endWeek/saHormonesEffects.js
index ff172220dc46a88627fae5a9623ae6b88fd5e094..0e0543bac2f2ac8e2ff476fa273ee14e6918b7a6 100644
--- a/src/endWeek/saHormonesEffects.js
+++ b/src/endWeek/saHormonesEffects.js
@@ -184,7 +184,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 					}
 				}
 				if (faceValue < 50 && slave.face < 75) {
-					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become less unattractive.</span>`);
+					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become ${slave.face >= 0 ? "more attractive" : "less unattractive"}.</span>`);
 					faceInc = 1 + V.hormoneUpgradePower;
 					if (slave.geneMods.NCS === 1 && jsRandom(1, 100) > 50) {
 						faceInc *= 2;
@@ -302,7 +302,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 					}
 				}
 				if (faceValue < 30 && slave.face < 60) {
-					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become less unattractive.</span>`);
+					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become ${slave.face >= 0 ? "more attractive" : "less unattractive"}.</span>`);
 					faceInc = 1 + V.hormoneUpgradePower;
 					if (slave.geneMods.NCS === 1 && jsRandom(1, 100) > 50) {
 						faceInc *= 2;
@@ -418,7 +418,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 					}
 				}
 				if (faceValue < 10 && slave.face < 30) {
-					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become less unattractive.</span>`);
+					r.push(`Hormonal effects cause <span class="lime">${his} facial structure to soften and become ${slave.face >= 0 ? "more attractive" : "less unattractive"}.</span>`);
 					faceInc = 1 + V.hormoneUpgradePower;
 					if (slave.geneMods.NCS === 1 && jsRandom(1, 100) > 50) {
 						faceInc *= 2;
@@ -1026,7 +1026,7 @@ App.SlaveAssignment.hormonesEffects = (function() {
 				}
 			} else if (slave.hormoneBalance <= -100) {
 				if (slave.geneMods.NCS === 0) {
-					if (faceValue && faceValue < 100) {
+					if (faceValue > 60 && faceValue < 100) {
 						r.push(`Hormonal effects cause <span class="orange">${his} facial structure to harden and become less attractive.</span>`);
 						faceDec = 1 + V.hormoneUpgradePower;
 						slave.face = Math.clamp(slave.face - faceDec, -100, 100);
diff --git a/src/endWeek/saNanny.js b/src/endWeek/saNanny.js
index 9b95fc3d0e2728f2ecd9aa76985d210ed648308c..1027fd6abf9b221d17f4a42289a68b97fb69fff3 100644
--- a/src/endWeek/saNanny.js
+++ b/src/endWeek/saNanny.js
@@ -3,7 +3,7 @@
  * @returns {string}
  */
 
- // TODO: redo this
+// TODO: redo this
 App.SlaveAssignment.nanny = function(slave) {
 	const
 		{ he, him, his, He, His } = getPronouns(slave),
@@ -254,27 +254,17 @@ App.SlaveAssignment.nanny = function(slave) {
 	function matronEffects(slave) {
 		let t = '';
 
-		if (V.Matron) {
-			t += `While there, ${he} benefits from ${V.Matron.slaveName}'s `;
-			if (V.Matron.physicalAge < 21) {
+		if (V.MatronID) {
+			t += `While there, ${he} benefits from ${S.Matron.slaveName}'s `;
+			if (S.Matron.physicalAge < 21) {
 				t += `youthful energy`;
 			} else {
 				t += `care`;
 			}
-			if (V.Matron.skill.oral) { // TODO: keep this? replace with something else?
+			if (S.Matron.skill.oral) { // TODO: keep this? replace with something else?
 				t += ` and talented tongue`;
 			}
 			t += `. `;
-			/* TODO: farmer is often not set and makes no sense here. What should this be? LCD.
-				if (slave.devotion < V.FarmerDevotionThreshold) {
-					slave.devotion += V.FarmerDevotionBonus;
-				}
-				if (slave.devotion < V.FarmerTrustThreshold) {
-					slave.trust += V.FarmerTrustBonus;
-				}
-				if (slave.condition < 100) {
-					improveCondition(slave, V.FarmerHealthBonus);
-				}*/
 		}
 
 		return t;
diff --git a/src/endWeek/saPleaseYou.js b/src/endWeek/saPleaseYou.js
index 351b644217e763232430745b0211b3ce8cfe71e8..6c57bdddbd5ab3967749a2b08dfff646682c1a7f 100644
--- a/src/endWeek/saPleaseYou.js
+++ b/src/endWeek/saPleaseYou.js
@@ -1211,7 +1211,7 @@ App.SlaveAssignment.pleaseYou = (function() {
 			}
 			slave.trust += 1;
 		} else if (!canWalk(slave)) {
-			r.push(`You carry ${him} around with you all week, using ${him} as your personal sex toy and bedwarmer.`);
+			r.push(`You keep ${him} around you all week, using ${him} as your personal sex toy and bedwarmer.`);
 			if (isAmputee(slave)) {
 				r.push(`Without arms and legs,`);
 			} else if (!hasAnyLegs(slave)) {
@@ -1220,16 +1220,18 @@ App.SlaveAssignment.pleaseYou = (function() {
 				r.push(`With just one leg,`);
 			} else if (tooFatSlave(slave)) {
 				r.push(`Immobilized by ${his} own weight,`);
+			} else if (!canMove(slave)) {
+				r.push(`Immobilized by ${his} own swollen body,`);
 			} else if (tooBigBreasts(slave)) {
-				r.push(`Immobilized by ${his} own tits,`);
+				r.push(`Restricted by ${his} own tits,`);
 			} else if (tooBigBelly(slave)) {
-				r.push(`Immobilized by ${his} own swollen orb of a midsection,`);
+				r.push(`Restricted by ${his} own swollen orb of a midsection,`);
 			} else if (tooBigBalls(slave)) {
-				r.push(`Immobilized by ${his} own oversized testicles,`);
+				r.push(`Restricted by ${his} own oversized testicles,`);
 			} else if (tooBigDick(slave)) {
-				r.push(`Immobilized by ${his} own oversized cock,`);
+				r.push(`Restricted by ${his} own oversized cock,`);
 			} else if (tooBigButt(slave)) {
-				r.push(`Immobilized by ${his} own massive ass,`);
+				r.push(`Restricted by ${his} own massive ass,`);
 			} else {
 				r.push(`With ${his} clipped heels,`);
 			}
@@ -1710,9 +1712,9 @@ App.SlaveAssignment.pleaseYou = (function() {
 				multiplier += 0.04;
 			}
 		}
-		if (slave.ID === V.Concubine.ID) {
+		if (slave.ID === V.ConcubineID) {
 			multiplier += 0.05;
-		} else if ((slave.ID === V.HeadGirl.ID) && (V.arcologies[0].FSEgyptianRevivalistLaw === 1)) {
+		} else if ((slave.ID === V.HeadGirlID) && (V.arcologies[0].FSEgyptianRevivalistLaw === 1)) {
 			multiplier += 0.05;
 		}
 
diff --git a/src/endWeek/saRules_old.js b/src/endWeek/saRules_old.js
index 67e08787a5d6a30a82ec9920b53301ec5c4af568..98cc22ed1c79e18a697c6caf9871a7219b4ccd74 100644
--- a/src/endWeek/saRules_old.js
+++ b/src/endWeek/saRules_old.js
@@ -49,9 +49,9 @@
 						} else if (slave.energy <= 20) {
 							r += `is frigid and has little interest in getting off. `;
 							slave.need = 0;
-						} else if (V.Matron) {
-							r += `is routinely relieved of any built up tension by ${V.Matron.slaveName} and ${his}`;
-							if (canPenetrate(slave) && V.Matron.boobs >= 500) {
+						} else if (S.Matron) {
+							r += `is routinely relieved of any built up tension by ${S.Matron.slaveName} and ${his}`;
+							if (canPenetrate(slave) && S.Matron.boobs >= 500) {
 								r += `luscious breasts. `;
 								actX(slave, "mammary", 14);
 							} else {
@@ -310,20 +310,20 @@
 									if (App.Entity.facilities.nursery.employeesIDs().size > 2) {
 										r += `Under the rules, ${he}'s allowed to demand that other slaves get ${him} off, and ${he} <span class="hotpink">adores</span> you for providing plentiful outlets for ${his} nymphomania. `;
 									} else {
-										r += `Under the rules, ${he}'s allowed to demand that other slaves get ${him} off, and ${he} <span class="hotpink">eagerly awaits</span> the day ${V.Matron ? `more of your slaves join ${him} in ${V.nurseryName} ` : `you assign more nurses to ${V.nurseryName} to help ${him} relieve ${his} ${slave.balls ? `swollen balls` : `tension`}`}. `;	// TODO:
+										r += `Under the rules, ${he}'s allowed to demand that other slaves get ${him} off, and ${he} <span class="hotpink">eagerly awaits</span> the day ${S.Matron ? `more of your slaves join ${him} in ${V.nurseryName} ` : `you assign more nurses to ${V.nurseryName} to help ${him} relieve ${his} ${slave.balls ? `swollen balls` : `tension`}`}. `;	// TODO:
 									}
 								} else if (slave.fetishKnown && slave.fetishStrength > 60) {
 									if (slave.fetish === "sadist") {
 										if (App.Entity.facilities.nursery.employeesIDs().size > 2) {
 											r += `Under the rules, ${he}'s allowed to sexually abuse other slaves, and ${he} <span class="hotpink">adores</span> you for providing a whole nursery of slaves for ${him} to rape. `;
 										} else {
-											r += `Under the rules, ${he}'s allowed to sexually abuse other slaves, and ${he} <span class="hotpink">eagerly awaits</span> the day ${V.Matron ? `more of your slaves join ${him} in ${V.nurseryName}` : `you assign more nurses to ${V.nurseryName} to help ${him} unwind`}. `;
+											r += `Under the rules, ${he}'s allowed to sexually abuse other slaves, and ${he} <span class="hotpink">eagerly awaits</span> the day ${S.Matron ? `more of your slaves join ${him} in ${V.nurseryName}` : `you assign more nurses to ${V.nurseryName} to help ${him} unwind`}. `;
 										}
 									} else if (slave.fetish === "dom") {
 										if (App.Entity.facilities.nursery.employeesIDs().size > 2) {
 											r += `Under the rules, ${he}'s allowed to force other slaves to have sex with ${him}, and ${he} <span class="hotpink">adores</span> you for providing a whole nursery of slaves for ${him} to dominate. `;
 										} else {
-											r += `Under the rules, ${he}'s allowed to force other slaves to have sex with ${him}, and ${he} <span class="hotpink">eagerly awaits</span> the day ${V.Matron ? `more of your slaves join ${him} in ${V.nurseryName}` : `you assign more nurses to ${V.nurseryName} to help ${him} unwind`}. `;
+											r += `Under the rules, ${he}'s allowed to force other slaves to have sex with ${him}, and ${he} <span class="hotpink">eagerly awaits</span> the day ${S.Matron ? `more of your slaves join ${him} in ${V.nurseryName}` : `you assign more nurses to ${V.nurseryName} to help ${him} unwind`}. `;
 										}
 									}
 								}
@@ -334,7 +334,7 @@
 								if (App.Entity.facilities.nursery.employeesIDs().size > 2) {
 									r += `Since ${he}'s low in the slave hierarchy, <span class="mediumaquamarine">${he} knows</span> that the rule that slaves must get consent before having sex with ${him} are all that protect ${him} from abuse. `;
 								} else {
-									r += `Since ${he}'s low in the slave hierarchy, <span class="mediumaquamarine">${he} knows</span> that the rule that slaves must get consent before having sex with ${him} are all that protect ${him} from abuse. Well, that and the fact ${V.nurseryName} is ${V.Matron ? `mostly frequented by other slaveowners' stock` : `${his} little private sanctuary`}. `;
+									r += `Since ${he}'s low in the slave hierarchy, <span class="mediumaquamarine">${he} knows</span> that the rule that slaves must get consent before having sex with ${him} are all that protect ${him} from abuse. Well, that and the fact ${V.nurseryName} is ${S.Matron ? `mostly frequented by other slaveowners' stock` : `${his} little private sanctuary`}. `;
 								}
 								slave.trust++;
 							}
diff --git a/src/endWeek/saServeYourOtherSlaves.js b/src/endWeek/saServeYourOtherSlaves.js
index fea31cb0dafea7f40d70e490c8592772ed314ca8..46d73d5fa14b874532dbbffcb8e72a77c91be374 100644
--- a/src/endWeek/saServeYourOtherSlaves.js
+++ b/src/endWeek/saServeYourOtherSlaves.js
@@ -110,13 +110,13 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 		if (slave.fuckdoll > 0) {
 			r.push(`is positioned as a sperm dispenser for fertile slaves to ride or milk at their discretion.`);
 		} else if (slave.fetish === "mindbroken") {
-			if (slave.career === "a breeding bull" && canWalk(slave)) {
+			if (slave.career === "a breeding bull" && canMove(slave)) {
 				r.push(`is allowed to freely breed any fertile slaves ${he} can find.`);
 			} else {
 				r.push(`is left as a sperm dispenser for fertile slaves to ride or milk at their discretion.`);
 			}
 		} else if (slave.career === "a breeding bull") {
-			r.push(`sees it as ${his} duty to impregnate everyone in ${his} path. A role ${he} <span class="hotpink">appreciates</span> you <span class="mediumaquamarine">untrusting ${him} with.</span>`);
+			r.push(`sees it as ${his} duty to impregnate everyone in ${his} path. A role ${he} <span class="hotpink">appreciates</span> you <span class="mediumaquamarine">entrusting ${him} with.</span>`);
 			slave.devotion += 1;
 			slave.trust += 1;
 			if (slave.fetishKnown === 1) {
@@ -230,8 +230,10 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 				slave.devotion += 1;
 			}
 		}
-		if (hasAnyLegs(slave) && !canWalk(slave)) {
+		if (!canWalk(slave) && canMove(slave)) {
 			r.push(`Since ${he}'s forced to crawl around, ${he}'s especially vulnerable.`);
+		} else if (!canMove(slave)) {
+			r.push(`Since ${he}'s completely immobile, ${he}'s especially vulnerable.`);
 		}
 		if (V.subSlaves <= 0) {
 			V.subSlaves = 1;
@@ -331,8 +333,8 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 		}
 
 		r.push(`is serving <strong>${domName}</strong> this week.`);
-		if (hasAnyLegs(domSlave) && !canWalk(slave)) {
-			r.push(`Since ${subName} cannot stand, ${domName} has no trouble taking whatever ${he2} wants from ${him}.`);
+		if (hasAnyLegs(domSlave) && !canMove(slave)) {
+			r.push(`Since ${subName} cannot move, ${domName} has no trouble taking whatever ${he2} wants from ${him}.`);
 		} else if (isAmputee(domSlave)) {
 			r.push(`Since ${he2} has no limbs, ${domName} has to put forth extra effort taking whatever ${he2} wants from ${subName}.`);
 		}
@@ -862,7 +864,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 					r.push(`${domName}, it turns out, <span class="lightcoral">really likes cum,</span> and ${subName} has balls, so`);
 					domSlave.fetishKnown = 1;
 				}
-				r.push(`Since ${domName} loves cum, and ${subName} has balls, ${domName} has ${his2} own private semen dispenser. ${domName} sometimes gets tired of having to work hard for cum, so ${he2} spends the week`);
+				r.push(`${domName} has ${his2} own private semen dispenser. ${domName} sometimes gets tired of having to work hard for cum, so ${he2} spends the week`);
 				if (hasAnyArms(domSlave)) {
 					r.push(`stimulating poor ${subName}'s`);
 				} else {
@@ -1180,7 +1182,7 @@ App.SlaveAssignment.serveYourOtherSlaves = (function() {
 					domSlave.fetishKnown = 1;
 				}
 				r.push(`the first indication ${subName} gets of what ${he}'s in for this week is when ${domName}`);
-				if (canWalk(domSlave)) {
+				if (canStand(domSlave)) {
 					if (domName.mpreg > 0 || domName.ovaries > 0) {
 						r.push(`pushes a full-term belly into ${subName}'s back and`);
 					}
diff --git a/src/endWeek/saWhore.js b/src/endWeek/saWhore.js
index a41161a612a3dd3b0b31280a91161744b4707e0d..c1023af381a80d51b3ccb479e8ac55903e7cbd53 100644
--- a/src/endWeek/saWhore.js
+++ b/src/endWeek/saWhore.js
@@ -174,15 +174,16 @@ App.SlaveAssignment.whore = (function() {
 						r += ` Almost everyone who comes to ${V.brothelName} is looking to pound a ${girl} who cums when buttfucked.`;
 					}
 				}
-				if (V.Madam !== 0) {
-					const madamPronouns = getPronouns(V.Madam);
+				if (V.MadamID !== 0) {
+					const madam = S.Madam;
+					const madamPronouns = getPronouns(madam);
 					if (V.madamCashBonus > 0) {
 						if (slave.assignment === Job.WHORE) {
 							r += ` Working`;
 						} else {
 							r += ` Living and working`;
 						}
-						r += ` out of the brothel, ${he} comes under ${SlaveFullName(V.Madam)}'s `;
+						r += ` out of the brothel, ${he} comes under ${SlaveFullName(madam)}'s `;
 						if (V.madamCashBonus < 0.1) {
 							r += `completely inept`;
 						} else if (V.madamCashBonus < 0.2) {
@@ -193,7 +194,7 @@ App.SlaveAssignment.whore = (function() {
 							r += `masterful`;
 						}
 						r += ` leadership.`;
-						if (V.Madam.dick > 2 && canAchieveErection(V.Madam)) {
+						if (madam.dick > 2 && canAchieveErection(madam)) {
 							if (slave.devotion <= 20) {
 								r += ` The Madam rapes ${him} when ${he} fails to meet standards.`;
 							} else {
@@ -201,14 +202,14 @@ App.SlaveAssignment.whore = (function() {
 							}
 						}
 						if (!canHear(slave)) {
-							r += ` Unfortunately, ${his} inability to hear wastes most of ${V.Madam.slaveName}'s advice.`;
+							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) {
-								r += ` ${SlaveFullName(V.Madam)} manages to <span class="flaw break">break</span> ${slave.slaveName} of ${his} sexual flaws.`;
+								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) {
-								r += ` ${SlaveFullName(V.Madam)} manages to <span class="flaw break">break</span> ${slave.slaveName} of ${his} flaws.`;
+								r += ` ${SlaveFullName(madam)} manages to <span class="flaw break">break</span> ${slave.slaveName} of ${his} flaws.`;
 								slave.behavioralFlaw = "none";
 							}
 						}
@@ -348,8 +349,8 @@ App.SlaveAssignment.whore = (function() {
 				r += ` ${He} spends reduced hours working ${V.brothelName} in order to <span class="green">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.Madam !== 0) {
-					r += `<span class="trust dec">severe punishment</span> by ${V.Madam.slaveName}. ${His} <span class="devotion dec">outbursts</span> are kept in check by your watchful Madam, preventing the customers from ever becoming aware of them while keeping ${him} productive.`;
+				if (V.MadamID !== 0) {
+					r += `<span class="trust dec">severe punishment</span> by ${S.Madam.slaveName}. ${His} <span class="devotion dec">outbursts</span> are kept in check by your watchful Madam, preventing the customers from ever becoming aware of them while keeping ${him} productive.`;
 				} else {
 					r += `<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>`;
 					repX(forceNeg(50), "disobedience", slave);
@@ -365,8 +366,8 @@ App.SlaveAssignment.whore = (function() {
 				r += ` ${He} spends reduced hours plying ${his} trade in order to <span class="green">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.Madam !== 0) {
-					r += `<span class="trust dec">severe punishment</span> by ${V.Madam.slaveName}. ${His} <span class="devotion dec">outbursts</span> are kept in check by your watchful Madam, preventing the customers from ever becoming aware of them and keeping ${him} productive.`;
+				if (V.brothel > 0 && V.universalRulesFacilityWork === 1 && V.brothelSpots > 0 && V.MadamID !== 0) {
+					r += `<span class="trust dec">severe punishment</span> by ${S.Madam.slaveName}. ${His} <span class="devotion dec">outbursts</span> are kept in check by your watchful Madam, preventing the customers from ever becoming aware of them and keeping ${him} productive.`;
 				} else {
 					r += `<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">causing a scene and damaging the image of your whores.</span>`;
 					repX(forceNeg(100), "disobedience", slave);
diff --git a/src/endWeek/saWorkTheFarm.js b/src/endWeek/saWorkTheFarm.js
index 274cfd76de013a243bc21773a4a585672bae33dd..72ddd62738ec3a41e3fc776689e3fef2bb90727f 100644
--- a/src/endWeek/saWorkTheFarm.js
+++ b/src/endWeek/saWorkTheFarm.js
@@ -1,566 +1,350 @@
+// FIXME: needs further review
+
 /**
  * @param {App.Entity.SlaveState} slave
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 App.SlaveAssignment.workTheFarm = function(slave) {
-	const arcology = V.arcologies[0];
-	const {
-			he, him, his, He, His
-		} = getPronouns(slave),
-		incomeStats = getSlaveStatisticData(slave, V.facility.farmyard);
-
-	let food = Math.trunc(App.Facilities.Farmyard.foodAmount(slave));
-	let t = `works as a farmhand this week. `;
-
-
-	const foodTotal = (slave, food) => {
-		t += foodFarmer();
-		t += foodDevotion(slave);
-		t += foodMuscles(slave);
-		t += foodWeight(slave);
-		t += foodHealth(slave);
-		t += foodSight(slave);
-		t += foodHearing(slave);
-
-		t += V.foodMarket ? `As a result, ${he} produces <span class="chocolate">${massFormat(food)}</span> of food over the week. ` : ``;
-	};
-
-	const foodFarmer = () => {
-		if (V.Farmer) {
-			return `${V.Farmer.slaveName} watches over ${him}, making sure that ${he} doesn't slack off and works as hard as ${he} should. `;
-		}
-		return ``;
-	};
+	let frag = document.createDocumentFragment();
+
+	const
+		{ he, him, his, He, His } = getPronouns(slave),
+		incomeStats = getSlaveStatisticData(slave, V.facility.farmyard),
+
+		slaveApproves = () => sexualQuirks.includes(slave.sexualQuirk) || behavioralQuirks.includes(slave.behavioralQuirk) || fetishes.includes(slave.fetish),
+
+		sexualQuirks = ["perverted", "unflinching"],
+		behavioralQuirks = ["sinful"],
+		fetishes = ["humiliation", "masochist"],
+
+		foodAmount = Math.trunc(App.Facilities.Farmyard.foodAmount(slave));
+
+	function fullReport(slave) {
+		const report = App.UI.DOM.appendNewElement("div", frag);
+
+		let r = [];
+
+		r.push(intro());
+		r.push(farmer(slave));
+		r.push(devotion(slave));
+		r.push(muscles(slave));
+		r.push(weight(slave));
+		r.push(health(slave));
+		r.push(sight(slave));
+		r.push(hearing(slave));
+		r.push(food());
+
+		r.push(slaveShows(slave));
+		r.push(longTermEffects(slave));
+		r.push(slaveVignettes());
+
+		$(report).append(r.join(' '));
+	}
 
-	const foodDevotion = slave => {
+	const intro = () => `${He} works as a farmhand this week.`;
+
+	// function farmer(slave) {
+	// TODO: update this with devotion and health effects
+	// 	const F = getPronouns(S.Farmer);
+
+	// 	if (S.Farmer) {
+	// 		return `${S.Farmer.slaveName} watches over ${him}, making sure that ${he} doesn't slack off and works as hard as ${he} should. ${F.He}'s a tough boss, but a fair one. ${slave.slaveName} benefits from ${F.his} care while working in ${V.farmyardName}.`;
+	// 	}
+	// }
+
+	function devotion(slave) {
 		if (slave.devotion > 50) {
-			return `${He}'s so devoted to you that ${he} works harder and produces more food. `;
+			return `${He}'s so devoted to you that ${he} works harder and produces more food.`;
 		} else if (slave.devotion < -50) {
-			return `${He}'s so resistant that ${he} doesn't work as hard, and thus produces less food. `;
+			return `${He}'s so resistant that ${he} doesn't work as hard, and thus produces less food.`;
 		} else {
-			return ``; // TODO: third case for neutral?
+			return `${He} doesn't feel particularly compelled to work hard or slack off and produces an average amount of food.`;
 		}
-	};
+	}
+
+	function health(slave) {
+		let r = [];
 
-	const foodHealth = slave => {
-		return foodHealthCondition(slave) + foodHealthIllness(slave);
-	};
+		r.push(healthCondition(slave));
+		r.push(healthIllness(slave));
 
-	const foodHealthCondition = slave => {
+		return r.join(' ');
+	}
+
+	function healthCondition(slave) {
 		if (slave.health.condition > 50) {
-			return `${His} shining health helps ${him} work harder and longer. `;
+			return `${His} shining health helps ${him} work harder and longer.`;
 		} else if (slave.health.condition < -50) {
-			return `${His} poor health impedes ${his} ability to work efficiently. `;
-		} else {
-			return ``; // TODO: third case?
+			return `${His} poor health impedes ${his} ability to work efficiently.`;
 		}
-	};
+	}
+
+	function healthIllness(slave) {
+		let
+			r = [],
+			health = ``,
+			exhaustion = ``;
 
-	const foodHealthIllness = slave => {
-		let t = ``;
 		if (slave.health.illness > 0 || slave.health.tired > 60) {
-			t += ` ${He} performed worse this week due to<span class="red">`;
 			if (slave.health.illness === 1) {
-				t += ` feeling under the weather`;
+				health = `feeling under the weather`;
 			} else if (slave.health.illness === 2) {
-				t += ` a minor illness`;
+				health = `a minor illness`;
 			} else if (slave.health.illness === 3) {
-				t += ` being sick`;
+				health = `being sick`;
 			} else if (slave.health.illness === 4) {
-				t += ` being very sick`;
+				health = `being very sick`;
 			} else if (slave.health.illness === 5) {
-				t += ` a terrible illness`;
-			}
-			if (slave.health.illness > 0 && slave.health.tired > 60) {
-				t += ` and`;
-				foodHealthTired(slave);
+				health = `a terrible illness`;
 			}
 
-			t += `.</span> `;
-			t += foodTired(slave);
-		}
-		return t;
-	};
-
-	const foodHealthTired = slave => {
-		if (slave.health.tired > 90) {
-			return ` exhaustion`;
-		} else if (slave.health.tired > 60) {
-			return ` being tired`;
-		}
-		return ``;
-	};
-
-	const foodTired = slave => {
-		let t = ``;
-		if (!slave.fuckdoll) {
-			if (slaveResting(slave)) {
-				t = ` ${He} spends reduced hours working the soil in order to <span class="green">offset ${his} lack of rest.</span>`;
-			} else if (slave.health.tired + 20 >= 90 && !willWorkToDeath(slave)) {
-				t = ` ${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="red">greatly reducing yields.</span>`;
-				slave.devotion -= 10;
-				slave.trust -= 5;
-			} else {
-				t = ` Hours of manual labor quickly add up, leaving ${him} <span class="red">physically drained</span> by the end of the day.`;
+			if (slave.health.tired > 90) {
+				exhaustion = `exhaustion`;
+			} else if (slave.health.tired > 60) {
+				exhaustion = `being tired`;
 			}
+
+			r.push(`${He} performed worse this week due to <span class="health dec">${health}${slave.health.illness > 0 && slave.health.tired > 60 ? ` and ` : ``}${exhaustion}.</span>`);
+
+			r.push(tired(slave));
 		}
-		tired(slave);
 
-		return t;
-	};
+		return r;
+	}
 
-	const foodMuscles = slave => {
+	function tired(slave) {
+		if (slaveResting(slave)) {
+			return `${He} spends reduced hours working the soil in order to <span class="health dec">offset ${his} lack of rest.</span>`;
+		} else if (slave.health.tired + 20 >= 90 && !willWorkToDeath(slave)) {
+			return `${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="health dec">greatly reducing yields.</span>`;
+		} else {
+			return `Hours of manual labor quickly add up, leaving ${him} <span class="health dec">physically drained</span> by the end of the day.`;
+		}
+	}
+
+	function muscles(slave) {
 		if (slave.muscles > 50) {
-			return `${His} muscular form helps ${him} work better, increasing ${his} productivity. `;
+			return `${His} muscular form helps ${him} work better, increasing ${his} productivity.`;
 		} else if (slave.muscles < -50) {
-			return `${He} is so weak that ${he} is not able to work effectively. `;
-		} else {
-			return ``; // TODO: third case?
+			return `${He} is so weak that ${he} is not able to work effectively.`;
 		}
-	};
+	}
 
-	const foodWeight = slave => {
-		return slave.weight > 95 ? `${He} is so overweight that ${he} has to stop every few minutes to catch ${his} breath, and so ${his} productivity suffers. ` : ``;
-	};
+	function weight(slave) {
+		if (slave.weight > 95) {
+			return `${He} is so overweight that ${he} has to stop every few minutes to catch ${his} breath, and so ${his} productivity suffers. `;
+		}
+	}
 
-	const foodSight = slave => {
+	function sight(slave) {
 		if (!canSee(slave)) {
-			return `${His} blindness makes it extremely difficult for ${him} to work, severely limiting ${his} production. `;
+			return `${His} blindness makes it extremely difficult for ${him} to work, severely limiting ${his} production.`;
 		} else if (!canSeePerfectly(slave)) {
-			return `${His} nearsightedness makes it harder for ${him} to work as hard as ${he} otherwise would. `;
-		} else {
-			return ``; // TODO: third case?
+			return `${His} nearsightedness makes it harder for ${him} to work as hard as ${he} otherwise would.`;
 		}
-	};
+	}
 
-	const foodHearing = slave => {
+	function hearing(slave) {
 		if (slave.hears === -1) {
-			return `${He} is hard-of-hearing, which gets in the way of ${his} work whenever ${he} misses directions${V.Farmer ? ` from ${V.Farmer.slaveName}` : ``}. `;
+			return `${He} is hard-of-hearing, which gets in the way of ${his} work whenever ${he} misses directions${S.Farmer ? ` from ${S.Farmer.slaveName}` : ``}.`;
 		} else if (slave.hears < -1) {
-			return `${He} is deaf, which gets in the way of ${his} work whenever ${he} misses directions${V.Farmer ? ` from ${V.Farmer.slaveName}` : ``}. `;
-		} else {
-			return ``; // TODO: third case?
+			return `${He} is deaf, which gets in the way of ${his} work whenever ${he} misses directions${S.Farmer ? ` from ${S.Farmer.slaveName}` : ``}.`;
 		}
-	};
+	}
 
+	function food() {
+		const fsGain = 0.0001 * foodAmount;
 
-	if (V.Farmer) {
-		// TODO: keep oral check? replace with something else?
-		t += `While there, ${he} benefits from ${V.Farmer.slaveName}'s ${V.Farmer.physicalAge < 21 ? `youthful energy` : `care`} ${V.Farmer.skill.oral ? ` and talented tongue` : ``}.`;
-		if (slave.devotion < V.FarmerDevotionThreshold) {
-			slave.devotion += V.FarmerDevotionBonus;
-		}
-		if (slave.devotion < V.FarmerTrustThreshold) {
-			slave.trust += V.FarmerTrustBonus;
-		}
-		if (slave.health.condition < 100) {
-			improveCondition(slave, V.FarmerHealthBonus);
+		FutureSocieties.DecorationBonus(V.farmyardDecoration, fsGain);
+
+		if (V.foodMarket) {
+			return `As a result, ${he} produces <span class="chocolate">${massFormat(foodAmount)}</span> of food over the week.`;
 		}
 	}
 
 	// Open Food Production
 
 	if (V.foodMarket) {
-		if (food < 1) {
-			food = 1;
-		}
-		V.food += food;
-		V.foodTotal += food;
-		incomeStats.food += food;
-
-		foodTotal(slave, food);
+		V.food += foodAmount;
+		V.foodTotal += foodAmount;
+		incomeStats.food += foodAmount;
 	}
 
 	// Close Food Production
 
 	// Open Shows
 
-	incomeStats.income += App.Facilities.Farmyard.farmShowsIncome(slave);
+	function slaveShows(slave) {
+		if (V.farmyardShows) {
+			return App.Facilities.Farmyard.putOnShows(slave);
+		}
+	}
 
-	if (V.farmyardShows) {
-		t += `${He} also puts on shows with animals this week. `;
+	// Close Shows
 
-		// Open FS Subsection
+	// Open Long-Term Effects
 
-		if (arcology.FSSupremacist !== "unset") {
-			if (slave.race === arcology.FSSupremacistRace) {
-				t += `Society <span class="red">disapproves</span> of your allowing a member of the elite race to be degraded in such a fashion, and thus ${he} didn't earn as much. `;
-			}
-		}
-		if (arcology.FSSubjugationist !== "unset") {
-			if (slave.race === arcology.FSSubjugationistRace) {
-				t += `Society <span class="green">approves</span> of the degradation you submit your ${arcology.FSSubjugationistRace} slaves to, and so ${he} earns you a bit more. `;
-			} else {
-				t += `Society doesn't disapprove of ${him} not being ${arcology.FSSubjugationistRace}, but ${he} doesn't earn as much as ${he} would if ${he} was. `;
-			}
-		}
-		if (arcology.FSRepopulationFocus !== "unset") {
-			if (slave.pregWeek > 16) {
-				if (slave.eggType !== "human") {
-					t += `Society is <span class="red">disgusted</span> by ${his} pregnancy when they realize that what it is in ${his} womb is not human. `;
-				} else {
-					t += `Society <span class="green">greatly approves</span> of your having pregnant slaves ${V.seeBestiality ? `have sex` : `put on shows`} with animals. `;
-				}
-			}
-		}
-		if (arcology.FSRestart !== "unset") {
-			if (slave.pregWeek > 16) {
-				if (slave.eggType !== "human") {
-					t += `Society is disgusted by ${his} pregnancy until they learn that what is in ${his} womb is not human. `;
+	function longTermEffects(slave) {
+		let r = [];
+
+		if (slave.fetishKnown && slaveApproves || slave.energy > 95) {
+			if (V.farmyardShows) {
+				if (V.seeBestiality) {
+					r.push(`Getting fucked by animals is the perfect job for ${him}, as far as ${he} can tell. <span class="devotion inc">${He} is happy</span> to spend ${his} days being utterly degraded.`);
+					slave.devotion += 1;
 				} else {
-					t += `Society is <span class="red">extremely disgusted</span> by ${his} pregnancy and the fact that you would have ${him} ${V.seeBestiality ? `have sex` : `put on shows`} with animals while sporting a baby bump. `;
+					r.push(`${He} loves putting on shows with animals, and as far as ${he} can tell, it's the perfect job for ${him}. It isn't as degrading as ${he} would like, but <span class="devotion inc">${he} is happy nonetheless.</span>`);
+					slave.devotion += 2;
 				}
 			}
+
+			slave.need = 0;
 		}
-		if (arcology.FSGenderRadicalist !== "unset") {
-			if (slave.dick > 0) {
-				t += `${His} patrons <span class="green">approve</span> of the fact that ${he} has a dick. `;
-			} else {
-				t += `${His} patrons <span class="red">are disappointed</span> that ${he} doesn't have a dick. `;
-			}
-		}
-		if (arcology.FSGenderFundamentalist !== "unset") {
-			if (slave.pregWeek > 16) {
-				t += `${His} viewers <span class="green">approve</span> of the fact that ${he} is sporting a baby bump. `;
-			} else {
-				t += `${His} viewers <span class="red">are disappointed</span> that ${he} isn't pregnant. `;
-			}
-			if (slave.dick > 0) {
-				// TODO: something here?
-			}
-		}
-		if (arcology.FSPaternalist !== "unset") {
-			if (V.seeBestiality && V.farmyardBreeding) {
-				t += `Your citizens are <span class="red">very disapproving</span> of the fact that you would allow something as degrading as having your slaves be knocked up by filthy beasts. `;
-			} else {
-				t += `Your citizens <span class="red">disapprove</span> of the fact that you would allow your slaves to be degraded in such a manner. `;
-			}
-		}
-		if (arcology.FSDegradationist !== "unset") {
-			if (V.seeBestiality && V.farmyardBreeding) {
-				t += `Your citizens are <span class="green">very approving</span> of the fact that you would allow something as degrading as having your slaves be knocked up by filthy beasts. `;
-			} else {
-				t += `Your citizens <span class="green">approve</span> of the fact that you would allow your slaves to be degraded in such a manner. `;
-			}
-		}
-		if (arcology.FSBodyPurist !== "unset") {
-			/* TODO: requirements for these might change */
-			if (slave.boobsImplant > 0) {
-				// TODO: add stuff here
-			}
-			if (slave.buttImplant > 0) {
-				// TODO: add stuff here
-			}
-			if (slave.faceImplant > 0) {
-				// TODO: add stuff here
-			}
-			if (slave.lipsImplant > 0) {
-				// TODO: add stuff here
+
+		return r;
+	}
+
+	// Close Long-Term Effects
+
+	// Open Vignettes
+
+	function slaveVignettes() {
+		if (V.showVignettes) {
+			const
+				vignette = GetVignette(slave);
+
+			let r = [];
+
+			r.push(`<span class="story-label">This week</span> ${vignette.text}`);
+
+			if (vignette.type === "cash") {
+				r.push(vignetteCash(vignette));
 			}
-		}
-		if (arcology.FSTransformationFetishist !== "unset") {
-			/* TODO: requirements for these might change */
-			if (slave.boobsImplant > 0) {
-				// TODO: add stuff here
+
+			if (vignette.type === "devotion") {
+				r.push(vignetteDevotion(vignette));
 			}
-			if (slave.buttImplant > 0) {
-				// TODO: add stuff here
+
+			if (vignette.type === "trust") {
+				r.push(vignetteTrust(vignette));
 			}
-			if (slave.faceImplant > 0) {
-				// TODO: add stuff here
+
+			if (vignette.type === "health") {
+				r.push(vignetteHealth(vignette));
 			}
-			if (slave.lipsImplant > 0) {
-				// TODO: add stuff here
+
+			if (vignette.type === "rep") {
+				r.push(vignetteReputation(vignette));
 			}
+
+			return r.join(' ');
 		}
-		if (arcology.FSYouthPreferentialist !== "unset") {
-			if (slave.visualAge > 25) { // TODO: not sure about this age
-				t += `${arcology.name}'s citizens <span class="red">disapprove</span> of your using such old slaves to put on shows. `;
-			} else {
-				t += `${arcology.name}'s citizens <span class="green">approve</span> of your using young slaves to put on shows. `;
-			}
+	}
+
+	function vignetteCash(vignette) {
+		const
+			FResultNumber = FResult(slave),
+			cash = Math.trunc(FResultNumber * vignette.effect);
+
+		incomeStats.income += cash;
+
+		if (vignette.effect > 0) {
+			cashX(cash, "slaveAssignmentFarmyardVign", slave);
+
+			return `<span class="yellowgreen">making you an extra ${cashFormat(cash)}.</span>`;
+		} else if (vignette.effect < 0) {
+			cashX(forceNeg(cash), "slaveAssignmentFarmyardVign", slave);
+
+			return `<span class="reputation dec">losing you ${cashFormat(Math.abs(cash))}.</span>`;
+		} else {
+			return `an incident without lasting effect.`;
 		}
-		if (arcology.FSMaturityPreferentialist !== "unset") {
-			if (slave.visualAge > 25) { // TODO: not sure about this age
-				t += `${arcology.name}'s citizens <span class="green">approve</span> of your using mature slaves to put on shows. `;
+	}
+
+	function vignetteDevotion(vignette) {
+		slave.devotion += 1 * vignette.effect;
+
+		if (vignette.effect > 0) {
+			if (slave.devotion > 50) {
+				return `<span class="devotion inc">increasing ${his} devotion to you.</span>`;
+			} else if (slave.devotion >= 20) {
+				return `<span class="devotion inc">increasing ${his} acceptance of you.</span>`;
+			} else if (slave.devotion >= -20) {
+				return `<span class="devotion inc">reducing ${his} dislike of you.</span>`;
 			} else {
-				t += `${arcology.name}'s citizens <span class="red">disapprove</span> of your using such young slaves to put on shows. `;
-			}
-		}
-		if (arcology.FSSlimnessEnthusiast !== "unset") {
-			if (slave.weight > 10) {
-				t += `Society finds your using a slave with such a flabby body <span class="red">absolutely disgusting.</span> `;
-			}
-			if (slave.boobs > 799) {
-				t += `Your citizens ${slave.weight > 10 ? `also ` : ``} don't approve of you using a slave with such large tits as a showgirl. `;
-			}
-			if (slave.butt > 3) {
-				// TODO: write this
-			}
-		}
-		if (arcology.FSAssetExpansionist !== "unset") {
-			if (slave.weight > 10) {
-				// TODO: should weight be in here?
-			}
-			if (slave.boobs > 799) {
-				// TODO: write this
-			}
-			if (slave.butt > 3) {
-				// TODO: write this
-			}
-		}
-		if (arcology.FSPastoralist !== "unset") {
-			if (slave.boobs > 799) {
-				// TODO: does this make sense?
-			}
-			if (slave.lactation > 0) {
-				// TODO: write this
-			}
-		}
-		if (arcology.FSPhysicalIdealist !== "unset") {
-			if (slave.height > 169) {
-				// TODO: write this
+				return `<span class="devotion inc">reducing ${his} hatred of you.</span>`;
+			}
+		} else if (vignette.effect < 0) {
+			if (slave.devotion > 50) {
+				return `<span class="devotion dec">reducing ${his} devotion to you.</span>`;
+			} else if (slave.devotion >= 20) {
+				return `<span class="devotion dec">reducing ${his} acceptance of you.</span>`;
+			} else if (slave.devotion >= -20) {
+				return `<span class="devotion dec">increasing ${his} dislike of you.</span>`;
 			} else {
-				// TODO: write this
+				return `<span class="devotion dec">increasing ${his} hatred of you.</span>`;
 			}
+		} else {
+			return `an incident without lasting effect.`;
 		}
-		if (arcology.FSHedonisticDecadence !== "unset") {
-			if (slave.weight > 10) {
-				// TODO: write this
+	}
+
+	function vignetteTrust(vignette) {
+		slave.trust += 1 * vignette.effect;
+
+		if (vignette.effect > 0) {
+			if (slave.trust > 20) {
+				return `<span class="trust inc">increasing ${his} trust in you.</span>`;
+			} else if (slave.trust >= -20) {
+				return `<span class="trust inc">reducing ${his} fear of you.</span>`;
 			} else {
-				// TODO: write this
+				return `<span class="trust inc">reducing ${his} terror of you.</span>`;
 			}
-		}
-		if (arcology.FSChattelReligionist !== "unset") {
-			if (slave.devotion <= 20) {
-				// TODO: write this
+		} else if (vignette.effect < 0) {
+			if (slave.trust > 20) {
+				return `<span class="trust dec">reducing ${his} trust in you.</span>`;
+			} else if (slave.trust >= -20) {
+				return `<span class="trust dec">increasing ${his} fear of you.</span>`;
 			} else {
-				// TODO: write this
+				return `<span class="trust dec">increasing ${his} terror of you.</span>`;
 			}
-		}
-
-		// Close FS Subsection
-
-		if (setup.entertainmentCareers.includes(slave.career)) {
-			t += ` ${He} has experience with putting on shows from ${his} life before ${he} was a slave, making ${him} more effective at putting on shows. `;
-		}
-		if (setup.farmerCareers.includes(slave.career)) { // TODO: does this even make sense to include?
-			// TODO: write this
-		}
-		if (slave.prestige === 1) {
-			t += `Because some of your citizens already know of ${him}, ${he} earns more. `;
-		} else if (slave.prestige === 2) {
-			t += `Because a lot of your citizens already know of ${him}, ${he} earns quite a bit more. `;
-		} else if (slave.prestige === 3) {
-			t += `Because ${he} is so famous, ${he} earns a lot more then ${he} would otherwise. `;
-		}
-		if (slave.porn.prestige === 1) { // TODO: are prestige and pornPrestige mutually exclusive?
-			t += `${He} earns a bit more because some of your citizens already know ${him} from porn. `;
-		} else if (slave.porn.prestige === 2) {
-			t += `${He} earns quite a bit more because a lot of your citizens already know ${him} from porn. `;
-		} else if (slave.porn.prestige === 3) {
-			t += `${He} earns a lot more because ${he} is so famous from porn. `;
-		}
-		if (slave.health.condition > 20) {
-			t += `${He} is in such excellent health that ${he} is able to put on longer and more energetic shows, earning you more. `;
-		} else if (slave.health.condition < -20) {
-			t += `${His} poor health negatively affects ${his} ability to put on good shows, cutting into your profits. `;
-		}
-		if (slave.face > 40) {
-			t += `${He} is so ${slave.genes === "XY" ? `handsome` : `beautiful`} that ${his} audience is willing to pay more to watch ${him} put on shows. `;
-		} else if (slave.face > 10) {
-			t += `${He} is so ${slave.genes === "XY" ? `good-looking` : `pretty`} that ${his} audience is willing to pay more to watch ${him} put on shows. `;
-		} else if (slave.face < -10) {
-			t += `${His} audience isn't willing to pay as much because of how unattractive ${his} face is. `;
-		} else if (slave.face < -40) {
-			t += `${His} audience isn't willing to pay as much because of how hard ${his} face is to look at. `;
-		}
-		// TODO: write this block with different combinations of trust / devotion
-		if (slave.devotion > 50) {
-			// TODO: write this
-		} else if (slave.devotion < -50) {
-			// TODO: write this
-		}
-		if (slave.trust > 50) {
-			// TODO: write this
-		} else if (slave.trust < -50) {
-			// TODO: write this
-		}
-		if (slave.weight > 30 && arcology.FSHedonisticDecadence === "unset") {
-			t += `Your citizens are not willing to pay as much to see such a fat slave put on shows, so ${he} loses some income. `;
-		} else if (slave.weight < -30) {
-			t += `Your citizens don't like watching such a sickly-looking slaves put on shows, so ${he} loses some income. `;
-		}
-		if (slave.muscles > 30) {
-			// TODO: write this - do we want something for muscles?
-		} else if (slave.muscles < -30) {
-			// TODO: write this - maybe something about the slave's ability to handle the animal?
-		}
-		if (!canSeePerfectly(slave)) {
-			t += `${His} ${!canSee(slave) ? `blindness makes it impossible` : `nearsightedness makes it harder`}} for ${him} to see what ${he}'s doing, affecting ${his} ability to put on a good show. `;
-		}
-		if (slave.hears < 0) {
-			t += `${His} ${slave.hears < -1 ? `lack of` : `poor`} hearing makes it difficult for ${him} to do a good job of putting on a show. `;
-		}
-		if (slave.boobs > 800) {
-			// TODO: write this
-		}
-		if (slave.butt > 4) {
-			// TODO: write this
-		}
-		if (slave.preg > 10) {
-			// TODO: write this
-		}
-		if (slave.health.tired > 60) {
-			t += `${He} is so tired that the energy in ${his} shows is basically nonexistent, affecting ${his} profits. `;
-		}
-		if (slave.chem > 10) {
-			// TODO: write this - would this make sense to include?
-		}
-		if (slave.intelligence > 50) {
-			// TODO: write this - include something about .intelligenceImplant?
-		} else if (slave.intelligence < -50) {
-			// TODO: write this
-		}
-		if (slave.energy <= 20) {
-			// TODO: write this
-		} else if (slave.energy <= 40) {
-			// TODO: write this
-		} else if (slave.energy <= 60) {
-			// TODO: write this
-		} else if (slave.energy <= 80) {
-			// TODO: write this
-		} else if (slave.energy <= 95) {
-			// TODO: write this
 		} else {
-			// TODO: write this
+			return `an incident without lasting effect.`;
 		}
-		if (slave.fetish === "submissive") {
-			// TODO: write this
-		} else if (slave.fetish === "humiliation") {
-			// TODO: write this
-		} else if (slave.fetish === "masochist") {
-			// TODO: write this
-		} // TODO: incorporate quirks
 	}
 
-	// Close Shows
+	function vignetteHealth(vignette) {
+		if (vignette.effect > 0) {
+			improveCondition(slave, 2 * vignette.effect);
 
-	// Open Long-Term Effects
+			return `<span class="reputation inc">improving ${his} health.</span>`;
+		} else if (vignette.effect < 0) {
+			healthDamage(slave, 2 * vignette.effect);
 
-	if (slave.fetishKnown && (slave.fetish === "submissive" || slave.fetish === "humiliation" || slave.fetish === "masochist") || slave.energy > 95) {
-		if (V.farmyardShows) {
-			if (V.seeBestiality) {
-				t += `Getting fucked by animals is the perfect job for ${him}, as far as ${he} can tell. <span class="devotion inc">${He} is happy</span> to spend ${his} days being utterly degraded. `;
-			} else {
-				t += `${He} loves putting on shows with animals, and as far as ${he} can tell, it's the perfect job for ${him}. <span class="devotion inc">${He} is happy</span> to spend ${his} days doing something so degrading. `; // TODO: not sure how degrading putting on shows is
-			}
-		}
-		slave.devotion += 1;
-		if (slave.need) {
-			slave.need = 0;
+			return `<span class="reputation dec">affecting ${his} health.</span>`;
+		} else {
+			return `an incident without lasting effect.`;
 		}
 	}
-	if (slave.behavioralQuirk === "fitness") {
-		t += `${slave.slaveName} <span class="devotion inc">privately enjoys</span> the exercise ${he} receives while working in ${V.farmyardName}. `;
-		slave.devotion += 1;
-	}
 
-	// Close Long-Term Effects
+	function vignetteReputation(vignette) {
+		const FResultNumber = FResult(slave);
 
-	// Open Vignettes
+		repX(Math.trunc(FResultNumber * vignette.effect * 0.1), "vignette", slave);
+		incomeStats.rep += Math.trunc(FResultNumber * vignette.effect * 0.1);
 
-	if (V.showVignettes) {
-		const vignette = GetVignette(slave);
-		t += `<span class="story-label">This week</span> ${vignette.text}`;
-		if (vignette.type === "cash") {
-			let FResultNumber = FResult(slave);
-			const cashVign = Math.trunc(FResultNumber * vignette.effect);
-			if (vignette.effect > 0) {
-				t += ` <span class="yellowgreen">making you an extra ${cashFormat(cashVign)}.</span> `;
-				cashX(cashVign, "slaveAssignmentFarmyardVign", slave);
-			} else if (vignette.effect < 0) {
-				t += ` <span class="red">losing you ${cashFormat(Math.abs(cashVign))}.</span> `;
-				cashX(forceNeg(cashVign), "slaveAssignmentFarmyardVign", slave);
-			} else {
-				t += ` an incident without lasting effect. `;
-			}
-			incomeStats.income += cashVign;
-		} else if (vignette.type === "devotion") {
-			if (vignette.effect > 0) {
-				if (slave.devotion > 50) {
-					t += ` <span class="devotion inc">increasing ${his} devotion to you.</span> `;
-				} else if (slave.devotion >= 20) {
-					t += ` <span class="devotion inc">increasing ${his} acceptance of you.</span> `;
-				} else if (slave.devotion >= -20) {
-					t += ` <span class="devotion inc">reducing ${his} dislike of you.</span> `;
-				} else {
-					t += ` <span class="devotion inc">reducing ${his} hatred of you.</span> `;
-				}
-			} else if (vignette.effect < 0) {
-				if (slave.devotion > 50) {
-					t += ` <span class="devotion dec">reducing ${his} devotion to you.</span> `;
-				} else if (slave.devotion >= 20) {
-					t += ` <span class="devotion dec">reducing ${his} acceptance of you.</span> `;
-				} else if (slave.devotion >= -20) {
-					t += ` <span class="devotion dec">increasing ${his} dislike of you.</span> `;
-				} else {
-					t += ` <span class="devotion dec">increasing ${his} hatred of you.</span> `;
-				}
-			} else {
-				t += ` an incident without lasting effect. `;
-			}
-			slave.devotion += 1 * vignette.effect;
-		} else if (vignette.type === "trust") {
-			if (vignette.effect > 0) {
-				if (slave.trust > 20) {
-					t += ` <span class="trust inc">increasing ${his} trust in you.</span> `;
-				} else if (slave.trust >= -20) {
-					t += ` <span class="trust inc">reducing ${his} fear of you.</span> `;
-				} else {
-					t += ` <span class="trust inc">reducing ${his} terror of you.</span> `;
-				}
-			} else if (vignette.effect < 0) {
-				if (slave.trust > 20) {
-					t += ` <span class="trust dec">reducing ${his} trust in you.</span> `;
-				} else if (slave.trust >= -20) {
-					t += ` <span class="trust dec">increasing ${his} fear of you.</span> `;
-				} else {
-					t += ` <span class="trust dec">increasing ${his} terror of you.</span> `;
-				}
-			} else {
-				t += ` an incident without lasting effect. `;
-			}
-			slave.trust += 1 * vignette.effect;
-		} else if (vignette.type === "health") {
-			if (vignette.effect > 0) {
-				t += ` <span class="green">improving ${his} health.</span> `;
-				improveCondition(slave, 2 * vignette.effect);
-			} else if (vignette.effect < 0) {
-				t += ` <span class="red">affecting ${his} health.</span> `;
-				healthDamage(slave, 2 * vignette.effect);
-			} else {
-				t += ` an incident without lasting effect. `;
-			}
+		if (vignette.effect > 0) {
+			return `<span class="reputation inc">gaining you a bit of reputation.</span>`;
+		} else if (vignette.effect < 0) {
+			return `<span class="reputation dec">losing you a bit of reputation.</span>`;
 		} else {
-			let FResultNumber = FResult(slave);
-			if (vignette.effect > 0) {
-				t += ` <span class="green">gaining you a bit of reputation.</span> `;
-			} else if (vignette.effect < 0) {
-				t += ` <span class="red">losing you a bit of reputation.</span> `;
-			} else {
-				t += ` an incident without lasting effect. `;
-			}
-			repX(Math.trunc(FResultNumber * vignette.effect * 0.1), "vignette", slave);
-			incomeStats.rep += Math.trunc(FResultNumber * vignette.effect * 0.1);
+			return `an incident without lasting effect.`;
 		}
 	}
 
 	// Close Vignettes
 
-	// Facility Decorations
-	const fsGain = 0.0001 * food;
-	FutureSocieties.DecorationBonus(V.farmyardDecoration, fsGain);
+	fullReport(slave);
 
-	return t;
+	return frag;
 };
diff --git a/src/endWeek/slaveAssignmentReport.js b/src/endWeek/slaveAssignmentReport.js
index 4fa4fce42e4cebccf09fdc6c7a370406c61fa905..be509a1599939f2a9f5ba3499f8aa3480754a85d 100644
--- a/src/endWeek/slaveAssignmentReport.js
+++ b/src/endWeek/slaveAssignmentReport.js
@@ -72,7 +72,6 @@ App.EndWeek.slaveAssignmentReport = function() {
 				}
 				break;
 			case Job.CONCUBINE:
-				V.Concubine = slave;
 				V.fuckSlaves++;
 				break;
 			case Job.MASTERSUITE:
@@ -167,10 +166,10 @@ App.EndWeek.slaveAssignmentReport = function() {
 		}
 	} // for (const slave of V.slaves)
 
-	if (V.HeadGirl !== 0) {
+	if (V.HeadGirlID !== 0) {
 		V.HGEnergy++;
-		const slave = slaveStateById(V.HeadGirl.ID);
-		if (V.personalAttention === PersonalAttention.SUPPORTHG) {
+		const slave = slaveStateById(V.HeadGirlID);
+		if (V.personalAttention === PersonalAttention.SUPPORTHG && V.PC.health.shortDamage < 30) {
 			V.HGEnergy++;
 			if (slave.trust > 95) {
 				V.HGEnergy++;
@@ -231,7 +230,7 @@ App.EndWeek.slaveAssignmentReport = function() {
 	if (S.HeadGirl) {
 		const HGPossibleSlaves = [[], [], [], [], [], []];
 		for (const slave of V.slaves) {
-			if (!assignmentVisible(slave) || slave.fuckdoll === 1 || slave.ID === V.Bodyguard.ID || slave.ID === V.HeadGirl.ID || slave.fetish === "mindbroken") {
+			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;
@@ -428,58 +427,57 @@ App.EndWeek.slaveAssignmentReport = function() {
 	function _ensureEmployeeMeetsJobRequirements(slave) {
 		switch (slave.assignment) {
 			case Job.HEADGIRL:
-				V.HeadGirl = slave;
-				if (V.HeadGirl.fetish === "mindbroken") {
+				if (slave.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.HeadGirl = 0;
-				} else if (!canTalk(V.HeadGirl)) {
+					V.HeadGirlID = 0;
+				} else if (!canTalk(slave)) {
 					_printSlaveUnassignedNote(slave, "can't give slaves verbal orders");
-					V.HeadGirl = 0;
-				} else if (!canWalk(V.HeadGirl)) {
+					V.HeadGirlID = 0;
+				} else if (!canWalk(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.HeadGirl = 0;
-				} else if (!canHold(V.HeadGirl)) {
+					V.HeadGirlID = 0;
+				} else if (!canHold(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer able to handle your slaves");
-					V.HeadGirl = 0;
-				} else if (!canSee(V.HeadGirl)) {
+					V.HeadGirlID = 0;
+				} else if (!canSee(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.HeadGirl = 0;
-				} else if (!canHear(V.HeadGirl)) {
+					V.HeadGirlID = 0;
+				} else if (!canHear(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer hear");
-					V.HeadGirl = 0;
-				} else if (V.HeadGirl.preg > 37 && V.HeadGirl.broodmother === 2) {
+					V.HeadGirlID = 0;
+				} else if (slave.preg > 37 && slave.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your Head Girl any longer`);
-					V.HeadGirl = 0;
-				} else if (V.HeadGirl.devotion <= 20) {
+					V.HeadGirlID = 0;
+				} else if (slave.devotion <= 20) {
 					_printSlaveUnassignedNote(slave, "is no longer even accepting of you");
-					V.HeadGirl = 0;
+					V.HeadGirlID = 0;
 				}
-				if (V.HeadGirl === 0) {
+				if (V.HeadGirlID === 0) {
 					removeJob(slave, Job.HEADGIRL);
 				}
 				break;
 			case Job.RECRUITER:
-				V.Recruiter = slave;
+				V.RecruiterID = slave.ID;
 				if (slave.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.Recruiter = 0;
+					V.RecruiterID = 0;
 				} else if (!canTalk(slave)) {
 					_printSlaveUnassignedNote(slave, "can't verbally entice marks");
-					V.Recruiter = 0;
+					V.RecruiterID = 0;
 				} else if (slave.preg > 37 && slave.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your recruiter any longer`);
-					V.Recruiter = 0;
+					V.RecruiterID = 0;
 				} else if (!canWalk(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.Recruiter = 0;
+					V.RecruiterID = 0;
 				} else if (!canSee(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.Recruiter = 0;
-				} else if (!canHear(V.Recruiter)) {
+					V.RecruiterID = 0;
+				} else if (!canHear(S.Recruiter)) {
 					_printSlaveUnassignedNote(slave, "an no longer hear");
-					V.Recruiter = 0;
+					V.RecruiterID = 0;
 				}
-				if (V.Recruiter === 0) {
+				if (V.RecruiterID === 0) {
 					removeJob(slave, Job.RECRUITER);
 				}
 				break;
@@ -499,7 +497,7 @@ App.EndWeek.slaveAssignmentReport = function() {
 				} else if (V.unMadam === 7) {
 					_printSlaveUnassignedNote(slave, `can no longer handle ${getPronouns(slave).his} underlings`);
 				}
-				if (V.Madam === 0) {
+				if (V.MadamID === 0) {
 					removeJob(slave, Job.MADAM);
 				}
 				break;
@@ -520,52 +518,50 @@ App.EndWeek.slaveAssignmentReport = function() {
 				}
 				break;
 			case Job.MILKMAID:
-				V.Milkmaid = slave;
-				if (V.Milkmaid.fetish === "mindbroken") {
+				if (slave.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.Milkmaid = 0;
-				} else if (V.Milkmaid.preg > 37 && V.Milkmaid.broodmother === 2) {
+					V.MilkmaidID = 0;
+				} else if (slave.preg > 37 && slave.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your Milkmaid any longer`);
-					V.Milkmaid = 0;
-				} else if (!canWalk(V.Milkmaid)) {
+					V.MilkmaidID = 0;
+				} else if (!canWalk(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.Milkmaid = 0;
-				} else if (!canHold(V.Milkmaid)) {
+					V.MilkmaidID = 0;
+				} else if (!canHold(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer handle nor milk your slaves");
-					V.Milkmaid = 0;
-				} else if (!canSee(V.Milkmaid)) {
+					V.MilkmaidID = 0;
+				} else if (!canSee(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.Milkmaid = 0;
-				} else if (!canHear(V.Milkmaid)) {
+					V.MilkmaidID = 0;
+				} else if (!canHear(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer hear");
-					V.Milkmaid = 0;
+					V.MilkmaidID = 0;
 				}
-				if (V.Milkmaid === 0) {
+				if (V.MilkmaidID === 0) {
 					removeJob(slave, Job.MILKMAID);
 				}
 				break;
 			case Job.FARMER:
-				V.Farmer = slave;
-				if (V.Farmer.fetish === "mindbroken") {
+				if (S.Farmer.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.Farmer = 0;
-				} else if (V.Farmer.preg > 37 && V.Farmer.broodmother === 2) {
+					V.FarmerID = 0;
+				} else if (S.Farmer.preg > 37 && S.Farmer.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your Farmer any longer`);
-					V.Farmer = 0;
-				} else if (!canWalk(V.Farmer)) {
+					V.FarmerID = 0;
+				} else if (!canWalk(S.Farmer)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.Farmer = 0;
-				} else if (!canHold(V.Farmer)) {
+					V.FarmerID = 0;
+				} else if (!canHold(S.Farmer)) {
 					_printSlaveUnassignedNote(slave, "can no longer grip things");
-					V.Farmer = 0;
-				} else if (!canSee(V.Farmer)) {
+					V.FarmerID = 0;
+				} else if (!canSee(S.Farmer)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.Farmer = 0;
-				} else if (!canHear(V.Farmer)) {
+					V.FarmerID = 0;
+				} else if (!canHear(S.Farmer)) {
 					_printSlaveUnassignedNote(slave, "can no longer hear");
-					V.Farmer = 0;
+					V.FarmerID = 0;
 				}
-				if (V.Farmer === 0) {
+				if (V.FarmerID === 0) {
 					removeJob(slave, Job.FARMER);
 				}
 				break;
@@ -660,27 +656,26 @@ App.EndWeek.slaveAssignmentReport = function() {
 				}
 				break;
 			case Job.MATRON:
-				V.Matron = slave;
 				if (slave.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.Matron = 0;
+					V.MatronID = 0;
 				} else if (slave.preg > 37 && slave.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your Matron any longer`);
-					V.Matron = 0;
+					V.MatronID = 0;
 				} else if (!canWalk(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.Matron = 0;
+					V.MatronID = 0;
 				} else if (!canHold(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer hold infants");
-					V.Matron = 0;
+					V.MatronID = 0;
 				} else if (!canHear(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer hear");
-					V.Matron = 0;
+					V.MatronID = 0;
 				} else if (!canSee(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.Matron = 0;
+					V.MatronID = 0;
 				}
-				if (V.Matron === 0) {
+				if (V.MatronID === 0) {
 					removeJob(slave, Job.MATRON);
 				}
 				break;
@@ -709,27 +704,26 @@ App.EndWeek.slaveAssignmentReport = function() {
 				}
 				break;
 			case Job.BODYGUARD:
-				V.Bodyguard = slave;
 				if (slave.fetish === "mindbroken") {
 					_printSlaveUnassignedNote(slave, "is mindbroken");
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				} else if (!canWalk(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer independently mobile");
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				} else if (!canHold(slave)) {
 					_printSlaveUnassignedNote(slave, "is no longer able to hold a weapon");
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				} else if (slave.preg > 37 && slave.broodmother === 2) {
 					_printSlaveUnassignedNote(slave, "spends so much time giving birth and laboring that", `${getPronouns(slave).he} cannot effectively serve as your bodyguard any longer`);
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				} else if (!canSee(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer see");
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				} else if (!canHear(slave)) {
 					_printSlaveUnassignedNote(slave, "can no longer hear");
-					V.Bodyguard = 0;
+					V.BodyguardID = 0;
 				}
-				if (V.Bodyguard === 0) {
+				if (V.BodyguardID === 0) {
 					removeJob(slave, Job.BODYGUARD);
 				}
 				break;
diff --git a/src/events/RECI/butthole.js b/src/events/RECI/butthole.js
index 33030cbed80a983621d66eb014c0eb1a1ca4011e..f797aaf8ab36d24816c040243ff3622c22f964fa 100644
--- a/src/events/RECI/butthole.js
+++ b/src/events/RECI/butthole.js
@@ -233,13 +233,13 @@ App.Events.RECIButthole = class RECIButthole extends App.Events.BaseEvent {
 			(eventSlave.chastityAnus)
 				? new App.Events.Result(`Remind ${him} of ${his} chastity`, chaste)
 				: new App.Events.Result(),
-			(V.HeadGirl !== 0 && eventSlave.ID !== V.HeadGirl.ID)
+			(eventSlave.ID !== V.HeadGirlID)
 				? new App.Events.Result(`Double anal with the Head Girl`, () => DoubleTeam(S.HeadGirl))
 				: new App.Events.Result(),
-			(V.Concubine !== 0 && eventSlave.ID !== V.Concubine.ID && hasAnyLegs(S.Concubine) && hasAnyArms(S.Concubine) && S.Concubine.fetish !== "mindbroken")
+			(eventSlave.ID !== V.ConcubineID && hasAnyLegs(S.Concubine) && hasAnyArms(S.Concubine) && S.Concubine.fetish !== "mindbroken")
 				? new App.Events.Result(`Double anal with your Concubine`, () => DoubleTeam(S.Concubine))
 				: new App.Events.Result(),
-			(V.Bodyguard !== 0 && eventSlave.ID !== V.Bodyguard.ID)
+			(eventSlave.ID !== V.BodyguardID)
 				? new App.Events.Result(`Double anal with your Body Guard`, () => DoubleTeam(S.Bodyguard))
 				: new App.Events.Result(),
 			new App.Events.Result(`Chat about ${his} ass`, talk),
diff --git a/src/events/RECI/ugly.js b/src/events/RECI/ugly.js
new file mode 100644
index 0000000000000000000000000000000000000000..4a2691d7fceb1047ee953132377e6243e31bb973
--- /dev/null
+++ b/src/events/RECI/ugly.js
@@ -0,0 +1,169 @@
+App.Events.RECIUgly = class RECIUgly extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.fetish !== "mindbroken",
+				s => V.REUglyCheckinIDs.includes(s.ID),
+				s => s.face >= -10,
+				s => (s.assignment === Job.PUBLIC || s.assignment === Job.WHORE),
+				s => s.devotion >= 10,
+				s => (Beauty(s) >= 50),
+				s => s.bodySwap === 0,
+				s => s.porn.prestige === 0,
+				canSee,
+				canStand,
+				canTalk,
+				canHear,
+				hasAnyArms,
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			// eslint-disable-next-line no-unused-vars
+			He, he, His, his, hers, him, himself, girl
+		} = getPronouns(eventSlave);
+		const {title: Master, say: say} = getEnunciation(eventSlave);
+		const freeMovement = (canWalk(eventSlave) || (canMove(eventSlave) && eventSlave.rules.mobility === "permissive"));
+
+		V.nextLink = "Next Week";
+		V.REUglyCheckinIDs.deleteWith((s) => s === eventSlave.ID);
+
+		let artDiv = document.createElement("div"); // named container so we can replace it later
+		App.Events.drawEventArt(artDiv, eventSlave, "no clothing");
+		node.appendChild(artDiv);
+
+		let t = [];
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		if (freeMovement) {
+			t.push(`comes`);
+		} else {
+			t.push(`is walked`);
+		}
+		t.push(`in for an inspection. You have a relaxed day scheduled, so you take the time to do an unusually thorough job. On a whim, you pull up ${his} complete file, flipping the virtual pages out across your desk. The induction pictures are particularly striking. ${He} doesn't look much like that, anymore, and that's a good thing. ${He} was not a pretty ${girl} when you got ${him}, though ${he}'s pretty enough now. With a desk control gesture you flip a full frontal shot of ${him} on the day of ${his} enslavement up onto a wallscreen.`);
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`${His} ${App.Desc.eyesColor(eventSlave)} track your sudden motion, of course, and ${he} follows it to the screen. Suddenly, the ${SlaveTitle(eventSlave)} is eye to eye with a life-size picture of who ${he} used to be, just a few`);
+		if (V.showInches === 2) {
+			t.push(`feet`);
+		} else {
+			t.push(`meters`);
+		}
+		t.push(`away. ${He} gasps with recognition, and then ${his} face clouds inscrutably. ${He}`);
+		if (freeMovement) {
+			t.push(`takes a couple of hesitant steps`);
+		} else {
+			t.push(`hesitates for a moment before struggling`);
+		}
+		t.push(`forward, and then reaches out to touch the cheek of the ${girl} in the picture.`);
+		if (hasBothArms(eventSlave)) {
+			t.push(`As ${his} fingertips brush the smooth surface of the wallscreen, ${his} other hand ghosts along ${his} own face.`);
+		} else {
+			t.push(`${His} fingertips brush the smooth surface of the wallscreen, before ghosting along ${his} own face.`);
+		}
+		t.push(`${His} expression is not sad, so it's surprising when a single tear rolls down ${his} cheek. You order ${him} to tell you how the picture makes ${him} feel.`);
+		App.Events.addParagraph(node, t);
+
+		t = [];
+		t.push(`${He} looks pensive, and goes through two false starts before ${he} clears ${his} throat, wrenches ${his} gaze away from ${his} picture, and ${say}s introspectively,`);
+		t.push(Spoken(eventSlave, `"${Master}, it's strange. We — we don't have pictures of ourselves, from before we were enslaved. I didn't really realize how much I'd changed. Some days being a slave is hard. But seeing that picture, it makes me feel better about it. You — you're really ${V.PC.title === 1 ? "handsome" : "pretty"}, ${Master}. I bet you always were."`));
+		t.push(`${He} inclines ${his} head towards the homely ${girl} on the screen.`);
+		t.push(Spoken(eventSlave, `"It's hard to be ugly, ${Master}. Really hard. I feel sorry for that ${girl}, and I'm glad I don't look like ${him} anymore."`));
+		t.push(`${He} laughs suddenly, a little self-consciously.`);
+		t.push(Spoken(eventSlave, `"The crazy thing is if you'd told that ${girl} that`));
+		if (eventSlave.assignment === Job.WHORE) {
+			t.push(Spoken(eventSlave, `lots and lots of people would pay to fuck ${him}`));
+		} else {
+			t.push(Spoken(eventSlave, `all kinds of people would happily fuck ${him}`));
+		}
+		t.push(Spoken(eventSlave, `someday, ${he} wouldn't have believed you. It's kind of reassuring, actually. Is that weird? That came out weird. Sorry."`));
+		App.Events.addParagraph(node, t);
+
+
+		App.Events.addResponses(node, [
+			new App.Events.Result(`${He}'s pretty enough to decorate your arm for a night out`, nightOut),
+			new App.Events.Result(`Show ${him} off online`, livestreaming)
+		]);
+
+		function nightOut() {
+			// replace slave art
+			$(artDiv).empty();
+			App.Events.drawEventArt(artDiv, eventSlave, "a mini dress");
+
+			let frag = document.createDocumentFragment();
+
+			t = [];
+			t.push(`You tell ${him} to head down to the wardrobe and put on the outfit that'll be laid out for ${him} there. ${He} obeys promptly, but does not return for some time, having gotten instructions from ${assistant.name} along the way to put extra effort into ${his} grooming. When ${he} finally returns, the effect is striking.`);
+			if (eventSlave.face > 10) {
+				t.push(`${He}'s a gorgeous ${girl} with or without makeup, dressed up or naked, but ${he}'s especially luscious tonight.`);
+			} else {
+				t.push(`${His} face is not flawless, but ${he}'s conscious of ${his} transformation, and the new confidence in ${his} beauty adds a special glow that cannot be faked.`);
+			}
+			t.push(`${His} evening dress is elegant; it's quite slutty by old world standards, but according to Free Cities fashion, it's just about the most conservative gown a slave can be expected to wear, and quite daring in that it isn't immediately obvious whether ${he}'s a slave or not.`);
+			if (eventSlave.areolae < 3) {
+				t.push(`The tops of ${his} areolae are hardly even visible.`);
+			}
+			App.Events.addParagraph(frag, t);
+
+			t = [];
+			t.push(`You take ${him} out to a nice lounge, with blue-toned light and soft music. ${He} clings to your arm, pressing ${himself} against you just the right amount: not enough to demand sex right now, but enough to raise the anticipation of it later. ${He}'s a slave, so ${he} does not eat or drink the usual fare on offer, but the establishment is used to slaves and offers flavorful variation on liquid slave food. ${He} drinks the translucent fluid out of a tall glass, carefully maintaining ${his} poise. You circulate, leaving ${him} at the bar when acquaintances appear. ${He} perches on a stool, conscious of and pleased by the discreet admiration of ${his} body, delectably outlined by the tight dress. Once a new arrival who did not see you with ${him} introduces himself to ${him}. He's tall and fit and silver-haired, but he picked ${him} out of the room to approach, and it's with polite disappointment that he reacts to ${his} indication of you, across the room:`);
+			t.push(Spoken(eventSlave, `"I'm sorry, Sir, that's my ${Master} there."`));
+			t.push(`He offers a nonverbal apology without coming over, which you accept with a wave: it's such a common mistake in Free Cities high society that it's universally brushed off without offense. It happens again later in the night, when a slightly tipsy free woman occupies the barstool next to ${eventSlave.slaveName}'s and keeps trying to relax against ${him} until the flattered slave explains ${himself} again. When you bring ${him} home at the end of the night, ${his}`);
+			if (hasBothEyes(eventSlave)) {
+				t.push(`eyes are`);
+			} else {
+				t.push(`eye is`);
+			}
+			t.push(`shining with <span class="trust inc">private assurance,</span> and ${he} presses ${himself} against you more closely than ever.`);
+			App.Events.addParagraph(frag, t);
+
+			eventSlave.trust += 4;
+
+			return frag;
+		}
+
+		function livestreaming() {
+			t = [];
+			t.push(`You tell ${him} that ${he}'s become a very pretty sex slave, and to prove it, you`);
+			if (eventSlave.porn.feed > 0) {
+				t.push(`let ${him} believe you've`);
+			}
+			t.push(`set up a live feed for ${him} that's available for free in the old world.`);
+			if (eventSlave.porn.feed === 0) {
+				t.push(`The Free Cities produce an utter torrent of hardcore video, so much that there's never any profit to be made off it anymore, but viewers begin to join the channel anyway and by the end of the week`);
+			} else {
+				t.push(`${He} already has a growing fanbase, and with the promise of a week of special content, even more join the channel to observe. Before long,`);
+			}
+			t.push(`${he} has thousands of eyes watching ${him} as ${he}`);
+			if (eventSlave.assignment === Job.WHORE) {
+				t.push(`sells ${his} body`);
+			} else {
+				t.push(`has sex with random citizens`);
+			}
+			t.push(`in the hallways of ${V.arcologies[0].name}. ${capFirstChar(V.assistant.name)} keeps ${him} constantly informed of how many people are watching ${him} get fucked, how many of them are likely masturbating to ${his} body, and how many inquiries about ${him} ${V.assistant.name} is culling out of your inbox. ${He} slowly gets used to ${his} starring role in an impromptu free hardcore stream, but never quite stops stealing wondering glances at the nearest camera, as though ${he} cannot believe that so many people would sexualize ${him}. This lends ${him} a naughtiness that cannot be faked, garnering ${him} <span class="reputation inc">much attention</span> by the end of the week.`);
+			if (eventSlave.fetish === "humiliation" && eventSlave.fetishStrength > 60) {
+				if (eventSlave.fetishKnown === 1) {
+					t.push(`Of course, since ${he}'s a humiliation fetishist, this experience is like sexual candy for ${him}. ${He} <span class="devotion inc">can't get enough.</span>`);
+				} else{
+					t.push(`Oddly enough, <span class="devotion inc">${he} seems to enjoy it far more than anticipated.</span>`);
+				}
+				eventSlave.devotion += 4;
+			}
+			if (eventSlave.porn.feed === 0) {
+				repX(1250, "event", eventSlave);
+			} else {
+				repX(500, "event", eventSlave);
+				eventSlave.porn.viewerCount += 7000;
+			}
+			return t;
+		}
+	}
+};
diff --git a/src/events/RESS/comfortableSeat.js b/src/events/RESS/comfortableSeat.js
index 628ecfe0cb5ecaecc37f9500385d31bad3319cb7..b297b0a69f9b5a11cebe701b001b4e42eee3597e 100644
--- a/src/events/RESS/comfortableSeat.js
+++ b/src/events/RESS/comfortableSeat.js
@@ -30,7 +30,7 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 		const {s, say, title: Master} = getEnunciation(eventSlave);
 		const desc = SlaveTitle(eventSlave);
 		const belly = bellyAdjective(eventSlave);
-		const dickSize = ["tiny", "", "big"][Math.ceil(Math.clamp(eventSlave.dick, 1, 5) / 2) - 1]; // ranges are "tiny" <= 2 < "" <= 4 < "big"
+		const dickSize = eventSlave.dick > 4 ? "big" : (eventSlave.dick > 2 ? "" : "tiny");
 
 		/** @type {App.Entity.PlayerState} */
 		let PC = V.PC;
@@ -77,7 +77,7 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 		App.Events.addParagraph(node, t);
 		t = [];
 
-		t.push(`You're nude, a consequence of ${V.Concubine !== 0 && V.Concubine.ID !== eventSlave.ID ? `recent activities involving ${V.Concubine.slaveName}` : "recent unrelated activities"}. ${PC.dick !== 0 ? "Your formidable dick is three quarters hard," : "Nude, that is, all except for the strap-on you were just using and haven't taken off yet,"} and ${eventSlave.slaveName} is pointing right at it. ${He} knows exactly what ${he}'s asking for and gives ${his}`);
+		t.push(`You're nude, a consequence of ${V.ConcubineID !== 0 && V.ConcubineID !== eventSlave.ID ? `recent activities involving ${S.Concubine.slaveName}` : "recent unrelated activities"}. ${PC.dick !== 0 ? "Your formidable dick is three quarters hard," : "Nude, that is, all except for the strap-on you were just using and haven't taken off yet,"} and ${eventSlave.slaveName} is pointing right at it. ${He} knows exactly what ${he}'s asking for and gives ${his}`);
 		if (eventSlave.hips > 0) {
 			t.push("broad");
 		} else if (eventSlave.hips > -1) {
@@ -151,10 +151,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 						t.push(`turns around and carefully perches ${himself} on ${PC.dick !== 0 ? "your cock" : "the phallus"},`);
 						if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 							t.push(`letting ${his} weight slide it inside ${his} wet pussy.`);
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 						} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`letting ${his} weight push it up ${his} asshole.`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`putting it between ${his} thighs.`);
 						}
@@ -168,10 +168,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 						t.push(`turns around and sits on ${PC.dick !== 0 ? "your cock" : "the phallus"}, leaning back against you and making sure all the other slaves who pass by can see`);
 						if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 							t.push(`where it penetrates ${his} cunt.`);
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 						} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`where it's lodged up ${his} butt.`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`where it's rubbing ${him} intimately between ${his} thighs.`);
 						}
@@ -180,7 +180,7 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 					case "buttslut":
 						if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`turns around and shivers with pleasure as ${he} hilts ${his} anal sphincter around the base of ${PC.dick !== 0 ? "your cock" : "the phallus"}. ${He} bounces on it happily, reaming ${his} own ass,`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`turns around and shivers with pleasure as ${he} feels ${PC.dick !== 0 ? "your cock" : "the phallus"} slip between ${his} buttcheeks. ${He} rubs against it, happy to share ${his} butt with you,`);
 						}
@@ -216,7 +216,7 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 							}
 							t.push(`${He} rides you hungrily,`);
 
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 							if (canImpreg(eventSlave, PC)) {
 								knockMeUp(eventSlave, 40, 0, -1);
 							}
@@ -228,10 +228,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 						t.push(`turns around and sits right down on ${PC.dick !== 0 ? "your cock" : "the phallus"}, eagerly`);
 						if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 							t.push(`taking it into ${his} cunt.`);
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 						} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`getting it shoved up ${his} butt.`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`squeezing it between ${his} thighs.`);
 						}
@@ -241,10 +241,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 						t.push(`turns around and hesitantly sits on ${PC.dick !== 0 ? "your cock" : "the phallus"}, letting`);
 						if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 							t.push(`it slide into ${his} cunt.`);
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 						} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`it slide up ${his} butt.`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`it slide between ${his} thighs.`);
 						}
@@ -254,10 +254,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 						t.push(`turns around and carefully perches ${himself} on ${PC.dick !== 0 ? "your cock" : "the phallus"},`);
 						if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 							t.push(`letting ${his} weight slide it inside ${his} wet pussy at an uncomfortable angle.`);
-							VCheck.Vaginal(1, eventSlave);
+							t.push(VCheck.Vaginal(1, eventSlave));
 						} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 							t.push(`letting ${his} weight push it up ${his} asshole at an uncomfortable angle.`);
-							VCheck.Anal(1, eventSlave);
+							t.push(VCheck.Anal(1, eventSlave));
 						} else {
 							t.push(`putting it between ${his} thighs at an uncomfortable angle.`);
 						}
@@ -269,10 +269,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 				t.push(`${eventSlave.fetishKnown === 1 ? `${He} can't really think of how to accommodate the situation to ${his} own preferred approach to sex` : `${He} isn't well versed in how ${his} own sexual needs might fit into the situation`}, so ${he} just services you like a good ${girl}. ${He} turns around and sits on ${PC.dick !== 0 ? "your cock" : "the phallus"},`);
 				if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 					t.push(`squatting to bounce ${his} cunt up and down on it.`);
-					VCheck.Vaginal(1, eventSlave);
+					t.push(VCheck.Vaginal(1, eventSlave));
 				} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 					t.push(`squatting to bounce ${his} butthole up and down on it.`);
-					VCheck.Anal(1, eventSlave);
+					t.push(VCheck.Anal(1, eventSlave));
 				} else {
 					t.push(`putting it between ${his} thighs for some intercrural sex, since ${his} ${eventSlave.vagina > -1 ? "holes aren't" : "hole isn't"} appropriate.`);
 				}
@@ -304,10 +304,10 @@ App.Events.RESSComfortableSeat = class RESSComfortableSeat extends App.Events.Ba
 			t.push(`Deciding that ${he} shouldn't use ${his} ${hasBothArms(eventSlave) ? "hands" : "hand"} to guide it, ${he} lowers ${himself} slowly,`);
 			if (canDoVaginal(eventSlave) && eventSlave.vagina > 0) {
 				t.push(`breathing a little harder as ${he} feels its head spread ${his} pussylips and then slide inside ${him}.`);
-				VCheck.Vaginal(1, eventSlave);
+				t.push(VCheck.Vaginal(1, eventSlave));
 			} else if (canDoAnal(eventSlave) && eventSlave.anus > 0) {
 				t.push(`letting out a breath and relaxing as ${he} feels its head press past ${his} sphincter and then all the way up ${his} butt.`);
-				VCheck.Anal(1, eventSlave);
+				t.push(VCheck.Anal(1, eventSlave));
 			} else {
 				t.push(`getting it situated between ${his} thighs, since that's the best option ${he} has available.`);
 			}
diff --git a/src/events/RESS/devotedAnalVirgin.js b/src/events/RESS/devotedAnalVirgin.js
index a710cd1c96dcc41af2c5ec7fa54c92b54c4e464c..f9a354087fef606b341f9244bd503d7271601a28 100644
--- a/src/events/RESS/devotedAnalVirgin.js
+++ b/src/events/RESS/devotedAnalVirgin.js
@@ -92,12 +92,12 @@ App.Events.RESSDevotedAnalVirgin = class RESSDevotedAnalVirgin extends App.Event
 				}
 				t.push(`lavishing attention on ${his} nipples to ensure that ${he} has fun, too.`);
 			}
-			t.push(`${He} leaves your office feeling <span class="hotpink">very close to ${his} ${WrittenMaster()} indeed,</span> and seems to have forgotten ${his} unfucked butthole, for now.`);
-
-			eventSlave.devotion += 4;
+			t.push(`${He} leaves your office feeling <span class="hotpink">very close to ${his} ${WrittenMaster(eventSlave)} indeed,</span> and seems to have forgotten ${his} unfucked butthole, for now.`);
 			if (eventSlave.vagina > 0 && canDoVaginal(eventSlave)) {
-				VCheck.Vaginal(1, eventSlave);
+				t.push(VCheck.Vaginal(1, eventSlave));
 			}
+
+			eventSlave.devotion += 4;
 			return t;
 		}
 
diff --git a/src/events/RESS/devotedEducated.js b/src/events/RESS/devotedEducated.js
new file mode 100644
index 0000000000000000000000000000000000000000..a14cc6ec5334ab36443f0268e74579ee84c40ca4
--- /dev/null
+++ b/src/events/RESS/devotedEducated.js
@@ -0,0 +1,150 @@
+App.Events.RESSDevotedEducated = class RESSDevotedEducated extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.fetish !== "mindbroken",
+				s => (canWalk(s) || (canMove(s) && s.rules.mobility === "permissive")),
+				hasAnyArms,
+				canTalk,
+				s => s.devotion > 50,
+				s => s.intelligence + s.intelligenceImplant > 50,
+				s => s.intelligenceImplant >= 15,
+				s => s.accent < 4
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, his, him, himself
+		} = getPronouns(eventSlave);
+		const {say, title: Master} = getEnunciation(eventSlave);
+		const desc = SlaveTitle(eventSlave);
+
+		/** @type {App.Entity.PlayerState} */
+		let PC = V.PC;
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, eventSlave, "no clothing");
+
+		let t = [];
+
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`comes before you for a routine inspection. The ${desc} is a well-educated and obedient slave. Though ${he} performs ${his} duties devotedly and to the best of ${his} abilities, slave life is not particularly conducive to straining an individual's brainpower. You happen to run into ${eventSlave.slaveName} in the hallways of the penthouse, where ${he} takes the opportunity to wordlessly signal ${he} wishes to gain your attention.`);
+		if (canTalk(eventSlave)) {
+			t.push(`"${Master}," ${he} ${say}s. "${Spoken(eventSlave, `I really enjoy my role as your slave, but I just don't feel like my new life stimulates me.`)}" ${He} blushes prettily at ${his} choice of words before continuing, "${Spoken(eventSlave, `Stimulate my mind, I mean.`)}"`);
+		} else {
+			t.push(`${He} uses gestures to beg your pardon and explains that while ${he} enjoys life as your slave, ${he} doesn't feel like ${his} new role in your arcology allows ${him} to stimulate ${his} mind as often as it does ${his} body.`);
+		}
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			new App.Events.Result(`Have a conversation with ${him}`, conversation),
+			canDoAnal(eventSlave) || canDoVaginal(eventSlave)
+				? new App.Events.Result(`Stimulate ${his} mind and body`, stimulate, virginityCheck())
+				: new App.Events.Result(),
+			V.arcologies[0].FSDegradationist !== "unset"
+				? new App.Events.Result(`A public blowbang will show ${him} what a Degradationist arcology thinks about 'educated' slaves`, blowbang)
+				: new App.Events.Result(),
+			V.arcologies[0].FSPaternalist !== "unset"
+				? new App.Events.Result(`Allow ${him} to audit some advanced classes at Paternalist slave schools`, schooling)
+				: new App.Events.Result()
+		]);
+
+		function conversation() {
+			t = [];
+
+			t.push(`You linger in the hallway a while with ${eventSlave.slaveName} and enjoy a brief but verbose discussion. It's clear ${he} hasn't had the chance to engage in any meaningful conversations in a while, so ${he} relishes the opportunity energetically.`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			t.push(`Though the two of you only touch upon a handful of sophisticated topics, by the time you leave ${him} to tend to other matters, ${eventSlave.slaveName} is beaming happily as ${he} continues on to ${his} duties for the day. ${He} <span class="mediumaquamarine">trusts you more</span> for taking the time to engage with ${him} intellectually.`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			eventSlave.trust += 4;
+			return t;
+		}
+
+		function stimulate() {
+			t = [];
+
+			t.push(`You find a comfortable seat to sit on ${PC.dick === 0 ? "and don a strap-on" : ""} as ${he} seats ${his}`);
+			if (eventSlave.butt < 2) {
+				t.push(`narrow`);
+			} else if (eventSlave.butt < 5) {
+				t.push(`average`);
+			} else if (eventSlave.butt < 8) {
+				t.push(`plump`);
+			} else if (eventSlave.butt < 12) {
+				t.push(`impressive`);
+			} else {
+				t.push(`mind-blowing`);
+			}
+			t.push(`ass on your ${PC.dick === 0 ? "strap-on" : "dick"} and rides you. While your cock plumbs the depths of ${his} ${canDoVaginal(eventSlave) ? "pussy" : "butt"}, the two of you engage in a casual debate over a number of topics of sophistry.`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			t.push(`At the crux of one of ${his} concluding arguments, the intense pleasure from the prolonged ${canDoVaginal(eventSlave) ? "fucking" : "buttfucking"} pushes ${him} over the edge into orgasm, robbing the thrust of ${his} defense of any gravitas it once had. ${He} doesn't seem to mind however, choosing to instead wrap up ${his} remarks by <span class="hotpink">blowing you a kiss.</span>`);
+			if (canDoVaginal(eventSlave)) {
+				t.push(VCheck.Vaginal(1, eventSlave));
+			} else {
+				t.push(VCheck.Anal(1, eventSlave));
+			}
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			eventSlave.devotion += 4;
+			return t;
+		}
+
+		function blowbang() {
+			t = [];
+
+			t.push(`You tell ${him} ${he}'ll get fucked in the mouth until ${he} either gets over the idea of being special for ${his} education or until all ${his} learning is fucked out of ${his} head. You drag the protesting ${eventSlave.slaveName} out into a public plaza, restrain ${him} in stocks so that ${his} mouth is available, and inform the gathering crowd of citizens that this particular slave thinks ${himself} more than a fuckhole because of some fancy 'education'.`);
+			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. ${SkillIncrease.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 = [];
+
+			seX(eventSlave, "oral", "public", "penetrative", jsRandom(65,80));
+			repX(500, "event", eventSlave);
+			healthDamage(eventSlave, 10);
+			return t;
+		}
+
+		function schooling() {
+			t = [];
+
+			t.push(`You tell ${him} to take the day off for a trip to audit classes at ${V.arcologies[0].name}'s most renowned Paternalist slave schools. ${eventSlave.slaveName} is shocked into a reverie by your words before running to wrap ${his} ${hasBothArms(eventSlave) ? "arms" : "arm"} around you in a tight hug. When ${he} eventually untangles ${himself} from you, ${he} races off to get ready for ${his} day of intellectualism while you direct ${V.assistant.name} to make the necessary arrangements.`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			t.push(`When ${eventSlave.slaveName} returns, ${he} looks exhausted but happier than you've ever seen ${him}. It seems ${he} benefited greatly from ${V.arcologies[0].name}'s Paternalist institutions, and has grown to <span class="mediumaquamarine">trust you more</span> while also <span class="hotpink">deepening ${his} acceptance of slavery.</span>`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			eventSlave.trust += 4;
+			eventSlave.devotion += 4;
+			return t;
+		}
+
+		function virginityCheck() {
+			if (canDoVaginal(eventSlave) && eventSlave.vagina === 0) {
+				return `This option will take ${his} virginity`;
+			} else if (canDoAnal(eventSlave) && eventSlave.anus === 0) {
+				return `This option will take ${his} anal virginity`;
+			}
+		}
+	}
+};
diff --git a/src/events/RESS/devotedVirgin.js b/src/events/RESS/devotedVirgin.js
index dd9089ef9c5523106b44669b65fdaa8afc95122f..5a9b3cd19076eff58f32c2f0cf91c82e1d33fb6e 100644
--- a/src/events/RESS/devotedVirgin.js
+++ b/src/events/RESS/devotedVirgin.js
@@ -74,7 +74,7 @@ App.Events.RESSDevotedVirgin = class RESSDevotedVirgin extends App.Events.BaseEv
 			if (eventSlave.belly >= 5000) {
 				t.push(`${eventSlave.bellyPreg >= 3000 ? "gravid" : "rounded"}`);
 			}
-			t.push(`body against you and shivering with delight. ${He} shudders, almost uncontrollably, when you grind your ${PC.dick === 0 ? "clitoris" : "dick"} against ${his} moistened, wet pussy between ${his} thighs, taking extra care not to penetrate the willing slave. ${He} leaves your office feeling <span class="hotpink">very close to ${his} ${WrittenMaster()} indeed,</span> and seems to have forgotten ${his} unfucked vagina, for now.`);
+			t.push(`body against you and shivering with delight. ${He} shudders, almost uncontrollably, when you grind your ${PC.dick === 0 ? "clitoris" : "dick"} against ${his} moistened, wet pussy between ${his} thighs, taking extra care not to penetrate the willing slave. ${He} leaves your office feeling <span class="hotpink">very close to ${his} ${WrittenMaster(eventSlave)} indeed,</span> and seems to have forgotten ${his} unfucked vagina, for now.`);
 
 			eventSlave.devotion += 4;
 			return t;
diff --git a/src/events/RESS/devotedWaist.js b/src/events/RESS/devotedWaist.js
index 442acee4c650e32687de8544f7a900bd32c30cdb..0665172859d59da1ec849d8b4e943120e941d48c 100644
--- a/src/events/RESS/devotedWaist.js
+++ b/src/events/RESS/devotedWaist.js
@@ -102,18 +102,18 @@ App.Events.RESSDevotedWaist = class RESSDevotedWaist extends App.Events.BaseEven
 				t.push(`This can be applied during sex many ways. First, ${he} sits on the bathroom counter and bends ${himself} almost double for`);
 				if (canDoAnal(eventSlave)) {
 					t.push(`anal. Your control over the pace is perfected by your grip around ${his} tiny middle.`);
-					VCheck.Anal(1, eventSlave);
+					t.push(VCheck.Anal(1, eventSlave));
 				} else {
 					t.push(`vaginal. Your control over the pace is perfected by your grip around ${his} tiny middle.`);
-					VCheck.Vaginal(1, eventSlave);
+					t.push(VCheck.Vaginal(1, eventSlave));
 				}
 				t.push(`Finally, ${he} goes down on ${hasAllLimbs(eventSlave) ? "all fours" : "the floor"} for a hard`);
 				if (canDoVaginal(eventSlave)) {
 					t.push(`pounding, doggy style, losing ${himself} in the intense penetration as you use your hold around ${him} to give it to ${him} even harder.`);
-					VCheck.Vaginal(1, eventSlave);
+					t.push(VCheck.Vaginal(1, eventSlave));
 				} else {
 					t.push(`buttfuck, doggy style, losing ${himself} in the intense anal as you use your hold around ${him} to give it to ${him} even harder.`);
-					VCheck.Anal(1, eventSlave);
+					t.push(VCheck.Anal(1, eventSlave));
 				}
 				t.push(`By the end ${he}'s tired but <span class="mediumaquamarine">confident in ${his} sexual uniqueness.</span>`);
 			}
@@ -145,9 +145,9 @@ App.Events.RESSDevotedWaist = class RESSDevotedWaist extends App.Events.BaseEven
 			if (canDoAnal(eventSlave) || canDoVaginal(eventSlave)) {
 				t.push(`${eventSlave.slaveName}'s gown allows you to take ${him} in a ${eventSlave.belly >= 5000 ? "tight" : "close"} lotus position on the cleared table, face to face.`);
 				if (canDoVaginal(eventSlave)) {
-					VCheck.Vaginal(1, eventSlave);
+					t.push(VCheck.Vaginal(1, eventSlave));
 				} else {
-					VCheck.Anal(1, eventSlave);
+					t.push(VCheck.Anal(1, eventSlave));
 				}
 			} else {
 				t.push(`you and ${eventSlave.slaveName} enjoy the ${canSee(eventSlave) ? "sights" : "atmosphere"} while fooling around. While you'd love to use ${him}, ${his} chastity keeps you at bay.`);
diff --git a/src/events/RESS/frighteningDick.js b/src/events/RESS/frighteningDick.js
index 1b976c93c4288cc4d10c374fd7dce9c974f39769..d8e44b8aa08da94bae98d58900ddb6937e503aa7 100644
--- a/src/events/RESS/frighteningDick.js
+++ b/src/events/RESS/frighteningDick.js
@@ -11,7 +11,7 @@ App.Events.RESSFrighteningDick = class RESSFrighteningDick extends App.Events.Ba
 			[ // single event slave
 				s => s.fetish !== "mindbroken",
 				hasAnyArms,
-				hasAnyLegs,
+				canMove,
 				canTalk,
 				s => s.assignment === Job.HOUSE,
 				s => s.attrXY <= 35 || s.behavioralFlaw === "hates men" || s.sexualFlaw === "repressed",
@@ -127,10 +127,10 @@ App.Events.RESSFrighteningDick = class RESSFrighteningDick extends App.Events.Ba
 			t.push(`You tell ${him} not to worry, because you're still pretty wet from the last slave you fucked, so this shouldn't hurt too much. Then you ram your cock`);
 			if (eventSlave.vagina > 0) {
 				t.push(`inside ${him}.`);
-				VCheck.Vaginal(1, eventSlave);
+				t.push(VCheck.Vaginal(1, eventSlave));
 			} else {
 				t.push(`up ${his} spasming ass.`);
-				VCheck.Anal(1, eventSlave);
+				t.push(VCheck.Anal(1, eventSlave));
 			}
 			t.push(`${He} whines and bucks, but ${he}'s entirely at your mercy. ${He} doesn't like dicks, and to go by ${his} facial expression as you piston in and out of ${him}, this experience isn't going to make ${him} reconsider. When you fill ${him} with cum, pull out, and let ${him} retreat to clean ${himself} up, ${he}'s relieved to go.`);
 
diff --git a/src/events/RESS/imScared.js b/src/events/RESS/imScared.js
index 0277a821fd4c4a19f28d2c4b5c46bf0fbe7f488f..8df132ff2c21793c445e651a2e4250edd2d23297 100644
--- a/src/events/RESS/imScared.js
+++ b/src/events/RESS/imScared.js
@@ -8,7 +8,7 @@ App.Events.RESSImScared = class RESSImScared extends App.Events.BaseEvent {
 			[ // single event slave
 				s => s.fetish !== "mindbroken",
 				hasAnyArms,
-				hasAnyLegs,
+				canMove,
 				canTalk,
 				s => s.assignment !== Job.QUARTER,
 				s => s.trust < -50,
@@ -111,7 +111,7 @@ App.Events.RESSImScared = class RESSImScared extends App.Events.BaseEvent {
 				t.push(`against your big ${PC.boobsImplant !== 0 ? "firm" : "soft"} breasts,`);
 			} else if (V.PC.boobs >= 300) {
 				t.push(`against your soft breasts,`);
-			} else if (V.PC.title == 0) {
+			} else if (V.PC.title === 0) {
 				t.push(`against your flat chest,`);
 			} else {
 				t.push(`into your shoulder,`);
@@ -156,7 +156,7 @@ App.Events.RESSImScared = class RESSImScared extends App.Events.BaseEvent {
 			} else {
 				t.push(`drained of ${his} milk, you tell ${eventSlave.slaveName} to pay close attention. ${He} ${watchListenFreezes} in horror as the slave's gigantic breasts are massaged and squeezed, while the milkers attached to ${his} absurd nipples tug and pull with industrial power. ${He} can't ${canSee(eventSlave) ? "look away; the orbs of jiggling flesh being manipulated are each as big as the slave's torso" : "shut out the sound of those enormous jiggling orbs of flesh being so roughly manipulated"}. It's good for milk production to drain slaves completely on occasion, and the machine doesn't stop until every drop is wrung from both udders.`);
 				if (!canSee(eventSlave)) {
-						t.push(`You force the horrified slave's ${hasBothArms(eventSlave) ? "hands" : "hand"} onto its breasts so that ${he} may get a good sense of just how big and swollen they are and how hard the milker is pulling at them.`);
+					t.push(`You force the horrified slave's ${hasBothArms(eventSlave) ? "hands" : "hand"} onto its breasts so that ${he} may get a good sense of just how big and swollen they are and how hard the milker is pulling at them.`);
 				}
 				t.push(`You run a hand over to ${eventSlave.slaveName}'s own breast and, teasing ${his} nipple, tell ${him} that unless ${he} wants to experience that twice a day,`);
 			}
diff --git a/src/events/RESS/kitchenMolestation.js b/src/events/RESS/kitchenMolestation.js
new file mode 100644
index 0000000000000000000000000000000000000000..949d5111aa6f5c196830fa7ca3e5651b67ac33d3
--- /dev/null
+++ b/src/events/RESS/kitchenMolestation.js
@@ -0,0 +1,215 @@
+App.Events.RESSKitchenMolestation = class RESSKitchenMolestation extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [
+			() => V.cockFeeder > 0,
+			() => V.universalRulesConsent === 0
+		];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.fetish !== "mindbroken",
+				hasAnyArms,
+				canWalk,
+				canTalk,
+				s => s.devotion > 50,
+				s => s.fetish === "sadist" || s.fetish === "dom" || s.energy > 95,
+				s => canPenetrate(s) || s.dick === 0
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, him, himself
+		} = getPronouns(eventSlave);
+		const {
+			HeU, heU, hisU, himU, himselfU
+		} = getNonlocalPronouns(V.seeDicks).appendSuffix('U');
+		const belly = bellyAdjective(eventSlave);
+		const targetJobs = ["be a servant", "be a subordinate slave", "get milked", "learn in the schoolroom", "please you", "rest in the spa", "rest", "serve in the club", "serve the public", "take classes", "whore", "work a glory hole", "work as a servant", "work in the brothel"];
+
+		/** @type {App.Entity.PlayerState} */
+		let PC = V.PC;
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, eventSlave, "no clothing");
+
+		let t = [];
+
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`is a horny bitch, and ${he} isn't particularly picky about how ${he} gets off. Since other slaves are not allowed to resist ${his} little molestations, ${he}'s taken to haunting the kitchen around mealtimes. Since everyone has to suck their meals out of the phallic food dispensers, every slave has to spend some minutes of every day in the kitchen with their face to the wall and their rear defenseless. ${eventSlave.slaveName} does ${his} best to arrive as early as possible and eat as rapidly as possible so ${he} can then play with anyone who's slower to finish.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		t.push(`You decide to stop by to see ${his} method at work. By the time you arrive, ${he}'s already eaten and orgasmed at least once. As you spectate, ${he}`);
+		if (canPenetrate(eventSlave) && eventSlave.prostate > 0) {
+			t.push(`pushes a couple of fingers up ${his} own ass to use prostate stimulation to force ${himself} hard again, and once this is accomplished, happily turns to select a slow eater to fuck.`);
+		} else {
+			t.push(`languidly plays with ${himself}, running ${his} ${hasBothArms(eventSlave) ? "hands" : "hand"} over the various vulnerable butts before picking a victim to roughly finger fuck.`);
+		}
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			new App.Events.Result(`Improve on ${his} abusive little game`, improve),
+			canDoAnal(eventSlave)
+				? new App.Events.Result("The rule about consent works both ways", turnTables, analVirginWarning())
+				: new App.Events.Result()
+		]);
+
+		function improve() {
+			t = [];
+			let virgins, chaste, anusOnly, doubleAnal, pussyOnly, doubleVaginal, subLove;
+
+			t.push(`You leave for the moment, but appear at the next mealtime before even ${he} does. You shut off all the phallic feeders but one, and make an announcement. You decree that just for this one meal, ${eventSlave.slaveName} is to lie in front of the one functional feeder, ${canPenetrate(eventSlave) ? `${his} cock in the air` : `with a dildo jutting up from ${his} crotch`} in order to eat, each slave must ride ${eventSlave.slaveName} for as long as it takes to suck down their ${eventSlave.belly >= 5000 ? `meal (given the ${belly} mass jutting from ${his} middle, it should be quite the sight)` : "meal"}.`);
+			t.push(`${eventSlave.slaveName} gapes at you openmouthed for a long moment, looking like ${he} wants to <span class="hotpink">declaim a speech of thanks,</span> but you cut ${him} off by pointing peremptorily at ${his} place; ${he} almost runs over, ${his} <span class="mediumaquamarine">trust in your whim</span> nearly absolute. But the true shape of your plan isn't apparent yet. When the first slave seats ${himselfU} on ${eventSlave.slaveName} and starts sucking off the dispenser dildo, you crouch behind ${himU} and insert yourself as well; the bitch is now airtight. ${HeU} gags and splutters with the discomfort but keeps working away until ${heU} gets ${hisU} meal down and struggles off the three phalluses ${heU} has in ${himU}. The next in line gets to it with some trepidation: and so it goes, slave by slave.`);
+
+			V.slaves.forEach(function(s) {
+				if (targetJobs.includes(s.assignment) && hasAnyLegs(s) && s.relationship !== -3 && s.ID !== eventSlave.ID) {
+					if (canDoAnal(s) && canDoVaginal(s)) {
+						if (s.anus === 0 && s.vagina === 0) {
+							virgins = true;
+						} else if (s.vagina === 0) {
+							virgins = true;
+							anusOnly = true;
+							seX(s, "anal", eventSlave, "penetrative");
+							seX(s, "anal", PC, "penetrative");
+							if (canImpreg(s, PC)) {
+								t.push(knockMeUp(s, 5, 1, -1, true));
+							}
+							if (canImpreg(s, eventSlave)) {
+								t.push(knockMeUp(s, 5, 1, eventSlave.ID, true));
+							}
+							if (s.anus === 1) {
+								s.trust -= 2;
+								doubleAnal = true;
+							}
+						} else if (s.anus === 0) {
+							virgins = true;
+							pussyOnly = true;
+							seX(s, "vaginal", eventSlave, "penetrative");
+							seX(s, "vaginal", PC, "penetrative");
+							if (canImpreg(s, PC)) {
+								t.push(knockMeUp(s, 5, 0, -1, true));
+							}
+							if (canImpreg(s, eventSlave)) {
+								t.push(knockMeUp(s, 5, 0, eventSlave.ID, true));
+							}
+							if (s.vagina === 1) {
+								s.trust -= 2;
+								doubleVaginal = true;
+							}
+						} else {
+							seX(s, "anal", eventSlave, "penetrative");
+							seX(s, "vaginal", eventSlave, "penetrative");
+							if (canImpreg(s, PC)) {
+								t.push(knockMeUp(s, 5, 2, -1, true));
+							}
+							if (canImpreg(s, eventSlave)) {
+								t.push(knockMeUp(s, 5, 1, eventSlave.ID, true));
+							}
+						}
+					} else if (canDoVaginal(s)) {
+						pussyOnly = true;
+						seX(s, "vaginal", eventSlave, "penetrative");
+						seX(s, "vaginal", PC, "penetrative");
+						if (canImpreg(s, PC)) {
+							t.push(knockMeUp(s, 5, 0, -1, true));
+						}
+						if (canImpreg(s, eventSlave)) {
+							t.push(knockMeUp(s, 5, 0, eventSlave.ID, true));
+						}
+						if (s.vagina === 1) {
+							s.trust -= 2;
+							doubleVaginal = true;
+						}
+					} else if (canDoAnal(s)) {
+						anusOnly = true;
+						seX(s, "anal", eventSlave, "penetrative");
+						seX(s, "anal", PC, "penetrative");
+						if (canImpreg(s, PC)) {
+							t.push(knockMeUp(s, 5, 1, -1, true));
+						}
+						if (canImpreg(s, eventSlave)) {
+							t.push(knockMeUp(s, 5, 1, eventSlave.ID, true));
+						}
+						if (s.anus === 1) {
+							s.trust -= 2;
+							doubleAnal = true;
+						}
+					} else {
+						chaste = true;
+					}
+					if (s.fetishKnown === 1 && s.fetish === "submissive") {
+						s.devotion++;
+						subLove = true;
+					}
+				}
+			});
+			if (virgins) {
+				t.push(`You let your virgins ${chaste ? "and chaste slaves" : ""} hold their thighs tight together for a little frottage rather than deflowering their holes like this.`);
+			} else if (chaste) {
+				t.push(`Your chaste slaves hold their thighs tight together for a little frottage should they lack the ability to accommodate.`);
+			}
+			if (anusOnly) {
+				t.push(`Those slaves without pussies are forced to take both you and ${eventSlave.slaveName} up the butt at once.`);
+				if (doubleAnal) {
+					t.push(`Experienced assholes can take the strain just fine, but your tighter-assed slaves are <span class="gold">frightened</span> by the anal pain they suffer.`);
+				}
+			}
+			if (pussyOnly) {
+				t.push(`Slaves with off-limit assholes quickly find both you and ${eventSlave.slaveName} delving the depths of their cunts.`);
+				if (doubleVaginal) {
+					t.push(`Experienced sluts can take the double penetration just fine, but your tighter slaves are <span class="gold">frightened</span> by the amount of stretching they are forced to undergo.`);
+				}
+			}
+			if (subLove) {
+				t.push(`Your subs on the other hand think this is <span class="hotpink">a meal worth remembering.</span>`);
+			}
+
+			eventSlave.devotion += 4;
+			eventSlave.trust += 4;
+			return t;
+		}
+
+		function turnTables() {
+			t = [];
+
+			t.push(`You tell ${eventSlave.slaveName} to get up on the kitchen counter and spread ${his} legs. ${He} catches something in the tone of your voice and looks frightened, but obeys. You then make several conversational observations, as though for no particular reason, to the rest of the slaves. First, you point out, the consent rule works for everyone: they, hypothetically, would not need to ask ${eventSlave.slaveName} ${his} permission to fuck ${his} ass, just like ${he} doesn't have to ask their permission to molest them during meals. (At this ${eventSlave.slaveName}'s fear deepens into obvious <span class="gold">terror.</span>) Second, you have decided ${eventSlave.slaveName} will not be getting down off the counter until everyone's done with their meals — and anything else they wish to do in the kitchen. And third, you conclude, any number of large strap-ons and dildos can be found in the kitchen cabinets. There is a general rush for these; you tell ${eventSlave.slaveName}, whose`);
+			if (eventSlave.lips > 40) {
+				t.push(`bimbo`);
+			} else if (eventSlave.lips > 20) {
+				t.push(`big`);
+			} else if (eventSlave.lips > 10) {
+				t.push(`soft`);
+			}
+			t.push(`lips are quivering, to come see you after ${he}'s done here. About an hour later, ${he} hobbles into your office, and you tell ${him} to show you ${his} anus. ${His} longtime targets for mealtime molestation were not merciful; they weren't stupid enough to damage ${him}, but that's one well-gaped butthole. You fuck it anyway, and ${he}'s too tired and desensitized to care. Your less trusting slaves carefully consider the rules, and realize that there's a <span class="mediumaquamarine">built-in mechanism for correction:</span> if anyone gets too rapey, they can rape them right back.`);
+			t.push(VCheck.Anal(20, eventSlave));
+
+			if (canGetPregnant(eventSlave) && eventSlave.mpreg === 1) {
+				const pregSources = V.slaves.filter(s => canImpreg(eventSlave, s) && targetJobs.includes(s.assignment) && s.ID !== eventSlave.ID);
+				if (pregSources.length > 0) {
+					t.push(knockMeUp(eventSlave, 50, 1, pregSources.pluck().ID, true));
+				}
+			}
+			V.slaves.forEach(function(s) { if (s.trust < 50 && targetJobs.includes(s.assignment) && s.ID !== eventSlave.ID) { s.trust += 4; } });
+			if (eventSlave.anus === 1) {
+				t.push(`Poor ${eventSlave.slaveName}'s butthole <span class="lime">isn't quite the same</span> afterward.`);
+				eventSlave.anus += 1;
+			}
+			eventSlave.trust -= 5;
+			return t;
+		}
+
+		function analVirginWarning() {
+			if (eventSlave.anus === 0) {
+				return `This option will take ${his} anal virginity`;
+			}
+		}
+	}
+};
diff --git a/src/events/RESS/obedientAddict.js b/src/events/RESS/obedientAddict.js
index 2af196656457f45cdf56afb537ede5eb1be00480..a29fd53a586abd4b2075e0bace36f303ff37ff76 100644
--- a/src/events/RESS/obedientAddict.js
+++ b/src/events/RESS/obedientAddict.js
@@ -100,13 +100,13 @@ App.Events.RESSObedientAddict = class RESSObedientAddict extends App.Events.Base
 				t.push(`${He} indicates through ${his} weeping that ${he} can't climax without the drugs.`);
 			}
 			t.push(`You pass ${him} the pill, and ${he} continues to weep inconsolably, apologizing all the while, until the drug takes away ${his} ability to care about anything but getting fucked. When you finish and extract ${PC.dick === 0 ? `yourself from between ${his} legs` : `your cock from ${his} well-used hole`}, though, you think you can detect a deep sadness in ${his} eyes that it cannot reach.`);
-
-			eventSlave.devotion += 4;
 			if (canDoVaginal(eventSlave)) {
-				VCheck.Vaginal(1, eventSlave);
+				t.push(VCheck.Vaginal(1, eventSlave));
 			} else {
-				VCheck.Anal(1, eventSlave);
+				t.push(VCheck.Anal(1, eventSlave));
 			}
+
+			eventSlave.devotion += 4;
 			return t;
 		}
 
diff --git a/src/events/RESS/obedientGirlish.js b/src/events/RESS/obedientGirlish.js
index 67e148be597c71fc3e2c33961528bdb82907c648..5900257f1235057431b562b56ebbc8d16dea5471 100644
--- a/src/events/RESS/obedientGirlish.js
+++ b/src/events/RESS/obedientGirlish.js
@@ -106,12 +106,12 @@ App.Events.RESSObedientGirlish = class RESSObedientGirlish extends App.Events.Ba
 			} else {
 				t.push(`${he}'s wet and ready and moans happily as you enter ${him}.`);
 			}
-			t.push(`There's no hesitation or fear at all on ${his} face when ${he} ${canSee(eventSlave) ? "sees" : "notices"} you're returning ${him} to your penthouse; <span class="mediumaquamarine">${his} trust in you has increased.</span>`);
 			if (canDoVaginal(eventSlave)) {
-				VCheck.Vaginal(1, eventSlave);
+				t.push(VCheck.Vaginal(1, eventSlave));
 			} else {
-				VCheck.Anal(1, eventSlave);
+				t.push(VCheck.Anal(1, eventSlave));
 			}
+			t.push(`There's no hesitation or fear at all on ${his} face when ${he} ${canSee(eventSlave) ? "sees" : "notices"} you're returning ${him} to your penthouse; <span class="mediumaquamarine">${his} trust in you has increased.</span>`);
 
 			eventSlave.trust += 4;
 			return t;
@@ -142,8 +142,8 @@ App.Events.RESSObedientGirlish = class RESSObedientGirlish extends App.Events.Ba
 				t.push(`lie face-down on the couch${PC.dick === 0 ? " while you don a strap-on" : ""}.`);
 			}
 			t.push(`${He} does doubtfully, only realizing what you intend when ${he} feels ${PC.dick === 0 ? "the strap-on" : "your dickhead"} forcing its way between ${his} narrow buttcheeks. ${He} whimpers and moans ${eventSlave.belly < 300000 ? "into the couch" : ""} as you roughly sodomize ${him}. It's true, ${he}'s pretty androgynous from this angle, especially while ${he} takes it up the butthole. <span class="hotpink">${He} has become more submissive to you,</span> but there's <span class="gold">some fear there, too.</span>`);
+			t.push(VCheck.Anal(1, eventSlave));
 
-			VCheck.Anal(1, eventSlave);
 			eventSlave.trust -= 2;
 			eventSlave.devotion += 4;
 			return t;
diff --git a/src/events/RESS/obedientShemale.js b/src/events/RESS/obedientShemale.js
index 54dc9773fb94f60f445836424168575265e8e909..eb7e880c5c498cf7a87b223d93ffe9e6235b4f6b 100644
--- a/src/events/RESS/obedientShemale.js
+++ b/src/events/RESS/obedientShemale.js
@@ -28,7 +28,7 @@ App.Events.RESSObedientShemale = class RESSObedientShemale extends App.Events.Ba
 			He, he, His, his, him, himself, girl
 		} = getPronouns(eventSlave);
 		const {title: Master} = getEnunciation(eventSlave);
-		const anusDesc = ["tight", "loose", "gaping"][Math.ceil(Math.clamp(eventSlave.anus, 1, 3)) - 1];
+		const anusDesc = eventSlave.anus > 2 ? "gaping" : (eventSlave.anus > 1 ? "loose" : "tight");
 
 		/** @type {App.Entity.PlayerState} */
 		let PC = V.PC;
@@ -76,9 +76,9 @@ App.Events.RESSObedientShemale = class RESSObedientShemale extends App.Events.Ba
 				t.push("shakes with release");
 			}
 			t.push(`after just a few strokes of your ${PC.dick === 0 ? "strap-on" : "cock"} up ${his} butt. ${His} ${anusDesc} ass spasms and tightens with ${his} climax${PC.dick !== 0 ? ", a wonderful sensation" : ""}. You aren't finished with ${him}, but ${he} rubs ${himself} languidly and enjoys the hard anal reaming more than ${he} ever has previously. ${His} devotion to you <span class="hotpink">has increased.</span>`);
+			t.push(VCheck.Anal(1, eventSlave));
 
 			eventSlave.devotion += 4;
-			VCheck.Anal(1, eventSlave);
 			return t;
 		}
 
@@ -86,9 +86,9 @@ App.Events.RESSObedientShemale = class RESSObedientShemale extends App.Events.Ba
 			t = [];
 
 			t.push(`${He} obeys your orders to keep ${his} ${hasBothArms(eventSlave) ? "hands" : "hand"} off ${his} dick, but can't hide ${his} disappointment and frustration. You keep a close watch on ${him}, and buttfuck ${him} every chance you get, teaching ${him} the finer points of taking a ${PC.dick === 0 ? "strap-on" : "dick"} up the butt. You focus entirely on your pleasure, teaching ${him} how to use ${his} ${anusDesc} anal ring to extract orgasms from cocks. This experience was hard for ${him} but has increased ${his} anal skill.`);
+			t.push(VCheck.Anal(9, eventSlave));
 
 			SkillIncrease.Anal(eventSlave, 10);
-			VCheck.Anal(9, eventSlave);
 			return t;
 		}
 
@@ -106,7 +106,7 @@ App.Events.RESSObedientShemale = class RESSObedientShemale extends App.Events.Ba
 				t.push(`Despite being used as an anal slut for a whole week, ${he} was not significantly affected.`);
 			}
 
-			VCheck.Anal(9, eventSlave);
+			t.push(VCheck.Anal(9, eventSlave));
 			return t;
 		}
 	}
diff --git a/src/events/RESS/passingDeclaration.js b/src/events/RESS/passingDeclaration.js
index 4b918926b2c7c114b8b4fb40881332ec85f21a55..9784ff3543611c360f62387a020232d0702413f6 100644
--- a/src/events/RESS/passingDeclaration.js
+++ b/src/events/RESS/passingDeclaration.js
@@ -8,7 +8,7 @@ App.Events.RESSPassingDeclaration = class RESSPassingDeclaration extends App.Eve
 			[ // single event slave
 				s => s.fetish !== "mindbroken",
 				hasAnyArms,
-				hasAnyLegs,
+				canWalk,
 				canTalk,
 				s => s.devotion > 50,
 				s => s.trust > 50,
diff --git a/src/events/RESS/serveThePublicDevoted.js b/src/events/RESS/serveThePublicDevoted.js
new file mode 100644
index 0000000000000000000000000000000000000000..83555403713fdeccd2b56c61dbbe0e88cddcfebb
--- /dev/null
+++ b/src/events/RESS/serveThePublicDevoted.js
@@ -0,0 +1,306 @@
+App.Events.RESSServeThePublicDevoted = class RESSServeThePublicDevoted extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.assignment === Job.PUBLIC,
+				s => canDoAnal(s) || canDoVaginal(s),
+				s => s.devotion > 50,
+				s => s.fetish !== "mindbroken",
+				hasAnyArms,
+				canTalk,
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, him, himself
+		} = getPronouns(eventSlave);
+		const belly = bellyAdjective(eventSlave);
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, eventSlave);
+
+		let t = [];
+
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`is a real public servant. This morning, ${he} rose early, did ${his} chores, and looked after ${himself}.`);
+		if (canWalk(eventSlave) || (canMove(eventSlave) && eventSlave.rules.mobility === "permissive")) {
+			t.push(`${He} heads out`);
+		} else {
+			t.push(`${He}'s being escorted`);
+		}
+		t.push(`past your desk toward the arcology's lower floors to offer ${himself} freely to everyone ${he} meets.`);
+
+		App.Events.addParagraph(node, t);
+		t = [];
+		t.push(`As ${he} goes, you notice that`);
+		if (eventSlave.chastityPenis === 1) {
+			t.push(`precum is starting to dribble out of ${his} chastity cage.`);
+		} else if ((eventSlave.dick > 0) && canAchieveErection(eventSlave)) {
+			t.push(`${his} cock is so hard it's standing out, perfectly straight.`);
+		} else if ((eventSlave.dick > 0) && (eventSlave.balls > 0) && !canAchieveErection(eventSlave)) {
+			t.push(`${his} semi-hard cock is dribbling precum, a sure sign of a bitch who wants to be buttfucked.`);
+		} else if ((eventSlave.dick > 0) && !canAchieveErection(eventSlave)) {
+			t.push(`${his} pathetically soft cock is streaming precum, a sure sign of a bitch who wants to be buttfucked.`);
+		} else if ((eventSlave.clit > 0)) {
+			t.push(`${his} big clit is obviously engorged.`);
+		} else if ((eventSlave.vaginaLube > 0)) {
+			t.push(`${his} pussy is already soaking wet.`);
+		} else {
+			t.push(`${his} cheeks are flushed and ${he}'s panting a little.`);
+		}
+		t.push(`Apparently, ${he} can't wait to`);
+		if (eventSlave.fetish === "submissive") {
+			t.push(`play the submissive.`);
+		} else if ((eventSlave.fetish === "humiliation")) {
+			t.push(`be publicly degraded.`);
+		} else if ((eventSlave.fetish === "masochist")) {
+			t.push(`be roughly handled.`);
+		} else if ((eventSlave.fetish === "dom")) {
+			t.push(`play the dom.`);
+		} else if ((eventSlave.fetish === "sadist")) {
+			t.push(`find a willing masochist.`);
+		} else if ((eventSlave.fetish === "pregnancy") && (eventSlave.preg > eventSlave.pregData.normalBirth / 4)) {
+			t.push(`have ${his} pregnancy fondled.`);
+		} else if ((eventSlave.fetish === "pregnancy")) {
+			t.push(`pretend to get knocked up.`);
+		} else if ((eventSlave.fetish === "boobs")) {
+			t.push(`have ${his} tits fondled.`);
+		} else if ((eventSlave.fetish === "cumslut")) {
+			t.push(`gorge ${himself} on cum.`);
+		} else if ((eventSlave.fetish === "buttslut")) {
+			t.push(`spend all day getting pounded in the ass.`);
+		} else if ((eventSlave.attrXX > 65)) {
+			t.push(`enjoy all the women.`);
+		} else if ((eventSlave.attrXY > 65)) {
+			t.push(`enjoy all the men.`);
+		} else {
+			t.push(`be a complete slut.`);
+		}
+		t.push(`If you went with ${him}, you could certainly show off a little.`);
+
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			(canDoAnal(eventSlave))
+				? new App.Events.Result(`Share ${his} body with the public`, share, virginityWarning())
+				: new App.Events.Result(),
+			new App.Events.Result(`Give ${him} some aftercare`, aftercare, virginityWarning()),
+			new App.Events.Result(`Chat about ${his} day`, chat),
+		]);
+
+
+		function virginityWarning() {
+			if (canDoVaginal(eventSlave) && (eventSlave.vagina === 0)) {
+				return `This option will take ${his} virginity`;
+			} else if (canDoAnal(eventSlave) && (eventSlave.anus === 0)) {
+				return `This option will take ${his} anal virginity`;
+			} else {
+				return null;
+			}
+		}
+
+
+		function share() {
+			let frag = document.createDocumentFragment();
+			t = [];
+			if (canWalk(eventSlave) || (canMove(eventSlave) && eventSlave.rules.mobility === "permissive")) {
+				t.push(`${eventSlave.slaveName} is surprised to find you walking beside ${him}, but obediently falls in behind you as a proper slave should.`);
+			} else if (canMove(eventSlave)) {
+				t.push(`${eventSlave.slaveName} is surprised to find you swapping places with ${his} assistant to support ${his} weight.`);
+			} else { // strength/height check here!
+				t.push(`You scoop ${eventSlave.slaveName}'s ${isAmputee(eventSlave) ? "limbless" : "immobile"} form up and carry ${him} out.`);
+			}
+			t.push(`You take ${him} to a lovely open balcony and seat yourself on a bench surrounded by the lush greenery and flowing water of ${V.arcologies[0].name}'s food systems. You pull ${his} ass down onto your`);
+			if (V.PC.dick === 0) {
+				t.push(`strap-on`);
+			} else {
+				t.push(`rigid cock`);
+			}
+			t.push(`so ${he} can offer everything else to the public.`);
+			if (V.PC.vagina !== -1 && V.PC.dick !== 0) {
+				t.push(`They know not to presume to use the pussy located beneath your thrusting cock.`);
+			}
+			t.push(VCheck.Anal(5, eventSlave));
+
+			App.Events.addParagraph(frag, t);
+			t = [];
+
+			if (canDoVaginal(eventSlave)) {
+				t.push(`For several hours, citizens come and go, most choosing to fuck ${his} wet and available pussy. You climax repeatedly from the`);
+				if (V.PC.dick === 0) {
+					t.push(`titillating nature of`);
+				} else {
+					t.push(`extra fullness of ${his} butt during`);
+				}
+				t.push(`double penetration, and by the time you're finished ${he}'s dripping ejaculate from both ${his} holes.`);
+			} else if (eventSlave.belly >= 120000) {
+				t.push(`Since`);
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${he}'s so enormously pregnant that ${his} ${belly} stomach blocks ${his} crotch`);
+				} else {
+					t.push(`${his} ${belly} stomach is so massive that is blocks ${his} crotch`);
+				}
+				t.push(`and you're wearing ${his} backdoor around your`);
+				t.push(V.PC.dick === 0 ? "strap-on," : "cock,");
+				t.push(`${his} mouth is all that's left. ${He} gives so many blowjobs by the time you're finished that ${his} face, hair, chest and belly are liberally spattered with cum.`);
+			} else if ((eventSlave.chastityVagina)) {
+				t.push(`Since ${he}'s wearing a chastity belt and you're wearing ${his} backdoor around your`);
+				t.push(V.PC.dick === 0 ? "strap-on," : "cock,");
+				t.push(`${his} mouth is all that's left. ${He} gives so many blowjobs by the time you're finished that ${his} face, hair, chest and`);
+				if (eventSlave.belly >= 5000) {
+					if (eventSlave.bellyPreg >= 3000) {
+						t.push(`pregnant`);
+					} else {
+						t.push(belly);
+					}
+					t.push(`belly`);
+				} else {
+					t.push(`even stomach`);
+				}
+				t.push(`are liberally spattered with cum.`);
+			} else {
+				t.push(`With your cock pumping ${his} butt as much as you can manage with ${him} seated in your lap, ${his}`);
+				if (eventSlave.dick !== 0 && eventSlave.belly >= 5000) {
+					t.push(`dick flops up and down against the bottom of ${his} rounded stomach,`);
+				} else if (eventSlave.dick !== 0) {
+					t.push(`dick flops up and down,`);
+				} else {
+					t.push(`tiny front hole begins to glisten with the promise of a messy little orgasm,`);
+				}
+				t.push(`lewdly advertising ${his} sexual availability. ${He} gives so many blowjobs by the time you're finished that ${his} face, hair, chest and`);
+				if (eventSlave.belly >= 5000) {
+					if (eventSlave.bellyPreg >= 3000) {
+						t.push(`pregnant`);
+					} else {
+						t.push(belly);
+					}
+					t.push(`belly`);
+				} else {
+					t.push(`even stomach`);
+				}
+				t.push(`are liberally spattered with cum.`);
+			}
+			t.push(`The public is sure to remember this spectacle; <span class="green">your reputation has increased.</span>`);
+			repX(500, "event", eventSlave);
+			if (canDoVaginal(eventSlave)) {
+				seX(eventSlave, "vaginal", "public", "penetrative", 5);
+				if (eventSlave.vagina === 0) {
+					t.push(`They have also <span class="lime">broken in ${eventSlave.slaveName}'s virgin pussy.</span>`);
+					eventSlave.vagina = 1;
+				}
+				if (canGetPregnant(eventSlave) && eventSlave.eggType === "human") {
+					t.push(knockMeUp(eventSlave, 10, 0, -2));
+				}
+			} else {
+				seX(eventSlave, "oral", "public", "penetrative", 5);
+			}
+			App.Events.addParagraph(frag, t);
+			t = [];
+			return [frag];
+		}
+
+		function aftercare() {
+			t = [];
+			t.push(`${eventSlave.slaveName} returns many hours later. ${He} carefully took breaks for ${his} own safety,`);
+			if (eventSlave.preg > eventSlave.pregData.normalBirth / 1.33) {
+				t.push(`especially so given ${his} advanced pregnancy,`);
+			}
+			t.push(`and cleaned ${himself} periodically, so there isn't much evidence, but ${he}'s obviously bone tired. When you meet ${him} at the entrance to your penthouse ${he}'s surprised to`);
+			t.push(canSee(eventSlave) ? "see" : "find");
+			t.push(`you, but ${he} gives you a little smile anyway. You give ${him} a strong massage and put ${him} to bed. Afterward you turn to be about your business, but ${he} wordlessly offers ${himself} to you as you do.`);
+			if (canDoVaginal(eventSlave)) {
+				if (eventSlave.vagina === 0) {
+					t.push(`${His} virgin pussy is certainly tempting.`);
+				} else if ((eventSlave.vagina === 1)) {
+					t.push(`${His} tight pussy is certainly tempting.`);
+				} else if ((eventSlave.vagina === 2)) {
+					t.push(`${His} soft pussy is certainly tempting.`);
+				} else if ((eventSlave.labia > 0)) {
+					t.push(`${His} generous petals are certainly tempting.`);
+				} else if ((eventSlave.clit > 0)) {
+					t.push(`${His} prominent clit is certainly eye-catching.`);
+				} else if ((eventSlave.vagina === -1)) {
+					t.push(`${His} relaxed anus is certainly inviting.`);
+				} else {
+					t.push(`${His} capacious pussy is certainly inviting.`);
+				}
+			} else {
+				if (eventSlave.anus === 0) {
+					t.push(`${His} virgin asshole is certainly tempting.`);
+				} else if ((eventSlave.anus === 1)) {
+					t.push(`${His} tight asshole is certainly tempting.`);
+				} else if ((eventSlave.anus === 2)) {
+					t.push(`${His} experienced asshole is certainly tempting.`);
+				} else {
+					t.push(`${His} capacious asshole is certainly inviting.`);
+				}
+			}
+			t.push(`${He}'s sore, so you spoon`);
+			if (eventSlave.belly >= 5000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} gravid`);
+				} else {
+					t.push(`${his} rounded`);
+				}
+				t.push(`body`);
+			} else {
+				t.push(him);
+			}
+			t.push(`gently in bed, fucking ${him} slowly to sleep.`);
+			if (canDoVaginal(eventSlave)) {
+				t.push(VCheck.Vaginal(1, eventSlave));
+			} else {
+				t.push(VCheck.Anal(1, eventSlave));
+			}
+			t.push(`${He} falls asleep with a serene expression on ${his} face. <span class="mediumaquamarine">${His} trust in you has increased.</span>`);
+			eventSlave.trust += 4;
+			return t;
+		}
+
+		function chat() {
+			t = [];
+			t.push(`${eventSlave.slaveName} returns many hours later. ${He}'s obviously bone tired. When you meet ${him} at the entrance to your penthouse ${he}'s surprised to`);
+			t.push(canSee(eventSlave) ? "see" : "find");
+			t.push(`you, but ${he} gives you a little smile anyway. You bring ${him} back to your office, and ${he}'s clearly expecting to get fucked, so ${he}'s surprised when you hand ${him} a hot beverage and sit down on the couch with ${him}. ${He} relaxes quickly and chats with you about ${his} day, gossiping about all the private doings of all the prominent citizens who fucked ${his}`);
+			if ((eventSlave.vagina > 0) && canDoVaginal(eventSlave)) {
+				t.push(`cunt`);
+			} else if ((eventSlave.anus > 0) && canDoAnal(eventSlave)) {
+				t.push(`asshole`);
+			} else {
+				t.push(`mouth`);
+			}
+			t.push(`today.`);
+			if (eventSlave.intelligence + eventSlave.intelligenceImplant > 50) {
+				t.push(`${He}'s witty and holds up ${his} end of the conversation without straying from ${his} role as a slave.`);
+			} else if ((eventSlave.intelligence + eventSlave.intelligenceImplant >= -15)) {
+				t.push(`${He} has a few juicy items to share, and even while gossiping, ${he}'s mindful of ${his} role as a slave.`);
+			} else {
+				t.push(`${He} may be an idiot, but ${his} babble is amusing enough.`);
+			}
+			t.push(`Time flies, and when you finally stand up to continue with your evening, ${he} thanks you prettily for listening to ${him}`);
+			if (eventSlave.belly >= 10000) {
+				t.push(`as you help ${his}`);
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`pregnant`);
+				} else {
+					t.push(`heavy`);
+				}
+				t.push(`body off the couch`);
+			}
+			t.push(t.pop() + ".");
+			t.push(`<span class="hotpink">${His} devotion to you has increased.</span>`);
+			eventSlave.devotion += 4;
+			return t;
+		}
+	}
+};
diff --git a/src/events/RESS/slaveOnSlaveClit.js b/src/events/RESS/slaveOnSlaveClit.js
new file mode 100644
index 0000000000000000000000000000000000000000..d40908cca51a7a9b56de496e667d485210cc8e0c
--- /dev/null
+++ b/src/events/RESS/slaveOnSlaveClit.js
@@ -0,0 +1,97 @@
+App.Events.RESSSlaveOnSlaveClit = class RESSSlaveOnSlaveClit extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.fetish !== "mindbroken",
+				s => App.Utils.hasFamilySex(s) || s.rules.release.slaves === 1,
+				s => s.clit > 2
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, him
+		} = getPronouns(eventSlave);
+		const {
+			heU, hisU, himU, himselfU
+		} = getNonlocalPronouns(0).appendSuffix('U');
+
+		/** @type {App.Entity.PlayerState} */
+		let PC = V.PC;
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, eventSlave, "no clothing");
+
+		let t = [];
+
+		t.push(`Through the glass walls of your office, you see `);
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`fucking another slave. Odd, since ${he} doesn't have a penis: it seems the other slave likes ${him} enough to try to make clitoral penetration work. ${eventSlave.slaveName}'s ${V.seeRace === 1 ? eventSlave.race : ""} clit is certainly big enough to make it possible. Since you gave ${him} orders that permit ${him} to fuck your other slaves, ${he}'s been having as much sex with them as ${he} can. The other slave is enjoying ${himselfU} even though the clit in ${hisU} pussy is a little disappointing compared to a real cock.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			new App.Events.Result("The slave taking it has a free anus, so use that", bottom),
+			canDoAnal(eventSlave)
+				? new App.Events.Result("The slave giving it has a free anus, so use that", topAnal, virginWarning("anal"))
+				: new App.Events.Result(),
+			canDoVaginal(eventSlave)
+				? new App.Events.Result("The slave giving it has a free pussy, so use that", topVaginal, virginWarning("vaginal"))
+				: new App.Events.Result()
+		]);
+
+		function bottom() {
+			t = [];
+
+			t.push(`Since the other slave is riding ${eventSlave.slaveName}'s huge clit, it's a trivial matter to ${PC.dick === 0 ? "don a strap-on," : ""} come up behind the fucking slaves, stop the other slave's riding for a moment, and insert yourself into ${hisU} anus. The other slave gasps as your ${PC.dick === 0 ? "strap-on" : "big dick"} complements the clit in ${hisU} pussy. The poor slave rides out the sexual storm as you and ${eventSlave.slaveName} fuck ${himU}. ${eventSlave.slaveName} flirts outrageously with you over the other slave's shoulder whenever ${he} can. <span class="mediumaquamarine">${He} has become more trusting of you.</span>`);
+
+			eventSlave.trust += 4;
+			seX(eventSlave, "penetrative", "slaves", "anal");
+			return t;
+		}
+
+		function topAnal() {
+			t = [];
+
+			t.push(`Since ${eventSlave.slaveName} is on top, it's a trivial matter to ${PC.dick === 0 ? "don a strap-on," : ""} come up behind the fucking slaves, stop ${his} thrusting for a moment, and insert yourself into ${his} anus.`);
+			if (eventSlave.fetish === "buttslut" && eventSlave.fetishKnown === 1) {
+				t.push(`${He} shivers with delight as ${he} feels ${his} anal ring stretch to accommodate your ${PC.dick === 0 ? "strap-on" : "dick"}.`);
+				eventSlave.devotion += 1;
+			}
+			t.push(VCheck.Anal(1, eventSlave));
+			t.push(`Fucking a slave with ${eventSlave.prostate > 0 ? "prostate" : ""} stimulation from your ${PC.dick === 0 ? "phallus" : "cock"} in ${his} ass makes ${him} cum with indecent speed. You let ${him} slide down so ${he} can finish the other slave with ${his} mouth while you continue using ${his} anus. The other slave definitely enjoys ${eventSlave.slaveName}'s moaning into ${hisU} pussy as you use ${eventSlave.slaveName}'s ass. The hard buttfucking ${eventSlave.slaveName} is getting keeps ${his} clit hard all the way through. <span class="hotpink">${His} submission to you has increased.</span>`);
+
+			eventSlave.devotion += 4;
+			seX(eventSlave, "penetrative", "slaves", "anal");
+			return t;
+		}
+
+		function topVaginal() {
+			t = [];
+
+			t.push(`Since ${eventSlave.slaveName} is on top, it's a trivial matter to< ${PC.dick === 0 ? "don a strap-on," : ""} come up behind the fucking slaves, stop ${his} thrusting for a moment, and insert yourself into ${his} pussy. ${He} obediently stops fucking so you can maneuver into ${him}.`);
+			t.push(VCheck.Vaginal(1, eventSlave));
+			t.push(`Having a ${PC.dick === 0 ? "strap-on" : "dick"} in ${his} pussy reduces ${his} ability to use ${his} engorged clit like a penis a little, so the slave beneath ${him} helps ${his} penetration as much as ${heU} can. It's not the most convenient of fucks, but that's to be expected when a ${properMaster()} and two slaves successfully have two separate instances of vaginal intercourse running at once. ${His} orgasm is general and intense. <span class="hotpink">${His} devotion to you has increased.</span>`);
+
+			eventSlave.devotion += 4;
+			seX(eventSlave, "penetrative", "slaves", "vaginal");
+			return t;
+		}
+
+		function virginWarning(type) {
+			if (type === "anal" && eventSlave.anus === 0) {
+				return `This option will take ${his} anal virginity`;
+			} else if (type === "vaginal" && eventSlave.vagina === 0) {
+				return `This option will take ${his} vaginal virginity`;
+			}
+		}
+	}
+};
diff --git a/src/events/RESS/slaveOnSlaveDick.js b/src/events/RESS/slaveOnSlaveDick.js
new file mode 100644
index 0000000000000000000000000000000000000000..435260fe9180a2373a22a705498dc3568f6e1610
--- /dev/null
+++ b/src/events/RESS/slaveOnSlaveDick.js
@@ -0,0 +1,131 @@
+App.Events.RESSSlaveOnSlaveDick = class RESSSlaveOnSlaveDick extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // single event slave
+				s => s.fetish !== "mindbroken",
+				hasAnyArms,
+				hasAnyLegs,
+				canPenetrate,
+				s => App.Utils.hasFamilySex(s) || s.rules.release.slaves === 1
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, him
+		} = getPronouns(eventSlave);
+		const {
+			heU, hisU, himU, himselfU
+		} = getNonlocalPronouns(0).appendSuffix('U');
+
+		/** @type {App.Entity.PlayerState} */
+		let PC = V.PC;
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, eventSlave, "no clothing");
+
+		let t = [];
+
+		t.push(`Through the glass walls of your office, you see`);
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`fucking another slave with ${his}`);
+		if (eventSlave.dick <= 2) {
+			t.push(`small penis.`);
+		} else if (eventSlave.dick <= 4) {
+			t.push(`long cock.`);
+		} else if (eventSlave.dick > 4) {
+			t.push(`huge, horselike phallus.`);
+		}
+		t.push(`Since you gave ${him} orders that permit ${him} to fuck your other slaves, ${he}'s been fucking them whenever ${he} can. The other slave is`);
+		if (eventSlave.dick <= 2) {
+			t.push(`enjoying ${himselfU} even though the ${V.seeRace === 1 ? eventSlave.race : ""} dick in ${hisU} pussy is a little disappointing.`);
+		} else if (eventSlave.dick <= 4) {
+			t.push(`enjoying the sex.`);
+		} else {
+			t.push(`enjoying the big ${V.seeRace === 1 ? eventSlave.race : ""} dick, even if ${heU} does wince from time to time.`);
+		}
+		if (eventSlave.scrotum > 0) {
+			if (eventSlave.balls > 3) {
+				t.push(`As ${eventSlave.slaveName} pounds, ${his} big balls slap against ${his} partner.`);
+			} else if (eventSlave.balls > 1) {
+				t.push(`As ${eventSlave.slaveName} pounds, ${his} balls tighten, preparing to empty themselves.`);
+			}
+		}
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			new App.Events.Result("Fuck the bottom", bottom),
+			canDoAnal(eventSlave)
+				? new App.Events.Result("Fuck the top", topAnal, virginWarning("anal"))
+				: new App.Events.Result(),
+			canDoVaginal(eventSlave)
+				? new App.Events.Result("The slave giving it has a free pussy, so use that", topVaginal, virginWarning("vaginal"))
+				: new App.Events.Result()
+		]);
+
+		function bottom() {
+			t = [];
+
+			t.push(`Since the other slave is riding ${eventSlave.slaveName}'s ${V.seeRace === 1 ? eventSlave.race : ""} dick, it's a trivial matter to ${PC.dick === 0 ? "don a strap-on," : ""} come up behind the fucking slaves, stop the other slave's riding for a moment, and insert yourself into ${hisU} anus. The other slave`);
+			if (eventSlave.dick <= 2) {
+				t.push(`gasps as your ${PC.dick === 0 ? "strap-on complements the small dick" : "big dick complements the small one"} in ${his} pussy.`);
+			} else if (eventSlave.dick < 4) {
+				t.push(`shrieks as ${heU} feels ${hisU} holes stretched by ${PC.dick === 0 ? "a strap-on and a cock" : "two cocks"}.`);
+			} else {
+				t.push(`struggles and begs for mercy as ${hisU} holes are brutally stretched.`);
+			}
+			t.push(`The poor slave rides out the sexual storm as you and ${eventSlave.slaveName} fuck ${PC.vagina !== -1 ? `${himU}, your pussy sliding against the base of ${eventSlave.slaveName}'s thrusting shaft` : `${himU}`}. ${eventSlave.slaveName} flirts outrageously with you over the other slave's shoulder whenever ${he} can. <span class="mediumaquamarine">${He} has become more trusting of you.</span>`);
+
+			eventSlave.trust += 4;
+			seX(eventSlave, "penetrative", "slaves", "anal");
+			seX(PC, "penetrative", "slaves", "anal");
+			return t;
+		}
+
+		function topAnal() {
+			t = [];
+
+			t.push(`Since ${eventSlave.slaveName} is on top, it's a trivial matter to ${PC.dick === 0 ? "don a strap-on," : ""} come up behind the fucking slaves, stop ${his} thrusting for a moment, and penetrate ${his} butthole.`);
+			if (eventSlave.fetish === "buttslut" && eventSlave.fetishKnown === 1) {
+				t.push(`${He} shivers with delight as ${he} feels ${his} anal ring stretch to accommodate your ${PC.dick === 0 ? "strap-on" : "dick"}.`);
+				eventSlave.devotion += 1;
+			}
+			t.push(VCheck.Anal(1, eventSlave));
+			t.push(`Fucking a slave with ${eventSlave.prostate > 0 ? "prostate" : ""} stimulation from your ${PC.dick === 0 ? "phallus" : "dick"} in ${his} ${V.seeRace === 1 ? eventSlave.race : ""} ass makes ${him} cum with indecent speed. You let ${him} slide down so ${he} can finish the other slave with ${his} mouth while you continue using ${his} anus. The other slave definitely enjoys ${eventSlave.slaveName}'s moaning into ${hisU} pussy as you use ${eventSlave.slaveName}'s ass. The hard buttfucking ${eventSlave.slaveName} is getting keeps ${his} dick stiff all the way through. <span class="hotpink">${His} submission to you has increased.</span>`);
+
+			eventSlave.devotion += 4;
+			seX(eventSlave, "penetrative", "slaves", "anal");
+			seX(eventSlave, "anal", PC, "penetrative");
+			return t;
+		}
+
+		function topVaginal() {
+			t = [];
+
+			t.push(`${eventSlave.slaveName}'s hermaphroditic genitalia is a little crammed together; it's busy down there. ${He} obediently stops fucking so you can maneuver into ${him}.`);
+			t.push(VCheck.Vaginal(1, eventSlave));
+			t.push(`Having a ${PC.dick === 0 ? "strap-on" : "dick"} in ${his} pussy reduces ${his} erection a little, so the slave beneath ${him} helps ${his} penetration as much as ${heU} can. It's not the most convenient of fucks, but that's to be expected when a ${properMaster()} and two slaves successfully have two separate instances of vaginal intercourse running at once. ${PC.vagina !== -1 && PC.dick !== 0 ? "You add a third by grabbing a free hand and guiding it to your own pussy; its owner gets the idea and strokes it as best they can." : ""} ${eventSlave.slaveName}'s orgasm is general and intense. <span class="hotpink">${His} devotion to you has increased.</span>`);
+
+			eventSlave.devotion += 4;
+			seX(eventSlave, "penetrative", "slaves", "vaginal");
+			return t;
+		}
+
+		function virginWarning(type) {
+			if (type === "anal" && eventSlave.anus === 0) {
+				return `This option will take ${his} anal virginity`;
+			} else if (type === "vaginal" && eventSlave.vagina === 0) {
+				return `This option will take ${his} vaginal virginity`;
+			}
+		}
+	}
+};
diff --git a/src/events/RESS/tooThinForCumDiet.js b/src/events/RESS/tooThinForCumDiet.js
new file mode 100644
index 0000000000000000000000000000000000000000..c2c416234e03caf2ef8a7f0b07a3549db7ba2027
--- /dev/null
+++ b/src/events/RESS/tooThinForCumDiet.js
@@ -0,0 +1,366 @@
+App.Events.RESSTooThinForCumDiet = class RESSTooThinForCumDiet extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [];
+	}
+
+
+	actorPrerequisites() {
+		return [
+			/** @param {App.Entity.SlaveState} s */
+			[
+				s => s.fetish !== "mindbroken",
+				hasAnyArms,
+				canWalk,
+				canTalk,
+				s => s.dietCum > 0,
+				s => s.diet === "fattening",
+				s => s.trust >= -50,
+				s => s.fetish !== "cumslut",
+				s => s.weight < -30,
+				s => s.health.condition > -80,
+				s => s.behavioralFlaw !== "anorexic",
+				s => s.sexualFlaw !== "self hating",
+				s => (s.fetishStrength <= 60 || s.fetishKnown !== 1),
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [eventSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, His, his, hers, him, himself, girl
+		} = getPronouns(eventSlave);
+		const {title: Master} = getEnunciation(eventSlave);
+
+		V.nextLink = "Next Week";
+		const belly = bellyAdjective(eventSlave);
+
+		App.Events.drawEventArt(node, eventSlave);
+
+		let t = [];
+		t.push(`You are in your office, tending to the tedious business of requisition orders for your penthouse when you see`);
+		t.push(App.UI.DOM.slaveDescriptionDialog(eventSlave));
+		t.push(`appear outside your door. ${He} hovers outside in the hall, peeking ${his} head around the entry-way, looking very unsure about disturbing you. You stop what you're doing and call ${him} in. ${He}'s reticent at first, but then ${he} obediently steps into your office. ${He} is`);
+		if (eventSlave.clothes === "no clothing") {
+			t.push(`forced to remain naked at all times, and the most striking thing about ${his} body is how`);
+			if (eventSlave.belly >= 100000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} ${belly} pregnancy utterly dwarfs ${his} skinny body.`);
+				} else {
+					t.push(`${his} ${belly} distended belly utterly dwarfs ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 15000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} full pregnancy completely dominates ${his} skinny body.`);
+				} else {
+					t.push(`${his} hugely distended belly completely dominates ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 10000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} advanced pregnancy dominates ${his} skinny body.`);
+				} else {
+					t.push(`${his} hugely distended belly dominates ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 5000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`massive ${his} pregnant belly is compared to ${his} skinny body.`);
+				} else {
+					t.push(`massive ${his} distended belly is compared to ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 1500) {
+				if (eventSlave.bellyPreg > 0) {
+					t.push(`noticeable ${his} growing pregnancy is against ${his} skinny body.`);
+				} else {
+					t.push(`noticeable the curve of ${his} belly is against ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 150) {
+				if (eventSlave.bellyPreg > 0) {
+					t.push(`noticeable ${his} early pregnancy is against ${his} skinny body.`);
+				} else {
+					t.push(`noticeable the curve of ${his} implant is against ${his} skinny body.`);
+				}
+			} else {
+				t.push(`skinny ${he} is.`);
+			}
+		} else {
+			t.push(`forced to wear ${eventSlave.clothes} all day, but even through ${his} outfit, it's easy to see how`);
+			if (eventSlave.belly >= 100000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} ${belly} pregnancy utterly dwarfs ${his} skinny body.`);
+				} else {
+					t.push(`${his} ${belly} distended belly utterly dwarfs ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 15000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} heavy pregnancy completely dominates ${his} skinny body.`);
+				} else {
+					t.push(`${his} hugely distended belly completely dominates ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 10000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`${his} advanced pregnancy dominates ${his} skinny body.`);
+				} else {
+					t.push(`${his} hugely distended belly dominates ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 5000) {
+				if (eventSlave.bellyPreg >= 3000) {
+					t.push(`massive ${his} pregnant belly is compared to ${his} skinny body.`);
+				} else {
+					t.push(`massive ${his} distended belly is compared to ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 1500) {
+				if (eventSlave.bellyPreg > 0) {
+					t.push(`noticeable ${his} growing pregnancy is against ${his} skinny body.`);
+				} else {
+					t.push(`noticeable the curve of ${his} belly is against ${his} skinny body.`);
+				}
+			} else if (eventSlave.belly >= 150) {
+				if (eventSlave.bellyPreg > 0) {
+					t.push(`noticeable ${his} early pregnancy is against ${his} skinny body.`);
+				} else {
+					t.push(`noticeable the curve of ${his} implant is against ${his} skinny body.`);
+				}
+			} else {
+				t.push(`impossibly skinny ${he} is.`);
+			}
+		}
+		if (eventSlave.weight > -80) {
+			t.push(`${He}'s not exclusively skin and bones, but ${he}'s close. ${His}`);
+		} else {
+			t.push(`${He}'s so thin that ${he} doesn't look entirely healthy. ${His}`);
+		}
+
+		if (eventSlave.boobs < 600) {
+			if (eventSlave.boobs < 300) {
+				t.push(`tiny`);
+			} else if (eventSlave.boobs < 600) {
+				t.push(`small`);
+			}
+			t.push("boobs are barely noticeable");
+			t.push(eventSlave.belly >= 5000 ? `above ${his} bloated` : `against ${his} concave`);
+			if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
+				t.push(`tummy, and that can be a very appealing look given the predilection your arcology has for slim ${girl}s.`);
+			} else {
+				t.push(`tummy, and to most clients, this makes ${him} a less appealing whore.`);
+			}
+		} else {
+			t.push(eventSlave.boobs < 900 ? "large" : "massive");
+			t.push(`tits are a sharp contrast to ${his}`);
+			t.push(eventSlave.belly >= 150 ? "thin, bloated frame," : "concave belly");
+			t.push(`and although impressive, they seem to especially stick out on a ${girl} who, in all other ways, seems`);
+			t.push(eventSlave.weight > -80 ? "slightly" : "alarmingly");
+			t.push(`malnourished. ${His} ${eventSlave.faceShape} face is clearly conflicted, and`);
+		}
+		t.push(canSee(eventSlave) ? `${his} ${App.Desc.eyesColor(eventSlave)} shift` : `${he} glances about`);
+		t.push(`with nervous energy. ${He} is clearly unsure whether ${he} should say what ${he} came here to say.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+		t.push(`When you finally ask ${him} what ${he} wants, ${he} hesitates for a moment and then suddenly seems to gain ${his}`);
+		if (eventSlave.accent > 1) {
+			t.push(`courage, speaking in an atrociously thick accent.`);
+		} else if (eventSlave.accent > 0) {
+			t.push(`courage, speaking in a cute little accent.`);
+		} else {
+			t.push("courage.");
+		}
+		t.push(Spoken(eventSlave, `"Please ${Master}, I'm so hungry! It's seems like all I get to eat is cum. It's disgusting! Please! I have to eat so much of it. Can I be allowed to eat regular food again? Please ${Master}, I'm so hungry. I need real food!"`));
+		App.Events.addParagraph(node, t);
+		t = [];
+		t.push(`It's true, cum`);
+		t.push(eventSlave.dietCum === 1 ? "supplemented" : "based");
+		t.push(`food can be hard on ${girl}s`);
+		t.push(`who are not fully habituated to being toys for sexual amusement — particularly when you've ordered them to gain weight on it. You look at the`);
+		if (eventSlave.belly >= 1500) {
+			t.push(`gravid,`);
+		}
+		t.push(`skinny whore and consider your options.`);
+
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			(eventSlave.chastityAnus === 1)
+				? new App.Events.Result(`Give the poor ${girl} a break and take ${him} off ${his} cum diet so ${he} can safely get to a healthier weight`, safely)
+				: new App.Events.Result(),
+			(canDoAnal(eventSlave))
+				? new App.Events.Result(`Give the emaciated slut your answer in the form of a rough butt-fuck`, buttfuck, virginityWarning())
+				: new App.Events.Result(),
+			new App.Events.Result(`Use aphrodisiacs and positive reinforcement to make ${his} cum diet more palatable`, aphrodisiac),
+		]);
+
+		function virginityWarning() {
+			if ((eventSlave.anus === 0) && (eventSlave.dietMilk === 1)) {
+				return `This option will take ${his} anal virginity and remove milk from ${his} diet.`;
+			} else if (eventSlave.anus === 0) {
+				return `This option will take ${his} anal virginity`;
+			} else if (eventSlave.dietMilk === 1) {
+				return `This option will remove milk from ${his} diet.`;
+			}
+			return null;
+		}
+
+
+		function safely() {
+			t = [];
+			t.push(`You silently appraise ${eventSlave.slaveName}'s desperate face and lean forward in your chair to check your terminal. You confirm that`);
+			if (eventSlave.dietCum === 1) {
+				t.push(`${his} food is supplemented with cum for flavor.`);
+			} else {
+				t.push(`${he} is being fed significant amounts of ejaculate.`);
+			}
+			t.push(`While ${his}`);
+			if (eventSlave.dietCum === 2) {
+				t.push(`extreme`);
+			}
+			t.push(`diet is still engineered to`);
+			if (eventSlave.dietCum === 2) {
+				t.push(`barely`);
+			}
+			t.push(`provide the nutrition ${he} needs, if ${he} can't hold the stuff down, ${he} can't gain`);
+			if (eventSlave.pregKnown === 1 && eventSlave.preg > eventSlave.pregData.normalBirth / 4) {
+				t.push(`weight, especially with ${his} growing`);
+				if (eventSlave.pregType > 1) {
+					t.push(`children taking whatever nutrients they can.`);
+				} else {
+					t.push(`child taking whatever nutrients it can.`);
+				}
+			} else {
+				t.push("weight.");
+			}
+			t.push(`With a few taps on the keyboard you change ${his} orders so that ${he} will be fed a much more nutritionally rich diet that is free of ejaculate. However, you make sure to warn ${him} that ${V.assistant.name} will be monitoring ${him}, and you may change your mind once ${he} reaches a more attractive weight. ${He} thanks you profusely,`);
+			if (eventSlave.vagina !== 0) {
+				t.push(`even attempting to offer ${his} body for use in gratitude,`);
+			}
+			t.push(`but you simply send ${him} away. You find yourself concerned that you are becoming soft by allowing slaves to dictate what they will and won't eat, but your benevolent decision has already had a <span class="hotpink">positive effect on ${his} attitude.</span> ${He} <span class="mediumaquamarine">trusts you a little more</span> too.`);
+			eventSlave.trust += 2;
+			eventSlave.devotion += 2;
+			eventSlave.dietCum = 0;
+			return t;
+		}
+
+		function buttfuck() {
+			t = [];
+			let frag = document.createDocumentFragment();
+			t.push(`You stand up behind your desk. ${He} flinches, but holds ${his} ground, ${his} lip quivering slightly. You slowly walk toward ${him}, appraising ${his}`);
+			t.push(eventSlave.belly >= 100 ? `bloated,` : `smooth,`);
+			t.push(`skinny body, touching ${his} shoulder as you disappear behind ${him}. ${He} starts to cry as you`);
+			if (V.PC.dick === 0) {
+				t.push(`pull a massive rubber dong attachment off the wall and hook it to your strap-on.`);
+			} else {
+				t.push(`unbuckle your pants and ${him} feel your warm dick harden against ${his} tight, bony thigh.`);
+			}
+			t.push(`Without word or ceremony, you shove ${him} forward so that ${he} is bent over and crushed against your desk. The tears start to flow out of ${him} as ${he} feels your`);
+			t.push(V.PC.dick === 0 ? `massive rubber dong` : `hard, thick cock`);
+			t.push(`pressing against ${his} unprotected anus.`);
+			App.Events.addParagraph(frag, t);
+
+			t = [];
+			t.push(`When you offer ${him} the opportunity to revise ${his} request, ${he} does.`);
+			t.push(Spoken(eventSlave, `"Please, ${Master} I'm sorry! Please don't fuck my ass! I'll eat all the cum you want! Please!"`));
+			t.push(`${He} screams`);
+			if (eventSlave.accent > 1) {
+				t.push(`in ${his} thick accent`);
+			}
+			t.push(`as you force your`);
+			if (V.PC.dick === 0) {
+				t.push(`strap-on`);
+			} else {
+				t.push(`self`);
+			}
+			t.push(`inside ${his} butt-hole and subdue ${his} weak, struggling body against your unforgiving desk. <span class="gold">Horrified tears</span> stream down ${his} face with each brutal thrust as you speak into ${his} ear and remind ${him} that slaves are not allowed to dictate their needs to their owners. You know what's best for ${him}, and right now, ${his} proper role is as an eager little depository for the products of the free-cities ejaculate market. It's ${his} job to suck down cum at the whim of ${his} owner, plain and simple, until ${he}'s told otherwise. You butt-fuck the cum-fed slag until ${he} simply lays there and accepts ${his} corrective assrape.`);
+			if (eventSlave.anus === 0) {
+				t.push(`<span class="lime">${His}</span>`);
+				if (eventSlave.butt < 5) {
+					t.push(`<span class="lime">skinny little</span>`);
+				} else {
+					t.push(`<span class="lime">plump little</span>`);
+				}
+				t.push(`<span class="lime">ass is now broken in.</span>`);
+			}
+			t.push(`${He} sobs quietly with each thrust of your hips, and when you finally`);
+			if (V.PC.dick !== 0) {
+				t.push(`make yet another cum deposit into ${his}`);
+				t.push(eventSlave.weight < -80 ? `emaciated body and` : `bony body and`);
+			}
+			t.push(`pull out, you instruct`);
+			if (eventSlave.dietCum === 1) {
+				t.push(`${V.assistant.name} to dramatically increase the amount of cum in ${his} diet from now on. You won't have a slave telling you what ${he} eats. ${eventSlave.slaveName}`);
+				if (canHear(eventSlave)) {
+					t.push(`hears your instructions`);
+				} else {
+					t.push(`correctly guesses the meaning behind your body language`);
+				}
+				t.push(`and whimpers before rubbing ${his} sore bottom while heading`);
+			} else {
+				t.push(`another slave to drag the broken slut to the kitchen, where ${he}'s to receive an additional feeding of thick, creamy ejaculate-based nutrients before proceeding`);
+			}
+			t.push(`to ${his} next assignment.`);
+			App.Events.addParagraph(frag, t);
+			t = [];
+			t.push(`${He}'s learned a valuable lesson about what it truly means to be a slave today, and it's one ${he} <span class="orchid">won't soon forget.</span>`);
+			eventSlave.devotion -= 5;
+			eventSlave.trust -= 5;
+			if (eventSlave.anus === 0) {
+				eventSlave.anus += 1;
+			}
+			VCheck.Anal(1, eventSlave);
+			if (eventSlave.dietCum === 1) {
+				eventSlave.dietCum = 2;
+				eventSlave.dietMilk = 0;
+			}
+
+			App.Events.addParagraph(frag, t);
+			return [frag];
+		}
+
+
+		function aphrodisiac() {
+			t = [];
+			let frag = document.createDocumentFragment();
+			t.push(`You do a quick check at your terminal. ${He} is indeed being fed`);
+			if (eventSlave.dietCum === 1) {
+				t.push(`food that is supplemented with cum for flavor.`);
+			} else {
+				t.push(`large amounts of human ejaculate.`);
+			}
+			t.push(`You tell ${him} that it can't be helped. When you assign a slave ${his} diet, it's for a reason, and in ${eventSlave.slaveName}'s case, you are using ${his} diet to make ${him} into a better whore. By the look on ${his} distressed face, you can tell ${he} doesn't understand or appreciate your perspective, but you tell ${him} you have just the thing to help change ${his} outlook.`);
+			App.Events.addParagraph(frag, t);
+			t = [];
+			t.push(`You pull out a heavy dose of aphrodisiacs from the drawer in your desk. They are expensive, but powerful drugs that can be used in a brute force way to manipulate the sexual urges and tastes of even the most frigid sluts. The mild dose included in every slave's food is usually enough to affect a slow, inevitable change in attitude for resistant slaves, but sometimes a more acute dose can be an amusing way to turn disgust into dependence.`);
+			App.Events.addParagraph(frag, t);
+			t = [];
+			t.push(`${eventSlave.slaveName} shivers as you reassure ${him} and inject ${his}`);
+			t.push(eventSlave.weight < -80 ? `bony` : `tiny`);
+			t.push(`body with the powerful drugs. You know they are working when ${he} begins to perspire a little, and`);
+			if (eventSlave.vagina > -1) {
+				t.push(`${he} starts to leak fresh girl-juice down ${his} shivering thighs.`);
+			} else if (eventSlave.dick === 0) {
+				t.push(`starts to turn instinctively to offer you ${his} only hole.`);
+			} else {
+				t.push(`${his} dick starts to come to attention.`);
+			}
+			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}`);
+			if (eventSlave.addict === 0) {
+				t.push(`<span class="cyan">new</span>`);
+			} else {
+				t.push(`<span class="cyan">developing</span>`);
+			}
+			t.push(`<span class="cyan">addiction to aphrodisiacs.</span>`);
+			eventSlave.devotion += 5;
+			eventSlave.weight += 10;
+			eventSlave.fetish = "cumslut";
+			eventSlave.fetishKnown = 1;
+			eventSlave.fetishStrength = 10;
+			eventSlave.addict += 1;
+			seX((eventSlave), "oral", "public", "penetrative", 50);
+			healthDamage((eventSlave), 10);
+
+			App.Events.addParagraph(frag, t);
+			return [frag];
+		}
+	}
+};
diff --git a/src/events/RETS/reSimpleAssault.js b/src/events/RETS/reSimpleAssault.js
new file mode 100644
index 0000000000000000000000000000000000000000..4558a0923ab83f4c633ad814a4603ffc14591718
--- /dev/null
+++ b/src/events/RETS/reSimpleAssault.js
@@ -0,0 +1,243 @@
+App.Events.RETSSimpleAssault = class RETSSimpleAssault extends App.Events.BaseEvent {
+	eventPrerequisites() {
+		return [
+			() => V.universalRulesConsent === 0
+		];
+	}
+
+	actorPrerequisites() {
+		return [
+			[ // event slave
+				s => s.fetish !== "mindbroken",
+				hasAnyArms,
+				canStand,
+				canTalk,
+				canPenetrate,
+				s => s.devotion > 50
+			],
+			[ // and her sub
+				s => s.fetish !== "mindbroken",
+				canStand,
+				canTalk,
+				isSlaveAvailable,
+				s => s.relationship <= 2 || s.relationshipTarget !== this.actors[0],
+				s => (canDoAnal(s) && s.anus !== 0) || (canDoVaginal(s) && s.vagina !== 0),
+				s => s.devotion < 20,
+				s => s.belly < 2000,
+				s => s.skill.combat === 0,
+				s => s.muscles <= 30
+			]
+		];
+	}
+
+	execute(node) {
+		/** @type {Array<App.Entity.SlaveState>} */
+		let [domSlave, subSlave] = this.actors.map(a => getSlave(a));
+		const {
+			He, he, his, him, himself
+		} = getPronouns(domSlave);
+		const {
+			He2, he2, His2, his2, him2
+		} = getPronouns(subSlave).appendSuffix("2");
+		const {say, title: Master} = getEnunciation(domSlave);
+		const {title: Master2} = getEnunciation(subSlave);
+		const desc = SlaveTitle(domSlave);
+
+		/** @type {App.Entity.PlayerState} */
+		let PC = V.PC;
+
+		const vaginal = subSlave.vagina !== 0 && canDoVaginal(subSlave);
+
+		V.nextLink = "Next Week";
+
+		App.Events.drawEventArt(node, [domSlave, subSlave], ["no clothing", subSlave.clothes]);
+
+		let t = [];
+
+		t.push(`You round a corner and almost trip over`);
+		t.push(App.UI.DOM.combineNodes(App.UI.DOM.slaveDescriptionDialog(domSlave), "."));
+		t.push(`${He}'s on top of another slave, humping away; ${his}`);
+		if (domSlave.butt > 8) {
+			t.push(`monstrous, naked ass jiggles lewdly`);
+		} else if (domSlave.anus > 2) {
+			t.push(`loose asspussy winks lewdly`);
+		} else if (domSlave.muscles > 30) {
+			t.push(`heavily muscled butt flexes powerfully`);
+		} else if (domSlave.butt > 3) {
+			t.push(`big butt pumps energetically`);
+		} else {
+			t.push(`nice little butt flexes cutely`);
+		}
+		t.push(`as ${he} thrusts. You can't see much of the slave ${vaginal ? `lying on ${his2} back` : "face-down"} underneath ${domSlave.slaveName}, but you recognize ${him2} as `);
+		t.push(contextualIntro(domSlave, subSlave, "DOM"));
+		t.push(`by ${his2} sobbing. ${He2}'s struggling a little, but ${domSlave.slaveName} has ${him2} pinned to the floor by ${his2} ${hasBothArms(subSlave) ? "wrists" : "wrist"}, and ${domSlave.slaveName} is quickly raping the resistance out of the ${SlaveTitle(subSlave)}.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		t.push(`${domSlave.slaveName} senses your presence above and behind ${him}, and twists ${his} ${domSlave.hColor} head around to ${canSee(domSlave) ? `see who it is. ${He} sees that it's you` : `discern who it is. ${He} realizes that it's you`}, and greets you cheerfully. "${Spoken(domSlave, `Hi, ${Master}`)}," ${he} trills.`);
+		if (domSlave.fetishKnown && domSlave.fetish === "sadist") {
+			t.push(`"${Spoken(domSlave, `This is so awesome`)}," the sadistic ${desc} gushes. "${Spoken(domSlave, `The crying, the struggling. Thank you for letting us do this`)}."`);
+		} else if (domSlave.fetishKnown && domSlave.fetish === "dom") {
+			t.push(`"${Spoken(domSlave, `The crying almost makes me feel bad, but fucking a bitch feels so, so good`)}," the dominant ${desc} admits conversationally.`);
+		} else if (domSlave.fetishKnown && domSlave.fetish === "pregnancy" && vaginal && canImpreg(subSlave, domSlave)) {
+			t.push(`"${Spoken(domSlave, `I couldn't help myself`)}," the ${desc} admits. "${Spoken(domSlave, `${He2}'d look so pretty with a pregnant belly and I just couldn't resist giving ${him2} one. ${He2} tried to say ${he2} didn't want to be a mother, so...`)}"`);
+		} else if (domSlave.energy > 95) {
+			t.push(`"${Spoken(domSlave, `I can't help myself`)}," the nymphomaniac ${desc} admits breathlessly. "${Spoken(domSlave, `Thank you for letting me take what I need from the other girls`)}."`);
+		} else if (domSlave.energy > 60) {
+			t.push(`"${Spoken(domSlave, `I couldn't help myself`)}," the ${desc} admits. "${Spoken(domSlave, `I was really, really horny and ${he2} was just, um, there. And ${he2} tried to say no`)}."`);
+		} else {
+			t.push(`"${Spoken(domSlave, `I know it's not like me`)}," the ${desc} admits. "${Spoken(domSlave, `But I asked ${him2}, like, mostly joking, and ${he2} tried to say no`)}."`);
+		}
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		t.push(`${subSlave.slaveName} ${vaginal ? `looks out from under ${domSlave.slaveName}` : `turns ${his2} head`} and ${canSee(subSlave) ? "looks at" : "faces"} you too. "${Spoken(subSlave, `${Master2}, please`)}," ${he2} begs. "${Spoken(subSlave, `P-please, make ${him} s-stop — mhhh —`)}" ${domSlave.slaveName} shuts ${him2} up by ${vaginal ? `kissing ${his2} unwilling mouth` : `shoving ${his2} face back against the floor`}. Once ${he} has ${subSlave.slaveName} back under control, ${domSlave.slaveName} slows ${his} thrusting, reaches around behind ${himself}, and ${domSlave.vagina !== 0 && canDoVaginal(domSlave) ? `spreads ${his} futa pussy for you` : `pulls one asscheek aside to offer you ${his} anus. To make the offer extra clear, ${he} starts winking it lewdly`}.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		t.push(`"${Spoken(domSlave, `Please fuck me while I rape ${him2}, ${Master}`)}," ${domSlave.slaveName} ${say}s in a mockery of ${subSlave.slaveName}'s `);
+		if (subSlave.voice > 2) {
+			t.push(`high-pitched whining.`);
+		} else if (subSlave.voice > 1) {
+			t.push(`begging.`);
+		} else {
+			t.push(`deep-voiced begging.`);
+		}
+		t.push(`"${Spoken(domSlave, `Ooh, or, please, ${Master}, may I flip ${him2} over? I'd love to feel ${PC.dick ? `your cock inside ${him2} alongside mine` : `that strap-on you use inside ${him2} alongside my cock`}`)}!"`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		t.push(`"${Spoken(subSlave, `Please, no`)}," sobs ${subSlave.slaveName}.`);
+		App.Events.addParagraph(node, t);
+		t = [];
+
+		App.Events.addResponses(node, [
+			((canDoAnal(domSlave) && domSlave.anus !== 0) || (canDoVaginal(domSlave) && domSlave.vagina !== 0)
+				? new App.Events.Result("Slide in behind", behind)
+				: new App.Events.Result()
+			),
+			new App.Events.Result("Slide in alongside", alongside),
+			new App.Events.Result("Put a stop to it", stop)
+		]);
+
+		function behind() {
+			t = [];
+
+			t.push(`You order ${domSlave.slaveName} to go back to what ${he} was doing. ${He}'s a little disappointed you're not joining in, but ${he} obeys, pounding the crying ${subSlave.slaveName} without mercy. Then ${domSlave.slaveName} feels the head of ${PC.dick ? "your dick" : "a strap-on"} brush ${his} butt. "Ooh!" ${he} squeals, <span class="hotpink">pleased ${he} was wrong after all.</span> "${Spoken(domSlave, `Yes, thank you, ${Master}! Fuck me! Fuck me while I rape ${him2}!`)}" Underneath ${him}, ${subSlave.slaveName} cries harder, even though ${domSlave.slaveName} has to stop ${his} thrusting for a moment to let you inside. In fact, you reflect as you hammer ${domSlave.slaveName}'s`);
+			if (domSlave.vagina !== 0 && canDoVaginal(domSlave)) {
+				if (domSlave.vagina > 2) {
+					t.push(`roomy`);
+				} else if (domSlave.vagina > 1) {
+					t.push(`delectable`);
+				} else {
+					t.push(`tight little`);
+				}
+				t.push(`cunt,`);
+			} else {
+				if (domSlave.anus > 2) {
+					t.push(`gaping`);
+				} else if (domSlave.anus > 1) {
+					t.push(`relaxed`);
+				} else {
+					t.push(`poor little`);
+				}
+				t.push(`asspussy,`);
+			}
+			t.push(`it's a little strange that ${subSlave.slaveName} <span class="gold">seems to think this is worse</span> than just being raped by ${domSlave.slaveName}. After all, having your ${PC.dick ? "turgid cock" : "formidable strap-on"} sliding energetically in and out of ${his} ${domSlave.vagina !== 0 && canDoVaginal(domSlave) ? "womanhood" : "rectum"} is cramping ${domSlave.slaveName}'s style a bit. Maybe it's that ${subSlave.slaveName} is a little squashed under there.`);
+
+			domSlave.devotion += 4;
+			subSlave.trust -= 4;
+			if (domSlave.vagina !== 0 && canDoVaginal(domSlave)) {
+				seX(domSlave, "vaginal", PC, "penetrative");
+				if (canImpreg(domSlave, PC)) {
+					t.push(knockMeUp(domSlave, 5, 0, -1, 1));
+				}
+			} else {
+				seX(domSlave, "anal", PC, "penetrative");
+				if (canImpreg(domSlave, PC)) {
+					t.push(knockMeUp(domSlave, 5, 1, -1, 1));
+				}
+			}
+			if (vaginal) {
+				seX(subSlave, "vaginal", domSlave, "penetrative");
+				if (canPenetrate(domSlave) && canImpreg(subSlave, domSlave)) {
+					t.push(knockMeUp(subSlave, 5, 0, domSlave.ID, 1));
+				}
+			} else {
+				seX(subSlave, "anal", domSlave, "penetrative");
+				if (canPenetrate(domSlave) && canImpreg(subSlave, domSlave)) {
+					t.push(knockMeUp(subSlave, 5, 1, domSlave.ID, 1));
+				}
+			}
+			return t;
+		}
+
+		function alongside() {
+			const fit = vaginal ? subSlave.vagina > 2 : subSlave.anus > 2;
+			t = [];
+
+			t.push(`You order ${domSlave.slaveName} to flip ${subSlave.slaveName} over and let you in too. Just as you expected, ${domSlave.slaveName} responds with a vicious giggle, and ${subSlave.slaveName} cries even harder. "${Spoken(subSlave, `Please!`)}" ${he2} screams. "${Spoken(subSlave, `${Master2}, it'll hurt! Please don't!`)}"`);
+			if (fit) {
+				t.push(`It's not clear what ${he2}'s so worked up about. ${His2} cavernous ${vaginal ? "cunt" : "asshole"} should be able to take two dicks without trouble.`);
+			} else {
+				if (domSlave.dick < 5) {
+					t.push(`It's not clear what ${he2}'s so worked up about. ${PC.dick ? "You're quite large" : "You use a big strap-on"}, but ${domSlave.slaveName}'s penis is reasonably sized. It's not like ${subSlave.slaveName}'s ${vaginal ? "cunt" : "asshole"} is going to be permanently damaged or anything.`);
+				} else {
+					t.push(`${He2}'s right to be concerned. ${PC.dick ? "You're quite large" : "You use a big strap-on"}, and ${domSlave.slaveName}'s penis is huge too. ${subSlave.slaveName}'s ${vaginal ? "cunt" : "asshole"} is in serious peril.`);
+				}
+			}
+			t.push(`${domSlave.slaveName} pulls out, sits ${his} bare butt down on the floor, and hauls a struggling ${subSlave.slaveName} onto ${his} lap, shoving ${his} stiff prick back where it belongs. Then ${domSlave.slaveName} hauls ${subSlave.slaveName}'s legs back, offering you ${his2} already-occupied hole. ${subSlave.vagina !== 0 && vaginal ? `${subSlave.slaveName} has another hole, and ${he2} tearfully begs you to use it, but in vain.` : ""}`);
+			t.push(`You jam yourself inside, enjoying ${subSlave.slaveName}'s ${!fit ? `wriggling and the extreme tightness of ${his2} overfilled insides. ${He2} spasms with pain as you force your way inside ${him2}` : "wriggling"}. ${domSlave.slaveName} can't thrust much from where ${he} is, and serves mostly to tighten ${subSlave.slaveName} for you, but ${he} ${canSee(domSlave) ? "stares into your eyes lovingly" : "lovingly smiles at you"}. Playing such an equal sexual role with you definitely <span class="mediumaquamarine">builds ${his} trust</span> in ${his} role. For ${his2} part, ${subSlave.slaveName} is <span class="gold">thoroughly degraded,</span> ${fit ? "but physically unhurt." : `and <span class="orange">stretched out.</span>`}`);
+
+			domSlave.trust += 4;
+			subSlave.trust -= 4;
+			if (vaginal) {
+				seX(subSlave, "vaginal", domSlave, "penetrative");
+				if (canImpreg(domSlave, PC)) {
+					t.push(knockMeUp(domSlave, 5, 0, -1, 1));
+				}
+				if (canPenetrate(domSlave) && canImpreg(subSlave, domSlave)) {
+					t.push(knockMeUp(subSlave, 5, 0, domSlave.ID, 1));
+				}
+			} else {
+				seX(subSlave, "anal", domSlave, "penetrative");
+				if (canImpreg(domSlave, PC)) {
+					t.push(knockMeUp(domSlave, 5, 1, -1, 1));
+				}
+				if (canPenetrate(domSlave) && canImpreg(subSlave, domSlave)) {
+					t.push(knockMeUp(subSlave, 5, 1, domSlave.ID, 1));
+				}
+			}
+			if (!fit) {
+				if (vaginal) {
+					subSlave.vagina++;
+				} else {
+					subSlave.anus++;
+				}
+			}
+			return t;
+		}
+
+		function stop() {
+			t = [];
+
+			t.push(`You order ${domSlave.slaveName} to stop raping ${subSlave.slaveName}.
+			"${Spoken(domSlave, `Yes, ${Master}`)}," ${he} ${say}s automatically, and gets up, pulling ${his} dick out of ${subSlave.slaveName}'s poor ${vaginal ? "pussy" : "asshole"}. ${domSlave.slaveName} doesn't understand, and ${his} prick softens quickly with ${his} confusion. ${He} thought ${he} didn't need consent to fuck other slaves, and ${he} <span class="gold">doubts ${himself}.</span>`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			t.push(`${subSlave.slaveName} gets to ${his2} feet too, using a hand to massage ${his2} outraged hole. "${Spoken(subSlave, `Thank you, ${Master2}, thank you`)}," ${he2} repeats over and over, <span class="mediumaquamarine">weeping with relief.</span>`);
+			App.Events.addParagraph(node, t);
+			t = [];
+
+			domSlave.trust -= 4;
+			subSlave.trust += 4;
+			if (vaginal) {
+				seX(subSlave, "vaginal", domSlave, "penetrative");
+			} else {
+				seX(subSlave, "anal", domSlave, "penetrative");
+			}
+			return t;
+		}
+	}
+};
diff --git a/src/events/gameover.tw b/src/events/gameover.tw
index 23b4ae536f42b7c31a43dd7fe99e9a7748ec019c..70975838ca93e6bb3184caf2ba74b874984c3431 100644
--- a/src/events/gameover.tw
+++ b/src/events/gameover.tw
@@ -10,8 +10,8 @@
 		The implanted bomb is small, and went off too far ahead to do anything more than stun. Three other assailants run at you from where they were waiting in the confusion, wielding improvised weapons that apparently did not set off the security scanners. Without a bodyguard, decent security systems, or any other means of personal defense, you meet your ignominious death at the end of a bludgeon fashioned from a maintenance tool.
 
 	<<case "idiot ball">>
-		<<setLocalPronouns $Bodyguard>>
-		You quickly move to deal $Bodyguard.slaveName a slap across the face. You have an instant to realize the depth of your folly as $his combat training kicks into gear: before $he realizes what $he's doing, $he has drawn $his sword, blocked your slap (and incidentally, removed your hand in doing so), and buried the sword in your chest on the riposte. $His devotion returns to $him as $his combat instincts subside. As you fade, you see $his eyes cloud with terrible, unhealable guilt; in one sure movement, $he draws $his weapon, sets it to semi-automatic, places it under $his chin, and fires a single round.
+		<<setLocalPronouns _S.Bodyguard>>
+		You quickly move to deal _S.Bodyguard.slaveName a slap across the face. You have an instant to realize the depth of your folly as $his combat training kicks into gear: before $he realizes what $he's doing, $he has drawn $his sword, blocked your slap (and incidentally, removed your hand in doing so), and buried the sword in your chest on the riposte. $His devotion returns to $him as $his combat instincts subside. As you fade, you see $his eyes cloud with terrible, unhealable guilt; in one sure movement, $he draws $his weapon, sets it to semi-automatic, places it under $his chin, and fires a single round.
 
 	<<case "debt">>
 		You have fallen so far into debt that it is mere child's play for another slaveowner to purchase your debt, call it in, and enslave you. The story of your remaining years may be worth telling, but it must be told elsewhere.
@@ -69,4 +69,4 @@
 
 <</switch>>
 <br><br>
-''GAME OVER''
\ No newline at end of file
+''GAME OVER''
diff --git a/src/events/intro/initNationalities.js b/src/events/intro/initNationalities.js
index 5b5ce19c9cb8b31e288f0c49fe1f142e77e6e4e0..b34f9d8f003f924eaddb302bcae69b86d8dddcfe 100644
--- a/src/events/intro/initNationalities.js
+++ b/src/events/intro/initNationalities.js
@@ -27,7 +27,6 @@ App.Intro.initNationalities = function() {
 			pathways: 0,
 			rapidVehicles: 0
 		};
-		V.riotCenter = 0;
 		V.riotUpgrades = {
 			freeMedia: 0,
 			rapidUnit: 0,
diff --git a/src/events/legacy/RECI.tw b/src/events/legacy/RECI.tw
new file mode 100644
index 0000000000000000000000000000000000000000..20a64041d53e36ba22152fce2dc654dbdd672491
--- /dev/null
+++ b/src/events/legacy/RECI.tw
@@ -0,0 +1,92 @@
+:: RECI [nobr]
+
+/* This is one of several files that contains and organizes many different events.	*/
+/*	genericPlotEvents.tw															*/
+/*	PESS.tw: Player Event, Single Slave												*/
+/*	PETS.tw: Player Event, Two Slaves												*/
+/*	RECI.tw: Random Event, Check In													*/
+/*	REFI.tw: Random Event, Fetish Interest											*/
+/*	REFS.tw: Random Event, Future Societies											*/
+/*	RESS.tw: Random Event, Single Slave												*/
+/*	RESSTR.tw: Random Event, Single Slave (Test Realm, for debugging events)		*/
+/*	RETS.tw: Random Event, Two Slaves												*/
+/*																					*/
+/* Events can also be in a dedicated *.tw file, formatted as follows:				*/
+/*	jeXXXXX.tw: Justice Event														*/
+/*	pXXXXXX.tw: Player event														*/
+/*	peXXXXX.tw: Player Event focused on a slave										*/
+/*	reXXXXX.tw: Random Event														*/
+/*	resXXXX.tw: Random Event, School												*/
+/*	seXXXXX.tw: Slave Event, focuses on slaves coming or going						*/
+/*	securityForceXXXXX.tw: Special (Security) Force event							*/
+/*																					*/
+/* Some scenes are also stored in useGuard.tw, walkPast.tw, and toychest.tw			*/
+
+/* This is a legacy container for whomever may still be using it for events. */
+/* They, if they exist, should strongly consider converting their events to the JS standard. */
+
+<<if Array.isArray($RECIevent)>>
+	<<set $activeSlave = $eventSlave>>
+	<<if $cheatMode == 1>>
+		<<set $nextButton = "Back", $nextLink = "Nonrandom Event", $returnTo = "Nonrandom Event">> /* if user just clicks spacebar */
+		''A random check-in event would have been selected from the following:''
+		<br>
+		<<for _i = 0; _i < $RECIevent.length; _i++>>
+			<<print "[[$RECIevent[_i]|RECI][$RECIevent = $RECIevent[" + _i + "]]]">>
+			<br>
+		<</for>>
+		<br><br>[[Go Back to Random Individual Event|Random Individual Event][$activeSlave = 0]]
+	<<else>>
+		<<set $RECIevent = $RECIevent.random()>>
+		<<goto "RECI">>
+	<</if>>
+<<else>>
+
+<<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Next Week">>
+
+<<set _clothesTemp = $activeSlave.clothes>>
+<<switch $RECIevent>>
+	/*Some events start with the slave naked (any event that starts with the daily inspection, for example). Here we switch their clothing just for the image to load, then switch it back quickly so the player's choice is not messed up.*/
+<<case "placeholder">>
+	<<set $activeSlave.clothes = "no clothing">>
+<</switch>>
+<span id="artFrame">
+/* 000-250-006 */
+<<if $seeImages == 1>>
+	<<if $imageChoice == 1>>
+		<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
+	<<else>>
+		<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
+	<</if>>
+<</if>>
+/* 000-250-006 */
+</span>
+<<set $activeSlave.clothes = _clothesTemp>>
+
+<<run Enunciate($activeSlave)>>
+<<set $desc = SlaveTitle($activeSlave)>>
+<<set _belly = bellyAdjective($activeSlave)>>
+<<setLocalPronouns $activeSlave>>
+<<setPlayerPronouns>>
+
+<<switch $RECIevent>>
+
+<<default>>
+	<br>ERROR: bad RECI event $RECIevent
+<</switch>>
+
+<br><br>
+<span id="result">
+<<switch $RECIevent>>
+
+<<default>>
+	<br>ERROR: bad RECI event $RECIevent
+<</switch>>
+
+<<if $cheatMode == 1>>
+	<br><br>DEBUG: &nbsp;&nbsp;&nbsp;&nbsp;[[Go back to Nonrandom Event|Nonrandom Event][$activeSlave = 0, $eventSlave = 0]]
+<</if>>
+
+</span>
+
+<</if>> /* CLOSES EVENT SELECTION */
diff --git a/src/events/randomEvent.js b/src/events/randomEvent.js
index a37ceb1c61233c1742930e07101e735d0a518e28..a59a75b03ae9b84cbd737930c8ea11da9a12610b 100644
--- a/src/events/randomEvent.js
+++ b/src/events/randomEvent.js
@@ -14,12 +14,14 @@ App.Events.getIndividualEvents = function() {
 		new App.Events.RESSCockFeederResistance(),
 		new App.Events.RESSComfortableSeat(),
 		new App.Events.RESSDevotedAnalVirgin(),
+		new App.Events.RESSDevotedEducated(),
 		new App.Events.RESSDevotedVirgin(),
 		new App.Events.RESSDevotedWaist(),
 		new App.Events.RESSEscapee(),
 		new App.Events.RESSFrighteningDick(),
 		new App.Events.RESSHotPC(),
 		new App.Events.RESSImScared(),
+		new App.Events.RESSKitchenMolestation(),
 		new App.Events.RESSLazyEvening(),
 		new App.Events.RESSMoistPussy(),
 		new App.Events.RESSMuscles(),
@@ -30,7 +32,11 @@ App.Events.getIndividualEvents = function() {
 		new App.Events.RESSObedientShemale(),
 		new App.Events.RESSPassingDeclaration(),
 		new App.Events.RESSRetchingCum(),
+		new App.Events.RESSServeThePublicDevoted(),
+		new App.Events.RESSSlaveOnSlaveClit(),
+		new App.Events.RESSSlaveOnSlaveDick(),
 		new App.Events.RESSSuppositoryResistance(),
+		new App.Events.RESSTooThinForCumDiet(),
 		new App.Events.RESSWaistlineWoes(),
 
 		new App.Events.RECIButthole(),
@@ -38,8 +44,10 @@ App.Events.getIndividualEvents = function() {
 		new App.Events.RECIFuta(),
 		new App.Events.RECIMilf(),
 		new App.Events.RECIOrientation(),
+		new App.Events.RECIUgly(),
 
 		new App.Events.RETSSiblingTussle(),
+		new App.Events.RETSSimpleAssault(),
 	];
 };
 
diff --git a/src/facilities/arcade/arcade.tw b/src/facilities/arcade/arcade.tw
index 1bd7640f3700e1547563d00072f68739135e01c1..7d6d6beb78806115219db5436d434774e08ebaee 100644
--- a/src/facilities/arcade/arcade.tw
+++ b/src/facilities/arcade/arcade.tw
@@ -179,7 +179,7 @@
 <<includeDOM App.Facilities.Arcade.Stats(true)>>
 
 <p>
-	<<print App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.arcade)>>
+	<<includeDOM App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.arcade)>>
 </p>
 
 <p>
diff --git a/src/facilities/armory/bgSelect.tw b/src/facilities/armory/bgSelect.tw
index b888caa6e8fcf56c4fb3b593e5205cfcc857366b..a1094cd42992729a37a444d0485d0fe4952b1560 100644
--- a/src/facilities/armory/bgSelect.tw
+++ b/src/facilities/armory/bgSelect.tw
@@ -6,10 +6,9 @@
 </p>
 
 <p>
-	<<if ($Bodyguard != 0)>>
-		<<set $Bodyguard = getSlave($Bodyguard.ID)>>
-		<<setLocalPronouns $Bodyguard>>
-		<span class="slave-name"><<= SlaveFullName($Bodyguard)>></span>
+	<<if (_S.Bodyguard)>>
+		<<setLocalPronouns _S.Bodyguard>>
+		<span class="slave-name"><<= SlaveFullName(_S.Bodyguard)>></span>
 		is serving as your bodyguard. $He will remain close to you at all times and guard your person.
 	<<else>>
 		You have not selected a bodyguard.
@@ -34,8 +33,8 @@
 	Appoint a bodyguard from your devoted slaves:
 </div>
 <div class="choices">
-	[[None|Main][removeJob($Bodyguard, "guard you")]]
+	[[None|Main][removeJob(_S.Bodyguard, Job.BODYGUARD)]]
 </div>
 <p>
-	<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.armory, "Main")>>
+	<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.armory, "Main")>>
 </p>
diff --git a/src/facilities/brothel/brothel.tw b/src/facilities/brothel/brothel.tw
index e638724aa409fa41cf65e14d39fa363c3b286b61..88eabec01bd81ec3ed06e73ef9b92bc8f78e0bc4 100644
--- a/src/facilities/brothel/brothel.tw
+++ b/src/facilities/brothel/brothel.tw
@@ -140,8 +140,8 @@
 		$brothelNameCaps is bustling with activity. Customers are coming and going and slave girls are displaying themselves. When a slave catches a customer's eye, he leads her back into a little cubicle.
 	<<elseif _BL > 0>>
 		$brothelNameCaps is working steadily. Customers are present and slave girls are on offer. When a slave catches a customer's eye, he leads her back into a little cubicle.
-	<<elseif $Madam != 0>>
-		$Madam.slaveName is alone in $brothelName, and has nothing to do but keep the place clean and plan future sales efforts.
+	<<elseif $MadamID != 0>>
+		_S.Madam.slaveName is alone in $brothelName, and has nothing to do but keep the place clean and plan future sales efforts.
 	<<else>>
 		$brothelNameCaps is empty and desolate.
 		<div class="choices" style="font-style:normal">
@@ -178,16 +178,24 @@
 	</div>
 </div>
 
-<<if $Madam != 0>>
+<<if $MadamID != 0>>
 	<div>
-		<<setLocalPronouns $Madam>>
+		<<setLocalPronouns _S.Madam>>
 		<<if $MadamIgnoresFlaws != 1>>
-			$Madam.slaveName will attempt to fix flaws in whores serving under $him.
+			_S.Madam.slaveName will attempt to fix flaws in whores serving under $him.
 			[[Ignore flaws|Brothel][$MadamIgnoresFlaws = 1]]
 		<<else>>
-			$Madam.slaveName has been instructed to ignore flaws in the whores serving under $him.
+			_S.Madam.slaveName has been instructed to ignore flaws in the whores serving under $him.
 			[[Fix flaws|Brothel][$MadamIgnoresFlaws = 0]]
 		<</if>>
+		<br>
+		<<if $MadamNoSex != 1>>
+			_S.Madam.slaveName will whore $himself out when $he doesn't have enough whores serving under $him.
+			[[Don't serve clients|Brothel][$MadamNoSex = 1]]
+		<<else>>
+			_S.Madam.slaveName will not whore $himself out even if $he has time.
+			[[Serve clients|Brothel][$MadamNoSex = 0]]
+		<</if>>
 	</div>
 <</if>>
 
@@ -253,7 +261,7 @@
 <<includeDOM App.Facilities.Brothel.Stats(true)>>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.brothel)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.brothel)>>
 </p>
 
 <p>
diff --git a/src/facilities/brothel/brothelAssignmentScene.tw b/src/facilities/brothel/brothelAssignmentScene.tw
index ef292aaf2caba44d3d185fb70f2ae000eae1c53f..05ef134da658c2691b2284113353603f1d30b486 100644
--- a/src/facilities/brothel/brothelAssignmentScene.tw
+++ b/src/facilities/brothel/brothelAssignmentScene.tw
@@ -30,7 +30,7 @@ You could direct $assistant.name to relay your orders to $activeSlave.slaveName,
 <<else>>
 	rebellious slave <<if hasAnyLegs($activeSlave)>>comes in slowly, having decided that $he can always decide to resist once $he hears what you want<<else>>comes in as soon as you order another slave to carry $him in, since $he can't exactly resist this without <<if isAmputee($activeSlave)>>limbs<<else>>legs<</if>><</if>>.
 <</if>>
-You tell $him $he's to report to <<if $Madam != 0>>$Madam.slaveName immediately, to serve in $brothelName<<else>>$brothelName immediately, to serve there<</if>> until further notice.
+You tell $him $he's to report to <<if $MadamID != 0>>_S.Madam.slaveName immediately, to serve in $brothelName<<else>>$brothelName immediately, to serve there<</if>> until further notice.
 
 <br><br>
 <<if $activeSlave.devotion > 50>>
@@ -52,7 +52,7 @@ You tell $him $he's to report to <<if $Madam != 0>>$Madam.slaveName immediately,
 					"the blowjob<<s>>. Ju<<s>>t, the blowjob<<s>>. That'<<s>> all."
 				<</if>>
 			<<case "humiliation">>
-				There'<<s>> a window that fa<<c>>e<<s>> the hallway there, right? <<if $Madam != 0>>Do you think $Madam.slaveName would let me<<else>>Could I<</if>> get fucked there? Where everyone could <<s>>ee me being fucked for money?"
+				There'<<s>> a window that fa<<c>>e<<s>> the hallway there, right? <<if $MadamID != 0>>Do you think _S.Madam.slaveName would let me<<else>>Could I<</if>> get fucked there? Where everyone could <<s>>ee me being fucked for money?"
 			<<case "buttslut">>
 				I mean," and $he shifts $his weight, $his mind obviously on $his backdoor,
 				<<if $activeSlave.attrXX > $activeSlave.attrXY>>
@@ -166,7 +166,7 @@ You tell $him $he's to report to <<if $Madam != 0>>$Madam.slaveName immediately,
 					"the blowjob<<s>>. Ju<<s>>t, the blowjob<<s>>. That'<<s>> all."
 				<</if>>
 			<<case "humiliation">>
-				there'<<s>> a window that fa<<c>>e<<s>> the hallway there, right? <<if $Madam != 0>>Do you think $Madam.slaveName would let me<<else>>Could I<</if>> get fucked there? Where everyone could <<s>>ee me being fucked for money?"
+				there'<<s>> a window that fa<<c>>e<<s>> the hallway there, right? <<if $MadamID != 0>>Do you think _S.Madam.slaveName would let me<<else>>Could I<</if>> get fucked there? Where everyone could <<s>>ee me being fucked for money?"
 			<<case "buttslut">>
 
 				uh," and $he shifts $his weight, $his mind obviously on $his backdoor,
@@ -373,6 +373,6 @@ You tell $him $he's to report to <<if $Madam != 0>>$Madam.slaveName immediately,
 	<<else>>
 		get an incredibly rude gesture out
 	<</if>>
-	before the compliance systems activate and $he's poleaxed to the ground. Anticipating this, you had <<if $Madam != 0>>$Madam.slaveName <<else>>another, more obedient slave<</if>> standing by to haul $him away. It'll take a couple of hundred customers, but being sold in a brothel for rape should knock the resistance out of $him.
+	before the compliance systems activate and $he's poleaxed to the ground. Anticipating this, you had <<if $MadamID != 0>>_S.Madam.slaveName <<else>>another, more obedient slave<</if>> standing by to haul $him away. It'll take a couple of hundred customers, but being sold in a brothel for rape should knock the resistance out of $him.
 
 <</if>> /* CLOSES DEVOTION */
diff --git a/src/facilities/brothel/brothelReport.tw b/src/facilities/brothel/brothelReport.tw
index dd98d3a32e33cc5129f6f4e2c71bd0a06dc623d0..53795cfe9091f1fa0108105f83b4350e0d7f50f6 100644
--- a/src/facilities/brothel/brothelReport.tw
+++ b/src/facilities/brothel/brothelReport.tw
@@ -11,44 +11,41 @@
 <!-- Statistics gathering -->
 <<set $facility = $facility || {}, $facility.brothel = initFacilityStatistics($facility.brothel)>>
 
-<<if ($Madam != 0)>>
-	<<set _FLs = $slaveIndices[$Madam.ID]>>
-
-	<<if ($slaves[_FLs].health.condition < -80)>>
-		<<run improveCondition($slaves[_FLs], 20)>>
-	<<elseif $slaves[_FLs].health.condition < -40>>
-		<<run improveCondition($slaves[_FLs], 15)>>
-	<<elseif $slaves[_FLs].health.condition < 0>>
-		<<run improveCondition($slaves[_FLs], 10)>>
-	<<elseif $slaves[_FLs].health.condition < 90>>
-		<<run improveCondition($slaves[_FLs], 7)>>
+<<if ($MadamID != 0)>>
+	<<if (_S.Madam.health.condition < -80)>>
+		<<run improveCondition(_S.Madam, 20)>>
+	<<elseif _S.Madam.health.condition < -40>>
+		<<run improveCondition(_S.Madam, 15)>>
+	<<elseif _S.Madam.health.condition < 0>>
+		<<run improveCondition(_S.Madam, 10)>>
+	<<elseif _S.Madam.health.condition < 90>>
+		<<run improveCondition(_S.Madam, 7)>>
 	<</if>>
-	<<if $slaves[_FLs].devotion <= 45>>
-		<<set $slaves[_FLs].devotion += 5>>
+	<<if _S.Madam.devotion <= 45>>
+		<<set _S.Madam.devotion += 5>>
 	<</if>>
-	<<if $slaves[_FLs].trust < 45>>
-		<<set $slaves[_FLs].trust += 5>>
+	<<if _S.Madam.trust < 45>>
+		<<set _S.Madam.trust += 5>>
 	<</if>>
-	<<if $slaves[_FLs].rules.living != "luxurious">>
-		<<set $slaves[_FLs].rules.living = "luxurious">>
+	<<if _S.Madam.rules.living != "luxurious">>
+		<<set _S.Madam.rules.living = "luxurious">>
 	<</if>>
-	<<if $slaves[_FLs].fetishStrength <= 95>>
-		<<if $slaves[_FLs].fetish != "dom">>
-			<<if fetishChangeChance($slaves[_FLs]) > random(0,100)>>
-				<<set _FLsFetish = 1, $slaves[_FLs].fetishKnown = 1, $slaves[_FLs].fetish = "dom">>
+	<<if _S.Madam.fetishStrength <= 95>>
+		<<if _S.Madam.fetish != "dom">>
+			<<if fetishChangeChance(_S.Madam) > random(0,100)>>
+				<<set _FLsFetish = 1, _S.Madam.fetishKnown = 1, _S.Madam.fetish = "dom">>
 			<</if>>
-		<<elseif $slaves[_FLs].fetishKnown == 0>>
-			<<set _FLsFetish = 1, $slaves[_FLs].fetishKnown = 1>>
+		<<elseif _S.Madam.fetishKnown == 0>>
+			<<set _FLsFetish = 1, _S.Madam.fetishKnown = 1>>
 		<<else>>
-			<<set _FLsFetish = 2, $slaves[_FLs].fetishStrength += 4>>
+			<<set _FLsFetish = 2, _S.Madam.fetishStrength += 4>>
 		<</if>>
 	<</if>>
 	/% Make sure we have registered living expenses as for any other slave %/
-	<<run getSlaveStatisticData($Madam, $facility.brothel)>>
-	<<set $Madam = $slaves[_FLs]>>
-	<<setLocalPronouns $Madam>>
-	&nbsp;&nbsp;&nbsp;&nbsp;<<= SlaveFullName($Madam)>> is serving as the Madam.
-	<<if $Madam.relationship == -3 && $Madam.devotion > 50>>
+	<<run getSlaveStatisticData(_S.Madam, $facility.brothel)>>
+	<<setLocalPronouns _S.Madam>>
+	&nbsp;&nbsp;&nbsp;&nbsp;<<= SlaveFullName(_S.Madam)>> is serving as the Madam.
+	<<if _S.Madam.relationship == -3 && _S.Madam.devotion > 50>>
 		As your loving $wife, $he does $his best to attract attention to your brothel.
 	<</if>>
 	<<if (_FLsFetish == 1)>>
@@ -56,36 +53,36 @@
 	<<elseif (_FLsFetish == 2)>>
 		$He's careful that all of the whores under $his supervision are all warmed up and ready to get fucked every morning, and $he <span class="fetish inc">becomes more dominant.</span>
 	<</if>>
-	<<if ($Madam.skill.whoring <= 10)>>
-		Though $Madam.slaveName does $his best to manage the brothel, with $his lack of skill $he can do little.
-	<<elseif ($Madam.skill.whoring <= 30)>>
-		$Madam.slaveName's basic skills marginally <span class="cash inc">improve</span> business at $brothelName.
-	<<elseif ($Madam.skill.whoring <= 60)>>
-		$Madam.slaveName's skills <span class="cash inc">improve</span> business at $brothelName.
-	<<elseif ($Madam.skill.whoring < 100)>>
-		$Madam.slaveName's skills greatly <span class="cash inc">improve</span> business at $brothelName.
+	<<if (_S.Madam.skill.whoring <= 10)>>
+		Though _S.Madam.slaveName does $his best to manage the brothel, with $his lack of skill $he can do little.
+	<<elseif (_S.Madam.skill.whoring <= 30)>>
+		_S.Madam.slaveName's basic skills marginally <span class="cash inc">improve</span> business at $brothelName.
+	<<elseif (_S.Madam.skill.whoring <= 60)>>
+		_S.Madam.slaveName's skills <span class="cash inc">improve</span> business at $brothelName.
+	<<elseif (_S.Madam.skill.whoring < 100)>>
+		_S.Madam.slaveName's skills greatly <span class="cash inc">improve</span> business at $brothelName.
 	<<else>>
-		$Madam.slaveName's mastery immensely <span class="cash inc">improves</span> business at $brothelName.
+		_S.Madam.slaveName's mastery immensely <span class="cash inc">improves</span> business at $brothelName.
 	<</if>>
-	<<if ($Madam.actualAge > 35)>>
+	<<if (_S.Madam.actualAge > 35)>>
 		$His age and experience also contribute.
 	<</if>>
-	<<if setup.madamCareers.includes($Madam.career)>>
+	<<if setup.madamCareers.includes(_S.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.
-	<<elseif $Madam.skill.madam >= $masteredXP>>
+	<<elseif _S.Madam.skill.madam >= $masteredXP>>
 		$He has experience from working for you that helps $him in the seedy business of selling other people's bodies for sex.
 	<</if>>
-	<<if ($Madam.intelligence+$Madam.intelligenceImplant > 15)>>
+	<<if (_S.Madam.intelligence+_S.Madam.intelligenceImplant > 15)>>
 		$He is a clever manager.
 	<</if>>
-	<<if ($Madam.dick > 2) && (canPenetrate($Madam))>>
+	<<if (_S.Madam.dick > 2) && (canPenetrate(_S.Madam))>>
 		$His turgid dick helps $him manage the bitches.
 	<</if>>
 	<<for _slave range _slaves>>
 		<<set $i = $slaveIndices[_slave.ID]>> /% for the included passages %/
 
-		<<if $Madam.rivalryTarget == _slave.ID>>
-			$He forces $his <<print rivalryTerm($Madam)>>, to service all the men in the brothel.
+		<<if _S.Madam.rivalryTarget == _slave.ID>>
+			$He forces $his <<print rivalryTerm(_S.Madam)>>, to service all the men in the brothel.
 			<<set _slave.devotion -= 2, _slave.trust -= 2>>
 			<<if canDoVaginal(_slave)>>
 				<<run seX($activeSlave, "vaginal", "public", "penetrative", 10)>>
@@ -95,15 +92,15 @@
 			<</if>>
 			<<run seX($activeSlave, "anal", "public", "penetrative", 10)>>
 			<<if random(1,100) > 65>>
-				<<set $slaves[_FLs].rivalry++, $Madam.rivalry++, _slave.rivalry++>>
+				<<set _S.Madam.rivalry++, _slave.rivalry++>>
 			<</if>>
-		<<elseif $Madam.relationshipTarget == _slave.ID>>
+		<<elseif _S.Madam.relationshipTarget == _slave.ID>>
 			<<setLocalPronouns _slave 2>>
-			$He dotes over $his <<print relationshipTerm($Madam)>>, _slave.slaveName, making sure _he2 is safe, but unfortunately driving potential customers away from _him2.
+			$He dotes over $his <<print relationshipTerm(_S.Madam)>>, _slave.slaveName, making sure _he2 is safe, but unfortunately driving potential customers away from _him2.
 			<<set _slave.devotion++>>
-		<<elseif areRelated($Madam, _slave)>>
+		<<elseif areRelated(_S.Madam, _slave)>>
 			<<setLocalPronouns _slave 2>>
-			$He pays special attention to $his <<print relativeTerm($Madam,_slave)>>, _slave.slaveName, making sure _he2 is treated well and showing off _his2 skills.
+			$He pays special attention to $his <<print relativeTerm(_S.Madam,_slave)>>, _slave.slaveName, making sure _he2 is treated well and showing off _his2 skills.
 			<<set _slave.trust++>>
 		<</if>>
 		<<if _slave.prestigeDesc == "$He is a famed Free Cities whore, and commands top prices.">>
@@ -118,7 +115,7 @@
 				<<if (_slave.muscles > 60) && (_slave.weight < 30) && (_slave.lactation > 0) && ((_slave.boobs-_slave.boobsImplant) > 6000)>>
 					$He shows off how even a cow like _slave.slaveName can achieve physical perfection.
 				<<else>>
-					A<<if (_slave.muscles < 30)>>n unmuscled,<</if>><<if (_slave.weight > 30)>> fat,<</if>> 'prestigious' <<if (_slave.lactation > 0)>>cow<<elseif ((_slave.boobs-_slave.boobsImplant) > 6000)>>mass of titflesh<<else>>slave<</if>> like _slave.slaveName is woefully out of fashion, so $Madam.slaveName tries to draw attention away from _him2.
+					A<<if (_slave.muscles < 30)>>n unmuscled,<</if>><<if (_slave.weight > 30)>> fat,<</if>> 'prestigious' <<if (_slave.lactation > 0)>>cow<<elseif ((_slave.boobs-_slave.boobsImplant) > 6000)>>mass of titflesh<<else>>slave<</if>> like _slave.slaveName is woefully out of fashion, so _S.Madam.slaveName tries to draw attention away from _him2.
 				<</if>>
 			<<else>>
 				<<if (_slave.lactation > 0) && ((_slave.boobs-_slave.boobsImplant) > 6000)>>
@@ -154,17 +151,16 @@
 		<</if>>
 	<</for>>
 
-	<<if (_DL+$brothelSlavesGettingHelp < 10)>>
-		<<set $i = _FLs>> /* apply following SA passages to facility leader */
-		<<setLocalPronouns $slaves[$i]>>
+	<<if (_DL+$brothelSlavesGettingHelp < 10) && $MadamNoSex != 1>>
+		<<setLocalPronouns _S.Madam>>
 		<<set _oldCash = $cash>>
 		<<if $showEWD != 0>>
-			<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= App.SlaveAssignment.whore($slaves[$i])>>
+			<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= App.SlaveAssignment.whore(_S.Madam)>>
 		<<else>>
-			<<run App.SlaveAssignment.whore($slaves[$i])>>
+			<<run App.SlaveAssignment.whore(_S.Madam)>>
 		<</if>>
 		<br>&nbsp;&nbsp;&nbsp;&nbsp;
-		$He whores $himself because $he doesn't have enough whores to manage to keep $him busy, and makes <span class="cash inc"><<print cashFormat($Madam.lastWeeksCashIncome)>>.</span> $He can charge more for $his time, since many citizens find it erotic to fuck the Madam.
+		$He whores $himself because $he doesn't have enough whores to manage to keep $him busy, and makes <span class="cash inc"><<print cashFormat(_S.Madam.lastWeeksCashIncome)>>.</span> $He can charge more for $his time, since many citizens find it erotic to fuck the Madam.
 		<<set _profits += $cash - _oldCash, _oldCash = $cash>>
 	<</if>>
 	<<if (_DL > 0)>><br><br><</if>>
@@ -180,42 +176,41 @@
 	</div>
 <</if>>
 
-<<if $Madam != 0>>
-	<<set $i = _FLs>> /* apply following SA passages to facility leader */
+<<if $MadamID != 0>>
+	<<set $i = $slaveIndices[$MadamID]>> /* apply following SA passages to facility leader */
 	<<if $showEWD != 0>>
 		<br><br>
 		/* 000-250-006 */
 		<<if $seeImages && $seeReportImages>>
 		<div class="imageRef tinyImg">
-			<<= SlaveArt($slaves[_FLs], 0, 0)>>
+			<<= SlaveArt(_S.Madam, 0, 0)>>
 		</div>
 		<</if>>
 		/* 000-250-006 */
-		<span class="slave-name"><<= SlaveFullName($slaves[_FLs])>></span> is serving as the Madam.
+		<span class="slave-name"><<= SlaveFullName(_S.Madam)>></span> is serving as the Madam.
 		<br>&nbsp;&nbsp;&nbsp;
-		<<= App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<= App.SlaveAssignment.choosesOwnClothes(_S.Madam)>>
+		<<run tired(_S.Madam)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
-		<<= App.SlaveAssignment.drugs($slaves[$i])>>
+		<<= App.SlaveAssignment.drugs(_S.Madam)>>
 		<<include "SA relationships">>
 		<<include "SA rivalries">>
 		<br><<include "SA devotion">>
 	<<else>>
 		<<silently>>
-		<<run App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<run App.SlaveAssignment.choosesOwnClothes(_S.Madam)>>
+		<<run tired(_S.Madam)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
-		<<run App.SlaveAssignment.drugs($slaves[$i])>>
+		<<run App.SlaveAssignment.drugs(_S.Madam)>>
 		<<include "SA relationships">>
 		<<include "SA rivalries">>
 		<<include "SA devotion">>
 		<</silently>>
 	<</if>>
-	<<set $Madam = $slaves[_FLs]>>
 <</if>>
 
 <<if (_DL > 0)>>
@@ -356,6 +351,6 @@
 	<</timed>>
 <</if>>
 
-<<if _DL > 0 || $Madam != 0>>
+<<if _DL > 0 || $MadamID != 0>>
 	<br><br>
 <</if>>
diff --git a/src/facilities/cellblock/cellblock.tw b/src/facilities/cellblock/cellblock.tw
index 33ebb3147d77b2ba736307528d4eee5c9484afe4..90b64622ccfb757dd33e685d463525ea0b5883b7 100644
--- a/src/facilities/cellblock/cellblock.tw
+++ b/src/facilities/cellblock/cellblock.tw
@@ -138,7 +138,7 @@
 </p>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.cellblock)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.cellblock)>>
 </p>
 
 <p>
diff --git a/src/facilities/cellblock/wardenessSelect.tw b/src/facilities/cellblock/wardenessSelect.tw
index d64de5bdce25ecff3aa384e8b93961e82288c7ba..6ae7bf5aed1652a53263bca874574444d24f3719 100644
--- a/src/facilities/cellblock/wardenessSelect.tw
+++ b/src/facilities/cellblock/wardenessSelect.tw
@@ -11,4 +11,4 @@
 <br><br>''Appoint a Wardeness from your devoted slaves:''
 <br><br>[[None|Cellblock][removeJob(_S.Wardeness, "be the Wardeness")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.cellblock, "Cellblock")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.cellblock, "Cellblock")>>
diff --git a/src/facilities/clinic/clinic.tw b/src/facilities/clinic/clinic.tw
index f29386e96abb096a1e10caf2931998cc460f2764..7e88681d257203e0e68d675d22ba91e9a5a1fc77 100644
--- a/src/facilities/clinic/clinic.tw
+++ b/src/facilities/clinic/clinic.tw
@@ -207,7 +207,7 @@
 </div>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.clinic, true)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.clinic, true)>>
 </p>
 
 <p>
diff --git a/src/facilities/clinic/nurseSelect.tw b/src/facilities/clinic/nurseSelect.tw
index f6ad039bf404f48abf8e98ab2abf596dba3ad50c..84211a0e169b22a1ea03c3bf0e92c6006fdfad19 100644
--- a/src/facilities/clinic/nurseSelect.tw
+++ b/src/facilities/clinic/nurseSelect.tw
@@ -12,4 +12,4 @@
 <br><br>[[None|Clinic][removeJob(_S.Nurse, "be the Nurse")]]
 <br><br>
 
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.clinic, "Clinic")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.clinic, "Clinic")>>
diff --git a/src/facilities/club/club.tw b/src/facilities/club/club.tw
index 2595248737fa5521982e80daea7032a1845820b2..a131b01108c8b808a04e648c103346600695aa20 100644
--- a/src/facilities/club/club.tw
+++ b/src/facilities/club/club.tw
@@ -265,6 +265,14 @@
 			_S.DJ.slaveName has been instructed to ignore flaws in <<print $clubName>>'s sluts.
 			[[Fix flaws|Club][$DJignoresFlaws = 0]]
 		<</if>>
+		<br>
+		<<if $DJnoSex != 1>>
+			_S.DJ.slaveName will slut it up when $he doesn't have enough sluts serving under $him.
+			[[Don't serve clients|Club][$DJnoSex = 1]]
+		<<else>>
+			_S.DJ.slaveName will not be slutting it up $himself even if $he has time.
+			[[Serve clients|Club][$DJnoSex = 0]]
+		<</if>>
 	<</if>>
 </div>
 
@@ -285,7 +293,7 @@
 <<includeDOM App.Facilities.Club.Stats(true)>>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.club)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.club)>>
 </p>
 
 <p>
diff --git a/src/facilities/club/clubReport.tw b/src/facilities/club/clubReport.tw
index ed03beb20a3cbab86779f808cc56f54867e68cb1..93b7460d7e30c43aca5bfdae1e0c560239e77cba 100644
--- a/src/facilities/club/clubReport.tw
+++ b/src/facilities/club/clubReport.tw
@@ -82,7 +82,7 @@
 	<<else>>
 		<<set _S.DJ.skill.DJ += random(1,Math.ceil((_S.DJ.intelligence+_S.DJ.intelligenceImplant)/15) + 8)>>
 	<</if>>
-	<<if (_DL + $clubSlavesGettingHelp < 10)>>
+	<<if (_DL + $clubSlavesGettingHelp < 10) && $DJnoSex != 1>>
 		<<if ($legendaryEntertainerID == 0) && (_S.DJ.prestige == 0) && (_S.DJ.skill.entertainment >= 100) && (_S.DJ.devotion > 50)>>
 			<<set $legendaryEntertainerID = _S.DJ.ID>>
 		<</if>>
@@ -114,23 +114,23 @@
 		/* 000-250-006 */
 		<span class='slave-name'><<= SlaveFullName(_S.DJ)>></span> is performing as the DJ in $clubName.
 		<br>&nbsp;&nbsp;&nbsp;
-		<<= App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<= App.SlaveAssignment.choosesOwnClothes(_S.DJ)>>
+		<<run tired(_S.DJ)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
-		<<= App.SlaveAssignment.drugs($slaves[$i])>>
+		<<= App.SlaveAssignment.drugs(_S.DJ)>>
 		<<include "SA relationships">>
 		<<include "SA rivalries">>
 		<br><<include "SA devotion">>
 	<<else>>
 		<<silently>>
-		<<run App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<run App.SlaveAssignment.choosesOwnClothes(_S.DJ)>>
+		<<run tired(_S.DJ)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
-		<<run App.SlaveAssignment.drugs($slaves[$i])>>
+		<<run App.SlaveAssignment.drugs(_S.DJ)>>
 		<<include "SA relationships">>
 		<<include "SA rivalries">>
 		<<include "SA devotion">>
diff --git a/src/facilities/club/djSelect.tw b/src/facilities/club/djSelect.tw
index 721284c8b0bf9be71ef29ac835897be55e377d5c..7a962c67acb2dcb087239931f6a6fc6ff834293f 100644
--- a/src/facilities/club/djSelect.tw
+++ b/src/facilities/club/djSelect.tw
@@ -11,4 +11,4 @@
 <br><br>''Appoint a DJ from your devoted slaves:''
 <br><br>[[None|Club][removeJob(_S.DJ, "be the DJ")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.club, "Club")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.club, "Club")>>
diff --git a/src/facilities/dairy/dairy.tw b/src/facilities/dairy/dairy.tw
index 396eb8897115e729bace9738ada62020150cf479..e3747483292ca12c0c085a5954c10e3bf266c578 100644
--- a/src/facilities/dairy/dairy.tw
+++ b/src/facilities/dairy/dairy.tw
@@ -20,10 +20,10 @@
 <</if>>
 
 <p>
-	<<if ($Milkmaid != 0) && ($dairyRestraintsSetting == 2)>>
-		<<setLocalPronouns $Milkmaid>>
-		$Milkmaid.slaveName has been removed from $his position as Milkmaid, since an industrialized dairy automates $his duties.
-		<<= removeJob($Milkmaid, "be the Milkmaid")>>
+	<<if ($MilkmaidID != 0) && ($dairyRestraintsSetting == 2)>>
+		<<setLocalPronouns _S.Milkmaid>>
+		_S.Milkmaid.slaveName has been removed from $his position as Milkmaid, since an industrialized dairy automates $his duties.
+		<<= removeJob(_S.Milkmaid, "be the Milkmaid")>>
 	<</if>>
 </p>
 
@@ -328,8 +328,8 @@
 		$dairyNameCaps is quiet and calm. The only sounds are faint sucking and gushing noises.
 	<<elseif _DL > 0>>
 		$dairyNameCaps is sparsely populated.
-	<<elseif $Milkmaid != 0>>
-		$Milkmaid.slaveName is alone in $dairyName, and has nothing to do but clean and maintain the equipment.
+	<<elseif $MilkmaidID != 0>>
+		_S.Milkmaid.slaveName is alone in $dairyName, and has nothing to do but clean and maintain the equipment.
 	<<else>>
 		$dairyNameCaps is empty and desolate.
 		<div class="choices" style="font-style:normal">
@@ -719,10 +719,10 @@
 
 <p>
 	<<set _facility = App.Entity.facilities.dairy>>
-	<<if ($Milkmaid != 0)>>
-		<<print App.UI.SlaveList.displayManager(_facility)>>
-		<<if canAchieveErection($Milkmaid) && $Milkmaid.pubertyXY == 1>>
-			<<setLocalPronouns $Milkmaid>>
+	<<if (_S.Milkmaid)>>
+		<<includeDOM App.UI.SlaveList.displayManager(_facility)>>
+		<<if canAchieveErection(_S.Milkmaid) && _S.Milkmaid.pubertyXY == 1>>
+			<<setLocalPronouns _S.Milkmaid>>
 			<<if $milkmaidImpregnates == 1>>
 				Keeping the cows pregnant is part of $his job.
 				<div class="choices">
@@ -762,7 +762,7 @@
 
 
 <p>
-	<<print App.UI.SlaveList.listSJFacilitySlaves(_facility)>>
+	<<includeDOM App.UI.SlaveList.listSJFacilitySlaves(_facility)>>
 </p>
 
 <p>
diff --git a/src/facilities/dairy/dairyReport.tw b/src/facilities/dairy/dairyReport.tw
index 4004c7f2d221c535da500a3e67b4d8e82c8c6538..a204ff92eb1979b930301ec36adf8bdd1739f440 100644
--- a/src/facilities/dairy/dairyReport.tw
+++ b/src/facilities/dairy/dairyReport.tw
@@ -11,8 +11,8 @@
 <!-- Statistics gathering -->
 <<set $facility = $facility || {}, $facility.dairy = initFacilityStatistics($facility.dairy)>>
 
-<<if ($Milkmaid != 0)>>
-	<<set _FLs = $slaveIndices[$Milkmaid.ID]>>
+<<if ($MilkmaidID != 0)>>
+	<<set _FLs = $slaveIndices[$MilkmaidID]>>
 	<<set _milkmaidImpregnated = 0, _milkers = 0, _cummers = 0, _breeders = 0, _prostateStim = 0>>
 
 	<<if (_DL > 0)>>
@@ -26,16 +26,16 @@
 			<<if (_slave.bellyPreg >= 500 && $dairyPregSetting > 0)>>
 				<<set _breeders++>>
 			<</if>>
-			<<if ($milkmaidImpregnates == 1) && (canPenetrate($Milkmaid)) && $Milkmaid.pubertyXY == 1>>
-				<<if (canImpreg(_slave, $Milkmaid))>>
-					<<set _slave.preg = 1, _slave.pregSource = $Milkmaid.ID, _slave.pregKnown = 1, _slave.pregWeek = 1, _milkmaidImpregnated++>>
+			<<if ($milkmaidImpregnates == 1) && (canPenetrate(_S.Milkmaid)) && _S.Milkmaid.pubertyXY == 1>>
+				<<if (canImpreg(_slave, _S.Milkmaid))>>
+					<<set _slave.preg = 1, _slave.pregSource = $MilkmaidID, _slave.pregKnown = 1, _slave.pregWeek = 1, _milkmaidImpregnated++>>
 					<<if _slave.mpreg == 1>>
 						<<set _slave.counter.anal += 10, $analTotal += 10>>
 					<<else>>
 						<<set _slave.counter.vaginal += 10, $vaginalTotal += 10>>
 					<</if>>
 					<<set _slave.pregType = setPregType(_slave)>>
-					<<set WombImpregnate(_slave, _slave.pregType, $Milkmaid.ID, 1)>>
+					<<set WombImpregnate(_slave, _slave.pregType, $MilkmaidID, 1)>>
 				<</if>>
 			<</if>>
 			/* how much effort the MM must take to force a slave into a stall */
@@ -63,7 +63,7 @@
 			<<if _slave.belly >= 5000>>
 				<<set _MMWorkout++>>
 			<</if>>
-			<<if !canWalk(_slave)>> /* big bonus if they can't move themselves and are fat as cows */
+			<<if !canMove(_slave)>> /* big bonus if they can't move themselves and are fat as cows */
 				<<if _slave.weight >= 70>>
 					<<set _MMWorkout += 2>>
 				<</if>>
@@ -79,8 +79,24 @@
 				<<if _slave.belly >= 5000>>
 					<<set _MMWorkout += 2>>
 				<</if>>
+			<<elseif !canWalk(_slave)>> /* smaller bonus if they are fat as cows and need assistanace with moving */
+				<<if _slave.weight >= 70>>
+					<<set _MMWorkout += 1>>
+				<</if>>
+				<<if _slave.weight >= 160>>
+					<<set _MMWorkout += 1>>
+				<</if>>
+				<<if _slave.boobs >= 20000>>
+					<<set _MMWorkout += 1>>
+				<</if>>
+				<<if _slave.balls >= 30>>
+					<<set _MMWorkout += 1>>
+				<</if>>
+				<<if _slave.belly >= 5000>>
+					<<set _MMWorkout += 1>>
+				<</if>>
 			<</if>>
-			<<if ($dairyStimulatorsSetting < 2) && ($Milkmaid.dick > 4) && (canPenetrate($Milkmaid)) && _prostateStim != 1>>
+			<<if ($dairyStimulatorsSetting < 2) && (_S.Milkmaid.dick > 4) && (canPenetrate(_S.Milkmaid)) && _prostateStim != 1>>
 				<<if (_slave.balls > 0) && (_slave.prostate > 0)>>
 					<<set _prostateStim = 1>>
 				<</if>>
@@ -88,67 +104,67 @@
 		<</for>>
 	<</if>>
 
-	<<if $slaves[_FLs].health.condition < 90>>
-		<<set $slaves[_FLs].curatives = 2>>
+	<<if _S.Milkmaid.health.condition < 90>>
+		<<set _S.Milkmaid.curatives = 2>>
 	<<else>>
-		<<set $slaves[_FLs].curatives = 0>>
+		<<set _S.Milkmaid.curatives = 0>>
 	<</if>>
-	<<if ($slaves[_FLs].diet != "healthy")>>
-		<<set ($slaves[_FLs].diet = "healthy")>>
+	<<if (_S.Milkmaid.diet != "healthy")>>
+		<<set (_S.Milkmaid.diet = "healthy")>>
 	<</if>>
-	<<if $slaves[_FLs].devotion <= 60>>
-		<<set $slaves[_FLs].devotion += 5>>
+	<<if _S.Milkmaid.devotion <= 60>>
+		<<set _S.Milkmaid.devotion += 5>>
 	<</if>>
-	<<if $slaves[_FLs].trust < 60>>
-		<<set $slaves[_FLs].trust += 5>>
+	<<if _S.Milkmaid.trust < 60>>
+		<<set _S.Milkmaid.trust += 5>>
 	<</if>>
-	<<if ($slaves[_FLs].skill.oral > 0)>>
-		<<set $milkmaidDevotionBonus += Math.trunc($slaves[_FLs].skill.oral/30)>>
+	<<if (_S.Milkmaid.skill.oral > 0)>>
+		<<set $milkmaidDevotionBonus += Math.trunc(_S.Milkmaid.skill.oral/30)>>
 	<</if>>
-	<<if ($slaves[_FLs].health.condition >= 80)>>
+	<<if (_S.Milkmaid.health.condition >= 80)>>
 		<<set $milkmaidHealthBonus++>>
 	<</if>>
-	<<if $slaves[_FLs].rules.living != "luxurious">>
-		<<set $slaves[_FLs].rules.living = "luxurious">>
+	<<if _S.Milkmaid.rules.living != "luxurious">>
+		<<set _S.Milkmaid.rules.living = "luxurious">>
 	<</if>>
-	<<if $slaves[_FLs].fetishStrength <= 95>>
+	<<if _S.Milkmaid.fetishStrength <= 95>>
 		<<if _milkers >= _cummers && _milkers >= _breeders>>
-			<<if $slaves[_FLs].fetish != "boobs">>
-				<<if fetishChangeChance($slaves[_FLs]) > random(0,100)>>
-					<<set _FLsFetish = 1, $slaves[_FLs].fetishKnown = 1, $slaves[_FLs].fetish = "boobs">>
+			<<if _S.Milkmaid.fetish != "boobs">>
+				<<if fetishChangeChance(_S.Milkmaid) > random(0,100)>>
+					<<set _FLsFetish = 1, _S.Milkmaid.fetishKnown = 1, _S.Milkmaid.fetish = "boobs">>
 				<</if>>
-			<<elseif $slaves[_FLs].fetishKnown == 0>>
-				<<set _FLsFetish = 1, $slaves[_FLs].fetishKnown = 1>>
+			<<elseif _S.Milkmaid.fetishKnown == 0>>
+				<<set _FLsFetish = 1, _S.Milkmaid.fetishKnown = 1>>
 			<<else>>
-				<<set _FLsFetish = 2, $slaves[_FLs].fetishStrength += 4>>
+				<<set _FLsFetish = 2, _S.Milkmaid.fetishStrength += 4>>
 			<</if>>
 		<<elseif _cummers >= _breeders>>
-			<<if $slaves[_FLs].fetish != "cumslut">>
-				<<if fetishChangeChance($slaves[_FLs]) > random(0,100)>>
-					<<set _FLsFetish = 3, $slaves[_FLs].fetishKnown = 1, $slaves[_FLs].fetish = "cumslut">>
+			<<if _S.Milkmaid.fetish != "cumslut">>
+				<<if fetishChangeChance(_S.Milkmaid) > random(0,100)>>
+					<<set _FLsFetish = 3, _S.Milkmaid.fetishKnown = 1, _S.Milkmaid.fetish = "cumslut">>
 				<</if>>
-			<<elseif $slaves[_FLs].fetishKnown == 0>>
-				<<set _FLsFetish = 3, $slaves[_FLs].fetishKnown = 1>>
+			<<elseif _S.Milkmaid.fetishKnown == 0>>
+				<<set _FLsFetish = 3, _S.Milkmaid.fetishKnown = 1>>
 			<<else>>
-				<<set _FLsFetish = 4, $slaves[_FLs].fetishStrength += 4>>
+				<<set _FLsFetish = 4, _S.Milkmaid.fetishStrength += 4>>
 			<</if>>
 		<<else>>
-			<<if $slaves[_FLs].fetish != "pregnancy">>
-				<<if fetishChangeChance($slaves[_FLs]) > random(0,100)>>
-					<<set _FLsFetish = 5, $slaves[_FLs].fetishKnown = 1, $slaves[_FLs].fetish = "pregnancy">>
+			<<if _S.Milkmaid.fetish != "pregnancy">>
+				<<if fetishChangeChance(_S.Milkmaid) > random(0,100)>>
+					<<set _FLsFetish = 5, _S.Milkmaid.fetishKnown = 1, _S.Milkmaid.fetish = "pregnancy">>
 				<</if>>
-			<<elseif $slaves[_FLs].fetishKnown == 0>>
-				<<set _FLsFetish = 5, $slaves[_FLs].fetishKnown = 1>>
+			<<elseif _S.Milkmaid.fetishKnown == 0>>
+				<<set _FLsFetish = 5, _S.Milkmaid.fetishKnown = 1>>
 			<<else>>
-				<<set _FLsFetish = 6, $slaves[_FLs].fetishStrength += 4>>
+				<<set _FLsFetish = 6, _S.Milkmaid.fetishStrength += 4>>
 			<</if>>
 		<</if>>
 	<</if>>
-	<<run getSlaveStatisticData($Milkmaid, $facility.dairy)>>
-	<<set $Milkmaid = $slaves[_FLs], $i = _FLs>>
-	<<setLocalPronouns $Milkmaid>>
-	&nbsp;&nbsp;&nbsp;&nbsp;<<= SlaveFullName($Milkmaid)>> is serving as your Milkmaid.
-	<<if $Milkmaid.relationship == -3 && $Milkmaid.devotion > 50>>
+	<<run getSlaveStatisticData(_S.Milkmaid, $facility.dairy)>>
+	<<set $i = _FLs>>
+	<<setLocalPronouns _S.Milkmaid>>
+	&nbsp;&nbsp;&nbsp;&nbsp;<<= SlaveFullName(_S.Milkmaid)>> is serving as your Milkmaid.
+	<<if _S.Milkmaid.relationship == -3 && _S.Milkmaid.devotion > 50>>
 		<<set $milkmaidDevotionBonus += 2, $milkmaidTrustBonus += 2>>
 		$He tries $his best to be your perfect farm<<= $wife>>.
 	<</if>>
@@ -159,16 +175,16 @@
 		<<else>>
 			fills the _milkmaidImpregnated cows' cunts with $his cum.
 		<</if>>
-		<<if $Milkmaid.career == "a breeding bull">>
+		<<if _S.Milkmaid.career == "a breeding bull">>
 			$He was conditioned to fill empty wombs, so $he takes a @@.hotpink;deep pleasure@@ in $his job.
-			<<set $slaves[_FLs].devotion++>>
-		<<elseif ($Milkmaid.fetish == "pregnancy" && $Milkmaid.fetishKnown == 1)>>
+			<<set _S.Milkmaid.devotion++>>
+		<<elseif (_S.Milkmaid.fetish == "pregnancy" && _S.Milkmaid.fetishKnown == 1)>>
 			$He has a pregnancy fetish, so $he finds $his job @@.hotpink;quite enjoyable,@@ especially when $he gets to watch the cows swell with $his children.
-			<<set $slaves[_FLs].devotion += Math.ceil($slaves[_FLs].fetishStrength/25)>>
-			<<set $slaves[_FLs].fetishStrength += 2>>
+			<<set _S.Milkmaid.devotion += Math.ceil(_S.Milkmaid.fetishStrength/25)>>
+			<<set _S.Milkmaid.fetishStrength += 2>>
 		<</if>>
-		<<set _Tmult = (_milkmaidImpregnated * 10), $slaves[_FLs].counter.penetrative += _Tmult, $Milkmaid.counter.penetrative += _Tmult, $penetrativeTotal += _Tmult>>
-		<<set $slaves[_FLs].need = 0>>
+		<<set _Tmult = (_milkmaidImpregnated * 10), _S.Milkmaid.counter.penetrative += _Tmult, $penetrativeTotal += _Tmult>>
+		<<set _S.Milkmaid.need = 0>>
 	<</if>>
 	<<if (_FLsFetish == 1)>>
 		In $his line of work, $he touches more breasts than even you do. $He lives in an atmosphere of quivering, heaving, milky breastflesh; of girls who shudder and moan when $he touches their creamy nipples. $He has @@.lightcoral;become more of a breast $girl.@@
@@ -183,58 +199,58 @@
 	<<elseif (_FLsFetish == 6)>>
 		It's a hard life, washing bellies and inspecting pussies, but it does @@.lightsalmon;make $him more of a pregnancy fetishist.@@
 	<</if>>
-	<<if ($Milkmaid.muscles > 30)>>
+	<<if (_S.Milkmaid.muscles > 30)>>
 		<<set $milkmaidHealthBonus++>>
 		$His muscles help $him handle the fattest or most reluctant cow.
 	<</if>>
-	<<if (_MMWorkout > random(1,30-$Milkmaid.geneticQuirks.mLoss+$Milkmaid.geneticQuirks.mGain)) && $Milkmaid.muscles < 60>>
+	<<if (_MMWorkout > random(1,30-_S.Milkmaid.geneticQuirks.mLoss+_S.Milkmaid.geneticQuirks.mGain)) && _S.Milkmaid.muscles < 60>>
 		Constantly having to wrestle unruly or aiding heavy cows into their stalls forces $him to @@.lime;build muscle.@@
-		<<set $slaves[_FLs].muscles++>>
+		<<set _S.Milkmaid.muscles++>>
 	<</if>>
-	<<if ($Milkmaid.skill.oral > 30)>>
+	<<if (_S.Milkmaid.skill.oral > 30)>>
 		$His skilled tongue helps $him keep $his cattle happy.
 	<</if>>
-	<<if ($Milkmaid.skill.oral < 90)>>
-		<<= SkillIncrease.Oral($slaves[_FLs], 3)>>
+	<<if (_S.Milkmaid.skill.oral < 90)>>
+		<<= SkillIncrease.Oral(_S.Milkmaid, 3)>>
 	<</if>>
-	<<if ($Milkmaid.sexualQuirk == "caring")>>
+	<<if (_S.Milkmaid.sexualQuirk == "caring")>>
 		<<set $milkmaidTrustBonus++>>
 		$He's very caring, and does $his best to get the cows to trust $him.
 	<</if>>
-	<<if ($Milkmaid.behavioralQuirk == "funny")>>
+	<<if (_S.Milkmaid.behavioralQuirk == "funny")>>
 		<<set $milkmaidTrustBonus++>>
 		$He's funny, and does $his best to get the cows to trust $him by keeping them laughing.
 	<</if>>
-	<<if setup.milkmaidCareers.includes($Milkmaid.career)>>
+	<<if setup.milkmaidCareers.includes(_S.Milkmaid.career)>>
 		<<set $milkmaidHealthBonus++>>
 		$He has career experience dealing with milk animals.
-	<<elseif $Milkmaid.skill.milkmaid >= $masteredXP>>
+	<<elseif _S.Milkmaid.skill.milkmaid >= $masteredXP>>
 		<<set $milkmaidHealthBonus++>>
 		$He has experience harvesting slave products from working for you.
 	<<else>>
-		<<set $slaves[_FLs].skill.milkmaid += random(1,Math.ceil(($Milkmaid.intelligence+$Milkmaid.intelligenceImplant)/15) + 8)>>
+		<<set _S.Milkmaid.skill.milkmaid += random(1,Math.ceil((_S.Milkmaid.intelligence+_S.Milkmaid.intelligenceImplant)/15) + 8)>>
 	<</if>>
 	<<if _prostateStim == 1>>
 		$He uses $his turgid cock to give prostate stimulation to slaves that need help ejaculating.
-		<<set $slaves[_FLs].need -= 50>>
+		<<set _S.Milkmaid.need -= 50>>
 	<</if>>
 	<<set $milkmaidDevotionThreshold += (5*$milkmaidDevotionBonus), $milkmaidTrustThreshold += (5*$milkmaidTrustBonus)>>
 	<<for _slave range _slaves>>
 		<<set $i = $slaveIndices[_slave.ID]>>
-		<<if $Milkmaid.rivalryTarget == _slave.ID>>
+		<<if _S.Milkmaid.rivalryTarget == _slave.ID>>
 			<<setLocalPronouns _slave 2>>
-			$He either neglects or harasses $his <<print rivalryTerm($Milkmaid)>>, _slave.slaveName, making sure _he2 is unhappy and uncomfortable.
+			$He either neglects or harasses $his <<print rivalryTerm(_S.Milkmaid)>>, _slave.slaveName, making sure _he2 is unhappy and uncomfortable.
 			<<set _slave.devotion -= 3, _slave.trust -= 3>>
 			<<if random(1,100) > 65>>
-				<<set $Milkmaid.rivalry++, $slaves[_FLs].rivalry++, _slave.rivalry++>>
+				<<set _S.Milkmaid.rivalry++, _slave.rivalry++>>
 			<</if>>
-		<<elseif $Milkmaid.relationshipTarget == _slave.ID>>
+		<<elseif _S.Milkmaid.relationshipTarget == _slave.ID>>
 			<<setLocalPronouns _slave 2>>
-			$He dotes over $his <<print relationshipTerm($Milkmaid)>>, _slave.slaveName, making sure _he2 is happy and comfortable.
+			$He dotes over $his <<print relationshipTerm(_S.Milkmaid)>>, _slave.slaveName, making sure _he2 is happy and comfortable.
 			<<set _slave.devotion++, _slave.trust++>>
-		<<elseif areRelated($Milkmaid, _slave)>>
+		<<elseif areRelated(_S.Milkmaid, _slave)>>
 			<<setLocalPronouns _slave 2>>
-			$He pays special attention to $his <<print relativeTerm($Milkmaid,_slave)>>, _slave.slaveName, making sure _he2 is well kept and happy.
+			$He pays special attention to $his <<print relativeTerm(_S.Milkmaid,_slave)>>, _slave.slaveName, making sure _he2 is well kept and happy.
 			<<set _slave.trust++>>
 		<</if>>
 		<<if _slave.prestigeDesc == "$He is remembered for winning best in show as a dairy cow.">>
@@ -255,7 +271,7 @@
 		<</if>>
 		<<if _slave.prestigeDesc == "$He is remembered for winning best in show as a breeder." && _slave.bellyPreg >= 1500>>
 			<<setLocalPronouns _slave 2>>
-			$He spends extra time with _slave.slaveName, the well-known breeder. $Milkmaid.slaveName is fascinated by _his2 growing pregnancy and popular womb. $He makes sure _his2 belly and its occupants are nice and comfortable.
+			$He spends extra time with _slave.slaveName, the well-known breeder. _S.Milkmaid.slaveName is fascinated by _his2 growing pregnancy and popular womb. $He makes sure _his2 belly and its occupants are nice and comfortable.
 			<<set _slave.devotion += 3, _slave.trust += 3>>
 		<</if>>
 	<</for>>
@@ -263,7 +279,7 @@
 
 <<set _Tadd = ($bioreactorsXY + $bioreactorsXX + $bioreactorsHerm + $bioreactorsBarren)>>
 <<if (_DL + _Tadd > 0)>>
-	<<if ($Milkmaid != 0)>><br><br><</if>>
+	<<if ($MilkmaidID != 0)>><br><br><</if>>
 	&nbsp;&nbsp;&nbsp;&nbsp;<<if (_DL != 1)>>''There are _DL cows in $dairyName.''<<else>>''There is one cow in $dairyName.''<</if>>
 	<<if ($dairyRestraintsSetting > 1)>>
 		The facility functions as an industrial slave products factory.
@@ -273,32 +289,32 @@
 	<</if>>
 <</if>>
 
-<<if ($Milkmaid != 0)>>
+<<if ($MilkmaidID != 0)>>
 	<<set $i = _FLs>> /* apply following SA passages to facility leader */
 	<<if $showEWD != 0>>
 		<br><br>
 		/* 000-250-006 */
 		<<if $seeImages && $seeReportImages>>
 		<div class="imageRef tinyImg">
-			<<= SlaveArt($slaves[_FLs], 0, 0)>>
+			<<= SlaveArt(_S.Milkmaid, 0, 0)>>
 		</div>
 		<</if>>
 		/* 000-250-006 */
-		<span class='slave-name'><<= SlaveFullName($slaves[_FLs])>></span> is serving as your Milkmaid.
+		<span class='slave-name'><<= SlaveFullName(_S.Milkmaid)>></span> is serving as your Milkmaid.
 		<br>&nbsp;&nbsp;&nbsp;
 		<<= App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<run tired(_S.Milkmaid)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
-		<<= App.SlaveAssignment.drugs($slaves[$i])>>
+		<<= App.SlaveAssignment.drugs(_S.Milkmaid)>>
 		<<include "SA relationships">>
 		<<include "SA rivalries">>
 		<br><<include "SA devotion">>
 	<<else>>
 		<<silently>>
-		<<run App.SlaveAssignment.choosesOwnClothes($slaves[$i])>>
-		<<run tired($slaves[$i])>>
+		<<run App.SlaveAssignment.choosesOwnClothes(_S.Milkmaid)>>
+		<<run tired(_S.Milkmaid)>>
 		<<include "SA rules">>
 		<<include "SA diet">>
 		<<include "SA long term effects">>
@@ -308,7 +324,6 @@
 		<<include "SA devotion">>
 		<</silently>>
 	<</if>>
-	<<set $Milkmaid = $slaves[_FLs]>>
 <</if>>
 
 <<set _oldCash = $cash>>
@@ -1076,7 +1091,7 @@ $dairyNameCaps produced <<print _milkWeek+_outputMilk>> liters of milk<<if _cumW
 		<<set $failedElite += 5>>
 	<</if>>
 	<<if _milkmaidImpregnates == 1>>
-		The Societal Elite know you've ordered $Milkmaid.slaveName to impregnate your cows. @@.red;They are not amused by your disinterest in eugenics.@@
+		The Societal Elite know you've ordered _S.Milkmaid.slaveName to impregnate your cows. @@.red;They are not amused by your disinterest in eugenics.@@
 		<<set $failedElite += 10>>
 	<</if>>
 <</if>>
@@ -1185,6 +1200,6 @@ $dairyNameCaps produced <<print _milkWeek+_outputMilk>> liters of milk<<if _cumW
 		<</replace>>
 	<</timed>>
 <</if>>
-<<if (_DL + _Tadd) > 0 || $Milkmaid != 0>>
+<<if (_DL + _Tadd) > 0 || $MilkmaidID != 0>>
 	<br><br>
 <</if>>
diff --git a/src/facilities/dairy/freeRangeDairyAssignmentScene.tw b/src/facilities/dairy/freeRangeDairyAssignmentScene.tw
index 113cb90f27a87c8e0bc2e849f1f892a6fb4dc64a..9eb607a17169b4682b578bbeae9ee13d3cdb0cf1 100644
--- a/src/facilities/dairy/freeRangeDairyAssignmentScene.tw
+++ b/src/facilities/dairy/freeRangeDairyAssignmentScene.tw
@@ -22,7 +22,7 @@
 /* AROUSAL WHEN ENTERING */
 <<set _aroused = false >>
 <<if App.Entity.facilities.dairy.employeesIDs().size > 1 && canSee($activeSlave)>>
-	While <<if canWalk($activeSlave) >>walking<<else>>being carried<</if>> to $his designated stall, $he passes the other cows currently at their milking machines. Not being hurried, $he occasionally watches a cow in detail.
+	While <<if canWalk($activeSlave)>>walking<<elseif canMove($activeSlave)>>being helped<<else>>being carried<</if>> to $his designated stall, $he passes the other cows currently at their milking machines. Not being hurried, $he occasionally watches a cow in detail.
 	<div>
 		<<if $activeSlave.fetish == "boobs">>
 			/* TODO: check if there actually are lactating cows at the dairy */
@@ -75,8 +75,8 @@
 					<<else>>
 						_He2 lacks a prostate, so the automatic sodomizer is forced to pound _him2 mercilessly to make _him2 cum.
 					<</if>>
-				<<elseif ($Milkmaid.dick > 4) && (canPenetrate($Milkmaid))>>
-					$Milkmaid.slaveName quickly steps in and penetrates _his2 rear to push _him2 powerfully over the edge.
+				<<elseif (_S.Milkmaid.dick > 4) && (canPenetrate(_S.Milkmaid))>>
+					_S.Milkmaid.slaveName quickly steps in and penetrates _his2 rear to push _him2 powerfully over the edge.
 				<<else>>
 					_His2 butthole opens and closes.
 				<</if>>
@@ -152,17 +152,17 @@
 	</div>
 
 	<div style="clear:both">
-		<<if $Milkmaid && $seeImages == 1>>
+		<<if _S.Milkmaid && $seeImages == 1>>
 			<div class="imageRef medImg">
-				<<= SlaveArt($Milkmaid, 2, 0)>>
+				<<= SlaveArt(_S.Milkmaid, 2, 0)>>
 			</div>
 		<</if>>
 		<div>
 		The only "furniture" in the stall looks like a dentist's chair. Despite the medical appearance, when $he <<if $activeSlave.devotion > 90>>eagerly<<elseif $activeSlave.slaveName < 40>>hesitantly<</if>> gets into the chair it turns out to be quite comfortable.
-		<<if $Milkmaid == 0>>
+		<<if _S.Milkmaid == 0>>
 			Automated machinery springs to life, preparing $him for milking.
 		<<else>>
-			Your appointed milkmaid $Milkmaid.slaveName helps $activeSlave.slaveName with installing the milking devices.
+			Your appointed milkmaid _S.Milkmaid.slaveName helps $activeSlave.slaveName with installing the milking devices.
 		<</if>>
 		<<if $activeSlave.lactation > 0 >>
 		Suction cups are attached <<if $activeSlave.nipples == "fuckable">>over<<else>>to<</if>> $his nipples.
@@ -238,19 +238,19 @@
 
 <p>
 	Sexual stimulation increases product output.
-	<<if $Milkmaid == 0 && $dairyStimulatorsSetting == 0>>
+	<<if _S.Milkmaid == 0 && $dairyStimulatorsSetting == 0>>
 		Unfortunately, there is neither a milkmaid providing personal assistance nor automatic sodomizers installed in your dairy.<br>
 	<<else>>
-		<<if $Milkmaid != 0>>
-			<<setLocalPronouns $Milkmaid 2>>
-			Your milkmaid $Milkmaid.slaveName is working in the dairy. Whenever a cow needs _his2 assistance, _he2 is happy to help.
-			<<if $Milkmaid.dick > 1 && canPenetrate($Milkmaid)>>
+		<<if $MilkmaidID != 0>>
+			<<setLocalPronouns _S.Milkmaid 2>>
+			Your milkmaid _S.Milkmaid.slaveName is working in the dairy. Whenever a cow needs _his2 assistance, _he2 is happy to help.
+			<<if _S.Milkmaid.dick > 1 && canPenetrate(_S.Milkmaid)>>
 				Most often, _he2 uses _his2 cock to penetrate the cows.
 			<<else>>
 				Lacking penile equipment of appropriate size, _he2 relies on _his2 tongue to stimulate the cow's sexual organs.
 			<</if>>
 			_He2 is quite skilled in giving mammary massages and handjobs, too.
-			<<if $milkmaidImpregnates == 1 && canPenetrate($Milkmaid) && $Milkmaid.pubertyXY == 1 && $Milkmaid.ballType == "human">>
+			<<if $milkmaidImpregnates == 1 && canPenetrate(_S.Milkmaid) && _S.Milkmaid.pubertyXY == 1 && _S.Milkmaid.ballType == "human">>
 				If at all possible, it is _his2 duty to thoroughly knock up _his2 charges.
 			<</if>>
 			<br>
diff --git a/src/facilities/dairy/industrialDairyAssignmentScene.tw b/src/facilities/dairy/industrialDairyAssignmentScene.tw
index 87706ae8439dad5212d902ec86c0d121b0c58486..f11092ded5895057c367edd4fa235544afd3bf37 100644
--- a/src/facilities/dairy/industrialDairyAssignmentScene.tw
+++ b/src/facilities/dairy/industrialDairyAssignmentScene.tw
@@ -237,10 +237,10 @@ When $activeSlave.slaveName reports to the dairy, it's to the <<if canSee($activ
 	<<else>>
 		$He turns and tries to run, so
 	<</if>>
-	<<if ($Milkmaid == 0)>>
+	<<if (_S.Milkmaid == 0)>>
 		you are
 	<<else>>
-		$Milkmaid.slaveName is
+		_S.Milkmaid.slaveName is
 	<</if>>
 	obliged to
 	<<if (!isAmputee($activeSlave))>>
@@ -275,10 +275,10 @@ When $activeSlave.slaveName reports to the dairy, it's to the <<if canSee($activ
 	<<else>>
 		$He turns and tries to run, so
 	<</if>>
-	<<if ($Milkmaid == 0)>>
+	<<if (_S.Milkmaid == 0)>>
 		you are
 	<<else>>
-		$Milkmaid.slaveName is
+		_S.Milkmaid.slaveName is
 	<</if>>
 	obliged to
 	<<if (!isAmputee($activeSlave))>>
diff --git a/src/facilities/dairy/milkmaidSelect.tw b/src/facilities/dairy/milkmaidSelect.tw
index 1123fb3d52790e7d81708c6330d72b4e06133fe1..02bfe668d0e973b28a3b8deef5ae6eb7d2e896b4 100644
--- a/src/facilities/dairy/milkmaidSelect.tw
+++ b/src/facilities/dairy/milkmaidSelect.tw
@@ -1,15 +1,14 @@
 :: Milkmaid Select [nobr jump-to-safe jump-hidden jump-from-safe]
 
 <<set $nextButton = "Back", $nextLink = "Dairy", $encyclopedia = "Milkmaid">>
-<<if ($Milkmaid != 0)>>
-	<<set $Milkmaid = getSlave($Milkmaid.ID)>>
-	<<setLocalPronouns $Milkmaid>>
-	<span class='slave-name'><<= SlaveFullName($Milkmaid)>></span> is serving as your Milkmaid.<br><br>$He will look after your livestock, helping them give milk<<if $seeDicks > 0>> and semen<</if>>.
+<<if ($MilkmaidID != 0)>>
+	<<setLocalPronouns _S.Milkmaid>>
+	<span class='slave-name'><<= SlaveFullName(_S.Milkmaid)>></span> is serving as your Milkmaid.<br><br>$He will look after your livestock, helping them give milk<<if $seeDicks > 0>> and semen<</if>>.
 <<else>>
 	You have not selected a Milkmaid.
 <</if>>
 
 <br><br>''Appoint a Milkmaid from your obedient slaves:''
-<br><br>[[None|Dairy][removeJob($Milkmaid, "be the Milkmaid")]]
+<br><br>[[None|Dairy][removeJob(_S.Milkmaid, "be the Milkmaid")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.dairy, "Dairy")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.dairy, "Dairy")>>
diff --git a/src/facilities/farmyard/farmerSelect.tw b/src/facilities/farmyard/farmerSelect.tw
index 8ea9151878320174b1fb79caa18beaa0fa466853..dfaadbb1a96df02ad478825766207ce21efd0ea8 100644
--- a/src/facilities/farmyard/farmerSelect.tw
+++ b/src/facilities/farmyard/farmerSelect.tw
@@ -1,15 +1,14 @@
 :: Farmer Select [nobr jump-to-safe jump-hidden jump-from-safe]
 
 <<set $nextButton = "Back", $nextLink = "Farmyard", $encyclopedia = "Farmer">>
-<<if ($Farmer != 0)>>
-	<<set $Farmer = getSlave($Farmer.ID)>>
-	<<setLocalPronouns $Farmer>>
-	<span class='slave-name'><<= SlaveFullName($Farmer)>></span> is serving as your Farmer.<br><br>$He will tend to your crops and animals.
+<<if ($FarmerID != 0)>>
+	<<setLocalPronouns _S.Farmer>>
+	<span class='slave-name'><<= SlaveFullName(_S.Farmer)>></span> is serving as your Farmer.<br><br>$He will tend to your crops and animals.
 <<else>>
 	You have not selected a Farmer.
 <</if>>
 
 <br><br>''Appoint a Farmer from your obedient slaves:''
-<br><br>[[None|Farmyard][removeJob($Farmer, "be the Farmer")]]
+<br><br>[[None|Farmyard][removeJob(_S.Farmer, Job.FARMER)]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.farmyard, "Farmyard")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.farmyard, "Farmyard")>>
diff --git a/src/facilities/farmyard/farmyard.css b/src/facilities/farmyard/farmyard.css
new file mode 100644
index 0000000000000000000000000000000000000000..1dfe466c3819a232b8fc6cc22803b0be5a12f319
--- /dev/null
+++ b/src/facilities/farmyard/farmyard.css
@@ -0,0 +1,39 @@
+/* MOST LIKELY TEMPORARY UNTIL I FIND A BETTER SPOT */
+
+.farmyard-intro {
+	margin-bottom: 1em;
+}
+
+.farmyard-expand {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-menials {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-rules {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-upgrades {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-animals {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-slaves {
+	margin-top: 1em;
+	margin-bottom: 1em;
+}
+
+.farmyard-rename {
+	margin-top: 1em;
+}
diff --git a/src/facilities/farmyard/farmyard.js b/src/facilities/farmyard/farmyard.js
new file mode 100644
index 0000000000000000000000000000000000000000..7f674f5ba94ba8ecc990a8dd01ea629842d7e0f0
--- /dev/null
+++ b/src/facilities/farmyard/farmyard.js
@@ -0,0 +1,864 @@
+App.Facilities.Farmyard.farmyard = function() {
+	const
+		frag = new DocumentFragment();
+
+	V.nextButton = "Back to Main";
+	V.nextLink = "Main";
+	V.returnTo = "Farmyard";
+	V.encyclopedia = "Farmyard";
+
+	if (V.farmyardName !== "the Farmyard") {
+		V.farmyardNameCaps = V.farmyardName.replace("the ", "The ");
+	}
+
+	App.UI.DOM.appendNewElement("div", frag, intro(), "farmyard-intro");
+	App.UI.DOM.appendNewElement("div", frag, expand(), "farmyard-expand");
+	App.UI.DOM.appendNewElement("div", frag, menials(), "farmyard-menials");
+	App.UI.DOM.appendNewElement("div", frag, rules(), "farmyard-rules");
+	App.UI.DOM.appendNewElement("div", frag, upgrades(), "farmyard-upgrades");
+	App.UI.DOM.appendNewElement("div", frag, animals(), "farmyard-animals");
+	App.UI.DOM.appendNewElement("div", frag, App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.farmyard), "farmyard-slaves");
+	App.UI.DOM.appendNewElement("div", frag, rename(), "farmyard-rename");
+
+	App.UI.SlaveList.ScrollPosition.restore();
+
+	function intro() {
+		const
+			frag = new DocumentFragment(),
+
+			desc = App.UI.DOM.makeElement("div", '', "scene-intro"),
+			link = App.UI.DOM.makeElement("div", '', "indent"),
+
+			count = App.Entity.facilities.farmyard.totalEmployeesCount;
+
+		desc.append(`${V.farmyardNameCaps} is an oasis of growth in the midst of the jungle of steel and concrete that is ${V.arcologies[0].name}. Animals are kept in pens, tended to by your slaves, while ${V.farmyardUpgrades.hydroponics ? `rows of hydroponics equipment` : `makeshift fields`} grow crops. `);
+
+		switch (V.farmyardDecoration) {
+			case "Roman Revivalist":
+				desc.append(`Its red tiles and white stone walls are the very picture of a Roman farm villa's construction, as are the marble statues and reliefs. Saturn and Ceres look over the prosperity of the fields${V.seeBestiality ? `. Mercury watches over the health of the animals, and Feronia ensures strong litters in your slaves.` : `, and Mercury watches over the health of the animals.`} The slaves here are all looked after well, as they have one of the most important jobs in ${V.arcologies[0].name}.`);
+				break;
+			case "Aztec Revivalist":
+				desc.append(`It can't completely recreate the floating farms in the ancient Aztec fashion, but it comes as close as it can, shallow pseudo-canals dividing each field into multiple sections. Smooth stone and colorful murals cover the walls, depicting bloody stories of gods and mortals alike.`);
+				break;
+			case "Egyptian Revivalist":
+				desc.append(`It does its best to capture the wide open nature of ancient Egyptian farms, including mimicking the irrigation systems fed by the Nile. The stone walls are decorated with murals detailing its construction and your prowess in general, ${V.seeBestiality ? `with animal-bloated slaves featured prominently.` : `hieroglyphs spelling out a volumes of praise.`}`);
+				break;
+			case "Edo Revivalist":
+				desc.append(`It does its best to mimic the rice patties and thatch roofed buildings of the Edo period despite the wide variety of crops tended by various slaves. Not every crop can thrive in flooded fields, but the ones that can take advantage of your attention to detail.`);
+				break;
+			case "Arabian Revivalist":
+				desc.append(`Large plots of olive trees and date palms line the outer edges of the main crop area, while a combination of wheat, flax, and barley occupies the interior space. Irrigation canals snake through the area, ensuring every inch of cropland is well-watered.`);
+				break;
+			case "Chinese Revivalist":
+				desc.append(`It does its best to capture the terraces that covered the ancient Chinese hills and mountains, turning every floor into ribbons of fields following a slight incline. Slaves wade through crops that can handle flooding and splash through the irrigation of the others when they aren't tending to${V.seeBestiality ? ` or breeding with` : ``} your animals.`);
+				break;
+			case "Chattel Religionist":
+				desc.append(`It runs like a well oiled machine, slaves bent in humble service as they tend crops grown on the Prophet's command, or see to the animals' needs. Their clothing is tucked up and out of the way as they see to their tasks, keeping them clean as they work ${V.seeBestiality ? `around animal-bloated bellies ` : ``}as divine will dictates.`);
+				break;
+			case "Degradationist":
+				desc.append(`It is constructed less as a converted warehouse and more as something to visit, allowing guests to enjoy the spectacle of slaves ${V.seeBestiality ? `being pounded by eager animals` : `elbow deep in scrubbing animal waste`} to their satisfaction.`);
+				break;
+			case "Repopulation Focus":
+				desc.append(`It teems with life, both in the belly of every animal and the belly of every slave, though the latter makes tending the fields difficult. They're ordered to take care, as they carry the future ${V.seeBestiality ? `of this farm` : `of the arcology`} in their bellies.`);
+				break;
+			case "Eugenics":
+				desc.append(`It holds a wide variety of crops and animals, but the best of the best is easy to find. They're set apart from the others, given only the best care and supplies${V.seeBestiality ? ` and bred with only the highest quality slaves` : ``}, while the sub-par stock is neglected off to the side.`);
+				break;
+			case "Asset Expansionist":
+				desc.append(`It is not easy to look after animals and till fields with such enormous body parts, but your slaves are diligent regardless, working hard to provide food and livestock for the arcology.`);
+				break;
+			case "Transformation Fetishist":
+				// desc.append(`TODO:`);
+				break;
+			case "Gender Radicalist":
+				// desc.append(`TODO:`);
+				break;
+			case "Gender Fundamentalist":
+				// desc.append(`TODO:`);
+				break;
+			case "Physical Idealist":
+				desc.append(`Its animals are in exceptional shape, their coats unable to hide how muscular they are, requiring your slaves to be equally toned to control them. There's plenty of space for their exercise as well${V.seeBestiality ? ` and an abundance of curatives for the slaves full of their fierce, kicking offspring` : ``}.`);
+				break;
+			case "Supremacist":
+				desc.append(`It is a clean and orderly operation, stables and cages mucked by a multitude of inferior slaves, along with grooming your animals and harvesting your crops.`);
+				break;
+			case "Subjugationist":
+				desc.append(`It is a clean and orderly operation, stables and cages mucked by a multitude of ${V.arcologies[0].FSSubjugationistRace} slaves, while the others are tasked with grooming your animals and harvesting your crops.`);
+				break;
+			case "Paternalist":
+				desc.append(`It's full of healthy animals, crops, and slaves, the former's every need diligently looked after by the latter. The fields flourish to capacity under such care, and the animals give the distinct impression of happiness${V.seeBestiality ? ` — some more than others if the growing bellies of your slaves are anything to go by, the only indication that such rutting takes place` : ``}.`);
+				break;
+			case "Pastoralist":
+				// desc.append(`TODO:`);
+				break;
+			case "Maturity Preferentialist":
+				// desc.append(`TODO:`);
+				break;
+			case "Youth Preferentialist":
+				// desc.append(`TODO:`);
+				break;
+			case "Body Purist":
+				// desc.append(`TODO:`);
+				break;
+			case "Slimness Enthusiast":
+				desc.append(`It features trim animals and slaves alike, not a pound of excess among them. The feed for both livestock and crops are carefully maintained to ensure optimal growth without waste, letting them flourish without being weighed down.`);
+				break;
+			case "Hedonistic":
+				desc.append(`It features wider gates and stalls, for both the humans visiting or tending the occupants, and the animals starting to mimic their handlers${V.seeBestiality ? ` and company` : ``}, with plenty of seats along the way.`);
+				break;
+			default:
+				desc.append(`It is very much a converted warehouse still, sectioned off in various 'departments'${V.farmyardUpgrades.machinery ? ` with machinery placed where it can be` : V.farmyardUpgrades.hydroponics ? ` and plumbing for the hydroponics system running every which way` : ``}.`);
+				break;
+		}
+
+		if (count > 2) {
+			desc.append(` ${V.farmyardNameCaps} is bustling with activity. Farmhands are hurrying about, on their way to feed animals and maintain farming equipment.`);
+		} else if (count) {
+			desc.append(` ${V.farmyardNameCaps} is working steadily. Farmhands are moving about, looking after the animals and crops.`);
+		} else if (S.Farmer) {
+			desc.append(` ${S.Farmer.slaveName} is alone in $farmyardName, and has nothing to do but look after the animals and crops.`);
+		} else {
+			desc.append(` ${V.farmyardNameCaps} is empty and quiet.`);
+		}
+
+		link.append(App.UI.DOM.passageLink(`Decommission ${V.farmyardName}`, "Main", () => {
+			if (V.farmMenials) {
+				V.menials += V.farmMenials;
+				V.farmMenials = 0;
+			}
+
+			V.farmyardName = "the Farmyard";
+			V.farmyard = 0;
+			V.farmyardDecoration = "standard";
+
+			V.farmMenials = 0;
+			V.farmMenialsSpace = 0;
+
+			V.farmyardShows = 0;
+			V.farmyardBreeding = 0;
+			V.farmyardCrops = 0;
+
+			V.farmyardKennels = 0;
+			V.farmyardStables = 0;
+			V.farmyardCages = 0;
+
+			V.activeCanine = 0;
+			V.activeHooved = 0;
+			V.activeFeline = 0;
+
+			V.pitAnimal = 0;
+			V.pitAnimalType = 0;
+
+			V.canines = [];
+			V.hooved = [];
+			V.felines = [];
+
+			V.farmyardUpgrades = {
+				pump: 0,
+				fertilizer: 0,
+				hydroponics: 0,
+				machinery: 0,
+				seeds: 0
+			};
+
+			clearAnimalsBought();
+			App.Arcology.cellUpgrade(V.building, App.Arcology.Cell.Manufacturing, "Farmyard", "Manufacturing");
+		}));
+
+		desc.append(link);
+		frag.append(desc);
+
+		return frag;
+	}
+
+	function expand() {
+		const
+			frag = new DocumentFragment(),
+
+			desc = document.createElement("div"),
+			link = App.UI.DOM.makeElement("div", '', "indent"),
+			note = App.UI.DOM.makeElement("span", '', "note"),
+			cost = App.UI.DOM.makeElement("span", '', "yellowgreen"),
+
+			upgradeCost = Math.trunc(V.farmyard * 1000 * V.upgradeMultiplierArcology),
+			farmhands = App.Entity.facilities.farmyard.totalEmployeesCount;
+
+		cost.append(cashFormat(upgradeCost));
+
+		desc.append(`It can support ${V.farmyard} farmhands. Currently there ${farmhands === 1 ? `is` : `are`} ${farmhands} ${farmhands === 1 ? `farmhand` : `farmhands`} in ${V.farmyardName}. `);
+		note.append(` Costs `, cost, ` and will increase upkeep costs`);
+
+		link.append(App.UI.DOM.passageLink(`Expand ${V.farmyardName}`, "Farmyard", () => {
+			cashX(forceNeg(cost));
+			V.farmyard += 5;
+			V.PC.skill.engineering += .1;
+		}));
+
+		link.append(note);
+		desc.append(link);
+		frag.append(desc);
+
+		if (V.farmyardFarmers || V.farmyardShowgirls) {
+			const
+				removeLink = App.UI.DOM.makeElement("div", '', "indent"),
+				warning = App.UI.DOM.makeElement("span", '', "red"),
+
+				count = App.Entity.facilities.farmyard.totalEmployeesCount,
+				newPop = count + V.dormitoryPopulation;
+
+			warning.append(` Dormitory capacity will be exceeded.`);
+
+			removeLink.append(App.UI.DOM.passageLink("Remove all slaves", "Farmyard", () => {
+				App.Utils.moveFacilityWorkers(App.Entity.facilities.farmyard);
+			}));
+
+			if (newPop > V.dormitory) {
+				removeLink.append(warning);
+			}
+
+			if (App.Entity.facilities.farmyard.totalEmployeesCount) {
+				frag.append(removeLink);
+			}
+		}
+
+		return frag;
+	}
+
+	function menials() {
+		const frag = new DocumentFragment();
+
+		frag.append(transferMenials());
+		frag.append(buyMenials());
+		frag.append(houseMenials());
+
+		return frag;
+	}
+
+	function transferMenials() {
+		const
+			frag = new DocumentFragment(),
+
+			links = [],
+			linksDiv = App.UI.DOM.makeElement("div", '', "indent"),
+
+			menials = V.menials,
+			farmMenials = V.farmMenials,
+			farmMenialsSpace = V.farmMenialsSpace;
+
+		if (farmMenials) {
+			frag.append(`Assigned to ${V.farmyardName} ${farmMenials === 1 ? `is` : `are`} ${farmMenials} menial ${farmMenials === 1 ? `slave` : `slaves`}, working to produce as much food for your arcology as they can. `);
+		}
+
+		if (farmMenialsSpace) {
+			frag.append(`You ${menials ? `own ${num(menials)}` : `don't own any`} free menial slaves. ${V.farmyardNameCaps} can house ${farmMenialsSpace} menial slaves total, with ${farmMenialsSpace - farmMenials} free spots. `);
+		}
+
+		if (farmMenialsSpace && farmMenials < farmMenialsSpace) {
+			if (menials) {
+				links.push(App.UI.DOM.passageLink("Transfer in a menial slave", "Farmyard", () => {
+					V.menials--;
+					V.farmMenials++;
+				}));
+			}
+
+			if (menials >= 10 && farmMenials <= farmMenialsSpace - 10) {
+				links.push(App.UI.DOM.passageLink("Transfer in 10 menial slaves", "Farmyard", () => {
+					V.menials -= 10;
+					V.farmMenials += 10;
+				}));
+			}
+
+			if (menials >= 100 && farmMenials <= farmMenialsSpace - 100) {
+				links.push(App.UI.DOM.passageLink("Transfer in 100 menial slaves", "Farmyard", () => {
+					V.menials -= 100;
+					V.farmMenials += 100;
+				}));
+			}
+
+			if (menials) {
+				links.push(App.UI.DOM.passageLink("Transfer in all free menial slaves", "Farmyard", () => {
+					if (menials > farmMenialsSpace - farmMenials) {
+						V.menials -= farmMenialsSpace - farmMenials;
+						V.farmMenials = farmMenialsSpace;
+					} else {
+						V.farmMenials += menials;
+						V.menials = 0;
+					}
+				}));
+			}
+		} else if (!farmMenialsSpace) {
+			frag.append(`${V.farmyardNameCaps} cannot currently house any menial slaves. `);
+		} else {
+			frag.append(`${V.farmyardNameCaps} has all the menial slaves it can currently house assigned to it. `);
+		}
+
+		if (farmMenials) {
+			links.push(App.UI.DOM.passageLink("Transfer out all menial slaves", "Farmyard", () => {
+				V.menials += farmMenials;
+				V.farmMenials = 0;
+			}));
+		}
+
+		linksDiv.append(App.UI.DOM.generateLinksStrip(links));
+		frag.append(linksDiv);
+
+
+		return frag;
+	}
+
+	function buyMenials() {
+		const
+			frag = new DocumentFragment(),
+
+			links = [],
+			linksDiv = App.UI.DOM.makeElement("div", '', "indent"),
+
+			menials = V.menials,
+			farmMenials = V.farmMenials,
+			farmMenialsSpace = V.farmMenialsSpace,
+			bulkMax = V.PopCap - menials - V.fuckdolls - V.menialBioreactors,
+
+			menialPrice = Math.trunc(menialSlaveCost()),
+			maxMenials = Math.trunc(Math.clamp(V.cash / menialPrice, 0, bulkMax));
+
+		if (farmMenialsSpace) {
+			MenialPopCap();
+
+			if (bulkMax > 0 || V.menials + V.fuckdolls + V.menialBioreactors === 0) {
+				links.push(App.UI.DOM.passageLink("Buy ", "Farmyard", () => {
+					V.menials++;
+					V.menialSupplyFactor--;
+					cashX(forceNeg(menialPrice), "farmyard");
+				}));
+
+				links.push(App.UI.DOM.passageLink("(x10)", "Farmyard", () => {
+					V.menials += 10;
+					V.menialSupplyFactor -= 10;
+					cashX(forceNeg(menialPrice * 10), "farmyard");
+				}));
+
+				links.push(App.UI.DOM.passageLink("(x100)", "Farmyard", () => {
+					V.menials += 100;
+					V.menialSupplyFactor -= 100;
+					cashX(forceNeg(menialPrice * 100), "farmyard");
+				}));
+
+				links.push(App.UI.DOM.passageLink("(max)", "Farmyard", () => {
+					V.menials += maxMenials;
+					V.menialSupplyFactor -= maxMenials;
+					cashX(forceNeg(maxMenials * menialPrice), "farmyard");
+				}));
+			}
+		}
+
+		if (farmMenials) {
+			linksDiv.append(App.UI.DOM.generateLinksStrip(links));
+			frag.append(linksDiv);
+		}
+
+		return frag;
+	}
+
+	function houseMenials() {
+		const
+			frag = new DocumentFragment(),
+
+			desc = document.createElement("div"),
+			link = App.UI.DOM.makeElement("div", '', "indent"),
+			note = App.UI.DOM.makeElement("span", '', "note"),
+			cost = App.UI.DOM.makeElement("span", '', "yellowgreen"),
+
+			unitCost = Math.trunc(1000 * V.upgradeMultiplierArcology);
+
+		cost.append(cashFormat(unitCost));
+
+		if (V.farmMenialsSpace < 500) {
+			desc.append(`There is enough room in ${V.farmyardName} to build housing, enough to give ${V.farmMenialsSpace + 100} menial slaves a place to sleep and relax. `);
+			note.append(` Costs `, cost, ` and will increase upkeep costs`);
+
+			link.append(App.UI.DOM.passageLink("Build a new housing unit", "Farmyard", () => {
+				cashX(forceNeg(unitCost), "farmyard");
+				V.farmMenialsSpace += 100;
+			}), note);
+
+			desc.append(link);
+			frag.append(desc);
+		}
+
+		return frag;
+	}
+
+	function rules() {
+		const frag = new DocumentFragment();
+
+		if (App.Entity.facilities.farmyard.employeesIDs().size > 0) {	// TODO: redo this with V.farmyardShowgirls
+			if (V.farmyardShows && (V.canines || V.hooved || V.felines)) {
+				const rule = makeRule(
+					['Slaves', 'are putting on shows with animals'],
+					'are',
+					"End shows",
+					[],
+					["farmyardShows", "farmyardBreeding", "farmyardRestraints"]
+				);
+
+				frag.append(rule);
+
+				if (V.seeBestiality) {
+					if (V.farmyardBreeding) {
+						const rule = makeRule(
+							['Slaves', 'are being bred with animals'],
+							'are',
+							"End breeding",
+							["farmyardShows"],
+							[ "farmyardBreeding", "farmyardRestraints"]
+						);
+
+						frag.append(rule);
+
+						if (V.farmyardRestraints) {
+							const rule = makeRule(
+								'are being restrained',
+								'All of the slaves',
+								"Restrain only disobedient slaves",
+								["farmyardShows", "farmyardBreeding"],
+								["farmyardRestraints"]
+							);
+
+							frag.append(rule);
+						} else {
+							const rule = makeRule(
+								'are being restrained',
+								'Only disobedient slaves',
+								"Restrain all slaves",
+								["farmyardShows", "farmyardBreeding", "farmyardRestraints"],
+								[]
+							);
+
+							frag.append(rule);
+						}
+					} else {
+						const rule = makeRule(
+							['Slaves', 'being bred with animals'],
+							'are not',
+							"Begin breeding",
+							["farmyardShows", "farmyardBreeding"],
+							["farmyardRestraints"]
+						);
+
+						frag.append(rule);
+					}
+				}
+			} else {
+				const rule = makeRule(
+					['Slaves', 'putting on shows with animals'],
+					'are not',
+					"Begin shows",
+					["farmyardShows"],
+					["farmyardBreeding", "farmyardRestraints"]
+				);
+
+				frag.append(rule);
+			}
+		}
+
+		/**
+		 * Creates a new rule button
+		 * @param {string|string[]} descText The base description for the rule
+		 * @param {string} boldText The part in bold
+		 * @param {string} linkText The link text
+		 * @param {string[]} enabled Variables to be set to 1
+		 * @param {string[]} disabled Variables to be set to 0
+		 */
+		function makeRule(descText, boldText, linkText, enabled, disabled) {
+			const
+				frag = new DocumentFragment(),
+
+				desc = document.createElement("div"),
+				bold = App.UI.DOM.makeElement("span", boldText, "bold"),
+				link = document.createElement("span");
+
+			if (Array.isArray(descText)) {
+				desc.append(`${descText[0]} `, bold, ` ${descText[1]}. `);
+			} else {
+				desc.append(bold, ` ${descText}. `);
+			}
+
+			link.append(App.UI.DOM.passageLink(linkText, "Farmyard", () => {
+				enabled.forEach(i => V[i] = 1);
+				disabled.forEach(i => V[i] = 0);
+			}));
+
+			desc.append(link);
+			frag.append(desc);
+
+			return frag;
+		}
+
+		return frag;
+	}
+
+	function upgrades() {
+		const frag = new DocumentFragment(),
+
+			farmyardUpgrades = V.farmyardUpgrades,
+
+			pumpCost = Math.trunc(5000 * V.upgradeMultiplierArcology),
+			fertilizerCost = Math.trunc(10000 * V.upgradeMultiplierArcology),
+			hydroponicsCost = Math.trunc(20000 * V.upgradeMultiplierArcology),
+			seedsCost = Math.trunc(25000 * V.upgradeMultiplierArcology),
+			machineryCost = Math.trunc(50000 * V.upgradeMultiplierArcology);
+
+		if (!farmyardUpgrades.pump) {
+			const
+				desc = document.createElement("div"),
+				upgrade = createUpgrade(
+					"Upgrade the water pump",
+					pumpCost,
+					'slightly decreases upkeep costs',
+					"pump"
+				);
+
+			desc.append(`${V.farmyardNameCaps} is currently using the basic water pump that it came with.`);
+
+			frag.append(desc, upgrade);
+		} else {
+			const desc = document.createElement("div");
+
+			desc.append(`The water pump in ${V.farmyardName} is a more efficient model, slightly improving the amount of crops it produces.`);
+
+			frag.append(desc);
+
+			if (!farmyardUpgrades.fertilizer) {
+				const upgrade = createUpgrade(
+					"Use a higher-quality fertilizer",
+					fertilizerCost,
+					'moderately increases crop yield and slightly increases upkeep costs',
+					"fertilizer"
+				);
+
+				frag.append(upgrade);
+			} else {
+				const desc = document.createElement("div");
+
+				desc.append(`${V.farmyardNameCaps} is using a higher-quality fertilizer, moderately increasing the amount of crops it produces and raising slightly raising upkeep costs.`);
+
+				frag.append(desc);
+
+				if (!farmyardUpgrades.hydroponics) {
+					const upgrade = createUpgrade(
+						"Purchase an advanced hydroponics system",
+						hydroponicsCost,
+						'moderately decreases upkeep costs',
+						"hydroponics"
+					);
+
+					frag.append(upgrade);
+				} else {
+					const desc = document.createElement("div");
+
+					desc.append(`${V.farmyardNameCaps} is outfitted with an advanced hydroponics system, reducing the amount of water your crops consume and thus moderately reducing upkeep costs.`);
+
+					frag.append(desc);
+
+					if (!farmyardUpgrades.seeds) {
+						const upgrade = createUpgrade(
+							"Purchase genetically modified seeds",
+							seedsCost,
+							'moderately increases crop yield and slightly increases upkeep costs',
+							"seeds"
+						);
+
+						frag.append(upgrade);
+					} else {
+						const desc = document.createElement("div");
+
+						desc.append(`${V.farmyardNameCaps} is using genetically modified seeds, significantly increasing the amount of crops it produces and moderately increasing upkeep costs.`);
+
+						frag.append(desc);
+
+						if (!farmyardUpgrades.machinery) {
+							const upgrade = createUpgrade(
+								"Upgrade the machinery",
+								machineryCost,
+								'moderately increases crop yield and slightly increases upkeep costs',
+								"machinery"
+							);
+
+							frag.append(upgrade);
+						} else {
+							const desc = document.createElement("div");
+
+							desc.append(`The machinery in ${V.farmyardName} has been upgraded, and is more efficient, significantly increasing crop yields and significantly decreasing upkeep costs.`);
+
+							frag.append(desc);
+						}
+					}
+				}
+			}
+		}
+
+		function createUpgrade(linkText, price, effect, type) {
+			const
+				desc = document.createElement("div"),
+				link = App.UI.DOM.makeElement("div", '', "indent"),
+				note = App.UI.DOM.makeElement("span", '', "note"),
+				cost = App.UI.DOM.makeElement("span", '', "yellowgreen");
+
+			cost.append(cashFormat(price));
+
+			link.append(App.UI.DOM.passageLink(linkText, "Farmyard", () => {
+				cashX(forceNeg(price), "farmyard");
+				farmyardUpgrades[type] = 1;
+			}));
+
+			note.append(` Costs `, cost, ` and ${effect}.`);
+			link.append(note);
+			desc.append(link);
+
+			return desc;
+		}
+
+		return frag;
+	}
+
+	function animals() {
+		const frag = new DocumentFragment(),
+
+			baseCost = Math.trunc(5000 * V.upgradeMultiplierArcology),
+			upgradedCost = Math.trunc(10000 * V.upgradeMultiplierArcology);
+
+		let
+			farmyardKennels = V.farmyardKennels,
+			farmyardStables = V.farmyardStables,
+			farmyardCages = V.farmyardCages;
+
+
+
+		// MARK: Kennels
+
+		const
+			CL = V.canines.length,
+
+			dogs = CL === 1 ? V.canines[0] : CL < 3 ?
+				`several different breeds of dogs` :
+				`all kinds of dogs`,
+			canines = CL === 1 ? V.canines[0].species === "dog" ?
+				V.canines[0].breed : V.canines[0].speciesPlural : CL < 3 ?
+					`several different ${V.canines.every(
+						c => c.species === "dog") ?
+						`breeds of dogs` : `species of canines`}` :
+					`all kinds of canines`,
+
+			kennels = document.createElement("div"),
+			kennelsUpgrade = App.UI.DOM.makeElement("div", '', "indent"),
+			kennelsNote = App.UI.DOM.makeElement("span", '', "note"),
+			kennelsCost = App.UI.DOM.makeElement("span", '', "yellowgreen");
+
+		if (farmyardKennels === 0) {
+			kennels.append(App.UI.DOM.passageLink("Add kennels", "Farmyard",
+				() => {
+					cashX(forceNeg(baseCost), "farmyard");
+					V.farmyardKennels = 1;
+				}));
+
+			kennelsCost.append(cashFormat(baseCost));
+			kennelsNote.append(` Costs `, kennelsCost, `, will incur upkeep costs, and unlocks domestic canines`);
+
+			kennels.append(kennelsNote);
+		} else if (farmyardKennels === 1) {
+			kennels.append(App.UI.DOM.passageLink("Kennels", "Farmyard Animals"));
+			kennels.append(` have been built in one corner of ${V.farmyardName}, and are currently ${CL < 1 ? `empty` : `occupied by ${dogs}`}.`);
+
+			if (V.rep > 10000) {
+				kennelsUpgrade.append(App.UI.DOM.passageLink("Upgrade kennels", "Farmyard",
+					() => {
+						cashX(forceNeg(upgradedCost), "farmyard");
+						V.farmyardKennels = 2;
+					}));
+
+				kennelsCost.append(cashFormat(upgradedCost));
+				kennelsNote.append(` Costs `, kennelsCost, `, will incur additional upkeep costs, and unlocks exotic canines`);
+
+				kennelsUpgrade.append(kennelsNote);
+				kennels.append(kennelsUpgrade);
+			}
+		} else if (farmyardKennels === 2) {
+			kennels.append(App.UI.DOM.passageLink("Large kennels", "Farmyard Animals"));
+			kennels.append(` have been built in one corner of ${V.farmyardName}, and are currently ${CL < 1 ? `empty` : `occupied by ${canines}`}.`);
+		}
+
+
+
+		// MARK: Stables
+
+		const
+			HL = V.hooved.length,
+
+			hooved = HL === 1 ? V.hooved[0] : HL < 3 ?
+				`several different types of hooved animals` :
+				`all kinds of hooved animals`,
+
+			stables = document.createElement("div"),
+			stablesUpgrade = App.UI.DOM.makeElement("div", '', "indent"),
+			stablesNote = App.UI.DOM.makeElement("span", '', "note"),
+			stablesCost = App.UI.DOM.makeElement("span", '', "yellowgreen");
+
+		if (farmyardStables === 0) {
+			stables.append(App.UI.DOM.passageLink("Add stables", "Farmyard",
+				() => {
+					cashX(forceNeg(baseCost), "farmyard");
+					V.farmyardStables = 1;
+				}));
+
+			stablesCost.append(cashFormat(baseCost));
+			stablesNote.append(` Costs `, stablesCost, `, will incur upkeep costs, and unlocks domestic hooved animals`);
+
+			stables.append(stablesNote);
+		} else if (farmyardStables === 1) {
+			stables.append(App.UI.DOM.passageLink("Stables", "Farmyard Animals"));
+			stables.append(` have been built in one corner of ${V.farmyardName}, and are currently ${HL < 1 ? `empty` : `occupied by ${hooved}`}.`);
+
+			if (V.rep > 10000) {
+				stablesUpgrade.append(App.UI.DOM.passageLink("Upgrade stables", "Farmyard",
+					() => {
+						cashX(forceNeg(upgradedCost), "farmyard");
+						V.farmyardStables = 2;
+					}));
+
+				stablesCost.append(cashFormat(upgradedCost));
+				stablesNote.append(` Costs `, stablesCost, `, will incur additional upkeep costs, and unlocks exotic hooved animals`);
+
+				stablesUpgrade.append(stablesNote);
+				stables.append(stablesUpgrade);
+			}
+		} else if (farmyardStables === 2) {
+			stables.append(App.UI.DOM.passageLink("Large stables", "Farmyard Animals"));
+			stables.append(` have been built in one corner of ${V.farmyardName}, and are currently ${HL < 1 ? `empty` : `occupied by ${hooved}`}.`);
+		}
+
+
+
+		// MARK: Cages
+
+		const
+			FL = V.felines.length,
+
+			cats = FL === 1 ? V.felines[0] : FL < 3 ?
+				`several different breeds of cats` :
+				`all kinds of cats`,
+			felines = FL === 1 ? V.felines[0].species === "cat" ?
+				V.felines[0].breed : V.felines[0].speciesPlural : FL < 3 ?
+					`several different ${V.felines.every(
+						c => c.species === "cat") ?
+						`breeds of cats` : `species of felines`}` :
+					`all kinds of felines`,
+
+			cages = document.createElement("div"),
+			cagesUpgrade = App.UI.DOM.makeElement("div", '', "indent"),
+			cagesNote = App.UI.DOM.makeElement("span", '', "note"),
+			cagesCost = App.UI.DOM.makeElement("span", '', "yellowgreen");
+
+		if (farmyardCages === 0) {
+			cages.append(App.UI.DOM.passageLink("Add cages", "Farmyard",
+				() => {
+					cashX(forceNeg(baseCost), "farmyard");
+					V.farmyardCages = 1;
+				}));
+
+			cagesCost.append(cashFormat(baseCost));
+			cagesNote.append(` Costs `, cagesCost, `, will incur upkeep costs, and unlocks domestic felines`);
+
+			cages.append(cagesNote);
+		} else if (farmyardCages === 1) {
+			cages.append(App.UI.DOM.passageLink("Cages", "Farmyard Animals"));
+			cages.append(` have been built in one corner of ${V.farmyardName}, and are currently ${FL < 1 ? `empty` : `occupied by ${cats}`}.`);
+
+			if (V.rep > 10000) {
+				cagesUpgrade.append(App.UI.DOM.passageLink("Upgrade cages", "Farmyard",
+					() => {
+						cashX(forceNeg(upgradedCost), "farmyard");
+						V.farmyardCages = 2;
+					}));
+
+				cagesCost.append(cashFormat(upgradedCost));
+				cagesNote.append(` Costs `, cagesCost, `, will incur additional upkeep costs, and unlocks exotic felines`);
+
+				cagesUpgrade.append(cagesNote);
+				cages.append(cagesUpgrade);
+			}
+		} else if (farmyardCages === 2) {
+			cages.append(App.UI.DOM.passageLink("Large cages", "Farmyard Animals"));
+			cages.append(` have been built in one corner of ${V.farmyardName}, and are currently ${FL < 1 ? `empty` : `occupied by ${felines}`}.`);
+		}
+
+
+
+		// MARK: Remove Housing
+
+		const
+			removeHousing = document.createElement("div"),
+			removeHousingNote = App.UI.DOM.makeElement("span", '', "note"),
+			removeHousingCost = App.UI.DOM.makeElement("span", '', "yellowgreen"),
+
+			removeHousingPrice = ((farmyardKennels + farmyardStables + farmyardCages) * 5000) * V.upgradeMultiplierArcology;
+
+		if (farmyardKennels || farmyardStables || farmyardCages) {
+			removeHousing.append(document.createElement("br"));
+
+			removeHousing.append(App.UI.DOM.passageLink("Remove the animal housing", "Farmyard",
+				() => {
+					V.farmyardKennels = 0;
+					V.farmyardStables = 0;
+					V.farmyardCages = 0;
+
+					V.farmyardShows = 0;
+					V.farmyardBreeding = 0;
+					V.farmyardRestraints = 0;
+
+					V.canines = [];
+					V.hooved = [];
+					V.felines = [];
+
+					clearAnimalsBought();
+					cashX(forceNeg(removeHousingPrice), "farmyard");
+				}));
+
+			removeHousingCost.append(cashFormat(removeHousingPrice));
+			removeHousingNote.append(` Will cost `, removeHousingCost);
+			removeHousing.append(removeHousingNote);
+		}
+
+		frag.append(kennels, stables, cages, removeHousing);
+
+		return frag;
+	}
+
+	function rename() {
+		const
+			frag = new DocumentFragment(),
+
+			renameDiv = App.UI.DOM.makeElement("div", '', "farmyard-rename"),
+			renameNote = App.UI.DOM.makeElement("span", '', "note");
+
+		renameDiv.append(`Rename ${V.farmyardName}: `);
+		renameNote.append(` Use a noun or similar short phrase`);
+
+		renameDiv.append(App.UI.DOM.makeTextBox(V.farmyardName, newName => {
+			V.farmyardName = newName;
+		}));
+
+		renameDiv.append(renameNote);
+
+		frag.append(renameDiv);
+
+		return frag;
+	}
+
+	function clearAnimalsBought() {
+		for (const i in V.animalsBought) {
+			V.animalsBought[i] = 0;
+		}
+	}
+
+	return frag;
+};
diff --git a/src/facilities/farmyard/farmyard.tw b/src/facilities/farmyard/farmyard.tw
index 60d10497b5c54964ad83dc078deb82b11b090c34..c7f495ae5e62b3e2b6eb07feb32ee61e6f293d70 100644
--- a/src/facilities/farmyard/farmyard.tw
+++ b/src/facilities/farmyard/farmyard.tw
@@ -4,459 +4,4 @@
 /* TODO: allow slaves that can't walk to put on shows */
 /* TODO: write placeholders */
 
-<<set $nextButton = "Back to Main", $nextLink = "Main", $returnTo = "Farmyard", $encyclopedia = "Farmyard">>
-
-<<if $farmyardName != "the Farmyard">>
-	<<set $farmyardNameCaps = $farmyardName.replace("the ", "The ")>>
-<</if>>
-
-<<set _CL = $canines.length, _HL = $hooved.length, _FL = $felines.length, _FyL = App.Entity.facilities.farmyard.employeesIDs().size>>
-<p class="scene-intro">
-	$farmyardNameCaps is an oasis of growth in the midst of the jungle of steel and concrete that is $arcologies[0].name. Animals are kept in pens, tended to by your slaves, while <<if $farmyardUpgrade.hydroponics == 1>>rows of hydroponics equipment<<else>>makeshift fields<</if>> grow crops.
-	<<switch $farmyardDecoration>>
-		<<case "Roman Revivalist">>
-			Its red tiles and white stone walls are the very picture of a Roman farm villa's construction, as are the marble statues and reliefs. Saturn and Ceres look over the prosperity of the fields<<if $seeBestiality == 1>>, Mercury watches over the health of the animals, and Feronia ensures strong litters in your slaves.<<else>> and Mercury watches over the health of the animals.<</if>>. The slaves here are all looked after well, as they have one of the most important jobs in $arcologies[0].name.
-		<<case "Aztec Revivalist">>
-			It can't completely recreate the floating farms in the ancient Aztec fashion, but it comes as close as it can, shallow pseudo-canals dividing each field into multiple sections. Smooth stone and colorful murals cover the walls, depicting bloody stories of gods and mortals alike.
-		<<case "Egyptian Revivalist">>
-			It does its best to capture the wide open nature of ancient Egyptian farms, including mimicking the irrigation systems fed by the Nile. The stone walls are decorated with murals detailing its construction and your prowess in general, <<if $seeBestiality == 1>>with animal-bloated slaves featured prominently.<<else>>hieroglyphs spelling out a volumes of praise.<</if>>
-		<<case "Edo Revivalist">>
-			It does its best to mimic the rice patties and thatch roofed buildings of the Edo period despite the wide variety of crops tended by various slaves. Not every crop can thrive in flooded fields, but the ones that can take advantage of your attention to detail.
-		<<case "Arabian Revivalist">>
-			//This is a placeholder//
-		<<case "Chinese Revivalist">>
-			It does its best to capture the terraces that covered the ancient Chinese hills and mountains, turning every floor into ribbons of fields following a slight incline. Slaves wade through crops that can handle flooding and splash through the irrigation of the others when they aren't tending <<if $seeBestiality == 1>>or breeding with<</if>> your animals.
-		<<case "Chattel Religionist">>
-			It runs like a well oiled machine, slaves bent in humble service as they tend crops grown on the Prophet's command, or see to the animals' needs. Their clothing is tucked up and out of the way as they see to their tasks, keeping them clean as they work <<if $seeBestiality == 1>>around animal bloated bellies<</if>> as divine will dictates.
-		<<case "Degradationist">>
-			It is constructed less as a converted warehouse and more as something to visit, allowing guests to enjoy the spectacle of slaves <<if $seeBestiality == 1>>being pounded by eager animals<<else>>elbow deep in scrubbing animal waste<</if>> to their satisfaction.
-		<<case "Repopulation Focus">>
-			It teems with life, both in the belly of every animal and the belly of every slave, though the latter makes tending the fields difficult. They're ordered to take care, as they carry the future <<if $seeBestiality == 1>>of this farm<<else>>of the arcology<</if>> in their bellies.
-		<<case "Eugenics">>
-			It holds a wide variety of crops and animals, but the best of the best is easy to find. They're set apart from the others, given only the best care and supplies<<if $seeBestiality == 1>>and bred with only the highest quality slaves<</if>>, while the sub-par stock is neglected off to the side.
-		<<case "Asset Expansionist">>
-			It is not easy to look after animals and till fields with such enormous body parts, but your slaves are diligent regardless, working hard to provide food and livestock for the arcology.
-		<<case "Transformation Fetishist">>
-			//This is a placeholder//
-		<<case "Gender Radicalist">>
-			//This is a placeholder//
-		<<case "Gender Fundamentalist">>
-			//This is a placeholder//
-		<<case "Physical Idealist">>
-			Its animals are in exceptional shape, their coats unable to hide how muscular they are, requiring your slaves to be equally toned to control them. There's plenty of space for their exercise as well<<if $seeBestiality == 1>> and an abundance of curatives for the slaves full of their fierce kicking offspring<</if>>.
-		<<case "Supremacist">>
-			It is a clean and orderly operation, stables and cages mucked by a multitude of inferior slaves, along with grooming your animals and harvesting your crops.
-		<<case "Subjugationist">>
-			It is a clean and orderly operation, stables and cages mucked by a multitude of $arcologies[0].FSSubjugationistRace slaves, while the others are tasked with grooming your animals and harvesting your crops.
-		<<case "Paternalist">>
-			It's full of healthy animals, crops, and slaves, the former's every need diligently looked after by the latter. The fields flourish to capacity under such care, and the animals give the distinct impression of happiness. <<if $seeBestiality == 1>>Some more than others if the growing bellies of your slaves are anything to go by, the only indication that such rutting takes place.<</if>>	/* TODO: this sentence seems weird */
-		<<case "Pastoralist">>
-			//This is a placeholder//
-		<<case "Maturity Preferentialist">>
-			//This is a placeholder//
-		<<case "Youth Preferentialist">>
-			//This is a placeholder//
-		<<case "Body Purist">>
-			//This is a placeholder//
-		<<case "Slimness Enthusiast">>
-			It features trim animals and slaves alike, not a pound of excess among them. The feed for both livestock and crops are carefully maintained to ensure optimal growth without waste, letting them flourish without being weighed down.
-		<<case "Hedonistic">>
-			It features wider gates and stalls, for both the humans visiting or tending the occupants, and the animals starting to mimic their handlers <<if $seeBestiality == 1>>and company<</if>>, with plenty of seats along the way.
-		<<default>>
-			It is very much a converted warehouse still, sectioned off in various 'departments'<<if $farmyardUpgrade.machinery == 1>> with machinery placed where it can be<<if $farmyardUpgrade.hydroponics > 0>> and plumbing for the hydroponics system running every which way<</if>><</if>>.
-	<</switch>>
-
-	<<if _FyL > 2>>
-		$farmyardNameCaps is bustling with activity. Farmhands are hurrying about, on their way to feed animals and maintain farming equipment.
-	<<elseif _FyL > 0>>
-		$farmyardNameCaps is working steadily. Farmhands are moving about, looking after the animals and crops.
-	<<elseif $Farmer != 0>>
-		$Farmer.slaveName is alone in $farmyardName, and has nothing to do but look after the animals and crops.
-	<<else>>
-		$farmyardNameCaps is empty and quiet.
-		<div class="choices" style="font-style:normal">
-			<<link "Decommission the Farmyard" "Main">>
-				<<if $farmMenials > 0>>
-					<<set $menials += $farmMenials>>
-				<</if>>
-				<<set $farmyardName = "the Farmyard", $farmyard = 0, $farmyardDecoration = "standard", $farmMenials = 0, $farmMenialsSpace = 0, $farmyardShows = 0, $farmyardBreeding = 0, $farmyardUpgrade = {pump: 0, fertilizer: 0, hydroponics: 0, machinery: 0, seeds: 0}, $farmyardCrops = 0, $farmyardKennels = 0, $farmyardStable = 0, $farmyardCages = 0, $activeCanine = 0, $activeHooved = 0, $activeFeline = 0, $animalsBought = {canines: 0, hooved: 0, felines: 0, labradorRetrievers: 0, germanShepherds: 0, goldenRetrievers: 0, frenchBulldogs: 0, bulldogs: 0, beagles: 0, poodles: 0, rottweilers: 0, yorkshireTerriers: 0, siberianHuskies: 0, horses: 0, bulls: 0, pigs: 0, siameses: 0, persians: 0, maineCoons: 0, ragdolls: 0, bengals: 0, abbysinians: 0, birmans: 0, orientalShorthairs: 0, sphynxes: 0, russianBlues: 0, wolves: 0, foxes: 0, jackals: 0, dingos: 0, zebras: 0, cougars: 0, jaguars: 0, pumas: 0, lynx: 0, leopards: 0, lions: 0, tigers: 0}, $pitAnimal = 0, $pitAnimalType = 0, $canines = [], $hooved = [], $felines = []>>
-				<<run App.Arcology.cellUpgrade($building, App.Arcology.Cell.Manufacturing, "Farmyard", "Manufacturing")>>
-			<</link>>
-		</div>
-	<</if>>
-</p>
-
-<div>
-	<<set _Tmult0 = Math.trunc($farmyard*1000*$upgradeMultiplierArcology)>>
-	It can support $farmyard farmhands. Currently there <<if _FyL == 1>>is<<else>>are<</if>> _FyL farmhand<<if _FyL != 1>>s<</if>> at $farmyardName.
-	<div class= "choices">
-		[[Expand the farmyard|Farmyard][cashX(forceNeg(_Tmult0), "capEx"), $farmyard += 5, $PC.skill.engineering += .1]]
-		<span class="note">
-			Costs <<print cashFormat(_Tmult0)>> and will increase upkeep costs
-		</span>
-	</div>
-	<div class= "choices">
-		<<if _FyL > 0>>
-			<<removeFacilityWorkers "farmyard" "rest" "rest">>
-		<</if>>
-	</div>
-</div>
-<span id="menials">
-<br>
-<<if $farmMenials > 0>>
-	Assigned here are $farmMenials slaves working to produce as much food as possible.
-<</if>>
-	<<if $farmMenialsSpace > 0>>
-	You <<if $menials > 0>> own <<print num($menials)>><<else>>don't own any<</if>> free menial slaves. $farmyardNameCaps can house <<print $farmMenialsSpace>> menial slaves total, with <<print $farmMenialsSpace - $farmMenials>> free slots.
-	<</if>>
-
-<br>
-
-<<if ($farmMenials < $farmMenialsSpace) && ($farmMenialsSpace > 0)>>
-	<<if $menials >= 1>>
-		<<link "Transfer in a menial slave">>
-			<<set $menials-->>
-			<<set $farmMenials++>>
-			<<goto "Farmyard">>
-		<</link>>
-	<</if>>
-	<<if $menials >= 10 && $farmMenials <= ($farmMenialsSpace - 10)>>
-		| <<link "Transfer in 10 menial slaves">>
-			<<set $menials -= 10>>
-			<<set $farmMenials += 10>>
-			<<goto "Farmyard">>
-		<</link>>
-	<</if>>
-	<<if $menials > 0>>
-		| <<link "Transfer in all free menial slaves">>
-			<<if $menials > $farmMenialsSpace - $farmMenials>>
-				<<set $menials -= $farmMenialsSpace - $farmMenials>>
-				<<set $farmMenials = $farmMenialsSpace>>
-			<<else>>
-				<<set $farmMenials += $menials>>
-				<<set $menials = 0>>
-			<</if>>
-			<<goto "Farmyard">>
-		<</link>>
-	<</if>>
-	<<if $farmMenials > 0>>
-	<<if $menials > 0>>|<</if>>
-		<<link "Transfer out all menial slaves">>
-			<<set $menials += $farmMenials>>
-			<<set $farmMenials = 0>>
-			<<goto "Farmyard">>
-		<</link>>
-	<</if>>
-<<elseif $farmMenialsSpace <= 0>>
-	$farmyardNameCaps cannot currently house any menial slaves.
-<<else>>
-	$farmyardNameCaps has all the menial slaves it can currently house assigned to it.
-	<<link "Transfer out all menial slaves">>
-		<<set $menials += $farmMenials>>
-		<<set $farmMenials = 0>>
-		<<goto "Farmyard">>
-	<</link>>
-<</if>>
-
-<<if $farmMenialsSpace > 0>>
-	<<run MenialPopCap()>>
-	<<set _menialPrice = menialSlaveCost()>>
-	<<set _bulkMax = $PopCap-$menials-$fuckdolls-$menialBioreactors>>
-	<<if $cash > _menialPrice>>
-	<<if _bulkMax > 0 || $menials+$fuckdolls+$menialBioreactors == 0>>
-		| [[Buy |Farmyard][$menials+=1,$menialSupplyFactor-=1,cashX(forceNeg(Math.trunc(_menialPrice, "farmyard")))]]
-		<<if $cash > (menialSlaveCost(10))*10>>
-			[[(x10)|Farmyard][$menials+=10,$menialSupplyFactor-=10,cashX(forceNeg(Math.trunc(menialSlaveCost(10))*10), "farmyard")]]
-		<</if>>
-		<<if $cash > (menialSlaveCost(100))*100>>
-			[[(x100)|Farmyard][$menials+=100,$menialSupplyFactor-=100,cashX(forceNeg(Math.trunc(menialSlaveCost(100))*100), "farmyard")]]
-		<</if>>
-		<<if $cash > (_menialPrice+1)*2>>
-			[[(max)|Farmyard][$menials+=Math.trunc(Math.clamp($cash/(_menialPrice),0,_bulkMax)),$menialSupplyFactor-=Math.trunc(Math.clamp($cash/(_menialPrice),0,_bulkMax)),cashX(forceNeg(Math.trunc(Math.clamp($cash/(_menialPrice),0,_bulkMax)*(_menialPrice))), "farmyard")]]
-		<</if>>
-	<</if>>
-	<</if>>
-<</if>>
-
-<<if $farmMenialsSpace < 500>>
-	<br>
-	There is enough room in $farmyardName to build housing, enough to give <<print $farmMenialsSpace+100>> menials a place to sleep and relax.
-	[[Build a unit|Farmyard][cashX(forceNeg(Math.trunc(1000*$upgradeMultiplierArcology), "farmyard")), $farmMenialsSpace += 100]]
-	<span class="note">
-		Costs @@.yellowgreen;<<print cashFormat(Math.trunc(1000*$upgradeMultiplierArcology))>>@@ and will increase upkeep costs
-	</span>
-<</if>>	/* closes menial subsection */
-</span>
-
-<span id="rules">
-<<if _FyL > 0>>
-<br><br>
-	<<if $farmyardShows == 1 && (_CL > 0 || _HL > 0 || _FL > 0)>>
-		Slaves ''are'' putting on shows with animals.
-		[[End shows|Farmyard][$farmyardShows = 0, $farmyardBreeding = 0, $farmyardRestraints = 0]]
-		<<if $seeBestiality>>
-			<br>
-			<<if $farmyardBreeding == 1>>
-				Slaves ''are'' being bred with animals.
-				[[End breeding|Farmyard][$farmyardBreeding = 0, $farmyardRestraints = 0]]
-				<br>
-				<<if $farmyardRestraints == 1>>
-					''All of the slaves'' are being restrained.
-					[[Restrain only disobedient slaves|Farmyard][$farmyardRestraints = 0]]
-				<<else>>
-					''Only disobedient slaves'' are being restrained.
-					[[Restrain all of the slaves|Farmyard][$farmyardRestraints = 1]]
-				<</if>>
-			<<else>>
-				Slaves ''are not'' being bred with animals.
-				[[Begin breeding|Farmyard][$farmyardBreeding = 1]]
-				<br>
-			<</if>>
-		<</if>>
-	<<else>>
-		Slaves ''are not'' putting on shows with animals.
-		<<if (_CL > 0 || _HL > 0 || _FL > 0)>>
-			[[Begin shows|Farmyard][$farmyardShows = 1]]
-		<<else>>
-			//You must purchase animals before you can put on shows//
-		<</if>>
-	<</if>>
-<</if>>
-<br><br>
-</span>
-
-<span id="upgrades">
-<<if $farmyardUpgrade.pump == 0>>
-	[[Upgrade the water pump|Farmyard][cashX(forceNeg(Math.trunc(5000*$upgradeMultiplierArcology)), "farmyard"), $farmyardUpgrade.pump = 1]]
-	<span class="note">
-		Costs <<print cashFormat(Math.trunc(5000*$upgradeMultiplierArcology))>> and slightly increases crop yield.
-	</span>
-	<br>
-<<else>>
-	The water pump in $farmyardName is a more efficient model, slightly improving the amount of crops $farmyardName produces.
-	<br>
-	<<if $farmyardUpgrade.fertilizer == 0>>
-		[[Use a higher-quality fertilizer|Farmyard][cashX(forceNeg(Math.trunc(10000*$upgradeMultiplierArcology)), "farmyard"), $farmyardUpgrade.fertilizer = 1]]
-		<span class="note">
-			Costs <<print cashFormat(Math.trunc(10000*$upgradeMultiplierArcology))>> moderately increases crop yield and slightly increases upkeep costs.
-		</span>
-		<br>
-	<<else>>
-		$farmyardNameCaps is using a higher-quality fertilizer, moderately increasing the amount of crops it produces and raising slightly raising upkeep costs.
-		<br>
-		<<if $farmyardUpgrade.hydroponics == 0>>
-			[[Purchase a hydroponics system|Farmyard][cashX(forceNeg(Math.trunc(10000*$upgradeMultiplierArcology)), "farmyard"), $farmyardUpgrade.hydroponics = 1]]
-			<span class="note">
-				Costs <<print cashFormat(Math.trunc(10000*$upgradeMultiplierArcology))>> and moderately decreases upkeep costs.
-			</span>
-			<br>
-		<<else>>
-			$farmyardNameCaps is outfitted with an advanced hydroponics system, reducing the amount of water your crops consume and thus moderately reducing upkeep costs.
-			<br>
-			<<if $farmyardUpgrade.seeds == 0>>
-				[[Purchase genetically modified seeds|Farmyard][cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"),$farmyardUpgrade.seeds = 1]]
-				<span class="note">
-					Costs <<print cashFormat(Math.trunc(20000*$upgradeMultiplierArcology))>> and significantly increases crop yield and moderately increases upkeep costs.
-				</span>
-				<br>
-			<<else>>
-				$farmyardNameCaps is using genetically modified seeds, significantly increasing the amount of crops it produces and moderately increasing upkeep costs.
-				<br>
-				<<if $farmyardUpgrade.machinery == 0>>
-					[[Upgrade the machinery|Farmyard][cashX(forceNeg(Math.trunc(50000*$upgradeMultiplierArcology)), "farmyard"), $farmyardUpgrade.machinery = 1]]
-					<span class="note">
-						Costs <<print cashFormat(Math.trunc(50000*$upgradeMultiplierArcology))>> significantly increases crop yield and significantly decreases upkeep costs.
-					</span>
-					<br>
-				<<else>>
-					The machinery in $farmyardName has been upgraded, and is more efficient, significantly increasing crop yields and significantly decreasing upkeep costs.
-					<br>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-</span>
-
-<span id="animalhousing">
-<<if ($farmyardKennels == 0)>>
-	<br>
-	[[Add kennels|Farmyard][cashX(forceNeg(Math.trunc(5000*$upgradeMultiplierArcology)), "farmyard"), $farmyardKennels = 1]]
-	<span class="note">
-		Costs <<print cashFormat(Math.trunc(5000*$upgradeMultiplierArcology))>> and allows you to keep dogs
-	</span>
-	<br>
-<<elseif ($farmyardKennels == 1)>>
-	<br>
-	<<link "Kennels" "FarmyardAnimals">><</link>> have been built in one corner of $farmyardName, and are currently <<if _CL <= 0>>empty.<<else>>occupied by
-		<<if _CL == 1>>
-			<<print $canines>>.
-		<<elseif _CL <= 3>>
-			a couple different breeds of dogs.
-		<<else>>
-			all kinds of dogs.
-		<</if>>
-	<</if>>
-
-	<<if $rep > 10000>>
-	<br>
-		[[Upgrade kennels|Farmyard][cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $farmyardKennels = 2]]
-		<span class="note">
-			Costs <<print cashFormat(Math.trunc(20000*$upgradeMultiplierArcology))>> and allows you to keep exotic canines
-		</span>
-	<</if>>
-<<elseif ($farmyardKennels == 2)>>
-	<br>
-	<<link "Large kennels" "FarmyardAnimals">><</link>> have been built in one corner of $farmyardName, and are currently <<if _CL <= 0>>empty.<<else>>occupied by
-		<<if _CL == 1>>
-			<<print $canines>>.
-		<<elseif _CL <= 3>>
-			a couple different
-			<<set $onlyDogs = $canines.every(c => c.species == "dog")>>
-			<<if $onlyDogs>>
-				breeds of dogs.
-			<<else>>
-				types of canines.
-			<</if>>
-		<<else>>
-			all kinds of canines.
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if ($farmyardStable == 0)>>
-	<br>
-	[[Add stables|Farmyard][cashX(forceNeg(Math.trunc(10000*$upgradeMultiplierArcology)), "farmyard"), $farmyardStable = 1]]
-	<span class="note">
-		Costs <<print cashFormat(Math.trunc(10000*$upgradeMultiplierArcology))>> and allows you to keep hooved animals
-	</span>
-	<br>
-<<elseif ($farmyardStable == 1)>>
-	<br>
-	<<link "Stables" "FarmyardAnimals">><</link>> have been built at the far end of $farmyardName, and are currently <<if _HL <= 0>>empty.<<else>>occupied by
-		<<if _HL == 1>>
-			<<print $hooved>>.
-		<<elseif _HL <= 3>>
-			a couple different types of hooved animals.
-		<<else>>
-			all kinds of hooved animals.
-		<</if>>
-	<</if>>
-	<<if $rep > 15000>>
-	<br>
-		[[Upgrade stables|Farmyard][cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $farmyardStable = 2]]
-		<span class="note">
-			Costs <<print cashFormat(Math.trunc(20000*$upgradeMultiplierArcology))>> and allows you to keep exotic hooved animals
-		</span>
-	<</if>>
-<<elseif $farmyardStable == 2>>
-	<br>
-	<<link "Large stables" "FarmyardAnimals">><</link>> have been build at the far end of $farmyardName, and are currently <<if _HL <= 0>>empty.<<else>>occupied by
-		<<if _HL == 1>>
-			<<print $hooved>>.
-		<<elseif _HL <= 3>>
-			a couple different types of hooved animals.
-		<<else>>
-			all kinds of hooved animals.
-		<</if>>
-	<</if>>
-<</if>>
-
-<<if ($farmyardCages == 0)>>
-	<br>
-	[[Add cages|Farmyard][cashX(forceNeg(Math.trunc(5000*$upgradeMultiplierArcology)), "farmyard"), $farmyardCages = 1]]
-	<span class="note">
-		Costs <<print cashFormat(Math.trunc(5000*$upgradeMultiplierArcology))>> and allows you to keep cats
-	</span>
-	<br>
-<<elseif ($farmyardCages == 1)>>
-	<br>
-	<<link "Cages" "FarmyardAnimals">><</link>> have been built in one corner of $farmyardName, and are currently <<if _FL <= 0>>empty.<<else>>occupied by
-		<<if _FL == 1>>
-			<<print $felines>>.
-		<<elseif _FL <= 3>>
-			a couple different breeds of cats.
-		<<else>>
-			all kinds of cats.
-		<</if>>
-	<</if>>
-	<<if $rep > 15000>>
-		<br>
-		[[Upgrade cages|Farmyard][cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $farmyardCages = 2]]
-		<span class="note">
-			Costs <<print cashFormat(Math.trunc(20000*$upgradeMultiplierArcology))>> and allows to keep exotic felines
-		</span>
-	<</if>>
-	<br>
-<<elseif ($farmyardCages == 2)>>
-	<br>
-	<<link "Large cages" "FarmyardAnimals">><</link>> have been built in one corner of $farmyardName, and are currently <<if _FL <= 0>>empty.<<else>>occupied by
-		<<if _FL == 1>>
-			<<print $felines>>.
-		<<elseif _FL <= 3>>
-			a couple different
-			<<set $onlyCats = $felines.every(f => f.species == "cat")>>
-			<<if $onlyCats>>
-				breeds of cats.
-			<<else>>
-				types of felines.
-			<</if>>
-		<<else>>
-			all kinds of felines.
-		<</if>>
-	<</if>>
-	<br>
-<</if>>
-<br>
-
-<<if $farmyardKennels > 0 || $farmyardStable > 0 || $farmyardCages > 0>>
-	<<set _removeCost = Math.trunc(($farmyardKennels+$farmyardStable+$farmyardCages)*5000)*$upgradeMultiplierArcology>>
-	[[Remove the animal housing|Farmyard][cashX(forceNeg(_removeCost), "farmyard"), $farmyardKennels = 0, $farmyardStable = 0, $farmyardCages = 0, $activeCanine = 0, $activeHooved = 0, $activeFeline = 0, $farmyardShows = 0, $farmyardBreeding = 0, $farmyardRestraints = 0, $animalsBought = {
-		canines: 0,
-		hooved: 0,
-		felines: 0,
-		labradorRetrievers: 0,
-		germanShepherds: 0,
-		goldenRetrievers: 0,
-		frenchBulldogs: 0,
-		bulldogs: 0,
-		beagles: 0,
-		poodles: 0,
-		rottweilers: 0,
-		yorkshireTerriers: 0,
-		siberianHuskies: 0,
-		horses: 0,
-		siameses: 0,
-		persians: 0,
-		maineCoons: 0,
-		ragdolls: 0,
-		bengals: 0,
-		abbysinians: 0,
-		birmans: 0,
-		orientalShorthairs: 0,
-		sphynxes: 0,
-		russianBlues: 0,
-		wolves: 0,
-		foxes: 0,
-		jackals: 0,
-		dingos: 0,
-		zebras: 0,
-		cougars: 0,
-		jaguars: 0,
-		pumas: 0,
-		lynx: 0,
-		leopards: 0,
-		lions: 0,
-		tigers: 0
-		}, $canines = [], $hooved = [], $felines = []]]
-	//Will cost @@.yellowgreen;<<print cashFormat(_removeCost)>>@@//
-<</if>>
-</span>
-
-<br><hr><br>
-
-<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.farmyard)>>
-
-<br><br>Rename $farmyardName: <<textbox "$farmyardName" $farmyardName "Farmyard">> //Use a noun or similar short phrase//
-
-<<run App.UI.SlaveList.ScrollPosition.restore()>>
+<<includeDOM App.Facilities.Farmyard.farmyard()>>
diff --git a/src/facilities/farmyard/farmyardAnimals.tw b/src/facilities/farmyard/farmyardAnimals.tw
index 9ab40b0e9d5b48597187d3fe392dbd5652ef551f..ede08720990317141f558c395a6802701d75547f 100644
--- a/src/facilities/farmyard/farmyardAnimals.tw
+++ b/src/facilities/farmyard/farmyardAnimals.tw
@@ -1,10 +1,10 @@
-:: FarmyardAnimals [nobr]
+:: Farmyard Animals [nobr]
 
-<<set $nextButton = "Back", $nextLink = "Farmyard", $returnTo = "FarmyardAnimals", $encyclopedia = "Farmyard">>
+<<set $nextButton = "Back", $nextLink = "Farmyard", $returnTo = "Farmyard Animals", $encyclopedia = "Farmyard">>
 
 /* TODO: write some descriptions for the different animals in their cages */
 
-<<if $farmyardKennels > 0 || $farmyardStable > 0 || $farmyardCages > 0>>
+<<if $farmyardKennels > 0 || $farmyardStables > 0 || $farmyardCages > 0>>
 <br>
 ''Domestic Animals''
 <hr>
@@ -15,12 +15,14 @@
 	<br>//Cost <<print cashFormat(20000)>> each// /* TODO: this number is mostly a placeholder, will need adjusting */
 	<br>
 	<<if $animalsBought.labradorRetrievers <= 0>>
-		<<link "Purchase dogs" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.labradorRetrievers = 1, $animalsBought.canines += 1, $canines.push("Labrador Retrievers")>><</link>>
+		<<link "Purchase dogs" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.labradorRetrievers = 1, $animalsBought.canines += 1, $canines.push({
+			species: "dog", speciesPlural: "dogs", breed: "Labrador Retrievers", type: "canine", dickSize: "big", ballType: "dog"
+		})>><</link>>
 		<br>
 	<<else>>
 		Dogs
 		<<if $activeCanine.breed != "Labrador Retriever">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Labrador Retriever", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Labrador Retriever", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
@@ -28,42 +30,42 @@
 	<</if>>
 <</if>>
 
-<<if $farmyardStable > 0>>
+<<if $farmyardStables > 0>>
 	<br>
 	__Hooved Animals__
 	<br>//Cost <<print cashFormat(20000)>> each// /* TODO: this number is mostly a placeholder, will need adjusting */
 	<br>
 	<<if $animalsBought.horses <= 0>>
-		<<link "Purchase horses" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.horses = 1, $animalsBought.hooved += 1, $hooved.push("horses")>><</link>>
+		<<link "Purchase horses" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.horses = 1, $animalsBought.hooved += 1, $hooved.push("horses")>><</link>>
 		<br>
 	<<else>>
 		Horses
 		<<if $activeHooved.species != "horse">>
-			[[Set as active hooved|FarmyardAnimals][$activeHooved = {species: "horse", speciesCap: "Horse", speciesPlural: "horses", type: "hooved", dickSize: "huge", ballType: "horse"}]]
+			[[Set as active hooved|Farmyard Animals][$activeHooved = {species: "horse", speciesCap: "Horse", speciesPlural: "horses", type: "hooved", dickSize: "huge", ballType: "horse"}]]
 		<<else>>
 			//Set as active hooved//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.bulls <= 0>>
-		<<link "Purchase bulls" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bulls = 1, $animalsBought.hooved += 1, $hooved.push("bulls")>><</link>>
+		<<link "Purchase bulls" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bulls = 1, $animalsBought.hooved += 1, $hooved.push("bulls")>><</link>>
 		<br>
 	<<else>>
 		Bulls
 		<<if $activeHooved.species != "bull">>
-			[[Set as active hooved|FarmyardAnimals][$activeHooved = {species: "bull", speciesCap: "Bull", speciesPlural: "bulls", type: "hooved", dickSize: "huge", ballType: "horse"}]]
+			[[Set as active hooved|Farmyard Animals][$activeHooved = {species: "bull", speciesCap: "Bull", speciesPlural: "bulls", type: "hooved", dickSize: "huge", ballType: "horse"}]]
 		<<else>>
 			//Set as active hooved//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.pigs <= 0>>
-		<<link "Purchase pigs" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.pigs = 1, $animalsBought.hooved += 1, $hooved.push("pigs")>><</link>>
+		<<link "Purchase pigs" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.pigs = 1, $animalsBought.hooved += 1, $hooved.push("pigs")>><</link>>
 		<br>
 	<<else>>
 		Pigs
 		<<if $activeHooved.species != "pig">>
-			[[Set as active hooved|FarmyardAnimals][$activeHooved = {species: "pig", speciesCap: "Pig", speciesPlural: "pigs", type: "hooved", dickSize: "large", ballType: "horse"}]]
+			[[Set as active hooved|Farmyard Animals][$activeHooved = {species: "pig", speciesCap: "Pig", speciesPlural: "pigs", type: "hooved", dickSize: "large", ballType: "horse"}]]
 		<<else>>
 			//Set as active hooved//
 		<</if>>
@@ -73,7 +75,7 @@
 <</if>>
 <br>
 
-<<if $farmyardKennels > 1 || $farmyardStable > 1 || $farmyardCages > 1>>
+<<if $farmyardKennels > 1 || $farmyardStables > 1 || $farmyardCages > 1>>
 <br>
 ''Exotic Animals''
 <hr>
@@ -84,13 +86,15 @@
 	<br>//Cost <<print cashFormat(20000)>> each//
 	<br>
 	<<if $animalsBought.wolves <= 0>>
-		<<link "Purchase wolves" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.wolves = 1, $animalsBought.canines += 1, $canines.push("wolves")>><</link>>
+		<<link "Purchase wolves" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.wolves = 1, $animalsBought.canines += 1, $canines.push({
+			species: "wolf", speciesPlural: "wolves", type: "canine", dickSize: "large", ballType: "wolf"
+		})>><</link>>
 		<br>
 	<<else>>
 		/*A couple of adult wolves are lounging about in their kennels.(OLD)*/
 		Wolves
 		<<if $activeCanine.species != "wolf">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "wolf", speciesCap: "Wolf", speciesPlural: "wolves", type: "canine", dickSize: "large", ballType: "wolf"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "wolf", speciesCap: "Wolf", speciesPlural: "wolves", type: "canine", dickSize: "large", ballType: "wolf"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
@@ -104,13 +108,14 @@
 	<br>//Cost <<print cashFormat(30000)>> each// /* TODO: this number is mostly a placeholder, will need adjusting */
 	<br>
 	<<if $animalsBought.lions <= 0>>
-		<<link "Purchase lions" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.lions = 1, $animalsBought.felines += 1, $felines.push("lions")>><</link>>
+		<<link "Purchase lions" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.lions = 1, $animalsBought.felines += 1, $felines.push({
+			species: "lion", speciesPlural: "lions", type: "feline", dickSize: "large", ballType: "lion"})>><</link>>
 		<br>
 	<<else>>
 		/*Most of the lions are sunning themselves. (OLD)*/
 		Lions
 		<<if $activeFeline.species != "lion">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "lion", speciesCap: "Lion", speciesPlural: "lions", type: "feline", dickSize: "large", ballType: "lion"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "lion", speciesCap: "Lion", speciesPlural: "lions", type: "feline", dickSize: "large", ballType: "lion"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
diff --git a/src/facilities/farmyard/farmyardReport.tw b/src/facilities/farmyard/farmyardReport.tw
index 7d002ca8e0004040250904520b70ab5918708443..f1f3443c0b0576a383498b8498646cc3c34cac52 100644
--- a/src/facilities/farmyard/farmyardReport.tw
+++ b/src/facilities/farmyard/farmyardReport.tw
@@ -1,3 +1,3 @@
 :: Farmyard Report [nobr]
 
-<<print App.Facilities.Farmyard.farmyardReport()>>
+<<includeDOM App.Facilities.Farmyard.farmyardReport()>>
diff --git a/src/facilities/farmyard/food/foodAmount.js b/src/facilities/farmyard/food/foodAmount.js
new file mode 100644
index 0000000000000000000000000000000000000000..af0650ff655820872849bdab1d2a43bf34bd86fc
--- /dev/null
+++ b/src/facilities/farmyard/food/foodAmount.js
@@ -0,0 +1,86 @@
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @returns {number}
+ */
+App.Facilities.Farmyard.foodAmount = function(slave) {
+	let food = 150;
+
+	if (!slave) {
+		throw `Current slave is not valid. Please report this`;
+	}
+
+	if (V.farmyardUpgrades.pump) {
+		food += 15;
+	}
+
+	if (V.farmyardUpgrades.fertilizer) {
+		food += 35;
+	}
+
+	if (V.farmyardUpgrades.seeds) {
+		food += 65;
+	}
+
+	if (V.farmyardUpgrades.machinery) {
+		food += 65;
+	}
+
+	if (S.Farmer !== 0) {
+		food *= 1.1;
+
+		if (S.Farmer.skill.farmer >= V.masteredXP) {
+			food *= 1.2;
+		}
+
+		if (setup.farmerCareers.includes(S.Farmer.career)) {
+			food *= 1.2;
+		}
+	}
+
+	if (slave.devotion > 50) {
+		food *= 1.1;
+	} else if (slave.devotion < -50) {
+		food *= 0.8;
+	}
+
+	if (slaveResting(slave)) {
+		food *= 0.9;
+	} else if (slave.health.tired + 20 >= 90 && !willWorkToDeath(slave)) {
+		slave.devotion -= 10;
+		slave.trust -= 5;
+		food *= 0.9;
+	}
+
+	if (slave.muscles > 30) {										// slave is muscular or more
+		food *= 1.1;
+	} else if (slave.muscles <= -6) {								// slave is weak or less
+		food *= 0.8;
+	}
+
+	if (slave.weight > 95) {										// slave is overweight or more
+		food *= 0.9;
+	} else if (slave.weight > 130) {								// slave is fat or more
+		food *= 0.8;
+	} else if (slave.weight > 160) {								// slave is obese or more
+		food *= 0.7;
+	} else if (slave.weight > 190) {								// slave is very obese or more
+		food *= 0.6;
+	}
+
+	if (!canSee(slave)) {											// slave is blind
+		food *= 0.6;
+	} else if (!canSeePerfectly(slave)) {							// slave is nearsighted
+		food *= 0.8;
+	}
+
+	if (slave.hears === -1) {										// slave is hard of hearing
+		food *= 0.8;
+	} else if (slave.hears < -1) {									// slave is deaf
+		food *= 0.6;
+	}
+
+	food *= restEffects(slave, 20);
+	food = Math.trunc(Math.max(food, 1));
+
+	return food;
+};
diff --git a/src/facilities/farmyard/futureAnimals.tw b/src/facilities/farmyard/futureAnimals.tw
index eda757aaee2bf7455bb3eafbf37d40d74b2229e2..f4d1ab464f04368977ba10d8a05b8df03935f976 100644
--- a/src/facilities/farmyard/futureAnimals.tw
+++ b/src/facilities/farmyard/futureAnimals.tw
@@ -1,108 +1,108 @@
 /* Putting them here because commenting them out was breaking for some reason */
 
 	<<if $animalsBought.germanShepherds == 0>>
-		<<link "Purchase German Shepherds" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.germanShepherds = 1, $animalsBought.canines += 1, $canines.push("German Shepherds")>><</link>>
+		<<link "Purchase German Shepherds" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.germanShepherds = 1, $animalsBought.canines += 1, $canines.push("German Shepherds")>><</link>>
 		<br>
 	<<elseif $animalsBought.germanShepherds == 1>>
 		German Shepherds
 		<<if $activeCanine.breed != "German Shepherd">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "German Shepherd", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "German Shepherd", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.goldenRetrievers == 0>>
-		<<link "Purchase Golden Retrievers" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.goldenRetrievers = 1, $animalsBought.canines += 1, $canines.push("Golden Retrievers")>><</link>>
+		<<link "Purchase Golden Retrievers" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.goldenRetrievers = 1, $animalsBought.canines += 1, $canines.push("Golden Retrievers")>><</link>>
 		<br>
 	<<elseif $animalsBought.goldenRetrievers == 1>>
 		Golden Retrievers
 		<<if $activeCanine.breed != "Golden Retriever">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Golden Retriever", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Golden Retriever", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.frenchBulldogs == 0>>
-		<<link "Purchase French Bulldogs" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.frenchBulldogs = 1, $animalsBought.canines += 1, $canines.push("French Bulldogs")>><</link>>
+		<<link "Purchase French Bulldogs" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.frenchBulldogs = 1, $animalsBought.canines += 1, $canines.push("French Bulldogs")>><</link>>
 		<br>
 	<<elseif $animalsBought.frenchBulldogs == 1>>
 		French Bulldogs
 		<<if $activeCanine.breed != "French Bulldog">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "French Bulldog", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "French Bulldog", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.bulldogs == 0>>
-		<<link "Purchase Bulldogs" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bulldogs = 1, $animalsBought.canines += 1, $canines.push("Bulldogs")>><</link>>
+		<<link "Purchase Bulldogs" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bulldogs = 1, $animalsBought.canines += 1, $canines.push("Bulldogs")>><</link>>
 		<br>
 	<<elseif $animalsBought.bulldogs == 1>>
 		Bulldogs
 		<<if $activeCanine.breed != "Bulldog">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Bulldog", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Bulldog", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.beagles == 0>>
-		<<link "Purchase Beagles" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.beagles = 1, $animalsBought.canines += 1, $canines.push("Beagles")>><</link>>
+		<<link "Purchase Beagles" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.beagles = 1, $animalsBought.canines += 1, $canines.push("Beagles")>><</link>>
 		<br>
 	<<elseif $animalsBought.beagles == 1>>
 		Beagles
 		<<if $activeCanine.breed != "Beagle">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Beagle", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Beagle", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.poodles == 0>>
-		<<link "Purchase Poodles" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.poodles = 1, $animalsBought.canines += 1, $canines.push("Poodles")>><</link>>
+		<<link "Purchase Poodles" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.poodles = 1, $animalsBought.canines += 1, $canines.push("Poodles")>><</link>>
 		<br>
 	<<elseif $animalsBought.poodles == 1>>
 		Poodles
 		<<if $activeCanine.breed != "Poodle">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Poodle", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Poodle", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.rottweilers == 0>>
-		<<link "Purchase Rottweilers" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.rottweilers = 1, $animalsBought.canines += 1, $canines.push("Rottweilers")>><</link>>
+		<<link "Purchase Rottweilers" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.rottweilers = 1, $animalsBought.canines += 1, $canines.push("Rottweilers")>><</link>>
 		<br>
 	<<elseif $animalsBought.rottweilers == 1>>
 		Rottweilers
 		<<if $activeCanine.breed != "Rottweiler">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Rottweiler", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Rottweiler", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.yorkshireTerriers == 0>>
-		<<link "Purchase Yorkshire Terriers" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.yorkshireTerriers = 1, $animalsBought.canines += 1, $canines.push("Yorkshire Terriers")>><</link>>
+		<<link "Purchase Yorkshire Terriers" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.yorkshireTerriers = 1, $animalsBought.canines += 1, $canines.push("Yorkshire Terriers")>><</link>>
 		<br>
 	<<elseif $animalsBought.yorkshireTerriers == 1>>
 		Yorkshire Terriers
 		<<if $activeCanine.breed != "Yorkshire Terrier">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Yorkshire Terrier", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Yorkshire Terrier", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.siberianHuskies == 0>>
-		<<link "Purchase Siberian Huskies" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.siberianHuskies = 1, $animalsBought.canines += 1, $canines.push("Siberian Huskies")>><</link>>
+		<<link "Purchase Siberian Huskies" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.siberianHuskies = 1, $animalsBought.canines += 1, $canines.push("Siberian Huskies")>><</link>>
 		<br>
 	<<elseif $animalsBought.siberianHuskies == 1>>
 		Siberian Huskies
 		<<if $activeCanine.breed != "Siberian Husky">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Siberian Husky", type: "canine", dickSize: "big", ballType: "dog"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dog", speciesCap: "Dog", speciesPlural: "dogs", breed: "Siberian Husky", type: "canine", dickSize: "big", ballType: "dog"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
@@ -114,120 +114,120 @@
 	Cats:
 	<br><br>
 	<<if $animalsBought.siameses == 0>>
-		<<link "Purchase Siamese cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.siameses = 1, $animalsBought.felines += 1, $felines.push("Siamese cats")>><</link>>
+		<<link "Purchase Siamese cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.siameses = 1, $animalsBought.felines += 1, $felines.push("Siamese cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.siameses == 1>>
 		Siamese cats
 		<<if $activeFeline.breed != "Siamese">>
-			[[Set as active canine|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Siamese", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active canine|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Siamese", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.persians == 0>>
-		<<link "Purchase Persian cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.persians = 1, $animalsBought.felines += 1, $felines.push("Persian cats")>><</link>>
+		<<link "Purchase Persian cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.persians = 1, $animalsBought.felines += 1, $felines.push("Persian cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.persians == 1>>
 		Persian cats
 		<<if $activeFeline.breed != "Persian">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Persian", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Persian", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.maineCoons == 0>>
-		<<link "Purchase Maine Coon cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.maineCoons = 1, $animalsBought.felines += 1, $felines.push("Maine Coon cats")>><</link>>
+		<<link "Purchase Maine Coon cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.maineCoons = 1, $animalsBought.felines += 1, $felines.push("Maine Coon cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.maineCoons == 1>>
 		Maine Coon cats
 		<<if $activeFeline.breed != "Maine Coon">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Maine Coon", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Maine Coon", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.ragdolls == 0>>
-		<<link "Purchase Ragdoll cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.ragdolls = 1, $animalsBought.felines += 1, $felines.push("Ragdoll cats")>><</link>>
+		<<link "Purchase Ragdoll cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.ragdolls = 1, $animalsBought.felines += 1, $felines.push("Ragdoll cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.ragdolls == 1>>
 		Ragdoll cats
 		<<if $activeFeline.breed != "Ragdoll">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Ragdoll", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Ragdoll", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.bengals == 0>>
-		<<link "Purchase Bengal cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bengals = 1, $animalsBought.felines += 1, $felines.push("Bengal cats")>><</link>>
+		<<link "Purchase Bengal cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.bengals = 1, $animalsBought.felines += 1, $felines.push("Bengal cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.bengals == 1>>
 		Bengal cats
 		<<if $activeFeline.breed != "Bengal">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Bengal", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Bengal", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.abbysinians == 0>>
-		<<link "Purchase Abbysinian cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.abbysinians = 1, $animalsBought.felines += 1, $felines.push("Abbysinian cats")>><</link>>
+		<<link "Purchase Abbysinian cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.abbysinians = 1, $animalsBought.felines += 1, $felines.push("Abbysinian cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.abbysinians == 1>>
 		Abbysinian cats
 		<<if $activeFeline.breed != "Abbysinian">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Abbysinian", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Abbysinian", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.birmans == 0>>
-		<<link "Purchase Birman cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.birmans = 1, $animalsBought.felines += 1, $felines.push("Birman cats")>><</link>>
+		<<link "Purchase Birman cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.birmans = 1, $animalsBought.felines += 1, $felines.push("Birman cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.birmans == 1>>
 		Birman cats
 		<<if $activeFeline.breed != "Birman">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Birman", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Birman", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.orientalShorthairs == 0>>
-		<<link "Purchase Oriental Shorthair cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.orientalShorthairs = 1, $animalsBought.felines += 1, $felines.push("Oriental Shorthair cats")>><</link>>
+		<<link "Purchase Oriental Shorthair cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.orientalShorthairs = 1, $animalsBought.felines += 1, $felines.push("Oriental Shorthair cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.orientalShorthairs == 1>>
 		Oriental Shorthair cats
 		<<if $activeFeline.breed != "Oriental Shorthair">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Oriental Shorthair", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Oriental Shorthair", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.sphynxes == 0>>
-		<<link "Purchase Sphynx cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.sphynxes = 1, $animalsBought.felines += 1, $felines.push("Sphynx cats")>><</link>>
+		<<link "Purchase Sphynx cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.sphynxes = 1, $animalsBought.felines += 1, $felines.push("Sphynx cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.sphynxes == 1>>
 		Sphynx cats
 		<<if $activeFeline.breed != "Sphynx">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Sphynx", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Sphynx", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.russianBlues == 0>>
-		<<link "Purchase Russian Blue cats" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.russianBlues = 1, $animalsBought.felines += 1, $felines.push("Russian Blue cats")>><</link>>
+		<<link "Purchase Russian Blue cats" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.russianBlues = 1, $animalsBought.felines += 1, $felines.push("Russian Blue cats")>><</link>>
 		<br>
 	<<elseif $animalsBought.russianBlues == 1>>
 		Russian Blue cats
 		<<if $activeFeline.breed != "Russian Blue">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Russian Blue", type: "feline", dickSize: "small", ballType: "cat"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cat", speciesCap: "Cat", speciesPlural: "cats", breed: "Russian Blue", type: "feline", dickSize: "small", ballType: "cat"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
@@ -236,39 +236,39 @@
 <</if>>
 
 	<<if $animalsBought.foxes == 0>>
-		<<link "Purchase foxes" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.foxes = 1, $animalsBought.canines += 1, $canines.push("foxes")>><</link>>
+		<<link "Purchase foxes" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.foxes = 1, $animalsBought.canines += 1, $canines.push("foxes")>><</link>>
 		<br>
 	<<elseif $animalsBought.foxes == 1>>
 		/*Red foxes play in one corner of their kennels, chasing one another.(OLD)*/
 		Foxes
 		<<if $activeCanine.species != "fox">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "fox", speciesCap: "Fox", speciesPlural: "foxes", type: "canine", dickSize: "large", ballType: "fox"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "fox", speciesCap: "Fox", speciesPlural: "foxes", type: "canine", dickSize: "large", ballType: "fox"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.jackals == 0>>
-		<<link "Purchase jackals" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.jackals = 1, $animalsBought.canines += 1, $canines.push("jackals")>><</link>>
+		<<link "Purchase jackals" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.jackals = 1, $animalsBought.canines += 1, $canines.push("jackals")>><</link>>
 		<br>
 	<<elseif $animalsBought.jackals == 1>>
 		/*A group of male jackals are fighting over a potential mate, causing quite a ruckus.(OLD)*/
 		Jackals
 		<<if $activeCanine.species != "jackal">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "jackal", speciesCap: "Jackal", speciesPlural: "jackals", type: "canine", dickSize: "large", ballType: "jackal"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "jackal", speciesCap: "Jackal", speciesPlural: "jackals", type: "canine", dickSize: "large", ballType: "jackal"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.dingos == 0>>
-		<<link "Purchase dingos" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.dingos = 1, $animalsBought.canines += 1, $canines.push("dingos")>><</link>>
+		<<link "Purchase dingos" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.dingos = 1, $animalsBought.canines += 1, $canines.push("dingos")>><</link>>
 		<br>
 	<<elseif $animalsBought.dingos == 1>>
 		/*The dingos are eating their meal, growling at each other when one gets to close to another's food.(OLD)*/
 		Dingos
 		<<if $activeCanine.species != "dingo">>
-			[[Set as active canine|FarmyardAnimals][$activeCanine = {species: "dingo", speciesCap: "Dingo", speciesPlural: "dingos", type: "canine", dickSize: "large", ballType: "dingo"}]]
+			[[Set as active canine|Farmyard Animals][$activeCanine = {species: "dingo", speciesCap: "Dingo", speciesPlural: "dingos", type: "canine", dickSize: "large", ballType: "dingo"}]]
 		<<else>>
 			//Set as active canine//
 		<</if>>
@@ -276,16 +276,16 @@
 	<</if>>
 
 
-<<if $farmyardStable > 1>>
+<<if $farmyardStables > 1>>
 	<br>
 	Hooved Animals:
 	<br><br>
 	<<if $animalsBought.zebras == 0>>
-		<<link "Purchase zebras" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.zebras = 1, $animalsBought.hooved += 1, $hooved.push("zebras")>><</link>>
+		<<link "Purchase zebras" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.zebras = 1, $animalsBought.hooved += 1, $hooved.push("zebras")>><</link>>
 	<<elseif $animalsBought.zebras == 1>>
 		Zebras
 		<<if $activeHooved.species != "zebra">>
-			[[Set as active hooved|FarmyardAnimals][$activeHooved = {species: "zebra", speciesCap: "Zebra", speciesPlural: "zebras", type: "hooved", dickSize: "huge", ballType: "zebra"}]]
+			[[Set as active hooved|Farmyard Animals][$activeHooved = {species: "zebra", speciesCap: "Zebra", speciesPlural: "zebras", type: "hooved", dickSize: "huge", ballType: "zebra"}]]
 		<<else>>
 			//Set as active hooved//
 		<</if>>
@@ -294,65 +294,65 @@
 <</if>>
 
 <<if $animalsBought.cougars == 0>>
-		<<link "Purchase cougars" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.cougars = 1, $animalsBought.felines += 1, $felines.push("cougars")>><</link>>
+		<<link "Purchase cougars" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.cougars = 1, $animalsBought.felines += 1, $felines.push("cougars")>><</link>>
 		<br>
 	<<elseif $animalsBought.cougars == 1>>
 		/*The cougars are sleeping, their lean bodies scattered around under trees. (OLD)*/
 		Cougars
 		<<if $activeFeline.species != "cougar">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "cougar", speciesCap: "Cougar", speciesPlural: "cougars", type: "feline", dickSize: "large", ballType: "cougar"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "cougar", speciesCap: "Cougar", speciesPlural: "cougars", type: "feline", dickSize: "large", ballType: "cougar"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.jaguars == 0>>
-		<<link "Purchase jaguars" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.jaguars = 1, $animalsBought.felines += 1, $felines.push("jaguars")>><</link>>
+		<<link "Purchase jaguars" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.jaguars = 1, $animalsBought.felines += 1, $felines.push("jaguars")>><</link>>
 		<br>
 	<<elseif $animalsBought.jaguars == 1>>
 		/*You can see a few jaguars laying around in the trees in their enclosure. (OLD)*/
 		Jaguars
 		<<if $activeFeline.species != "jaguar">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "jaguar", speciesCap: "Jaguar", speciesPlural: "jaguars", type: "feline", dickSize: "large", ballType: "jaguar"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "jaguar", speciesCap: "Jaguar", speciesPlural: "jaguars", type: "feline", dickSize: "large", ballType: "jaguar"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.lynx == 0>>
-		<<link "Purchase lynxs" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.lynx = 1, $animalsBought.felines += 1, $felines.push("lynx")>><</link>>
+		<<link "Purchase lynxs" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.lynx = 1, $animalsBought.felines += 1, $felines.push("lynx")>><</link>>
 		<br>
 	<<elseif $animalsBought.lynx == 1>>
 		/*The lynxes are playfully running around their enclosure. (OLD)*/
 		Lynx
 		<<if $activeFeline.species != "lynx">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "lynx", speciesCap: "Lynx", speciesPlural: "lynx", type: "feline", dickSize: "large", ballType: "lynx"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "lynx", speciesCap: "Lynx", speciesPlural: "lynx", type: "feline", dickSize: "large", ballType: "lynx"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.leopards == 0>>
-		<<link "Purchase leopards" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.leopards = 1, $animalsBought.felines += 1, $felines.push("leopards")>><</link>>
+		<<link "Purchase leopards" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.leopards = 1, $animalsBought.felines += 1, $felines.push("leopards")>><</link>>
 		<br>
 	<<elseif $animalsBought.leopards == 1>>
 		/*The leopards are lazing about in the trees in their enclosure. (OLD)*/
 		Leopards
 		<<if $activeFeline.species != "leopard">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "leopard", speciesCap: "Leopard", speciesPlural: "leopards", type: "feline", dickSize: "large", ballType: "leopard"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "leopard", speciesCap: "Leopard", speciesPlural: "leopards", type: "feline", dickSize: "large", ballType: "leopard"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
 		<br>
 	<</if>>
 	<<if $animalsBought.tigers == 0>>
-		<<link "Purchase tigers" "FarmyardAnimals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.tigers = 1, $animalsBought.felines += 1, $felines.push("tigers")>><</link>>
+		<<link "Purchase tigers" "Farmyard Animals">><<set cashX(forceNeg(Math.trunc(30000*$upgradeMultiplierArcology)), "farmyard"), $animalsBought.tigers = 1, $animalsBought.felines += 1, $felines.push("tigers")>><</link>>
 		<br>
 	<<elseif $animalsBought.tigers == 1>>
 		/*Some of the tigers are swimming, and the ones that aren't are lazing about. (OLD)*/
 		Tigers
 		<<if $activeFeline.species != "tiger">>
-			[[Set as active feline|FarmyardAnimals][$activeFeline = {species: "tiger", speciesCap: "Tiger", speciesPlural: "tigers", type: "feline", dickSize: "large", ballType: "tiger"}]]
+			[[Set as active feline|Farmyard Animals][$activeFeline = {species: "tiger", speciesCap: "Tiger", speciesPlural: "tigers", type: "feline", dickSize: "large", ballType: "tiger"}]]
 		<<else>>
 			//Set as active feline//
 		<</if>>
diff --git a/src/facilities/farmyard/reports/farmyardReport.js b/src/facilities/farmyard/reports/farmyardReport.js
index e1905757b1b701bb909a612a68664c10dd0be6ce..05b0ae7a5b814ac304e302f0c80f5736a59f9a66 100644
--- a/src/facilities/farmyard/reports/farmyardReport.js
+++ b/src/facilities/farmyard/reports/farmyardReport.js
@@ -1,77 +1,57 @@
 App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
-	"use strict";
+	let frag = document.createDocumentFragment();
 
 	const
-		Farmer = V.Farmer,
-		FL = App.Entity.facilities.farmyard.employeesIDs().size;
+		slaves = App.Utils.sortedEmployees(App.Entity.facilities.farmyard),
+		devBonus = (V.farmyardDecoration !== "standard") ? 1 : 0,
+		Farmer = S.Farmer;
 
 	let
-		t = ``,
 		profits = 0,
-		foodWeek = 0;
-	let he, him, his, hers, himself, girl, loli, He, His;   // TODO: alternative method?
+		foodWeek = 0,
+		farmerBonus = 0;
 
 
 
-	// FARMER
+	// MARK: Farmer
 
-	const farmerHealth = slave => {
+	function farmerChanges() {
+		farmerHealth(Farmer);
+		farmerDevotion(Farmer);
+		farmerTrust(Farmer);
+		farmerLivingRules(Farmer);
+		farmerCashBonus(Farmer);
+	}
+
+	function farmerHealth(slave) {
 		if (slave.health.condition < -80) {
-			improveCondition(slave, 20);
+			improveCondition(Farmer, 20);
 		} else if (slave.health.condition < -40) {
-			improveCondition(slave, 15);
+			improveCondition(Farmer, 15);
 		} else if (slave.health.condition < 0) {
-			improveCondition(slave, 10);
+			improveCondition(Farmer, 10);
 		} else if (slave.health.condition < 90) {
-			improveCondition(slave, 7);
+			improveCondition(Farmer, 7);
 		}
-	};
+	}
 
-	const farmerFetish = slave => {
-		if (slave.fetish !== 'dom') {
-			if (fetishChangeChance(slave) > jsRandom(0, 100)) {
-				slave.fetishKnown = 1;
-				slave.fetish = 'dom';
-				return 1;
-			}
-		} else if (!slave.fetishKnown) {
-			slave.fetishKnown = 1;
-			return 1;
-		} else {
-			slave.fetishStrength += 4;
-			return 2;
-		}
-	};
+	function farmerDevotion(slave) {
+		slave.devotion += devBonus;
 
-	const farmerFetishEffects = (fetish = 0) => {
-		if (fetish === 1) {
-			return `${He} isn't above sampling the merchandise ${himself}; before long it's obvious to ${his} workers that ${he} <span class="lightcoral">really likes fucking them.</span> `;
-		} else if (fetish === 2) {
-			return `${He}'s careful that all of the farmhands under ${his} supervision are all ready to work every morning, and ${he} <span class="lightsalmon">becomes more dominant.</span> `;
+		if (slave.devotion < 45) {
+			slave.devotion += 5;
 		}
-	};
-
-	const farmerSkill = slave => {
-		let t = ``;
+	}
 
-		if (slave.skill.farmer <= 10) {
-			t += `Though ${slave.slaveName} does ${his} best to manage the farmyard, with ${his} lack of skill ${he} can do little. `;
-		} else if (slave.skill.farmer <= 30) {
-			t += `${slave.slaveName}'s basic skills marginally <span class="yellowgreen">improve</span> business at ${V.farmyardName}. `;
-		} else if (slave.skill.farmer <= 60) {
-			t += `${slave.slaveName}'s skills <span class="yellowgreen">improve</span> business at ${V.farmyardName}. `;
-		} else if (slave.skill.farmer < 100) {
-			t += `${slave.slaveName}'s skills greatly <span class="yellowgreen">improve</span> business at ${V.farmyardName}. `;
-		} else {
-			t += `${slave.slaveName}'s mastery immensely <span class="yellowgreen">improves</span> business at ${V.farmyardName}. `;
+	function farmerTrust(slave) {
+		if (slave.trust < 45) {
+			slave.trust += 5;
 		}
+	}
 
-		t += slave.actualAge > 35 ?`${His} age and experience also contribute. ` : ``;
-
-		return t;
-	};
+	const farmerLivingRules = slave => slave.rules.living = 'luxurious';
 
-	const farmerCashBonus = slave => {
+	function farmerCashBonus(slave) {
 		let FarmerCashBonus = Math.min(0.2, slave.skill.farmer * 0.002);
 
 		FarmerCashBonus += slave.intelligence + slave.intelligenceImplant > 15 ? 0.05 : 0;
@@ -93,6 +73,7 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 			FarmerCashBonus += 0.05;
 		}
 
+		// TODO: keep this?
 		if (Farmer.relationshipTarget === slave.ID) {
 			FarmerCashBonus -= 0.05;
 		} else if (areRelated(Farmer, slave)) {
@@ -100,142 +81,209 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 		}
 
 		return FarmerCashBonus;
-	};
+	}
 
 
-	const farmerExperience = slave => {
-		if (setup.farmerCareers.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. `;
+
+	function farmerText() {
+		let r = [];
+
+		r.push(farmerIntro(Farmer));
+		r.push(farmerRelationshipPC(Farmer));
+		r.push(farmerFetishEffects(Farmer, farmerFetish(Farmer)));
+		r.push(farmerSkill(Farmer));
+		r.push(farmerAgeSkill(Farmer));
+		r.push(farmerExperience(Farmer));
+		r.push(farmerIntelligence(Farmer));
+		r.push(farmerSmell(Farmer));
+		r.push(farmerRelationshipSlaves(Farmer));
+		r.push(farmerContracts(Farmer));
+
+		return r.join(' ');
+	}
+
+	function farmerFetish(slave) {
+		if (slave.fetish !== 'dom') {
+			if (fetishChangeChance(slave) > jsRandom(0, 100)) {
+				slave.fetishKnown = 1;
+				slave.fetish = 'dom';
+				return 1;
+			}
+		} else if (!slave.fetishKnown) {
+			slave.fetishKnown = 1;
+			return 1;
 		} else {
-			slave.skill.farmer += jsRandom(1, Math.ceil((slave.intelligence + slave.intelligenceImplant) / 32));
+			slave.fetishStrength += 4;
+			return 2;
 		}
-	};
+	}
+
+	function farmerFetishEffects(slave, fetish = 0) {
+		const
+			{ he, his, himself, He } = getPronouns(slave);
 
+		if (fetish === 1) {
+			return `${He} isn't above sampling the merchandise ${himself}; before long it's obvious to ${his} workers that ${he} <span class="lightcoral">really likes fucking them.</span> `;
+		} else if (fetish === 2) {
+			return `${He}'s careful that all of the farmhands under ${his} supervision are all ready to work every morning, and ${he} <span class="lightsalmon">becomes more dominant.</span> `;
+		}
+	}
+
+	function farmerSkill(slave) {
+		const
+			{ he, his, His } = getPronouns(slave);
+
+		let r = [];
+
+		if (slave.skill.farmer <= 10) {
+			r.push(`Though ${slave.slaveName} does ${his} best to manage the farmyard, with ${his} lack of skill ${he} can do little.`);
+		} else if (slave.skill.farmer <= 30) {
+			r.push(`${slave.slaveName}'s basic skills marginally <span class="yellowgreen">improve</span> business at ${V.farmyardName}.`);
+		} else if (slave.skill.farmer <= 60) {
+			r.push(`${slave.slaveName}'s skills <span class="yellowgreen">improve</span> business at ${V.farmyardName}.`);
+		} else if (slave.skill.farmer < 100) {
+			r.push(`${slave.slaveName}'s skills greatly <span class="yellowgreen">improve</span> business at ${V.farmyardName}.`);
+		} else {
+			r.push(`${slave.slaveName}'s mastery immensely <span class="yellowgreen">improves</span> business at ${V.farmyardName}.`);
+		}
 
-	const farmerDick = slave => {
-		if (slave.dick > 2 && canPenetrate(slave)) {
-			return `${His} turgid dick helps ${him} manage ${his} workers. `;
+		if (slave.actualAge > 35) {
+			r.push(`${His} age and experience also contribute.`);
 		}
-	};
 
+		return r.join(' ');
+	}
+
+	function farmerExperience(slave) {
+		const { he, his, him, He } = getPronouns(slave);
+
+		if (setup.farmerCareers.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.`;
+		} else {
+			slave.skill.farmer += jsRandom(1, Math.ceil((slave.intelligence + slave.intelligenceImplant) / 32));
+		}
+	}
 
-	/** @param {App.Entity.SlaveState} Farmer */
 	function farmerRelationshipSlaves(Farmer) {
-		const p = getPronouns(Farmer);
-		for (const slave of App.Utils.sortedEmployees(App.Entity.facilities.farmyard)) {
+		const { he, his, He } = getPronouns(Farmer);
 
-			let t = ``;
+		for (const slave of slaves) {
+			let r = [];
 
 			if (Farmer.rivalryTarget === slave.ID) {
-				t += `${p.He} forces ${p.his} ${rivalryTerm(Farmer)} to service all the slaves in ${V.farmyardName}. `;    // TODO: not sure about this
+				r.push(`${He} leverages the fact that ${he} is ${slave.slaveName}'s superior to make ${his} ${rivalryTerm(Farmer)}'s life a living hell.`);
 				slave.devotion -= 2; slave.trust -= 2;
+
 				if (canDoVaginal(slave)) {
 					seX(slave, 'vaginal', 'public', 'penetrative', 10);
 				}
+
 				if (canDoAnal(slave)) {
 					seX(slave, 'anal', 'public', 'penetrative', 10);
 				}
+
 				seX(slave, 'oral', 'public', 'penetrative', 10);
 				if (jsRandom(1, 100) > 65) {
 					Farmer.rivalry++; slave.rivalry++;
 				}
 			} else if (Farmer.relationshipTarget === slave.ID) {
-				t += `${p.He} dotes over ${p.his} ${relationshipTerm(Farmer)}, ${slave.slaveName}, making sure ${he} isn't worked too hard, but unfortunately manages to get in the way of ${his} work. `;
+				r.push(`${He} dotes over ${his} ${relationshipTerm(Farmer)}, ${slave.slaveName}, making sure ${he} isn't worked too hard, but unfortunately manages to get in the way of ${his} work.`);
 				slave.devotion++;
 			} else if (areRelated(Farmer, slave)) {
-				t += `${p.He} pays special attention to ${p.his} ${relativeTerm(Farmer, slave)}, ${slave.slaveName}, making sure ${he} is treated well and showing off ${his} skills. `;
+				r.push(`${He} pays special attention to ${his} ${relativeTerm(Farmer, slave)}, ${slave.slaveName}, making sure ${he} is treated well and showing off ${his} skills.`);
 				slave.trust++;
 			}
 
-			return t;
+			return r;
 		}
 	}
 
-	const farmerWorks = slave => {
+	function farmerContracts(slave) {
+		const { he, his, himself } = getPronouns(slave);
+
 		let
-			t = ``,
-			oldCash = V.cash,
-			seed,
-			beauty;
+			r = [],
+			seed = V.farmyardShowgirls ? App.Facilities.Farmyard.farmShowsIncome(slave) : jsRandom(1, 10) * (jsRandom(150, 170) + (farmerBonus * 10));
 
-		if (V.showEWD) {
-			t += `<br>&nbsp;&nbsp;&nbsp;&nbsp;${He} ${App.SlaveAssignment.workTheFarm(slave)}`;
-		} else {
-			App.SlaveAssignment.workTheFarm(slave);
+		if (V.farmyardShows && !V.farmyardShowgirls) {
+			r.push(`<p class="indent">Since ${he} doesn't have enough showgirls to entertain your arcology's citizens, ${he} puts on shows with your animals on ${his} own, earning <span class="yellowgreen">${cashFormat(seed)}.</span></p>`);
+		} else if (!V.farmyardFarmers) {
+			r.push(`<p class="indent">Since ${V.farmyardName} doesn't have anyone tending to the crops, ${he} looks after them ${himself}, earning <span class="yellowgreen">${cashFormat(seed)}.</span></p>`);
 		}
 
-		seed = V.cash - oldCash;
-		cashX(Math.trunc(0.5 * seed), 'farmyard');
-		seed = Math.trunc(1.5 * seed);
-		profits += seed;
-		beauty = Beauty(V.activeSlave);
+		return r;
+	}
 
-		t += `<br>&nbsp;&nbsp;&nbsp;&nbsp;Since ${he} doesn't have enough farmhands to manage to keep ${him} busy, ${he} sees ${beauty} customers ${himself} (${Math.trunc(beauty / 7)} a day), earning you <span class="yellowgreen">${cashFormat(seed)}</span> ${He} can charge more for ${his} time, since many citizens find it erotic to fuck the Farmer. `;
+	function farmerRelationshipPC(slave) {
+		const { he, his, wife } = getPronouns(slave);
 
-		return t;
-	};
+		if (slave.relationship === -3 && slave.devotion > 50) {
+			return `As your loving ${wife}, ${he} does ${his} best to ensure ${V.farmyardName} runs smoothly.`;
+		}
+	}
 
-	const farmerEndWeek = slave => {
-		let t = ``;
+	function farmerAgeSkill(slave) {
+		const { His } = getPronouns(slave);
 
-		if (V.showEWD) {
-			t += `<br><br>`;
-			/* 000-250-006 */   // TODO: legacy - needed?
-			if (V.seeImages && V.seeReportImages) {
-				t += `<div class="imageRef tinyImg">
-					  ${SlaveArt(slave, 0, 0)}
-				  </div>`;
-			}
-			/* 000-250-006 */
-			t += `<span class="slave-name">${SlaveFullName(slave)}</span> is serving as the Farmer. `;
-			t += `<br>&nbsp;&nbsp;&nbsp;&nbsp;`;
-			t += App.SlaveAssignment.choosesOwnClothes(slave);
-			t += `<<include "SA rules">>`;
-			t += `<<include "SA diet">>`;
-			t += `<<include "SA long term effects">>`;
-			t += App.SlaveAssignment.drugs(slave);
-			t += `<<include "SA relationships">>`;
-			t += `<<include "SA rivalries">>`;
-			t += `<br><<include "SA devotion">>`;
-		} else {
-			App.SlaveAssignment.choosesOwnClothes(slave);
-			t += `<<silently>><<include "SA rules">><</silently>>`;
-			t += `<<silently>><<include "SA diet">><</silently>>`;
-			t += `<<silently>><<include "SA long term effects">><</silently>>`;
-			App.SlaveAssignment.drugs(slave);
-			t += `<<silently>><<include "SA relationships">><</silently>>`;
-			t += `<<silently>><<include "SA rivalries">><</silently>>`;
-			t += `<<silently>><br><<include "SA devotion">><</silently>>`;
+		if (slave.actualAge > 35) {
+			return `${His} age and experience also contribute.`;
 		}
+	}
 
-		return t;
-	};
+	function farmerIntelligence(slave) {
+		const { He } = getPronouns(slave);
 
-	const farmerRelationshipPC = slave => slave.relationship === -3 && slave.devotion > 50 ? `As your loving ${wife}, ${he} does ${his} best to ensure ${V.farmyardName} runs smoothly. ` : ``;
+		if (slave.intelligence + slave.intelligenceImplant > 15) {
+			return `${He} is a clever manager.`;
+		}
+	}
 
-	const farmerAgeSkill = slave => slave.actualAge > 35 ? `${His} age and experience also contribute. ` : ``;
+	function farmerSmell(slave) {
+		const { him, His } = getPronouns(slave);
 
-	const farmerIntelligence = slave => slave.intelligence + slave.intelligenceImplant > 15 ? `${He} is a clever manager. ` : ``;
+		if (!canSmell(slave)) {
+			return `${His} lack of a sense of smell protects ${him} from that omnipresent barnyard stench.`;
+		}
+	}
 
-	const farmerSmell = slave => !canSmell(slave) ? `${His} lack of a sense of smell protects ${him} from that omnipresent barnyard stench. ` : ``;
+	const farmerIntro = slave => `<span class="indent">${SlaveFullName(slave)} is serving as the Farmer</span>.`;
 
-	const farmerIntro = slave => `&nbsp;&nbsp;&nbsp;&nbsp;${SlaveFullName(slave)} is serving as the Farmer. `;
+	if (Farmer) {
+		const
+			farmerEffects = App.UI.DOM.appendNewElement("p", frag, '', "indent");
 
-	const farmerDevotion = slave => slave.devotion < 45 ? slave.devotion += 5 : null;
+		V.i = V.slaveIndices[Farmer.ID];
+		App.Utils.setLocalPronouns(Farmer);	// needed for "include"s
 
-	const farmerTrust = slave => slave.trust < 45 ? slave.trust += 5 : null;
+		if (V.showEWD) {
+			const
+				farmerEntry = App.UI.DOM.appendNewElement("div", frag, '', "slave-report");
 
-	const farmerLivingRules = slave => slave.rules.living !== 'luxurious' ? slave.rules.living = 'luxurious' : null;
+			if (V.seeImages && V.seeReportImages) {
+				App.UI.DOM.appendNewElement("div", farmerEntry, App.Art.SlaveArtElement(Farmer, 0, 0), ["imageRef", "tinyImg"]);
+			}
 
+			$(farmerEntry).append(`<span class="slave-name">${SlaveFullName(Farmer)}</span> is serving as the Farmer.`);
+			$(farmerEntry).append(App.SlaveAssignment.standardSlaveReport(Farmer, false));
+		} else {
+			App.SlaveAssignment.standardSlaveReport(Farmer, true);
+		}
+
+		farmerChanges();
+		$(farmerEffects).append(farmerText());
+	}
 
 
-	// FARMHANDS
 
-	const farmhandCount = count => count > 0 ? `&nbsp;&nbsp;&nbsp;&nbsp;There ${count !== 1 ? `are ${count} farmhands` : `is one farmhand`} working out of ${V.farmyardName}.` : ``;
+	// MARK: Farmhands
 
-	const farmhandLivingRules = slave => {
+	function farmhandCount(count) {
+		return `<strong>There ${count > 1 ? `are ${count} farmhands` : `is one farmhand`} working out of ${V.farmyardName}.</strong>`;
+	}
+
+	function farmhandLivingRules(slave) {
 		switch (V.farmyardDecoration) {
 			case 'Degradation':
 			case 'standard':
@@ -246,11 +294,11 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 				break;
 			default:
 				slave.rules.living = 'normal';
+				break;
 		}
-		// TODO: should FS with 'spare' living rules cause minor health damage and devotion / trust loss?
-	};
+	}
 
-	const farmhandHealth = slave => {
+	function farmhandHealth(slave) {
 		if (slave.health.condition < -80) {
 			improveCondition(slave, 20);
 		} else if (slave.health.condition < -40) {
@@ -260,10 +308,9 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 		} else if (slave.health.condition < 90) {
 			improveCondition(slave, 7);
 		}
-	};
+	}
 
-	// TODO: check over all of these numbers, make sure they make sense
-	const farmhandDevotion = slave => {
+	function farmhandDevotion(slave) {
 		if (slave.devotion <= 20 && slave.trust >= -20) {
 			slave.devotion -= 5; slave.trust -= 5;
 		} else if (slave.devotion < 45) {
@@ -271,84 +318,109 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 		} else if (slave.devotion > 50) {
 			slave.devotion -= 4;
 		}
-	};
-
-	const farmhandEndWeek = slave => {
-		let t = ``;
-
-		if (V.showEWD) {
-			t += `<br><br>`;
-			/* 000-250-006 */   // TODO: legacy - needed?
-			if (V.seeImages && V.seeReportImages) {
-				t += `<div class="imageRef tinyImg">
-					  ${SlaveArt(slave, 0, 0)}
-				  </div>`;
-			}
-			/* 000-250-006 */
-			t += `<span class="slave-name">${SlaveFullName(slave)}</span> ${slave.choosesOwnAssignment === 2 ? `<<include "SA chooses own job">>` : `is working out of ${V.farmyardName}`}. `;
-			t += `<br>&nbsp;&nbsp;&nbsp;&nbsp;${He} ${App.SlaveAssignment.workTheFarm(slave)}`;
-			t += `<br>&nbsp;&nbsp;&nbsp;&nbsp;`;
-			t += App.SlaveAssignment.choosesOwnClothes(slave);
-			t += `<<include "SA rules">>`;
-			t += `<<include "SA diet">>`;
-			t += `<<include "SA long term effects">>`;
-			t += App.SlaveAssignment.drugs(slave);
-			t += `<<include "SA relationships">>`;
-			t += `<<include "SA rivalries">>`;
-			t += `<br><<include "SA devotion">>`;
-		} else {
-			t += `<<silently>><<include "SA chooses own job">><</silently>>`;
-			App.SlaveAssignment.choosesOwnClothes(slave);
-			t += `<<silently>><<include "SA rules">><</silently>>`;
-			t += `<<silently>><<include "SA diet">><</silently>>`;
-			t += `<<silently>><<include "SA long term effects">><</silently>>`;
-			App.SlaveAssignment.drugs(slave);
-			t += `<<silently>><<include "SA relationships">><</silently>>`;
-			t += `<<silently>><<include "SA rivalries">><</silently>>`;
-			t += `<<silently>><br><<include "SA devotion">><</silently>>`;
-		}
-
-		return t;
-	};
+	}
 
-	const farmhandProfit = slave => {
+	function farmhandProfit(slave) {
 		let incomeStats = getSlaveStatisticData(slave, slave.assignment === Job.FARMYARD ? V.facility.farmyard : undefined);
 		return incomeStats.income;
-	};
+	}
 
-	const farmhandFood = slave => {
+	function farmhandFood(slave) {
 		let
 			incomeStats = getSlaveStatisticData(slave, slave.assignment === Job.FARMYARD ? V.facility.farmyard : undefined),
-			foodWeek = 0;
+			foodWeek = incomeStats.food || 0;
 
-		foodWeek += incomeStats.food;
 		if (V.farmMenials > 0) {
 			foodWeek += (V.farmMenials * 350);
 		}
 
 		return foodWeek;
-	};
+	}
 
-	const farmhandTrust = slave => slave.trust < 30 ? slave.trust += 5 : null;
+	function farmhandTrust(slave) {
+		if (slave.trust < 30) {
+			slave.trust += 5;
+		}
+	}
 
-	const farmhandEnergy = slave => slave.energy > 40 && slave.energy < 95 ? slave.energy++ : null;
+	function farmhandEnergy(slave) {
+		if (slave.energy > 40 && slave.energy < 95) {
+			slave.energy++;
+		}
+	}
 
+	if (slaves) {
+		const intro = App.UI.DOM.appendNewElement("p", frag, '', "indent");
 
-	// MENIALS
+		let r = [];
+
+		r.push(farmhandCount(slaves.length));
+
+		for (const slave of slaves) {
+			V.i = V.slaveIndices[slave.ID];
+
+			slave.devotion += devBonus;
+
+			App.Utils.setLocalPronouns(slave);	// needed for "include"s
+
+			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(`<span class="slave-name">${SlaveFullName(slave)}</span> `);
+
+				if (slave.choosesOwnAssignment === 2) {
+					$(slaveEntry).append(App.UI.DOM.renderPassage("SA chooses own job"));
+				} else {
+					$(slaveEntry).append(`is working out of ${V.farmyardName}.`);
+				}
 
-	if (V.farmMenials > 0) {
+				farmhandLivingRules(slave);
+				farmhandHealth(slave);
+				farmhandDevotion(slave);
+				farmhandTrust(slave);
+				farmhandEnergy(slave);
+				farmhandFood(slave);
+
+				profits += farmhandProfit(slave);
+
+				const farmhandContent = App.UI.DOM.appendNewElement("div", slaveEntry, '', "indent");
+
+				$(farmhandContent).append(App.SlaveAssignment.workTheFarm(slave));
+				$(slaveEntry).append(App.SlaveAssignment.standardSlaveReport(slave, false));
+			} else {	// silently discard return values
+				App.SlaveAssignment.workTheFarm(slave);
+				App.SlaveAssignment.standardSlaveReport(slave, true);
+			}
+		}
+
+		$(intro).append(r);
+	}
+
+
+
+	// MARK: Menials
+
+	if (V.farmMenials) {
 		let farmMenialProductivity = 9;
 
-		if (V.farmyardUpgrade.pump) {
+		if (V.farmyardUpgrades.pump) {
 			farmMenialProductivity += 1;
 		}
-		if (V.farmyardUpgrade.fertilizer) {
+
+		if (V.farmyardUpgrades.fertilizer) {
 			farmMenialProductivity += 2;
 		}
-		if (V.farmyardUpgrade.seeds) {
+
+		if (V.farmyardUpgrades.seeds) {
 			farmMenialProductivity += 3;
 		}
-		if (V.farmyardUpgrade.machinery) {
+
+		if (V.farmyardUpgrades.machinery) {
 			farmMenialProductivity += 3;
 		}
 
@@ -356,19 +428,20 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 	}
 
 
-	// FARMYARD
 
-	const farmyardStatsRecords = () => {
-		// TODO: check over these setters and remove those we don't need
+	// MARK: Farmyard
+
+	function farmyardStatsRecords() {
 		const f = V.facility.farmyard;
+
 		if (typeof f === "undefined") {
 			return;
 		}
+
 		f.farmhandIncome = 0;
 		f.customers = 0;
 		f.farmhandCosts = 0;
 		f.rep = 0;
-		f.adsCosts = V.farmyardAdsSpending;
 		f.maintenance = V.farmyard * V.facilityCost;
 		f.totalIncome = f.farmhandIncome + f.adsIncome;
 		f.totalExpenses = f.farmhandCosts + f.maintenance;
@@ -380,117 +453,72 @@ App.Facilities.Farmyard.farmyardReport = function farmyardReport() {
 			f.farmhandCosts += i.cost;
 			f.rep += i.rep;
 		}
-	};
+	}
 
-	const farmyardDecoration = () => {
-		let t = ``;
+	function farmyardDecoration() {
+		let r = [];
 
 		// TODO: add checks for the different FSs
 		if (V.farmyardDecoration !== 'standard') {
-			if (!profits && !foodWeek) {
-				t += `<br>`;
-			}
-			t += `<br>${V.farmyardNameCaps}'s customer's enjoyed `;
-			if (V.seeBestiality && V.farmyardBreeding > 0 && (V.canines.length || V.hooved.length || V.felines.length)) {
-				t += `<span class="green">watching farmhands fuck animals in ${V.farmyardDecoration} surroundings.</span>`;
-			} else if (V.farmyardShows) {
-				t += `<span class="green">watching farmhands put on shows in ${V.farmyardDecoration} surroundings.</span>`;
-			} else {
-				// TODO: not sure about this one
-				t += `<span class="green">partaking of ${V.farmyardName}'s fine produce in its ${V.farmyardDecoration} décor.</span>`;
-			}
-		}
+			const decorationEffects = App.UI.DOM.appendNewElement("p", frag, '', "indent");
 
-		return t;
-	};
+			$(decorationEffects).append(document.createElement("br"));
 
-	const farmyardProfit = (profit, foodWeek) => {
-		let t = ``;
+			r.push(`${V.farmyardNameCaps}'s customer's enjoyed`);
 
-		if (profit || foodWeek) {
-			t += `<br><br>${V.farmyardNameCaps} `;
-			if (profit) {
-				t += `makes you <span class="yellowgreen">${cashFormat(Math.trunc(profit))}</span>`;
-			}
-			if (profit && foodWeek && V.foodMarket) {
-				t += ` and `;
-			}
-			if (foodWeek && V.foodMarket) {
-				t += `produced <span class="chocolate"> ${massFormat(foodWeek)}</span> of food`;
+			if (V.seeBestiality && V.policies.bestialityOpeness && (V.canines || V.hooved || V.felines)) {
+				r.push(`<span class="green">watching farmhands fuck animals in ${V.farmyardDecoration} surroundings.</span>`);
+			} else if (V.farmyardShows) {
+				r.push(`<span class="green">watching farmhands put on shows in ${V.farmyardDecoration} surroundings.</span>`);
+			} else {
+				r.push(`<span class="green">partaking of ${V.farmyardName}'s fine produce in its ${V.farmyardDecoration} décor.</span>`);
 			}
-			t += ` this week. `;
-		}
 
-		return t;
-	};
-
-	const farmyardStatsDisplay = () => `<<includeDOM App.Facilities.Farmyard.Stats(false)>><<timed 50ms>><<replace #farmyardstats>><<includeDOM App.Facilities.Farmyard.Stats(true)>><</replace>><</timed>>`;
-
-	t += `<span id="farmyardstats"></span>`;
-
-	// MAIN LOOP
+			$(decorationEffects).append(r.join(' '));
+		}
 
-	if (Farmer) {
-		t += farmerHealth(Farmer);
-		t += farmerDevotion(Farmer);
-		t += farmerTrust(Farmer);
-		t += farmerLivingRules(Farmer);
-
-		getSlaveStatisticData(Farmer, V.facility.farmyard); // make sure we have registered living expenses
-
-		t += farmerIntro(Farmer);
-		t += farmerRelationshipPC(Farmer);
-		t += farmerFetishEffects(farmerFetish(Farmer));
-		t += farmerSkill(Farmer);
-		t += farmerCashBonus(Farmer);
-		t += farmerAgeSkill(Farmer);
-		t += farmerExperience(Farmer);
-		t += farmerIntelligence(Farmer);
-		t += farmerDick(Farmer);
-		t += farmerSmell(Farmer);
-		t += farmerRelationshipSlaves(Farmer);
-		t += farmerWorks(Farmer);
+		return r;
 	}
 
-	t += farmhandCount(FL);
+	function farmyardProfit(profit, food) {
+		const profitContent = App.UI.DOM.appendNewElement("p", frag, '', "indent");
 
-	if (Farmer) {
-		t += farmerEndWeek(Farmer);
-	}
-
-	if (FL > 0) {
-		let oldCash = V.cash,
-			oldFood = V.food;
+		let r = [];
 
-		for (const slave of App.Utils.sortedEmployees(App.Entity.facilities.farmyard)) {
-			V.i = V.slaveIndices[slave.ID];
+		$(profitContent).append(document.createElement("br"));
 
-			({
-				he, him, his, hers, himself, girl, He, His, loli
-			} = getPronouns(slave));
+		if (profit || food) {
+			r.push(V.farmyardNameCaps);
 
-			farmhandLivingRules(slave);
-			farmhandHealth(slave);
-			farmhandDevotion(slave);
-			farmhandTrust(slave);
-			farmhandEnergy(slave);
-			farmhandFood(slave);
-			profits += farmhandProfit(slave);
+			if (profit) {
+				r.push(`makes you <span class="yellowgreen">${cashFormat(Math.trunc(profit))}</span>`);
+			}
 
-			t += farmhandEndWeek(slave);
+			if (V.foodMarket) {
+				if (profit && food) {
+					r.push(`and`);
+				}
+				if (food) {
+					r.push(`produced <span class="chocolate"> ${massFormat(food)}</span> of food`);
+				}
+			}
+			r.push(`this week.`);
 		}
 
-		profits += V.cash - oldCash;    // FIXME: equal to 0 - what is this supposed to represent?
-		oldCash = V.cash;
-		foodWeek += V.food + oldFood;
+		return $(profitContent).append(r.join(' '));
 	}
 
+	// FIXME: no idea what I'm doing here
+	const statsSpan = document.createElement("span");
+
 	farmyardStatsRecords();
-	t += farmyardProfit(profits, foodWeek);
-	t += farmyardDecoration();
-	t += farmyardStatsDisplay();
+	farmyardProfit(profits, foodWeek);
+	farmyardDecoration();
 
 	V.food += foodWeek;
 
-	return t;
+	frag.append(App.Facilities.Farmyard.Stats(false));
+	statsSpan.append(App.Facilities.Farmyard.Stats(true));
+
+	return frag;
 };
diff --git a/src/facilities/farmyard/shows/farmShowsIncome.js b/src/facilities/farmyard/shows/farmShowsIncome.js
new file mode 100644
index 0000000000000000000000000000000000000000..f80c965449435cfc3a0d8bfc0563f561d8d50390
--- /dev/null
+++ b/src/facilities/farmyard/shows/farmShowsIncome.js
@@ -0,0 +1,602 @@
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @returns {number}
+ */
+App.Facilities.Farmyard.farmShowsIncome = function(slave) {
+	const
+		arcology = V.arcologies[0],
+		heavyMods = SlaveStatsChecker.modScore(slave).total > 20,
+
+		sexualQuirks = ["perverted", "unflinching"],
+		behavioralQuirks = ["sinful"],
+		fetishes = ["humiliation", "masochist"];
+
+	let cash = 7500;
+
+	if (!slave) {
+		return null;
+	} else {
+		if (S.Farmer) {
+			cash *= 1.1;
+			if (S.Farmer.skill.farmer >= V.masteredXP) {
+				cash *= 1.1;
+			}
+		}
+
+		// MARK: FS Effects
+
+		if (arcology.FSSupremacist !== "unset") {
+			if (isSuperiorRace(slave)) {
+				if (V.seeBestiality) {
+					cash *= 0.8;
+					repX(-10, "shows");
+				} else {
+					cash *= 1.2;
+				}
+			} else {
+				cash *= 0.9;
+			}
+		}
+
+		if (arcology.FSSubjugationist !== "unset") {
+			if (isInferiorRace(slave)) {
+				if (V.seeBestiality) {
+					cash *= 1.2;
+					repX(10, "shows");
+				} else {
+					cash *= 0.8;
+				}
+			} else {
+				cash *= 0.9;
+			}
+		}
+
+		if (arcology.FSRepopulationFocus !== "unset") {
+			if (isPreg(slave)) {
+				if (slave.eggType === "human") {
+					cash *= 1.2;
+					repX(15, "shows");
+				} else {
+					cash *= 0.8;
+					repX(-15, "shows");
+				}
+			}
+		}
+
+		if (arcology.FSRestart !== "unset") {
+			if (isPreg(slave)) {
+				if (isEliteBreeder(slave)) {
+					if (slave.eggType === "human") {
+						cash *= 0.7;
+						repX(-20, "shows");
+					} else {
+						cash *= 0.6;
+						repX(-25, "shows");
+					}
+				} else {
+					if (slave.eggType === "human") {
+						cash *= 0.8;
+						repX(-15, "shows");
+					} else {
+						cash += 0.9;
+						repX(-10, "shows");
+					}
+				}
+			} else {
+				if (V.seeBestiality && isEliteBreeder(slave)) {
+					cash *= 0.7;
+					repX(-10, "shows");
+				}
+			}
+		}
+
+		if (arcology.FSGenderRadicalist !== "unset") {
+			if (slave.genes === "XX") {
+				if (slave.dick > 0) {
+					cash *= 1.2;
+					repX(10, "shows");
+				} else {
+					cash *= 0.9;
+					repX(-5, "shows");
+				}
+			}
+		}
+
+		if (arcology.FSGenderFundamentalist !== "unset") {
+			if (isPreg(slave) || setup.fakeBellies.includes(slave.bellyAccessory)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			} else {
+				cash *= 0.9;
+				repX(-5, "shows");
+			}
+
+			// FIXME: keep this? not sure it makes sense
+			if (slave.genes === "XX") {
+				if (slave.dick > 0) {
+					cash *= 0.8;
+				} else {
+					cash *= 1.1;
+				}
+			}
+		}
+
+		if (arcology.FSPaternalist !== "unset") {
+			if (V.seeBestiality) {
+				if (V.farmyardBreeding) {
+					if (V.farmyardRestraints) {
+						cash *= 0.5;
+					} else {
+						cash *= 0.7;
+					}
+				} else {
+					if (V.farmyardRestraints) {
+						cash *= 0.8;
+					} else {
+						cash *= 0.9;
+					}
+				}
+			} else {
+				// TODO: something here?
+			}
+		}
+
+		if (arcology.FSDegradationist !== "unset") {
+			if (V.seeBestiality) {
+				if (V.farmyardBreeding) {
+					if (V.farmyardRestraints) {
+						cash *= 1.5;
+					} else {
+						cash *= 1.3;
+					}
+				} else {
+					if (V.farmyardRestraints) {
+						cash *= 1.2;
+					} else {
+						cash *= 1.1;
+					}
+				}
+			} else {
+				// TODO: something here?
+			}
+		}
+
+		if (arcology.FSBodyPurist !== "unset") {
+			if (SlaveStatsChecker.isModded(slave)) {
+				if (heavyMods) {
+					cash *= 0.7;
+					repX(-15, "shows");
+				} else {
+					cash *= 0.8;
+					repX(-10, "shows");
+				}
+			}
+		}
+
+		if (arcology.FSTransformationFetishist !== "unset") {
+			if (SlaveStatsChecker.isModded(slave)) {
+				if (heavyMods) {
+					cash *= 1.3;
+					repX(15, "shows");
+				} else {
+					cash *= 1.2;
+					repX(10, "shows");
+				}
+			}
+		}
+
+		if (arcology.FSYouthPreferentialist !== "unset") {
+			if (isYoung(slave)) {
+				cash *= 1.2;
+				repX(10, "shows");
+			} else {
+				cash *= 0.8;
+				repX(-5, "shows");
+			}
+		}
+
+		if (arcology.FSMaturityPreferentialist !== "unset") {
+			if (!isYoung(slave)) {
+				cash *= 1.2;
+				repX(10, "shows");
+			} else {
+				cash *= 0.8;
+				repX(-5, "shows");
+			}
+		}
+
+		if (arcology.FSSlimnessEnthusiast !== "unset") {
+			if (slimLawPass(slave)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			} else {
+				cash *= 0.9;
+				repX(-5, "shows");
+			}
+		}
+
+		// FIXME: marked for possible rewrite
+		if (arcology.FSAssetExpansionist !== "unset") {
+			if (isStacked(slave)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			}
+		}
+
+		if (arcology.FSPastoralist !== "unset") {
+			if (slave.boobs >= 1000) {
+				cash *= 1.2;
+				repX(10, "shows");
+			}
+
+			if (slave.lactation > 0) {
+				cash *= 1.1;
+				repX(5, "shows");
+			}
+		}
+
+		// FIXME: marked for review
+		if (arcology.FSPhysicalIdealist !== "unset") {
+			if (genderLawPass(slave)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			} else {
+				cash *= 0.9;
+				repX(-5, "shows");
+			}
+
+			if (slave.muscles > 30) {								// slave is muscular or more
+				cash *= 1.1;
+			} else {
+				cash *= 0.9;
+			}
+		}
+
+		if (arcology.FSHedonisticDecadence !== "unset") {
+			if (slave.weight > 10) {								// slave is curvy or more
+				cash *= 1.1;
+			} else {
+				cash *= 0.9;
+			}
+		}
+
+		if (arcology.FSChattelReligionist !== "unset") { 			// TODO: I don't know what to put for this one
+			if (slave.devotion < 21) { 								// if slave is careful or less
+				cash *= 0.9;
+			}
+		}
+
+		if (arcology.FSPetiteAdmiration !== "unset") {
+			if (heightPass(slave)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			} else {
+				cash *= 0.9;
+				repX(-5, "shows");
+			}
+		}
+
+		if (arcology.FSStatuesqueGlorification !== "unset") {
+			if (heightPass(slave)) {
+				cash *= 1.1;
+				repX(10, "shows");
+			} else {
+				cash *= 0.9;
+				repX(-5, "shows");
+			}
+		}
+
+		if (arcology.FSRomanRevivalist !== "unset") {
+			// TODO:
+		}
+
+		if (arcology.FSAztecRevivalist !== "unset") {
+			// TODO:
+		}
+
+		if (arcology.FSEgyptianRevivalist !== "unset") {
+			// TODO:
+		}
+
+		if (arcology.FSEdoRevivalist !== "unset") {
+			// TODO:
+		}
+
+		if (arcology.FSArabianRevivalist !== "unset") {
+			// TODO:
+		}
+
+		if (arcology.FSChineseRevivalist !== "unset") {
+			// TODO:
+		}
+
+		// Close FS Effects
+
+		if (setup.entertainmentCareers.includes(slave.career)) {
+			cash *= 1.1;
+		}
+
+		// FIXME: can slaves' careers overlap categories?
+		if (setup.farmerCareers.includes(slave.career)) {
+			cash *= 1.1;
+		}
+
+		if (slave.prestige === 1) { 								// slave is prestigious
+			cash *= 1.1;
+		} else if (slave.prestige === 2) { 							// slave is very prestigious
+			cash *= 1.5;
+		} else if (slave.prestige === 3) { 							// slave is extremely prestigious
+			cash *= 1.9;
+		}
+
+		if (slave.porn.prestige === 1) { 							// slave is prestigious from porn
+			cash *= 1.1;
+		} else if (slave.porn.prestige === 2) { 					// slave is very prestigious from porn
+			cash *= 1.4;
+		} else if (slave.porn.prestige === 3) { 					// slave is extremely prestigious from porn
+			cash *= 1.7;
+		}
+
+		// FIXME: needed when using healthPenalty()?
+		if (slave.health.condition > 50) {							// slave is very healthy or more
+			cash *= 1.2;
+		} else if (slave.health.condition < -20) {
+			cash *= 0.9;
+		}
+
+		// TODO: add checks for family and relationships
+
+		if (slave.face > 40) {										// slave is beautiful or more
+			cash *= 1.4;
+		} else if (slave.face > 10) {								// slave is very pretty or more
+			cash *= 1.1;
+		} else if (slave.face < -10) {								// slave is less than unattractive
+			cash *= 0.9;
+		} else if (slave.face < -40) {								// slave is less than ugly
+			cash *= 0.7;
+		}
+
+		setSlaveDevotion(cash);
+		setSlaveTrust(cash);
+
+		if (slave.weight > 30) { 									// slave is curvy or more
+			if (arcology.FSHedonisticDecadence !== "unset") {
+				cash *= 0.8;
+			}
+		} else if (slave.weight < -30) { 							// slave is very thin or less
+			cash *= 0.8;											// TODO: put this on a scale
+		}
+
+		// FIXME: marked for rewrite
+		if (slave.muscles > 30) { 									// slave is muscular or more
+			cash *= 0.9;
+		} else if (slave.muscles < -30) {							// slave is very weak or less
+			cash *= 0.6;
+		}
+
+		if (!canSeePerfectly(slave)) {
+			if (canSee(slave)) {
+				cash *= 0.8;
+			} else {
+				cash *= 0.6;
+			}
+		}
+
+		if (!canHear(slave)) {
+			cash *= 0.8;
+		}
+
+		if (isPreg(slave)) {
+			cash *= 0.8;											// TODO: not sure how to incorporate pregnancy
+		}
+		// TODO: incorporate skills
+		cash *= healthPenalty(slave);
+
+		if (slave.intelligence > 50) {								// slave is very smart or better
+			cash *= 1.4;
+		} else if (slave.intelligence < -50) {						// slave is very slow or less
+			cash *= 0.6;
+		}
+
+		if (slave.energy > 95) {									// slave is a nymphomaniac
+			cash *= 1.5;
+		} else if (slave.energy > 80) {								// slave has powerful sex drive
+			cash *= 1.3;
+		} else if (slave.energy > 60) {								// slave has good sex drive
+			cash *= 1.1;
+		} else if (slave.energy > 40) {								// slave has average sex drive
+			cash *= 0.9;
+		} else if (slave.energy > 20) {								// slave has poor sex drive
+			cash *= 0.8;
+		} else {													// slave has no sex drive
+			cash *= 0.6;
+		}
+
+		switch (slave.fetish) {
+			case "submissive":
+			case "humiliation":
+				if (V.seeBestiality) {
+					if (slave.fetishKnown) {
+						cash *= 1.1;
+					} else {
+						slave.fetishKnown = jsRandom(1, 100) > 80 ? 1 : 0;
+					}
+				} else {
+					if (slave.fetishKnown) {
+						cash *= 0.9;
+					} else {
+						slave.fetishKnown = jsRandom(1, 100) > 80 ? 1 : 0;
+					}
+				}
+				break;
+
+			default:
+				break;
+		}
+
+		switch (slave.behavioralFlaw) {
+			case "devout":
+			case "arrogant":
+				cash *= 0.9;
+				break;
+
+			default:
+				break;
+		}
+
+		switch (slave.behavioralQuirk) {
+			case "sinful":
+				cash *= 1.1;
+				break;
+
+			default:
+				break;
+		}
+
+		switch (slave.sexualFlaw) {
+			case "shamefast":
+				cash *= 1.1;
+				break;
+
+			default:
+				break;
+		}
+
+		switch (slave.sexualQuirk) {
+			case "perverted":
+				cash *= 1.1;
+				break;
+
+			default:
+				break;
+		}
+
+		return cash;
+	}
+
+	function setSlaveDevotion(amount) {
+		const slaveApproves = () =>
+			sexualQuirks.includes(slave.sexualQuirk) ||
+			behavioralQuirks.includes(slave.behavioralQuirk) ||
+			fetishes.includes(slave.fetish);
+
+		if (slave.devotion > 50) {
+			amount *= 1.5;
+
+			if (V.seeBestiality) {
+				if (slaveApproves) {
+					slave.devotion += 2;
+				} else {
+					slave.devotion--;
+				}
+			}
+
+			if (V.farmyardBreeding) {
+				if (slaveApproves) {
+					slave.devotion += 2;
+				} else {
+					slave.devotion--;
+				}
+			}
+
+			if (V.farmyardRestraints) {
+				if (slaveApproves) {
+					slave.devotion += 3;
+				} else {
+					slave.devotion -= 2;
+				}
+			}
+		} else if (slave.devotion < -50) {
+			amount *= 0.5;
+
+			if (V.seeBestiality) {
+				if (slaveApproves) {
+					slave.devotion++;
+				} else {
+					slave.devotion -= 2;
+				}
+			}
+
+			if (V.farmyardBreeding) {
+				if (slaveApproves) {
+					slave.devotion++;
+				} else {
+					slave.devotion -= 2;
+				}
+			}
+
+			if (V.farmyardRestraints) {
+				if (slaveApproves) {
+					slave.devotion += 2;
+				} else {
+					slave.devotion -= 3;
+				}
+			}
+		}
+
+		return amount;
+	}
+
+	function setSlaveTrust(amount) {
+		const slaveApproves = () =>
+			sexualQuirks.includes(slave.sexualQuirk) ||
+			behavioralQuirks.includes(slave.behavioralQuirk) ||
+			fetishes.includes(slave.fetish);
+
+		if (slave.trust > 50) {
+			amount *= 1.2;
+
+			if (V.seeBestiality) {
+				if (slaveApproves) {
+					slave.trust += 2;
+				} else {
+					slave.trust--;
+				}
+			}
+
+			if (V.farmyardBreeding) {
+				if (slaveApproves) {
+					slave.trust += 2;
+				} else {
+					slave.trust--;
+				}
+			}
+
+			if (V.farmyardRestraints) {
+				if (slaveApproves) {
+					slave.trust += 3;
+				} else {
+					slave.trust -= 2;
+				}
+			}
+		} else if (slave.trust < -50) {
+			amount *= slave.devotion > 50 ? 1.2 : 0.9;
+
+			if (V.seeBestiality) {
+				if (slaveApproves) {
+					slave.trust++;
+				} else {
+					slave.trust -= 2;
+				}
+			}
+
+			if (V.farmyardBreeding) {
+				if (slaveApproves) {
+					slave.trust++;
+				} else {
+					slave.trust -= 2;
+				}
+			}
+
+			if (V.farmyardRestraints) {
+				if (slaveApproves) {
+					slave.trust += 2;
+				} else {
+					slave.trust -= 3;
+				}
+			}
+		}
+
+		return amount;
+	}
+};
diff --git a/src/facilities/farmyard/shows/saFarmyardShows.js b/src/facilities/farmyard/shows/saFarmyardShows.js
new file mode 100644
index 0000000000000000000000000000000000000000..f871fd325b800bd9ec6580ce1237443050279a30
--- /dev/null
+++ b/src/facilities/farmyard/shows/saFarmyardShows.js
@@ -0,0 +1,443 @@
+/**
+ * Returns a string describing the effects of the slave putting on shows only
+ * To see full effects, see farmyardShows.js
+ * @param {App.Entity.SlaveState} slave
+ * @returns {string}
+ */
+App.Facilities.Farmyard.putOnShows = function(slave) {
+	const
+		{ he, him, his, hers, He, His } = getPronouns(slave),
+		incomeStats = getSlaveStatisticData(slave, V.facility.farmyard),
+		arcology = V.arcologies[0],
+		_beautiful = beautiful(slave),
+		_pretty = pretty(slave),
+
+		heavyMods = SlaveStatsChecker.modScore(slave).total > 20;
+
+	let r = [];
+
+	incomeStats.income += App.Facilities.Farmyard.farmShowsIncome(slave);
+
+	r.push(`${He} also puts on shows with animals this week.`);
+
+	// Open FS Subsection
+
+	if (arcology.FSSupremacist !== "unset") {
+		if (isSuperiorRace(slave)) {
+			if (V.seeBestiality) {
+				r.push(`Society <span class="reputation dec">disapproves</span> of your allowing a member of the elite race to be degraded in such a fashion, and thus ${he} didn't earn as much.`);
+			} else {
+				r.push(`Since ${arcology.FSSupremacistRace} slaves are in fashion, ${he} attracts a little more attention than if ${he} wasn't ${arcology.FSSupremacistRace}.`);
+			}
+		} else {
+			r.push(`Society doesn't disapprove of ${him} not being ${arcology.FSSupremacistRace}, but ${he} doesn't earn as much as ${he} would if ${he} was.`);
+		}
+	}
+
+	if (arcology.FSSubjugationist !== "unset") {
+		if (isInferiorRace(slave)) {
+			if (V.seeBestiality) {
+				r.push(`Society <span class="reputation inc">approves</span> of the degradation you submit your ${arcology.FSSubjugationistRace} slaves to, and so ${he} earns you a bit more.`);
+			} else {
+				r.push(`Fewer people want to go see a ${arcology.FSSubjugationistRace} slave put on a show, and so ${he} doesn't earn as much as ${he} otherwise could.`);
+			}
+		} else {
+			r.push(`Society doesn't disapprove of ${him} not being ${arcology.FSSubjugationistRace}, but ${he} doesn't earn as much as ${he} would if ${he} was.`);
+		}
+	}
+
+	if (arcology.FSRepopulationFocus !== "unset") {
+		if (isPreg(slave)) {
+			if (slave.eggType === "human") {
+				r.push(`Society <span class="reputation inc">greatly approves</span> of your having pregnant slaves ${V.seeBestiality ? `have sex` : `put on shows`} with animals.`);
+			} else {
+				r.push(`Society is <span class="reputation dec">disgusted</span> by ${his} pregnancy when they realize that what it is in ${his} womb is not human.`);
+			}
+		}
+	}
+
+	if (arcology.FSRestart !== "unset") {
+		if (isPreg(slave)) {
+			if (isEliteBreeder(slave)) {
+				if (slave.eggType === "human") {
+					r.push(`Society is <span class="reputation dec">horrified</span> by the fact that you would dare defile a slave deemed fit for humanity's revival in such a manner, especially one that is carrying the future of the planet.`);
+				} else {
+					r.push(`Society is <span class="reputation dec">absolutely repulsed</span> by the fact that you would dare defile a slave deemed fit for humanity's revival in such a manner, especially when they learn that what is in ${his} womb is not even human.`);
+				}
+			} else {
+				if (slave.eggType === "human") {
+					r.push(`Society is <span class="reputation dec">extremely disgusted</span> by ${his} pregnancy and the fact that you would have ${him} ${V.seeBestiality ? `have sex` : `put on shows`} with animals while sporting a baby bump.`);
+				} else {
+					r.push(`Society is disgusted by ${his} pregnancy until they learn that what is in ${his} womb is not human.`);
+				}
+			}
+		} else {
+			if (V.seeBestiality && isEliteBreeder(slave)) {
+				r.push(`Society is <span class="reputation dec">heavily disapproving</span> of the fact that you would dare defile a slave deemed fit for humanity's revival in such a manner.`);
+			}
+		}
+	}
+
+	if (arcology.FSGenderRadicalist !== "unset") {
+		if (slave.dick > 0) {
+			r.push(`${His} patrons <span class="reputation inc">approve</span> of the fact that ${he} has a dick.`);
+		} else {
+			r.push(`${His} patrons <span class="reputation dec">are disappointed</span> that ${he} doesn't have a dick.`);
+		}
+	}
+
+	if (arcology.FSGenderFundamentalist !== "unset") {
+		if (isPreg(slave) || setup.fakeBellies.includes(slave.bellyAccessory)) {
+			r.push(`${His} viewers <span class="reputation inc">approve</span> of the fact that ${he} is sporting a baby bump${setup.fakeBellies.includes(slave.bellyAccessory) ? `, even though ${hers} isn't real` : ``}.`);
+		} else {
+			r.push(`${His} viewers <span class="reputation dec">are disappointed</span> that ${he} isn't pregnant.`);
+		}
+	}
+
+	if (arcology.FSPaternalist !== "unset") {
+		if (V.seeBestiality) {
+			if (V.farmyardBreeding) {
+				if (V.farmyardRestraints) {
+					r.push(`Your citizens are <span class="reputation dec">extremely disapproving</span> of the fact that you would allow your slaves to be treated as nothing more than a bound breeding toy for your animals.`);
+				} else {
+					r.push(`Your citizens are <span class="reputation dec">highly disapproving</span> of the fact that you would allow your slaves to be treated as nothing more than a breeding toy for your animals.`);
+				}
+			} else {
+				if (V.farmyardRestraints) {
+					r.push(`Your citizens are <span class="reputation dec">very disapproving</span> of the fact that you would allow your slaves to be treated as nothing more than a bound toy for your animals.`);
+				} else {
+					r.push(`Your citizens are <span class="reputation dec">disapproving</span> of the fact that you would allow your slaves to be treated as nothing more than a toy for your animals.`);
+				}
+			}
+		} else {
+			// TODO:
+		}
+	}
+
+	if (arcology.FSDegradationist !== "unset") {
+		if (V.seeBestiality) {
+			if (V.farmyardBreeding) {
+				if (V.farmyardRestraints) {
+					r.push(`Your citizens are <span class="reputation dec">extremely approving</span> of the fact that you would allow your slaves to be treated as nothing more than a bound breeding toy for your animals.`);
+				} else {
+					r.push(`Your citizens are <span class="reputation dec">highly approving</span> of the fact that you would allow your slaves to be treated as nothing more than a breeding toy for your animals.`);
+				}
+			} else {
+				if (V.farmyardRestraints) {
+					r.push(`Your citizens are <span class="reputation dec">very approving</span> of the fact that you would allow your slaves to be treated as nothing more than a bound toy for your animals.`);
+				} else {
+					r.push(`Your citizens are <span class="reputation dec">approving</span> of the fact that you would allow your slaves to be treated as nothing more than a toy for your animals.`);
+				}
+			}
+		} else {
+			// TODO:
+		}
+	}
+
+	if (arcology.FSBodyPurist !== "unset") {
+		if (SlaveStatsChecker.isModded(slave)) {
+			r.push(`The members of the audience <span class="reputation dec">disapprove</span> that you would use a slave with ${heavyMods ? `such heavy` : ``} modifications to put on shows.`);
+		}
+	}
+
+	if (arcology.FSTransformationFetishist !== "unset") {
+		if (SlaveStatsChecker.isModded(slave)) {
+			r.push(`The members of the audience <span class="reputation inc">approve</span> that you would use a slave with ${heavyMods ? `such heavy` : ``} modifications to put on shows.`);
+		}
+	}
+
+	if (arcology.FSYouthPreferentialist !== "unset") {
+		if (isYoung(slave)) {
+			r.push(`${arcology.name}'s citizens <span class="reputation dec">disapprove</span> of your using such old slaves to put on shows.`);
+		} else {
+			r.push(`${arcology.name}'s citizens <span class="reputation inc">approve</span> of your using young slaves to put on shows.`);
+		}
+	}
+
+	if (arcology.FSMaturityPreferentialist !== "unset") {
+		if (isYoung(slave)) {
+			r.push(`${arcology.name}'s citizens <span class="reputation inc">approve</span> of your using mature slaves to put on shows.`);
+		} else {
+			r.push(`${arcology.name}'s citizens <span class="reputation dec">disapprove</span> of your using such young slaves to put on shows.`);
+		}
+	}
+
+	if (arcology.FSSlimnessEnthusiast !== "unset") {
+		if (slimLawPass(slave)) {
+			r.push(`${His} slim body <span class="reputation inc">attracts a wider audience.</span>`);
+		} else {
+			r.push(`Society finds your using a slave with such a flabby body <span class="reputation dec">disgusting.</span>`);
+		}
+	}
+
+	// FIXME: marked for possible rewrite
+	if (arcology.FSAssetExpansionist !== "unset") {
+		if (isStacked(slave)) {
+			r.push(`Society <span class="reputation inc">approves</span> of the fact that you respect their ideals in using a slave with larger assets.`);
+		}
+	}
+
+	if (arcology.FSPastoralist !== "unset") {
+		if (slave.boobs >= 1000) {
+			r.push(`The fact that ${slave.slaveName} has such large udders <span class="reputation inc">pleases your citizens.</span>`);
+		}
+
+		if (slave.lactation > 0) {
+			r.push(`Your citizens ${slave.boobs >= 1000 ? `also ` : ``}<span class="reputation inc">approve</span> of the fact that ${he} is visibly lactating.`);
+		}
+	}
+
+	// FIXME: marked for review
+	if (arcology.FSPhysicalIdealist !== "unset") {
+		if (genderLawPass(slave)) {
+			r.push(`The fact that ${slave.slaveName} has what is considered the ideal form also <span class="reputation inc">helps ${him} attract attention,</span> and so ${he} earns a bit more.`);
+		} else {
+			r.push(`${His} form isn't exactly what ${arcology.name}'s citizens consider ideal, and so ${he} doesn't <span class="reputation dec">earn as much.</span>`);
+		}
+	}
+
+	// FIXME: marked for rewrite
+	if (arcology.FSHedonisticDecadence !== "unset") {
+		if (slave.weight > 10) {
+			r.push(`Since ${slave.slaveName} is large enough for ${arcology.name}'s citizens, ${he} earns slightly more.`);
+		} else {
+			r.push(`${He} doesn't earn quite as much ${he} would have if ${he} was a bit larger in size.`);
+		}
+	}
+
+	if (arcology.FSChattelReligionist !== "unset") {
+		if (slave.devotion <= 20) {
+			// TODO: write this
+		} else {
+			// TODO: write this
+		}
+	}
+
+	if (arcology.FSPetiteAdmiration !== "unset") {
+		if (heightPass(slave)) {
+			r.push(`The fact that you are using such small slaves <span class="reputation inc">pleases your citizens.</span>`);
+		} else {
+			r.push(`The fact that you are using such small slaves <span class="reputation dec">displeases your citizens.</span>`);
+		}
+	}
+
+	if (arcology.FSStatuesqueGlorification !== "unset") {
+		if (heightPass(slave)) {
+			r.push(`Your citizens <span class="reputation inc">approve</span> of your using such statuesque slaves for putting on shows.`);
+		} else {
+			r.push(`Your citizens <span class="reputation dec">disapprove</span> of your not using taller slaves for putting on shows.`);
+		}
+	}
+
+	if (arcology.FSRomanRevivalist !== "unset") {
+		// TODO:
+	}
+
+	if (arcology.FSAztecRevivalist !== "unset") {
+		// TODO:
+	}
+
+	if (arcology.FSEgyptianRevivalist !== "unset") {
+		// TODO:
+	}
+
+	if (arcology.FSEdoRevivalist !== "unset") {
+		// TODO:
+	}
+
+	if (arcology.FSArabianRevivalist !== "unset") {
+		// TODO:
+	}
+
+	if (arcology.FSChineseRevivalist !== "unset") {
+		// TODO:
+	}
+
+	// Close FS Subsection
+
+	if (setup.entertainmentCareers.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 (slave.prestige === 1) {
+		r.push(`Because some of your citizens already know of ${him}, ${he} earns more.`);
+	} else if (slave.prestige === 2) {
+		r.push(`Because a lot of your citizens already know of ${him}, ${he} earns quite a bit more.`);
+	} else if (slave.prestige === 3) {
+		r.push(`Because ${he} is so famous, ${he} earns a lot more then ${he} would otherwise.`);
+	}
+
+	if (slave.porn.prestige === 1) {
+		r.push(`${He} earns a bit more because some of your citizens already know ${him} from porn.`);
+	} else if (slave.porn.prestige === 2) {
+		r.push(`${He} earns quite a bit more because a lot of your citizens already know ${him} from porn.`);
+	} else if (slave.porn.prestige === 3) {
+		r.push(`${He} earns a lot more because ${he} is so famous from porn.`);
+	}
+
+	if (slave.health.condition > 50) {
+		r.push(`${He} is in such excellent health that ${he} is able to put on longer and more energetic shows, earning you more.`);
+	} else if (slave.health.condition < -20) {
+		r.push(`${His} poor health negatively affects ${his} ability to put on good shows, cutting into your profits.`);
+	}
+
+	// TODO: add checks for family and relationships
+
+	if (slave.face > 40) {
+		r.push(`${He} is so ${_beautiful} that ${his} audience is willing to pay more to watch ${him} put on shows.`);
+	} else if (slave.face > 10) {
+		r.push(`${He} is so ${_pretty} that ${his} audience is willing to pay more to watch ${him} put on shows.`);
+	} else if (slave.face < -10) {
+		r.push(`${His} audience isn't willing to pay as much because of how unattractive ${his} face is.`);
+	} else if (slave.face < -40) {
+		r.push(`${His} audience isn't willing to pay as much because of how hard ${his} face is to look at.`);
+	}
+
+	// TODO: incorporate seeBestiality, breeding, and restraints
+	if (slave.devotion > 50) {
+		if (slave.trust > 50) {
+			r.push(`${He} is so devoted that ${he} works ${his} hardest to make ${his} show a good one.`);
+		} else if (slave.trust < -50) {
+			r.push(`${He} is both devoted to you and terrified of you, so ${he} tries ${his} best to make ${his} show a good one.`);
+		}
+	} else if (slave.devotion < -50) {
+		if (slave.trust > 50) {
+			r.push(`${slave.slaveName} purposefully does the bare minimum ${he} can get away with to spite you.`);
+		} else if (slave.trust < -50) {
+			r.push(`${slave.slaveName} refuses to do anything without punishment, which is evident from ${his} meager earnings.`);
+		}
+	}
+
+	// FIXME: marked for rewrite
+	if (slave.weight > 30) {
+		if (arcology.FSHedonisticDecadence === "unset") {
+			r.push(`Your citizens are not willing to pay as much to see such a fat slave put on shows, so ${he} loses some income.`);
+		}
+	} else if (slave.weight < -30) {
+		r.push(`Your citizens don't like watching such a sickly-looking slaves put on shows, so ${he} loses some income.`);
+	}
+
+	if (slave.muscles > 30) {
+		// TODO: write this - do we want something for muscles?
+	} else if (slave.muscles < -30) {
+		r.push(`${slave.slaveName} is so weak that ${he} cannot even properly handle the animals ${he}'s assigned to ${V.seeBestiality ? `fuck` : `work with`}, and isn't able to put on any sort of meaningful show.`);
+	}
+
+	if (!canSeePerfectly(slave)) {
+		r.push(`${His} ${!canSee(slave) ? `blindness makes it impossible` : `nearsightedness makes it harder`} for ${him} to see what ${he}'s doing, affecting ${his} ability to put on a good show.`);
+	}
+
+	if (slave.hears < 0) {
+		r.push(`${His} ${slave.hears < -1 ? `lack of` : `poor`} hearing makes it difficult for ${him} to hear what ${his} audience wants from ${him}, which really affects ${his} earnings.`);
+	}
+
+	// FIXME: marked for rewrite
+	if (isPreg(slave)) {
+		r.push(`${His}${slave.bellyPreg > 100000 ? ` advanced` : ``} pregnancy makes it more difficult for him to effectively put on a good show.`);
+	}
+
+	if (slave.health.tired > 60) {
+		r.push(`${He} is so tired that the energy in ${his} shows is basically nonexistent, affecting ${his} profits.`);
+	}
+
+	if (slave.intelligence > 50) {
+		r.push(`Because ${he} is so intelligent, ${he} is able to tailor ${his} shows to ${his} audience, helping ${him} bring in more in profits.`);
+	} else if (slave.intelligence < -50) {
+		r.push(`${He} is so slow that all ${he} can really do is just ${V.seeBestiality ? `lie there and take it` : `put on the most basic of moves`}, which your audience finds dull and uninteresting.`);
+	}
+
+	if (slave.energy > 95) {
+		r.push(`The fact that ${he} is a nymphomaniac helps ${him} to go for longer, allowing ${him} to really put on an amazing show.`);
+	} else if (slave.energy > 80) {
+		r.push(`The fact that ${his} sex drive is so powerful helps ${him} to really put on good shows.`);
+	} else if (slave.energy > 60) {
+		r.push(`The fact that ${his} sex drive is so good helps ${him} to put on good shows.`);
+	} else if (slave.energy > 40) {
+		r.push(`${His} average sex drive allows ${him} to put on a decent show.`);
+	} else if (slave.energy > 20) {
+		r.push(`The fact that ${his} sex drive is so poor affects ${his} performance.`);
+	} else {
+		r.push(`The fact that ${his} sex drive is nonexistent really hinders ${his} ability to put on a decent show.`);
+	}
+
+	// TODO: add more to the fetishes and flaws / quirks
+	switch (slave.fetish) {
+		case "submissive":
+			if (V.seeBestiality) {
+				if (slave.fetishKnown) {
+					r.push(`${He} is so submissive that ${he} willingly accepts ${his} position as an animal's fucktoy and <span class="reputation inc">is able to put on a decent show</span>.`);
+				} else {
+					r.push(`${S.HeadGirl ? `${S.HeadGirl.slaveName} notices` : `You notice`} that ${slave.slaveName} seems to have really taken to ${his} position as a fucktoy for animals. <span class="lightcoral">${He}'s a submissive!</span>`);
+				}
+			} else {
+				if (slave.fetishKnown) {
+					r.push(`Being a submissive, ${he} <span class="reputation dec">doesn't have the confidence required</span> to really put on a good show.`);
+				} else {
+					r.push(`${slave.slaveName} doesn't seem to have the type of fortitude needed to put on a show, and after some probing, you discover why - it turns out <span class="lightcoral">${he}'s a submissive!</span>`);
+				}
+			}
+			break;
+
+		case "humiliation":
+			if (V.seeBestiality) {
+				if (slave.fetishKnown) {
+					r.push(`${slave.slaveName} uses the most of this humiliating experience to really put on a show, to <span class="reputation inc">the approval of ${his} audience</span>.`);
+				} else {
+					// TODO:
+				}
+			} else {
+				// TODO: not sure about this one
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	switch (slave.behavioralFlaw) {
+		case "arrogant":
+			r.push(`Because ${he} is so arrogant, ${he} tries ${his} hardest to avoid ${V.seeBestiality ? `fucking animals` : `putting on shows`}, <span class="cash dec">which affects ${his} profits</span> and <span class="reputation dec">your reputation.</span>`);
+			break;
+
+		case "devout":
+			r.push(`${He} often prays ${V.seeBestiality ? ` while getting fucked by animals` : ` during ${his} shows`}, which your citizens <span class="reputation dec">find off-putting.</span>`);
+			break;
+
+		default:
+			break;
+	}
+
+	switch (slave.behavioralQuirk) {
+		case "sinful":
+			r.push(`${He} relishes in ${his} ability to do something so sinful and depraved, and <span class="reputation inc">really puts on a show.</span>`);
+			break;
+
+		default:
+			break;
+	}
+
+	switch (slave.sexualFlaw) {
+		case "shamefast":
+			r.push(`${His} crippling shamefastness <span class="reputation dec">limits ${his} ability</span> to put on a decent show.`);
+			break;
+
+		default:
+			break;
+	}
+
+	switch (slave.sexualQuirk) {
+		case "perverted":
+			r.push(`${His} shows are a <span class="reputation inc">real spectacle,</span> since ${he} is so perverted that ${he} is willing to go far beyond the bare minimum.`);
+			break;
+
+		default:
+			break;
+	}
+
+	return r.join(' ');
+};
diff --git a/src/facilities/headGirlSuite/headGirlSuite.tw b/src/facilities/headGirlSuite/headGirlSuite.tw
index af33f0331020b0323f7509c9dcb70063ec36771d..d9c0f55a06568459c9f292e2dc1365de8733b25d 100644
--- a/src/facilities/headGirlSuite/headGirlSuite.tw
+++ b/src/facilities/headGirlSuite/headGirlSuite.tw
@@ -10,20 +10,20 @@
 <p class="scene-intro">
 </p>
 
-<<if $HeadGirl == 0>>
+<<if $HeadGirlID == 0>>
 	You have no Head Girl appointed to live in $HGSuiteName.
 	<<link "Select Head Girl">><<goto "HG Select">><</link>>
 <<elseif App.Entity.facilities.headGirlSuite.hasEmployees>>
-	<<setLocalPronouns $HeadGirl>>
-	Your Head Girl $HeadGirl.slaveName has $slaves[_i].slaveName living in $HGSuiteName with $him, as $his servant, assistant, and sexual appliance.
+	<<setLocalPronouns _S.HeadGirl>>
+	Your Head Girl _S.HeadGirl.slaveName has $slaves[_i].slaveName living in $HGSuiteName with $him, as $his servant, assistant, and sexual appliance.
 	<<link "Manage Head Girl">><<goto "HG Select">><</link>>
 <<else>>
-	<<setLocalPronouns $HeadGirl>>
-	Your Head Girl $HeadGirl.slaveName lives alone in $his handsome suite.
+	<<setLocalPronouns _S.HeadGirl>>
+	Your Head Girl _S.HeadGirl.slaveName lives alone in $his handsome suite.
 	<<link "Manage Head Girl">><<goto "HG Select">><</link>>
 <</if>>
 
-<<if $HeadGirl != 0>>
+<<if $HeadGirlID != 0>>
 	<div class="note">
 		Any slave assigned to live with your Head Girl will become, for most practical purposes, $his slave.
 	</div>
@@ -58,7 +58,7 @@
 		</div>
 	<</if>>
 	<p>
-		<<print App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.headGirlSuite)>>
+		<<includeDOM App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.headGirlSuite)>>
 	</p>
 <</if>>
 <p>
diff --git a/src/facilities/headGirlSuite/hgApplication.tw b/src/facilities/headGirlSuite/hgApplication.tw
index 23da3ec0b84cafaddb6bad2124974a1614e6307f..a1dfb4a7131a5ad700fba76d5b924cb8cc4c29b5 100644
--- a/src/facilities/headGirlSuite/hgApplication.tw
+++ b/src/facilities/headGirlSuite/hgApplication.tw
@@ -1,6 +1,6 @@
 :: HG Application [nobr]
 
-<<setLocalPronouns $HeadGirl>>
+<<setLocalPronouns _S.HeadGirl>>
 <<setLocalPronouns $activeSlave 2>>
 
 <<if ndef $HGSeverity>>
@@ -8,18 +8,18 @@
 <</if>>
 
 <<set $activeSlave.training = Math.clamp($activeSlave.training, 0, 150)>>
-<<set _effectiveness = $HeadGirl.actualAge+(($HeadGirl.intelligence+$HeadGirl.intelligenceImplant)/3)-($HeadGirl.accent*5)+($HGSeverity*10)+(($activeSlave.intelligence+$activeSlave.intelligenceImplant)/4)-($activeSlave.accent*5)>>
+<<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-$HeadGirl.actualAge))>>
+	<<set _effectiveness += Math.max(0,(30-_S.HeadGirl.actualAge))>>
 <</if>>
-<<if $HeadGirl.sexualFlaw == "abusive" && $HGSeverity >= 0>>
+<<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'>$HeadGirl.slaveName</span> notices that <span class='slave-name'>$activeSlave.slaveName</span>
+&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.
@@ -52,31 +52,31 @@ $He helps $activeSlave.slaveName however $he can. The tender care has @@.green;i
 
 <<else>>
 
-<<if $HeadGirl.career == "an arcology owner">>
+<<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($HeadGirl.career)>>
+<<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 $HeadGirl.skill.headGirl >= $masteredXP>>
+<<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 $HeadGirl.actualAge > 30>>
+<<if _S.HeadGirl.actualAge > 30>>
 	$His age and experience help $him as $he works with $activeSlave.slaveName.
-<<elseif $HeadGirl.actualAge < 25 && $AgePenalty > 0>>
+<<elseif _S.HeadGirl.actualAge < 25 && $AgePenalty > 0>>
 	$His youth and inexperience impede $him as $he works with $activeSlave.slaveName.
 <</if>>
 
-<<if $HeadGirl.accent >= 3>>
+<<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 $HeadGirl.accent == 2>>
-	$His heavy <<print aNational($HeadGirl.nationality)>> accent impedes $his clarity in $language, making $him somewhat 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 $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+	<<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>>
@@ -86,7 +86,7 @@ $He helps $activeSlave.slaveName however $he can. The tender care has @@.green;i
 		<</if>>
 	<</if>>
 <<else>>
-	<<if $HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50>>
+	<<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>>
@@ -99,15 +99,15 @@ $He helps $activeSlave.slaveName however $he can. The tender care has @@.green;i
 
 <<if $HGSeverity > 0>>
 	Your directives encourage $him to rape any slave that fails in the slightest way, a task $he approaches
-	<<if $HeadGirl.sexualFlaw == "abusive">>
+	<<if _S.HeadGirl.sexualFlaw == "abusive">>
 		eagerly, since $he prefers to take $his pleasure without consent.
-	<<elseif $HeadGirl.fetish == "sadist">>
+	<<elseif _S.HeadGirl.fetish == "sadist">>
 		with sadistic enthusiasm.
-	<<elseif $HeadGirl.fetish == "dom">>
+	<<elseif _S.HeadGirl.fetish == "dom">>
 		with confidence.
-	<<elseif $HeadGirl.energy > 95>>
+	<<elseif _S.HeadGirl.energy > 95>>
 		as a convenient way to
-		<<if ($HeadGirl.balls > 0)>>
+		<<if (_S.HeadGirl.balls > 0)>>
 			drain $his perpetually overfilled balls into a series of spasming holes.
 		<<else>>
 			sate $his excess sexual appetite.
@@ -132,18 +132,18 @@ $He helps $activeSlave.slaveName however $he can. The tender care has @@.green;i
 		<<set $activeSlave.trust -= 1>>
 		<<set $activeSlave.devotion -= 1>>
 	<</if>>
-	$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.
+	_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 $HeadGirl.sexualFlaw == "abusive">>
+	<<if _S.HeadGirl.sexualFlaw == "abusive">>
 		and $he usually decides in favor of forcing misbehaving slaves to get $him off.
-	<<elseif $HeadGirl.fetish == "sadist">>
+	<<elseif _S.HeadGirl.fetish == "sadist">>
 		permission the sadist uses for $his own gratification.
-	<<elseif $HeadGirl.fetish == "dom">>
+	<<elseif _S.HeadGirl.fetish == "dom">>
 		which the dominant $girl uses confidently.
-	<<elseif $HeadGirl.energy > 95>>
+	<<elseif _S.HeadGirl.energy > 95>>
 		which $he uses to
-		<<if ($HeadGirl.balls > 0)>>
+		<<if (_S.HeadGirl.balls > 0)>>
 			force $his subordinates to keep $his balls drained.
 		<<else>>
 			extract the constant sexual favors $his libido demands.
@@ -187,7 +187,7 @@ $He helps $activeSlave.slaveName however $he can. The tender care has @@.green;i
 
 <<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
 <<set $activeSlave.training += _effectiveness>>
-$HeadGirl.slaveName does $his best to get $activeSlave.slaveName past it with punishments and rewards,
+_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>>
@@ -200,7 +200,7 @@ $HeadGirl.slaveName does $his best to get $activeSlave.slaveName past it with pu
 
 <<set _effectiveness -= ($activeSlave.intelligence+$activeSlave.intelligenceImplant)/3>>
 <<set $activeSlave.training += _effectiveness>>
-$HeadGirl.slaveName punishes $activeSlave.slaveName whenever $he catches _him2 indulging in _his2 bad habits,
+_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>>
@@ -233,13 +233,13 @@ $He punishes $activeSlave.slaveName whenever $he sees _him2 breaking the rules y
 <<elseif $HGtraining == "oral skill">>
 
 <<set $activeSlave.training = 0>>
-<<if ($HeadGirl.fetish == "cumslut") && ($HeadGirl.fetishStrength > 60)>>
+<<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.@@
 	<<= SkillIncrease.Oral($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.dick > 0) && canPenetrate($HeadGirl)>>
+<<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.@@
 	<<= SkillIncrease.Oral($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.clit > 2)>>
+<<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.@@
 	<<= SkillIncrease.Oral($activeSlave, random(5,10))>>
 <<else>>
@@ -250,13 +250,13 @@ $He punishes $activeSlave.slaveName whenever $he sees _him2 breaking the rules y
 <<elseif $HGtraining == "anal skill">>
 
 <<set $activeSlave.training = 0>>
-<<if ($HeadGirl.fetish == "buttslut") && ($HeadGirl.fetishStrength > 60)>>
+<<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.@@
 	<<= SkillIncrease.Anal($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.dick > 0) && canPenetrate($HeadGirl)>>
+<<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.@@
 	<<= SkillIncrease.Anal($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.clit > 2)>>
+<<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.@@
 	<<= SkillIncrease.Anal($activeSlave, random(5,10))>>
 <<else>>
@@ -267,13 +267,13 @@ $He punishes $activeSlave.slaveName whenever $he sees _him2 breaking the rules y
 <<elseif $HGtraining == "fuck skill">>
 
 <<set $activeSlave.training = 0>>
-<<if ($HeadGirl.energy > 95)>>
+<<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.@@
 	<<= SkillIncrease.Vaginal($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.dick > 0) && canPenetrate($HeadGirl)>>
+<<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.@@
 	<<= SkillIncrease.Vaginal($activeSlave, random(5,10))>>
-<<elseif ($HeadGirl.clit > 2)>>
+<<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.@@
 	<<= SkillIncrease.Vaginal($activeSlave, random(5,10))>>
 <<else>>
@@ -290,7 +290,7 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 <<elseif $HGtraining == "entertain skill">>
 
 <<set $activeSlave.training = 0>>
-<<if ($HeadGirl.fetish == "humiliation") && ($HeadGirl.fetishStrength > 60)>>
+<<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.@@
 	<<= SkillIncrease.Entertain($activeSlave, random(5,10))>>
 <<else>>
@@ -304,7 +304,7 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 <<if ($activeSlave.devotion >= -20)>>
 <<if ($activeSlave.attrKnown == 0)>>
 	<<set $activeSlave.attrKnown = 1>>
-	$HeadGirl.slaveName also investigates the slave's basic sexual orientation, finding that _he2 is
+	_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)>>
@@ -342,9 +342,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 					<<set _piercingForbidden = 1, $activeSlave.earPiercing = 0>>
 				<<else>>
 					<<if $arcologies[0].FSDegradationist != "unset">>
-						$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.
+						_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>>
-						$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.
+						_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>>
@@ -361,9 +361,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 					<<set _piercingForbidden = 1, $activeSlave.nosePiercing = 0>>
 				<<else>>
 					<<if $arcologies[0].FSDegradationist != "unset">>
-						$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.
+						_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>>
-						$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.
+						_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>>
@@ -380,9 +380,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 					<<set _piercingForbidden = 1, $activeSlave.eyebrowPiercing = 0>>
 				<<else>>
 					<<if $arcologies[0].FSDegradationist != "unset">>
-						$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.
+						_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>>
-						$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.
+						_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>>
@@ -399,9 +399,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 					<<set _piercingForbidden = 1, $activeSlave.lipsPiercing = 0>>
 				<<else>>
 					<<if $arcologies[0].FSDegradationist != "unset">>
-						$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.
+						_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>>
-						$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.
+						_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>>
@@ -418,9 +418,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 					<<set _piercingForbidden = 1, $activeSlave.navelPiercing = 0>>
 				<<else>>
 					<<if $arcologies[0].FSDegradationist != "unset">>
-						$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, $HeadGirl.slaveName makes clear to _him2 that _his2 body belongs to you.
+						_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>>
-						$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.
+						_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>>
@@ -430,9 +430,9 @@ In spare moments $he teaches $activeSlave.slaveName how to prostitute _himself2.
 		<</for>>
 		<<if _piercingForbidden>>
 			<<if $arcologies[0].FSDegradationist != "unset">>
-				$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.
+				_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>>
-				$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.
+				_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>>
diff --git a/src/facilities/headGirlSuite/hgSelect.tw b/src/facilities/headGirlSuite/hgSelect.tw
index f729326e0e55e7b14bd8c46ce20f2db40176bba3..50bf5f532d7704bd7c28b61f7f92a8d622cbac8b 100644
--- a/src/facilities/headGirlSuite/hgSelect.tw
+++ b/src/facilities/headGirlSuite/hgSelect.tw
@@ -5,10 +5,9 @@
 <h1>Head Girl Management</h1>
 
 <<set _HGName = "Your Head Girl">>
-<<if ($HeadGirl != 0)>>
-	<<set $HeadGirl = getSlave($HeadGirl.ID)>>
-	<<setLocalPronouns $HeadGirl>>
-	<<set _HGName = SlaveFullName($HeadGirl)>>
+<<if ($HeadGirlID != 0)>>
+	<<setLocalPronouns _S.HeadGirl>>
+	<<set _HGName = SlaveFullName(_S.HeadGirl)>>
 	<span class='slave-name'>_HGName</span> is serving as your Head Girl<<if $arcologies[0].FSEgyptianRevivalistLaw == 1>> and Consort; it is $his role to serve and comfort you at your side, in addition to performing normal Head Girl duties<</if>>.
 	<<if ($HGSuite)>>
 		<br> Currently $he lives in <<link "$HGSuiteNameCaps""Head Girl Suite">><</link>>
@@ -66,22 +65,22 @@ _HGName
 <</if>>
 
 <<if $seePreg != 0>>
-	<<if $HeadGirl != 0>>
+	<<if $HeadGirlID != 0>>
 		<<if $universalRulesImpregnation == "HG">>
 			<br><br>_HGName is responsible for impregnating fertile slaves.
-			<<set $HGCum = 2+Math.trunc(($HeadGirl.balls/5)+($HeadGirl.energy/95)+($HeadGirl.health.condition/95)+($HeadGirl.devotion/95)+($reproductionFormula*5))>>
-			<<if canPenetrate($HeadGirl) && $HeadGirl.pubertyXY == 1>>
-				To maximize the chances of impregnation, $he will fuck fertile pussies frequently during the week. $HeadGirl.slaveName can service $HGCum slaves this way.
-				<<if $HeadGirl.devotion > 95>>$He loves you so much $he'll fuck them until $he's sore.<<else>>If $he were more devoted to you, $he might be able to drive $himself to get hard and service one more.<</if>>
-				<<if $HeadGirl.balls >= 120>>$His unreal balls produce nearly an endless supply of semen; $his ability to impregnate is almost limitless.<<elseif $HeadGirl.balls >= 80>>$His inhuman balls produce so much semen $he can easily impregnate twenty girls in one sitting.<<elseif $HeadGirl.balls >= 50>>$His giant balls produce so much semen $he can easily impregnate twelve girls in one sitting.<<elseif $HeadGirl.balls >= 25>>$His oversized balls produce so much semen $he can cum repeatedly in a single session.<<elseif $HeadGirl.balls >= 5>>$His big balls produce so much semen $he can cum more before $he's drained.<<else>>Bigger balls would let $him cum more before $he's drained.<</if>>
-				<<if $HeadGirl.health.condition > 95>>$His wonderful health lets $him get hard and stay hard all the time.<<else>>If $his health were perfect, $he might be able to get hard more often.<</if>>
-				<<if $HeadGirl.energy > 95>>$His nymphomania drives $him to go above and beyond in this.<<else>>A more powerful sex drive could reduce $his refractory period.<</if>>
+			<<set $HGCum = 2+Math.trunc((_S.HeadGirl.balls/5)+(_S.HeadGirl.energy/95)+(_S.HeadGirl.health.condition/95)+(_S.HeadGirl.devotion/95)+($reproductionFormula*5))>>
+			<<if canPenetrate(_S.HeadGirl) && _S.HeadGirl.pubertyXY == 1>>
+				To maximize the chances of impregnation, $he will fuck fertile pussies frequently during the week. _S.HeadGirl.slaveName can service $HGCum slaves this way.
+				<<if _S.HeadGirl.devotion > 95>>$He loves you so much $he'll fuck them until $he's sore.<<else>>If $he were more devoted to you, $he might be able to drive $himself to get hard and service one more.<</if>>
+				<<if _S.HeadGirl.balls >= 120>>$His unreal balls produce nearly an endless supply of semen; $his ability to impregnate is almost limitless.<<elseif _S.HeadGirl.balls >= 80>>$His inhuman balls produce so much semen $he can easily impregnate twenty girls in one sitting.<<elseif _S.HeadGirl.balls >= 50>>$His giant balls produce so much semen $he can easily impregnate twelve girls in one sitting.<<elseif _S.HeadGirl.balls >= 25>>$His oversized balls produce so much semen $he can cum repeatedly in a single session.<<elseif _S.HeadGirl.balls >= 5>>$His big balls produce so much semen $he can cum more before $he's drained.<<else>>Bigger balls would let $him cum more before $he's drained.<</if>>
+				<<if _S.HeadGirl.health.condition > 95>>$His wonderful health lets $him get hard and stay hard all the time.<<else>>If $his health were perfect, $he might be able to get hard more often.<</if>>
+				<<if _S.HeadGirl.energy > 95>>$His nymphomania drives $him to go above and beyond in this.<<else>>A more powerful sex drive could reduce $his refractory period.<</if>>
 			<<else>>
-				However, $HeadGirl.slaveName cannot perform this duty.
+				However, _S.HeadGirl.slaveName cannot perform this duty.
 			<</if>>
 			[["Rescind " + $his + " impregnation responsibility"|HG Select][$universalRulesImpregnation = "none"]] | [[See to it yourself|HG Select][$universalRulesImpregnation = "PC"]]
 		<<else>>
-			<<if canPenetrate($HeadGirl) && $HeadGirl.pubertyXY == 1>>
+			<<if canPenetrate(_S.HeadGirl) && _S.HeadGirl.pubertyXY == 1>>
 				<br><br>_HGName is capable of impregnating slaves, but it's not part of $his responsibilities. [["Assign " + $him + " to impregnate"|HG Select][$universalRulesImpregnation = "HG"]]
 			<</if>>
 		<</if>>
@@ -89,6 +88,6 @@ _HGName
 <</if>>
 
 <br><br>''Appoint a Head Girl from among your devoted slaves:''
-<br><br>[[None|Main][removeJob($HeadGirl, "be your Head Girl")]]
+<br><br>[[None|Main][removeJob(_S.HeadGirl, "be your Head Girl")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.headGirlSuite, "Main")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.headGirlSuite, "Main")>>
diff --git a/src/facilities/masterSuite/concubineSelect.tw b/src/facilities/masterSuite/concubineSelect.tw
index 9f153e7544385ea6b05ec7b253285604f9b0fd08..bc2b520e072bc93718c5252d550861726e49ff40 100644
--- a/src/facilities/masterSuite/concubineSelect.tw
+++ b/src/facilities/masterSuite/concubineSelect.tw
@@ -1,15 +1,14 @@
 :: Concubine Select [nobr jump-to-safe jump-hidden jump-from-safe]
 
 <<set $nextButton = "Back", $nextLink = "Master Suite", $encyclopedia = "Concubine">>
-<<if ($Concubine != 0)>>
-	<<set $Concubine = getSlave($Concubine.ID)>>
-	<<setLocalPronouns $Concubine>>
-	<span class='slave-name'><<= SlaveFullName($Concubine)>></span> is serving as your Concubine.<br><br>$He will take the lead in seeing to your pleasure.
+<<if ($ConcubineID != 0)>>
+	<<setLocalPronouns _S.Concubine>>
+	<span class='slave-name'><<= SlaveFullName(_S.Concubine)>></span> is serving as your Concubine.<br><br>$He will take the lead in seeing to your pleasure.
 <<else>>
 	You have not selected a Concubine.
 <</if>>
 
 <br><br>''Appoint a Concubine from your devoted slaves:''
-<br><br>[[None|Master Suite][removeJob($Concubine, "be your Concubine")]]
+<br><br>[[None|Master Suite][removeJob(_S.Concubine, Job.CONCUBINE)]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.masterSuite, "Master Suite")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.masterSuite, "Master Suite")>>
diff --git a/src/facilities/masterSuite/masterSuite.tw b/src/facilities/masterSuite/masterSuite.tw
index efa64270a4d4f9649e1bc1f395ec8c4a96ec3496..42a8792219a00200441e8f5582cef00220be59b0 100644
--- a/src/facilities/masterSuite/masterSuite.tw
+++ b/src/facilities/masterSuite/masterSuite.tw
@@ -9,9 +9,9 @@
 
 <<set $masterSuiteAverageEnergy = 0, _masterSuiteAverageCum = 0, _masterSuiteAverageDick = 0, _masterSuiteAverageDom = 0, _masterSuiteAverageMilk = 0, _masterSuiteAveragePreg = 0, _masterSuiteAverageSadism = 0, _masterSuitePregnantSlaves = 0, _masterSuitePregnantSlavesMultiple = 0>>
 
-<<if ($Concubine != 0 && $Concubine.pregKnown == 1)>>
+<<if (_S.Concubine && _S.Concubine.pregKnown == 1)>>
 	<<set _masterSuitePregnantSlaves++>>
-	<<if $Concubine.pregType > 1>>
+	<<if _S.Concubine.pregType > 1>>
 		<<set _masterSuitePregnantSlavesMultiple++>>
 	<</if>>
 <</if>>
@@ -323,7 +323,7 @@ $masterSuiteNameCaps is furnished
 <<set _Tmult0 = Math.trunc($masterSuite*1000*$upgradeMultiplierArcology)>>
 <div class="choices" style="font-style:normal">
 	[[Expand the Master Suite|Master Suite][cashX(forceNeg(_Tmult0), "capEx"), $masterSuite += 2, $PC.skill.engineering += .1]] //Costs <<print cashFormat(_Tmult0)>>//
-	<<if $Concubine == 0 && _MsL == 0>>
+	<<if $ConcubineID == 0 && _MsL == 0>>
 		| [[Decommission the Master Suite|Main][$masterSuite = 0, $masterSuiteUpgradeLuxury = 0, $masterSuitePregnancySlaveLuxuries = 0, $masterSuiteDecoration = "standard", $masterSuitePregnancyFertilityDrugs = 0, $masterSuitePregnancyFertilitySupplements = 0, $masterSuiteUpgradePregnancy = 0, $masterSuiteHyperPregnancy = 0]]
 	<</if>>
 </div>
@@ -360,7 +360,7 @@ $masterSuiteNameCaps is furnished
 <</if>>
 
 <br><br>
-<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.masterSuite)>>
+<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.masterSuite)>>
 
 <br><br>Rename $masterSuiteName: <<textbox "$masterSuiteName" $masterSuiteName "Master Suite">> //Use a noun or similar short phrase//
 
diff --git a/src/facilities/masterSuite/masterSuiteReport.tw b/src/facilities/masterSuite/masterSuiteReport.tw
index beebf96c583f4c9e24e7b5243e0b66986703053f..f12436ead030151aad747a046ef20a3a5c542e36 100644
--- a/src/facilities/masterSuite/masterSuiteReport.tw
+++ b/src/facilities/masterSuite/masterSuiteReport.tw
@@ -2,13 +2,9 @@
 
 <<set _slaves = App.Utils.sortedEmployees(App.Entity.facilities.masterSuite)>>
 <<set _DL = _slaves.length, _SL = $slaves.length, $legendaryAbolitionistID = 0, $masterSuiteAverageEnergy = 0, _masterSuiteAverageCum = 0, _masterSuiteAverageDick = 0, _masterSuiteAverageDom = 0, _masterSuiteAverageMilk = 0, _masterSuiteAveragePreg = 0, _masterSuiteAverageSadism = 0, _masterSuitePregnantSlaves = 0, _masterSuitePregnantSlavesMultiple = 0>>
-<<if $Concubine != 0>>
-	<<set _FLs = $slaveIndices[$Concubine.ID]>>
-
-	<<if def _FLs>>
-		/% Add the Concubine to the _slaves list at the front of the array, so that $he displays first %/
-		<<set _slaves.unshift($Concubine), _DL++>>
-	<</if>>
+<<if $ConcubineID != 0>>
+	/% Add the Concubine to the _slaves list at the front of the array, so that $he displays first %/
+	<<set _slaves.unshift(_S.Concubine), _DL++>>
 <</if>>
 
 <<if _DL > 0>>
@@ -105,45 +101,44 @@
 		</div>
 		<</if>>
 		/* 000-250-006 */
-		<<if (_slave.assignment == "be your Concubine")>>
-			<<set $Concubine = $slaves[_FLs]>>
-			''Your concubine <<= SlaveFullName($Concubine)>> is serving you in $masterSuiteName.'' More than any other slave, $his sexual brilliance and physical appeal are @@.green;critical@@ to your reputation.
+		<<if (_slave.assignment == Job.CONCUBINE)>>
+			''Your concubine <<= SlaveFullName(_S.Concubine)>> is serving you in $masterSuiteName.'' More than any other slave, $his sexual brilliance and physical appeal are @@.green;critical@@ to your reputation.
 
-			<<if (($Concubine.prestigeDesc == "You bankrupted and enslaved $him in revenge for $his part in the attack on your arcology by the Daughters of Liberty.") && ($Concubine.newGamePlus == 0))>>
+			<<if ((_S.Concubine.prestigeDesc == "You bankrupted and enslaved $him in revenge for $his part in the attack on your arcology by the Daughters of Liberty.") && (_S.Concubine.newGamePlus == 0))>>
 				$He was once your rival, and your relationship is widely thought to be @@.green;the perfect modern romance.@@
-				<<run repX(500, "concubine", $Concubine)>>
-			<<elseif ($Concubine.prestige > 0)>>
-				<<run repX(500*$Concubine.prestige, "concubine", $Concubine)>>
+				<<run repX(500, "concubine", _S.Concubine)>>
+			<<elseif (_S.Concubine.prestige > 0)>>
+				<<run repX(500*_S.Concubine.prestige, "concubine", _S.Concubine)>>
 				<<if $verboseDescriptions == 1>>
 					Keeping such a prestigious slave as your concubine @@.green;adds to your fame.@@
-					<<if $Concubine.prestigeDesc == "$He is a famed Free Cities whore, and commands top prices.">>
+					<<if _S.Concubine.prestigeDesc == "$He is a famed Free Cities whore, and commands top prices.">>
 						When $he has a free moment, $he refines $his flexibility so that $he is prepared for any sexual position you can think of, and many more that $he researched just for your pleasure.
 					<</if>>
-					<<if $Concubine.prestigeDesc == "$He is a famed Free Cities slut, and can please anyone.">>
+					<<if _S.Concubine.prestigeDesc == "$He is a famed Free Cities slut, and can please anyone.">>
 						When $he has a free moment, $he pretties $himself up so that $he may be as beautiful as possible to make you look even better.
 					<</if>>
-					<<if $Concubine.prestigeDesc == "$He is remembered for winning best in show as a cockmilker.">>
-						<<if $Concubine.balls > 10 || $Concubine.prostate > 1>>
+					<<if _S.Concubine.prestigeDesc == "$He is remembered for winning best in show as a cockmilker.">>
+						<<if _S.Concubine.balls > 10 || _S.Concubine.prostate > 1>>
 							$He does $his best to prevent $himself from making a mess when you don't want one. When $he has free time, $he researches new techniques to make use of $his huge loads to entertain you.
-						<<elseif $Concubine.balls > 0>>
+						<<elseif _S.Concubine.balls > 0>>
 							When $he has a free moment, $he researches ways to increase the size of $his loads for you; $he dreams of the day $he show you just what $he was capable of.
 						<<else>>
 							$He wishes $he could blow huge loads for you, but without balls, there is little $he can do.
 						<</if>>
 					<</if>>
-					<<if $Concubine.prestigeDesc == "$He is remembered for winning best in show as a dairy cow.">>
-						<<if $Concubine.lactation > 1 || $Concubine.lactationAdaptation > 90>>
+					<<if _S.Concubine.prestigeDesc == "$He is remembered for winning best in show as a dairy cow.">>
+						<<if _S.Concubine.lactation > 1 || _S.Concubine.lactationAdaptation > 90>>
 							$He does $his best to prevent $himself from making a mess when you don't want one. When $he has free time, $he researches new techniques to make use of $his excessive milk to entertain you.
-						<<elseif $Concubine.lactation > 0>>
+						<<elseif _S.Concubine.lactation > 0>>
 							$He does $his best to prevent $himself from making a mess when you don't want one. When $he has free time, $he researches new techniques to make use of $his breast milk to entertain you.
 						<<else>>
 							$He wishes $he could lactate for you, but $he has dried up since $his glory days.
 						<</if>>
 					<</if>>
-					<<if $Concubine.prestigeDesc == "$He is remembered for winning best in show as a breeder.">>
-						<<if $Concubine.bellyPreg >= 5000>>
+					<<if _S.Concubine.prestigeDesc == "$He is remembered for winning best in show as a breeder.">>
+						<<if _S.Concubine.bellyPreg >= 5000>>
 							When $he has a free moment, $he refines $his flexibility so that $he can perform any sexual position possible for $him in $his motherly state. $He takes the time to look up new positions to work around $his growing pregnancy.
-						<<elseif isFertile($Concubine) || $Concubine.preg > 0>>
+						<<elseif isFertile(_S.Concubine) || _S.Concubine.preg > 0>>
 							When $he has a free moment, $he refines $his flexibility so that $he is prepared for any sexual position you can think of, no matter how heavy $he becomes with children.
 						<<else>>
 							When $he has a free moment, $he researches ways to restore $his fertility; $he dreams of the day $he can carry <<if $PC.dick != 0>>your children<<else>>children for you<</if>>.
@@ -151,16 +146,16 @@
 					<</if>>
 				<</if>>
 			<</if>>
-			<<if $Concubine.porn.prestige > 2>>
-				<<run repX(800, "concubine", $Concubine)>>
+			<<if _S.Concubine.porn.prestige > 2>>
+				<<run repX(800, "concubine", _S.Concubine)>>
 				Having a porn star as your personal bed warmer @@.green;reflects on your standing.@@ Your citizens can only wonder at what kinky things happen behind closed doors.
-			<<elseif $Concubine.porn.prestige > 1>>
-				<<run repX(300, "concubine", $Concubine)>>
+			<<elseif _S.Concubine.porn.prestige > 1>>
+				<<run repX(300, "concubine", _S.Concubine)>>
 				Having a rising porn star as your personal bed warmer @@.green;reflects on your standing.@@
 			<</if>>
-			<<if ($Concubine.counter.oral + $Concubine.counter.anal + $Concubine.counter.vaginal + $Concubine.counter.mammary + $Concubine.counter.penetrative > 1000)>>
-				<<if $verboseDescriptions == 1>>Many citizens <<if $Concubine.counter.publicUse > 10>>remember having had $him themselves, and <</if>>@@.green;respectfully@@ envy you $his exclusive company.<</if>>
-				<<run repX(300, "concubine", $Concubine)>>
+			<<if (_S.Concubine.counter.oral + _S.Concubine.counter.anal + _S.Concubine.counter.vaginal + _S.Concubine.counter.mammary + _S.Concubine.counter.penetrative > 1000)>>
+				<<if $verboseDescriptions == 1>>Many citizens <<if _S.Concubine.counter.publicUse > 10>>remember having had $him themselves, and <</if>>@@.green;respectfully@@ envy you $his exclusive company.<</if>>
+				<<run repX(300, "concubine", _S.Concubine)>>
 			<</if>>
 			<<if $masterSuiteUpgradeLuxury > 0>>
 				<<set _slave.rules.living = "luxurious", _slave.devotion += 4, _slave.trust += 4>>
@@ -170,7 +165,7 @@
 			<<if $masterSuiteDecoration != "standard">>
 				<<set _slave.devotion++>>
 			<</if>>
-			<<run repX(Beauty($Concubine) * 5 + ($Concubine.skill.vaginal || 0) + ($Concubine.skill.anal || 0) + ($Concubine.skill.oral || 0) + ($Concubine.skill.whoring || 0) + ($Concubine.skill.entertainment || 0), "concubine", $Concubine)>>
+			<<run repX(Beauty(_S.Concubine) * 5 + (_S.Concubine.skill.vaginal || 0) + (_S.Concubine.skill.anal || 0) + (_S.Concubine.skill.oral || 0) + (_S.Concubine.skill.whoring || 0) + (_S.Concubine.skill.entertainment || 0), "concubine", _S.Concubine)>>
 		<<else>> /* not Concubine */
 			<span class='slave-name'><<= SlaveFullName(_slave)>></span>
 			<<if _slave.choosesOwnAssignment == 2>>
@@ -365,7 +360,7 @@
 		<<if $verboseDescriptions == 1>>
 			<br>&nbsp;&nbsp;&nbsp;&nbsp;$He <<= App.SlaveAssignment.pleaseYou(_slave)>>
 			<br>&nbsp;&nbsp;&nbsp;
-			<<if $servantMilkers == 1 && _slave.lactation > 0 && _slave.fuckdoll == 0 && _slave.fetish != "mindbroken" && canWalk(_slave) && _slave.intelligence+_slave.intelligenceImplant >= -90>>
+			<<if $servantMilkers == 1 && _slave.lactation > 0 && _slave.fuckdoll == 0 && _slave.fetish != "mindbroken" && canMove(_slave) && _slave.intelligence+_slave.intelligenceImplant >= -90>>
 				When $his breasts begin to feel full and you aren't around, $he avails $himself to the penthouse milkers and
 				<<set $servantMilkersMultiplier = 0.25>>
 				<<run App.SlaveAssignment.getMilked(_slave)>>
@@ -385,7 +380,7 @@
 			<<silently>>
 			<<include "SA chooses own job">>
 			<<run App.SlaveAssignment.pleaseYou(_slave)>>
-			<<if $servantMilkers == 1 && _slave.lactation > 0 && _slave.fuckdoll == 0 && _slave.fetish != "mindbroken" && canWalk(_slave) && _slave.intelligence+_slave.intelligenceImplant >= -90>>
+			<<if $servantMilkers == 1 && _slave.lactation > 0 && _slave.fuckdoll == 0 && _slave.fetish != "mindbroken" && canMove(_slave) && _slave.intelligence+_slave.intelligenceImplant >= -90>>
 				<<set $servantMilkersMultiplier = 0.25>>
 				<<run App.SlaveAssignment.getMilked(_slave)>>
 				<<set $servantMilkersMultiplier = 1>>
@@ -421,6 +416,6 @@
 		<br><br>&nbsp;&nbsp;&nbsp;&nbsp;$masterSuiteNameCaps's $masterSuiteDecoration atmosphere @@.hotpink;has a minor impact on your fucktoys.@@
 	<</if>>
 <</if>>
-<<if _DL > 0 || $Concubine != 0>>
+<<if _DL > 0 || _S.Concubine>>
 	<br><br>
 <</if>>
diff --git a/src/facilities/nursery/childInteract.tw b/src/facilities/nursery/childInteract.tw
index e485043b328268ab1281a47770ff9fb2b561f76d..e9cecbcf9af702aa8230b7a3e43fcf69f923fe47 100644
--- a/src/facilities/nursery/childInteract.tw
+++ b/src/facilities/nursery/childInteract.tw
@@ -60,7 +60,7 @@
 FIXME:
 <br><br>__Take slave to another room:__
 [[Wardrobe|Wardrobe Use][$degradation = 0]]
-| [[Auto salon|Salon][$degradation = 0, $primaryHairColor = "", $secondaryHairColor = "", $artificialEyeColor = "" ,$artificialEyeShape = "", $artificialEyeFill = "", $tattooChoice = "", $piercingLevel = ""]]
+| [[Auto salon|Salon][$degradation = 0, $tattooChoice = "", $piercingLevel = ""]]
 | [[Body mod studio|Body Modification][$degradation = 0, $tattooChoice = undefined]]
 | [[Remote surgery|Remote Surgery][$degradation = 0]]
 | [[Configure cybernetics|Prosthetics Configuration][$prostheticsConfig = "main"]]
@@ -305,7 +305,7 @@ FIXME:
 				<<replace "#miniscene">><<include "BeastFucked">><br>&nbsp;&nbsp;&nbsp;&nbsp;<</replace>>
 			<</link>> |
 		<</if>>
-		<<if $farmyardStable > 0 && $activeHooved.species != 0>>
+		<<if $farmyardStables > 0 && $activeHooved.species != 0>>
 			<<link "Let a $activeHooved.species mount $him">>
 				<<set $animalType = "hooved">>
 				<<replace "#miniscene">><<include "BeastFucked">><br>&nbsp;&nbsp;&nbsp;&nbsp;<</replace>>
@@ -1288,14 +1288,14 @@ Hormones: <b><span id="hormones">$activeChild.hormones</span>.</b>
 		<</link>>
 	<</if>>
 <</if>>
-<<if ($activeChild.muscles <= 95) && !isAmputee($activeChild)>>
+<<if ($activeChild.muscles < 100) && !isAmputee($activeChild)>>
 |	<<link "Build muscle">><<set $activeChild.diet = "muscle building">><<replace "#diet">>$activeChild.diet<</replace>><</link>>
-<<elseif $activeChild.muscles > 95 && !isAmputee($activeChild)>>
+<<elseif $activeChild.muscles >= 100 && !isAmputee($activeChild)>>
 	| //$He is maintaining $his enormous musculature//
 <<else>>
 	| //$He has no limbs and thus can't effectively build muscle//
 <</if>>
-<<if $activeChild.muscles > 5 && canWalk($activeChild)>>
+<<if $activeChild.muscles > 0 && canWalk($activeChild)>>
 |	<<link "Slim down">>
 		<<set $activeChild.diet = "slimming">>
 		<<replace "#diet">>$activeChild.diet<</replace>>
@@ -1546,7 +1546,6 @@ Target destination: <b><span id="targetLocation">$activeChild.targetLocation</sp
 <<if $cheatMode>>
 	<br>''Cheatmode:''
 	<<link "Retrieve immediately" "Nursery Retrieval Workaround">>
-		<<set $nurseryOldID = $cribs[$i].ID>>
 		<<set $readySlave = $cribs.pluck([$i], [$i])>>
 	<</link>>
 <</if>>
diff --git a/src/facilities/nursery/customizeChild.tw b/src/facilities/nursery/customizeChild.tw
index e8d5fbab17fc1e5e417497f681ed109c78dacf68..243dec682954c23a60aad9dc166508eff67a68c3 100644
--- a/src/facilities/nursery/customizeChild.tw
+++ b/src/facilities/nursery/customizeChild.tw
@@ -1,7 +1,9 @@
 :: Customize Child [nobr]
 
-<<set $nextButton = "Continue", $nextLink = "Rename Child">>
-<<set $oldName = $activeChild.slaveName, $oldSurname = $activeChild.slaveSurname>>
+<<set $nextButton = "Continue">>
+<<set _oldName = $activeChild.slaveName, _oldSurname = $activeChild.slaveSurname>>
+
+<p id="rename"></p>
 
 You may enter custom descriptors for the child's hair color, hair style, tattoos, or anything else here. After typing, press enter to commit your change. These custom descriptors will appear in descriptions of the child, but will have no gameplay effect. Changing them is free.
 	<br><br>
@@ -62,35 +64,61 @@ You may enter custom descriptors for the child's hair color, hair style, tattoos
 
 <<if ((($activeChild.devotion >= -50) || ($activeChild.trust < -20)) && ($activeChild.birthName != $activeChild.slaveName)) || (($activeChild.devotion > 20) || ($activeChild.trust < -20))>>
 	Change $his given name<<if $activeChild.birthName != $activeChild.slaveName>> ($his birth name was $activeChild.birthName)<</if>>:
-		<<textbox "$activeChild.slaveName" $activeChild.slaveName "Rename Child">>
-	<<link "Restore $his birth name" "Rename Child">>
+		<<includeDom App.UI.DOM.makeTextBox(
+			$activeChild.slaveName,
+			v => {
+				$activeChild.slaveName = v;
+				App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname});
+			},
+			false
+		)>>
+	<<link "Restore $his birth name">>
 		<<set $activeChild.slaveName = $activeChild.birthName>>
+		<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 	<</link>>
 	<<if $arcologies[0].FSPastoralist != "unset">>
 		<<if $activeChild.lactation > 0>>
-			| <<link "Give $him a random cow given name" "Rename Child">>
+			| <<link "Give $him a random cow given name">>
 				<<set $activeChild.slaveName = setup.cowSlaveNames.random()>>
+				<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 			<</link>>
 		<</if>>
 	<</if>>
 	<<if $arcologies[0].FSChattelReligionist != "unset">>
-		| <<link "Give $him a random devotional given name" "Rename Child">>
+		| <<link "Give $him a random devotional given name">>
 			<<set $activeChild.slaveName = setup.chattelReligionistSlaveNames.random()>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<</if>>
 	<br><br>
 	Change $his surname<<if $activeChild.birthSurname != $activeChild.slaveSurname>> ($his birth surname was $activeChild.birthSurname)<</if>>:
 	<<if $activeChild.slaveSurname>>
-		<<textbox "$activeChild.slaveSurname" $activeChild.slaveSurname "Rename Child">>
+		<<includeDom App.UI.DOM.makeTextBox(
+			$activeChild.slaveSurname,
+			v => {
+				$activeChild.slaveSurname = v;
+				App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname});
+			},
+			false
+		)>>
 	<<else>>
-		<<textbox "$activeChild.slaveSurname" "" "Rename Child">>
+		<<includeDom App.UI.DOM.makeTextBox(
+			"",
+			v => {
+				$activeChild.slaveSurname = v;
+				App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname});
+			},
+			false
+		)>>
 	<</if>>
-	<<link "Restore $his birth surname" "Rename Child">>
+	<<link "Restore $his birth surname">>
 		<<set $activeChild.slaveSurname = $activeChild.birthSurname>>
+		<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 	<</link>>
 	<<if $activeChild.slaveSurname>>
-		| <<link "Take $his surname away" "Rename Child">>
+		| <<link "Take $his surname away">>
 			<<set $activeChild.slaveSurname = 0>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<</if>>
 	<<if $activeChild.relationship >= 5>>
@@ -98,8 +126,9 @@ You may enter custom descriptors for the child's hair color, hair style, tattoos
 			<<if $activeChild.relationshipTarget == $slaves[_i].ID>>
 				<<if $slaves[_i].slaveSurname>>
 					<<if $activeChild.slaveSurname != $slaves[_i].slaveSurname>>
-						| <<link "Give $him $his wife's surname" "Rename Child">>
+						| <<link "Give $him $his wife's surname">>
 							<<set $activeChild.slaveSurname = $slaves[_i].slaveSurname>>
+							<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 						<</link>>
 						<<break>>
 					<</if>>
@@ -110,33 +139,43 @@ You may enter custom descriptors for the child's hair color, hair style, tattoos
 	<<if $activeChild.relationship == -3>>
 		<<if $PC.slaveSurname>>
 			<<if $activeChild.slaveSurname != $PC.slaveSurname>>
-				| <<link "Give $him your surname" "Rename Child">>
+				| <<link "Give $him your surname">>
 					<<set $activeChild.slaveSurname = $PC.slaveSurname>>
+					<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 				<</link>>
 			<</if>>
 		<</if>>
 	<</if>>
 	<<if $arcologies[0].FSRomanRevivalist != "unset">>
-		| <<link "Give $him a random full Roman name" "Rename Child">>
-			<<set $activeChild.slaveName = setup.romanSlaveNames.random(), $activeChild.slaveSurname = setup.romanSlaveSurnames.random()>>
+		| <<link "Give $him a random full Roman name">>
+			<<set $activeChild.slaveName = setup.romanSlaveNames.random()>>
+			<<set $activeChild.slaveSurname = setup.romanSlaveSurnames.random()>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<<elseif $arcologies[0].FSAztecRevivalist != "unset">>
-		| <<link "Give $him a random full Aztec name" "Rename Child">>
-			<<set $activeChild.slaveName = setup.aztecSlaveNames.random(), $activeChild.slaveSurname = 0>>
+		| <<link "Give $him a random full Aztec name">>
+			<<set $activeChild.slaveName = setup.aztecSlaveNames.random()>>
+			<<set $activeChild.slaveSurname = 0>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<<elseif $arcologies[0].FSEgyptianRevivalist != "unset">>
-		| <<link "Give $him a random full ancient Egyptian name" "Rename Child">>
-			<<set $activeChild.slaveName = setup.ancientEgyptianSlaveNames.random(), $activeChild.slaveSurname = 0>>
+		| <<link "Give $him a random full ancient Egyptian name">>
+			<<set $activeChild.slaveName = setup.ancientEgyptianSlaveNames.random()>>
+			<<set $activeChild.slaveSurname = 0>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<<elseif $arcologies[0].FSEdoRevivalist != "unset">>
-		| <<link "Give $him a random full feudal Japanese name" "Rename Child">>
-			<<set $activeChild.slaveName = setup.edoSlaveNames.random(), $activeChild.slaveSurname = setup.edoSlaveSurnames.random()>>
+		| <<link "Give $him a random full feudal Japanese name">>
+			<<set $activeChild.slaveName = setup.edoSlaveNames.random()>>
+			<<set $activeChild.slaveSurname = setup.edoSlaveSurnames.random()>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
 		<</link>>
 	<</if>>
 	<<if $arcologies[0].FSDegradationist > -1>>
-		| <<link "Give $him a degrading full name" "Rename Child">>
-	<<run DegradingName($activeChild)>>
-	<</link>>
+		| <<link "Give $him a degrading full name">>
+			<<run DegradingName($activeChild)>>
+			<<run App.UI.SlaveInteract.rename($activeChild, {oldName:_oldName, oldSurname:_oldSurname})>>
+		<</link>>
 	<</if>>
 <<else>>
 	//You must break $his will further before you can successfully force a new name on $him.//
diff --git a/src/facilities/nursery/customizeInfant.tw b/src/facilities/nursery/customizeInfant.tw
index 6969f6b8aedc0cf7b049b779f3c582cbb31dd857..c4687bfe6128d2180f0281bb1080c60d895896df 100644
--- a/src/facilities/nursery/customizeInfant.tw
+++ b/src/facilities/nursery/customizeInfant.tw
@@ -1,7 +1,6 @@
 :: Customize Infant [nobr]
 
-<<set $nextButton = "Continue", $nextLink = "Rename Child">>
-<<set $oldName = $activeChild.slaveName, $oldSurname = $activeChild.slaveSurname>>
+<<set $nextButton = "Continue">>
 
 /* TODO: all of this */
 You may enter custom descriptors for your child's hair color, hair style, tattoos, or anything else here. After typing, press enter to commit your change. These custom descriptors will appear in descriptions of your child, but will have no gameplay effect. Changing them is free.
diff --git a/src/facilities/nursery/matronSelect.tw b/src/facilities/nursery/matronSelect.tw
index 0b52309d0ff93d1b91e2d27dc1f76f066caefa6b..1f00599be61f945e66b825c517dc574407accc5f 100644
--- a/src/facilities/nursery/matronSelect.tw
+++ b/src/facilities/nursery/matronSelect.tw
@@ -1,15 +1,14 @@
 :: Matron Select [nobr jump-to-safe jump-hidden jump-from-safe]
 
 <<set $nextButton = "Back", $nextLink = "Nursery", $encyclopedia = "Matron">>
-<<if ($Matron != 0)>>
-	<<set $Matron = getSlave($Matron.ID)>>
-	<<setLocalPronouns $Matron>>
-	<span class='slave-name'><<= SlaveFullName($Matron)>></span> is serving as your Matron.<br><br>$He will manage the nursery.
+<<if ($MatronID != 0)>>
+	<<setLocalPronouns _S.Matron>>
+	<span class='slave-name'><<= SlaveFullName(_S.Matron)>></span> is serving as your Matron.<br><br>$He will manage the nursery.
 <<else>>
 	You have not selected a Matron.
 <</if>>
 
 <br><br>''Appoint a Matron from your devoted slaves:''
-<br><br>[[None|Nursery][removeJob($Matron, "be the Matron")]]
+<br><br>[[None|Nursery][removeJob(_S.Matron, Job.MATRON)]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.nursery, "Nursery")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.nursery, "Nursery")>>
diff --git a/src/facilities/nursery/nursery.tw b/src/facilities/nursery/nursery.tw
index 86e754f2f8c30e6c56794f2fc1d3c93e2cbf5820..577151882dc763ed73ef36aba149c34d7751a094 100644
--- a/src/facilities/nursery/nursery.tw
+++ b/src/facilities/nursery/nursery.tw
@@ -72,8 +72,8 @@
 			is as comfortable and child-friendly as it needs to be. They have everything they need to grow into a proper slave.
 	<</switch>>
 
-	<<if $Matron>>
-		<<setLocalPronouns $Matron>>
+	<<if $MatronID>>
+		<<setLocalPronouns _S.Matron>>
 	<</if>>
 
 	<<if _NL > 2>>
@@ -81,8 +81,8 @@
 	<<elseif _NL > 0>>
 		$nurseryNameCaps is working steadily. Nannies are moving about, cleaning up and feeding hungry children.
 		<<set $nannyInfluence = 1>>
-	<<elseif $Matron>>
-		$Matron.slaveName is alone in $nurseryName, and keeps the place clean and looks after the children $himself.
+	<<elseif $MatronID>>
+		_S.Matron.slaveName is alone in $nurseryName, and keeps the place clean and looks after the children $himself.
 		<<set $MatronInfluence = 1>>
 	<<elseif (_NL <= 0) && ($nurseryBabies <= 0)>>
 		$nurseryNameCaps is empty and quiet.
@@ -110,7 +110,7 @@
 		<<set _Tmult0 = Math.trunc($nurseryNannies*1000*$upgradeMultiplierArcology)>>
 		<br>It can support $nurseryNannies nann<<if $nurseryNannies != 1>>ies<<else>>y<</if>>.
 		There <<if _NL == 1>>is<<else>>are<</if>> _NL nann<<if _NL != 1>>ies<<else>>y<</if>> in $nurseryName.
-		<<if $nurseryBabies > 0 && _NL < 1 && (!$Matron)>>
+		<<if $nurseryBabies > 0 && _NL < 1 && (!_S.Matron)>>
 			<div class="note">
 				You must have a Matron or at least one nanny to be able to influence the children's upbringing
 			</div>
@@ -135,14 +135,14 @@
 		</div>
 
 		<div>
-			<<if $Matron>>
+			<<if _S.Matron>>
 				<<if $MatronIgnoresFlaws != 1>>
-					$Matron.slaveName will attempt to fix flaws in nannies serving under $him.
+					_S.Matron.slaveName will attempt to fix flaws in nannies serving under $him.
 					<div class="choices">
 						[[Ignore flaws|Nursery][$MatronIgnoresFlaws = 1]]
 					</div>
 				<<else>>
-					$Matron.slaveName has been instructed to ignore flaws in the nannies serving under $him.
+					_S.Matron.slaveName has been instructed to ignore flaws in the nannies serving under $him.
 					<div class="choices">
 						[[Fix flaws|Nursery][$MatronIgnoresFlaws = 0]]
 					</div>
@@ -151,7 +151,7 @@
 		</div>
 
 		<p>
-			<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.nursery)>>
+			<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.nursery)>>
 		</p>
 		/*
 	</div>
diff --git a/src/facilities/nursery/nurseryRetrievalWorkaround.tw b/src/facilities/nursery/nurseryRetrievalWorkaround.tw
index 46b0c7e404c21743e281022ac5a2aae8e2bbfa92..0a15f53b40e3736bd75083d1a11e66a9ae767c36 100644
--- a/src/facilities/nursery/nurseryRetrievalWorkaround.tw
+++ b/src/facilities/nursery/nurseryRetrievalWorkaround.tw
@@ -13,7 +13,7 @@ $readySlave.slaveName has been discharged from $nurseryName and is ready to beco
 /*	TODO:
 <<if $nurseryOrgans.length > 0>>
 	<<for _i = 0; _i < $nurseryOrgans.length; _i++>>
-		<<if $nurseryOrgans[_i].ID == $nurseryOldID>>
+		<<if $nurseryOrgans[_i].ID == $activeSlave.ID>>
 			<<set _newOrgan = {type: $nurseryOrgans[_i].type, weeksToCompletion: $nurseryOrgans[_i].weeksToCompletion, ID: $activeSlave.ID}>>
 			<<if _newOrgan.weeksToCompletion <= 0>>
 				<<set $completedOrgans.push($nurseryOrgans[_i])>>
diff --git a/src/facilities/nursery/renameChild.tw b/src/facilities/nursery/renameChild.tw
deleted file mode 100644
index f3536031727f4f4a8749dab19f4fe407e98a4454..0000000000000000000000000000000000000000
--- a/src/facilities/nursery/renameChild.tw
+++ /dev/null
@@ -1,142 +0,0 @@
-:: Rename Child [nobr]
-
-<<set $nextButton = "Back", $nextLink = "Slave Interact">>
-
-<<if $activeChild.slaveName === $oldName && $activeChild.slaveSurname === $oldSurname>>
-	<<goto "Slave Interact">>
-<<else>>
-/* First time renaming reaction */
-<<if $activeChild.slaveName === "" || ndef $activeChild.slaveName>>
-	$oldName needs to be called something on the records, so $oldName $he stays.
-	<<set $activeChild.slaveName = $oldName>>
-<<elseif $activeChild.fetish == "mindbroken">>
-	$activeChild.slaveName doesn't even recognize that $he's been renamed. It simply does not register.
-<<elseif ($oldName != $activeChild.birthName && $oldSurname != $activeChild.birthSurname) && ($activeChild.slaveName == $activeChild.birthName || $activeChild.slaveSurname == $activeChild.birthSurname)>>
-	$activeChild.slaveName
-	<<if $activeChild.devotion > 50>>
-		cheerfully accepts $his old name back.
-	<<elseif $activeChild.devotion >= -20>>
-		obediently accepts $his old name back.
-	<<elseif $activeChild.devotion >= -50>>
-		happily accepts $his original name.
-	<<elseif $activeChild.trust > 20>>
-		scoffs at the notion that $he was ever anything other than <<= SlaveFullBirthName($activeChild)>>, @@.orangered;weakening your control over $him.@@
-		<<set $activeChild.trust += 5>>
-	<<else>>
-		cautiously accepts the return of $his name.
-	<</if>>
-<<elseif $oldName == $activeChild.birthName || $oldSurname == $activeChild.birthSurname>>
-	$activeChild.slaveName
-	<<if $activeChild.devotion > 50>>
-		accepts $his new name cheerfully. This attachment to you @@.hotpink; increases $his devotion.@@
-		<<set $activeChild.devotion += 4>>
-	<<elseif $activeChild.devotion >= -20>>
-		obediently accepts $his new name. This surrender to you @@.hotpink; increases $his obedience.@@
-		<<set $activeChild.devotion += 4>>
-	<<elseif $activeChild.devotion >= -50>>
-		reluctantly accepts $his new name.
-	<<else>>
-		angrily tries to resist $his new name, insisting that $his name is <<= SlaveFullBirthName($activeChild)>>. This resistance @@.mediumorchid; increases $his rebelliousness.@@
-		<<set $activeChild.devotion -= 5>>
-	<</if>>
-<<else>>
-	Since $activeChild.slaveName has already had a new name <<if $activeChild.devotion > 20>>given to<<else>>forced on<</if>> $him before, the mere fact of having it changed again doesn't really affect $him.
-<</if>>
-
-<<set _insultingName = 0>>
-<<if $oldName != $activeChild.slaveName && typeof $activeChild.slaveName == "string">>
-	<<set _toSearch = $activeChild.slaveName.toLowerCase()>>
-	<<for $i = 0; $i < setup.badWords.length; $i++>>
-		<<if _toSearch.indexOf(setup.badWords[$i]) != -1>>
-			<<set _insultingName = 1>>
-			<<break>>
-		<</if>>
-	<</for>>
-<</if>>
-<<if !_insultingName>>
-	<<if $oldSurname != $activeChild.slaveSurname && typeof $activeChild.slaveSurname == "string">>
-		<<set _toSearch = $activeChild.slaveSurname.toLowerCase()>>
-		<<for $i = 0; $i < setup.badWords.length; $i++>>
-			<<if _toSearch.indexOf(setup.badWords[$i]) != -1>>
-				<<set _insultingName = 1>>
-				<<break>>
-			<</if>>
-		<</for>>
-	<</if>>
-<</if>>
-<<if _insultingName == 1>>
-	<<if $activeChild.fetish == "mindbroken">>
-		$His new name would be insulting to a normal $girl, but $he dully accepts that it is an accurate description and goes about $his duties.
-	<<elseif $activeChild.devotion < -50>>
-		Being given such a degrading name @@.mediumorchid;further increases $his hatred@@ of you.
-		<<set $activeChild.devotion -= 5>>
-	<<elseif $activeChild.devotion <= 50>>
-		Being given such a degrading name @@.gold;terrifies $him,@@ since $he thinks it's fair warning for what $he'll suffer in the future.
-		<<set $activeChild.trust -= 5>>
-	<</if>>
-<</if>>
-
-/* Wife's surname reaction */
-<<if $oldSurname != $activeChild.slaveSurname>>
-	<<if $activeChild.relationship >= 5>>
-		<<set _i = $slaveIndices[$activeChild.relationshipTarget]>>
-		<<if ndef _i>>@@.red;Error, relationshipTarget not found.@@<</if>>
-			<<if $slaves[_i].slaveSurname>>
-			<<if $activeChild.slaveSurname == $slaves[_i].slaveSurname>>
-				$He's touched that $he now shares a surname with $his wife $slaves[_i].slaveName $slaves[_i].slaveSurname, and is @@.mediumaquamarine;more confident than ever@@ that you intend to keep them together in marital bliss.
-				<<set $activeChild.trust += 5>>
-			<<elseif $oldSurname == $slaves[_i].slaveSurname>>
-				$He's concerned that $he no longer shares a surname with $his wife $slaves[_i].slaveName $slaves[_i].slaveSurname, and is @@.gold;very worried@@ that you might be considering splitting them up.
-				<<set $activeChild.trust -= 5>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-/* PC's surname reaction */
-<<if $oldSurname != $activeChild.slaveSurname>>
-	<<if $activeChild.relationship == -3>>
-		<<if $PC.slaveSurname>>
-			<<if $activeChild.fetish == "mindbroken">>
-				Names are meaningless to $him and it is unlikely $he'll remember it.
-			<<elseif $activeChild.devotion+$activeChild.trust >= 175>>
-				<<if $activeChild.slaveSurname == $PC.slaveSurname>>
-					<<if hasAnyArms($activeChild)>>
-						When you tell $him that $he's to be known as $activeChild.slaveName $activeChild.slaveSurname now, $he starts to cry. $He tries to get $himself under control and thank you as best $he can, wiping at the tears running down $his $activeChild.skin cheeks, but $he can't seem to stop weeping as $he thanks you over and over. $He's a sex slave, your property, and it's understandable that some doubts about the permanence of $his place as your slave wife. This has @@.mediumaquamarine;helped reassure $him,@@ and explains the strength of $his emotional reaction. The next time you make love to $him, $he @@.hotpink;presses $himself as close to you as $he can,@@ eager to drink in as much of your presence as $he can get.
-					<<else>>
-						$He's @@.hotpink;deeply touched@@ that $he now shares a surname with you, and has @@.mediumaquamarine;an additional source of confidence@@ that $he'll remain your amputee slave wife.
-					<</if>>
-					<<set $activeChild.devotion += 5, $activeChild.trust += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He's devastated that you'd rename $him something other than your name. $He's @@.gold;terrified@@ that you intend to discard $him as your slave wife, and @@.mediumorchid;saddened@@ that you would take away something that was precious to $him.
-					<<set $activeChild.devotion -= 5, $activeChild.trust -= 5>>
-				<</if>>
-			<<elseif $activeChild.devotion < -20 && $activeChild.trust > 20>>
-				<<if $activeChild.slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as $activeChild.slaveName $activeChild.slaveSurname now, $he shows no reaction. You already took everything from $him, what's a name at this point?
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He doesn't care about losing your surname at first, but it quickly sets in that $he may have @@.gold;pushed $his position too far.@@
-					<<set $activeChild.trust -= 20>>
-				<</if>>
-			<<elseif $activeChild.devotion < -20>>
-				<<if $activeChild.slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as $activeChild.slaveName $activeChild.slaveSurname now, $he pleads with you not to steal $his name. It matters little to you, and $he is forced to @@.hotpink;accept your will.@@
-					<<set $activeChild.devotion += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He's @@.mediumorchid;happy@@ to no longer have to share a name with $his tormentor, but the bliss doesn't last long as @@.gold;dread@@ quickly sets in about what this may mean.
-					<<set $activeChild.devotion += 5, $activeChild.trust -= 10>>
-				<</if>>
-			<<else>>
-				<<if $activeChild.slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as $activeChild.slaveName $activeChild.slaveSurname now, $he nods with approval feeling @@.mediumaquamarine;that $he may hold at least some value in your eyes.@@
-					<<set $activeChild.trust += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He accepts that you'd rename $him something other than your name. $He's @@.gold;a little scared@@ that you intend to discard $him as your slave wife, or worse, but realizes this was not only a possibility, but likely.
-					<<set $activeChild.trust -= 5>>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<</if>> /* close new name check */
diff --git a/src/facilities/nursery/widgets/children/ChildState.js b/src/facilities/nursery/widgets/children/ChildState.js
index 8de31f2d3e8fb185dbe124bb6123328325e7dd3b..4adb08d4e2c425c7988eda3e9315ba5bc86acd24 100644
--- a/src/facilities/nursery/widgets/children/ChildState.js
+++ b/src/facilities/nursery/widgets/children/ChildState.js
@@ -1931,5 +1931,7 @@ App.Facilities.Nursery.ChildState = class ChildState {
 		this.lastWeeksRepIncome = 0;
 		/** Not currently used, will work similarly to the cash variables above */
 		this.lastWeeksRepExpenses = 0;
+		/** Slave's inbreeding coefficient */
+		this.inbreedingCoeff = 0;
 	}
 };
diff --git a/src/facilities/nursery/widgets/children/childSummary.js b/src/facilities/nursery/widgets/children/childSummary.js
index 0ba86b5aa61596fb29fb5444472f39a49df9921b..655e4bdab3e99e89183ba525936f3a950f89af1c 100644
--- a/src/facilities/nursery/widgets/children/childSummary.js
+++ b/src/facilities/nursery/widgets/children/childSummary.js
@@ -53,10 +53,8 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 				r += "&nbsp;&nbsp;&nbsp;&nbsp;";
 			}
 		}
-		V.desc = SlaveTitle(child);
-		const firstLetter = V.desc.substring(0, 1).toUpperCase();
-		V.desc = firstLetter + V.desc.substring(1);
-		r += `<strong><span class="coral">${V.desc}${abbreviate.physicals === 2? '.' : ''}</span></strong> `;
+		const desc = capFirstChar(SlaveTitle(child));
+		r += `<strong><span class="coral">${desc}${abbreviate.physicals === 2? '.' : ''}</span></strong> `;
 		if (V.seeRace) {
 			r += `<span class="tan">`;
 			if (abbreviate.race === 1) {
@@ -3821,7 +3819,7 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 		} else if (child.mother === -1) {
 			r += `Your daughter`;
 			if (child.relationship < -1) {
-				res += ` & ${PCrelationshipTerm(child)}`;
+				r += ` & ${PCrelationshipTerm(child)}`;
 				handled = 1;
 			}
 			r += " ";
@@ -3844,7 +3842,7 @@ App.Facilities.Nursery.ChildSummary = function(child) {
 		} else if (child.father === -1 && child.mother !== -1) {
 			r += `Your daughter`;
 			if (child.relationship < -1) {
-				res += ` & ${PCrelationshipTerm(child)}`;
+				r += ` & ${PCrelationshipTerm(child)}`;
 				handled = 1;
 			}
 			r += " ";
diff --git a/src/facilities/nursery/widgets/children/longChildDescription.js b/src/facilities/nursery/widgets/children/longChildDescription.js
index c4aa78761a622c029132e842c450a625e3437158..450353465b8c4f5e4969c136c4f78de75959b799 100644
--- a/src/facilities/nursery/widgets/children/longChildDescription.js
+++ b/src/facilities/nursery/widgets/children/longChildDescription.js
@@ -11,15 +11,15 @@ App.Facilities.Nursery.LongChildDescription = function(child, {market = 0, event
 		PC = V.PC,
 		slaves = V.slaves,
 		desc = child.actualAge < 13 ? `child` : `teen`, // TODO:
-		girl = V.girl,
 		father = child.father === -1 ? PC : getSlave(child.father),
 		mother = child.mother === -1 ? PC : getSlave(child.mother);
 
 	let
 		r = ``;
 
-	const
-		{ he, him, his, hers, himself, boy, He, His } = getPronouns(child);
+	const {
+		he, him, his, hers, himself, boy, He, His, girl
+	} = getPronouns(child);
 
 	// helper functions
 	function accent(child) {
diff --git a/src/facilities/nursery/widgets/infants/InfantState.js b/src/facilities/nursery/widgets/infants/InfantState.js
index f50c3506b54d851f0bbd744d250c0b8ab9f8644b..bad32d731988231611f9575cd12549d622658b95 100644
--- a/src/facilities/nursery/widgets/infants/InfantState.js
+++ b/src/facilities/nursery/widgets/infants/InfantState.js
@@ -187,5 +187,7 @@ App.Facilities.Nursery.InfantState = class InfantState {
 		this.spermY = 50;
 		/** how many weeks until the child is ready for release */
 		this.growTime = 156;
+		/** Slave's inbreeding coefficient */
+		this.inbreedingCoeff = 0;
 	}
 };
diff --git a/src/facilities/nursery/widgets/utils/nurseryUtils.js b/src/facilities/nursery/widgets/utils/nurseryUtils.js
index b05e16a745919abfa1c026517b99d2afe15fc224..a20b79001d1aba4cd9223a03c2443746ad232564 100644
--- a/src/facilities/nursery/widgets/utils/nurseryUtils.js
+++ b/src/facilities/nursery/widgets/utils/nurseryUtils.js
@@ -35,8 +35,6 @@ App.Facilities.Nursery.childList = function childList() {
 			}
 
 			if (child.actualAge >= 18) {
-				V.nurseryOldID = child.ID;
-
 				if (child.targetLocation === "freedom") {
 					V.freedSlaves.push(child);
 				}
@@ -516,7 +514,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 	}
 	if (mother !== PC && mother !== 0) {
 		App.Utils.setLocalPronouns(mother, 2);
-		if (jsDef(V.Concubine) && mother.ID === V.Concubine.ID) {
+		if (mother.ID === V.ConcubineID) {
 			r += `<br>
 			<<link "Permit your Concubine to name _his2 daughter">>
 				<<replace "#naming">>`;
@@ -534,7 +532,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 			r += `After some careful consideration, ${mother.slaveName} picks a name suitable for your daughter; from now on ${he2} will be known as "${child.slaveName}".
 				<</replace>>
 			<</link>>`;
-		} else if (jsDef(V.Bodyguard) && mother.ID === V.Bodyguard.ID) {
+		} else if (mother.ID === V.BodyguardID) {
 			r += `<br>
 			<<link "Permit your bodyguard to name ${his2} daughter">>
 				<<replace "#naming">>`;
@@ -543,7 +541,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 			r += `After some careful consideration, ${mother.slaveName} decides on "${child.slaveName}" for ${his2} daughter. ${He2} hopes you'll find it fitting ${his} station.
 				<</replace>>
 			<</link>>`;
-		} else if (jsDef(V.Headgirl) && mother.ID === V.Headgirl.ID) {
+		} else if (mother.ID === V.HeadGirlID) {
 			r += `<br>
 			<<link "Permit your Head Girl to name ${his2} daughter">>
 				<<replace "#naming">>`;
@@ -565,7 +563,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 	}
 	if (father !== PC && father !== 0 && father !== mother) {
 		App.Utils.setLocalPronouns(father, 2);
-		if (jsDef(V.Concubine) && father.ID === V.Concubine.ID) {
+		if (father.ID === V.ConcubineID) {
 			r += `<br>
 			<<link "Permit your Concubine to name ${his2} daughter">>
 				<<replace "#naming">>`;
@@ -583,7 +581,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 			r += `After some careful consideration, ${father.slaveName} picks a name suitable for your daughter; from now on ${he} will be known as "${child.slaveName}".
 				<</replace>>
 			<</link>>`;
-		} else if (jsDef(V.Bodyguard) && father.ID === V.Bodyguard.ID) {
+		} else if (father.ID === V.BodyguardID) {
 			r += `<br>
 			<<link "Permit your bodyguard to name ${his2} daughter">>
 				<<replace "#naming">>`;
@@ -592,7 +590,7 @@ App.Facilities.Nursery.nameChild = function nameChild(child) {
 			r += `After some careful consideration, ${father.slaveName} decides on "${child.slaveName}" for ${his2} daughter. ${He2} hopes you'll find it fitting ${his} station.
 				<</replace>>
 			<</link>>`;
-		} else if (jsDef(V.Headgirl) && father.ID === V.Headgirl.ID) {
+		} else if (father.ID === V.HeadGirlID) {
 			r += `<br>
 			<<link "Permit your Head Girl to name ${his2} daughter">>
 				<<replace "#naming">>`;
diff --git a/src/facilities/penthouse/penthouseReport.tw b/src/facilities/penthouse/penthouseReport.tw
index ecbf0225270a7333bf7c1efde3bb9f90058faa40..ee0fb9e21b76ff76723f1a1122c2a87a3da79a94 100644
--- a/src/facilities/penthouse/penthouseReport.tw
+++ b/src/facilities/penthouse/penthouseReport.tw
@@ -1,11 +1,6 @@
 :: Penthouse Report [nobr]
 
 <<set _SL = $slaves.length>>
-<<if _S.HeadGirl>>
-	<<set _HGID = _S.HeadGirl.ID>>
-<<else>>
-	<<set _HGID = 0>>
-<</if>>
 
 <<set _HGSuitSlaves = App.Utils.jobForAssignment(Job.HEADGIRLSUITE).employees()>>
 
@@ -19,7 +14,7 @@
 		<<include "Full Report">>
 		<br><br>
 
-		<<if ($slaves[$i].ID == _HGID) && (_HGSuitSlaves.length > 0)>>
+		<<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]>>
diff --git a/src/facilities/pit/pePitFight.tw b/src/facilities/pit/pePitFight.tw
index 962dd2d72ce2aa089ae56d3e9db4dcff92243785..bbc7850decc8badfdad4b11b4ea8d8227316900c 100644
--- a/src/facilities/pit/pePitFight.tw
+++ b/src/facilities/pit/pePitFight.tw
@@ -2,7 +2,7 @@
 
 <<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Random Nonindividual Event">>
 
-<<set $activeSlave = $Bodyguard>>
+<<set $activeSlave = _S.Bodyguard>>
 <<setLocalPronouns $activeSlave>>
 
 <<set _deadliness = Deadliness($activeSlave)>>
diff --git a/src/facilities/pit/pePitFightInvite.tw b/src/facilities/pit/pePitFightInvite.tw
index 13ae2a8eae689b55145d80e84c84dab99ef77563..97cba9c2f4bfcaac73e0503ee4ac5787afc926bf 100644
--- a/src/facilities/pit/pePitFightInvite.tw
+++ b/src/facilities/pit/pePitFightInvite.tw
@@ -4,7 +4,7 @@
 <<set $nextLink = "RIE Eligibility Check">>
 
 <<set $PPit = 0>>
-<<setLocalPronouns $Bodyguard>>
+<<setLocalPronouns _S.Bodyguard>>
 <span id="artFrame">
 </span>
 
@@ -21,9 +21,9 @@
 	/* 000-250-006 */
 	<<if $seeImages == 1>>
 		<<if $imageChoice == 1>>
-			<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt($Bodyguard, 2, 0)>></div>
+			<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt(_S.Bodyguard, 2, 0)>></div>
 		<<else>>
-			<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt($Bodyguard, 2, 0)>></div>
+			<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt(_S.Bodyguard, 2, 0)>></div>
 		<</if>>
 	<</if>>
 	/* 000-250-006 */
diff --git a/src/facilities/pit/pit.tw b/src/facilities/pit/pit.tw
index 8379add2ba0818f9351fa299b9d0a8a6e45a9e56..8ca6957340c2612260c4da0a1c301f00824c57c3 100644
--- a/src/facilities/pit/pit.tw
+++ b/src/facilities/pit/pit.tw
@@ -4,8 +4,8 @@
 <<if $pitName != "the Pit">>
 	<<set $pitNameCaps = $pitName.replace("the ", "The ")>>
 <</if>>
-<<if $pitBG == 1 && $fighterIDs.includes($Bodyguard.ID)>>
-	<<set $fighterIDs.delete($Bodyguard.ID), _DL-->>
+<<if $pitBG == 1 && $fighterIDs.includes($BodyguardID)>>
+	<<set $fighterIDs.delete($BodyguardID), _DL-->>
 <</if>>
 
 $pitNameCaps is clean and ready,
@@ -37,16 +37,16 @@ $pitNameCaps is clean and ready,
 <</if>>
 
 <<if $killChoice != 2>>
-	<<if $Bodyguard != 0 || (_CL > 0 && $activeCanine != 0) || (_HL > 0 && $activeHooved != 0) || (_FL > 0 && $activeFeline != 0)>>
+	<<if _S.Bodyguard || (_CL > 0 && $activeCanine != 0) || (_HL > 0 && $activeHooved != 0) || (_FL > 0 && $activeFeline != 0)>>
 		<br>
 		<<if $pitBG == 0>>
 			<<if $pitAnimal == 0>>
 				Two fighters will be selected from the pool at random.
-				<<if $Bodyguard != 0>>[[Guarantee your Bodyguard a slot|Pit][$pitBG = 1, $pitAnimal = 0]]<</if>>
-				<<if $Bodyguard != 0 && ($activeCanine != 0 || $activeHooved != 0 || $activeFeline != 0)>> | <</if>>
+				<<if _S.Bodyguard>>[[Guarantee your Bodyguard a slot|Pit][$pitBG = 1, $pitAnimal = 0]]<</if>>
+				<<if _S.Bodyguard && ($activeCanine != 0 || $activeHooved != 0 || $activeFeline != 0)>> | <</if>>
 				<<if $activeCanine != 0 || $activeHooved != 0 || $activeFeline != 0>>[[Have a slave fight an animal|Pit][$pitBG = 0, $pitAnimal = 1]]<</if>>
 			<<else>>
-				A random slave will fight an animal. [[Have them fight another slave|Pit][$pitBG = 0, $pitAnimal = 0]]<<if $Bodyguard != 0>> | [[Have them fight your Bodyguard|Pit][$pitBG = 1, $pitAnimal = 0]]<</if>>
+				A random slave will fight an animal. [[Have them fight another slave|Pit][$pitBG = 0, $pitAnimal = 0]]<<if _S.Bodyguard>> | [[Have them fight your Bodyguard|Pit][$pitBG = 1, $pitAnimal = 0]]<</if>>
 			<</if>>
 		<<else>>
 			Your Bodyguard will fight a slave selected from the pool at random. [[Make both slots random|Pit][$pitBG = 0, $pitAnimal = 0]]
@@ -331,7 +331,7 @@ $pitNameCaps is clean and ready,
 <</if>>
 <br><br>
 
-<<print App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.pit, passage(), false,
+<<includeDOM App.UI.SlaveList.listSJFacilitySlaves(App.Entity.facilities.pit, passage(), false,
 {assign: "Select a slave to fight", remove: "Cancel a slave's fight"})>>
 
 <br><br>Rename $pitName: <<textbox "$pitName" $pitName "Pit">> //Use a noun or similar short phrase//
diff --git a/src/facilities/pit/seLethalPit.tw b/src/facilities/pit/seLethalPit.tw
index 6275d330b9b9ac084a85d082a00cd62e7261fa96..eb2162ba57fe4e8d588cb7df4ecedeb1ca71fb19 100644
--- a/src/facilities/pit/seLethalPit.tw
+++ b/src/facilities/pit/seLethalPit.tw
@@ -5,7 +5,7 @@
 <<if $killChoice == 2>>
 	<<set _fighterOne = $slaveFightingBG.ID>>
 	<<set _fighterOne = getSlave(_fighterOne)>>
-	<<set _fighterTwo = getSlave($Bodyguard.ID)>>
+	<<set _fighterTwo = getSlave($BodyguardID)>>
 	<<setLocalPronouns _fighterOne>>
 	<<setLocalPronouns _fighterTwo 2>>
 <<else>>
@@ -14,8 +14,8 @@
 		<<set _fighterOne = getSlave(_fighterOne)>>
 	<<else>>
 		/* BG should not be in here! */
-		<<run $fighterIDs.deleteWith((s) => s === $Bodyguard.ID)>>
-		<<set _fighterOne = getSlave($Bodyguard.ID)>>
+		<<run $fighterIDs.delete($BodyguardID)>>
+		<<set _fighterOne = _S.Bodyguard>>
 	<</if>>
 	<<setLocalPronouns _fighterOne>>
 	<<if $pitAnimal == 0>>
@@ -811,7 +811,7 @@ In any case, <<if $pitAnimal == 0>>both of the slaves have<<else>>the slave and
 	<<set $pitKillsTotal += 1>>
 
 	<<set $slaves[$slaveIndices[_winner.ID]] = _winner>>
-	<<if $pitBG == 0 || ($Bodyguard.ID != _winner.ID)>>
+	<<if $pitBG == 0 || ($BodyguardID != _winner.ID)>>
 		<<set $fighterIDs.push(_winner.ID)>>
 	<</if>>
 	<<if $pitAnimal == 0>>
@@ -823,4 +823,4 @@ In any case, <<if $pitAnimal == 0>>both of the slaves have<<else>>the slave and
 	<<= removeActiveSlave() >>
 <</if>>
 
-<<set $pitFightsTotal++>>
\ No newline at end of file
+<<set $pitFightsTotal++>>
diff --git a/src/facilities/pit/seNonlethalPit.tw b/src/facilities/pit/seNonlethalPit.tw
index 7176d4f877414fe12f9ac4e6153d76807462c73e..91e4a4189ebe643f389c022e77fcb522e1965f58 100644
--- a/src/facilities/pit/seNonlethalPit.tw
+++ b/src/facilities/pit/seNonlethalPit.tw
@@ -7,8 +7,8 @@
 	<<set _fighterOne = getSlave(_fighterOne)>>
 <<else>>
 	/* BG should not be in here! */
-	<<run $fighterIDs.deleteWith((s) => s === $Bodyguard.ID)>>
-	<<set _fighterOne = getSlave($Bodyguard.ID)>>
+	<<run $fighterIDs.delete($BodyguardID)>>
+	<<set _fighterOne = _S.Bodyguard>>
 <</if>>
 <<setLocalPronouns _fighterOne>>
 <<if $pitAnimal == 0>>
@@ -61,7 +61,7 @@
 <<else>>
 	This week, _fighterOne.slaveName was scheduled to be used by an animal, but is spared because no animal has been chosen.
 	<<set $slaves[$slaveIndices[_fighterOne.ID]] = _fighterOne>>
-	<<if $pitBG == 0 || ($Bodyguard.ID != _fighterOne.ID)>>
+	<<if $pitBG == 0 || ($BodyguardID != _fighterOne.ID)>>
 		<<set $fighterIDs.push(_fighterOne.ID)>>
 	<</if>>
 <</if>>
@@ -952,10 +952,10 @@
 
 	<<set $slaves[$slaveIndices[_winner.ID]] = _winner>>
 	<<set $slaves[$slaveIndices[_loser.ID]] = _loser>>
-	<<if $pitBG == 0 || ($Bodyguard.ID != _winner.ID)>>
+	<<if $pitBG == 0 || ($BodyguardID != _winner.ID)>>
 		<<set $fighterIDs.push(_winner.ID)>>
 	<</if>>
-	<<if $pitBG == 0 || ($Bodyguard.ID != _loser.ID)>>
+	<<if $pitBG == 0 || ($BodyguardID != _loser.ID)>>
 		<<set $fighterIDs.push(_loser.ID)>>
 	<</if>>
 
@@ -1031,9 +1031,9 @@
 		/* TODO: write an ending / post-fight message */
 	<</if>>
 	<<set $slaves[$slaveIndices[_fighterOne.ID]] = _fighterOne>>
-	<<if $pitBG == 0 || ($Bodyguard.ID != _fighterOne.ID)>>
+	<<if $pitBG == 0 || ($BodyguardID != _fighterOne.ID)>>
 		<<set $fighterIDs.push(_fighterOne.ID)>>
 	<</if>>
 <</if>>
 
-<<set $pitFightsTotal++>>
\ No newline at end of file
+<<set $pitFightsTotal++>>
diff --git a/src/facilities/schoolroom/schoolroom.tw b/src/facilities/schoolroom/schoolroom.tw
index 57698e3065dce302a8342418f14361a2199b0866..4e0ea70ca30a3e9f2749ba841bf8ba57cf6a324c 100644
--- a/src/facilities/schoolroom/schoolroom.tw
+++ b/src/facilities/schoolroom/schoolroom.tw
@@ -179,7 +179,7 @@
 </div>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.schoolroom, true)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.schoolroom, true)>>
 </p>
 
 <p>
diff --git a/src/facilities/schoolroom/schoolteacherSelect.tw b/src/facilities/schoolroom/schoolteacherSelect.tw
index e7fa4148322b5e9f61491f256f1a425bf1eb9781..7572a3d974d9a863dcab5b573fc2886f6a5cb300 100644
--- a/src/facilities/schoolroom/schoolteacherSelect.tw
+++ b/src/facilities/schoolroom/schoolteacherSelect.tw
@@ -11,4 +11,4 @@
 <br><br>''Appoint a Schoolteacher from your devoted slaves:''
 <br><br>[[None|Schoolroom][removeJob(_S.Schoolteacher, "be the Schoolteacher")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.schoolroom, "Schoolroom")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.schoolroom, "Schoolroom")>>
diff --git a/src/facilities/servantsQuarters/servantsQuarters.tw b/src/facilities/servantsQuarters/servantsQuarters.tw
index 1c1d71e93e47d0b973d75c721ddc7bccc60df860..ffd1cec37fd131f76556c31cf62aca76fcc13402 100644
--- a/src/facilities/servantsQuarters/servantsQuarters.tw
+++ b/src/facilities/servantsQuarters/servantsQuarters.tw
@@ -126,7 +126,7 @@
 	<<set _facility = App.Entity.facilities.servantsQuarters>>
 	<<if _S.Stewardess>>
 		<<setLocalPronouns _S.Stewardess>>
-		<<print App.UI.SlaveList.displayManager(_facility)>>
+		<<includeDOM App.UI.SlaveList.displayManager(_facility)>>
 		<<if canAchieveErection(_S.Stewardess) && _S.Stewardess.pubertyXY == 1>>
 			<<if $stewardessImpregnates == 1>>
 				Keeping the maids pregnant is part of $his job.
@@ -146,7 +146,7 @@
 </p>
 
 <p>
-	<<print App.UI.SlaveList.listSJFacilitySlaves(_facility)>>
+	<<includeDOM App.UI.SlaveList.listSJFacilitySlaves(_facility)>>
 </p>
 
 <p>
diff --git a/src/facilities/servantsQuarters/stewardessSelect.tw b/src/facilities/servantsQuarters/stewardessSelect.tw
index dfac601f9c962157abd5bff5c29e7e16b88b5f7a..a9dc36cfaa208ee59e6792dc62cb935d5337c05b 100644
--- a/src/facilities/servantsQuarters/stewardessSelect.tw
+++ b/src/facilities/servantsQuarters/stewardessSelect.tw
@@ -11,4 +11,4 @@
 <br><br>''Appoint a Stewardess from your devoted slaves:''
 <br><br>[[None|Servants' Quarters][removeJob(_S.Stewardess, Job.STEWARD)]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.servantsQuarters, "Servants' Quarters")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.servantsQuarters, "Servants' Quarters")>>
diff --git a/src/facilities/spa/attendantSelect.tw b/src/facilities/spa/attendantSelect.tw
index 9145c5cb49f08cf0b87873ef76cf8558d8dc6ae2..f563ca18ad97374086798e02bcee9b96fc7b9285 100644
--- a/src/facilities/spa/attendantSelect.tw
+++ b/src/facilities/spa/attendantSelect.tw
@@ -11,4 +11,4 @@
 <br><br>''Appoint an Attendant from your devoted slaves:''
 <br><br>[[None|Spa][removeJob(_S.Attendant, Job.ATTENDANT)]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.spa, "Spa")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.spa, "Spa")>>
diff --git a/src/facilities/spa/spa.tw b/src/facilities/spa/spa.tw
index 696db7c3bcdffeb274137dff9adb72d4a84a601c..a3ef59996152c693a576c88a34c8ff1fc63ca28d 100644
--- a/src/facilities/spa/spa.tw
+++ b/src/facilities/spa/spa.tw
@@ -148,7 +148,7 @@
 </div>
 
 <p>
-	<<print App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.spa, true)>>
+	<<includeDOM App.UI.SlaveList.stdFacilityPage(App.Entity.facilities.spa, true)>>
 </p>
 
 <p>
diff --git a/src/facilities/statistics.js b/src/facilities/statistics.js
index 4690d66e91ef59ebbc4d6f56cd9696a336838a46..448b0da00ff0648068614b0ab03032249c2f60de 100644
--- a/src/facilities/statistics.js
+++ b/src/facilities/statistics.js
@@ -507,7 +507,7 @@ App.Facilities.Farmyard.Stats = (function() {
 	const H = new App.Facilities.StatsHelper();
 	const assureList = [ "farmhandIncome", "farmhandCosts", "maintenance", "totalIncome", "totalExpenses", "food", "profit" ];
 
-	/** Generate the arcade statistics table
+	/** Generate the Farmyard statistics table
 	 * @param {boolean} showDetails
 	 * @returns {HTMLElement|DocumentFragment}
 	 */
diff --git a/src/gui/Encyclopedia/encyclopedia.tw b/src/gui/Encyclopedia/encyclopedia.tw
index 8140c9442d83218be401d606ebcb797e8e93eb15..c6360d21273c43f080c84bee23c856278bd4feb4 100644
--- a/src/gui/Encyclopedia/encyclopedia.tw
+++ b/src/gui/Encyclopedia/encyclopedia.tw
@@ -2828,6 +2828,7 @@ LORE: INTERVIEWS
 	<br>''lowercasedonkey'' for various additions, not limited to the budget overhauls. Set up all the tabs too. Gave events dynamic vector art. Hammered the scarring and branding systems into place. Been a real boon writing events and other things as well. Used ezsh's facility framework to enhance slave summaries. Set up a system to recall where slaves were serving. Striving to master DOM with great gains.
 	<br>''amomynous0'' for bug reports and testing in addition to SFmod unit descriptions.
 	<br>''wepsrd'' for QOL (hormonal balance cheat and lactation adaptation to new menu) fixes.
+	<br>''i107760'' for Costs Budget, CashX work, The Utopian Orphanage and Farmyard work.
 
 	<br><br>''Many other anonymous contributors'' helped fix bugs via GitHub. They will be credited by name upon request.
 
@@ -3179,6 +3180,13 @@ MODS
 	What would a slaveowner be without the ability to customize their slaves' bodies? The Free Cities offer a variety of ways to achieve this for an arcology owner. Choose a more particular entry below:
 	<br>
 
+<<case "Inbreeding">>
+	At the intersection of incest and pregnancy lies inbreeding. As seen in royal families throughout history, high levels of inbreeding can result in severe issues, often manifesting as facial deformities or reduced intellectual capacity.
+
+	<br><br>One metric for quantifying inbreeding is the coefficient of inbreeding (CoI), which is the probability that both copies of a person's genes come from the same common ancestor. For example, without any previous inbreeding a child from self-fertilization has a CoI of 0.5, a child of two full siblings has a CoI of 0.25, and a child of two first cousins has a CoI of 0.0625.
+
+	<br><br>Enterprising breeders trying to breed specific traits should be mindful of the inbreeding coefficients of their stock: the higher the coefficient, the higher the chance that children will be slow or deformed.
+
 <<default>>
 	Error: bad title.
 <</switch>>
diff --git a/src/gui/Encyclopedia/encyclopediaRelatedLinks.tw b/src/gui/Encyclopedia/encyclopediaRelatedLinks.tw
index 977e489706c77724ea61c26500adbdee70b913f6..7fddb42e1033992d8e5f3ea2d6e8e22c61271a87 100644
--- a/src/gui/Encyclopedia/encyclopediaRelatedLinks.tw
+++ b/src/gui/Encyclopedia/encyclopediaRelatedLinks.tw
@@ -244,7 +244,7 @@ SLAVE RELATIONSHIPS
 /**********
 PREGNANCY
 **********/
-<<case "Artificial Insemination" "Breeders Dietary Blend" "Childbirth and C-Secs" "Cloning" "Fertility Mix" "Gestation Drugs and Labor Suppressants" "Hyper-Pregnancy" "Ova Transplantation" "Player Pregnancy" "Pregnancy" "Pregnancy Generator" "Slave Fertility" "Super Fertility Drugs" "Surrogacy">>
+<<case "Artificial Insemination" "Breeders Dietary Blend" "Childbirth and C-Secs" "Cloning" "Fertility Mix" "Gestation Drugs and Labor Suppressants" "Hyper-Pregnancy" "Ova Transplantation" "Player Pregnancy" "Pregnancy" "Pregnancy Generator" "Slave Fertility" "Super Fertility Drugs" "Surrogacy" "Inbreeding">>
 	<<= App.Encyclopedia.Dialog.linkSC("Pregnancy", "Pregnancy")>>
 	| <<= App.Encyclopedia.Dialog.linkSC("Slave Fertility", "Slave Fertility")>>
 	| <<= App.Encyclopedia.Dialog.linkSC("Player Pregnancy", "Player Pregnancy")>>
@@ -261,6 +261,7 @@ PREGNANCY
 	| <<= App.Encyclopedia.Dialog.linkSC("Ova Transplantation", "Ova Transplantation")>>
 	| <<= App.Encyclopedia.Dialog.linkSC("Eugenics Breeding Proposal", "Eugenics Breeding Proposal")>>
 	| <<= App.Encyclopedia.Dialog.linkSC("Repopulationist Breeding School", "Repopulationist Breeding School")>>
+	| <<= App.Encyclopedia.Dialog.linkSC("Inbreeding", "Inbreeding")>>
 
 /**********
 INFLATION
diff --git a/src/gui/css/accordion.css b/src/gui/css/accordion.css
index 18afe84877c3117cba07ff730f7596977ad735fd..81c02b3b460fbcf8d2c8e7b90639453e0e16bc26 100644
--- a/src/gui/css/accordion.css
+++ b/src/gui/css/accordion.css
@@ -1,3 +1,17 @@
+/* --- base functionality --- */
+
+.accordion-content.hidden {
+	display: none;
+}
+
+/* --- no button case --- */
+
+:not(button).accordion {
+	cursor: pointer;
+}
+
+/* --- button/div case --- */
+
 /* style button */
 button.accordion {
 	width: 100%;
@@ -5,13 +19,13 @@ button.accordion {
 	border: 2px solid var(--button-border-color);
 	padding: 10px;
 	padding-left: 2.2em;
-	background-color: var(--button-color);
-	/* for the absolute positioned elements */
+	background-color: var(--button-selected-color);
+	/* to position the open/close symbol */
 	position: relative;
 }
 
-button.accordion.open {
-	background-color: var(--button-selected-color);
+button.accordion.closed {
+	background-color: var(--button-color);
 }
 
 button.accordion.empty {
@@ -28,11 +42,11 @@ button.accordion::before {
 	font-family: "tme-fa-icons";
 	position: absolute;
 	left: 10px;
-	content: "\e81e";
+	content: "\e81c";
 }
 
-button.accordion.open::before {
-	content: "\e81c";
+button.accordion.closed::before {
+	content: "\e81e";
 }
 
 button.accordion.empty::before {
@@ -44,10 +58,6 @@ div.accordion-content {
 	margin: 0 15px;
 }
 
-div.accordion-content.hidden {
-	display: none;
-}
-
 /* modifiers for the toggle button */
 button.accordion span.title {
 	float: left;
@@ -55,4 +65,4 @@ button.accordion span.title {
 
 button.accordion span.info {
 	float: right;
-}
\ No newline at end of file
+}
diff --git a/src/gui/madamSelect.tw b/src/gui/madamSelect.tw
index e72ea4a101805ba5b7f76eda8709d2ee7a88ad01..35ec6c33eb6b97f7f5f6e8f5dca0c69b1643e038 100644
--- a/src/gui/madamSelect.tw
+++ b/src/gui/madamSelect.tw
@@ -1,15 +1,14 @@
 :: Madam Select [nobr jump-to-safe jump-hidden jump-from-safe]
 
 <<set $nextButton = "Back", $nextLink = "Brothel", $encyclopedia = "Madam">>
-<<if ($Madam != 0)>>
-	<<set $Madam = getSlave($Madam.ID)>>
-	<<setLocalPronouns $Madam>>
-	<span class='slave-name'><<= SlaveFullName($Madam)>></span> is serving as your Madam.<br><br>$He will manage the brothel.
+<<if ($MadamID != 0)>>
+	<<setLocalPronouns _S.Madam>>
+	<span class='slave-name'><<= SlaveFullName(_S.Madam)>></span> is serving as your Madam.<br><br>$He will manage the brothel.
 <<else>>
 	You have not selected a Madam.
 <</if>>
 
 <br><br>''Appoint a Madam from your devoted slaves:''
-<br><br>[[None|Brothel][removeJob($Madam, "be the Madam")]]
+<br><br>[[None|Brothel][removeJob(_S.Madam, "be the Madam")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.brothel, "Brothel")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.brothel, "Brothel")>>
diff --git a/src/gui/options/summaryOptions.tw b/src/gui/options/summaryOptions.tw
index bb269e7d04b462c6a004bc93f6468a106ef0d059..9c079d55801ba4aedaed37a3247503a442dcf52e 100644
--- a/src/gui/options/summaryOptions.tw
+++ b/src/gui/options/summaryOptions.tw
@@ -43,11 +43,7 @@
 
 <h2>Individual panels</h2>
 Sample summary:
-<<= App.UI.SlaveList.render.listMarkup(
-	[$activeSlave.ID],
-	[],
-	App.UI.SlaveList.SlaveInteract.stdInteract
-)>>
+<<includeDOM App.UI.SlaveList.render([$activeSlave.ID], new Array(), App.UI.SlaveList.SlaveInteract.stdInteract)>>
 
 <<set _options = (new App.UI.OptionsGroup()).enableDoubleColumn()>>
 
diff --git a/src/init/storyInit.tw b/src/init/storyInit.tw
index 326153feebfb9de116e42e385fa908fcc02ddb89..c73026fe4263074d52d6341330cf7fa3143fc67a 100644
--- a/src/init/storyInit.tw
+++ b/src/init/storyInit.tw
@@ -40,7 +40,6 @@ You should have received a copy of the GNU General Public License along with thi
 <</for>>
 
 <<run assistant.object()>>
-<<run FCTV.manage()>>
 <<run repX(1000, "event")>>
 <<run setup.prostheticIDs.forEach(function(id) {
 	$prosthetics[id] = {amount: 0, research: 0};
diff --git a/src/interaction/main/mainLinks.js b/src/interaction/main/mainLinks.js
index d2be8893bee20de2008b2bc227d5a8d62731d11f..cf8f0af1bc6c13e19ccc89de58943c9e940bf048 100644
--- a/src/interaction/main/mainLinks.js
+++ b/src/interaction/main/mainLinks.js
@@ -30,8 +30,8 @@ App.UI.View.mainLinks = function() {
 				break;
 			case "HG":
 				fragment.append(`You plan to support your Head Girl this week, `);
-				if (V.HeadGirl) {
-					const {he, his} = getPronouns(V.HeadGirl);
+				if (S.HeadGirl) {
+					const {he, his} = getPronouns(S.HeadGirl);
 					fragment.append(`so ${he} can give more slaves ${his} attention.`);
 				} else {
 					fragment.append(`should you assign one.`);
@@ -90,8 +90,8 @@ App.UI.View.mainLinks = function() {
 
 	if (V.useSlaveSummaryOverviewTab === 0) {
 		let div = document.createElement("div");
-		if (typeof V.slaveIndices[V.HeadGirl.ID] !== 'undefined') {
-			div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.HeadGirl), "slave-name"), " is serving as your Head Girl");
+		if (S.HeadGirl) {
+			div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.HeadGirl), "slave-name"), " is serving as your Head Girl");
 			if (V.arcologies[0].FSEgyptianRevivalistLaw === 1) {
 				div.append(` and Consort`);
 			}
@@ -114,8 +114,8 @@ App.UI.View.mainLinks = function() {
 		fragment.append(div);
 
 		div = document.createElement("div");
-		if (typeof V.slaveIndices[V.Recruiter.ID] !== 'undefined') {
-			div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.Recruiter), "slave-name"), " is working to recruit girls. ",
+		if (V.RecruiterID) {
+			div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.Recruiter), "slave-name"), " is working to recruit girls. ",
 				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Recruiter", "Recruiter Select"), "major-link"));
 		} else {
 			div.append("You have not selected a Recruiter. ",
@@ -127,8 +127,8 @@ App.UI.View.mainLinks = function() {
 
 		if (V.dojo) {
 			div = document.createElement("div");
-			if (typeof V.slaveIndices[V.Bodyguard.ID] !== 'undefined') {
-				div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.Bodyguard), "slave-name"), " is serving as your bodyguard. ",
+			if (V.BodyguardID !== 0) {
+				div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.Bodyguard), "slave-name"), " is serving as your bodyguard. ",
 					App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Bodyguard", "BG Select"), "major-link"));
 			} else {
 				div.append("You have not selected a Bodyguard. ",
@@ -174,13 +174,13 @@ App.UI.View.mainLinks = function() {
 
 	if (V.adjustProstheticsCompleted > 0) {
 		for (let j = 0; j < V.adjustProsthetics.length; j++) {
-			if (getSlave(V.adjustProsthetics[j].slaveID) !== undefined) {
-				const i = V.slaveIndices[V.adjustProsthetics[j].slaveID];
+			const slave = getSlave(V.adjustProsthetics[j].slaveID);
+			if (slave) {
 				if (V.adjustProsthetics[j].workLeft <= 0) {
 					const div = document.createElement("div");
 					div.classList.add("yellow");
 					div.append(`The lab has completed ${addA(setup.prosthetics[V.adjustProsthetics[j].id].name)} for `,
-						App.UI.DOM.makeElement("span", App.UI.DOM.link(SlaveFullName(V.slaves[i]), () => { V.activeSlave = V.slaves[i]; }, [], "Slave Interact"), "clear-formatting"),
+						App.UI.DOM.makeElement("span", App.UI.DOM.link(SlaveFullName(slave), () => { V.activeSlave = slave; }, [], "Slave Interact"), "clear-formatting"),
 						" which is ready to be attached.");
 					fragment.append(div);
 				}
@@ -250,6 +250,9 @@ App.UI.View.mainLinks = function() {
 		if (V.TSS.schoolSale !== 0) {
 			schoolSale("The Slavegirl School");
 		}
+		if (V.TUO.schoolSale !== 0) {
+			schoolSale("The Utopian Orphanage");
+		}
 		if (V.GRI.schoolSale !== 0) {
 			schoolSale("Growth Research Institute");
 		}
diff --git a/src/interaction/main/walkPast.js b/src/interaction/main/walkPast.js
index 60ffe65235274f9f99f47bd72db652c28b06260a..b32bc92467675163c6426e848f72d6ac258b4475 100644
--- a/src/interaction/main/walkPast.js
+++ b/src/interaction/main/walkPast.js
@@ -232,8 +232,8 @@ globalThis.walkPast = (function() {
 					t += `trying their best to maintain their relationship with ${partnerName} being nothing more than a hole in ${V.arcadeName}.`;
 				}
 			} else if (seed >= 66) { /* SEXY TIMES */
-				let fuckSpot;
-				if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+				let fuckSpot = "";
+				if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 					fuckSpot = `in ${partnerName}'s suite`;
 				} else if (activeSlave.rules.living === "luxurious") {
 					fuckSpot = `in the nice little room they share`;
@@ -668,7 +668,7 @@ globalThis.walkPast = (function() {
 							t += `having open and visible sex `;
 							if (activeSlave.rules.living === "luxurious") {
 								t += `in the doorway of the nice little room they share.`;
-							} else if (activeSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+							} else if (activeSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 								t += `in ${name}'s suite.`;
 							} else {
 								t += `out in the hallway near the slave dormitory.`;
@@ -1004,7 +1004,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `${partnerName} understands that what ${he} really wants from ${him2} is emotional intimacy. They're cuddling quietly, offering each other silent comfort and companionship.`;
 				} else if (canPenetrate(activeSlave) && (partnerSlave.vagina > 0) && canDoVaginal(partnerSlave) && hasAllLimbs(partnerSlave) && (activeSlave.belly + partnerSlave.belly < 10000)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1019,7 +1019,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `as ${he2} enjoys the wonderful feeling of ${his2} ${activeSlaveRel}'s cock in ${his2} womanhood.`;
 				} else if (activeSlave.clit > 2 && canDoVaginal(activeSlave) && partnerSlave.vagina > 0 && canDoVaginal(partnerSlave) && hasAllLimbs(partnerSlave) && (activeSlave.belly + partnerSlave.belly < 10000)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1034,7 +1034,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `as ${he2} enjoys the wonderful feeling of ${his2} ${activeSlaveRel}'s huge clit in ${his2} womanhood.`;
 				} else if (activeSlave.dick > 1 && canPenetrate(activeSlave) && canDoAnal(partnerSlave) && partnerSlave.anus > 0 && hasBothArms(activeSlave) && activeSlave.belly < 10000) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1047,7 +1047,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `neck, cupping a breast with one hand, and lightly stimulating ${him2} with the other.`;
 				} else if (activeSlave.clit > 2 && canDoAnal(partnerSlave) && partnerSlave.anus > 0 && hasAnyArms(partnerSlave) && hasAnyLegs(activeSlave)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1056,7 +1056,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `${fuckSpot}, managing to have clitoral-anal sex. ${partnerName} is face-down with ${his2} ass up, spreading ${his2} buttocks as wide as possible, giving ${his2} ${activeSlaveRel} the opportunity to squat over ${him2} and penetrate it with ${his} huge, erect clit. ${name} can't thrust much, but the shocking lewdness of the act is enough for both of them.`;
 				} else if (canPenetrate(activeSlave) && hasBothLegs(partnerSlave) && hasBothArms(activeSlave) && activeSlave.belly < 10000) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1069,7 +1069,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `neck, cupping a breast with one hand, and lightly stimulating ${him2} with the other.`;
 				} else if (activeSlave.clit > 2 && canDoVaginal(activeSlave) && hasBothLegs(partnerSlave)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1078,7 +1078,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `${fuckSpot}, with ${partnerName} down on ${his2} knees in front of ${name}. From behind ${partnerName} it looks like ${he2}'s giving ${his2} ${activeSlaveRel} a conventional, if enthusiastic, blowjob. Only on closer inspection does it become clear how unusual the oral is: ${name} has such a huge clit that ${his} ${partnerSlaveRel} can suck ${him} off just like it were a penis.`;
 				} else if (partnerSlave.vagina > 0 && canDoVaginal(partnerSlave) && hasAllLimbs(partnerSlave) && (activeSlave.belly + partnerSlave.belly < 10000)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1093,7 +1093,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `as ${he2} enjoys the feeling of ${his2} ${activeSlaveRel} fucking ${him2} with a strap-on.`;
 				} else if (partnerSlave.anus > 0 && canDoAnal(partnerSlave) && hasBothArms(activeSlave) && activeSlave.belly < 10000) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1106,7 +1106,7 @@ globalThis.walkPast = (function() {
 					}
 					t += `neck, cupping a breast with one hand, and lightly stimulating ${him} with the other.`;
 				} else if (hasAnyArms(partnerSlave) && hasAnyArms(activeSlave)) {
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1116,7 +1116,7 @@ globalThis.walkPast = (function() {
 					t += `${fuckSpot}, enjoying some mutual masturbation.`;
 				} else if (isAmputee(partnerSlave)) {
 					t += `just cuddling `;
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1126,7 +1126,7 @@ globalThis.walkPast = (function() {
 					t += `${fuckSpot}. ${name} is using ${partnerName}'s limbless torso as a pillow, which ${partnerName} seems to be enjoying, by ${his2} contented expression.`;
 				} else {
 					t += `just cuddling `;
-					if (partnerSlave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+					if (partnerSlave.ID === V.HeadGirlID && V.HGSuite === 1) {
 						t += `in bed `;
 					} else if (activeSlave.rules.living === "luxurious") {
 						t += `in bed `;
@@ -1842,8 +1842,8 @@ globalThis.walkPast = (function() {
 				}
 				break;
 			case Job.HEADGIRLSUITE:
-				if (V.HeadGirl !== 0) {
-					t += `is getting the Head Girl's suite cleaned up while ${V.HeadGirl.slaveName} is out working.`;
+				if (V.HeadGirlID !== 0) {
+					t += `is getting the Head Girl's suite cleaned up while ${S.HeadGirl.slaveName} is out working.`;
 				} else {
 					t += `is making sure the Head Girl's suite is in order for your next Head Girl.`;
 				}
@@ -1873,19 +1873,15 @@ globalThis.walkPast = (function() {
 				t += `is working in ${V.dairyName}, looking after your stock.`;
 				break;
 			default: /* WALKPASTS START HERE */
-				if (slave.heels === 1 && !["flats", "none"].includes(slave.shoes) && canWalk(slave)) {
-					t += `walks past your desk with the tiny swaying steps ${he} must take in order to walk on ${his} surgically altered leg`;
-					if (hasBothLegs(slave)) {
-						t += `s`;
-					}
-					t += `. ${He} is on ${his} way to `;
-				} else if (["heels", "pumps"].includes(slave.shoes) && canWalk(slave)) {
+				if (slave.heels === 1 && canWalk(slave)) {
+					t += `walks past your desk with the tiny swaying steps ${he} must take in order to walk on ${his} surgically altered legs. ${He} is on ${his} way to `;
+				} else if (["heels", "platform heels", "pumps"].includes(slave.shoes) && canWalk(slave)) {
 					t += `walks past your desk with the swaying steps ${he} must take in ${his} high heels. ${He} is on ${his} way to `;
 				} else if (slave.shoes === "boots" && canWalk(slave)) {
 					t += `walks past your desk with the confident gait encouraged by ${his} high heeled boots. ${He} is on ${his} way to `;
-				} else if (slave.shoes === "extreme heels" && canWalk(slave)) {
+				} else if (["extreme heels", "extreme platform heels"].includes(slave.shoes) && canWalk(slave)) {
 					t += `walks past your desk with the tiny swaying steps ${he} must take in ${his} ridiculous heels. ${He} is on ${his} way to `;
-				} else if (slave.heels === 1) {
+				} else if (slave.heels === 1 && canMove(slave)) {
 					t += `crawls past your desk on `;
 					if (hasBothArms(slave) && hasBothLegs(slave)) {
 						t += `all fours, `;
@@ -1896,7 +1892,21 @@ globalThis.walkPast = (function() {
 				} else if (!hasAnyLegs(slave)) {
 					t += `is carried past your desk by one of your other slaves. ${He} is on ${his} way to `;
 				} else if (!canWalk(slave)) {
-					t += `is helped past your desk by one of your other slaves. ${He} is on ${his} way to `;
+					if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							t += `wheels past your desk on ${his} way to`;
+						} else {
+							t += `crawls past your desk on `;
+							if (hasBothArms(slave) && hasBothLegs(slave)) {
+								t += `all fours, `;
+							} else {
+								t += `the ground, `;
+							}
+							t += `since ${he} has expanded past the point of being able to walk. ${He} is on ${his} way to `;
+						}
+					} else {
+						t += `is helped past your desk by one of your other slaves. ${He} is on ${his} way to `;
+					}
 				} else {
 					t += `walks past your desk on ${his} way to `;
 				}
diff --git a/src/interaction/policies/policies.js b/src/interaction/policies/policies.js
index a266ae3e29fca26bd3ea82b645142664a8aba8d5..b2e5ee597a90982a3ddcee19cb83096e3d8a470d 100644
--- a/src/interaction/policies/policies.js
+++ b/src/interaction/policies/policies.js
@@ -1,7 +1,6 @@
 globalThis.policies = (function() {
 	return {
 		countEugenicsSMRs: countEugenicsSMRs,
-		BC: BC,
 		cost: cost,
 	};
 
@@ -19,91 +18,6 @@ globalThis.policies = (function() {
 		return value;
 	}
 
-	function BC() {
-		function convertMain(variable, pro, anti) {
-			if (V[pro]) {
-				V.policies[variable] = 1;
-			} else if (V[anti]) {
-				V.policies[variable] = -1;
-			}
-		}
-
-		function convertRetirement(variable, retireType, amountRequired) {
-			if (V[retireType] && V[amountRequired]) {
-				V.policies.retirement[variable] = V[amountRequired];
-			}
-		}
-
-		if (V.releaseID < 1069) {
-			V.policies.childProtectionAct = V.childProtectionAct;
-			V.policies.culturalOpenness = V.CulturalOpenness;
-			V.policies.sexualOpeness = V.sexualOpeness;
-			V.policies.proRefugees = V.ProRefugees;
-			V.policies.publicFuckdolls = V.publicFuckdolls;
-
-			V.policies.proRecruitment = V.ProRecruitment;
-			V.policies.cash4Babies = V.Cash4Babies;
-			V.policies.regularParties = V.RegularParties;
-			V.policies.publicPA = V.PAPublic;
-			V.policies.coursingAssociation = V.CoursingAssociation;
-
-			V.policies.raidingMercenaries = V.RaidingMercenaries;
-			V.policies.mixedMarriage = V.MixedMarriage;
-			V.policies.goodImageCampaign = V.goodImageCampaign;
-			V.policies.alwaysSubsidizeRep = V.alwaysSubsidizeRep;
-			V.policies.alwaysSubsidizeGrowth = V.alwaysSubsidizeGrowth;
-
-			convertMain('immmigrationCash', 'ProImmigrationCash', 'AntiImmigrationCash');
-			convertMain('immmigrationRep', 'ProImmigrationRep', 'AntiImmigrationRep');
-			convertMain('enslavementCash', 'ProEnslavementCash', 'AntiEnslavementCash');
-			convertMain('enslavementRep', 'ProEnslavementRep', 'AntiEnslavementRep');
-			convertMain('cashForRep', 'CashForRep', 'RepForCash');
-
-			convertMain('oralAppeal', 'OralEncouragement', 'OralDiscouragement');
-			convertMain('vaginalAppeal', 'VaginalEncouragement', 'VaginalDiscouragement');
-			convertMain('analAppeal', 'AnalEncouragement', 'AnalDiscouragement');
-
-			convertRetirement('sex', 'SexMilestoneRetirement', 'retirementSex');
-			convertRetirement('milk', 'MilkMilestoneRetirement', 'retirementMilk');
-			convertRetirement('cum', 'CumMilestoneRetirement', 'retirementCum');
-			convertRetirement('births', 'BirthsMilestoneRetirement', 'retirementBirths');
-			convertRetirement('kills', 'KillsMilestoneRetirement', 'retirementKills');
-
-			if (V.BioreactorRetirement) {
-				V.policies.retirement.fate = "bioreactor";
-			} else if (V.ArcadeRetirement) {
-				V.policies.retirement.fate = "arcade";
-			} else if (V.CitizenRetirement) {
-				V.policies.retirement.fate = "citizen";
-			}
-
-			V.policies.retirement.menial2Citizen = V.citizenRetirementMenials;
-			V.policies.retirement.customAgePolicy = V.policies.retirement.customAgePolicySet || V.CustomRetirementAgePolicy;
-			V.policies.retirement.physicalAgePolicy = V.PhysicalRetirementAgePolicy;
-
-			V.policies.SMR.basicSMR = V.BasicSMR;
-			V.policies.SMR.healthInspectionSMR = V.HealthInspectionSMR;
-			V.policies.SMR.educationSMR = V.EducationSMR;
-			V.policies.SMR.frigiditySMR = V.FrigiditySMR;
-
-			V.policies.SMR.weightSMR = V.BasicWeightSMR;
-			V.policies.SMR.honestySMR = V.HonestySMR;
-
-			V.policies.SMR.beauty.basicSMR = V.BasicBeautySMR;
-			V.policies.SMR.beauty.qualitySMR = V.QualityBeautySMR;
-
-			V.policies.SMR.height.basicSMR = V.BasicHeightSMR;
-			V.policies.SMR.height.advancedSMR = V.AdvancedHeightSMR;
-
-			V.policies.SMR.intelligence.basicSMR = V.BasicIntelligenceSMR;
-			V.policies.SMR.intelligence.qualitySMR = V.QualityIntelligenceSMR;
-
-			V.policies.SMR.eugenics.faceSMR = V.FaceEugenicsSMR;
-			V.policies.SMR.eugenics.heightSMR = V.HeightEugenicsSMR;
-			V.policies.SMR.eugenics.intelligenceSMR = V.IntelligenceEugenicsSMR;
-		}
-	}
-
 	function cost() { return 5000; }
 })();
 /**
diff --git a/src/interaction/policies/policies.tw b/src/interaction/policies/policies.tw
index ff3d3266a215f775eddfc9670018c8785bc1cdcf..6ad5c559d24774528d2e0fb8ecec334823927807 100644
--- a/src/interaction/policies/policies.tw
+++ b/src/interaction/policies/policies.tw
@@ -101,7 +101,7 @@
 			policy("RetirementPolicies");
 		</script>
 		<h1>Menials</h1>
-		
+
 		<span id="MenialRetirementPolicies"></span>
 		<script>
 			policy("MenialRetirementPolicies");
diff --git a/src/interaction/prostheticConfig.tw b/src/interaction/prostheticConfig.tw
index 9a070a53933a6e406785c591216302b9d9ee3be2..8a68dbe21a99b923a830f662d4882e026c8c74ad 100644
--- a/src/interaction/prostheticConfig.tw
+++ b/src/interaction/prostheticConfig.tw
@@ -115,7 +115,7 @@ This room is lined with shelves and cabinets; it could be easily mistaken for a
 	</p>
 
 	<p class="indent">
-	$He has <<print App.Desc.eyesColor(getSlave($AS))>>. To change $his eye color visit the [[auto salon|Salon][$primaryHairColor = "", $secondaryHairColor = "", $primaryEarColor = "", $secondaryEarColor = "", $primaryTailColor = "", $secondaryTailColor = "", $artificialEyeColor = "", $artificialEyeShape = "",$artificialEyeFill = ""]].
+	$He has <<print App.Desc.eyesColor(getSlave($AS))>>. To change $his eye color visit the [[auto salon|Salon]].
 	</p>
 <</if>>
 
diff --git a/src/interaction/rename.js b/src/interaction/rename.js
new file mode 100644
index 0000000000000000000000000000000000000000..6785d52bfc8cafc070cab8f6d31b0ce0fe7005d8
--- /dev/null
+++ b/src/interaction/rename.js
@@ -0,0 +1,152 @@
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @param {object} params
+ * @param {string} [params.oldName] Their name before you renamed them by calling this function
+ * @param {string} [params.oldSurname] Their surname before you renamed them by calling this function
+ */
+App.UI.SlaveInteract.rename = function(slave, {oldName = "", oldSurname = ""} = {}) {
+	const r = [];
+	const {He, he, His, his, him, girl, himself, wife} = getPronouns(slave);
+	if (slave.slaveName === oldName && slave.slaveSurname === oldSurname) {
+		return;
+	} else {
+		/* First time renaming reaction */
+		if (slave.slaveName === "" || !(slave.hasOwnProperty("slaveName"))) {
+			r.push(`${oldName} needs to be called something on the records, so ${oldName} ${he} stays.`);
+			slave.slaveName = oldName;
+		} else if (slave.fetish === "mindbroken") {
+			r.push(`${slave.slaveName} doesn't even recognize that ${he}'s been renamed. It simply does not register.`);
+		} else if ((oldName !== slave.birthName && slave.slaveName === slave.birthName) || (oldSurname !== slave.birthSurname && slave.slaveSurname === slave.birthSurname)) {
+			r.push(`${slave.slaveName}`);
+			if (slave.devotion > 50) {
+				r.push(`cheerfully accepts ${his} old name back.`);
+			} else if (slave.devotion >= -20) {
+				r.push(`obediently accepts ${his} old name back.`);
+			} else if (slave.devotion >= -50) {
+				r.push(`happily accepts ${his} original name.`);
+			} else if (slave.trust > 20) {
+				r.push(`scoffs at the notion that ${he} was ever anything other than ${SlaveFullBirthName(slave)}, <span class="orangered">weakening your control over ${him}.</span>`);
+				slave.trust += 5;
+			} else {
+				r.push(`cautiously accepts the return of ${his} name.`);
+			}
+		} else if ((oldName === slave.birthName && slave.slaveName !== slave.birthName) || (oldSurname === slave.birthSurname && slave.slaveSurname !== slave.birthSurname)) {
+			r.push(`${slave.slaveName}`);
+			if (slave.devotion > 50) {
+				r.push(`accepts ${his} new name cheerfully. This attachment to you <span class="devotion inc"> increases ${his} devotion.</span>`);
+				slave.devotion += 4;
+			} else if (slave.devotion >= -20) {
+				r.push(`obediently accepts ${his} new name. This surrender to you <span class="devotion inc"> increases ${his} obedience.</span>`);
+				slave.devotion += 4;
+			} else if (slave.devotion >= -50) {
+				r.push(`reluctantly accepts ${his} new name.`);
+			} else {
+				r.push(`angrily tries to resist ${his} new name, insisting that ${his} name is ${SlaveFullBirthName(slave)}. This resistance <span class="devotion dec"> increases ${his} rebelliousness.</span>`);
+				slave.devotion -= 5;
+			}
+		} else {
+			r.push(`Since ${slave.slaveName} has already had a new name`);
+			if (slave.devotion > 20) {
+				r.push(`given to`);
+			} else {
+				r.push(`forced on`);
+			}
+			r.push(`${him} before, the mere fact of having it changed again doesn't really affect ${him}.`);
+		}
+
+		let insultingName = false;
+		let toSearch;
+		if (oldName !== slave.slaveName && typeof slave.slaveName === "string") {
+			toSearch = slave.slaveName.toLowerCase();
+			insultingName = setup.badWords.some((s) => toSearch.includes(s));
+		}
+		if (!insultingName) {
+			if (oldSurname !== slave.slaveSurname && typeof slave.slaveSurname === "string") {
+				toSearch = slave.slaveSurname.toLowerCase();
+				insultingName = setup.badWords.some((s) => toSearch.includes(s));
+			}
+		}
+		if (insultingName) {
+			if (slave.fetish === "mindbroken") {
+				r.push(`${His} new name would be insulting to a normal ${girl}, but ${he} dully accepts that it is an accurate description and goes about ${his} duties.`);
+			} else if (slave.devotion < -50) {
+				r.push(`Being given such a degrading name <span class="devotion dec">further increases ${his} hatred</span> of you.`);
+				slave.devotion -= 5;
+			} else if (slave.devotion <= 50) {
+				r.push(`Being given such a degrading name <span class="trust dec">terrifies ${him},</span> since ${he} thinks it's fair warning for what ${he}'ll suffer in the future.`);
+				slave.trust -= 5;
+			}
+		}
+
+		/* Wife's surname reaction */
+		if (oldSurname !== slave.slaveSurname) {
+			if (slave.relationship >= 5) {
+				const slaveWife = getSlave(slave.relationshipTarget);
+				if (!slaveWife) {
+					r.push(`<span class="red">Error, relationshipTarget not found.</span>`);
+				} else {
+					if (slaveWife.slaveSurname) {
+						const {wife2} = getPronouns(slaveWife).appendSuffix('2');
+						if (slave.slaveSurname === slaveWife.slaveSurname) {
+							r.push(`${He}'s touched that ${he} now shares a surname with ${his} ${wife2} ${slaveWife.slaveName} ${slaveWife.slaveSurname}, and is <span class="trust inc">more confident than ever</span> that you intend to keep them together in marital bliss.`);
+							slave.trust += 5;
+						} else if (oldSurname === slaveWife.slaveSurname) {
+							r.push(`${He}'s concerned that ${he} no longer shares a surname with ${his} ${wife2} ${slaveWife.slaveName} ${slaveWife.slaveSurname}, and is <span class="trust dec">very worried</span> that you might be considering splitting them up.`);
+							slave.trust -= 5;
+						}
+					}
+				}
+			}
+		}
+
+		/* PC's surname reaction */
+		if (oldSurname !== slave.slaveSurname) {
+			if (slave.relationship === -3) {
+				if (V.PC.slaveSurname) {
+					if (slave.fetish === "mindbroken") {
+						r.push(`Names are meaningless to ${him} and it is unlikely ${he}'ll remember it.`);
+					} else if (slave.devotion + slave.trust >= 175) {
+						if (slave.slaveSurname === V.PC.slaveSurname) {
+							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} starts to cry. ${He} tries to get ${himself} under control and thank you as best ${he} can,`);
+							if (hasAnyArms(slave)) {
+								r.push(`wiping at the tears running down ${his} ${slave.skin} cheeks,`);
+							}
+							r.push(`but ${he} can't seem to stop weeping as ${he} thanks you over and over. ${He}'s a sex slave, your property, and it's understandable that some doubts about the permanence of ${his} place as your slave ${wife}. This has <span class="trust inc">helped reassure ${him},</span> and explains the strength of ${his} emotional reaction. The next time you make love to ${him}, ${he} <span class="devotion inc">presses ${himself} as close to you as ${he} can,</span> eager to drink in as much of your presence as ${he} can get.`);
+							slave.devotion += 5;
+							slave.trust += 5;
+						} else if (oldSurname === V.PC.slaveSurname) {
+							r.push(`${He}'s devastated that you'd rename ${him} something other than your name. ${He}'s <span class="trust dec">terrified</span> that you intend to discard ${him} as your slave ${wife}, and <span class="devotion dec">saddened</span> that you would take away something that was precious to ${him}.`);
+							slave.devotion -= 5;
+							slave.trust -= 5;
+						}
+					} else if (slave.devotion < -20 && slave.trust > 20) {
+						if (slave.slaveSurname === V.PC.slaveSurname) {
+							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} shows no reaction. You already took everything from ${him}, what's a name at this point?`);
+						} else if (oldSurname === V.PC.slaveSurname) {
+							r.push(`${He} doesn't care about losing your surname at first, but it quickly sets in that ${he} may have <span class="trust dec">pushed ${his} position too far.</span>`);
+							slave.trust -= 20;
+						}
+					} else if (slave.devotion < -20) {
+						if (slave.slaveSurname === V.PC.slaveSurname) {
+							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} pleads with you not to steal ${his} name. It matters little to you, and ${he} is forced to <span class="devotion inc">accept your will.</span>`);
+							slave.devotion += 5;
+						} else if (oldSurname === V.PC.slaveSurname) {
+							r.push(`${He}'s <span class="devotion dec">happy</span> to no longer have to share a name with ${his} tormentor, but the bliss doesn't last long as <span class="trust dec">dread</span> quickly sets in about what this may mean.`);
+							slave.devotion += 5;
+							slave.trust -= 10;
+						}
+					} else {
+						if (slave.slaveSurname === V.PC.slaveSurname) {
+							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} nods with approval feeling <span class="trust inc">that ${he} may hold at least some value in your eyes.</span>`);
+							slave.trust += 5;
+						} else if (oldSurname === V.PC.slaveSurname) {
+							r.push(`${He} accepts that you'd rename ${him} something other than your name. ${He}'s <span class="trust dec">a little scared</span> that you intend to discard ${him} as your slave ${wife}, or worse, but realizes this was not only a possibility, but likely.`);
+							slave.trust -= 5;
+						}
+					}
+				}
+			}
+		}
+	}
+	return jQuery("#rename").empty().append(r.join(" "));
+};
diff --git a/src/interaction/slaveInteract.js b/src/interaction/slaveInteract.js
index aa4bd94453b88fda2c8d558fa610f5f0b9f14c5e..36654d15032c2bef163c04c03ec4ba46980bb97c 100644
--- a/src/interaction/slaveInteract.js
+++ b/src/interaction/slaveInteract.js
@@ -61,15 +61,6 @@ App.UI.SlaveInteract.modify = function(slave) {
 	makeRoomLink(el, "Auto salon", "Salon", ' Modify hair (color, length, style), nails, and even skin color.',
 		() => {
 			V.activeSlave = slave;
-			V.primaryHairColor = "";
-			V.secondaryHairColor = "";
-			V.primaryEarColor = "";
-			V.secondaryEarColor = "";
-			V.primaryTailColor = "";
-			V.secondaryTailColor = "";
-			V.artificialEyeColor = "";
-			V.artificialEyeShape = "";
-			V.artificialEyeFill = "";
 		}
 	);
 
@@ -782,7 +773,7 @@ App.UI.SlaveInteract.diet = function(slave) {
 	}
 
 	const muscle = [];
-	if (slave.muscles <= 95 && !isAmputee(slave)) {
+	if (slave.muscles < 100 && !isAmputee(slave)) {
 		muscle.push({text: `Build muscle`, updateSlave: {diet: "muscle building"}});
 	} else if (!isAmputee(slave)) {
 		muscle.push({text: `Build muscle`, disabled: `${He} is maintaining ${his} enormous musculature`});
@@ -790,10 +781,10 @@ App.UI.SlaveInteract.diet = function(slave) {
 		muscle.push({text: `Build muscle`, disabled: `${He} has no limbs and thus can't effectively build muscle`});
 	}
 
-	if ((slave.muscles > 5 || slave.fuckdoll === 0) && canWalk(slave)) {
+	if ((slave.muscles > 0 || slave.fuckdoll === 0) && canWalk(slave)) {
 		muscle.push({text: `Slim down`, updateSlave: {diet: "slimming"}});
 	} else if (!canWalk(slave)) {
-		muscle.push({text: `Slim down`, disabled: `${He} can't move and thus can't trim down`});
+		muscle.push({text: `Slim down`, disabled: `${He} can't walk and thus can't trim down`});
 	} else if (slave.fuckdoll > 0) {
 		muscle.push({text: `Slim down`, disabled: `${He} has no muscles left to lose`});
 	}
@@ -1112,14 +1103,14 @@ App.UI.SlaveInteract.useSlaveDisplay = function(slave) {
 		} else if (slave.clit >= 4) {
 			sexOptions.push({text: `Have another slave ride ${his} clit-dick`, scene: `FSlaveSlaveDick`});
 		}
-		if (V.seeBestiality && V.cheatMode === 1) {
-			if (V.farmyardKennels > 0 && V.activeCanine !== 0) {
+		if (V.seeBestiality) {
+			if (V.farmyardKennels > 0 && V.activeCanine) {
 				sexOptions.push({text: `Have a ${V.activeCanine.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "canine"}});
 			}
-			if (V.farmyardStable > 0 && V.activeHooved !== 0) {
+			if (V.farmyardStables > 0 && V.activeHooved) {
 				sexOptions.push({text: `Let a ${V.activeHooved.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "hooved"}});
 			}
-			if (V.farmyardCages > 0 && V.activeFeline !== 0) {
+			if (V.farmyardCages > 0 && V.activeFeline) {
 				sexOptions.push({text: `Have a ${V.activeFeline.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "feline"}});
 			}
 		}
@@ -2206,6 +2197,21 @@ App.UI.SlaveInteract.rules = function(slave) {
 		}
 	}
 
+	// Mobility Aids
+	if (slave.fuckdoll > 0) {
+		// Sex toys don't move around on their own//
+	} else if (canMove(slave)) {
+		div = document.createElement("div");
+		div.append("Use of mobility aids: ");
+		App.UI.DOM.appendNewElement("span", div, slave.rules.mobility, "bold");
+		choices = [
+			{value: "restrictive"},
+			{value: "permissive"},
+		];
+		div.append(listChoices(choices, "mobility"));
+		p.append(div);
+	}
+
 	// Punishment
 	div = document.createElement("div");
 	div.append("Typical punishment: ");
@@ -2480,14 +2486,19 @@ App.UI.SlaveInteract.custom = (function() {
 	}
 
 	function intro(slave) {
-		V.oldName = slave.slaveName;
-		V.oldSurname = slave.slaveSurname;
-		let intro = document.createElement('p');
-		intro.className = "scene-intro";
-		intro.append(`You may enter custom descriptors for your slave's hair color, hair style, tattoos, or anything else here. After typing, press `);
-		intro.appendChild(App.UI.DOM.makeElement("kbd", "enter"));
-		intro.append(` to commit your change. These custom descriptors will appear in descriptions of your slave, but will have no gameplay effect. Changing them is free.`);
-		return intro;
+		const frag = new DocumentFragment();
+		let p;
+		p = document.createElement('p');
+		p.id = "rename";
+		frag.append(p);
+
+		p = document.createElement('p');
+		p.className = "scene-p";
+		p.append(`You may enter custom descriptors for your slave's hair color, hair style, tattoos, or anything else here. After typing, press `);
+		p.appendChild(App.UI.DOM.makeElement("kbd", "enter"));
+		p.append(` to commit your change. These custom descriptors will appear in descriptions of your slave, but will have no gameplay effect. Changing them is free.`);
+		frag.append(p);
+		return frag;
 	}
 
 	function playerTitle(slave) {
@@ -2606,6 +2617,8 @@ App.UI.SlaveInteract.custom = (function() {
 		return slaveFullNameNode;
 
 		function slaveName() {
+			const oldName = slave.slaveName;
+			const oldSurname = slave.slaveSurname;
 			// Slave Name
 			let slaveNameNode = document.createElement('p');
 			label = document.createElement('div');
@@ -2623,9 +2636,10 @@ App.UI.SlaveInteract.custom = (function() {
 				slave.slaveName,
 				v => {
 					slave.slaveName = v;
+					updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 				},
 				false,
-				"Rename");
+			);
 			label.appendChild(textbox);
 
 			slaveNameNode.appendChild(label);
@@ -2634,9 +2648,9 @@ App.UI.SlaveInteract.custom = (function() {
 				` Restore ${his} birth name`,
 				() => {
 					slave.slaveName = slave.birthName;
+					updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 				},
 				false,
-				"Rename"
 			));
 
 			if (V.arcologies[0].FSPastoralist !== "unset") {
@@ -2646,9 +2660,9 @@ App.UI.SlaveInteract.custom = (function() {
 						`Give ${him} a random cow given name`,
 						() => {
 							slave.slaveName = setup.cowSlaveNames.random();
+							updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 						},
 						false,
-						"Rename"
 					));
 				}
 			}
@@ -2659,9 +2673,9 @@ App.UI.SlaveInteract.custom = (function() {
 						`Give ${him} a random stipper given name`,
 						() => {
 							slave.slaveName = setup.bimboSlaveNames.random();
+							updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 						},
 						false,
-						"Rename"
 					));
 				}
 			}
@@ -2671,9 +2685,9 @@ App.UI.SlaveInteract.custom = (function() {
 					`Give ${him} a random devotional given name`,
 					() => {
 						slave.slaveName = setup.chattelReligionistSlaveNames.random();
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			}
 			slaveNameNode.appendChild(result);
@@ -2682,6 +2696,8 @@ App.UI.SlaveInteract.custom = (function() {
 
 		function slaveSurname() {
 			// Slave Surname
+			const oldName = slave.slaveName;
+			const oldSurname = slave.slaveSurname;
 			let slaveSurnameNode = document.createElement('p');
 			label = document.createElement('div');
 			result = document.createElement('div');
@@ -2698,9 +2714,9 @@ App.UI.SlaveInteract.custom = (function() {
 				slave.slaveSurname,
 				v => {
 					slave.slaveSurname = textbox.value;
+					updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 				},
 				false,
-				"Rename"
 			);
 			label.appendChild(textbox);
 
@@ -2710,9 +2726,9 @@ App.UI.SlaveInteract.custom = (function() {
 				` Restore ${his} birth surname`,
 				() => {
 					slave.slaveSurname = slave.birthSurname;
+					updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 				},
 				false,
-				"Rename"
 			));
 
 			if (slave.slaveSurname) {
@@ -2721,9 +2737,9 @@ App.UI.SlaveInteract.custom = (function() {
 					`Take ${his} surname away`,
 					() => {
 						slave.slaveSurname = 0;
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			}
 			if (slave.relationship >= 5) {
@@ -2737,9 +2753,9 @@ App.UI.SlaveInteract.custom = (function() {
 									`Give ${him} ${his} ${wifePronouns.wife}'s surname`,
 									() => {
 										slave.slaveSurname = V.slaves[_i].slaveSurname;
+										updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 									},
 									false,
-									"Rename"
 								));
 								break;
 							}
@@ -2755,9 +2771,9 @@ App.UI.SlaveInteract.custom = (function() {
 							`Give ${him} your surname`,
 							() => {
 								slave.slaveSurname = V.PC.slaveSurname;
+								updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 							},
 							false,
-							"Rename"
 						));
 					}
 				}
@@ -2769,9 +2785,9 @@ App.UI.SlaveInteract.custom = (function() {
 					() => {
 						slave.slaveName = setup.romanSlaveNames.random();
 						slave.slaveSurname = setup.romanSlaveSurnames.random();
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			} else if (V.arcologies[0].FSAztecRevivalist !== "unset") {
 				result.append(` | `);
@@ -2780,9 +2796,9 @@ App.UI.SlaveInteract.custom = (function() {
 					() => {
 						slave.slaveName = setup.aztecSlaveNames.random();
 						slave.slaveSurname = 0;
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			} else if (V.arcologies[0].FSEgyptianRevivalist !== "unset") {
 				result.append(` | `);
@@ -2791,9 +2807,9 @@ App.UI.SlaveInteract.custom = (function() {
 					() => {
 						slave.slaveName = setup.ancientEgyptianSlaveNames.random();
 						slave.slaveSurname = 0;
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			} else if (V.arcologies[0].FSEdoRevivalist !== "unset") {
 				result.append(` | `);
@@ -2802,9 +2818,9 @@ App.UI.SlaveInteract.custom = (function() {
 					() => {
 						slave.slaveName = setup.edoSlaveNames.random();
 						slave.slaveSurname = setup.edoSlaveSurnames.random();
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			}
 			if (V.arcologies[0].FSDegradationist > -1) {
@@ -2813,14 +2829,18 @@ App.UI.SlaveInteract.custom = (function() {
 					`Give ${him} a degrading full name`,
 					() => {
 						DegradingName(slave);
+						updateName(slave, {oldName:oldName, oldSurname:oldSurname});
 					},
 					false,
-					"Rename"
 				));
 			}
 			slaveSurnameNode.appendChild(result);
 			return slaveSurnameNode;
 		}
+		function updateName(slave, {oldName:oldName, oldSurname:oldSurname}) {
+			App.UI.SlaveInteract.custom(slave);
+			App.UI.SlaveInteract.rename(slave, {oldName:oldName, oldSurname:oldSurname});
+		}
 	}
 
 	function hair(slave) {
diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js
index 1e355321f4068dd0cdbdaee54b22067fc37ff3f3..8f78e503baacfd19a78f7d5e492c1525113e1faf 100644
--- a/src/js/DefaultRules.js
+++ b/src/js/DefaultRules.js
@@ -82,6 +82,9 @@ globalThis.DefaultRules = (function() {
 			ProcessRelationship(slave, rule);
 			ProcessRelease(slave, rule);
 			ProcessLactation(slave, rule);
+			if (!canWalk(slave) && canMove(slave)) {
+				ProcessMobility(slave, rule);
+			}
 			ProcessPunishment(slave, rule);
 			ProcessReward(slave, rule);
 		}
@@ -1955,6 +1958,19 @@ globalThis.DefaultRules = (function() {
 		}
 	}
 
+	/**
+	 * @param {App.Entity.SlaveState} slave
+	 * @param {App.RA.RuleSetters} rule
+	 */
+	function ProcessMobility(slave, rule) {
+		if ((rule.mobilityRules !== undefined) && (rule.mobilityRules !== null)) {
+			if (slave.rules.mobility !== rule.mobilityRules ) {
+				slave.rules.mobility = rule.mobilityRules;
+				r += `<br>${slave.slaveName}'s usage of mobility aids has been set to ${rule.mobilityRules}.`;
+			}
+		}
+	}
+
 	/**
 	 * @param {App.Entity.SlaveState} slave
 	 * @param {App.RA.RuleSetters} rule
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index 92f35f702d2f61f60101b8c11ff2fd131ffaa698..7a8469dc6fe4b89ee1e84105d53958106b3995ab 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -2507,6 +2507,8 @@ App.Entity.SlaveState = class SlaveState {
 		this.whoreClass = 0;
 		/** Maximum class for whore to target */
 		this.effectiveWhoreClass = 0;
+		/** Slave's inbreeding coefficient */
+		this.inbreedingCoeff = 0;
 	}
 
 	/** Creates an object suitable for setting nested attributes as it would be a SlaveState
diff --git a/src/js/assayJS.js b/src/js/assayJS.js
index 5be576962ca5afce0b39732b498a65cd20d8935b..3fdb77c704a0a599b54747caaeca5d0150dac7d4 100644
--- a/src/js/assayJS.js
+++ b/src/js/assayJS.js
@@ -77,10 +77,9 @@ globalThis.inferiorRaceP = function(slave) {
  * @returns {boolean}
  */
 globalThis.isLeaderP = function(slave) {
-	/** @type {FC.SlaveStateOrZero[]} */
-	const leaders = [V.HeadGirl, V.Bodyguard, V.Recruiter, V.Concubine, S.Nurse, S.Attendant, V.Matron, V.Madam, S.DJ, V.Milkmaid, V.Farmer, S.Stewardess, S.Schoolteacher, S.Wardeness];
+	const leaders = [S.HeadGirl, S.Bodyguard, S.Recruiter, S.Concubine, S.Nurse, S.Attendant, S.Matron, S.Madam, S.DJ, S.Milkmaid, S.Farmer, S.Stewardess, S.Schoolteacher, S.Wardeness];
 
-	return leaders.some(leader => leader && leader.ID && leader.ID === slave.ID);
+	return leaders.some(leader => leader && leader.ID === slave.ID);
 };
 
 /**
@@ -462,17 +461,12 @@ globalThis.getWrittenTitle = function(slave) {
 	}
 };
 
-/** Get the written title for a given slave, or for the most recently enunciated slave, write it into global state, and return it.
+/** Get the written title for a given slave, or for the active slave if none is provided.
  * @param {App.Entity.SlaveState} [slave]
  * @returns {string}
  */
 globalThis.WrittenMaster = function(slave) {
-	if (slave !== undefined) {
-		V.writtenTitle = getWrittenTitle(slave);
-	} else if (V.writtenTitle === undefined) {
-		V.writtenTitle = getWrittenTitle(V.activeSlave);
-	}
-	return V.writtenTitle;
+	return getWrittenTitle(slave || V.activeSlave);
 };
 
 /** Get all the enunciations used by a particular slave as a destructurable object.
@@ -659,7 +653,6 @@ globalThis.getEnunciation = function(slave) {
  */
 globalThis.Enunciate = function(slave) {
 	V.enunciate = getEnunciation(slave);
-	V.writtenTitle = getWrittenTitle(slave);
 };
 
 /**
@@ -1842,7 +1835,7 @@ globalThis.Deadliness = function(slave) {
 	deadliness -= getLimbCount(slave, 4) * 0.25;
 	deadliness += getLimbCount(slave, 5) * 1.25;
 	deadliness += getLimbCount(slave, 6) * 2.5;
-	if (hasBothLegs(slave) && !canWalk(slave)) {
+	if (hasBothLegs(slave) && !canStand(slave)) {
 		deadliness -= 20;
 	}
 
diff --git a/src/js/assignJS.js b/src/js/assignJS.js
index d6c1560cf9e1eb742cced3ce31f2956cdf01d00b..16eeef675674422bd8919f312995716c9230dd62 100644
--- a/src/js/assignJS.js
+++ b/src/js/assignJS.js
@@ -29,7 +29,7 @@ globalThis.assignJob = function(slave, job) {
 	const idx = V.slaveIndices[slave.ID];
 
 	/**
-	 * this helper makes sure global references ($HeadGirl, etc) or global IDs ($AttendantID, etc) are set correctly
+	 * this helper makes sure global references global IDs ($HeadGirlID, $AttendantID, etc) are set correctly
 	 * @param {string} propName
 	 */
 	function uniqueJob(propName) {
@@ -88,7 +88,7 @@ globalThis.assignJob = function(slave, job) {
 			}
 			slave.shoes = "none";
 			slave.collar = "none";
-			slave.rules.living = "spare";
+			slave.rules.living = LivingRule.SPARE;
 			break;
 
 		case Job.BROTHEL.toLowerCase():
@@ -97,10 +97,10 @@ globalThis.assignJob = function(slave, job) {
 			switch (V.brothelDecoration) {
 				case "Degradationist":
 				case "standard":
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 				default:
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 			}
 			break;
@@ -110,10 +110,10 @@ globalThis.assignJob = function(slave, job) {
 			slave.assignment = Job.CELLBLOCK;
 			switch (V.cellblockDecoration) {
 				case "Paternalist":
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 				default:
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 			}
 			break;
@@ -134,7 +134,7 @@ globalThis.assignJob = function(slave, job) {
 				case "Intellectual Dependency":
 				case "Petite Admiration":
 				case "Statuesque Glorification":
-					slave.rules.living = "luxurious";
+					slave.rules.living = LivingRule.LUXURIOUS;
 					break;
 
 				case "Roman Revivalist":
@@ -144,11 +144,11 @@ globalThis.assignJob = function(slave, job) {
 				case "Chinese Revivalist":
 				case "Chattel Religionist":
 				case "Edo Revivalist":
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 
 				default:
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 			}
 			break;
@@ -156,7 +156,7 @@ globalThis.assignJob = function(slave, job) {
 		case Job.CLUB.toLowerCase():
 		case "club":
 			slave.assignment = Job.CLUB;
-			slave.rules.living = "normal";
+			slave.rules.living = LivingRule.NORMAL;
 			break;
 
 		case Job.DAIRY.toLowerCase():
@@ -173,10 +173,10 @@ globalThis.assignJob = function(slave, job) {
 				case "Supremacist":
 				case "Subjugationist":
 				case "Degradationist":
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 				default:
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 			}
 			break;
@@ -194,13 +194,13 @@ globalThis.assignJob = function(slave, job) {
 				case "Supremacist":
 				case "Subjugationist":
 				case "Degradationist":
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 				case "Roman Revivalist":
-					slave.rules.living = "luxurious";
+					slave.rules.living = LivingRule.LUXURIOUS;
 					break;
 				default:
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 			}
 			break;
@@ -209,7 +209,7 @@ globalThis.assignJob = function(slave, job) {
 		case "head girl suite":
 		case "hgsuite":
 			slave.assignment = Job.HEADGIRLSUITE;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.MASTERSUITE.toLowerCase():
@@ -217,16 +217,16 @@ globalThis.assignJob = function(slave, job) {
 		case "mastersuite":
 			slave.assignment = Job.MASTERSUITE;
 			if (V.masterSuiteUpgradeLuxury > 0) {
-				slave.rules.living = "luxurious";
+				slave.rules.living = LivingRule.LUXURIOUS;
 			} else {
-				slave.rules.living = "normal";
+				slave.rules.living = LivingRule.NORMAL;
 			}
 			break;
 
 		case Job.SCHOOL.toLowerCase():
 		case "schoolroom":
 			slave.assignment = Job.SCHOOL;
-			slave.rules.living = "normal";
+			slave.rules.living = LivingRule.NORMAL;
 			break;
 
 		case Job.QUARTER.toLowerCase():
@@ -244,25 +244,25 @@ globalThis.assignJob = function(slave, job) {
 				case "Degradationist":
 				case "Arabian Revivalist":
 				case "Egyptian Revivalist":
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 				case "Slave Professionalism":
 					if (slave.intelligence + slave.intelligenceImplant > 15) {
-						slave.rules.living = "normal";
+						slave.rules.living = LivingRule.NORMAL;
 					} else {
-						slave.rules.living = "spare";
+						slave.rules.living = LivingRule.SPARE;
 					}
 					break;
 				case "Petite Admiration":
 				case "Statuesque Glorification":
 					if (heightPass(slave)) {
-						slave.rules.living = "normal";
+						slave.rules.living = LivingRule.NORMAL;
 					} else {
-						slave.rules.living = "spare";
+						slave.rules.living = LivingRule.SPARE;
 					}
 					break;
 				default:
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 			}
 			break;
@@ -273,13 +273,13 @@ globalThis.assignJob = function(slave, job) {
 			switch (V.spaDecoration) {
 				case "Chattel Religionist":
 				case "Chinese Revivalist":
-					slave.rules.living = "normal";
+					slave.rules.living = LivingRule.NORMAL;
 					break;
 				case "Degradationist":
-					slave.rules.living = "spare";
+					slave.rules.living = LivingRule.SPARE;
 					break;
 				default:
-					slave.rules.living = "luxurious";
+					slave.rules.living = LivingRule.LUXURIOUS;
 					break;
 			}
 			break;
@@ -287,67 +287,67 @@ globalThis.assignJob = function(slave, job) {
 		case Job.NURSERY.toLowerCase():
 		case "nursery":
 			slave.assignment = Job.NURSERY;
-			slave.rules.living = "normal";
+			slave.rules.living = LivingRule.NORMAL;
 			break;
 
 		case Job.ATTENDANT.toLowerCase():
 			uniqueJob("Attendant");
 			slave.assignment = Job.ATTENDANT;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.MATRON.toLowerCase():
 			uniqueJob("Matron");
 			slave.assignment = Job.MATRON;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.DJ.toLowerCase():
 			uniqueJob("dj");
 			slave.assignment = Job.DJ;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.MADAM.toLowerCase():
 			uniqueJob("Madam");
 			slave.assignment = Job.MADAM;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.MILKMAID.toLowerCase():
 			uniqueJob("Milkmaid");
 			slave.assignment = Job.MILKMAID;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.FARMER.toLowerCase():
 			uniqueJob("Farmer");
 			slave.assignment = Job.FARMER;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.NURSE.toLowerCase():
 			uniqueJob("Nurse");
 			slave.assignment = Job.NURSE;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.TEACHER.toLowerCase():
 			uniqueJob("Schoolteacher");
 			slave.assignment = Job.TEACHER;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.STEWARD.toLowerCase():
 			uniqueJob("Stewardess");
 			slave.assignment = Job.STEWARD;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.WARDEN.toLowerCase():
 			uniqueJob("Wardeness");
 			slave.assignment = Job.WARDEN;
-			slave.rules.living = "luxurious";
+			slave.rules.living = LivingRule.LUXURIOUS;
 			break;
 
 		case Job.RECRUITER:
@@ -358,18 +358,14 @@ globalThis.assignJob = function(slave, job) {
 		case Job.CONCUBINE.toLowerCase():
 			uniqueJob("Concubine");
 			slave.assignment = Job.CONCUBINE;
-			if (V.masterSuiteUpgradeLuxury > 0) {
-				slave.rules.living = "luxurious";
-			} else {
-				slave.rules.living = "normal";
-			}
+			slave.rules.living = V.masterSuiteUpgradeLuxury > 0 ? LivingRule.LUXURIOUS : LivingRule.NORMAL;
 			break;
 
 		case Job.HEADGIRL.toLowerCase():
 			uniqueJob("HeadGirl");
 			slave.assignment = Job.HEADGIRL;
 			if (V.HGSuite === 1) {
-				slave.rules.living = "luxurious";
+				slave.rules.living = LivingRule.LUXURIOUS;
 			}
 			V.HGTimeInGrade = 0;
 			break;
@@ -378,7 +374,7 @@ globalThis.assignJob = function(slave, job) {
 			uniqueJob("Bodyguard");
 			slave.assignment = Job.BODYGUARD;
 			if (V.dojo > 1) {
-				slave.rules.living = "luxurious";
+				slave.rules.living = LivingRule.LUXURIOUS;
 			}
 			if (V.pitBG === 1 && V.fighterIDs.includes(slave.ID)) { V.fighterIDs.delete(slave.ID); }
 			break;
@@ -471,8 +467,8 @@ globalThis.assignJobSafely = function(slave, assignmentStr) {
 globalThis.removeJob = function(slave, assignment, saveRecord = false) {
 	if (!slave) {
 		// it is well-formed, but does nothing, to remove an assignment from nobody.
-		// this lets us call <<run removeJob($HeadGirl, Job.HEADGIRL)>> and similar,
-		// without first checking to see whether a slave is really assigned to $HeadGirl or not.
+		// this lets us call <<run removeJob(_S.HeadGirl, Job.HEADGIRL)>> and similar,
+		// without first checking to see whether a slave is really assigned to _S.HeadGirl or not.
 		return '';
 	}
 
@@ -499,35 +495,35 @@ globalThis.removeJob = function(slave, assignment, saveRecord = false) {
 			V.JobIDMap[slave.assignment].delete(slave.ID);
 		}
 
-		if (V.HeadGirl !== 0 && slave.ID === V.HeadGirl.ID) {
-			V.HeadGirl = 0;
-		} else if (V.Recruiter !== 0 && slave.ID === V.Recruiter.ID) {
-			V.Recruiter = 0;
+		if (slave.ID === V.HeadGirlID) {
+			V.HeadGirlID = 0;
+		} else if (slave.ID === V.RecruiterID) {
+			V.RecruiterID = 0;
 			V.recruiterIOUs = 0;
-		} else if (V.Bodyguard !== 0 && slave.ID === V.Bodyguard.ID) {
-			V.Bodyguard = 0;
-		} else if (V.Madam !== 0 && slave.ID === V.Madam.ID) {
-			V.Madam = 0;
-		} else if (V.djID === slave.ID) {
+		} else if (slave.ID === V.BodyguardID) {
+			V.BodyguardID = 0;
+		} else if (slave.ID === V.MadamID) {
+			V.MadamID = 0;
+		} else if (slave.ID === V.djID) {
 			V.djID = 0;
-		} else if (V.Milkmaid !== 0 && slave.ID === V.Milkmaid.ID) {
-			V.Milkmaid = 0;
-		} else if (V.Farmer !== 0 && slave.ID === V.Farmer.ID) {
-			V.Farmer = 0;
-		} else if (V.SchoolteacherID === slave.ID) {
+		} else if (slave.ID === V.MilkmaidID) {
+			V.MilkmaidID = 0;
+		} else if (slave.ID === V.FarmerID) {
+			V.FarmerID = 0;
+		} else if (slave.ID === V.SchoolteacherID) {
 			V.SchoolteacherID = 0;
 		} else if (V.AttendantID === slave.ID) {
 			V.AttendantID = 0;
-		} else if (V.Matron !== 0 && slave.ID === V.Matron.ID) {
-			V.Matron = 0;
-		} else if (V.NurseID === slave.ID) {
+		} else if (slave.ID === V.MatronID) {
+			V.MatronID = 0;
+		} else if (slave.ID === V.NurseID) {
 			V.NurseID = 0;
-		} else if (V.StewardessID === slave.ID) {
+		} else if (slave.ID === V.StewardessID) {
 			V.StewardessID = 0;
-		} else if (V.WardenessID === slave.ID) {
+		} else if (slave.ID === V.WardenessID) {
 			V.WardenessID = 0;
-		} else if (V.Concubine !== 0 && slave.ID === V.Concubine.ID) {
-			V.Concubine = 0;
+		} else if (slave.ID === V.ConcubineID) {
+			V.ConcubineID = 0;
 		} else if (slave.ID === V.StudID) {
 			V.StudID = 0;
 		}
@@ -608,20 +604,14 @@ globalThis.removeJob = function(slave, assignment, saveRecord = false) {
 			case "be your head girl":
 				let attentionCheck = 1;
 				slave.assignment = Job.REST;
-				const HGSlave = V.slaves.findIndex(s => s.assignment === Job.HEADGIRLSUITE);
-				if (HGSlave !== -1) {
-					if (V.HGSuiteEquality === 1) {
-						if (V.slaves[HGSlave].devotion > 50) {
-							removeJob(V.slaves[HGSlave], Job.HEADGIRLSUITE);
-							assignJob(V.slaves[HGSlave], Job.HEADGIRL);
-							V.HeadGirl = V.slaves[HGSlave];
-							V.slaves[HGSlave].diet = "healthy";
-							attentionCheck = 0;
-						} else {
-							removeJob(V.slaves[HGSlave], Job.HEADGIRLSUITE);
-						}
-					} else {
-						removeJob(V.slaves[HGSlave], Job.HEADGIRLSUITE);
+				const HGSlave = V.slaves.find(s => s.assignment === Job.HEADGIRLSUITE);
+				if (HGSlave) {
+					removeJob(HGSlave, Job.HEADGIRLSUITE);
+					if (V.HGSuiteEquality === 1 && HGSlave.devotion > 50) {
+						assignJob(HGSlave, Job.HEADGIRL);
+						V.HeadGirlID = HGSlave.ID;
+						HGSlave.diet = "healthy";
+						attentionCheck = 0;
 					}
 				}
 				if (V.personalAttention === "HG" && attentionCheck === 1) {
@@ -664,8 +654,8 @@ globalThis.removeJob = function(slave, assignment, saveRecord = false) {
 
 		V.JobIDMap[slave.assignment].add(slave.ID);
 
-		if (slave.rules.living === "luxurious" && !assignmentVisible(slave)) {
-			slave.rules.living = "normal";
+		if (slave.rules.living === LivingRule.LUXURIOUS && !assignmentVisible(slave)) {
+			slave.rules.living = LivingRule.NORMAL;
 		}
 
 		slave.choosesOwnAssignment = 0;
diff --git a/src/js/economyJS.js b/src/js/economyJS.js
index 2f5b97a3dd332f048f98eb72162ef773808cc4d6..3712b2bb7f3d9fc7d4b3c83e5146773ded0ae15e 100644
--- a/src/js/economyJS.js
+++ b/src/js/economyJS.js
@@ -608,10 +608,10 @@ globalThis.calculateCosts = (function() {
 		return (
 			(
 				(facility)
-				+(0.1*V.farmyardUpgrade.fertilizer*facility)
-				-(0.2*V.farmyardUpgrade.hydroponics*facility)
-				+(0.2*V.farmyardUpgrade.seeds*facility)
-				-(0.4*V.farmyardUpgrade.machinery*facility)
+				+(0.1*V.farmyardUpgrades.fertilizer*facility)
+				-(0.2*V.farmyardUpgrades.hydroponics*facility)
+				+(0.2*V.farmyardUpgrades.seeds*facility)
+				-(0.4*V.farmyardUpgrades.machinery*facility)
 			)*2
 		);
 	}
@@ -711,7 +711,7 @@ globalThis.calculateCosts = (function() {
 
 	// policy and other expenses
 	function getRecruiterCosts() {
-		return (V.Recruiter !== 0) ? 250 : 0;
+		return V.RecruiterID ? 250 : 0;
 	}
 
 	function getSchoolCosts() {
@@ -719,6 +719,9 @@ globalThis.calculateCosts = (function() {
 		if (V.TSS.schoolPresent === 1) {
 			costs += 1000;
 		}
+		if (V.TUO.schoolPresent === 1) {
+			costs += 1000;
+		}
 		if (V.GRI.schoolPresent === 1) {
 			costs += 1000;
 		}
@@ -1199,8 +1202,10 @@ globalThis.getSlaveCostArray = function(s) {
 		retval.push({text: "Increased living expenses due to muffled hearing", value: 15});
 	}
 	if ((s.assignment !== Job.DAIRY || V.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
-		if (!canWalk) {
+		if (!canMove) {
 			retval.push({text: "Increased living expenses due to immobility", value: rulesCost});
+		} else if (!canWalk) {
+			retval.push({text: "Increased living expenses due to limited mobility", value: rulesCost * 0.50});
 		} else {
 			if (!hasAllLimbs(s)) {
 				retval.push({
@@ -1502,80 +1507,79 @@ globalThis.slaveJobValues = function(lowerClassSexDemandRef, middleClassSexDeman
 	}
 
 	// Accounting for the Madam
-	V.Madam = V.slaves.find(s => {
-		return s.assignment === Job.MADAM;
-	});
-	if (V.Madam !== 0) {
-		if (!canTalk(V.Madam)) {
-			V.Madam = 0;
+	if (V.MadamID !== 0) {
+		const madam = S.Madam;
+		if (!canTalk(madam)) {
+			V.MadamID = 0;
 			V.unMadam = 1;
-		} else if (V.Madam.preg > 37 && V.Madam.broodmother === 2) {
-			V.Madam = 0;
+		} else if (madam.preg > 37 && madam.broodmother === 2) {
+			V.MadamID = 0;
 			V.unMadam = 2;
-		} else if (V.Madam.fetish === "mindbroken") {
-			V.Madam = 0;
+		} else if (madam.fetish === "mindbroken") {
+			V.MadamID = 0;
 			V.unMadam = 3;
-		} else if (!canWalk(V.Madam)) {
-			V.Madam = 0;
+		} else if (!canWalk(madam)) {
+			V.MadamID = 0;
 			V.unMadam = 4;
-		} else if (!canSee(V.Madam)) {
-			V.Madam = 0;
+		} else if (!canSee(madam)) {
+			V.MadamID = 0;
 			V.unMadam = 5;
-		} else if (!canHear(V.Madam)) {
-			V.Madam = 0;
+		} else if (!canHear(madam)) {
+			V.MadamID = 0;
 			V.unMadam = 6;
-		} else if (!canHold(V.Madam)) {
-			V.Madam = 0;
+		} else if (!canHold(madam)) {
+			V.MadamID = 0;
 			V.unMadam = 7;
 		}
 	}
-	if (V.Madam !== 0) {
-		const madamIntel = V.Madam.intelligence + V.Madam.intelligenceImplant;
+	if (V.MadamID !== 0) {
+		const madam = S.Madam;
+		const madamIntel = madam.intelligence + madam.intelligenceImplant;
 		V.madamCashBonus = 0;
-		if (V.Madam.relationship === -3 && V.Madam.devotion > 50) {
+		if (madam.relationship === -3 && madam.devotion > 50) {
 		// As your loving $wife, $he does $his best to attract attention to your brothel.
 			V.madamCashBonus += 0.25;
 		}
-		if (V.Madam.skill.whoring > 10 && V.Madam.skill.whoring <= 30) {
-			// V.Madam.slaveName's basic skills marginally yellowgreen;improve business at $brothelName.
+		if (madam.skill.whoring > 10 && madam.skill.whoring <= 30) {
+			// S.Madam.slaveName's basic skills marginally yellowgreen;improve business at $brothelName.
 			V.madamCashBonus += 0.05;
-		} else if (V.Madam.skill.whoring <= 60) {
-			// V.Madam.slaveName's skills yellowgreen;improve business at $brothelName.
+		} else if (madam.skill.whoring <= 60) {
+			// S.Madam.slaveName's skills yellowgreen;improve business at $brothelName.
 			V.madamCashBonus += 0.1;
-		} else if (V.Madam.skill.whoring < 100) {
-			// V.Madam.slaveName's skills greatly yellowgreen;improve business at $brothelName.
+		} else if (madam.skill.whoring < 100) {
+			// S.Madam.slaveName's skills greatly yellowgreen;improve business at $brothelName.
 			V.madamCashBonus += 0.15;
 		} else {
-			// V.Madam.slaveName's mastery immensely yellowgreen;improves business at $brothelName.
+			// S.Madam.slaveName's mastery immensely yellowgreen;improves business at $brothelName.
 			V.madamCashBonus += 0.20;
 		}
-		if (V.Madam.actualAge > 35) {
+		if (madam.actualAge > 35) {
 			// $His age and experience also contribute.
 			V.madamCashBonus += 0.05;
 		} else if (V.AgePenalty === 0) {
 			V.madamCashBonus += 0.05;
 		}
-		if (setup.madamCareers.includes(V.Madam.career)) {
+		if (setup.madamCareers.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.
 			V.madamCashBonus += 0.05;
-		} else if (V.Madam.skill.madam >= V.masteredXP) {
+		} else if (madam.skill.madam >= V.masteredXP) {
 			// $He has experience from working for you that helps $him in the seedy business of selling other people's bodies for sex.
 			V.madamCashBonus += 0.05;
 		} else {
-			V.Madam.skill.madam += jsRandom(1, Math.ceil((madamIntel) / 15) + 8);
+			madam.skill.madam += jsRandom(1, Math.ceil((madamIntel) / 15) + 8);
 		}
 		if (madamIntel > 15) {
 			// $He is a clever manager.
 			V.madamCashBonus += 0.05 * Math.floor((-0.00008 * madamIntel * madamIntel) + (0.0337 * madamIntel) + 0.5);
 		}
-		if (V.Madam.dick > 2 && canPenetrate(V.Madam)) {
+		if (madam.dick > 2 && canPenetrate(madam)) {
 			// $His turgid dick helps $him manage the bitches.
 			V.madamCashBonus += 0.05;
 		}
 		for (const slave of App.Entity.facilities.brothel.employees()) {
-			if (V.Madam.relationshipTarget === slave.ID) {
+			if (madam.relationshipTarget === slave.ID) {
 				V.madamCashBonus -= 0.05;
-			} else if (areRelated(V.Madam, slave)) {
+			} else if (areRelated(madam, slave)) {
 				V.madamCashBonus += 0.05;
 			}
 			if (slave.prestigeDesc === "$He is a famed Free Cities whore, and commands top prices.") {
@@ -1608,7 +1612,7 @@ globalThis.slaveJobValues = function(lowerClassSexDemandRef, middleClassSexDeman
 		}
 
 		// The Madam adding to 'brothel'
-		SJVBrothel(V.slaves[V.slaveIndices[V.Madam.ID]], lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef);
+		SJVBrothel(S.Madam, lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef);
 	}
 
 
@@ -1816,7 +1820,7 @@ globalThis.slaveJobValues = function(lowerClassSexDemandRef, middleClassSexDeman
 				if (V.brothelAdsSpending !== 0) {
 					beautyMultiplier += 0.05 * App.Ads.getMatchedCategoryCount(s, "brothel");
 				}
-				if (V.Madam !== 0) {
+				if (V.MadamID !== 0) {
 					if (V.madamCashBonus > 0) {
 						if (!canHear(s)) {
 							beautyMultiplier += V.madamCashBonus * 0.5;
diff --git a/src/js/eventHandlers.js b/src/js/eventHandlers.js
index f14431042abe44d14087718351f99e04421f7c82..9942b621297570890298d38d5fda9ec658ee5e4a 100644
--- a/src/js/eventHandlers.js
+++ b/src/js/eventHandlers.js
@@ -22,6 +22,7 @@ App.EventHandlers = function() {
 	}
 
 	function storyReady() {
+		App.UI.Hotkeys.init();
 	}
 
 	function optionsChanged() {
diff --git a/src/js/eventSelectionJS.js b/src/js/eventSelectionJS.js
index a175fc14dddd2f0540aab5ec2874d727ce33f6e5..fc2516dde2e53073e631f705947da21c778e0c09 100644
--- a/src/js/eventSelectionJS.js
+++ b/src/js/eventSelectionJS.js
@@ -48,16 +48,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 					}
 				}
 
-				if (eventSlave.intelligence + eventSlave.intelligenceImplant > 50) {
-					if (eventSlave.devotion > 50) {
-						if (eventSlave.intelligenceImplant >= 15) {
-							if (eventSlave.accent < 4) {
-								V.RESSevent.push("devoted educated slave");
-							}
-						}
-					}
-				}
-
 				if (eventSlave.skill.entertainment >= 60 || ["a ballerina", "a camgirl", "a camwhore", "a cheerleader", "a classical dancer", "a dancer", "a house DJ", "a party girl", "an aspiring pop star", "an exotic dancer", "an idol"].includes(eventSlave.career)) {
 					if (canHear(eventSlave)) {
 						if (eventSlave.health.condition > 40) {
@@ -112,7 +102,7 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 					}
 				}
 
-				if (V.HeadGirl !== 0) {
+				if (V.HeadGirlID !== 0) {
 					if (eventSlave.devotion <= 50) {
 						if (eventSlave.anus !== 0 && canDoAnal(eventSlave)) {
 							if (V.HGSeverity >= 0) {
@@ -121,19 +111,19 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 							V.events.push("RE shower punishment");
 						}
 					}
-					if (eventSlave.ID === V.HeadGirl.ID) {
+					if (eventSlave.ID === V.HeadGirlID) {
 						if (eventSlave.trust > 50) {
 							V.RESSevent.push("trusting HG");
 						}
 					}
-					if (eventSlave.ID !== V.HeadGirl.ID) {
+					if (eventSlave.ID !== V.HeadGirlID) {
 						if (canSee(eventSlave) && canWalk(eventSlave)) {
 							if (eventSlave.rules.speech !== "restrictive") {
 								if (eventSlave.trust > 75) {
 									if (eventSlave.devotion > 50) {
 										if (eventSlave.skill.oral > 30) {
-											if (eventSlave.intelligence + eventSlave.intelligenceImplant >= V.HeadGirl.intelligence + V.HeadGirl.intelligenceImplant) {
-												if (eventSlave.skill.oral > V.HeadGirl.skill.oral) {
+											if (eventSlave.intelligence + eventSlave.intelligenceImplant >= S.HeadGirl.intelligence + S.HeadGirl.intelligenceImplant) {
+												if (eventSlave.skill.oral > S.HeadGirl.skill.oral) {
 													V.events.push("RE HG replacement");
 												}
 											}
@@ -287,9 +277,9 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 				if (eventSlave.devotion > 50) {
 					if (eventSlave.trust > 50) {
 						let giver = 0;
-						if (V.HeadGirl === 0) {
+						if (V.HeadGirlID === 0) {
 							giver = 1;
-						} else if (V.HeadGirl.ID !== eventSlave.ID) {
+						} else if (V.HeadGirlID !== eventSlave.ID) {
 							giver = 1;
 						}
 						if (giver === 1) {
@@ -335,18 +325,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 					}
 				}
 
-				if (canSee(eventSlave)) {
-					if (eventSlave.face >= -10) {
-						if (eventSlave.devotion >= 10) {
-							if ([Job.PUBLIC, Job.WHORE].includes(eventSlave.assignment)) {
-								if (V.REUglyCheckinIDs.includes(eventSlave.ID)) {
-									V.RECIevent.push("ugly");
-								}
-							}
-						}
-					}
-				}
-
 				/*
 				if(eventSlave.drugs === "breast injections") {
 					if(eventSlave.anus > 0 || eventSlave.vagina > 0) {
@@ -384,20 +362,10 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 									V.RETSevent.push("sadistic description");
 								}
 							}
-							if (V.cockFeeder > 0) {
-								if (canPenetrate(eventSlave) || eventSlave.dick === 0) {
-									V.RESSevent.push("kitchen molestation");
-								}
-							}
 						}
 						if (V.REIfYouEnjoyItSubIDs.length > 0) {
 							V.RETSevent.push("if you enjoy it");
 						}
-						if (V.RESimpleAssaultIDs.length > 0) {
-							if (canPenetrate(eventSlave)) {
-								V.RETSevent.push("simple assault");
-							}
-						}
 					}
 				}
 
@@ -581,28 +549,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 					}
 				}
 
-				if (canTalk(eventSlave)) {
-					if (eventSlave.dietCum > 0) {
-						if (eventSlave.diet === "fattening") {
-							if (eventSlave.trust >= -50) {
-								if (eventSlave.fetish !== "cumslut") {
-									if (eventSlave.weight < -30) {
-										if (eventSlave.health.condition > -80) {
-											if (eventSlave.behavioralFlaw !== "anorexic") {
-												if (eventSlave.sexualFlaw !== "self hating") {
-													if (eventSlave.fetishStrength <= 60 || eventSlave.fetishKnown !== 1) {
-														V.RESSevent.push("too thin for cum diet");
-													}
-												}
-											}
-										}
-									}
-								}
-							}
-						}
-					}
-				}
-
 				if (eventSlave.drugs === "intensive breast injections" || eventSlave.drugs === "hyper breast injections") {
 					if (eventSlave.boobs <= 8000) {
 						if (eventSlave.boobs > 2000) {
@@ -696,7 +642,7 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 								if (eventSlave.health.condition > 20) {
 									if (eventSlave.energy > 50) {
 										if (canPenetrate(eventSlave)) {
-											if (V.Bodyguard === 0) {
+											if (V.BodyguardID === 0) {
 												V.RESSevent.push("mutinery attempt");
 											}
 										}
@@ -822,8 +768,8 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 			if (canWalk(eventSlave)) {
 				if (eventSlave.devotion > 20) {
 					if (eventSlave.trust > -10) {
-						if (eventSlave.ID !== V.HeadGirl.ID) {
-							if (eventSlave.ID !== V.Concubine.ID) {
+						if (eventSlave.ID !== V.HeadGirlID) {
+							if (eventSlave.ID !== V.ConcubineID) {
 								V.RESSevent.push("refreshment delivery");
 							}
 						}
@@ -988,12 +934,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 				}
 			}
 
-			if (App.Utils.hasFamilySex(eventSlave) || eventSlave.rules.release.slaves === 1) {
-				if (canPenetrate(eventSlave)) {
-					V.RESSevent.push("slave dick on slave");
-				}
-			}
-
 			if (eventSlave.devotion >= -20) {
 				if (eventSlave.lactation > 1) {
 					if (canDoVaginal(eventSlave) || canDoAnal(eventSlave)) {
@@ -1202,14 +1142,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 					}
 				}
 			}
-
-			if (eventSlave.assignment === Job.PUBLIC) {
-				if (canDoAnal(eventSlave) || canDoVaginal(eventSlave)) {
-					if (eventSlave.devotion > 50) {
-						V.RESSevent.push("serve the public devoted");
-					}
-				}
-			}
 		} /* closes mute exempt */
 
 		if (eventSlave.boobs > 25000) {
@@ -1300,12 +1232,6 @@ globalThis.generateRandomEventPoolStandard = function(eventSlave) {
 			}
 		}
 
-		if (App.Utils.hasFamilySex(eventSlave) || eventSlave.rules.release.slaves === 1) {
-			if (eventSlave.clit > 2) {
-				V.RESSevent.push("slave clit on slave");
-			}
-		}
-
 		if (eventSlave.rules.release.masturbation === 1) {
 			if (eventSlave.dick > 4) {
 				if (hasAnyArms(eventSlave)) {
@@ -1624,7 +1550,7 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 					}
 				}
 
-				if (V.HeadGirl !== 0) {
+				if (V.HeadGirlID !== 0) {
 					if (eventSlave.devotion <= 50) {
 						if (eventSlave.anus !== 0 && canDoAnal(eventSlave)) {
 							if (V.HGSeverity >= 0) {
@@ -1685,9 +1611,9 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 				if (eventSlave.devotion > 50) {
 					if (eventSlave.trust > 50) {
 						let giver = 0;
-						if (V.HeadGirl === 0) {
+						if (V.HeadGirlID === 0) {
 							giver = 1;
-						} else if (V.HeadGirl.ID !== eventSlave.ID) {
+						} else if (V.HeadGirlID !== eventSlave.ID) {
 							giver = 1;
 						}
 						if (giver === 1) {
@@ -1725,20 +1651,10 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 									V.RETSevent.push("sadistic description");
 								}
 							}
-							if (V.cockFeeder > 0) {
-								if (canPenetrate(eventSlave) || eventSlave.dick === 0) {
-									V.RESSevent.push("kitchen molestation");
-								}
-							}
 						}
 						if (V.REIfYouEnjoyItSubIDs.length > 0) {
 							V.RETSevent.push("if you enjoy it");
 						}
-						if (V.RESimpleAssaultIDs.length > 0) {
-							if (canPenetrate(eventSlave)) {
-								V.RETSevent.push("simple assault");
-							}
-						}
 					}
 				}
 
@@ -1896,28 +1812,6 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 					}
 				}
 
-				if (canTalk(eventSlave)) {
-					if (eventSlave.dietCum > 0) {
-						if (eventSlave.diet === "fattening") {
-							if (eventSlave.trust >= -50) {
-								if (eventSlave.fetish !== "cumslut") {
-									if (eventSlave.weight < -30) {
-										if (eventSlave.health.condition > -80) {
-											if (eventSlave.behavioralFlaw !== "anorexic") {
-												if (eventSlave.sexualFlaw !== "self hating") {
-													if (eventSlave.fetishStrength <= 60 || eventSlave.fetishKnown !== 1) {
-														V.RESSevent.push("too thin for cum diet");
-													}
-												}
-											}
-										}
-									}
-								}
-							}
-						}
-					}
-				}
-
 				if (eventSlave.fetish === "sadist") {
 					if (eventSlave.fetishStrength > 20) {
 						if (App.Entity.facilities.arcade.established) {
@@ -1939,7 +1833,7 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 								if (eventSlave.health.condition > 20) {
 									if (eventSlave.energy > 50) {
 										if (canPenetrate(eventSlave)) {
-											if (V.Bodyguard === 0) {
+											if (V.BodyguardID === 0) {
 												V.RESSevent.push("mutinery attempt");
 											}
 										}
@@ -2017,8 +1911,8 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 			if (canWalk(eventSlave)) {
 				if (eventSlave.devotion > 20) {
 					if (eventSlave.trust > -10) {
-						if (eventSlave.ID !== V.HeadGirl.ID) {
-							if (eventSlave.ID !== V.Concubine.ID) {
+						if (eventSlave.ID !== V.HeadGirlID) {
+							if (eventSlave.ID !== V.ConcubineID) {
 								V.RESSevent.push("refreshment delivery");
 							}
 						}
@@ -2149,12 +2043,6 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 				}
 			}
 
-			if (App.Utils.hasFamilySex(eventSlave) || eventSlave.rules.release.slaves === 1) {
-				if (canPenetrate(eventSlave)) {
-					V.RESSevent.push("slave dick on slave");
-				}
-			}
-
 			if (eventSlave.devotion >= -20) {
 				if (eventSlave.lactation > 1) {
 					if (canDoVaginal(eventSlave) || canDoAnal(eventSlave)) {
@@ -2305,12 +2193,6 @@ globalThis.generateRandomEventPoolServant = function(eventSlave) {
 			}
 		}
 
-		if (App.Utils.hasFamilySex(eventSlave) || eventSlave.rules.release.slaves === 1) {
-			if (eventSlave.clit > 2) {
-				V.RESSevent.push("slave clit on slave");
-			}
-		}
-
 		if (eventSlave.rules.release.masturbation === 1) {
 			if (eventSlave.dick > 4) {
 				if (hasAnyArms(eventSlave)) {
diff --git a/src/js/familyTreeJS.js b/src/js/familyTreeJS.js
index f451441c52dc514fea3110d6fc0548448fcebc6a..9447ac74d549c656901be1adb9c710f8cab70420 100644
--- a/src/js/familyTreeJS.js
+++ b/src/js/familyTreeJS.js
@@ -274,8 +274,8 @@ globalThis.buildFamilyTree = function(slaves, filterID) {
 					let missing = V.missingTable[mom];
 					charList.push({
 						ID: mom,
-						mother: 0,
-						father: 0,
+						mother: missing.mother,
+						father: missing.father,
 						is_mother: true,
 						dick: missing.dick,
 						vagina: missing.vagina,
@@ -309,8 +309,8 @@ globalThis.buildFamilyTree = function(slaves, filterID) {
 					let missing = V.missingTable[dad];
 					charList.push({
 						ID: dad,
-						mother: 0,
-						father: 0,
+						mother: missing.mother,
+						father: missing.father,
 						is_father: true,
 						dick: missing.dick,
 						vagina: missing.vagina,
diff --git a/src/js/findSlave.js b/src/js/findSlave.js
index 6587925d17b6a6508b246b1b342a3ca0b6c7e6d6..bd138ce9bdcad7685308e764fd152f2068582594 100644
--- a/src/js/findSlave.js
+++ b/src/js/findSlave.js
@@ -34,7 +34,7 @@ App.FindSlave._appendResultList = function(ids, frag) {
 	if (ids.length === 0) {
 		App.UI.DOM.appendNewElement("p", frag, "No matching slaves.");
 	} else {
-		frag.appendChild(App.UI.SlaveList.render.listDOM(ids, [], App.UI.SlaveList.SlaveInteract.stdInteract));
+		frag.appendChild(App.UI.SlaveList.render(ids, [], App.UI.SlaveList.SlaveInteract.stdInteract));
 	}
 };
 
diff --git a/src/js/food.js b/src/js/food.js
deleted file mode 100644
index 6ff8ce76947e838f606b7ad3394e1b040a6d8ec8..0000000000000000000000000000000000000000
--- a/src/js/food.js
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * @param {App.Entity.SlaveState} slave
- * @returns {number}
- */
-App.Facilities.Farmyard.foodAmount = function(slave) {
-	if (!slave) {
-		return `Current slave is not valid. Please report this`;
-	}
-	let food = 150;													// kg of food produced per week by slave, before upgrades
-	if (V.farmyardUpgrade.pump === 1) {
-		food += 15;													// pump upgrade for farmyard
-	}
-	if (V.farmyardUpgrade.fertilizer === 1) {
-		food += 35;													// fertilizer upgrade for farmyard
-	}
-	if (V.farmyardUpgrade.seeds === 1) {
-		food += 65;													// seeds upgrade for farmyard
-	}
-	if (V.farmyardUpgrade.machinery === 1) {
-		food += 65;													// machinery upgrade for farmyard
-	}
-	if (V.Farmer !== 0) {										// if a farmer is assigned
-		food *= 1.1;											// TODO: expand this to account for farmer XP and skill
-		if (V.Farmer.skill.farmer >= V.masteredXP) {			// if farmer is master
-			food *= 1.1;
-		}
-	}
-	if (slave.devotion > 50) {
-		food *= 1.1;
-	} else if (slave.devotion < -50) {
-		food *= 0.8;
-	}
-	if (slave.muscles > 30) {									// slave is muscular or more
-		food *= 1.1;
-	} else if (slave.muscles <= -6) {							// slave is weak or less
-		food *= 0.8;
-	}
-	if (slave.weight > 95) {									// slave is overweight or more
-		food *= 0.9;
-	} else if (slave.weight > 130) {							// slave is fat or more
-		food *= 0.8;
-	} else if (slave.weight > 160) {							// slave is obese or more
-		food *= 0.7;
-	} else if (slave.weight > 190) {							// slave is very obese or more
-		food *= 0.6;
-	}
-	if (!canSee(slave)) {										// slave is blind
-		food *= 0.6;
-	} else if (!canSeePerfectly(slave)) {						// slave is nearsighted
-		food *= 0.8;
-	}
-	if (slave.hears === -1) {									// slave is hard of hearing
-		food *= 0.8;
-	} else if (slave.hears < -1) {								// slave is deaf
-		food *= 0.6;
-	}
-	food *= restEffects(slave, 20);
-	food = Math.trunc(food);
-	food = Math.clamp(food, 1, 1000000000000000000);
-	return food;
-};
-
-/**
- * @param {App.Entity.SlaveState} slave
- * @returns {number}
- */
-App.Facilities.Farmyard.farmShowsIncome = function(slave) {
-	// TODO: incorporate farmyardRestraints
-	let arcology = V.arcologies[0];
-	let cash = 100;													// TODO: this is just a placeholder
-	if (!slave) {
-		return null;
-	} else {
-		if (V.Farmer !== 0) {										// farmer is assigned
-			cash *= 1.1;
-			if (V.Farmer.skill.farmer >= V.masteredXP) {			// farmer is master
-				cash *= 1.1;
-			}
-		}
-		/* OPEN FS EFFECTS */
-		if (arcology.FSSupremacist !== "unset") {
-			if (slave.race === arcology.FSSupremacistRace) {		// slave is of supreme race
-				cash *= 0.9;										// TODO: should supreme race slaves receive a penalty?
-				repX(-10, "food");									// TODO: should this cause a rep loss
-			}
-		}
-		if (arcology.FSSubjugationist !== "unset") {
-			if (slave.race === arcology.FSSubjugationistRace) { 	// slave is of subjugated race
-				cash *= 1.1;
-				repX(10, "food");									// TODO: should this cause a rep gain?
-			} else {
-				cash *= 0.9;										// TODO: should unsubjugated race slaves receive a penalty?
-			}
-		}
-		if (arcology.FSRepopulationFocus !== "unset") {
-			if (slave.pregWeek > 16) {								// slave is visibly pregnant
-				if (slave.eggType !== "human") {					// with a non-human
-					cash *= 1.1;
-				}
-				cash *= 1.2;
-			} else {												// slave is not visibly pregnant
-				cash *= 0.8;										// TODO: should non-pregnant slaves receive a penalty?
-			}
-		}
-		if (arcology.FSRestart !== "unset") {
-			if (slave.pregWeek > 16) {								// slave is visibly pregnant
-				if (slave.eggType !== "human") {					// with a non-human
-					cash *= 0.9;
-					repX(-15, "food");
-				}
-				cash *= 0.6;
-				repX(-10, "food");
-			}
-		}
-		if (arcology.FSGenderRadicalist !== "unset") {
-			if (slave.dick > 0) {									// TODO: does this make sense?
-				cash *= 1.1;
-			} else {
-				cash *= 0.8;
-			}
-		}
-		if (arcology.FSGenderFundamentalist !== "unset") {
-			if (slave.dick > 0) {
-				cash *= 1.1;										// TODO: does this make sense?
-			} else {
-				cash *= 0.8;
-			}
-		}
-		if (arcology.FSPaternalist !== "unset") {
-			if (V.seeBestiality) {
-				if (V.farmyardBreeding === 1) {						// slaves are being bred
-					cash *= 0.8;
-				} else {											// slaves are putting on shows
-					cash *= 0.9;
-				}
-			}
-		}
-		if (arcology.FSDegradationist !== "unset") {
-			if (V.seeBestiality) {
-				if (V.farmyardBreeding === 1) {						// slaves are being bred
-					cash *= 1.2;
-				} else {											// slaves are putting on shows
-					cash *= 1.1;
-				}
-			}
-		}
-		if (arcology.FSBodyPurist !== "unset") {
-			if (slave.boobsImplant > 0) {							// slave has breast implants
-				cash *= 0.9;
-			}
-			if (slave.buttImplant > 0) {							// slave has butt implants
-				cash *= 0.9;
-			}
-			if (slave.faceImplant > 0) {							// slave has had facial surgery
-				cash *= 0.9;
-			}
-			if (slave.lipsImplant > 0) {							// slave has lip implants
-				cash *= 0.9;
-			}
-		}
-		if (arcology.FSTransformationFetishist !== "unset") {
-			if (slave.boobsImplant > 0) {							// slave has breast implants
-				cash *= 1.1;
-			}
-			if (slave.buttImplant > 0) {							// slave has butt implants
-				cash *= 1.1;
-			}
-			if (slave.faceImplant > 0) {							// slave has had facial surgery
-				cash *= 1.1;
-			}
-			if (slave.lipsImplant > 0) {							// slave has lip implants
-				cash *= 1.1;
-			}
-		}
-		if (arcology.FSYouthPreferentialist !== "unset") {
-			if (slave.visualAge > 25) {								// slave looks older than 25
-				cash *= 0.8;										// TODO: not sure what age this should be
-			} else {
-				cash *= 1.2;
-			}
-		}
-		if (arcology.FSMaturityPreferentialist !== "unset") {
-			if (slave.visualAge > 25) {								// slave looks older than 25
-				cash *= 1.2;										// TODO: not sure what age this should be
-			} else {
-				cash *= 0.8;
-			}
-		}
-		if (arcology.FSSlimnessEnthusiast !== "unset") {
-			if (slave.weight > 10) {								// slave is curvy or more
-				cash *= 0.9;										// TODO: not sure what weight this should be
-			}														// TODO: should implants affect income?
-			if (slave.boobs > 799) {								// slave has larger than a D cup
-				cash *= 0.9;										// TODO: should this be smaller?
-			}
-			if (slave.butt > 3) {									// slave has bigger than a bubble butt
-				cash *= 0.9;
-			}
-		}
-		if (arcology.FSAssetExpansionist !== "unset") {
-			if (slave.weight > 10) {								// slave is curvy or more
-				cash *= 1.1;										// TODO: not sure what weight this should be
-			}														// TODO: should implants affect income?
-			if (slave.boobs > 799) {								// slave has larger than a D cup
-				cash *= 1.1;										// TODO: should this be smaller?
-			}
-			if (slave.butt > 3) {									// slave has bigger than a bubble butt
-				cash *= 1.1;
-			}
-		}
-		if (arcology.FSPastoralist !== "unset") {
-			if (slave.boobs > 799) {								// slave has larger than a D cup
-				cash *= 1.2;
-			}
-			if (slave.lactation > 0) {								// slave is lactating
-				cash *= 1.1;
-			}
-		}
-		if (arcology.FSPhysicalIdealist !== "unset") {
-			if (slave.height > 169) {								// slave is tall or taller
-				cash *= 1.1;
-			} else if (slave.height < 160) {						// slave is short or shorter
-				cash *= 0.9;
-			}
-		}
-		if (arcology.FSHedonisticDecadence !== "unset") {
-			if (slave.weight > 10) { 								// slave is curvy or fatter
-				cash *= 1.1;
-			} else if (slave.weight < -10) { 						// slave is thin or thinner
-				cash *= 0.9;
-			}
-		}
-		if (arcology.FSChattelReligionist !== "unset") { 			// TODO: I don't know what to put for this one
-			if (slave.devotion < 21) { 								// if slave is careful or less
-				cash *= 0.9;
-			}
-		}
-		// TODO: should I add the ancient cultures FS?
-		/* CLOSE FS EFFECTS */
-
-		if (setup.entertainmentCareers.includes(slave.career)) {
-			cash *= 1.1;
-		} else if (setup.farmerCareers.includes(slave.career)) {
-			cash *= 1.1;
-		}
-		if (slave.prestige === 1) { 								// slave is prestigious
-			cash *= 1.1;
-		} else if (slave.prestige === 2) { 							// slave is very prestigious
-			cash *= 1.2;
-		} else if (slave.prestige === 3) { 							// slave is extremely prestigious
-			cash *= 1.3;
-		}
-		if (slave.porn.prestige === 1) { 							// slave is prestigious from porn
-			cash *= 1.1;
-		} else if (slave.porn.prestige === 2) { 						// slave is very prestigious from porn
-			cash *= 1.2;
-		} else if (slave.porn.prestige === 3) { 						// slave is extremely prestigious from porn
-			cash *= 1.3;
-		} 															// TODO: add relationship checks
-		if (slave.face > 40) {										// slave is beautiful or more
-			cash *= 1.3;
-		} else if (slave.face > 10) {								// slave is very pretty or more
-			cash *= 1.1;
-		} else if (slave.face < -10) {								// slave is less than unattractive
-			cash *= 0.9;
-		} else if (slave.face < -40) {								// slave is less than ugly
-			cash *= 0.7;
-		}
-		if (slave.devotion > 50) { 									// slave is devoted or more
-			cash *= 1.1;
-		} else if (slave.devotion < -50) { 							// slave is reluctant or less
-			cash *= 0.8;
-		}
-		if (slave.trust > 50) { 									// slave is trusting or more
-			cash *= 1.1;
-		} else if (slave.trust < -50) { 							// slave is less than frightened
-			cash *= 0.8;											// TODO: should trust be a factor?
-		}
-		if (slave.weight > 30) { 									// slave is curvy or more
-			cash *= 0.8;											// TODO: tie in Hedonistic FS
-		} else if (slave.weight < -30) { 							// slave is very thin or less
-			cash *= 0.8;											// TODO: put this on a scale
-		}
-		if (slave.muscles > 30) { 									// slave is muscular or more
-			cash *= 0.9;											// TODO: tie in height eugenics policy
-		} else if (slave.muscles < -30) {							// slave is very weak or less
-			cash *= 0.9;											// TODO: should this be on a scale?
-		}
-		if (!canSeePerfectly(slave)) {								// slaves eyesight is nearsighted or worse
-			cash *= 0.9;											// TODO: should nearsighted and blind slaves receive a penalty?
-		}
-		if (slave.hears < 0) {										// slave is hard of hearing or less
-			cash *= 0.9;											// TODO: should hard of hearing slaves receive a penalty?
-		}
-		if (slave.boobs > 800) {									// slave has a DD cup or bigger
-			cash *= 0.9;											// TODO: would this make sense?
-		}
-		if (slave.butt > 4) {										// slave has an enormous butt or bigger
-			cash *= 0.9;											// TODO: would this make sense?
-		}
-		if (slave.preg > 10) {										// slave is pregnant and showing
-			cash *= 0.8;											// TODO: not sure how to incorporate pregnancy
-		}															// TODO: incorporate skills
-		cash *= healthPenalty(slave);
-		if (slave.intelligence > 50) {								// slave is very smart or better
-			cash *= 1.1;
-		} else if (slave.intelligence < -50) {						// slave is very slow or less
-			cash *= 0.8;
-		}
-		if (slave.energy <= 20) {									// slave has no sex drive
-			cash *= 0.7;
-		} else if (slave.energy <= 40) {							// slave has poor sex drive
-			cash *= 0.8;
-		} else if (slave.energy <= 60) {							// slave has average sex drive
-			cash *= 0.9;
-		} else if (slave.energy <= 80) {							// slave has good sex drive
-			cash *= 1.1;
-		} else if (slave.energy <= 95) {							// slave has powerful sex drive
-			cash *= 1.2;
-		} else {													// slave is a nymphomaniac
-			cash *= 1.3;
-		}
-		if (slave.fetish === "submissive") {
-			cash *= 1.1;
-		} else if (slave.fetish === "humiliation") {
-			cash *= 1.1;
-		} else if (slave.fetish === "masochist") {
-			cash *= 1.1;
-		}															// TODO: tie in quirks and flaws
-		return cash;
-	}
-};
diff --git a/src/js/health.js b/src/js/health.js
index 824a1a3376aab8d9b4c8d17c84319d9dafe83abc..5bdaef114218dee478c9b127dee7c7fb371cc58a 100644
--- a/src/js/health.js
+++ b/src/js/health.js
@@ -28,6 +28,9 @@ globalThis.healthPenalty = function(slave) {
  * @returns {void}
  */
 globalThis.healthDamage = function(slave, damage) {
+	if (!_.isFinite(damage)) {
+		throw "Health damage must be a finite number.";
+	}
 	const H = slave.health;
 	damage = Math.max(Math.trunc(damage), 0);
 	H.shortDamage += damage;
@@ -43,6 +46,9 @@ globalThis.healthDamage = function(slave, damage) {
  * @returns {void}
  */
 globalThis.healthCure = function(slave, cure) {
+	if (!_.isFinite(cure)) {
+		throw "Health cure must be a finite number.";
+	}
 	const H = slave.health;
 	cure = Math.max(Math.trunc(cure), 0);
 	if (cure > H.shortDamage) {
@@ -71,6 +77,9 @@ globalThis.surgeryDamage = function(slave, damage) {
  * @returns {void}
  */
 globalThis.improveCondition = function(slave, condition) {
+	if (!_.isFinite(condition)) {
+		throw "Health condition change must be a finite number.";
+	}
 	const H = slave.health;
 	H.condition += Math.max(Math.trunc(condition), 0);
 	updateHealth(slave);
diff --git a/src/js/ibcJS.js b/src/js/ibcJS.js
new file mode 100644
index 0000000000000000000000000000000000000000..66a22c0c815ea7790f593dbdbe1eb167748bf204
--- /dev/null
+++ b/src/js/ibcJS.js
@@ -0,0 +1,501 @@
+globalThis.ibc = (() => {
+	// These IDs are considered to be unknown parents
+	let or_null = (s) => specificCharacterID(s) ? s : null;
+
+	// Find some sort of state for a slave. Checks first the gene pool, then V.slaves, then the
+	// missing table
+	let find_gp = (id) => (slaveStateById(id) || V.genePool.find((s) => s.ID === id) || ((id in V.missingTable) ? V.missingTable[id] : null) || null);
+
+	// Create a node for the given ID
+	let create_node = (id) => ({
+		id: id, // Node ID
+		mother: null,
+		father: null,
+		nodecodes: [], // NodeCodes
+		nodecodes_str: [], // String version of the NodeCodes, for comparison
+		_coeff: null, // Cached CoI
+		child_count: 0 // Number of children of the node, for computing NodeCodes
+	});
+
+	// Determine the length of the shared prefix between the two NodeCode parameters
+	let prefix_len = (nca, ncb) => {
+		let i = 0;
+		for (i=0; i<Math.min(nca.length, ncb.length); i++) {
+			if (nca[i] !== ncb[i])
+				break;
+		}
+		return i;
+	};
+
+	// Determine the set of longest common prefixes for a node pair
+	let prefixes = (a, b) => {
+		let found = [];
+		let found_s = [];
+		a.nodecodes.forEach(nca => {
+			let match = false;
+			b.nodecodes.forEach(ncb => {
+				let l = prefix_len(nca, ncb);
+				if (l === 0 && match)
+					return;
+
+				if (l > 0) {
+					match = true;
+					let pfx = nca.slice(0, l);
+					let pfx_s = pfx.join(';');
+
+					if (!found_s.includes(pfx_s)) {
+						found_s.push(pfx_s);
+						found.push(pfx);
+					}
+				}
+			});
+		});
+
+		return found;
+	};
+
+	// Search up the tree to find a given NodeCode, starting at `n`
+	let find_nc = (nc, n) => {
+		if (n.nodecodes_str.includes(nc.join(';'))) {
+			return n;
+		}
+
+		let ret = null;
+		if (n.mother !== null)
+			ret = find_nc(nc, n.mother);
+		if (n.father !== null && ret === null)
+			ret = find_nc(nc, n.father);
+
+		return ret;
+	};
+
+	// Determine the set of common ancestors between a node pair
+	let common = (a, b) => {
+		let pfx = prefixes(a, b);
+		let pfx_s = pfx.map(s => s.join(';'));
+		let anc = [];
+
+		while (pfx.length > 0) {
+			let p = pfx.pop(0);
+			pfx_s.pop(0);
+			let ret = find_nc(p, a);
+
+			ret.nodecodes.forEach(nc => {
+				let i = pfx_s.indexOf(nc.join(';'));
+				if (i === -1)
+					return;
+
+				pfx.pop(i);
+				pfx_s.pop(i);
+			});
+
+			if (anc.findIndex(s => (s[0] == ret)) === -1)
+				anc.push([ret, p]);
+		}
+
+		return anc;
+	};
+
+	// Determine the set of NodeCodes on `n` with prefix `x`
+	let mps = (n, x) => {
+		let x_s = x.join(';');
+		return n.nodecodes.filter(nc => (nc.slice(0, x.length).join(';') === x_s));
+	};
+
+	// Compute the set of all paths to the parents of `n` with prefix `x`
+	let pp = (mother, father, x) => {
+		let m = mps(mother, x);
+		let f = mps(father, x);
+
+		let prod = [];
+		m.forEach(i => {
+			f.forEach(j => {
+				prod.push([i, j]);
+			});
+		});
+
+		return prod;
+	};
+
+	let kinship = (mother, father) => {
+		let _coeff = 0;
+		if (mother === null || father === null)
+			_coeff = 0;
+		else if (mother == father)
+			_coeff = 0.5 * (1 + coeff(mother));
+		else {
+			let cf = 0;
+			let cmn = common(mother, father);
+
+			cmn.forEach(el => {
+				let c = el[0];
+				let p = el[1];
+				let p_s = p.join(';');
+
+				let paths = pp(mother, father, p);
+				let paths_s = paths.map(p => [p[0].join(';'), p[1].join(';')].join(','));
+
+				cmn.forEach(el2 => {
+					let co = el2[0];
+					if (co == c)
+						return;
+					let m_pp = [];
+					let f_pp = [];
+
+					co.nodecodes.forEach(nc => {
+						if (nc.slice(0, p.length).join(';') != p_s)
+							return;
+
+						m_pp = m_pp.concat(mps(mother, nc));
+						f_pp = f_pp.concat(mps(father, nc));
+					});
+
+					m_pp.forEach(mp => {
+						f_pp.forEach(fp => {
+							let mf_s = [mp.join(';'), fp.join(';')].join(',');
+							let i = paths_s.indexOf(mf_s);
+							if (i === -1)
+								return;
+							paths_s.pop(i);
+							paths.pop(i);
+						});
+					});
+				});
+				paths.forEach(p => {
+					let pfx = prefix_len(p[0], p[1]);
+
+					cf += 0.5**(p[0].length + p[1].length+1 - 2*pfx) * (1 + coeff(c));
+				});
+			});
+
+			_coeff = cf;
+		}
+
+		return _coeff;
+	};
+
+	// Determine the coefficient of inbreeding of a node `n`
+	let coeff = n => {
+		if (n._coeff === null)
+			n._coeff = kinship(n.mother, n.father);
+		return n._coeff;
+	};
+
+	// Populate the NodeCodes.
+	//
+	// Each node has a set of NodeCodes, which represent the set of paths from it to its ancestors.
+	// NodeCodes here are represented by arrays of integers.
+	//
+	// NodeCodes are constructed recursively in this fashion:
+	//
+	// - Assign each of the founders (nodes with both parents === null) an unique ID, starting from
+	//   0 and incrementing each time (the order doesn't matter); a founder's set of NodeCodes has
+	//   exactly one NodeCode, which is [ID] (an array containing only their ID)
+	//
+	// - For each other node, let M be its child number w.r.t. its mother and N its child number
+	//   w.r.t. its father, i.e. the number of children that the respective parent has had before
+	//   this one (the order is not important to the algorithm, it's arbitrary here for
+	//   convenience). Its set of NodeCodes is the set of all its mother's NodeCodes with M appended
+	//   and all of its father's NodeCodes with N appended. For example, if its mother has the
+	//   NodeCodes [[2]] and M = 3 and its father has the NodeCodes [[0,1], [3,1]] and N = 1 then
+	//   the set of NodeCodes for this node would be
+	//
+	//       [[2, 3], [0, 1, 1], [3, 1, 1]]
+	//
+	// We do this iteratively here, looping over the set of all nodes until each has been assigned
+	// a NodeCode. This requires looping through a number of times equal to the number of
+	// generations, since as soon as both parents have NodeCodes their children's NodeCodes may be
+	// computed.
+	let make_nc = nodes => {
+		// Generate founder NodeCodes
+		let total = Object.keys(nodes).length;
+		let seen = [];
+		let curid = 0;
+		Object.values(nodes).forEach(n => {
+			if (n.mother !== null || n.father !== null)
+				return;
+			n.nodecodes.push([curid]);
+			curid += 1;
+			seen.push(n.id);
+		});
+
+		// Generate the rest of the NodeCodes
+		let oldSeenLength = -1;
+		while (seen.length !== total) {
+			oldSeenLength = seen.length;
+			Object.keys(nodes).forEach(s=> {
+				let n = nodes[s];
+				if (seen.includes(+s)) // We've already done this
+					return;
+				else if ((n.mother !== null && n.mother.nodecodes.length === 0) || (n.father !== null && n.father.nodecodes.length === 0)) // Too soon, we haven't done its parents
+					return;
+
+				seen.push(n.id);
+				// Compute the NodeCodes from its parents
+				[n.mother, n.father].forEach((a, i) => {
+					if (a === null || (n.mother === n.father && i === 1)) // Ignore missing parents/repeated
+						return;
+
+					a.nodecodes.forEach(nc => {
+						// Copy the NodeCode, push the child number, then add it
+						let nnc = nc.slice();
+						nnc.push(a.child_count);
+						n.nodecodes.push(nnc);
+					});
+					a.child_count += 1;
+				});
+
+				// NodeCodes must be sorted; this suffices
+				n.nodecodes.sort();
+			});
+			// check to make sure we're progressing...if not, there's a cycle in the graph
+			// dump all the nodes participating in or descended from the cycle and let the player figure it out
+			if (oldSeenLength === seen.length) {
+				const badSlaveIDs = Object.keys(nodes).filter(s => !seen.includes(+s)).map(k => nodes[k].id);
+				throw(`Inbreeding calculation: heritance cycle detected. Check slave IDs: ${badSlaveIDs}`);
+			}
+		}
+
+		// Cache the string NodeCodes
+		Object.values(nodes).forEach(n => {
+			n.nodecodes_str = n.nodecodes.map(nc => nc.join(';'));
+		});
+	};
+
+	// Make nodes for an array of slaves
+	let nodes_slaves = (slaves, ignore_coeffs=false) => {
+		let nodes = {};
+
+		// Recursively create the nodes we need, moving upwards from the given slave
+		let create_node_rec = s => {
+			// Certain parents (e.g. 0, societal elite) are not considered to be related, despite
+			// having the same ID; convert them to null
+			let m = or_null(s.mother);
+			let f = or_null(s.father);
+
+			// Ensure that parent nodes are created
+			[m, f].forEach(p => {
+				if (p !== null && !(p in nodes)) { // Not created, we have to do something
+					if (p === -1) {
+						create_node_rec(SugarCube.State.variables.PC);
+					} else {
+						// Search for a slave state, genePool entry, or missingTable entry
+						let gp = find_gp(p);
+						if (gp !== null) {
+							// If we find one, we might have ancestry information: recurse
+							create_node_rec(gp);
+						} else {
+							// Otherwise, just create a plain node
+							nodes[p] = create_node(p);
+						}
+					}
+				}
+			});
+
+			if (!(s.ID in nodes)) // Create this node if necessary
+				nodes[s.ID] = create_node(s.ID);
+			// We created its parents earlier, so set them to the actual nodes
+			nodes[s.ID].mother = (m === null) ? m : nodes[m];
+			nodes[s.ID].father = (f === null) ? f : nodes[f];
+
+			// Try to use a cached CoI for performance
+			let sg = find_gp(s.ID);
+			if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
+				nodes[s.ID]._coeff = sg.inbreedingCoeff;
+			}
+		};
+
+		// Populate the nodes
+		slaves.forEach(s=> create_node_rec(s));
+
+		// Populate NodeCodes
+		make_nc(nodes);
+
+		return nodes;
+	};
+
+	// Determine the coefficients of inbreeding of an array of slaves. Returns a mapping of their
+	// ID to their coefficient of inbreeding
+	let coeff_slaves = (slaves, ignore_coeffs=false) => {
+		let ret = {};
+		// First, pull as many existing CoI off the slaves
+		slaves.forEach(s => {
+			let sg = find_gp(s.ID);
+			if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
+				ret[s.ID] = sg.inbreedingCoeff;
+			}
+		});
+
+		// Now do any we haven't done already
+		slaves = slaves.filter(s => (!(s.ID in ret)));
+		if (slaves.length > 0) {
+			let nodes = nodes_slaves(slaves, ignore_coeffs);
+
+			// Compute coefficients
+			slaves.forEach(s => {
+				ret[s.ID] = coeff(nodes[s.ID]);
+			});
+		}
+
+		return ret;
+	};
+
+	// Determine the kinship between two slaves `a` and `b`
+	let kinship_slaves = (a, b, ignore_coeffs=false) => {
+		if (a === 0 || b === 0)
+			return 0;
+
+		return kinship_one_many(a, [b], ignore_coeffs)[b.ID];
+	};
+
+	// Determine the coefficient of inbreeding of a single slave
+	let coeff_slave = (slave, ignore_coeffs=false) => {
+		if (!ignore_coeffs && "inbreedingCoeff" in slave && slave.inbreedingCoeff !== -1)
+			return slave.inbreedingCoeff;
+
+		let gp = find_gp(slave.ID);
+		if (!ignore_coeffs && gp !== null && "inbreedingCoeff" in gp && gp.inbreedingCoeff !== -1)
+			return gp.inbreedingCoeff;
+
+		return coeff_slaves([slave], ignore_coeffs)[slave.ID];
+	};
+
+	// Determine the kinship between one and many slaves. Returns an mapping from the ID of each of
+	// the slaves in `others` to its kinship with slave `a`
+	let kinship_one_many = (a, others, ignore_coeffs=false) => {
+		if (a === 0) {
+			let ks = {};
+			others.forEach(s => {
+				if (s === 0)
+					ks[s] = 0;
+				else
+					ks[s.ID] = 0;
+			});
+			return ks;
+		}
+
+		let nodes = nodes_slaves(others.concat([a]), ignore_coeffs);
+
+		let ks = {};
+		others.forEach(s => {
+			if (s === -3) {
+				// Fake a slave object for the player's old master
+				s = {ID: -3, mother: 0, father: 0, inbreedingCoeff: 0};
+			}
+
+			if (s === 0)
+				ks[s] = 0;
+			else
+				ks[s.ID] = kinship(nodes[a.ID], nodes[s.ID]);
+		});
+
+		return ks;
+	};
+
+	// Recalculate the inbreeding coefficient for all slaves dependent on the passed IDs (e.g. the
+	// slaves themselves and all of their children). This will replace the inbreeding coefficients
+	// wherever they exist with the computed values, ignoring all cached values.
+	//
+	// This should be called if parents are changed.
+	let recalculate_coeff_ids = (ids) => {
+		// These are all the slave-like objects, i.e. they have ID, mother, and father. There will
+		// be multiple elements with the same ID: we want this, since we have to replace all
+		// occurrences of the COI for the affected slaves
+		let all_slave_like = V.slaves.concat(V.genePool).concat(V.cribs).concat(V.tanks).concat(Object.values(V.missingTable));
+		if (V.boomerangSlave !== 0)
+			all_slave_like.push(V.boomerangSlave);
+		if (V.traitor !== 0)
+			all_slave_like.push(V.traitor);
+		if (V.activeSlave !== 0)
+			all_slave_like.push(V.activeSlave);
+		all_slave_like.push(V.PC);
+		// Add a fake entry for the PC's old master
+		all_slave_like.push({ID: -3, mother: 0, father: 0, inbreedingCoeff: 0});
+
+		// Gather the genetics of all current fetuses
+		let all_fetuses = V.slaves.filter(s => s.preg > 0).map(s => s.womb.map(i => i.genetics)).reduce((res, cur) => res.concat(cur), []);
+
+		// Recursively find all of the given ID's children, born and unborn
+		let find_children_rec = (id, cur_slaves, cur_fetuses, cur_fetus_parents) => {
+			// Add fetuses
+			all_fetuses.filter(f => (f.father === id || f.mother === id)).forEach(f => {
+				// We may have to manually add the parents later
+				let actual_f = or_null(f.father);
+				let actual_m = or_null(f.mother);
+				if (actual_f !== null)
+					cur_fetus_parents.add(actual_f);
+				if (actual_m !== null)
+					cur_fetus_parents.add(actual_m);
+
+				cur_fetuses.add(f);
+			});
+
+			// Recursively add slaves
+			all_slave_like.filter(s => (s.father === id || s.mother === id)).forEach(s => {
+				if (!cur_slaves.has(s.ID)) {
+					cur_slaves.add(s.ID);
+					find_children_rec(s.ID, cur_slaves, cur_fetuses, cur_fetus_parents);
+				}
+			});
+		};
+
+		// We only need slave IDs, since we have to update all of their entries (including GP)
+		let needed_slave_ids = new Set();
+		// Since each fetus has a unique record, a set still suffices
+		let needed_fetuses = new Set();
+		let needed_parent_ids = new Set();
+
+		// Find all the children of the IDs we need to do
+		ids.forEach(id => {
+			needed_slave_ids.add(id);
+			find_children_rec(id, needed_slave_ids, needed_fetuses, needed_parent_ids);
+		});
+
+		// Now we assemble the tree from the slaves
+		let needed_slaves = [];
+		needed_slave_ids.forEach(id => {
+			if (typeof id !== "undefined")
+				needed_slaves.push(all_slave_like.find(s => s.ID === id));
+		});
+		needed_parent_ids.forEach(id => {
+			if (typeof id !== "undefined" && !needed_slave_ids.has(id))
+				needed_slaves.push(all_slave_like.find(s => s.ID === id));
+		});
+		let nodes = nodes_slaves(needed_slaves, true);
+
+		// Now calculate the inbreeding coefficients (they're cached in the tree once calculated)
+		all_slave_like.filter(s => needed_slave_ids.has(s.ID)).forEach(s => {
+			s.inbreedingCoeff = coeff(nodes[s.ID]);
+		});
+
+		// Finally, handle all of the kinship for the fetuses
+		let kinship_cache = new Map(); // Manually cache it
+		needed_fetuses.forEach(f => {
+			if (or_null(f.mother) === null || or_null(f.father) === null) {
+				f.inbreedingCoeff = 0;
+				return;
+			}
+
+			// Use a string of the form "parent;parent" to store the cache value; since kinship is
+			// commutative, the minimum parent ID will be first
+			let kinship_str = Math.min(f.mother, f.father) + ';' + Math.max(f.mother, f.father);
+			if (!kinship_cache.has(kinship_str))
+				kinship_cache.set(kinship_str, kinship(nodes[f.mother], nodes[f.father]));
+
+			f.inbreedingCoeff = kinship_cache.get(kinship_str);
+		});
+	};
+
+	let recalculate_coeff_id = (id) => {
+		return recalculate_coeff_ids([id]);
+	};
+
+	return {
+		coeff: coeff_slave,
+		coeff_slaves,
+		kinship: kinship_slaves,
+		kinship_one_many,
+		recalculate_coeff_ids,
+		recalculate_coeff_id
+	};
+})();
diff --git a/src/js/main.js b/src/js/main.js
index 4fb77bb5a0503e30817abcf1d99534990f79b714..0a96871e591df887d8f751d5387f8df04d550723 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -166,7 +166,7 @@ App.MainView.useGuard = function() {
 
 	const outerDiv = document.createElement("div");
 
-	if (!guard || guard.assignment !== "guard you") {
+	if (!guard || guard.assignment !== Job.BODYGUARD) {
 		return outerDiv;
 	}
 
diff --git a/src/js/modification.js b/src/js/modification.js
index aaedaf67f8498577e15f90fa6c04ed815924f6e8..2a9358b08cdf07248ec5b33b88270aa6a3782fee 100644
--- a/src/js/modification.js
+++ b/src/js/modification.js
@@ -556,224 +556,3 @@ App.Medicine.Modification.setTattoo = function(slave, location, design) {
 	slave[`${location}Tat`] = design;
 	return r;
 };
-
-/**
- * @param {App.Entity.PlayerState|App.Entity.SlaveState} entity
- * @param {boolean} player
- * @returns {HTMLDivElement}
- */
-App.Medicine.Modification.eyeSelector = function(entity, player = false) {
-	const {He, him, his} = getPronouns(entity);
-
-	let selectedSide = "none";
-	let selectedIris = "none";
-	let selectedPupil = "none";
-	let selectedSclera = "none";
-
-	let removeDiv = document.createElement("div");
-	removeDiv.classList.add("choices");
-	let applyDiv = document.createElement("div");
-
-	const container = document.createElement("div");
-	container.append(
-		`${player ? "You have" : `${He} has`} ${App.Desc.eyesColorLong(entity)}, ${hasBothEyes(
-			entity) ? "they are" : "it is"} ${App.Desc.eyesType(entity)}.`,
-		removeDiv, "You have a number of contact lenses in various colors available. ",
-		App.UI.DOM.makeElement("span", `You can change what ${player ? "your" : his} eyes look like.`, "note"),
-		assembleLinks(), applyDiv
-	);
-	updateRemoveDiv();
-	updateApplyDiv();
-	return container;
-
-	function assembleLinks() {
-		const sides = ["left", "right", "both"];
-		const irisColors = ["amber", "black", "blue", "brown", "green", "hazel", "orange", "pale-grey", "pink", "red",
-			"sky-blue", "turquoise", "white", "yellow"];
-		const pupilShapes = ["none", "circular", "almond-shaped", "bright", "catlike", "demonic", "devilish",
-			"goat-like", "heart-shaped", "hypnotic", "serpent-like", "star-shaped", "teary", "vacant", "wide-eyed"];
-		const scleraColors = ["none", "white", "amber", "black", "blue", "brown", "green", "hazel", "orange",
-			"pale-grey", "pink", "red", "sky-blue", "turquoise", "yellow"];
-		const div = document.createDocumentFragment();
-		div.append(
-			assembleList("Side: ", sides, value => selectedSide = value, selectedIris),
-			assembleList("Iris: ", irisColors, value => selectedIris = value, selectedSide),
-			assembleList("Pupil: ", pupilShapes, value => selectedPupil = value, selectedPupil),
-			assembleList("Sclera: ", scleraColors, value => selectedSclera = value, selectedSclera)
-		);
-		return div;
-	}
-
-	/**
-	 * @param {string} name
-	 * @param {Array<string>} list
-	 * @param {Function}callback
-	 * @param {string} selected
-	 * @returns {HTMLDivElement}
-	 */
-	function assembleList(name, list, callback, selected) {
-		const links = [];
-
-		for (let i = 0; i < list.length; i++) {
-			addToggle(list[i], callback, links, list[i] === selected);
-		}
-
-		const div = document.createElement("div");
-		div.classList.add("choices");
-		div.append(name, App.UI.DOM.arrayToList(links, " | ", " | "));
-		return div;
-	}
-
-	/**
-	 * @param {string} value
-	 * @param {Function} callback
-	 * @param {Array<HTMLAnchorElement>} links
-	 * @param {boolean} [disabled]
-	 */
-	function addToggle(value, callback, links, disabled = false) {
-		const a = document.createElement("a");
-		a.append(capFirstChar(value));
-		if (disabled) {
-			a.classList.add("disabled");
-		}
-		a.onclick = () => {
-			for (let link of links) {
-				link.classList.remove("disabled");
-			}
-			a.classList.add("disabled");
-			callback(value);
-			updateRemoveDiv();
-			updateApplyDiv();
-		};
-		links.push(a);
-	}
-
-	function updateApplyDiv() {
-		$(applyDiv).empty();
-		if (selectedSide !== "none" && selectedIris !== "none") {
-			// make the following easier to read
-			let both = selectedSide === "both";
-			let leftGlass = !hasLeftEye(entity) || getLeftEyeType(entity) === 2;
-			let rightGlass = !hasRightEye(entity) || getRightEyeType(entity) === 2;
-
-			// base eye
-			let r = player ? "" : ` ${him}`;
-			if (both) {
-				if (leftGlass && rightGlass) {
-					r += ` ${selectedIris} glass eyes`;
-				} else if (leftGlass || rightGlass) {
-					r += ` a glass eye and a ${selectedIris} lens`;
-				} else {
-					r += ` ${selectedIris} lenses`;
-				}
-			} else {
-				r += " a";
-				if ((selectedSide === "left" && leftGlass) || (selectedSide === "right" && rightGlass)) {
-					r += ` ${selectedIris} glass eye`;
-				} else {
-					r += ` ${selectedIris} lens`;
-				}
-			}
-			// pupil & sclera
-			if (selectedPupil !== "none" || selectedSclera !== "none") {
-				r += " with";
-				if (selectedPupil !== "none") {
-					r += ` ${both ? selectedPupil : addA(selectedPupil)}`;
-					if (both) {
-						r += " pupils";
-					} else {
-						r += " pupil";
-					}
-					if (selectedSclera !== "none") {
-						r += " and";
-					}
-				}
-				if (selectedSclera !== "none") {
-					r += ` ${selectedSclera}`;
-					if (both) {
-						r += " sclerae";
-					} else {
-						r += " sclera";
-					}
-				}
-			}
-			if (!both) {
-				r += ` for ${player ? "your" : his} ${selectedSide} eye`;
-			}
-			r += "?";
-
-			const a = document.createElement("a");
-			a.append(player ? "Take" : "Give");
-			a.onclick = applyLink;
-			applyDiv.append(a, r);
-			if (!player) {
-				applyDiv.append(" ",
-					App.UI.DOM.makeElement("span", "This is independent from eyewear choices.", "note"));
-			}
-		}
-	}
-
-	function applyLink() {
-		// make sure the eye exists; give glass eye if there is none
-		if ((selectedSide === "left" || selectedSide === "both") && getLeftEyeType(entity) === 0) {
-			eyeSurgery(entity, "left", "glass");
-		}
-		if ((selectedSide === "right" || selectedSide === "both") && getRightEyeType(entity) === 0) {
-			eyeSurgery(entity, "right", "glass");
-		}
-
-		// apply modifications
-		setEyeColorFull(entity, selectedIris,
-			selectedPupil === "none" ? "" : selectedPupil,
-			selectedSclera === "none" ? "" : selectedSclera,
-			selectedSide);
-		cashX(forceNeg(V.modCost), "slaveMod", entity);
-
-		App.UI.reload();
-	}
-
-
-	function updateRemoveDiv() {
-		$(removeDiv).empty();
-		const links = [];
-		let _n = 0;
-		// remove lenses
-		if (hasLeftEye(entity) && getLeftEyeColor(entity) !== getGeneticEyeColor(entity, "left")) {
-			_n++;
-			links.push(removeLink("Remove left lens", () => resetEyeColor(entity, "left")));
-		}
-		if (hasRightEye(entity) && getRightEyeColor(entity) !== getGeneticEyeColor(entity, "right")) {
-			_n++;
-			links.push(removeLink("Remove right lens", () => resetEyeColor(entity, "right")));
-		}
-		if (_n === 2) {
-			links.push(removeLink("Remove both lenses", () => resetEyeColor(entity, "both")));
-		}
-		// remove glass eyes
-		_n = 0;
-		if (getLeftEyeType(entity) === 2) {
-			_n++;
-			links.push(removeLink("Remove left glass eye", () => eyeSurgery(entity, "left", "remove")));
-		}
-		if (getRightEyeType(entity) === 2) {
-			_n++;
-			links.push(removeLink("Remove right glass eye", () => eyeSurgery(entity, "right", "remove")));
-		}
-		if (_n === 2) {
-			links.push(removeLink("Remove both glass eyes", () => eyeSurgery(entity, "both", "remove")));
-		}
-		if (links.length > 0) {
-			removeDiv.append(App.UI.DOM.arrayToList(links, " | ", " | "));
-		}
-	}
-
-	function removeLink(text, callback) {
-		const a = document.createElement("a");
-		a.append(text);
-		a.onclick = () => {
-			callback();
-			App.UI.reload();
-		};
-		return a;
-	}
-};
diff --git a/src/js/physicalDevelopment.js b/src/js/physicalDevelopment.js
index f3e1e4dfabf3d32ac8006c6b0fe04b0b156e957c..09c46b5a5c5521cbe46fa33d5622e6be8005c2d1 100644
--- a/src/js/physicalDevelopment.js
+++ b/src/js/physicalDevelopment.js
@@ -54,7 +54,7 @@ globalThis.physicalDevelopment = (function physicalDevelopment() {
 				} else {
 					increaseHeightXX(slave);
 				}
-				if (slave.physicalAge === 13) {
+				if (slave.physicalAge === 13 || (slave.physicalAge > 13 && (slave.hormoneBalance >= 100 || slave.hormoneBalance <= -100))) {
 					increaseFaceXX(slave);
 					if (slave.voice > 0) {
 						increaseVoiceXX(slave);
@@ -80,7 +80,7 @@ globalThis.physicalDevelopment = (function physicalDevelopment() {
 				} else {
 					increaseHeightXY(slave);
 				}
-				if (slave.physicalAge === 13) {
+				if (slave.physicalAge === 13 || (slave.physicalAge > 13 && (slave.hormoneBalance >= 100 || slave.hormoneBalance <= -100))) {
 					increaseFaceXY(slave);
 					if (slave.voice > 1) {
 						increaseVoiceXY(slave);
diff --git a/src/js/removeActiveSlave.js b/src/js/removeActiveSlave.js
index 95ea18e2bda23cc26107f1a28c8f14fe641dbe28..eb32e2872f8dede7976205ae5c0deca11ca5f9c8 100644
--- a/src/js/removeActiveSlave.js
+++ b/src/js/removeActiveSlave.js
@@ -197,6 +197,10 @@ globalThis.removeActiveSlave = function() {
 				V.genePool.deleteAt(_geneIndex);
 			}
 		}
+		Object.values(V.missingTable).forEach(s => {
+			if (s.mother === V.activeSlave.ID || s.father === V.activeSlave.ID)
+				missing = true;
+		});
 		if (missing) {
 			V.missingTable[V.missingParentID] = {
 				slaveName: V.activeSlave.slaveName,
@@ -204,7 +208,10 @@ globalThis.removeActiveSlave = function() {
 				fullName: SlaveFullName(V.activeSlave),
 				dick: V.activeSlave.dick,
 				vagina: V.activeSlave.vagina,
-				ID: V.missingParentID
+				ID: V.missingParentID,
+				mother: V.activeSlave.mother,
+				father: V.activeSlave.father,
+				inbreedingCoeff: V.activeSlave.inbreedingCoeff
 			};
 			if (V.traitor.ID === V.activeSlave.ID) {
 				/* To link developing fetuses to their parent */
@@ -212,6 +219,12 @@ globalThis.removeActiveSlave = function() {
 			} else if (V.boomerangSlave.ID === V.activeSlave.ID) {
 				V.boomerangSlave.missingParentTag = V.missingParentID;
 			}
+			Object.values(V.missingTable).forEach(s => {
+				if (s.mother === V.activeSlave.ID)
+					s.mother = V.missingParentID;
+				if (s.father === V.activeSlave.ID)
+					s.father = V.missingParentID;
+			});
 			V.missingParentID--;
 		}
 
@@ -310,6 +323,10 @@ globalThis.removeNonNGPSlave = function(removedSlave) {
 				V.genePool.deleteAt(_geneIndex);
 			}
 		}
+		Object.values(V.missingTable).forEach(s => {
+			if (s.mother == removedSlave.ID || s.father == removedSlave.ID)
+				missing = true;
+		});
 		if (missing) {
 			V.missingTable[V.missingParentID] = {
 				slaveName: removedSlave.slaveName,
@@ -317,8 +334,17 @@ globalThis.removeNonNGPSlave = function(removedSlave) {
 				fullName: SlaveFullName(removedSlave),
 				dick: removedSlave.dick,
 				vagina: removedSlave.vagina,
-				ID: V.missingParentID
+				ID: V.missingParentID,
+				mother: removedSlave.mother,
+				father: removedSlave.father,
+				inbreedingCoeff: removedSlave.inbreedingCoeff
 			};
+			Object.values(V.missingTable).forEach(s => {
+				if (s.mother === removedSlave.ID)
+					s.mother = V.missingParentID;
+				if (s.father === removedSlave.ID)
+					s.father = V.missingParentID;
+			});
 			V.missingParentID--;
 		}
 
diff --git a/src/js/rulesAssistant.js b/src/js/rulesAssistant.js
index 375e1cc37effbad10074bf38ed1c373a6723befd..2e83d5c3624d9d67afc92498143d6d5d17df9eec 100644
--- a/src/js/rulesAssistant.js
+++ b/src/js/rulesAssistant.js
@@ -257,6 +257,7 @@ App.RA.newRule = function() {
 			scarDesign: null,
 			curatives: null,
 			livingRules: null,
+			mobilityRules: null,
 			relationshipRules: null,
 			lactationRules: null,
 			standardPunishment: null,
diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js
index 515d0590def0d001cedd1626f89cfe453b281077..a11485dba822af7fd49a55e279d97ba8a90f7fdf 100644
--- a/src/js/rulesAssistantOptions.js
+++ b/src/js/rulesAssistantOptions.js
@@ -1666,6 +1666,7 @@ globalThis.rulesAssistantOptions = (function() {
 			this.appendChild(new SpeechList());
 			this.appendChild(new RelationshipList());
 			this.appendChild(new LactationList());
+			this.appendChild(new MobilityDeviceList());
 			if (V.studio === 1) {
 				this.appendChild(new PornBroadcastStatus());
 				this.appendChild(new PornList());
@@ -2812,6 +2813,18 @@ globalThis.rulesAssistantOptions = (function() {
 		}
 	}
 
+	class MobilityDeviceList extends ListSelector {
+		constructor() {
+			const pairs = [
+				["restrictive"],
+				["permissive"],
+			];
+			super("Mobility device usage rules", pairs);
+			this.setValue(current_rule.set.mobilityRules);
+			this.onchange = (value) => current_rule.set.mobilityRules = value;
+		}
+	}
+
 	class PornBroadcastStatus extends RadioSelector {
 		constructor() {
 			const pairs = [
diff --git a/src/js/salon.js b/src/js/salon.js
new file mode 100644
index 0000000000000000000000000000000000000000..4069c507d753be823befbfcb7ebdd2f69d627b6f
--- /dev/null
+++ b/src/js/salon.js
@@ -0,0 +1,794 @@
+/**
+ * @param {App.Entity.PlayerState|App.Entity.SlaveState} entity
+ * @param {boolean} player
+ * @returns {HTMLDivElement}
+ */
+App.Medicine.Modification.eyeSelector = function(entity, player = false) {
+	const {He, him, his} = getPronouns(entity);
+
+	let selectedSide = "none";
+	let selectedIris = "none";
+	let selectedPupil = "none";
+	let selectedSclera = "none";
+
+	let removeDiv = document.createElement("div");
+	removeDiv.classList.add("choices");
+	let applyDiv = document.createElement("div");
+
+	const container = document.createElement("div");
+	container.append(
+		`${player ? "You have" : `${He} has`} ${App.Desc.eyesColorLong(entity)}, ${hasBothEyes(
+			entity) ? "they are" : "it is"} ${App.Desc.eyesType(entity)}.`,
+		removeDiv, "You have a number of contact lenses in various colors available. ",
+		App.UI.DOM.makeElement("span", `You can change what ${player ? "your" : his} eyes look like.`, "note"),
+		assembleLinks(), applyDiv
+	);
+	updateRemoveDiv();
+	updateApplyDiv();
+	return container;
+
+	function assembleLinks() {
+		const sides = ["left", "right", "both"];
+		const irisColors = ["amber", "black", "blue", "brown", "green", "hazel", "orange", "pale-grey", "pink", "red",
+			"sky-blue", "turquoise", "white", "yellow"];
+		const pupilShapes = ["none", "circular", "almond-shaped", "bright", "catlike", "demonic", "devilish",
+			"goat-like", "heart-shaped", "hypnotic", "serpent-like", "star-shaped", "teary", "vacant", "wide-eyed"];
+		const scleraColors = ["none", "white", "amber", "black", "blue", "brown", "green", "hazel", "orange",
+			"pale-grey", "pink", "red", "sky-blue", "turquoise", "yellow"];
+		const div = document.createDocumentFragment();
+		div.append(
+			assembleList("Side: ", sides, value => selectedSide = value, selectedIris),
+			assembleList("Iris: ", irisColors, value => selectedIris = value, selectedSide),
+			assembleList("Pupil: ", pupilShapes, value => selectedPupil = value, selectedPupil),
+			assembleList("Sclera: ", scleraColors, value => selectedSclera = value, selectedSclera)
+		);
+		return div;
+	}
+
+	/**
+	 * @param {string} name
+	 * @param {Array<string>} list
+	 * @param {Function}callback
+	 * @param {string} selected
+	 * @returns {HTMLDivElement}
+	 */
+	function assembleList(name, list, callback, selected) {
+		const links = [];
+
+		for (let i = 0; i < list.length; i++) {
+			addToggle(list[i], callback, links, list[i] === selected);
+		}
+
+		const div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(name, App.UI.DOM.arrayToList(links, " | ", " | "));
+		return div;
+	}
+
+	/**
+	 * @param {string} value
+	 * @param {Function} callback
+	 * @param {Array<HTMLAnchorElement>} links
+	 * @param {boolean} [disabled]
+	 */
+	function addToggle(value, callback, links, disabled = false) {
+		const a = document.createElement("a");
+		a.append(capFirstChar(value));
+		if (disabled) {
+			a.classList.add("disabled");
+		}
+		a.onclick = () => {
+			for (let link of links) {
+				link.classList.remove("disabled");
+			}
+			a.classList.add("disabled");
+			callback(value);
+			updateRemoveDiv();
+			updateApplyDiv();
+		};
+		links.push(a);
+	}
+
+	function updateApplyDiv() {
+		$(applyDiv).empty();
+		if (selectedSide !== "none" && selectedIris !== "none") {
+			// make the following easier to read
+			let both = selectedSide === "both";
+			let leftGlass = !hasLeftEye(entity) || getLeftEyeType(entity) === 2;
+			let rightGlass = !hasRightEye(entity) || getRightEyeType(entity) === 2;
+
+			// base eye
+			let r = player ? "" : ` ${him}`;
+			if (both) {
+				if (leftGlass && rightGlass) {
+					r += ` ${selectedIris} glass eyes`;
+				} else if (leftGlass || rightGlass) {
+					r += ` a glass eye and a ${selectedIris} lens`;
+				} else {
+					r += ` ${selectedIris} lenses`;
+				}
+			} else {
+				r += " a";
+				if ((selectedSide === "left" && leftGlass) || (selectedSide === "right" && rightGlass)) {
+					r += ` ${selectedIris} glass eye`;
+				} else {
+					r += ` ${selectedIris} lens`;
+				}
+			}
+			// pupil & sclera
+			if (selectedPupil !== "none" || selectedSclera !== "none") {
+				r += " with";
+				if (selectedPupil !== "none") {
+					r += ` ${both ? selectedPupil : addA(selectedPupil)}`;
+					if (both) {
+						r += " pupils";
+					} else {
+						r += " pupil";
+					}
+					if (selectedSclera !== "none") {
+						r += " and";
+					}
+				}
+				if (selectedSclera !== "none") {
+					r += ` ${selectedSclera}`;
+					if (both) {
+						r += " sclerae";
+					} else {
+						r += " sclera";
+					}
+				}
+			}
+			if (!both) {
+				r += ` for ${player ? "your" : his} ${selectedSide} eye`;
+			}
+			r += "?";
+
+			const a = document.createElement("a");
+			a.append(player ? "Take" : "Give");
+			a.onclick = applyLink;
+			applyDiv.append(a, r);
+			if (!player) {
+				applyDiv.append(" ",
+					App.UI.DOM.makeElement("span", "This is independent from eyewear choices.", "note"));
+			}
+		}
+	}
+
+	function applyLink() {
+		// make sure the eye exists; give glass eye if there is none
+		if ((selectedSide === "left" || selectedSide === "both") && getLeftEyeType(entity) === 0) {
+			eyeSurgery(entity, "left", "glass");
+		}
+		if ((selectedSide === "right" || selectedSide === "both") && getRightEyeType(entity) === 0) {
+			eyeSurgery(entity, "right", "glass");
+		}
+
+		// apply modifications
+		setEyeColorFull(entity, selectedIris,
+			selectedPupil === "none" ? "" : selectedPupil,
+			selectedSclera === "none" ? "" : selectedSclera,
+			selectedSide);
+		cashX(forceNeg(V.modCost), "slaveMod", entity);
+
+		App.UI.reload();
+	}
+
+	function updateRemoveDiv() {
+		$(removeDiv).empty();
+		const links = [];
+		let _n = 0;
+		// remove lenses
+		if (hasLeftEye(entity) && getLeftEyeColor(entity) !== getGeneticEyeColor(entity, "left")) {
+			_n++;
+			links.push(removeLink("Remove left lens", () => resetEyeColor(entity, "left")));
+		}
+		if (hasRightEye(entity) && getRightEyeColor(entity) !== getGeneticEyeColor(entity, "right")) {
+			_n++;
+			links.push(removeLink("Remove right lens", () => resetEyeColor(entity, "right")));
+		}
+		if (_n === 2) {
+			links.push(removeLink("Remove both lenses", () => resetEyeColor(entity, "both")));
+		}
+		// remove glass eyes
+		_n = 0;
+		if (getLeftEyeType(entity) === 2) {
+			_n++;
+			links.push(removeLink("Remove left glass eye", () => eyeSurgery(entity, "left", "remove")));
+		}
+		if (getRightEyeType(entity) === 2) {
+			_n++;
+			links.push(removeLink("Remove right glass eye", () => eyeSurgery(entity, "right", "remove")));
+		}
+		if (_n === 2) {
+			links.push(removeLink("Remove both glass eyes", () => eyeSurgery(entity, "both", "remove")));
+		}
+		if (links.length > 0) {
+			removeDiv.append(App.UI.DOM.arrayToList(links, " | ", " | "));
+		}
+	}
+
+	function removeLink(text, callback) {
+		const a = document.createElement("a");
+		a.append(text);
+		a.onclick = () => {
+			callback();
+			App.UI.reload();
+		};
+		return a;
+	}
+};
+
+/**
+ * Update ears in salon
+ * @param {App.Entity.SlaveState} slave
+ * @param {object} params
+ * @param {number|string} [params.primaryEarColor]
+ * @param {string} [params.secondaryEarColor]
+ * @returns {node}
+ */
+App.Medicine.Salon.ears = function(slave, {primaryEarColor = 0, secondaryEarColor = ""} = {}) {
+	const frag = new DocumentFragment();
+	let updatePrimary = (newVal) => { primaryEarColor = newVal; apply(); };
+	let updateSecondary = (newVal) => { secondaryEarColor = newVal; apply(); };
+
+	if (slave.earT !== "none" && slave.earTColor !== "hairless") {
+		const {His, his} = getPronouns(slave);
+		let div;
+		let p;
+		frag.append(`${His} fluffy ears are ${slave.earTColor}.`);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		if (slave.earTColor !== slave.hColor) {
+			div.append(
+				App.UI.DOM.link(
+					"Match current hair",
+					() => {
+						slave.earTColor = slave.hColor;
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						apply();
+					}
+				)
+			);
+			div.append(" or ");
+			App.UI.DOM.appendNewElement("span", div, "choose a new one: ", "note");
+		} else {
+			App.UI.DOM.appendNewElement("span", div, `Choose a dye color before dyeing ${his} ears:`, "note");
+		}
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Colors:`);
+		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Highlights:`);
+		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
+		frag.append(div);
+
+		if (primaryEarColor !== 0) {
+			p = document.createElement("p");
+			p.classList.add("choices");
+			p.append(
+				App.UI.DOM.link(
+					`Color ${his} ears`,
+					() => {
+						slave.earTColor = (primaryEarColor + secondaryEarColor);
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						App.Medicine.Salon.ears(slave); // discard selections after locking them in.
+					}
+				)
+			);
+			p.append(` ${primaryEarColor}${secondaryEarColor} now?`);
+			frag.append(p);
+		}
+	}
+	return jQuery("#salonEars").empty().append(frag);
+
+	function createList(array, method) {
+		const links = [];
+		for (const item of array) {
+			const title = item.title || capFirstChar(item.value);
+			links.push(
+				App.UI.DOM.link(
+					title,
+					() => method(item.value)
+				)
+			);
+		}
+		return App.UI.DOM.generateLinksStrip(links);
+	}
+	function apply() {
+		App.Art.refreshSlaveArt(slave, 3, "artFrame");
+		App.Medicine.Salon.ears(
+			slave,
+			{
+				primaryEarColor: primaryEarColor,
+				secondaryEarColor: secondaryEarColor,
+			}
+		);
+	}
+};
+
+/**
+ * Update hair in salon
+ * @param {App.Entity.SlaveState} slave
+ * @param {object} params
+ * @param {number|string} [params.primaryHairColor]
+ * @param {string} [params.secondaryHairColor]
+ * @returns {node}
+ */
+App.Medicine.Salon.hair = function(slave, {primaryHairColor = 0, secondaryHairColor = ""} = {}) {
+	const frag = new DocumentFragment();
+	let updatePrimary = (newVal) => { primaryHairColor = newVal.value; apply(); };
+	let updateSecondary = (newVal) => { secondaryHairColor = newVal.value; apply(); };
+	const {His, his, He, him} = getPronouns(slave);
+
+	if (slave.bald !== 1) {
+		frag.append(hairDye());
+		frag.append(hairStyle());
+		frag.append(hairLength());
+		frag.append(hairMaint());
+	} else {
+		// Bald
+		if (slave.hStyle === "bald") {
+			frag.append(`${He} is completely bald.`);
+		} else {
+			frag.append(wigDye());
+		}
+		frag.append(wigStyle());
+		frag.append(wigLength());
+	}
+	return jQuery("#salonHair").empty().append(frag);
+
+	function hairDye() {
+		const frag = new DocumentFragment();
+		let div;
+		let p;
+		frag.append(`${His} hair is ${slave.hColor}.`);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		if (slave.origHColor !== slave.hColor) {
+			div.append(
+				App.UI.DOM.link(
+					"Restore natural color",
+					() => {
+						slave.hColor = slave.origHColor;
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						apply();
+					}
+				)
+			);
+			div.append(" or ");
+			App.UI.DOM.appendNewElement("span", div, "choose a new one: ", "note");
+		} else {
+			App.UI.DOM.appendNewElement("span", div, `Choose a dye color before dyeing ${his} hair:`, "note");
+		}
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Colors:`);
+		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Highlights:`);
+		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
+		frag.append(div);
+
+		if (primaryHairColor !== 0) {
+			p = document.createElement("p");
+			p.classList.add("choices");
+			p.append(
+				App.UI.DOM.link(
+					`Color ${his} hair`,
+					() => {
+						slave.hColor = (primaryHairColor + secondaryHairColor);
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						App.Medicine.Salon.hair(slave); // discard selections after locking them in.
+					}
+				)
+			);
+			p.append(` ${primaryHairColor}${secondaryHairColor} now?`);
+			frag.append(p);
+		}
+		return frag;
+	}
+
+	function hairStyle() {
+		const frag = new DocumentFragment();
+		let div;
+		let method;
+		div = document.createElement("div");
+		if (slave.hStyle !== "shaved") {
+			div.append(`${His} ${slave.hStyle} hair is ${lengthToEitherUnit(slave.hLength)} long. `);
+		} else {
+			div.append(`${His} hair is shaved smooth. `);
+		}
+		App.UI.DOM.appendNewElement("span", div, `General hairstyles will conform to hair length and clothing choices.`, "note");
+		frag.append(div);
+
+		// Normal styles
+		div = document.createElement("div");
+		div.classList.add("choices");
+		method = (newVal) => {
+			slave.hStyle = newVal.value;
+			cashX(forceNeg(V.modCost), "slaveMod", slave);
+			apply();
+		};
+		if (slave.hLength > 1) {
+			div.append(`Style ${his} hair:`);
+			div.append(createList(App.Medicine.Modification.hairStyles.Normal, method));
+		} else {
+			App.UI.DOM.appendNewElement("span", div, `${His} hair is too short to style meaningfully`, "note");
+		}
+		frag.append(div);
+
+		// Short styles, includes cutting
+		div = document.createElement("div");
+		div.classList.add("choices");
+		method = (newVal) => {
+			slave.hStyle = newVal.value;
+			slave.hLength = newVal.hLength;
+			cashX(forceNeg(V.modCost), "slaveMod", slave);
+			apply();
+		};
+		if (slave.hLength > 1) {
+			div.append(`Cut and style ${his} hair:`);
+			div.append(createList(App.Medicine.Modification.hairStyles.Cut, method));
+		}
+		frag.append(div);
+
+		return frag;
+	}
+
+	function hairLength() {
+		const frag = new DocumentFragment();
+		let div = document.createElement("div");
+		div.classList.add("choices");
+		let method = (newVal) => {
+			if (newVal.hasOwnProperty("onApplication")) {
+				newVal.onApplication(slave);
+			}
+			if (newVal.hasOwnProperty("hLength")) {
+				slave.hLength = newVal.hLength;
+			}
+			apply();
+		};
+		const oldHLength = (V.showInches === 2) ? Math.round(slave.hLength / 2.54) : slave.hLength;
+
+		App.UI.DOM.appendNewElement("span", div, `Cut or lengthen ${his} hair:`);
+		div.append(createList(App.Medicine.Modification.hairStyles.Length, method));
+		div.append(" | Custom length: ");
+		div.append(
+			App.UI.DOM.makeTextBox(
+				oldHLength,
+				v => {
+					v = Math.max(v, 0); // Positive hair length only
+					// If they entered "inches," convert
+					if (V.showInches === 2) {
+						v = Math.round(v * 2.54);
+					}
+					slave.hLength = v;
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					apply();
+				},
+				true
+			)
+		);
+		if (V.showInches === 1) {
+			div.append(`cm (${cmToInchString(slave.hLength)})`);
+		} else if (V.showInches === 2) {
+			div.append(`inches`);
+		}
+
+		frag.append(div);
+
+		return frag;
+	}
+
+	function hairMaint() {
+		let div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Have ${his} hair carefully maintained at its current length: `);
+		let haircuts;
+		let text;
+		if (slave.haircuts === 1) {
+			text = "Cease maintenance";
+			haircuts = 0;
+		} else {
+			text = "Begin maintenance";
+			haircuts = 1;
+		}
+		div.append(
+			App.UI.DOM.link(
+				text,
+				() => slave.haircuts = haircuts
+			)
+		);
+		return div;
+	}
+
+	function wigDye() {
+		const frag = new DocumentFragment();
+		let div;
+		let p;
+		frag.append(`${His} current wig is ${slave.hColor}. `);
+
+		if (slave.hStyle !== "bald") {
+			frag.append(
+				App.UI.DOM.link(
+					"Remove wig",
+					() => {
+						slave.hStyle = "bald";
+						slave.hLength = 0;
+						// I'm not going to charge you for taking off a fucking wig.
+						apply();
+					}
+				)
+			);
+			frag.append(" or ");
+			App.UI.DOM.appendNewElement("span", frag, "choose a new one: ", "note");
+		} else {
+			App.UI.DOM.appendNewElement("span", frag, `Choose a wig color:`, "note");
+		}
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Colors:`);
+		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Highlights:`);
+		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
+		frag.append(div);
+
+		if (primaryHairColor !== 0) {
+			p = document.createElement("p");
+			p.classList.add("choices");
+			p.append(
+				App.UI.DOM.link(
+					`Change`,
+					() => {
+						slave.earTColor = (primaryHairColor + secondaryHairColor);
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						App.Medicine.Salon.hair(slave); // discard selections after locking them in.
+					}
+				)
+			);
+			p.append(` ${his} wig color to ${primaryHairColor}${secondaryHairColor} now?`);
+			frag.append(p);
+		}
+		return frag;
+	}
+
+	function wigLength() {
+		const frag = new DocumentFragment();
+		if (slave.hStyle === "bald") {
+			return frag;
+		}
+		let div = document.createElement("div");
+		div.classList.add("choices");
+		const array = [];
+		for (const number of [10, 30, 60, 90, 120, 150]) {
+			const obj = {};
+			obj.title = lengthToEitherUnit(number);
+			obj.hLength = number;
+			array.push(obj);
+		}
+		let method = (newVal) => {
+			slave.hLength = newVal.hLength;
+			apply();
+		};
+		const oldHLength = (V.showInches === 2) ? Math.round(slave.hLength / 2.54) : slave.hLength;
+		App.UI.DOM.appendNewElement("span", div, `Set wig length to:`, "choices");
+		div.append(createList(array, method));
+		div.append(" | Custom length: ");
+		div.append(
+			App.UI.DOM.makeTextBox(
+				oldHLength,
+				v => {
+					v = Math.max(v, 10); // Wigs must be at least 10 cm
+					// If they entered "inches," convert
+					if (V.showInches === 2) {
+						v = Math.round(v * 2.54);
+					}
+					slave.hLength = v;
+					cashX(forceNeg(V.modCost), "slaveMod", slave);
+					apply();
+				},
+				true
+			)
+		);
+		if (V.showInches === 1) {
+			div.append(`cm (${cmToInchString(slave.hLength)})`);
+		} else if (V.showInches === 2) {
+			div.append(`inches`);
+		}
+
+		frag.append(div);
+
+		return frag;
+	}
+
+	function wigStyle() {
+		const frag = new DocumentFragment();
+		let div = document.createElement("div");
+		div.classList.add("choices");
+		const method = (newVal) => {
+			slave.hStyle = newVal.value;
+			cashX(forceNeg(V.modCost), "slaveMod", slave);
+			apply();
+		};
+
+		if (slave.hStyle !== "bald") {
+			frag.append(`${His} ${slave.hStyle} wig is ${lengthToEitherUnit(slave.hLength)} long. `);
+		} else {
+			frag.append(`${He} is not wearing a wig. `);
+		}
+		App.UI.DOM.appendNewElement("span", frag, `General hairstyles will conform to hair length and clothing choices.`, "note");
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		if (slave.hStyle === "bald") {
+			div.append(`Give ${him} a wig:`);
+		} else {
+			div.append(`Set wig style:`);
+		}
+		div.append(createList(App.Medicine.Modification.hairStyles.Normal, method));
+		frag.append(div);
+		return frag;
+	}
+
+	function createList(array, method) {
+		const links = [];
+		for (const item of array) {
+			if (item.hasOwnProperty("requirements")) {
+				if (item.requirements(slave) === false) {
+					continue;
+				}
+			}
+			const title = item.title || capFirstChar(item.value);
+			links.push(
+				App.UI.DOM.link(
+					title,
+					() => method(item)
+				)
+			);
+		}
+		return App.UI.DOM.generateLinksStrip(links);
+	}
+
+	function apply() {
+		App.Art.refreshSlaveArt(slave, 3, "artFrame");
+		App.Medicine.Salon.hair(
+			slave,
+			{
+				primaryHairColor: primaryHairColor,
+				secondaryHairColor: secondaryHairColor,
+			}
+		);
+	}
+};
+
+/**
+ * Update hair in salon
+ * @param {App.Entity.SlaveState} slave
+ * @param {object} params
+ * @param {number|string} [params.primaryTailColor]
+ * @param {string} [params.secondaryTailColor]
+ * @returns {node}
+ */
+App.Medicine.Salon.tail = function(slave, {primaryTailColor = 0, secondaryTailColor = ""} = {}) {
+	const frag = new DocumentFragment();
+	let updatePrimary = (newVal) => { primaryTailColor = newVal.value; apply(); };
+	let updateSecondary = (newVal) => { secondaryTailColor = newVal.value; apply(); };
+	const {His, his} = getPronouns(slave);
+
+	if (slave.tail !== "none") {
+		frag.append(tailDye());
+	}
+
+	return jQuery("#salonTail").empty().append(frag);
+
+	function tailDye() {
+		const frag = new DocumentFragment();
+		let div;
+		let p;
+		frag.append(`${His} tail is ${slave.tailColor}.`);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		if (slave.origHColor !== slave.hColor) {
+			div.append(
+				App.UI.DOM.link(
+					"Match current hair",
+					() => {
+						slave.tailColor = slave.hColor;
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						apply();
+					}
+				)
+			);
+			div.append(" or ");
+			App.UI.DOM.appendNewElement("span", div, "choose a new one: ", "note");
+		} else {
+			App.UI.DOM.appendNewElement("span", div, `Choose a dye color before dyeing ${his} tail:`, "note");
+		}
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Colors:`);
+		div.append(createList(App.Medicine.Modification.Color.Primary, updatePrimary));
+		frag.append(div);
+
+		div = document.createElement("div");
+		div.classList.add("choices");
+		div.append(`Highlights:`);
+		div.append(createList(App.Medicine.Modification.Color.Secondary, updateSecondary));
+		frag.append(div);
+
+		if (primaryTailColor !== 0) {
+			p = document.createElement("p");
+			p.classList.add("choices");
+			p.append(
+				App.UI.DOM.link(
+					`Color ${his} tail`,
+					() => {
+						slave.tailColor = (primaryTailColor + secondaryTailColor);
+						App.Art.refreshSlaveArt(slave, 3, "artFrame");
+						cashX(forceNeg(V.modCost), "slaveMod", slave);
+						App.Medicine.Salon.tail(slave); // discard selections after locking them in.
+					}
+				)
+			);
+			p.append(` ${primaryTailColor}${secondaryTailColor} now?`);
+			frag.append(p);
+		}
+		return frag;
+	}
+
+	function createList(array, method) {
+		const links = [];
+		for (const item of array) {
+			if (item.hasOwnProperty("requirements")) {
+				if (item.requirements(slave) === false) {
+					continue;
+				}
+			}
+			const title = item.title || capFirstChar(item.value);
+			links.push(
+				App.UI.DOM.link(
+					title,
+					() => method(item)
+				)
+			);
+		}
+		return App.UI.DOM.generateLinksStrip(links);
+	}
+
+	function apply() {
+		App.Art.refreshSlaveArt(slave, 3, "artFrame");
+		App.Medicine.Salon.tail(
+			slave,
+			{
+				primaryTailColor: primaryTailColor,
+				secondaryTailColor: secondaryTailColor,
+			}
+		);
+	}
+};
diff --git a/src/js/sexActsJS.js b/src/js/sexActsJS.js
index 3c6eebf99d5ccb55d3eb137dfbfe766c1c76f7ac..095ba03a20b227056e8f3d2b9fcc75056c472b2d 100644
--- a/src/js/sexActsJS.js
+++ b/src/js/sexActsJS.js
@@ -86,11 +86,11 @@ globalThis.VCheck = (function() {
 				if (slave.tankBaby === 2) {
 					r += `${He} thinks of losing ${his} virginity to ${his} ${WrittenMaster(slave)} a <span class="hotpink">necessity to be happy.</span> ${He} expects ${his} pussy to be seeing a lot more attention in the future.`;
 				} else {
-					r += `<span class="hotpink">${He} enjoys losing ${his} cherry to you.</span> `;
+					r += `<span class="hotpink">${He} enjoys losing ${his} cherry to you,</span> and `;
 					if ((slave.fetishKnown && slave.fetish === "pregnancy") || (slave.energy > 95) || (slave.attrXY >= 85 && V.PC.dick > 0)) {
-						r += `${He} can't wait to have ${his} pussy fucked by you again.`;
+						r += `can't wait to have ${his} pussy fucked by you again.`;
 					} else {
-						r += `${He} looks forward to having ${his} pussy fucked by you again.`;
+						r += `looks forward to having ${his} pussy fucked by you again.`;
 					}
 				}
 				slave.devotion += 4;
diff --git a/src/js/slaveExpenses.js b/src/js/slaveExpenses.js
index 9327b167f0a27bd01feb87deb0025fc4658a47f3..73b7cb5b81ea0d50c72c310e4e22ce6c85883c58 100644
--- a/src/js/slaveExpenses.js
+++ b/src/js/slaveExpenses.js
@@ -214,15 +214,18 @@ globalThis.slaveImpactLongTerm = function(slave) {
 				p.append(div);
 			} else {
 				/* lowercasedonkey: TODO: I don't like how vague my placeholder is. Probably need to set up some kind of sliding scale to describe how much rep (roughly) she has made or lost. Need to get a sense of common ranges. */
-				text = `Overall, ${he} has `;
+				div = document.createElement("div");
+				div.classList.add("indent");
+				div.append(`Overall, ${he} has `);
 				if (_repTotal === 0) {
-					text += `had no impact on your reputation.`;
+					div.append(`had no impact on `);
 				} else if (_repTotal > 0) {
-					text += `<span class="green">increased</span> your reputation.`;
+					App.UI.DOM.appendNewElement("span", div, `increased `, "green");
 				} else if (_repTotal < 0) {
-					text += `<span class="red">decreased</span> your reputation.`;
+					App.UI.DOM.appendNewElement("span", div, `decreased `, "red");
 				}
-				App.UI.DOM.appendNewElement("div", p, text, "indent");
+				div.append(`your reputation.`);
+				p.append(div);
 			}
 		}
 		frag.append(p);
diff --git a/src/js/slaveListing.js b/src/js/slaveListing.js
index dec491595859e5549737ae4d1043c15a801caa7a..8052c5bd80847693811a8fe70788bb23d958ffcb 100644
--- a/src/js/slaveListing.js
+++ b/src/js/slaveListing.js
@@ -29,18 +29,10 @@ App.UI.SlaveList.render = function() {
 	// potentially can be a problem if played long enough to reach Number.MAX_SAFE_INTEGER
 	let listID = Number.MIN_SAFE_INTEGER;
 
-	/** @type {Object.<number, Node>}*/
-	const readyResults = {
-
-	};
-
 	/** @type {App.Art.SlaveArtBatch?} */
 	let batchRenderer = null;
 
-	return {
-		listMarkup: listMarkup,
-		listDOM: listDOM
-	};
+	return listDOM;
 
 	/**
 	 * @param {number[]} IDs
@@ -122,37 +114,6 @@ App.UI.SlaveList.render = function() {
 		return res;
 	}
 
-	/**
-	 * @param {number[]} IDs
-	 * @param {Array.<{id: number, rejects: string[]}>} rejectedSlaves
-	 * @param {slaveToElement} interactionLink
-	 * @param {slaveToElement} [postNote]
-	 * @returns {string}
-	 */
-	function listMarkup(IDs, rejectedSlaves, interactionLink, postNote) {
-		const listIDStr = `slave-list-${listID}`;
-		readyResults[listID] = listDOM(IDs, rejectedSlaves, interactionLink, postNote);
-
-		$(document).one(':passagedisplay', function() {
-			for (const e of document.querySelectorAll('[id^=slave-list]')) {
-				const id = e.getAttribute("data-listId");
-				if (readyResults.hasOwnProperty(id)) {
-					if (e.childNodes.length > 0) {
-						e.innerHTML = '';
-					}
-					e.appendChild(readyResults[id]);
-					delete readyResults[id];
-				}
-			}
-		});
-
-		let resMarkup = `<div id="${listIDStr}" data-listId="${listID}"></div>`;
-		++listID;
-
-		return resMarkup;
-	}
-
-
 	/**
 	 * @param {number} id
 	 * @param {slaveToElement} interactionLink
@@ -356,7 +317,7 @@ App.UI.SlaveList.render = function() {
 		}
 		res.appendChild(document.createTextNode(' '));
 
-		if ((V.displayAssignments === 1) && (passageName === "Main") && (slave.ID !== V.HeadGirl.ID) && (slave.ID !== V.Recruiter.ID) && (slave.ID !== V.Bodyguard.ID)) {
+		if ((V.displayAssignments === 1) && (passageName === "Main") && (slave.ID !== V.HeadGirlID) && (slave.ID !== V.RecruiterID) && (slave.ID !== V.BodyguardID)) {
 			res.appendChild(App.UI.jobLinks.assignmentsFragment(slave.ID, "Main", (slave, assignment) => { App.UI.SlaveList.ScrollPosition.record(); assignJob(slave, assignment); }));
 		}
 		if (showTransfers) {
@@ -615,31 +576,11 @@ App.UI.selectSlaveForPersonalAttention = function(id) {
 	SugarCube.Engine.play("Personal Attention Select");
 };
 
-/**
- * Generates fragment with sorting options, that link to the given passage
- * @param {string} passage The passage to link to
- * @returns {string}
- */
-App.UI.SlaveList.sortingLinks = function(passage) {
-	let r = '&nbsp;&nbsp;&nbsp;&nbsp;Sort by: ';
-	let textify = (cc) => capFirstChar(cc.replace(/([A-Z])/g, " $1"));
-	r += ["devotion", "name", "assignment", "seniority", "actualAge", "visualAge", "physicalAge", "weeklyIncome"]
-		.map(so => V.sortSlavesBy !== so ?
-			App.UI.passageLink(textify(so), passage, `$sortSlavesBy = "${so}"`) : textify(so))
-		.join("&thinsp;|&thinsp;");
-
-	r += '&nbsp;&nbsp;&nbsp;&nbsp;Sort: ';
-	r += ["descending", "ascending"].map(so => V.sortSlavesOrder !== so ?
-		App.UI.passageLink(capFirstChar(so), passage, `$sortSlavesOrder = "${so}"`) : capFirstChar(so))
-		.join("&thinsp;|&thinsp;");
-	return r;
-};
-
 /**
  * @param {string} passage
- * @returns {*}
+ * @returns {HTMLElement}
  */
-App.UI.SlaveList.sortingLinksDOM = function(passage) {
+App.UI.SlaveList.sortingLinks = function(passage) {
 	const outerDiv = document.createElement("div");
 	outerDiv.classList.add("flex-container");
 	const textify = string => capFirstChar(string.replace(/([A-Z])/g, " $1"));
@@ -666,7 +607,7 @@ App.UI.SlaveList.sortingLinksDOM = function(passage) {
  * @param {string} [facilityPassage]
  * @param {boolean} [showTransfersTab=false]
  * @param {{assign: string, remove: string, transfer: (string| undefined)}} [tabCaptions]
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, showTransfersTab = false, tabCaptions = undefined) {
 	const job = facility.job();
@@ -677,30 +618,31 @@ App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, show
 		remove: 'Remove a slave',
 		transfer: 'Transfer from Facility'
 	};
-	let r = '';
+	const frag = document.createDocumentFragment();
 	if (V.sortSlavesMain) {
-		r += this.sortingLinks(facilityPassage) + '<br>';
+		frag.append(this.sortingLinks(facilityPassage));
 	}
-	r += '<div class="tabbar">' +
-		App.UI.tabbar.tabButton('assign', tabCaptions.assign) +
-		App.UI.tabbar.tabButton('remove', tabCaptions.remove) +
-		(showTransfersTab ? App.UI.tabbar.tabButton('transfer', tabCaptions.transfer) : '') +
-		'</div>';
+	const tabbar = App.UI.DOM.appendNewElement("div", frag, '', "tabbar");
+	tabbar.append(
+		App.UI.tabbar.tabButtonDOM('assign', tabCaptions.assign),
+		App.UI.tabbar.tabButtonDOM('remove', tabCaptions.remove),
+		(showTransfersTab ? App.UI.tabbar.tabButtonDOM('transfer', tabCaptions.transfer) : '')
+	);
 
 	const facilitySlaves = [...job.employeesIDs()];
 	if (facilitySlaves.length > 0) {
 		SlaveSort.IDs(facilitySlaves);
-		r += App.UI.tabbar.makeTab("remove", App.UI.SlaveList.render.listMarkup(facilitySlaves, [],
+		frag.append(App.UI.tabbar.makeTabDOM("remove", App.UI.SlaveList.render(facilitySlaves, [],
 			App.UI.SlaveList.SlaveInteract.stdInteract,
 			(slave) => App.UI.DOM.link(`Retrieve ${getPronouns(slave).object} from ${facility.name}`, () => removeJob(slave, job.desc.assignment), [], facilityPassage)
-		));
+		)));
 	} else {
-		r += App.UI.tabbar.makeTab("remove", `<em>${capFirstChar(facility.name)} is empty for the moment</em>`);
+		frag.append(App.UI.tabbar.makeTabDOM("remove", App.UI.DOM.makeElement("em", `${capFirstChar(facility.name)} is empty for the moment`)));
 	}
 
 	/**
 	 * @param {number[]} slaveIDs
-	 * @returns {string}
+	 * @returns {DocumentFragment}
 	 */
 	function assignableTabContent(slaveIDs) {
 		SlaveSort.IDs(slaveIDs);
@@ -714,7 +656,7 @@ App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, show
 				passedSlaves.push(id);
 			}
 		}, []);
-		return App.UI.SlaveList.render.listMarkup(passedSlaves, rejectedSlaves,
+		return App.UI.SlaveList.render(passedSlaves, rejectedSlaves,
 			App.UI.SlaveList.SlaveInteract.stdInteract,
 			(slave) => App.UI.DOM.link(`Send ${getPronouns(slave).object} to ${facility.name}`, () => { assignmentTransition(slave, job.desc.assignment, facilityPassage); }));
 	}
@@ -722,9 +664,9 @@ App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, show
 		const assignableSlaveIDs = job.desc.partTime ?
 			V.slaves.map(slave => slave.ID) : // all slaves can work here
 			[...App.Entity.facilities.penthouse.employeesIDs()]; // only slaves from the penthouse can be transferred here
-		r += App.UI.tabbar.makeTab("assign", assignableTabContent(assignableSlaveIDs));
+		frag.append(App.UI.tabbar.makeTabDOM("assign", assignableTabContent(assignableSlaveIDs)));
 	} else {
-		r += App.UI.tabbar.makeTab("assign", `<strong>${capFirstChar(facility.name)} is full and cannot hold any more slaves</strong>`);
+		frag.append(App.UI.tabbar.makeTabDOM("assign", App.UI.DOM.makeElement("strong", `${capFirstChar(facility.name)} is full and cannot hold any more slaves`)));
 	}
 
 	if (showTransfersTab) {
@@ -736,14 +678,14 @@ App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, show
 				}
 				return acc;
 			}, []);
-			r += App.UI.tabbar.makeTab("transfer", assignableTabContent(transferableIDs));
+			frag.append(App.UI.tabbar.makeTabDOM("transfer", assignableTabContent(transferableIDs)));
 		} else {
-			r += App.UI.tabbar.makeTab("transfer", `<strong>${capFirstChar(facility.name)} is full and cannot hold any more slaves</strong>`);
+			frag.append(App.UI.tabbar.makeTabDOM("transfer", App.UI.DOM.makeElement("strong", `${capFirstChar(facility.name)} is full and cannot hold any more slaves`)));
 		}
 	}
 	App.UI.tabbar.handlePreSelectedTab();
 
-	return r;
+	return frag;
 };
 
 /**
@@ -761,77 +703,75 @@ App.UI.SlaveList.makeNameDecorator = function(classNames) {
 };
 
 /**
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 App.UI.SlaveList.listNGPSlaves = function() {
 	const thisPassage = 'New Game Plus';
-	let r = this.sortingLinks(thisPassage) + '<br>';
-
-	r += '<div class="tabbar">' +
-		App.UI.tabbar.tabButton('assign', 'Import a slave') +
-		App.UI.tabbar.tabButton('remove', 'Remove from import') +
-		'</div>';
-
-	const NGPassignment = Job.IMPORTED;
-	/** @type {App.Entity.SlaveState[]} */
-	const slaves = V.slaves;
 
-	/* handle the legacy assignment string */
-	for (const slave of slaves) {
+	const frag = document.createDocumentFragment();
+	frag.append(this.sortingLinks(thisPassage));
+	const tabbar = App.UI.DOM.appendNewElement("div", frag, '', "tabbar");
+	tabbar.append(
+		App.UI.tabbar.tabButtonDOM('assign', "Import a slave"),
+		App.UI.tabbar.tabButtonDOM('remove', "Remove from import")
+	);
+
+	let imported = [];
+	let nonImported = [];
+	for (const slave of V.slaves) {
+		/* handle the legacy assignment string */
 		if (slave.assignment === "be imported") {
 			slave.assignment = Job.IMPORTED;
 		}
+		if (slave.assignment === Job.IMPORTED) {
+			imported.push(slave.ID);
+		} else {
+			nonImported.push(slave.ID);
+		}
 	}
 
-	if (V.slavesToImport > 0) {
-		const importedSlavesIDs = slaves.reduce((acc, s) => {
-			if (s.assignment === NGPassignment) {
-				acc.push(s.ID);
-			}
-			return acc;
-		}, []);
-		SlaveSort.IDs(importedSlavesIDs);
-		r += App.UI.tabbar.makeTab("remove", App.UI.SlaveList.render.listMarkup(importedSlavesIDs, [],
+	if (imported.length > 0) {
+		SlaveSort.IDs(imported);
+		frag.append(App.UI.tabbar.makeTabDOM("remove", App.UI.SlaveList.render(imported, [],
 			App.UI.SlaveList.makeNameDecorator(["emphasizedSlave", "pink"]),
-			(s) => App.UI.DOM.passageLink('Remove from import list', thisPassage,
-				() => { V.slavesToImport -= 1; removeJob(s, NGPassignment); }
-			)));
+			(s) => App.UI.DOM.passageLink('Remove from import list', thisPassage, () => removeJob(s, Job.IMPORTED))
+		)));
 	} else {
-		r += App.UI.tabbar.makeTab("remove", `<em>No slaves will go with you to the new game</em>`);
+		frag.append(App.UI.tabbar.makeTabDOM("remove", App.UI.DOM.makeElement('em', "No slaves will go with you to the new game")));
 	}
 
-	if (V.slavesToImport < V.slavesToImportMax) {
-		const slavesToImportIDs = slaves.reduce((acc, s) => { if (s.assignment !== NGPassignment) { acc.push(s.ID); } return acc; }, []);
-		SlaveSort.IDs(slavesToImportIDs);
-		r += App.UI.tabbar.makeTab("assign", App.UI.SlaveList.render.listMarkup(slavesToImportIDs, [],
+	if (imported.length < V.slavesToImportMax) {
+		SlaveSort.IDs(nonImported);
+		frag.append(App.UI.tabbar.makeTabDOM("assign", App.UI.SlaveList.render(nonImported, [],
 			App.UI.SlaveList.makeNameDecorator(["emphasizedSlave", "pink"]),
-			(s) => App.UI.DOM.passageLink('Add to import list', thisPassage,
-				() => { V.slavesToImport += 1; assignJob(s, NGPassignment); }
-			)));
+			(s) => App.UI.DOM.passageLink('Add to import list', thisPassage, () => assignJob(s, Job.IMPORTED))
+		)));
 	} else {
-		r += App.UI.tabbar.makeTab("assign", `<strong>Slave import limit reached</strong>`);
+		frag.append(App.UI.tabbar.makeTabDOM("assign", App.UI.DOM.makeElement('strong', `Slave import limit reached`)));
 	}
 
 	App.UI.tabbar.handlePreSelectedTab();
-	return r;
+	return frag;
 };
 
 /**
  * Renders facility manager summary or a note with a link to select one
  * @param {App.Entity.Facilities.Facility} facility
  * @param {string} [selectionPassage] passage name for manager selection. "${Manager} Select" if omitted
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 App.UI.SlaveList.displayManager = function(facility, selectionPassage) {
 	const managerCapName = capFirstChar(facility.desc.manager.position);
 	selectionPassage = selectionPassage || `${managerCapName} Select`;
 	const manager = facility.manager.currentEmployee;
 	if (manager) {
-		return this.render.listMarkup([manager.ID], [],
+		return this.render([manager.ID], [],
 			App.UI.SlaveList.SlaveInteract.stdInteract,
 			() => App.UI.DOM.passageLink(`Change or remove ${managerCapName}`, selectionPassage));
 	} else {
-		return `You do not have a slave serving as a ${managerCapName}. ${App.UI.passageLink(`Appoint one`, selectionPassage, "")}`;
+		const frag = document.createDocumentFragment();
+		frag.append(`You do not have a slave serving as a ${managerCapName}. `, App.UI.DOM.passageLink(`Appoint one`, selectionPassage));
+		return frag;
 	}
 };
 
@@ -839,10 +779,13 @@ App.UI.SlaveList.displayManager = function(facility, selectionPassage) {
  * Displays standard facility page with manager and list of workers
  * @param {App.Entity.Facilities.Facility} facility
  * @param {boolean} [showTransfersPage]
- * @returns {string}
+ * @returns {DocumentFragment}
  */
 App.UI.SlaveList.stdFacilityPage = function(facility, showTransfersPage) {
-	return this.displayManager(facility) + '<br><br>' + this.listSJFacilitySlaves(facility, passage(), showTransfersPage);
+	const frag = this.displayManager(facility);
+	frag.append(document.createElement('br')); // TODO: replace with margin on one of the divs?
+	frag.append(this.listSJFacilitySlaves(facility, passage(), showTransfersPage));
+	return frag;
 };
 
 App.UI.SlaveList.penthousePage = function() {
@@ -853,9 +796,8 @@ App.UI.SlaveList.penthousePage = function() {
 		const A = V.arcologies[0];
 
 		let slaveWrapper = document.createElement("div"); // first is a div so we have no space between slave and buttons
-		if (V.HeadGirl) {
-			/** @type {App.Entity.SlaveState} */
-			const HG = V.HeadGirl;
+		if (V.HeadGirlID) {
+			const HG = S.HeadGirl;
 			slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(HG), "slave-name"),
 				" is serving as your Head Girl");
 			if (A.FSEgyptianRevivalistLaw === 1) {
@@ -865,7 +807,7 @@ App.UI.SlaveList.penthousePage = function() {
 			const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Head Girl", "HG Select"), "major-link");
 			link.id = "manageHG";
 			slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("HG Select"), "hotkey"));
-			slaveWrapper.append(App.UI.SlaveList.render.listDOM([HG.ID], [],
+			slaveWrapper.append(App.UI.SlaveList.render([HG.ID], [],
 				App.UI.SlaveList.SlaveInteract.penthouseInteract));
 		} else {
 			if (V.slaves.length > 1) {
@@ -887,9 +829,9 @@ App.UI.SlaveList.penthousePage = function() {
 		fragment.append(slaveWrapper);
 
 		slaveWrapper = document.createElement("p");
-		if (V.Recruiter) {
+		if (V.RecruiterID) {
 			/** @type {App.Entity.SlaveState} */
-			const RC = V.Recruiter;
+			const RC = S.Recruiter;
 			const {he} = getPronouns(RC);
 			slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(RC), "slave-name"),
 				" is working");
@@ -908,7 +850,7 @@ App.UI.SlaveList.penthousePage = function() {
 			const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Recruiter", "Recruiter Select"), "major-link");
 			link.id = "manageRecruiter";
 			slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Recruiter Select"), "hotkey"));
-			slaveWrapper.append(App.UI.SlaveList.render.listDOM([RC.ID], [],
+			slaveWrapper.append(App.UI.SlaveList.render([RC.ID], [],
 				App.UI.SlaveList.SlaveInteract.penthouseInteract));
 		} else {
 			slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Recruiter. ",
@@ -923,15 +865,14 @@ App.UI.SlaveList.penthousePage = function() {
 
 		if (V.dojo) {
 			slaveWrapper = document.createElement("p");
-			/** @type {App.Entity.SlaveState} */
-			const BG = V.Bodyguard;
+			const BG = S.Bodyguard;
 			if (BG) {
 				slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(BG), "slave-name"),
 					" is serving as your bodyguard. ");
 				const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Bodyguard", "BG Select"), "major-link");
 				link.id = "manageBG";
 				slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("BG Select"), "hotkey"));
-				slaveWrapper.append(App.UI.SlaveList.render.listDOM([BG.ID], [],
+				slaveWrapper.append(App.UI.SlaveList.render([BG.ID], [],
 					App.UI.SlaveList.SlaveInteract.penthouseInteract));
 				slaveWrapper.append(App.MainView.useGuard());
 			} else {
@@ -962,7 +903,7 @@ App.UI.SlaveList.penthousePage = function() {
 		SlaveSort.IDs(employeesIDs);
 		return {
 			n: employeesIDs.length,
-			dom: App.UI.SlaveList.render.listDOM(employeesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract,
+			dom: App.UI.SlaveList.render(employeesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract,
 				V.fucktoyInteractionsPosition === 1 && job === "fucktoy" ? App.MainView.useFucktoy : null)
 		};
 	}
@@ -1051,7 +992,7 @@ App.UI.SlaveList.penthousePage = function() {
 		}
 		SlaveSort.IDs(penthouseSlavesIDs);
 		return makeTabDesc('all', `All${V.useSlaveSummaryTabs > 0 ? ` (${penthouseSlavesIDs.length})` : ""}`,
-			App.UI.SlaveList.render.listDOM(penthouseSlavesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract));
+			App.UI.SlaveList.render(penthouseSlavesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract));
 	}
 
 	let fragment = document.createDocumentFragment();
@@ -1061,7 +1002,7 @@ App.UI.SlaveList.penthousePage = function() {
 	}
 
 	if (V.sortSlavesMain) {
-		fragment.append(App.UI.SlaveList.sortingLinksDOM("Main"));
+		fragment.append(App.UI.SlaveList.sortingLinks("Main"));
 	}
 
 	/** @type {tabDesc[]} */
@@ -1127,12 +1068,6 @@ App.UI.SlaveList.penthousePage = function() {
 	return fragment;
 };
 
-/**
- * @callback assignmentFilterGenerateCallback
- * @param {string} value
- * @returns {string}
- */
-
 /**
  * @callback slaveFilterCallbackReasoned
  * @param {App.Entity.SlaveState} slave
@@ -1148,49 +1083,53 @@ App.UI.SlaveList.penthousePage = function() {
 App.UI.SlaveList.slaveSelectionList = function() {
 	const selectionElementId = "slaveSelectionList";
 
-	return selection;
-
 	/**
-	 * @typedef ListOptions
 	 * @property {slaveFilterCallbackReasoned|slaveFilterCallbackSimple} filter
-	 * @property {slaveTestCallback} [expCheck]
 	 * @property {slaveToElement} interactionLink
+	 * @property {slaveTestCallback} [expCheck]
 	 * @property {slaveToElement} [postNote]
 	 */
+	let options = null;
+
+	return selection;
 
 	/**
 	 * @param {slaveFilterCallbackReasoned|slaveFilterCallbackSimple} filter
 	 * @param {slaveToElement} interactionLink
-	 * @param {slaveTestCallback} [experianceChecker]
+	 * @param {slaveTestCallback} [experienceChecker]
 	 * @param {slaveToElement} [postNote]
-	 * @returns {string}
+	 * @returns {HTMLElement}
 	 */
-	function selection(filter, interactionLink, experianceChecker, postNote) {
-		if (experianceChecker === null) { experianceChecker = undefined; }
-		State.temporary.slaveSelection = {
+	function selection(filter, interactionLink, experienceChecker, postNote) {
+		if (experienceChecker === null) { experienceChecker = undefined; }
+		options = {
 			filter: filter,
-			expCheck: experianceChecker,
 			interactionLink: interactionLink,
-			postNote: postNote,
-			update: _updateList
+			expCheck: experienceChecker,
+			postNote: postNote
 		};
 
 		$(document).one(':passagedisplay', () => { _updateList('all'); });
-		return `<div>${_assignmentFilter(s => `<<run _slaveSelection.update('${s}')>>`, experianceChecker !== undefined)} <div id=${selectionElementId}></div></div>`;
+
+		const div = document.createElement("div");
+		div.append(_assignmentFilter(experienceChecker !== undefined));
+		const selectionElement = App.UI.DOM.appendNewElement("div", div);
+		selectionElement.id = selectionElementId;
+		return div;
 	}
 
 	function _updateList(assignment) {
 		const e = document.getElementById(selectionElementId);
 		e.innerHTML = '';
-		e.appendChild(_listSlaves(assignment, State.temporary.slaveSelection));
+		e.appendChild(_listSlaves(assignment));
 	}
+
 	/**
-	 * Displays assignment filter links, whose action are generated by the callback
-	 * @param {assignmentFilterGenerateCallback} callback
+	 * Displays assignment filter links
 	 * @param {boolean} includeExperienced
-	 * @returns {string}
+	 * @returns {HTMLElement}
 	 */
-	function _assignmentFilter(callback, includeExperienced) {
+	function _assignmentFilter(includeExperienced) {
 		let filters = {
 			all: "All"
 		};
@@ -1204,30 +1143,21 @@ App.UI.SlaveList.slaveSelectionList = function() {
 			}
 		}
 		let links = [];
-		/* seems like SC2 does not process data-setter when data-passage is not set
 		for (const f in filters) {
-			links.push(App.UI.passageLink(filters[f], passage, callback(f)));
+			links.push(App.UI.DOM.link(filters[f], () => _updateList(f)));
 		}
 		if (includeExperienced) {
-			links.push(`<span class="lime">${App.UI.passageLink('Experienced', passage, callback('experienced'))}</span>`);
-		}*/
-		for (const f in filters) {
-			links.push(`<<link "${filters[f]}">>${callback(f)}<</link>>`);
-		}
-		if (includeExperienced) {
-			links.push(`<span class="lime"><<link "Experienced">>${callback('experienced')}<</link>></span>`);
+			links.push(App.UI.DOM.makeElement("span", App.UI.DOM.link('Experienced', () => _updateList('experienced')), "lime"));
 		}
 
-		return links.join('&thinsp;|&thinsp;');
+		return App.UI.DOM.generateLinksStrip(links);
 	}
 
 	/**
-	 *
 	 * @param {string} assignmentStr
-	 * @param {ListOptions} options
 	 * @returns {DocumentFragment}
 	 */
-	function _listSlaves(assignmentStr, options) {
+	function _listSlaves(assignmentStr) {
 		const slaves = V.slaves;
 		let unfilteredIDs = [];
 		switch (assignmentStr) {
@@ -1235,7 +1165,7 @@ App.UI.SlaveList.slaveSelectionList = function() {
 				unfilteredIDs = slaves.map(s => s.ID);
 				break;
 			case 'experienced':
-				unfilteredIDs = slaves.reduce((acc, s, idx) => {
+				unfilteredIDs = slaves.reduce((acc, s) => {
 					if (options.expCheck(s)) {
 						acc.push(s.ID);
 					}
@@ -1273,14 +1203,14 @@ App.UI.SlaveList.slaveSelectionList = function() {
 				s => options.postNote(s) :
 				() => null;
 
-		return App.UI.SlaveList.render.listDOM(passingIDs, rejects, options.interactionLink, listPostNote);
+		return App.UI.SlaveList.render(passingIDs, rejects, options.interactionLink, listPostNote);
 	}
 }();
 
 /**
  * @param {App.Entity.Facilities.Facility} facility
  * @param {string} passage go here after the new facility manager is selected
- * @returns {string}
+ * @returns {HTMLElement}
  */
 App.UI.SlaveList.facilityManagerSelection = function(facility, passage) {
 	return this.slaveSelectionList(slave => facility.manager.canEmploy(slave),
diff --git a/src/js/slaveSummaryHelpers.js b/src/js/slaveSummaryHelpers.js
index e9ce09f5717847e01598c6e22b23a34e9f1eebe5..4acafb5e74e4c3a87efbb00d598196aaa2e22962 100644
--- a/src/js/slaveSummaryHelpers.js
+++ b/src/js/slaveSummaryHelpers.js
@@ -1104,6 +1104,7 @@ App.UI.SlaveSummaryImpl = function() {
 				helpers.makeStyledSpan(c, sd.synergy.nymphomni);
 			} else if (slave.attrXX > 95 && slave.attrXY > 95) {
 				helpers.makeStyledSpan(c, sd.synergy.omni);
+				helpers.makeRatedStyledSpan(c, sd.energy, slave.energy, 0, true);
 			} else {
 				helpers.makeRatedStyledSpan(c, sd.XY, slave.attrXY, 0, true);
 				helpers.makeRatedStyledSpan(c, sd.XX, slave.attrXX, 0, true);
@@ -1191,6 +1192,7 @@ App.UI.SlaveSummaryImpl = function() {
 				helpers.makeStyledSpan(c, sd.synergy.nymphomni);
 			} else if (slave.attrXX > 95 && slave.attrXY > 95) {
 				helpers.makeStyledSpan(c, sd.synergy.omni);
+				helpers.makeRatedStyledSpan(c, sd.energy, slave.energy, 0, true);
 			} else {
 				helpers.makeRatedStyledSpan(c, sd.XY, slave.attrXY, 0, true);
 				helpers.makeRatedStyledSpan(c, sd.XX, slave.attrXX, 0, true);
diff --git a/src/js/slaveSummaryWidgets.js b/src/js/slaveSummaryWidgets.js
index e0adc5cbe33f5bddf80a9a136338c11fdcafe010..8469e52d0b93db064628fbde85acca2f28094766 100644
--- a/src/js/slaveSummaryWidgets.js
+++ b/src/js/slaveSummaryWidgets.js
@@ -138,7 +138,7 @@ App.UI.SlaveSummaryRenderers = function() {
 			}
 			if (slave.vagina === 0) {
 				makeSpan(c, "VV", "lime");
-			} else if ((slave.pregKnown === 1) && canWalk(slave) && (slave.clothes === "no clothing" || slave.clothes === "body oil") && (slave.shoes === "none")) {
+			} else if ((slave.pregKnown === 1) && canStand(slave) && (slave.clothes === "no clothing" || slave.clothes === "body oil") && (slave.shoes === "none")) {
 				makeSpan(c, "NBP", "pink");
 			}
 			if (slave.anus === 0) {
@@ -460,7 +460,7 @@ App.UI.SlaveSummaryRenderers = function() {
 			}
 			if (slave.vagina === 0) {
 				helpers.makeSpan(c, "Virgin.", "lime");
-			} else if ((slave.pregKnown === 1) && canWalk(slave) && (slave.clothes === "no clothing" || slave.clothes === "body oil") && (slave.shoes === "none")) {
+			} else if ((slave.pregKnown === 1) && canStand(slave) && (slave.clothes === "no clothing" || slave.clothes === "body oil") && (slave.shoes === "none")) {
 				helpers.makeSpan(c, "Naked, barefoot, and pregnant.", "pink");
 			}
 			if (slave.anus === 0) {
diff --git a/src/js/statsChecker/statsChecker.js b/src/js/statsChecker/statsChecker.js
index 2c8cb5c43b01df5cad743276c4ecb99926dc6aea..afb63c489bc68e8724150118ee675ace188fd813 100644
--- a/src/js/statsChecker/statsChecker.js
+++ b/src/js/statsChecker/statsChecker.js
@@ -834,28 +834,16 @@ 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)) {
 		return false;
 	} else if (tooFatSlave(slave)) {
 		return false;
 	} else if (tooBigBreasts(slave)) {
 		return false;
-	} else if (slave.physicalAge <= 3) {
-		if (slave.belly >= 150000 + (slave.muscles * 500)) {
-			return false;
-		}
-	} else if (slave.physicalAge <= 12) {
-		if (slave.belly >= 450000 + (slave.muscles * 800)) {
-			return false;
-		}
-	} else if (slave.physicalAge < 18) {
-		if (slave.belly >= 450000 + (slave.muscles * 1000)) {
-			return false;
-		}
-	} else if (slave.physicalAge >= 18) {
-		if (slave.belly >= 1500000 + (slave.muscles * 2000)) {
-			return false;
-		}
+	} else if (tooBigBelly(slave)) {
+		return false;
 	}
 	return true;
 };
@@ -877,20 +865,20 @@ globalThis.canMove = function(slave) {
 		if (slave.boobs > 25000 + (slave.muscles * 20)) {
 			return false;
 		}
-		if (slave.belly >= 100000) {
+		if (slave.belly >= 150000) {
 			return false;
 		}
-		if (slave.balls >= 30 + (slave.muscles * 0.3) && slave.physicalAge <= 3) {
+		if (slave.balls >= 30 + (slave.muscles * 0.3)) {
 			return false;
 		}
 	} else if (slave.physicalAge <= 12) {
 		if (slave.boobs > 100000 + (slave.muscles * 50)) {
 			return false;
 		}
-		if (slave.belly >= 400000) {
+		if (slave.belly >= 300000) {
 			return false;
 		}
-		if (slave.balls >= 60 + (slave.muscles * 0.5) && slave.physicalAge <= 12) {
+		if (slave.balls >= 60 + (slave.muscles * 0.5)) {
 			return false;
 		}
 	} else if (slave.physicalAge < 18) {
@@ -1018,13 +1006,13 @@ globalThis.tooBigBreasts = function(slave) {
 globalThis.tooBigBelly = function(slave) {
 	if (!slave) {
 		return null;
-	} else if (slave.belly >= 450000 + (slave.muscles * 2000) && slave.physicalAge >= 18) {
+	} else if (slave.belly >= 450000 + (slave.muscles * 2000) && slave.physicalAge >= 18) { // 250k - 650k
 		return true;
-	} else if (slave.belly >= 350000 + (slave.muscles * 1000) && slave.physicalAge >= 13) {
+	} else if (slave.belly >= 350000 + (slave.muscles * 1000) && slave.physicalAge >= 13) { // 250k - 450k
 		return true;
-	} else if (slave.belly >= 30000 + (slave.muscles * 500) && slave.physicalAge <= 3) {
+	} else if (slave.belly >= 120000 + (slave.muscles * 500) && slave.physicalAge <= 3) { // 70k - 170k
 		return true;
-	} else if (slave.belly >= 150000 + (slave.muscles * 800) && slave.physicalAge <= 12) {
+	} else if (slave.belly >= 150000 + (slave.muscles * 800) && slave.physicalAge <= 12) { // 70k - 230k
 		return true;
 	}
 	return false;
diff --git a/src/js/underperformingSlaves.js b/src/js/underperformingSlaves.js
new file mode 100644
index 0000000000000000000000000000000000000000..04f1444bb0278fddb639b02ac3d012eb352ce9dc
--- /dev/null
+++ b/src/js/underperformingSlaves.js
@@ -0,0 +1,58 @@
+App.Underperformers = {};
+
+/** Select only slaves which are not reasonably expected to produce any income (brand new slaves, servants, fucktoys, etc)
+ * @param {App.Entity.SlaveState} slave
+ * @returns {boolean}
+ */
+App.Underperformers.expectIncome = function(slave) {
+	const productionJobs = [Job.ARCADE, Job.MILKED, Job.WHORE, Job.GLORYHOLE, Job.BROTHEL, Job.DAIRY];
+	return productionJobs.includes(slave.assignment) && ((slave.weekAcquired + 1) < V.week) && _.isFinite(slave.lastWeeksCashIncome);
+};
+
+App.Underperformers.highSale = function() {
+	const description = App.UI.DOM.makeElement("div", "Take the rough value of a slave and divide it by how much they made overall last week. This will tell you how many weeks it might take them to earn the same amount you'd get for selling them right now.", "note");
+
+	const frag = App.UI.SlaveList.render(
+		getBestSlavesIDs(
+			{
+				part:(slave) => {
+					const ratio = slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave));
+					return ratio > 0 ? ratio : 100000000 + ratio;
+				},
+				count: 7,
+				filter: App.Underperformers.expectIncome
+			}
+		),
+		[],
+		App.UI.SlaveList.SlaveInteract.stdInteract,
+		(slave) => $(document.createDocumentFragment()).append(
+			`Worth ${cashFormatColor(slaveCost(slave))} / Nets ${cashFormatColor(slave.lastWeeksCashIncome - getSlaveCost(slave))} a week = ${(Math.trunc(slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave)))) > 0 ? (Math.trunc(slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave)))) : "infinite"} weeks`
+		).get(0)
+	);
+
+	frag.prepend(description);
+	return frag;
+};
+
+App.Underperformers.expensive = function() {
+	const description = App.UI.DOM.makeElement("div", "This list looks for moochers by weighing their weekly income against the weekly cost of providing for them.", "note");
+
+	const frag = App.UI.SlaveList.render(
+		getBestSlavesIDs(
+			{
+				part:(slave) => (slave.lastWeeksCashIncome - getSlaveCost(slave)),
+				largest: false,
+				count: 7,
+				filter: App.Underperformers.expectIncome
+			}
+		),
+		[],
+		App.UI.SlaveList.SlaveInteract.stdInteract,
+		(slave) => $(document.createDocumentFragment()).append(
+			`${cashFormatColor(Math.trunc(slave.lastWeeksCashIncome - getSlaveCost(slave)))} net last week`
+		).get(0)
+	);
+
+	frag.prepend(description);
+	return frag;
+};
diff --git a/src/js/utilsDOM.js b/src/js/utilsDOM.js
index efd222f544541de35cd3d3df43b596a2aab60db1..0996959a4dc991766efc4d092843605b2c02f079 100644
--- a/src/js/utilsDOM.js
+++ b/src/js/utilsDOM.js
@@ -348,16 +348,7 @@ App.UI.DOM.accordion = function(head, content, hidden = true) {
 	const button = App.UI.DOM.appendNewElement("button", fragment, head, "accordion");
 
 	if (content) {
-		content.classList.add("accordion-content");
-		if (hidden) {
-			content.classList.add("hidden");
-		} else {
-			button.classList.add("open");
-		}
-		button.onclick = () => {
-			button.classList.toggle("open");
-			content.classList.toggle("hidden");
-		};
+		App.UI.DOM.elementToggle(button, [content], hidden);
 		fragment.append(content);
 	} else {
 		button.classList.add("empty");
@@ -366,6 +357,28 @@ App.UI.DOM.accordion = function(head, content, hidden = true) {
 	return fragment;
 };
 
+/**
+ * @param {HTMLElement} toggleElement
+ * @param {Array<HTMLElement>} content
+ * @param {boolean} [startHidden=true]
+ */
+App.UI.DOM.elementToggle = function(toggleElement, content, startHidden = true) {
+	toggleElement.classList.add("accordion");
+	for (let htmlElement of content) {
+		htmlElement.classList.add("accordion-content");
+	}
+	const toggle = () => {
+		toggleElement.classList.toggle("closed");
+		for (let htmlElement of content) {
+			htmlElement.classList.toggle("hidden");
+		}
+	};
+	toggleElement.onclick = toggle;
+	if (startHidden) {
+		toggle();
+	}
+};
+
 /*
 	<<includeDOM element>>
 	Simply inserts a given DOM element.
diff --git a/src/js/utilsFC.js b/src/js/utilsFC.js
index 6953e0af1df7cfa84488f7827ed525c0dfe3c0dd..fb9f0f08b807f3799ecc58966f082caf757da23f 100644
--- a/src/js/utilsFC.js
+++ b/src/js/utilsFC.js
@@ -2689,25 +2689,6 @@ globalThis.nippleColorLevel = function(color) {
 	return nippleColor[color];
 };
 
-/**
- * Expression for SugarCube for referencing a slave by index
- * @param {number} i slave array index or -1 for activeSlave
- * @returns {string}
- */
-App.Utils.slaveRefString = function(i) {
-	return i >= 0 ? `$slaves[${i}]` : '$activeSlave';
-};
-
-/**
- * Sets active slave to the slave with the given index. Does nothing if index < 0
- * @param {number} index
- */
-App.Utils.setActiveSlaveByIndex = function(index) {
-	if (index >= 0) {
-		V.activeSlave = V.slaves[index];
-	}
-};
-
 /**
  * Sets temporary variables named by the scheme, described below, to pronouns for the given slave
  * @param {App.Entity.SlaveState} slave
@@ -3003,4 +2984,4 @@ globalThis.deflate = function(slave) {
 	slave.milkSource = 0;
 	slave.cumSource = 0;
 	SetBellySize(slave);
-};
+};
\ No newline at end of file
diff --git a/src/js/utilsSC.js b/src/js/utilsSC.js
index 2aec598922d7c4e30a71f338817d95e7eb974c96..c424d660b07a8ad10dd97262ec90e1717fb751bd 100644
--- a/src/js/utilsSC.js
+++ b/src/js/utilsSC.js
@@ -274,7 +274,7 @@ App.UI.disabledLink = function(link, reasons) {
 /** handler function for slaveDescriptionDialog. do not call directly. */
 App.UI._showDescriptionDialog = function(slave) {
 	Dialog.setup(SlaveFullName(slave));
-	const image = App.UI.DOM.makeElement("div", App.Art.SlaveArtElement(slave, 2, 0), ["imageRef", "medImg"]);
+	const image = V.seeImages ? App.UI.DOM.makeElement("div", App.Art.SlaveArtElement(slave, 2, 0), ["imageRef", "medImg"]) : '';
 	Dialog.append(image).append(App.Desc.longSlave(slave, {eventDescription: 1}));
 	Dialog.open();
 };
diff --git a/src/js/vignettes.js b/src/js/vignettes.js
index 7719f6ccd13a0bfa02462adb29aa57a0567376f2..4c8c856db9d17302d27f16db2efbdaf44ebad21e 100644
--- a/src/js/vignettes.js
+++ b/src/js/vignettes.js
@@ -2,11 +2,11 @@
  * @param {App.Entity.SlaveState} slave
  * @returns {{text: string, type: string, effect: number}}
  */
-globalThis.GetVignette = function(slave) {
+globalThis.GetVignette = function (slave) {
 	/** @type {{text: string, type: string, effect: number}[]} */
 	let vignettes = [];
 
-	const {he, him, his, hers, himself, boy, He/* , His */} = getPronouns(slave);
+	const { he, him, his, hers, himself, boy, He } = getPronouns(slave);
 
 	if (slave.assignment === window.Job.WHORE || slave.assignment === window.Job.BROTHEL || slave.assignment === window.Job.MADAM) {
 		let seed = jsRandom(1, 10);
@@ -1144,7 +1144,7 @@ globalThis.GetVignette = function(slave) {
 				effect: 1,
 			});
 		}
-		if (!canWalk(slave)) {
+		if (!canMove || (!canWalk(slave) && canMove(slave) && slave.rules.mobility === "restrictive")) {
 			vignettes.push({
 				text: `${his} lack of mobility severely hindered ${his} attempts to find customers,`,
 				type: "cash",
@@ -1977,7 +1977,7 @@ globalThis.GetVignette = function(slave) {
 			type: "trust",
 			effect: -1,
 		});
-	} else if (slave.assignment === window.Job.PUBLIC || slave.assignment === window.Job.CLUB || slave.assignment === window.Job.DJ) {
+	} else if (slave.assignment === Job.PUBLIC || slave.assignment === Job.CLUB || slave.assignment === Job.DJ) {
 		let seed = jsRandom(1, 10);
 		switch (seed) {
 			case 1:
@@ -3113,7 +3113,7 @@ globalThis.GetVignette = function(slave) {
 				effect: 1,
 			});
 		}
-		if (!canWalk(slave)) {
+		if (!canMove || (!canWalk(slave) && canMove(slave) && slave.rules.mobility === "restrictive")) {
 			vignettes.push({
 				text: `${his} lack of mobility severely hindered ${his} attempts to please citizens,`,
 				type: "rep",
@@ -4204,7 +4204,7 @@ globalThis.GetVignette = function(slave) {
 				effect: 1,
 			});
 		}
-		if (canWalk(slave)) {
+		if (canStand(slave)) {
 			vignettes.push({
 				text: `${he} slipped and fell in an errant puddle of various bodily fluids,`,
 				type: "health",
@@ -4234,73 +4234,7 @@ globalThis.GetVignette = function(slave) {
 			effect: 0,
 		});
 	} else if (slave.assignment === window.Job.FARMYARD || slave.assignment === window.Job.FARMER) {
-		if (canWalk(slave)) {
-			vignettes.push({
-				text: `${he} rather comically injured ${himself} by stepping on a rake,`,
-				type: "health",
-				effect: -1,
-			});
-		}
-		// TODO: add more vignettes
-		if ((V.farmyardBreeding) && (V.seeBestiality)) {
-			vignettes.push({
-				text: `a citizen didn't realize how disgusting he found bestiality until he attended one of ${V.farmyardName}'s shows,`,
-				type: "rep",
-				effect: -1,
-			});
-			vignettes.push({
-				text: `a citizen attended one of ${V.farmyardName}'s shows and discovered he had a bestiality fetish,`,
-				type: "rep",
-				effect: 1,
-			});
-			vignettes.push({
-				text: `${he} dreamt that you decided to have ${him} do nothing but fuck animals for the rest of ${his} life,`,
-				type: "devotion",
-				effect: -1
-			});
-			vignettes.push({
-				text: `${he} dreamt that you wouldn't come to ${his} aid when one of ${his} bestial partners became overeager,`,
-				type: "trust",
-				effect: -1
-			});
-			if ((V.canines.length > 0) && hasAnyNaturalLegs(slave)) {
-				vignettes.push({
-					text: `an over-excited dog left scratch marks on ${his} leg,`,
-					type: "health",
-					effect: -1
-				});
-			}
-			if (V.hooved.length > 0) {
-				vignettes.push({
-					text: `${he} was injured by a particularly well-endowed horse,`,
-					type: "health",
-					effect: -1
-				});
-			}
-			if (V.felines.length > 0) {
-				vignettes.push({
-					text: `${he} managed to take two tigers at once, aweing the crowd,`,
-					type: "cash",
-					effect: 1
-				});
-			}
-		} else {
-			vignettes.push({
-				text: `${he} worked extra hard, and managed to sell extra produce,`,
-				type: "cash",
-				effect: 1,
-			});
-		}
-		vignettes.push({
-			text: `${he} felt energized by the simulated outdoor environment`,
-			type: "health",
-			effect: 1,
-		});
-		vignettes.push({
-			text: `the pesticides ${he} accidentally inhaled made ${him} feel nauseous,`,
-			type: "health",
-			effect: -1,
-		});
+		vignettes.push(farmyardVignettes(slave));
 	} else if (slave.assignment === window.Job.REST || slave.assignment === window.Job.SPA) {
 		switch (slave.behavioralFlaw) {
 			case "anorexic":
@@ -4454,6 +4388,12 @@ globalThis.GetVignette = function(slave) {
 					type: "health",
 					effect: -1,
 				});
+			} else if (canMove(slave)) {
+				vignettes.push({
+					text: `${he} was so horny that ${he} sleepcrawled into your room, only to fall and bump ${his} head while climbing into bed with you,`,
+					type: "health",
+					effect: -1,
+				});
 			} else {
 				if (hasAnyArms(slave)) {
 					if (slave.rules.release.masturbation === 1) {
@@ -4593,6 +4533,12 @@ globalThis.GetVignette = function(slave) {
 				type: "health",
 				effect: 1,
 			});
+		} else if (canMove(slave) && slave.rules.mobility === "permissive") {
+			vignettes.push({
+				text: `${he} spent much of ${his} free time making use of ${his} restored mobility by exploring the arcology,`,
+				type: "health",
+				effect: 1,
+			});
 		}
 		vignettes.push({
 			text: `${he} spent much of ${his} free time napping,`,
@@ -4600,5 +4546,93 @@ globalThis.GetVignette = function(slave) {
 			effect: 1,
 		});
 	}
+
 	return jsEither(vignettes);
+
+
+
+	// MARK: Farmyard
+
+	function farmyardVignettes(slave) {
+		let r = [];
+
+		if (canWalk(slave)) {
+			r.push({
+				text: `${he} rather comically injured ${himself} by stepping on a rake,`,
+				type: "health",
+				effect: -1,
+			});
+		}
+
+		if ((V.farmyardBreeding) && (V.seeBestiality)) {
+			r.push({
+				text: `a citizen didn't realize how disgusting he found bestiality until he attended one of ${V.farmyardName}'s shows,`,
+				type: "rep",
+				effect: -1,
+			});
+
+			r.push({
+				text: `a citizen attended one of ${V.farmyardName}'s shows and discovered he had a bestiality fetish,`,
+				type: "rep",
+				effect: 1,
+			});
+
+			r.push({
+				text: `${he} dreamt that you decided to have ${him} do nothing but fuck animals for the rest of ${his} life,`,
+				type: "devotion",
+				effect: -1
+			});
+
+			r.push({
+				text: `${he} dreamt that you wouldn't come to ${his} aid when one of ${his} bestial partners became overeager,`,
+				type: "trust",
+				effect: -1
+			});
+
+			if ((V.canines.length > 0) && hasAnyNaturalLegs(slave)) {
+				r.push({
+					text: `an over-excited dog left scratch marks on ${his} leg,`,
+					type: "health",
+					effect: -1
+				});
+			}
+
+			if (V.hooved.length > 0) {
+				r.push({
+					text: `${he} was injured by a particularly well-endowed horse,`,
+					type: "health",
+					effect: -1
+				});
+			}
+
+			if (V.felines.length > 0) {
+				r.push({
+					text: `${he} managed to take two tigers at once, aweing the crowd,`,
+					type: "cash",
+					effect: 1
+				});
+
+			}
+		} else {
+			r.push({
+				text: `${he} worked extra hard, and managed to sell extra produce,`,
+				type: "cash",
+				effect: 1,
+			});
+		}
+
+		r.push({
+			text: `${he} felt energized by the simulated outdoor environment`,
+			type: "health",
+			effect: 1,
+		});
+
+		r.push({
+			text: `the pesticides ${he} accidentally inhaled made ${him} feel nauseous,`,
+			type: "health",
+			effect: -1,
+		});
+
+		return jsEither(r);
+	}
 };
diff --git a/src/js/wombJS.js b/src/js/wombJS.js
index e560f361fc5fd2203a9f1c90ca2f05c82b345c3d..2cdf98b6a0ff14da877cc76498f9ba6f798a83f0 100644
--- a/src/js/wombJS.js
+++ b/src/js/wombJS.js
@@ -94,6 +94,14 @@ globalThis.WombInit = function(actor) {
 			WombImpregnate(actor, bLeft, actor.pregSource, i + 1); // setting up leftover of fetuses.
 		}
 	}
+
+	actor.womb.forEach(f => {
+		if (!jsDef(f.genetics.inbreedingCoeff)) {
+			f.genetics.inbreedingCoeff = ibc.coeff(
+				{ID: null, mother: f.genetics.mother, father: f.genetics.father}
+			);
+		}
+	});
 };
 
 globalThis.WombImpregnate = function(actor, fCount, fatherID, age, surrogate) {
diff --git a/src/neighbor/neighborInteract.js b/src/neighbor/neighborInteract.js
index 92701210edd08bb9c1c7aade7e828316aa197d71..3abdab94c9fa7e198a71f6b4f4cabd693b5572b8 100644
--- a/src/neighbor/neighborInteract.js
+++ b/src/neighbor/neighborInteract.js
@@ -181,7 +181,7 @@ App.Neighbor.Interact = (function() {
 				linkText = `Recall them and reenslave your agent`;
 				residentList.push(agentPartner.ID);
 			}
-			container.append(App.UI.SlaveList.render.listDOM(residentList, [], App.UI.SlaveList.SlaveInteract.stdInteract));
+			container.append(App.UI.SlaveList.render(residentList, [], App.UI.SlaveList.SlaveInteract.stdInteract));
 			container.append(App.UI.DOM.link(linkText, (f) => { removeJob(agent, "be your agent"); arcChanged(arcID); }));
 		}
 		container.append(" | ");
diff --git a/src/npc/acquisition.tw b/src/npc/acquisition.tw
index ebb7ec3198702510af01eb6f78bb89c73570b508..6c34e73a825c689d24d30f11189818767ce23f5a 100644
--- a/src/npc/acquisition.tw
+++ b/src/npc/acquisition.tw
@@ -178,6 +178,23 @@
 		<<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++>>
@@ -455,7 +472,7 @@ The previous owner seems to have left in something of a hurry.
 			<<set $activeSlave.behavioralFlaw = "none", $activeSlave.behavioralQuirk = "fitness">>
 			<<set $activeSlave.behavioralFlaw = "none">>
 			<<set $activeSlave.clothes = "a toga", $activeSlave.collar = "pretty jewelry", $activeSlave.shoes = "flats">>
-			<<set $activeSlave.assignment = "guard you", $Bodyguard = $activeSlave>>
+			<<set $activeSlave.assignment = "guard you", $BodyguardID = $activeSlave.ID>>
 		<<case "AztecRevivalist">>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $fixedRace = "latina">>
@@ -468,7 +485,7 @@ The previous owner seems to have left in something of a hurry.
 			<<set $activeSlave.skill.combat = 1>>
 			<<set $activeSlave.behavioralFlaw = "malicious", $activeSlave.behavioralQuirk = "none">>
 			<<set $activeSlave.clothes = "a huipil", $activeSlave.collar = "pretty jewelry", $activeSlave.shoes = "none">>
-			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirl = $activeSlave>>
+			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirlID = $activeSlave.ID>>
 		<<case "EgyptianRevivalist">>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $activeSlave = GenerateNewSlave()>>
@@ -528,7 +545,7 @@ The previous owner seems to have left in something of a hurry.
 			<<run SoftenBehavioralFlaw($activeSlave)>>
 			<<run SoftenSexualFlaw($activeSlave)>>
 			<<set $activeSlave.clothes = "a slutty qipao", $activeSlave.collar = "pretty jewelry", $activeSlave.shoes = "heels">>
-			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirl = $activeSlave>>
+			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirlID = $activeSlave.ID>>
 		<<case "Eugenics">>
 			<<set $oneTimeDisableDisability = 1>>
 			<<set $activeSlave = GenerateNewSlave()>>
@@ -617,7 +634,7 @@ The previous owner seems to have left in something of a hurry.
 			<<run SoftenBehavioralFlaw($activeSlave)>>
 			<<run SoftenSexualFlaw($activeSlave)>>
 			<<set $activeSlave.clothes = "a nice maid outfit", $activeSlave.collar = "pretty jewelry">>
-			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirl = $activeSlave>>
+			<<set $activeSlave.assignment = "be your Head Girl", $HeadGirlID = $activeSlave.ID>>
 		<<case "PetiteAdmiration">>
 			<<set $activeSlaveOneTimeMinAge = 14>>
 			<<set $activeSlaveOneTimeMaxAge = 18>>
diff --git a/src/npc/agent/agentCompany.tw b/src/npc/agent/agentCompany.tw
index 9abdbd28f152a9dd15bc694ce4f7fcafbf4690f5..cf96a70df8e3d79911930a909669014df7c7090f 100644
--- a/src/npc/agent/agentCompany.tw
+++ b/src/npc/agent/agentCompany.tw
@@ -21,7 +21,7 @@
 
 <<setLocalPronouns $subSlave 2>>
 
-Working in _neighborArcology.name's penthouse office, $subSlave.slaveName gets a video call from you. Naturally, _his2 personal assistant program has been instructed to give these the highest priority, and your face appears in front of _him2 immediately. _He2 greets you properly, and asks you what _he2 can do to serve you. You tell _him2 you're planning to send one of your slaves over to _his2 arcology to assist _him2. Exchanges of this kind are neither unusual nor particularly interesting, so a slight look of perplexity clouds $subSlave.slaveName's face as _he2 wonders why this merits a personal call. Normally, <<if $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>> would handle it with _him2 directly.
+Working in _neighborArcology.name's penthouse office, $subSlave.slaveName gets a video call from you. Naturally, _his2 personal assistant program has been instructed to give these the highest priority, and your face appears in front of _him2 immediately. _He2 greets you properly, and asks you what _he2 can do to serve you. You tell _him2 you're planning to send one of your slaves over to _his2 arcology to assist _him2. Exchanges of this kind are neither unusual nor particularly interesting, so a slight look of perplexity clouds $subSlave.slaveName's face as _he2 wonders why this merits a personal call. Normally, <<if $HeadGirlID>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>> would handle it with _him2 directly.
 
 <br><br>
 
@@ -61,4 +61,4 @@ $subSlave.slaveName smiles back at $him, glowing with pleasure, and responds wit
 <<default>>
 	"Babe, I can't wait to <<if hasAnyArms($subSlave)>>give you a hug and <</if>>tell you about _neighborArcology.name," the loving
 <</switch>>
-leader of an entire arcology <<say>>s.
\ No newline at end of file
+leader of an entire arcology <<say>>s.
diff --git a/src/npc/agent/agentSelect.tw b/src/npc/agent/agentSelect.tw
index 147416474dbbb8ae100fc6168f18b47cb21a9610..e337c0d0ba671437a066fa34cdf110e684d62163 100644
--- a/src/npc/agent/agentSelect.tw
+++ b/src/npc/agent/agentSelect.tw
@@ -3,7 +3,7 @@
 <<set $nextButton = "Back", $nextLink = "Neighbor Interact", $encyclopedia = "Agents">>
 ''Appoint an Agent from your devoted slaves:''
 
-<<= App.UI.SlaveList.slaveSelectionList(
+<<includeDOM App.UI.SlaveList.slaveSelectionList(
 		s => (s.fuckdoll === 0 && s.devotion > 20 && s.intelligence + s.intelligenceImplant > 15 && s.intelligenceImplant >= 15 && canWalk(s) && canSee(s) && canHear(s) && canTalk(s) && s.broodmother < 2 && (s.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")),
 		(slave) => App.UI.DOM.passageLink(SlaveFullName(slave), 'Agent Workaround',
 			() => { V.i = V.slaves.findIndex((s) => s.ID === slave.ID); }),
diff --git a/src/npc/bodyguard/bgDescription.js b/src/npc/bodyguard/bgDescription.js
index 429868280875d7e71fa6fc97313544f3299d535e..3526467c4ab8433e8bc6c3bb3cc54083b9a187e2 100644
--- a/src/npc/bodyguard/bgDescription.js
+++ b/src/npc/bodyguard/bgDescription.js
@@ -7,7 +7,7 @@ App.Desc.bodyguard = function(slave) {
 	const {
 		his, He, he
 	} = getPronouns(slave);
-	if (slave.fuckdoll !== 0 || slave.ID !== V.Bodyguard.ID) {
+	if (slave.fuckdoll !== 0 || slave.ID !== V.BodyguardID) {
 		return;
 	}
 	r.push(`${He} is your bodyguard.`);
diff --git a/src/npc/databases/cheatmodeDatabase.tw b/src/npc/databases/cheatmodeDatabase.tw
index fd3571b09d0151f82f39af59a847d8fe044c9f62..479591222ef956ff5d20910bdadb75f973705405 100644
--- a/src/npc/databases/cheatmodeDatabase.tw
+++ b/src/npc/databases/cheatmodeDatabase.tw
@@ -58,7 +58,7 @@
 	<<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 $HeadGirl = clone(_HS)>>
+<<set $HeadGirlID = _HS.ID>>
 //There are quite a few left; their names are _HS.slaveName,//
 
 <<set _HS = clone($activeSlave)>>
@@ -99,7 +99,7 @@
 	<<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 $Bodyguard = clone(_HS)>>
+<<set $BodyguardID = _HS.ID>>
 //_HS.slaveName,//
 
 <<set _HS = clone($activeSlave)>>
diff --git a/src/npc/descriptions/belly/belly.js b/src/npc/descriptions/belly/belly.js
index 7e39a4bee79aeeb4ad3f9f440984b1061a3671c6..aba3677ebd840189936f3a74d74ee94eb569dc54 100644
--- a/src/npc/descriptions/belly/belly.js
+++ b/src/npc/descriptions/belly/belly.js
@@ -188,14 +188,8 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is nothing more than a womb with a ${loli} attached. ${He} struggles to not be lost under the squirming mass of infants that dwarf ${his} body.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							r.push(`stand up`);
-						} else {
-							r.push(`get to ${his} feet`);
-						}
-						r.push(`and aid ${him} through ${his} daily affairs.`);
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${him} and aid ${him} through ${his} daily affairs.`);
 					}
 				} else {
 					r.push(`and ${he} is nothing more than a womb with a ${girl} attached. ${He} is physically unable to keep ${his} boundless middle off the ground for long, if at all.`);
@@ -245,14 +239,8 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is nothing more than an obscene over-filled implant with a ${loli} attached. ${He} struggles to not be lost under the straining mass that dwarfs ${his} body.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							r.push(`stand up`);
-						} else {
-							r.push(`get to ${his} feet`);
-						}
-						r.push(`and aid ${him} through ${his} daily affairs.`);
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${him} and aid ${him} through ${his} daily affairs.`);
 					}
 				} else {
 					r.push(`and ${he} is nothing more than an over-filled implant with a ${girl} attached. ${He} is physically unable to keep ${his} boundless middle off the ground for long, if at all.`);
@@ -319,24 +307,8 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`an overstuffed womb with a ${loli} attached. ${He} struggles to not be lost under the squirming mass of infants that fill ${his} body.`);
 					}
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere`);
-						}
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame can keep ${his}`);
@@ -345,25 +317,6 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`life brimming belly off the ground; but only for a moment. ${He} opts to not try this often as ${his} occupants are liable to voice their complaints with a hail of kicks.`);
 					}
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
-						}
-					}
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is nothing more than`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
@@ -371,25 +324,6 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`an overstuffed womb with a ${girl} attached. ${He} struggles to remain seen under the squirming mass of infants that fill ${his} body.`);
 					}
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
-						}
-					}
 				} else if (slave.muscles > 90) {
 					r.push(`and ${his} muscular body`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
@@ -397,26 +331,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`perfectly handles`);
 					}
-					r.push(`${his} monolithic belly, though ${he} still can't move with it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere`);
-						}
-					}
+					r.push(`${his} monolithic belly, though ${he} still can't really function with it.`);
 				} else {
 					r.push(`and ${he} is nothing more than`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
@@ -425,24 +340,24 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 						r.push(`an overstuffed womb with a ${girl} attached.`);
 					}
 					r.push(`${He} is physically unable to keep ${his} straining belly off the ground.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+				}
+				if (slave.physicalAge > 12) {
+					if (canWalk(slave)) {
+						r.push(`Amazingly, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Amazingly, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`As unpleasant as it may be, ${he} is still capable of slowly dragging ${himself} along the ground.`);
 						}
+					} else if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 					}
 				}
 				if (slave.weight > 190) {
@@ -497,24 +412,8 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`fills ${his} body.`);
 					}
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame can keep ${his}`);
@@ -522,25 +421,6 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 						r.push(`straining`);
 					}
 					r.push(`belly off the ground; but only for a moment. ${He} opts to not try this often as ${his} implant is already at its limit and could do without being slammed against the floor.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
-					}
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is nothing more than a ready-to-rupture implant with a ${girl} attached. ${He} struggles to remain seen under the straining mass that`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
@@ -548,25 +428,6 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`fills ${his} body.`);
 					}
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
-					}
 				} else if (slave.muscles > 90) {
 					r.push(`and ${his} muscular body`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
@@ -574,50 +435,31 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else {
 						r.push(`perfectly handles`);
 					}
-					r.push(`${his} monolithic belly, though ${he} still can't move with it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
-					}
+					r.push(`${his} monolithic belly, though ${he} still can't really function with it.`);
 				} else {
 					r.push(`and ${he} is nothing more than a ready-to-rupture implant with a ${girl} attached. ${He} is physically unable to keep ${his}`);
 					if (slave.belly > (slave.pregAdaptation * 1000)) {
 						r.push(`straining`);
 					}
 					r.push(`belly off the ground.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+				}
+				if (slave.physicalAge > 12) {
+					if (canWalk(slave)) {
+						r.push(`Amazingly, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Amazingly, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`As unpleasant as it may be, ${he} is still capable of slowly dragging ${himself} along the ground.`);
 						}
+					} else if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 					}
 				}
 				if (slave.weight > 190) {
@@ -647,89 +489,86 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is nothing more than a womb with a ${loli} attached. ${He} has to be careful not to get crushed under ${his} own quivering mass.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
+					}
+				} else if (slave.muscles >= 75) {
+					r.push(`and ${his} muscular body barely allows ${him} to manage ${his} titanic belly.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks a slow, ponderous waddle fitting for a ${girl} of ${his} size and weight.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} gets a serious workout just from standing with such a heavy stomach.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`Despite ${his} size and weight, ${he} requires little assistance to get to ${his} feet, but still must use a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere, but, despite ${his} size and weight, is still able to get in and out of it on ${his} own.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Impressively, ${he} is strong enough to keep ${his} belly from dragging along the ground as ${he} crawls.`);
 						}
+					} else {
+						r.push(`It does not stop it from completely immodilizing ${him}, however.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame barely keeps ${his} titanic belly off the ground; with considerable effort, of course.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
+					} else {
+						r.push(`A talent that is ultimately meaningless when it is so unwieldy it immobilizes you.`);
 					}
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is nothing more than a womb with a ${girl} attached. ${He} has to be careful not to get crushed under ${his} own quivering mass.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
+							r.push(`Dragging the mass along is nigh impossible due to its sheer weight.`);
 						}
+					} else {
 						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 						}
 					}
-				} else if (slave.muscles > 90) {
-					r.push(`and ${his} muscular body barely allows ${him} to manage ${his} titanic belly.`);
 				} else {
 					r.push(`and ${he} is nothing more than a womb with a ${girl} attached. While ${he} can barely heft ${his} titanic belly off the floor, it takes a considerable amount of effort to do so and quickly comes to rest back where it started.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
+					} else {
+						r.push(`A talent that is ultimately meaningless when it is so unwieldy it immobilizes you.`);
 					}
 				}
 				if (slave.weight > 190) {
@@ -766,89 +605,86 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is nothing more than a greatly overfilled implant with a ${loli} attached. ${He} has to be careful not to get crushed under ${his} own taut mass.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (!market) {
+						r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
+					}
+				} else if (slave.muscles >= 75) {
+					r.push(`and ${his} muscular body barely allows ${him} to manage ${his} titanic belly.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks a slow, ponderous waddle fitting for a ${girl} of ${his} size and weight.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} gets a serious workout just from standing with such a heavy stomach.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`Despite ${his} size and weight, ${he} requires little assistance to get to ${his} feet, but still must use a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere, but, despite ${his} size and weight, is still able to get in and out of it on ${his} own.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Impressively, ${he} is strong enough to keep ${his} belly from dragging along the ground as ${he} crawls.`);
 						}
+					} else {
+						r.push(`It does not stop it from completely immodilizing ${him}, however.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame barely keeps ${his} titanic belly off the ground; with considerable effort, of course.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
+					} else {
+						r.push(`A talent that is ultimately meaningless when it is so unwieldy it immobilizes you.`);
 					}
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is nothing more than a greatly overfilled implant with a ${girl} attached. ${He} has to be careful not to get crushed under ${his} own taut mass.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
+							r.push(`Dragging the mass along is nigh impossible due to its sheer weight.`);
 						}
+					} else {
 						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`${He} requires multiple slaves to move ${his} bulk when ${he} must go somewhere.`);
 						}
 					}
-				} else if (slave.muscles > 90) {
-					r.push(`and ${his} muscular body barely allows ${him} to manage ${his} titanic belly.`);
 				} else {
 					r.push(`and ${he} is nothing more than a greatly overfilled implant with a ${girl} attached. While ${he} can barely heft ${his} titanic belly off the floor, it takes a considerable amount of effort to do so and quickly comes to rest back where it started.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is powerful enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
+					} else {
+						r.push(`A talent that is ultimately meaningless when it is so unwieldy it immobilizes you.`);
 					}
 				}
 				if (slave.weight > 190) {
@@ -880,88 +716,75 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} youthful figure is grotesquely bloated by ${his} pregnancy. ${He} can't keep ${his} gigantic belly off the floor and fights a losing battle to not be pinned by it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
-						}
+					if (!market) {
+						r.push(`${He} requires assistance to move when ${he} must go somewhere.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame barely keeps ${his} gigantic belly off the ground; with considerable effort, of course.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				} else if (slave.height < 150) {
 					r.push(`and ${his} small figure is utterly dwarfed by ${his} pregnancy. ${He} can't keep ${his} gigantic belly off the floor and fights a losing battle to not be pinned by it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				} else if (slave.muscles > 30) {
 					r.push(`and ${his} fit body barely allows ${him} to manage ${his} gigantic belly.`);
-				} else {
-					r.push(`and ${he} is dwarfed by ${his} pregnancy. While ${he} can keep barely ${his} gigantic belly off the floor, it takes a considerable amount of effort to do so.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks a slow, ponderous waddle fitting for a ${girl} of ${his} size and weight.`);
+					} else if (canStand(slave)) {
+						r.push(`${His} legs get a serious workout just from standing with such a heavy stomach.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`Despite ${his} size and weight, ${he} requires no assistance to get to ${his} feet, but still must use a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere, but, despite ${his} size and weight, is still able to get in and out of it on ${his} own.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
+							r.push(`Impressively, ${he} is strong enough to keep ${his} belly from dragging along the ground as ${he} crawls.`);
+						}
+					}
+				} else {
+					r.push(`and ${he} is dwarfed by ${his} pregnancy. While ${he} can keep barely ${his} gigantic belly off the floor, it takes a considerable amount of effort to do so.`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`get to ${his} feet.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+						} else {
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				}
@@ -989,88 +812,75 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} youthful figure is grotesquely bloated by ${his} overfilled implant. ${He} can't keep ${his} gigantic belly off the floor and fights a losing battle to not be pinned by it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
+					if (!market) {
+						r.push(`${He} requires assistance to move when ${he} must go somewhere.`);
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame barely keeps ${his} gigantic belly off the ground; with considerable effort, of course.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires multiple slaves to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				} else if (slave.height < 150) {
 					r.push(`and ${his} small figure is utterly dwarfed by ${his} overfilled implant. ${He} can't keep ${his} gigantic belly off the floor and fights a losing battle to not be pinned by it.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				} else if (slave.muscles > 30) {
 					r.push(`and ${his} fit body barely allows ${him} to manage ${his} gigantic belly.`);
-				} else {
-					r.push(`and ${he} is dwarfed by ${his} overfilled implant. While ${he} can keep barely ${his} gigantic belly off the floor, it takes a considerable amount of effort to do so.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks a slow, ponderous waddle fitting for a ${girl} of ${his} size and weight.`);
+					} else if (canStand(slave)) {
+						r.push(`${His} legs get a serious workout just from standing with such a heavy stomach.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`Despite ${his} size and weight, ${he} requires no assistance to get to ${his} feet, but still must use a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere, but, despite ${his} size and weight, is still able to get in and out of it on ${his} own.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
+							r.push(`Impressively, ${he} is strong enough to keep ${his} belly from dragging along the ground as ${he} crawls.`);
+						}
+					}
+				} else {
+					r.push(`and ${he} is dwarfed by ${his} overfilled implant. While ${he} can keep barely ${his} gigantic belly off the floor, it takes a considerable amount of effort to do so.`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, once helped to ${his} feet, ${he} is still able to waddle along despite ${his} size.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires considerable assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`get to ${his} feet.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires considerable assistance to get in and out of it.`);
 							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+						} else {
+							r.push(`Dragging the mass along is becoming a challenge between its awkward size and sheer weight.`);
 						}
 					}
 				}
@@ -1098,69 +908,60 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is more belly than ${girl}. ${He} can barely keep ${his} massive belly from touching the floor.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (!market) {
+						r.push(`${He} requires assistance to move when ${he} must go somewhere.`);
+					}
+				} else if (slave.height >= 185) {
+					r.push(`but ${his} tall frame keeps ${his} massive belly off the ground.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
-				} else if (slave.height >= 185) {
-					r.push(`but ${his} tall frame keeps ${his} massive belly off the ground.`);
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is more belly than ${girl}. ${He} can barely keep ${his} massive belly from touching the floor.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
 				} else if (slave.muscles > 30) {
 					r.push(`and ${his} fit body allows ${him} to carry ${his} oversized belly normally with effort.`);
 				} else {
 					r.push(`and ${he} is more belly than ${girl}.While ${he} can keep ${his} massive belly from touching the floor, it takes a lot of effort to do so.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
 				}
@@ -1186,69 +987,60 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} is more belly than ${girl}. ${He} can barely keep ${his} massive belly from touching the floor.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
+					if (!market) {
+						r.push(`${He} requires assistance to move when ${he} must go somewhere.`);
+					}
+				} else if (slave.height >= 185) {
+					r.push(`but ${his} tall frame keeps ${his} massive belly off the ground.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`get to ${his} feet.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+						} else {
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
-				} else if (slave.height >= 185) {
-					r.push(`but ${his} tall frame keeps ${his} massive belly off the ground.`);
 				} else if (slave.height < 150) {
 					r.push(`and ${he} is more belly than ${girl}. ${He} can barely keep ${his} massive belly from touching the floor.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
 				} else if (slave.muscles > 30) {
 					r.push(`and ${his} fit body allows ${him} to carry ${his} oversized belly normally with effort.`);
 				} else {
 					r.push(`and ${he} is more belly than ${girl}.While ${he} can keep ${his} massive belly from touching the floor, it takes a lot of effort to do so.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
 						}
 					}
 				}
@@ -1268,55 +1060,91 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is massively pregnant, beyond any typical pregnancy,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} belly pins ${him} to the ground. ${His} toddlerish form is dwarfed by ${his} pregnancy, and try as ${he} might ${he} cannot even drag the oversized thing.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
+					r.push(`and ${his} belly pins ${him} to the ground.`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of still walking, despite ${his} toddlerish form being dwarfed by ${his} oversized pregnancy.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright, despite ${his} toddlerish form being dwarfed by ${his} oversized pregnancy.`);
+					} else {
+						r.push(`${His} toddlerish form is dwarfed by ${his} pregnancy, and try as ${he} might ${he} cannot even drag the oversized thing.`);
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`get to ${his} feet.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
-						}
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} can barely function with ${his} oversized belly.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of still walking, if barely.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright, if barely.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`${He} can barely even crawl with such a huge stomach bulging out from ${him}. It takes everything ${he} has to avoid being forced to drag ${himself} along.`);
 						}
 					}
 				} else if (slave.height >= 185) {
 					r.push(`but ${his} tall frame barely bears ${his} oversized, drum-taut belly.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} long limbs give ${him} an edge in crawling with such a large stomach, if it's any consolation.`);
+						}
+					}
 				} else if (slave.height < 150) {
 					r.push(`and ${he} can barely function with ${his} oversized belly.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`Crawling with such a mass extending from ${his} body is near impossible, so ${he} is forced to drag ${himself} on ${his} side should ${he} need to go somewhere.`);
+						}
+					}
 				} else if (slave.muscles > 30) {
 					r.push(`and ${his} fit body allows ${him} to carry ${his} oversized belly without too much trouble.`);
 				} else {
 					r.push(`and ${he} can barely function with ${his} oversized belly.`);
+					if (canWalk(slave)) {
+						r.push(`${He} walks with an exaggerated waddle in an attempt to keep balance with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's substantial weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${He} can barely even crawl with such a huge stomach bulging out from ${him}. It takes everything ${he} has to avoid being forced to drag ${himself} along.`);
+						}
+					}
 				}
 				if (slave.weight > 190) {
 					r.push(`${His} massively fat belly is stretched considerably; ${his} folds are nearly pulled flat from the strain. ${His} pregnancy is covered in a thick layer of fat, save for the bulging upper portion where ${his} fat is thinnest.`);
@@ -1352,45 +1180,36 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks massively pregnant, beyond any typical pregnancy,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} belly pins ${him} to the ground. ${His} toddlerish form is dwarfed by ${his} implant, and try as ${he} might ${he} cannot even drag the oversized thing.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
-							} else {
-								r.push(`stand up.`);
-							}
-						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
+					r.push(`and ${his} belly pins ${him} to the ground.`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of still walking, despite ${his} toddlerish form being dwarfed by ${his} oversized implant.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright, despite ${his} toddlerish form being dwarfed by ${his} oversized implant.`);
+					} else {
+						r.push(`${His} toddlerish form is dwarfed by ${his} implant, and try as ${he} might ${he} cannot even drag the oversized thing.`);
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`get to ${his} feet.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
-						}
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${he} can barely function with ${his} oversized belly.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of still walking, if barely.`);
+					} else if (canStand(slave)) {
+						r.push(`Impressively, ${he} is strong enough that, if helped to ${his} feet first, ${he} is capable of remaining upright, if barely.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							r.push(`${He} can barely even crawl with such a huge stomach bulging out from ${him}. It takes everything ${he} has to avoid being forced to drag ${himself} along.`);
 						}
 					}
 				} else if (slave.height >= 185) {
@@ -1419,44 +1238,36 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 				r.push(`${He} is massively pregnant,`);
 				if (slave.physicalAge <= 3) {
 					r.push(`and ${his} giant belly is as big as ${he} is.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} can barely waddle with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`${He} can barely even crawl with such a huge stomach bulging out from ${him}. It takes everything ${he} has just to avoid being immobilized by it.`);
 						}
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} giant belly is nearly as big as ${he} is.`);
-					if (hasAnyNaturalLegs(slave)) {
-						r.push(`${He} requires assistance to`);
-						if (!hasBothLegs(slave)) {
-							if (!market) {
-								r.push(`stand up,`);
+					if (canWalk(slave)) {
+						r.push(`${He} can barely waddle with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
 							} else {
-								r.push(`stand up.`);
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
 							}
 						} else {
-							if (!market) {
-								r.push(`get to ${his} feet,`);
-							} else {
-								r.push(`get to ${his} feet.`);
-							}
-						}
-						if (!market) {
-							r.push(`and uses a wheeled stand to support ${his} pregnancy when ${he} must walk anywhere.`);
+							r.push(`${He} can barely crawl with such a huge stomach bulging out from ${him}, pushing into the ground.`);
 						}
 					}
 				} else if (slave.weight > 190) {
@@ -1500,22 +1311,36 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 				r.push(`${He} looks full term with octuplets,`);
 				if (slave.physicalAge <= 3) {
 					r.push(`and ${his} giant belly is as big as ${he} is.`);
-					if (hasAnyLegs(slave)) {
-						r.push(`${He} requires assistance to get to ${his}`);
-						if (!market) {
-							r.push(`feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere`);
+					if (canWalk(slave)) {
+						r.push(`${He} can barely waddle with such an awkward bulge hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back heavily arched in an attempt to support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
 						} else {
-							r.push(`feet.`);
+							r.push(`${He} can barely crawl with such a huge stomach bulging out from ${him}, pushing into the ground.`);
 						}
 					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} giant belly is nearly as big as ${he} is.`);
-					if (hasAnyLegs(slave)) {
-						r.push(`${He} requires assistance to get to ${his}`);
-						if (!market) {
-							r.push(`feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
 						} else {
-							r.push(`feet.`);
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
 						}
 					}
 				} else if (slave.weight > 190) {
@@ -1542,9 +1367,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not too obvious. Only the firmness at its top gives away ${his} pregnancy.`);
 				} else if (slave.height >= 185) {
@@ -1583,9 +1438,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with septuplets,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not obvious. Only the firmness at ${his} belly's top and the weight in its sway give any hint that something's bulging it outwards.`);
 				} else if (slave.height >= 185) {
@@ -1610,9 +1495,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not too obvious. Only the firmness at its top gives away ${his} pregnancy.`);
 				} else if (slave.height >= 185) {
@@ -1649,9 +1564,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with sextuplets,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not obvious. Only the firmness at ${his} belly's top and the weight in its sway give any hint that something's bulging it outwards.`);
 				} else if (slave.height >= 185) {
@@ -1676,9 +1621,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not too obvious. Only the firmness at its top gives away ${his} pregnancy.`);
 				} else if (slave.height >= 185) {
@@ -1713,9 +1688,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with quintuplets,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not obvious. Only the firmness at ${his} belly's top and the weight in its sway give any hint that something's bulging it outwards.`);
 				} else if (slave.height >= 185) {
@@ -1740,9 +1745,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not too obvious. Only the firmness at its top gives away ${his} pregnancy.`);
 				} else if (slave.height >= 185) {
@@ -1775,9 +1810,39 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with quadruplets,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.weight > 190) {
 					r.push(`but ${he}'s so massively fat that it's not obvious. Only the firmness at ${his} belly's top and the weight in its sway give any hint that something's bulging it outwards.`);
 				} else if (slave.height >= 185) {
@@ -1802,7 +1867,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1835,7 +1915,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with triplets,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1862,7 +1957,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1893,7 +2003,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks full term with twins,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach pushes into the floor as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1920,7 +2045,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else if (slave.bellyPreg > 0) {
 				r.push(`${He} is enormously pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} womb making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach hangs heavily, navel tickling the floor, as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly lewdly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1949,7 +2089,22 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			} else {
 				r.push(`${He} looks hugely pregnant,`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} implant making ${him} resemble an over inflated blow-up doll.`);
+					if (canWalk(slave)) {
+						r.push(`${His} walk is more of an awkward waddle as ${he} struggles to handle the weight hanging from ${his} middle.`);
+					} else if (canStand(slave)) {
+						r.push(`${He} stands with ${his} back arched to better support ${his} stomach's weight.`);
+					} else if (canMove(slave)) {
+						if (slave.rules.mobility === "permissive") {
+							if (hasBothLegs(slave)) {
+								r.push(`${He} requires assistance to get to ${his} feet, and uses a wheeled stand to support ${his} middle when ${he} must walk anywhere.`);
+							} else {
+								r.push(`${He} uses a wheelchair when ${he} must go somewhere and requires assistance to get in and out of it.`);
+							}
+						} else {
+							r.push(`${His} stomach hangs heavily, navel tickling the floor, as ${he} crawls.`);
+						}
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} huge, drum-taut belly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 190) {
@@ -1975,7 +2130,12 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 			if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
 				r.push(`${His} middle is enormously distended with ${slave.inflationType},`);
 				if (slave.physicalAge <= 3) {
-					r.push(`and ${his} toddlerish body is absolutely filled by ${his} bloated innards. ${He} can barely move ${himself} and resembles an over inflated blow-up doll.`);
+					r.push(`and ${his} toddlerish body is absolutely filled by ${his} bloated innards.`);
+					if (canWalk(slave)) {
+						r.push(`${He} can barely waddle and resembles an over inflated blow-up doll.`);
+					} else {
+						r.push(`${He} greatly resembles an over inflated blow-up doll.`);
+					}
 				} else if (slave.physicalAge <= 12) {
 					r.push(`and ${his} massive, drum-taut belly dominates ${his} poor little frame.`);
 				} else if (slave.weight > 130) {
@@ -6392,7 +6552,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 				} else if (slave.belly >= 120000) {
 					if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
 						if (slave.boobs > 12000) {
-							r.push(`${slave.slaveName}'s giant ${slave.inflationType}-filled belly peaks out from between ${his} massive tits. ${He} finds it impossible to fasten ${his} trousers with ${his} stomach in the way.`);
+							r.push(`${slave.slaveName}'s giant ${slave.inflationType}-filled belly peeks out from between ${his} massive tits. ${He} finds it impossible to fasten ${his} trousers with ${his} stomach in the way.`);
 						} else if (slave.boobs > 4000) {
 							r.push(`${slave.slaveName}'s nurse outfit could be called conservative, if it could cover more than half of ${his} breasts; ${his} giant ${slave.inflationType}-filled belly hangs out from under them, bulging hugely from ${his} unfastened trousers.`);
 						} else {
@@ -6400,7 +6560,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 						}
 					} else if (slave.bellyImplant > 0) {
 						if (slave.boobs > 12000) {
-							r.push(`${slave.slaveName}'s giant implant-filled belly peaks out from between ${his} massive tits. ${He} finds it impossible to fasten ${his} trousers with ${his} stomach in the way.`);
+							r.push(`${slave.slaveName}'s giant implant-filled belly peeks out from between ${his} massive tits. ${He} finds it impossible to fasten ${his} trousers with ${his} stomach in the way.`);
 						} else if (slave.boobs > 4000) {
 							r.push(`${slave.slaveName}'s nurse outfit could be called conservative, if it could cover more than half of ${his} breasts; ${his} giant implant-filled belly hangs out from under them, bulging hugely from ${his} unfastened trousers.`);
 						} else {
@@ -6408,7 +6568,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 						}
 					} else {
 						if (slave.boobs > 12000) {
-							r.push(`${slave.slaveName}'s giant pregnant belly peaks out from between ${his} massive tits. In addition, ${he}'s left ${his} trousers unfastened to give ${his} overfilled womb more room.`);
+							r.push(`${slave.slaveName}'s giant pregnant belly peeks out from between ${his} massive tits. In addition, ${he}'s left ${his} trousers unfastened to give ${his} overfilled womb more room.`);
 						} else if (slave.boobs > 4000) {
 							r.push(`${slave.slaveName}'s nurse outfit could be called conservative, if it could cover more than half of ${his} breasts; ${his} giant pregnant belly hangs out from under them, bulging from ${his} unfastened trousers.`);
 						} else {
@@ -6678,7 +6838,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					} else if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
 						r.push(`${slave.slaveName}'s mini dress barely clings to ${his} hugely swollen belly, leaving it looking much shorter than it really is.`);
 					} else if (slave.bellyImplant > 0) {
-						r.push(`${slave.slaveName}'s mini dress barely clings to ${his} huge implant-filled belly, leaving it looking much shorter than it really is.`);
+						r.push(`${slave.slaveName}'s mini dress barely clings to ${his} big implant-filled belly, leaving it looking much shorter than it really is.`);
 					} else {
 						r.push(`${slave.slaveName}'s mini dress tightly clings to ${his} big pregnant belly, leaving it looking much shorter than it really is.`);
 					}
@@ -11444,7 +11604,7 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					r.push(`${slave.slaveName}'s harem girl outfit sensually accentuates ${his} toned belly.`);
 				}
 				break;
-			case "a comfortable bodysuit":
+			case "a comfortable bodysuit": // WIP belly overhaul point
 				if (slave.belly >= 1000000) {
 					// WIP//
 				} else if (slave.belly >= 750000) {
@@ -11462,27 +11622,41 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 				} else if (slave.weight > 190) {
 					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} massively fat belly, displaying every fold, roll and motion in it.`);
 				} else if (slave.bellyPreg >= 10000 || (slave.bellyAccessory === "a huge empathy belly") || (slave.bellyAccessory === "a large empathy belly")) {
-					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} big pregnant belly, displaying ${his} popped`);
-					if (!["a huge empathy belly", "a large empathy belly"].includes(slave.bellyAccessory)) {
-						r.push(`navel and any movement ${his} babies make.`);
+					if (slave.bellyAccessory === "a large empathy belly" || slave.bellyAccessory === "a huge empathy belly") {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} big pregnant belly, displaying ${his} popped navel.`);
+					} else if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} hugely swollen belly, displaying ${his} bloated figure and every jiggle of ${his} full stomach.`);
+					} else if (slave.bellyImplant > 0) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} big implant-filled belly, prominently displaying ${his} popped navel.`);
 					} else {
-						r.push(`navel.`);
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} big pregnant belly, displaying ${his} popped navel and any movement ${his} babies make.`);
 					}
 				} else if (slave.weight > 160) {
 					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} hugely fat belly, displaying every fold, roll and motion in it.`);
 				} else if (slave.weight > 130) {
 					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} big fat belly, displaying every fold and roll in it.`);
-				} else if (slave.bellyPreg >= 5000 || (slave.bellyAccessory === "a medium empathy belly")) {
-					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} pregnant belly, displaying ${his} popped`);
-					if (!["a medium empathy belly"].includes(slave.bellyAccessory)) {
-						r.push(`navel and any movement ${his} babies make.`);
+				} else if (slave.belly >= 5000 || (slave.bellyAccessory === "a medium empathy belly")) {
+					if (slave.bellyAccessory === "a medium empathy belly") {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} pregnant belly, displaying ${his} popped navel.`);
+					} else if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} jiggling ${slave.inflationType}-filled belly, displaying ${his} bloating and every motion ${his} contents make.`);
+					} else if (slave.bellyImplant > 0) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} implant-filled belly, prominently displaying ${his} flattening navel.`);
 					} else {
-						r.push(`navel.`);
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} pregnant belly, displaying ${his} flattening navel and any movement ${his} babies make.`);
 					}
 				} else if (slave.weight > 95) {
 					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} fat belly, displaying every fold and roll in it.`);
-				} else if (((slave.bellyPreg >= 1500) || (slave.bellyAccessory === "a small empathy belly"))) {
-					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} growing belly, displaying ${his} ripening body.`);
+				} else if (slave.belly >= 1500 || slave.bellyAccessory === "a small empathy belly") {
+					if (slave.bellyAccessory === "a small empathy belly") {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} small pregnant belly, displaying ${his} ripening body.`);
+					} else if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} ${slave.inflationType}-swollen belly, displaying ${his} bloated body.`);
+					} else if (slave.bellyImplant > 0) {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} implant-rounded belly, displaying ${his} swollen body.`);
+					} else {
+						r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} growing belly, displaying ${his} ripening body.`);
+					}
 				} else if (slave.weight > 30) {
 					r.push(`${slave.slaveName}'s bodysuit tightly clings to ${his} chubby belly, displaying every fold and roll in it.`);
 				}
@@ -11520,8 +11694,16 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					r.push(`${slave.slaveName}'s jacket barely closes over ${his} pregnant belly leaving its buttons threatening to pop.`);
 				} else if (slave.weight > 95) {
 					r.push(`${slave.slaveName}'s jacket barely closes over ${his} fat belly forcing plenty of flab out from under its bottom and between the straining buttons.`);
-				} else if (((slave.bellyPreg >= 1500) || (slave.bellyAccessory === "a small empathy belly"))) {
-					r.push(`${slave.slaveName}'s jacket bulges with ${his} growing belly, which can be seen peeking out from underneath.`);
+				} else if (slave.belly >= 1500 || slave.bellyAccessory === "a small empathy belly") {
+					if (slave.bellyAccessory === "a small empathy belly") {
+						r.push(`${slave.slaveName}'s jacket bulges with ${his} small pregnant belly, which can be seen peeking out from underneath.`);
+					} else if (slave.bellyFluid >= slave.bellyPreg && slave.bellyFluid >= slave.bellyImplant) {
+						r.push(`${slave.slaveName}'s mini dress tightly clings to ${his} ${slave.inflationType}-swollen belly.`);
+					} else if (slave.bellyImplant > 0) {
+						r.push(`${slave.slaveName}'s mini dress tightly clings to ${his} implant-rounded belly.`);
+					} else {
+						r.push(`${slave.slaveName}'s jacket bulges with ${his} growing belly, which can be seen peeking out from underneath.`);
+					}
 				} else if (slave.weight > 30) {
 					r.push(`${slave.slaveName}'s jacket bulges with ${his} chubby belly, which can be seen peeking out from underneath as it hangs over ${his} waist of ${his} pants.`);
 				}
@@ -12643,22 +12825,40 @@ App.Desc.belly = function(slave, {market, eventDescription} = {}) {
 					}
 				}
 				if (heavyBelly === 1) {
-					if (canWalk(slave)) {
+					if (canMove(slave)) {
 						r.push(`${His}`);
 						if (slave.bellyPreg >= 3000) {
 							r.push(`pregnancy`);
 						} else {
 							r.push(`stomach`);
 						}
-						r.push(`is so massive that it is difficult for ${him} to move.`);
+						r.push(`is so massive that it is difficult for ${him} to function.`);
 						if (slave.muscles > 95) {
-							r.push(`However, ${he} is so powerfully built that ${he} can manage it with effort, using ${his} arms to support it.`);
+							r.push(`However, ${he} is so powerfully built that ${he} can manage it with effort, using ${his}`);
+							if (hasBothArms(slave)) {
+								r.push(`arms`);
+							} else if (hasAnyArms(slave)) {
+								r.push(`arm`);
+							} else {
+								r.push(`rippling back muscles`);
+							}
+							r.push(`to support it during day to day affairs.`);
 						} else if (slave.muscles > 30) {
-							r.push(`${He} can barely manage to get to ${his} feet unaided, and usually walks with ${his} arms under ${his} belly to help take its weight.`);
+							r.push(`${He} can barely manage to perform daily tasks, and usually moves`);
+							if (hasAnyArms(slave)) {
+								if (hasBothArms(slave)) {
+									r.push(`with ${his} arms under ${his} belly`);
+								} else {
+									r.push(`with ${his} arm under ${his} belly`);
+								}
+								r.push(`to help with its weight.`);
+							} else {
+								r.push(`hunched over due to the weight of ${his} belly.`);
+							}
 						} else if (slave.muscles > 5) {
-							r.push(`${He} requires assistance to get to ${his} feet, and tends to lean on things to help relieve the weight.`);
+							r.push(`${He} requires assistance to do things, and tends to lean on things to help relieve the weight.`);
 						} else {
-							r.push(`${He} cannot get to ${his} feet unaided, and tries to stay seated as much as ${he} can.`);
+							r.push(`${He} requires frequent assistance, and tries to stay seated as much as ${he} can.`);
 						}
 					} else if (slave.belly >= 750000) {
 						r.push(`It dwarfs ${his} torso, making ${him} a vestigial accessory to ${his} belly.`);
diff --git a/src/npc/descriptions/boobs/boobs.js b/src/npc/descriptions/boobs/boobs.js
index 97c279fe42747ba27ccc520db5c3557fcb25b448..63bab1973489cec437094ac516ed076d1dc32444 100644
--- a/src/npc/descriptions/boobs/boobs.js
+++ b/src/npc/descriptions/boobs/boobs.js
@@ -260,9 +260,7 @@ App.Desc.boobs = function() {
 		const noun = App.Desc.boobBits.noun(slave.boobs, false, false);
 		const adjNoun = App.Desc.boobBits.noun(slave.boobs, false, true);
 		let r = '';
-		const {he, his, him,
-			He, His,
-			girl} = getPronouns(slave);
+		const {he, his, him, His, girl} = getPronouns(slave);
 
 		switch (slave.clothes) {
 			case "a Fuckdoll suit":
@@ -1424,7 +1422,7 @@ App.Desc.boobs = function() {
  */
 App.Desc.boobsExtra = function(slave, {market, eventDescription} = {}) {
 	const thisArcology = V.arcologies[0];
-	const {he, his, him, He, His, girl} = getPronouns(slave);
+	const {he, his, him, He, His, girl, himself} = getPronouns(slave);
 
 	function penthouseAccessibility() {
 		let rt = '';
@@ -1474,11 +1472,21 @@ App.Desc.boobsExtra = function(slave, {market, eventDescription} = {}) {
 				r += `hunched over due to the weight of ${his} tits.`;
 			}
 		} else if (slave.muscles > 5) {
-			r += `${He} requires assistance to get to ${his} feet, and uses a stand to support them when ${he} must remain upright for more than a short time.`;
+			r += `${He} requires assistance to get to ${his} feet, and`;
+			if (slave.rules.mobility === "permissive") {
+				r += ` uses a stand to support them when ${he} must remain upright for more than a short time.`;
+			} else {
+				r += ` has to make use of walls and objects to support ${himself} or risk falling under their weight.`;
+			}
 		} else if ((slave.assignment === App.Data.Facilities.dairy.jobs.cow.assignment) && (V.dairyRestraintsSetting > 1) && (dairyTest(slave))) {
 			r += `The straps that secure ${him} to the milking machine have become less necessary since ${his} breasts grew to the point where they pin ${him} to it.`;
 		} else {
-			r += `${He} cannot get to ${his} feet unaided, and uses a wheeled stand to support ${his} boobs when ${he} must walk or remain upright.`;
+			r += `${He} cannot get to ${his} feet unaided, and`;
+			if (slave.rules.mobility === "permissive") {
+				r += ` uses a wheeled stand to support ${his} boobs when ${he} must walk or remain upright.`;
+			} else {
+				r += ` has to make use of walls and objects to support ${himself} and remain upright.`;
+			}
 		}
 		return r;
 	}
diff --git a/src/npc/descriptions/describePiercings.js b/src/npc/descriptions/describePiercings.js
index d1a66a8ea26f15ca4d9b21a0ae750eb6a975a17c..0c8f026cd6f98a48a0bbeee6a192bbaf9e29f651 100644
--- a/src/npc/descriptions/describePiercings.js
+++ b/src/npc/descriptions/describePiercings.js
@@ -803,8 +803,10 @@ App.Desc.piercing = function(slave, surface) {
 									if (slave.devotion > 20) {
 										if (slave.energy > 90) {
 											r.push(`${His} very special body allows ${him} to wear an incredibly lewd piece of jewelry: a thin golden chain that runs from nipple to nipple, through ${his} pierced cockhead. The chain is short, and any motion at all tugs at ${his} nipples and penis.`);
-											 if (canWalk(slave)) {
+											 if (canWalk(slave) || (canMove(slave) && slave.rules.mobility === "permissive" && hasBothLegs(slave))) {
 												r.push(`The stimulation is almost unbearable, and the mere act of walking sometimes causes ${him} to cum.`);
+											} else if (canMove(slave)) {
+												r.push(`The stimulation is almost unbearable, as any effort to move ${himself} starts building up an orgasm.`);
 											}
 										}
 									}
diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js
index 0b0a67fd0dd013f9cded7abdc6b894b0b0e0abc1..291c36663b334ffc807481027440efa09d948fdf 100644
--- a/src/npc/descriptions/descriptionWidgets.js
+++ b/src/npc/descriptions/descriptionWidgets.js
@@ -575,143 +575,144 @@ App.Desc.ageAndHealth = function(slave) {
 					r += `ancient by any measure, over a century old. `;
 				}
 			}
-			if (slave.actualAge !== slave.physicalAge) {
-				r += ` However, ${he} has the body of a ${num(slave.physicalAge)}-year-old; `;
-				if (slave.physicalAge < 18 && slave.actualAge >= 18) {
-					r += `a stark contrast given ${his} maturity. `;
-				} else if (slave.physicalAge < 18 && slave.actualAge < 18) {
-					r += `a noticeable difference thanks to ${his} immaturity. `;
-				} else if (slave.physicalAge <= slave.actualAge - 20 || slave.physicalAge >= slave.actualAge + 20) {
-					r += `a shocking difference from ${his} actual age. `;
-				} else if (slave.physicalAge <= slave.actualAge - 10 || slave.physicalAge >= slave.actualAge + 10) {
-					r += `a noticeable difference from ${his} actual age. `;
-				} else if (slave.physicalAge <= slave.actualAge - 5 || slave.physicalAge >= slave.actualAge + 5) {
-					r += `a barely noticeable difference from ${his} actual age. `;
+		}
+		
+		if (slave.actualAge !== slave.physicalAge) {
+			r += ` However, ${he} has the body of a ${num(slave.physicalAge)}-year-old; `;
+			if (slave.physicalAge < 18 && slave.actualAge >= 18) {
+				r += `a stark contrast given ${his} maturity. `;
+			} else if (slave.physicalAge >= 18 && slave.actualAge < 18) {
+				r += `a noticeable difference thanks to ${his} immaturity. `;
+			} else if (slave.physicalAge <= slave.actualAge - 20 || slave.physicalAge >= slave.actualAge + 20) {
+				r += `a shocking difference from ${his} actual age. `;
+			} else if (slave.physicalAge <= slave.actualAge - 10 || slave.physicalAge >= slave.actualAge + 10) {
+				r += `a noticeable difference from ${his} actual age. `;
+			} else if (slave.physicalAge <= slave.actualAge - 5 || slave.physicalAge >= slave.actualAge + 5) {
+				r += `a barely noticeable difference from ${his} actual age. `;
+			} else {
+				r += `though it is hard to tell the difference from ${his} actual age. `;
+			}
+		}
+		/*
+		 ** This section replaces the age/therapy texts, giving more details for the NCS condition.
+		 */
+		if (slave.geneMods.NCS) {
+			let bodyNCS;
+			if (slave.vagina < 0 && slave.dick <= 0) {
+				bodyNCS = "childlike";
+			} else if (slave.vagina < 0 && slave.dick > 0) {
+				bodyNCS = "shota";
+			} else if (slave.vagina > 0 && slave.dick <= 0) {
+				bodyNCS = "loli";
+			} else {
+				bodyNCS = "loli/shota";
+			}
+			r += ` ${He} appears to be ${slave.visualAge} years old and ${he} `;
+			if (slave.visualAge <= 8) {
+				r += `has induced <span class="orange">NCS</span> and will always have a ${bodyNCS} body, no matter how long ${he} lives. `;
+			} else if (slave.visualAge < 13) {
+				r += `has induced <span class="orange">NCS</span> and will have a ${bodyNCS} body for the rest of ${his} life. `;
+			} else if (slave.visualAge < 20) {
+				r += `still has a teen body for now, but with ${his} <span class="orange">NCS,</span> ${he} will eventually regress in age to look like a little ${boy} again. `;
+			} else {
+				r += `still has the body of an adult, but ${his} <span class="orange">NCS</span> has `;
+				if (slave.physicalAge - slave.visualAge <= 5) {
+					r += `not really begun to youthen ${his} appearance yet. `;
+				} else if (slave.physicalAge - slave.visualAge <= 10) {
+					r += `clearly been at work on ${him}, making ${him} appear younger. `;
+				} else if (slave.physicalAge - slave.visualAge <= 20) {
+					r += `obviously helped take more than a decade off of ${his} age. `;
 				} else {
-					r += `though it is hard to tell the difference from ${his} actual age. `;
+					r += `intensely youthened ${him}. `;
 				}
 			}
-			/*
-			 ** This section replaces the age/therapy texts, giving more details for the NCS condition.
-			 */
-			if (slave.geneMods.NCS) {
-				let bodyNCS;
-				if (slave.vagina < 0 && slave.dick <= 0) {
-					bodyNCS = "childlike";
-				} else if (slave.vagina < 0 && slave.dick > 0) {
-					bodyNCS = "shota";
-				} else if (slave.vagina > 0 && slave.dick <= 0) {
-					bodyNCS = "loli";
-				} else {
-					bodyNCS = "loli/shota";
-				}
-				r += ` ${He} appears to be ${slave.visualAge} years old and ${he} `;
-				if (slave.visualAge <= 8) {
-					r += `has induced <span class="orange">NCS</span> and will always have a ${bodyNCS} body, no matter how long ${he} lives. `;
-				} else if (slave.visualAge < 13) {
-					r += `has induced <span class="orange">NCS</span> and will have a ${bodyNCS} body for the rest of ${his} life. `;
+		} else if (slave.physicalAge !== slave.visualAge) {
+			if (slave.visualAge <= slave.physicalAge - 20 || slave.visualAge >= slave.physicalAge + 20) {
+				r += ` ${He} has undergone radical age therapy that makes ${him} look `;
+			} else if (slave.visualAge <= slave.physicalAge - 10 || slave.visualAge >= slave.physicalAge + 10) {
+				r += ` ${He} has undergone drastic age therapy that makes ${him} look `;
+			} else if (slave.visualAge <= slave.physicalAge - 5 || slave.visualAge >= slave.physicalAge + 5) {
+				r += ` ${He} has undergone noticeable age therapy that makes ${him} look `;
+			} else {
+				r += ` For various reasons, ${he} looks `;
+			}
+			if (slave.physicalAge > slave.visualAge) {
+				if (slave.physicalAge < slave.visualAge + 5) {
+					r += `a slightly younger ${slave.visualAge}. `;
 				} else if (slave.visualAge < 20) {
-					r += `still has a teen body for now, but with ${his} <span class="orange">NCS,</span> ${he} will eventually regress in age to look like a little ${boy} again. `;
-				} else {
-					r += `still has the body of an adult, but ${his} <span class="orange">NCS</span> has `;
-					if (slave.physicalAge - slave.visualAge <= 5) {
-						r += `not really begun to youthen ${his} appearance yet. `;
-					} else if (slave.physicalAge - slave.visualAge <= 10) {
-						r += `clearly been at work on ${him}, making ${him} appear younger. `;
-					} else if (slave.physicalAge - slave.visualAge <= 20) {
-						r += `obviously helped take more than a decade off of ${his} age. `;
-					} else {
-						r += `intensely youthened ${him}. `;
-					}
-				}
-			} else if (slave.physicalAge !== slave.visualAge) {
-				if (slave.visualAge <= slave.physicalAge - 20 || slave.visualAge >= slave.physicalAge + 20) {
-					r += ` ${He} has undergone radical age therapy that makes ${him} look `;
-				} else if (slave.visualAge <= slave.physicalAge - 10 || slave.visualAge >= slave.physicalAge + 10) {
-					r += ` ${He} has undergone drastic age therapy that makes ${him} look `;
-				} else if (slave.visualAge <= slave.physicalAge - 5 || slave.visualAge >= slave.physicalAge + 5) {
-					r += ` ${He} has undergone noticeable age therapy that makes ${him} look `;
+					r += `like ${he}'s barely an adult. `;
+				} else if (slave.visualAge < 25) {
+					r += `barely into ${his} early twenties. `;
+				} else if (slave.visualAge < 30) {
+					r += `like ${he}'s still in ${his} twenties. `;
+				} else if (slave.visualAge < 35) {
+					r += `barely thirty. `;
+				} else if (slave.visualAge < 40) {
+					r += `still in ${his} thirties. `;
+				} else if (slave.visualAge < 45) {
+					r += `barely forty. `;
+				} else if (slave.visualAge < 50) {
+					r += `still in ${his} forties. `;
+				} else if (slave.visualAge < 55) {
+					r += `barely fifty. `;
+				} else if (slave.visualAge < 60) {
+					r += `still in ${his} fifties. `;
+				} else if (slave.visualAge < 65) {
+					r += `barely sixty. `;
+				} else if (slave.visualAge < 70) {
+					r += `still in ${his} sixties. `;
+				} else if (slave.visualAge < 75) {
+					r += `barely seventy. `;
+				} else if (slave.visualAge < 80) {
+					r += `still in ${his} seventies. `;
+				} else if (slave.visualAge < 85) {
+					r += `barely eighty. `;
+				} else if (slave.visualAge < 90) {
+					r += `still in ${his} eighties. `;
+				} else if (slave.visualAge < 95) {
+					r += `barely ninety. `;
+				} else if (slave.visualAge < 100) {
+					r += `still in ${his} nineties. `;
 				} else {
-					r += ` For various reasons, ${he} looks `;
+					r += `a younger ${slave.visualAge}. `;
 				}
-				if (slave.physicalAge > slave.visualAge) {
-					if (slave.physicalAge < slave.visualAge + 5) {
-						r += `a slightly younger ${slave.visualAge}. `;
-					} else if (slave.visualAge < 20) {
-						r += `like ${he}'s barely an adult. `;
-					} else if (slave.visualAge < 25) {
-						r += `barely into ${his} early twenties. `;
-					} else if (slave.visualAge < 30) {
-						r += `like ${he}'s still in ${his} twenties. `;
-					} else if (slave.visualAge < 35) {
-						r += `barely thirty. `;
-					} else if (slave.visualAge < 40) {
-						r += `still in ${his} thirties. `;
-					} else if (slave.visualAge < 45) {
-						r += `barely forty. `;
-					} else if (slave.visualAge < 50) {
-						r += `still in ${his} forties. `;
-					} else if (slave.visualAge < 55) {
-						r += `barely fifty. `;
-					} else if (slave.visualAge < 60) {
-						r += `still in ${his} fifties. `;
-					} else if (slave.visualAge < 65) {
-						r += `barely sixty. `;
-					} else if (slave.visualAge < 70) {
-						r += `still in ${his} sixties. `;
-					} else if (slave.visualAge < 75) {
-						r += `barely seventy. `;
-					} else if (slave.visualAge < 80) {
-						r += `still in ${his} seventies. `;
-					} else if (slave.visualAge < 85) {
-						r += `barely eighty. `;
-					} else if (slave.visualAge < 90) {
-						r += `still in ${his} eighties. `;
-					} else if (slave.visualAge < 95) {
-						r += `barely ninety. `;
-					} else if (slave.visualAge < 100) {
-						r += `still in ${his} nineties. `;
-					} else {
-						r += `a younger ${slave.visualAge}. `;
-					}
+			} else {
+				if (slave.physicalAge > slave.visualAge - 5) {
+					r += `a slightly older ${slave.visualAge}. `;
+				} else if (slave.visualAge < 20) {
+					r += `like a fresh adult. `;
+				} else if (slave.visualAge < 25) {
+					r += `just over twenty. `;
+				} else if (slave.visualAge < 30) {
+					r += `nearly thirty. `;
+				} else if (slave.visualAge < 35) {
+					r += `just over thirty. `;
+				} else if (slave.visualAge < 40) {
+					r += `nearly forty. `;
+				} else if (slave.visualAge < 45) {
+					r += `just over forty. `;
+				} else if (slave.visualAge < 50) {
+					r += `nearly fifty. `;
+				} else if (slave.visualAge < 55) {
+					r += `just over fifty. `;
+				} else if (slave.visualAge < 60) {
+					r += `nearly sixty. `;
+				} else if (slave.visualAge < 65) {
+					r += `just over sixty. `;
+				} else if (slave.visualAge < 70) {
+					r += `nearly seventy. `;
+				} else if (slave.visualAge < 75) {
+					r += `just over seventy. `;
+				} else if (slave.visualAge < 80) {
+					r += `nearly eighty. `;
+				} else if (slave.visualAge < 85) {
+					r += `just over eighty. `;
+				} else if (slave.visualAge < 90) {
+					r += `nearly ninety. `;
+				} else if (slave.visualAge < 95) {
+					r += `just over ninety. `;
 				} else {
-					if (slave.physicalAge > slave.visualAge - 5) {
-						r += `a slightly older ${slave.visualAge}. `;
-					} else if (slave.visualAge < 20) {
-						r += `like a fresh adult. `;
-					} else if (slave.visualAge < 25) {
-						r += `just over twenty. `;
-					} else if (slave.visualAge < 30) {
-						r += `nearly thirty. `;
-					} else if (slave.visualAge < 35) {
-						r += `just over thirty. `;
-					} else if (slave.visualAge < 40) {
-						r += `nearly forty. `;
-					} else if (slave.visualAge < 45) {
-						r += `just over forty. `;
-					} else if (slave.visualAge < 50) {
-						r += `nearly fifty. `;
-					} else if (slave.visualAge < 55) {
-						r += `just over fifty. `;
-					} else if (slave.visualAge < 60) {
-						r += `nearly sixty. `;
-					} else if (slave.visualAge < 65) {
-						r += `just over sixty. `;
-					} else if (slave.visualAge < 70) {
-						r += `nearly seventy. `;
-					} else if (slave.visualAge < 75) {
-						r += `just over seventy. `;
-					} else if (slave.visualAge < 80) {
-						r += `nearly eighty. `;
-					} else if (slave.visualAge < 85) {
-						r += `just over eighty. `;
-					} else if (slave.visualAge < 90) {
-						r += `nearly ninety. `;
-					} else if (slave.visualAge < 95) {
-						r += `just over ninety. `;
-					} else {
-						r += `an ancient ${slave.visualAge}. `;
-					}
+					r += `an ancient ${slave.visualAge}. `;
 				}
 			}
 		}
@@ -1083,8 +1084,10 @@ App.Desc.shortLimbs = function(slave) {
 	}
 
 	r += " ";
-	if (!canWalk(slave)) {
+	if (!canMove(slave)) {
 		r += " Immob ";
+	} else if (!canWalk(slave)) {
+		r += " Hindered ";
 	}
 	if (slave.heels === 1) {
 		r += " Heel ";
@@ -1196,8 +1199,10 @@ App.Desc.longLimbs = function(slave) {
 		}
 	}
 
-	if (!canWalk(slave)) {
+	if (!canMove(slave)) {
 		r += "Immobile. ";
+	} else if (!canWalk(slave)) {
+		r += "Hindered. ";
 	}
 	if (slave.heels === 1) {
 		r += "Heeled. ";
diff --git a/src/npc/descriptions/dimensions.js b/src/npc/descriptions/dimensions.js
index 6a5a25208ba98965e4637b38701fed034c7f7c23..669f9deaf678d8e30efde9c67e74d0c5ddd3b828 100644
--- a/src/npc/descriptions/dimensions.js
+++ b/src/npc/descriptions/dimensions.js
@@ -238,28 +238,28 @@ App.Desc.dimensions = function(slave) {
 		if (slave.muscles > 95) {
 			r.push(`<span class="pink">extremely muscular,</span> with defined pecs, powerful glutes, and massive`);
 			if (slave.weight > 95) {
-				r.push(`traps hidden beneath a layer of fat`);
+				r.push(`traps hidden beneath a layer of fat.`);
 			} else {
 				r.push(`traps.`);
 			}
 		} else if (slave.muscles > 50) {
 			r.push(`<span class="pink">quite muscular,</span> with ripped abs, strong shoulders, and defined`);
 			if (slave.weight > 95) {
-				r.push(`lats hidden beneath a layer of fat`);
+				r.push(`lats hidden beneath a layer of fat.`);
 			} else {
 				r.push(`lats.`);
 			}
 		} else if (slave.muscles > 30) {
 			r.push(`<span class="pink">well built,</span> yet feminine with defined abs and strong shapely`);
 			if (slave.weight > 95) {
-				r.push(`muscles hidden beneath a layer of fat`);
+				r.push(`muscles hidden beneath a layer of fat.`);
 			} else {
 				r.push(`muscles.`);
 			}
 		} else if (slave.muscles > 5) {
 			r.push(`<span class="pink">well built,</span> yet feminine, with just-visible`);
 			if (slave.weight > 30) {
-				r.push(`muscles hidden beneath a layer of fat`);
+				r.push(`muscles hidden beneath a layer of fat.`);
 			} else {
 				r.push(`muscles.`);
 			}
diff --git a/src/npc/descriptions/longSlave.js b/src/npc/descriptions/longSlave.js
index 9569c7e4b6abe59f4cc807c10ccb96f7944685f1..dc9cbb95dcfac55469775ac2fc92a077fcaf96e6 100644
--- a/src/npc/descriptions/longSlave.js
+++ b/src/npc/descriptions/longSlave.js
@@ -439,10 +439,10 @@ App.Desc.longSlave = function(slave = V.activeSlave, {market = 0, eventDescripti
 
 	let scarCounter = 0;
 	for (let scarName in slave.scar) {
-		if (slave.ID === V.Bodyguard.ID && scarCounter > 1) {
+		if (slave.ID === V.BodyguardID && scarCounter > 1) {
 			r.push(`${His} scars make ${him} look even more menacing than ${he} actually is. `);
 			break;
-		} else if ((slave.ID === V.Bodyguard.ID) && slave.scar[scarName].menacing > 0) {
+		} else if ((slave.ID === V.BodyguardID) && slave.scar[scarName].menacing > 0) {
 			r.push(`${His} menacing scar makes ${him} look terrifying. `);
 			break;
 		} else if ((slave.ID === V.WardenessID) && scarCounter > 1) {
@@ -786,4 +786,3 @@ App.Desc.longSlave = function(slave = V.activeSlave, {market = 0, eventDescripti
 
 	return el;
 };
-
diff --git a/src/npc/descriptions/sceneIntro.js b/src/npc/descriptions/sceneIntro.js
index 4ceea4159e9140afb998fb2836de19c039bf7c6c..a069b6a4426157f5ce00c33c9007e5729f39a778 100644
--- a/src/npc/descriptions/sceneIntro.js
+++ b/src/npc/descriptions/sceneIntro.js
@@ -272,9 +272,9 @@ App.Desc.sceneIntro = function(slave, {market, eventDescription} = {}) {
 	function sleepLoc() {
 		const r = [];
 
-		if (slave.ID === V.HeadGirl.ID && V.HGSuite === 1) {
+		if (slave.ID === V.HeadGirlID && V.HGSuite === 1) {
 			r.push(`${He} lives in ${his} own suite within your penthouse,`);
-		} else if ((slave.ID === V.Bodyguard.ID) && (V.dojo > 1)) {
+		} else if ((slave.ID === V.BodyguardID) && (V.dojo > 1)) {
 			r.push(`${He} lives in ${his} own room within the armory,`);
 		} else if ((slave.assignment === "work in the dairy") && (V.dairyRestraintsSetting > 1)) {
 			r.push(`${He} sleeps attached to a milking machine,`);
diff --git a/src/npc/descriptions/style/clothing.js b/src/npc/descriptions/style/clothing.js
index 21b118bd427eeab4fa4fbcb16a98663710f18ad6..231e14bf1b0e51833bd3cf95c0791b7060f5c153 100644
--- a/src/npc/descriptions/style/clothing.js
+++ b/src/npc/descriptions/style/clothing.js
@@ -785,9 +785,9 @@ App.Desc.clothing = function(slave, {market, eventDescription} = {}) {
 					r.push(`tunic, and matching skirt.`);
 				}
 				r.push(`${His} uniform is pinned with the`);
-				if (slave.ID === V.Bodyguard.ID || slave.ID === V.HeadGirl.ID || slave.ID === V.Recruiter.ID || slave.ID === V.Concubine.ID) {
+				if (slave.ID === V.BodyguardID || slave.ID === V.HeadGirlID || slave.ID === V.RecruiterID || slave.ID === V.ConcubineID) {
 					r.push(`golden whip and manacles insignia of a senior slave officer,`);
-				} else if ((slave.ID === V.Madam.ID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.Matron.ID) || (slave.ID === V.djID) || (slave.ID === V.Milkmaid.ID) || (slave.ID === V.Farmer.ID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
+				} else if ((slave.ID === V.MadamID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.MatronID) || (slave.ID === V.djID) || (slave.ID === V.MilkmaidID) || (slave.ID === V.FarmerID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
 					r.push(`golden manacles insignia of a junior slave officer,`);
 				} else if ((slave.skill.combat > 0) && getLimbCount(slave, 105) > 0) {
 					r.push(`silver spear and manacles insignia of an augmented slave soldier,`);
@@ -807,9 +807,9 @@ App.Desc.clothing = function(slave, {market, eventDescription} = {}) {
 					r.push(`tunic, and matching trousers.`);
 				}
 				r.push(`${His} uniform is pinned with the`);
-				if (slave.ID === V.Bodyguard.ID || slave.ID === V.HeadGirl.ID || slave.ID === V.Recruiter.ID || slave.ID === V.Concubine.ID) {
+				if (slave.ID === V.BodyguardID || slave.ID === V.HeadGirlID || slave.ID === V.RecruiterID || slave.ID === V.ConcubineID) {
 					r.push(`golden whip and manacles insignia of a senior slave officer,`);
-				} else if ((slave.ID === V.Madam.ID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.Matron.ID) || (slave.ID === V.djID) || (slave.ID === V.Milkmaid.ID) || (slave.ID === V.Farmer.ID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
+				} else if ((slave.ID === V.MadamID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.MatronID) || (slave.ID === V.djID) || (slave.ID === V.MilkmaidID) || (slave.ID === V.FarmerID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
 					r.push(`golden manacles insignia of a junior slave officer,`);
 				} else if ((slave.skill.combat > 0) && getLimbCount(slave, 105)) {
 					r.push(`silver spear and manacles insignia of an augmented slave soldier,`);
@@ -829,9 +829,9 @@ App.Desc.clothing = function(slave, {market, eventDescription} = {}) {
 					r.push(`tunic, and matching miniskirt.`);
 				}
 				r.push(`${His} uniform is pinned with the`);
-				if ((slave.ID === V.Bodyguard.ID) || (slave.ID === V.HeadGirl.ID) || (slave.ID === V.Recruiter.ID) || (slave.ID === V.Concubine.ID)) {
+				if ((slave.ID === V.BodyguardID) || (slave.ID === V.HeadGirlID) || (slave.ID === V.RecruiterID) || (slave.ID === V.ConcubineID)) {
 					r.push(`golden whip and manacles insignia of a senior slave officer,`);
-				} else if ((slave.ID === V.Madam.ID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.Matron.ID) || (slave.ID === V.djID) || (slave.ID === V.Milkmaid.ID) || (slave.ID === V.Farmer.ID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
+				} else if ((slave.ID === V.MadamID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.MatronID) || (slave.ID === V.djID) || (slave.ID === V.MilkmaidID) || (slave.ID === V.FarmerID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
 					r.push(`golden manacles insignia of a junior slave officer,`);
 				} else if ((slave.skill.combat > 0) && getLimbCount(slave, 105)) {
 					r.push(`silver spear and manacles insignia of an augmented slave soldier,`);
@@ -851,9 +851,9 @@ App.Desc.clothing = function(slave, {market, eventDescription} = {}) {
 					r.push(`tunic, and matching skirt.`);
 				}
 				r.push(`${His} uniform is pinned with the`);
-				if ((slave.ID === V.Bodyguard.ID) || (slave.ID === V.HeadGirl.ID) || (slave.ID === V.Recruiter.ID) || (slave.ID === V.Concubine.ID)) {
+				if ((slave.ID === V.BodyguardID) || (slave.ID === V.HeadGirlID) || (slave.ID === V.RecruiterID) || (slave.ID === V.ConcubineID)) {
 					r.push(`golden whip and manacles insignia of a senior slave officer,`);
-				} else if ((slave.ID === V.Madam.ID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.Matron.ID) || (slave.ID === V.djID) || (slave.ID === V.Milkmaid.ID) || (slave.ID === V.Farmer.ID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
+				} else if ((slave.ID === V.MadamID) || (slave.ID === V.NurseID) || (slave.ID === V.AttendantID) || (slave.ID === V.MatronID) || (slave.ID === V.djID) || (slave.ID === V.MilkmaidID) || (slave.ID === V.FarmerID) || (slave.ID === V.StewardessID) || (slave.ID === V.SchoolteacherID) || (slave.ID === V.WardenessID)) {
 					r.push(`golden manacles insignia of a junior slave officer,`);
 				} else if ((slave.skill.combat > 0) && getLimbCount(slave, 105)) {
 					r.push(`silver spear and manacles insignia of an augmented slave soldier,`);
diff --git a/src/npc/descriptions/style/clothingCorset.js b/src/npc/descriptions/style/clothingCorset.js
index 421c149eac37568b484cb6eef6b3ae17058522d4..30165f13965de46c8959c67bd2be8dd4109316fe 100644
--- a/src/npc/descriptions/style/clothingCorset.js
+++ b/src/npc/descriptions/style/clothingCorset.js
@@ -312,20 +312,20 @@ App.Desc.clothingCorset = function(slave) {
 			break;
 		case "a monokini":
 			if (slave.bellyAccessory === "a corset") {
-				r.push(`A corset peaks out from the top of the swimsuit.`);
+				r.push(`A corset peeks out from the top of the swimsuit.`);
 			} else if (slave.bellyAccessory === "an extreme corset") {
-				r.push(`An extreme corset peaks out from the top of the swimsuit.`);
+				r.push(`An extreme corset peeks out from the top of the swimsuit.`);
 			} else if (slave.bellyAccessory === "a support band") {
-				r.push(`${His} support band peaks out from the top of the swimsuit.`);
+				r.push(`${His} support band peeks out from the top of the swimsuit.`);
 			}
 			break;
 		case "overalls":
 			if (slave.bellyAccessory === "a corset") {
-				r.push(`A corset peaks out from the top of the overalls.`);
+				r.push(`A corset peeks out from the top of the overalls.`);
 			} else if (slave.bellyAccessory === "an extreme corset") {
-				r.push(`An extreme corset peaks out from the top of the overalls.`);
+				r.push(`An extreme corset peeks out from the top of the overalls.`);
 			} else if (slave.bellyAccessory === "a support band") {
-				r.push(`${His} support band peaks out from the top of the overalls.`);
+				r.push(`${His} support band peeks out from the top of the overalls.`);
 			}
 			break;
 		case "an apron":
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index 431f2c1aac1cc0e9f49dc93f1a293f5f57910a68..8e119e198680560dd547a20b25a3da46e4c205ec 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -97,12 +97,13 @@ globalThis.generateGenetics = (function() {
 		genes.motherName = setMotherName(activeMother);
 		genes.father = setFatherID(actor2);
 		genes.fatherName = setFatherName(father, activeFather, actor2);
+		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.intelligence = setIntelligence(father, mother, activeMother, actor2);
-		genes.face = setFace(father, mother, activeMother, actor2, genes.geneticQuirks);
+		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);
 		if (genes.geneticQuirks.heterochromia === 2) {
@@ -592,7 +593,7 @@ globalThis.generateGenetics = (function() {
 	}
 
 	// intelligence
-	function setIntelligence(father, mother, activeMother, actor2) {
+	function setIntelligence(father, mother, activeMother, actor2, inbreedingCoeff) {
 		let smarts;
 		if (mother.ID === -1) {
 			if (actor2 === -6) {
@@ -616,29 +617,15 @@ globalThis.generateGenetics = (function() {
 			smarts = mother.intelligence;
 		}
 		if (V.inbreeding === 1) {
-			if (mother.ID !== -1) {
-				if (father !== 0 && father.ID === -1 && activeMother.breedingMark !== 1) {
-					if (smarts >= -95 && jsRandom(1, 100) < 40) {
-						smarts -= jsRandom(1, 10);
-						if (smarts >= -95 && jsRandom(1, 100) < 20) {
-							smarts -= jsRandom(1, 5);
-						}
-					}
-				} else {
-					if (smarts >= -95 && jsRandom(1, 100) < 50) {
-						smarts -= jsRandom(1, 15);
-						if (smarts >= -95 && jsRandom(1, 100) < 30) {
-							smarts -= jsRandom(1, 15);
-						}
-					}
-				}
+			if (jsRandom(1, 100) < inbreedingCoeff*200) {
+				smarts -= Math.abs(normalRandInt(5*inbreedingCoeff, 30*inbreedingCoeff, -100*inbreedingCoeff, 100*inbreedingCoeff));
 			}
 		}
 		return Math.clamp(smarts, -100, 100);
 	}
 
 	// face
-	function setFace(father, mother, activeMother, actor2, genes) {
+	function setFace(father, mother, activeMother, actor2, genes, inbreedingCoeff) {
 		let face;
 		if (genes.pFace > 0 && genes.uFace > 0) {
 			face = 0;
@@ -668,19 +655,8 @@ globalThis.generateGenetics = (function() {
 			face = mother.face;
 		}
 		if (V.inbreeding === 1 && genes.pFace === 0 && genes.uFace === 0) {
-			if (mother.ID !== -1) {
-				if (father !== 0 && father.ID === -1 && activeMother.breedingMark !== 1) {
-					if (face > -100 && jsRandom(1, 100) > 60) {
-						face -= jsRandom(2, 20);
-					}
-				} else {
-					if (face > -100 && jsRandom(1, 100) < 50) {
-						face -= jsRandom(1, 15);
-						if (face >= -95 && jsRandom(1, 100) < 30) {
-							face -= jsRandom(5, 20);
-						}
-					}
-				}
+			if (jsRandom(1, 100) < inbreedingCoeff*200) {
+				face -= Math.abs(normalRandInt(5*inbreedingCoeff, 35*inbreedingCoeff, -100*inbreedingCoeff, 100*inbreedingCoeff));
 			}
 		}
 		return Math.clamp(face, -100, 100);
@@ -1367,6 +1343,8 @@ globalThis.generateChild = function (mother, ovum, incubator=false) {
 		child.navelPiercing = 0;
 	}
 
+	child.inbreedingCoeff = genes.inbreedingCoeff;
+
 	generatePronouns(child);
 
 	return child;
diff --git a/src/npc/generate/generateMarketSlave.js b/src/npc/generate/generateMarketSlave.js
index 96b1e0d13d97e4940d9dc97a73c31aca43b7e626..1fd4b5a734329064f1e2b8c9d46a691b88095e69 100644
--- a/src/npc/generate/generateMarketSlave.js
+++ b/src/npc/generate/generateMarketSlave.js
@@ -1549,6 +1549,63 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1
 
 			break;
 		}
+		case "TUO": {
+			SGProp.minAge = V.minimumSlaveAge;
+			SGProp.maxAge = Math.max(V.fertilityAge + jsRandom(0,1), V.minimumSlaveAge + jsRandom(0,3));
+			SGProp.disableDisability = 1;
+			slave = GenerateNewSlave("", SGProp);
+			slave.origin = "You bought $him from The Utopian Orphanage right after $his graduation.";
+			slave.career = "a slave";
+			setHealth(slave, jsRandom(60, 80), 0, Math.max(normalRandInt(0, 4), 0), 0, jsRandom(0, 20));
+			slave.devotion = jsRandom(50, 75);
+			slave.trust = jsRandom(50, 75);
+			if (V.TUO.schoolUpgrade === 1) {
+				slave.face = random(30, 100);
+				slave.intelligence = Intelligence.random({limitIntelligence: [55, 100]});
+				slave.intelligenceImplant = 30;
+				slave.accent = 1;
+				slave.skill.entertainment = 75;
+				slave.skill.combat = jsEither(0,1,1);
+			} else {
+				slave.face = random(10, 65);
+				slave.intelligence = Intelligence.random({limitIntelligence: [35, 75]});
+				slave.intelligenceImplant = 15;
+				slave.accent = jsEither(0,1);
+				slave.skill.entertainment = 45;
+				slave.skill.combat = 0;
+			}
+			if (V.TUO.schoolUpgrade === 2) {
+				slave.skill.vaginal = 15;
+				slave.skill.oral = 15;
+				slave.skill.anal = 15;
+				slave.skill.whoring = 15;
+				slave.energy = jsRandom(40,95);
+			} else {
+				slave.skill.vaginal = 0;
+				slave.skill.oral = 0;
+				slave.skill.anal = 0;
+				slave.skill.whoring = 0;
+				slave.energy = jsRandom(15,65);
+			}
+			slave.faceImplant = 0;
+			slave.weight = jsRandom(-17,17);
+			slave.muscles = jsRandom(0,20);
+			slave.lips = jsRandom(10,40);
+			slave.lipsImplant = 0;
+			if (slave.physicalAge > 16) {
+				slave.boobs = jsEither(200, jsRandom(200,400), jsRandom(200, 950));
+			} else if (slave.physicalAge <= 16 && slave.physicalAge > 13) {
+				slave.boobs = jsEither(200, jsRandom(200, 400));
+			} else {
+				slave.boobs = 200;
+			}
+			slave.boobsImplant = 0;
+			slave.butt = jsRandom(0,2);
+			slave.buttImplant = 0;
+			slave.vagina = 0;
+			slave.anus = 0;
+			break;
+		}
 		case "GRI": {
 			SGProp.minAge = 16;
 			SGProp.maxAge = 19;
diff --git a/src/npc/interaction/FFuckdollAnal.tw b/src/npc/interaction/FFuckdollAnal.tw
index 1eb3cb0e90e5b3ea82476f72778db26eb3ba835b..f06e4b2967a93750080b6ca1010a5b9432065c86 100644
--- a/src/npc/interaction/FFuckdollAnal.tw
+++ b/src/npc/interaction/FFuckdollAnal.tw
@@ -49,7 +49,3 @@ You climax<<if $PC.dick != 0>>, filling $his rectum with your cum,<</if>> and re
 	In any case, <span class="virginity loss">$his rear hole has been broken in.</span>
 	<<set getSlave($AS).anus = 1>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/FFuckdollImpreg.tw b/src/npc/interaction/FFuckdollImpreg.tw
index 00db19147b0c1e2f4c0499b841734303eb08a34f..64745f102e46e0307816f23f91b67e7c5ea3f4eb 100644
--- a/src/npc/interaction/FFuckdollImpreg.tw
+++ b/src/npc/interaction/FFuckdollImpreg.tw
@@ -85,7 +85,3 @@ You repeat this ritual throughout the week, ensuring that $he will be an @@.preg
 		<<set $failedElite += 5>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/FFuckdollOral.tw b/src/npc/interaction/FFuckdollOral.tw
index 66cfc24ef2f8af2be2b5bad756442374a1c96503..b1f425c1a1d9b5460e91463d98e228b4221164ba 100644
--- a/src/npc/interaction/FFuckdollOral.tw
+++ b/src/npc/interaction/FFuckdollOral.tw
@@ -30,7 +30,3 @@ You climax, <<if $PC.dick != 0>>blowing your load down $his throat<<else>>giving
 	<<else>>$He swallows repeatedly as $he returns to $his resting posture.
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/FFuckdollVaginal.tw b/src/npc/interaction/FFuckdollVaginal.tw
index 77f62d82afb749e73e274e237ad5f739ea22fd88..1daa848d979a5be77c22338365168c94d9d015b6 100644
--- a/src/npc/interaction/FFuckdollVaginal.tw
+++ b/src/npc/interaction/FFuckdollVaginal.tw
@@ -49,7 +49,3 @@ You climax<<if $PC.dick != 0>>, your cum shooting forward to splash against $his
 	In any case, @@.lime;$his front hole has been broken in.@@
 	<<set getSlave($AS).vagina = 1>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/FSuckle.tw b/src/npc/interaction/FSuckle.tw
index 4a2a5eb7eb129d2db6d19cb3ea8ee583230cb2b9..a145d6c893f5e8a7b6e81d2fd4b2b16de2c7cd7c 100644
--- a/src/npc/interaction/FSuckle.tw
+++ b/src/npc/interaction/FSuckle.tw
@@ -35,7 +35,7 @@ You <<if _mood == 2>>demand<<else>>beckon<</if>> <<= getSlave($AS).slaveName>> t
 	to take a seat on the floor close to you.
 <</if>>
 
-<<if canWalk(getSlave($AS))>>
+<<if canMove(getSlave($AS))>>
 	<<if getSlave($AS).fetish == "mindbroken">>
 		$He complies without a thought.
 	<<elseif getSlave($AS).devotion > 20 && getSlave($AS).trust > 20>>
diff --git a/src/npc/interaction/fAbuse.tw b/src/npc/interaction/fAbuse.tw
index 8ca0f3933661cc7f6182cbf2e30f2e57ee0ca269..b764505c3566b13972d92eec0f0da9a12080b9c6 100644
--- a/src/npc/interaction/fAbuse.tw
+++ b/src/npc/interaction/fAbuse.tw
@@ -3,7 +3,7 @@
 <<set $AS = $activeSlave.ID>>
 <<setLocalPronouns $activeSlave>>
 
-<<if (getSlave($AS).ID == $Bodyguard.ID)>>
+<<if (getSlave($AS).ID == $BodyguardID)>>
 	<<if (getSlave($AS).fetish == "masochist") && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).fetishStrength > 95)>>
 		Knowing how much your bodyguard likes being hurt, you decide to reward $him in $his own particular way.
 	<<elseif (getSlave($AS).muscles < -5) && (getSlave($AS).skill.combat == 0)>>
@@ -416,15 +416,15 @@ from your victim.
 	$He's got no special physical targets for abuse, so you just rape $him hard and fast, raining stinging slaps down on $him as you do. $He cries and whimpers; you finish.
 	<<= VCheck.Both()>>
 <</if>>
-<<if (getSlave($AS).ID !== $Bodyguard.ID)>>
+<<if (getSlave($AS).ID !== $BodyguardID)>>
 	This leaves $him sobbing on the floor <<if ($PC.dick == 0)>>as you shuck off the strap-on and drop it on $his face<<else>>with cum dripping out of $him<</if>>.
-<<elseif (getSlave($AS).ID == $Bodyguard.ID) && (getSlave($AS).fetish == "masochist") && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).fetishStrength > 95)>>
+<<elseif (getSlave($AS).ID == $BodyguardID) && (getSlave($AS).fetish == "masochist") && (getSlave($AS).fetishKnown == 1) && (getSlave($AS).fetishStrength > 95)>>
 	$He thanks you rapidly, trying to break away from the pleasure of your reward. $He quickly gets back to $his feet and stumbles towards the shower, to make sure you won't stay unprotected too long.
 <<else>>
 	Even though $he's in a somewhat bad shape, $he still jumps back to $his feet and stumbles towards the shower, to make sure you won't stay unprotected too long.
 <</if>>
 
-<<if (getSlave($AS).ID !== $Bodyguard.ID)>>
+<<if (getSlave($AS).ID !== $BodyguardID)>>
 	<<if getSlave($AS).minorInjury == 0>>
 		<<if _asspain == 1>>
 			The anal rape leaves $him with a sore butthole.
@@ -440,7 +440,7 @@ from your victim.
 	<<run healthDamage(getSlave($AS), 40)>>
 <</if>>
 
-<<if (getSlave($AS).ID == $Bodyguard.ID)>>
+<<if (getSlave($AS).ID == $BodyguardID)>>
 	<<if (getSlave($AS).muscles <= 5) && (getSlave($AS).skill.combat == 0)>>
 		Your bodyguard accepts this as a punishment for $his uselessness.
 	<<elseif (getSlave($AS).muscles <= 5) && (getSlave($AS).skill.combat != 0)>>
@@ -477,7 +477,7 @@ from your victim.
 <</if>>
 
 <<if (random(1,100) > (20+getSlave($AS).devotion+getSlave($AS).trust))>>
-	<<if (getSlave($AS).fetish !== "submissive") && (getSlave($AS).fetish !== "masochist") && (getSlave($AS).ID != $Bodyguard.ID)>>
+	<<if (getSlave($AS).fetish !== "submissive") && (getSlave($AS).fetish !== "masochist") && (getSlave($AS).ID != $BodyguardID)>>
 		<<set _seed = random(1,8)>>
 		Brutal abuse has left $him
 		<<if (_seed == 1) && (getSlave($AS).behavioralFlaw !== "odd")>>
@@ -510,7 +510,7 @@ from your victim.
 		<</if>>
 	<</if>>
 <</if>>
-<<if (random(1,100) > (50+getSlave($AS).devotion+getSlave($AS).trust)) && (getSlave($AS).ID !== $Bodyguard.ID)>>
+<<if (random(1,100) > (50+getSlave($AS).devotion+getSlave($AS).trust)) && (getSlave($AS).ID !== $BodyguardID)>>
 	<<if (getSlave($AS).fetish !== "mindbroken") && (getSlave($AS).fetishKnown == 0) && (getSlave($AS).clitSetting !== getSlave($AS).fetish)>>
 		$His acceptance of your abuse has twisted $his
 		<<if (random(1,2) == 1) && (getSlave($AS).fetish !== "submissive")>>
diff --git a/src/npc/interaction/fAnus.tw b/src/npc/interaction/fAnus.tw
index 2218ff43a6ea48b24d9451d0f88897eb20ec9c4a..90ee11ac4bfe327ee7b4335cf80f9f6120219a55 100644
--- a/src/npc/interaction/fAnus.tw
+++ b/src/npc/interaction/fAnus.tw
@@ -196,7 +196,7 @@ You call $him over so you can
 	<</if>>
 
 	<<if $postSexCleanUp > 0>>
-		<<if canWalk(getSlave($AS))>>
+		<<if canMove(getSlave($AS))>>
 			<<if getSlave($AS).anus < 2>>
 				<<set _Anus = "tight butt">>
 			<<elseif getSlave($AS).anus < 3>>
@@ -246,7 +246,3 @@ You call $him over so you can
 		<</if>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/fAssistedSex.tw b/src/npc/interaction/fAssistedSex.tw
index e809d033e2bd48e1ddba47c53d8687f8c09ebb98..8efdc030ed43b3d44494d8ca081e23bf30bf5d42 100644
--- a/src/npc/interaction/fAssistedSex.tw
+++ b/src/npc/interaction/fAssistedSex.tw
@@ -96,10 +96,3 @@ $he draws toward you, half-floating on a river of silent, groping hands. When $h
 	<<set getSlave($AS).counter.oral++, $oralTotal++>>
 <</if>>
 Once you are satisfied, you send $him away to clean $himself up before returning to $his duties.
-
-<<if passage() != "Slave Interact">>
-	<<set _fv = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fv>>
-		<<set $slaves[_fv] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fBeg.tw b/src/npc/interaction/fBeg.tw
index a1f94f2c2159a3d0613fdecb2d92607a40a3f36f..0cd9fdb571883ce84ba28826b8367be5b0e5740c 100644
--- a/src/npc/interaction/fBeg.tw
+++ b/src/npc/interaction/fBeg.tw
@@ -438,10 +438,3 @@ You give $him permission to speak, and tell $him that $he may beg $his <<= Writt
 		<<set getSlave($AS).fetish = "submissive", getSlave($AS).fetishKnown = 1>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set _fl = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fl>>
-		<<set $slaves[_fl] = getSlave($AS)>>
-	<</if>>
-<</if>>
\ No newline at end of file
diff --git a/src/npc/interaction/fBellyFuck.tw b/src/npc/interaction/fBellyFuck.tw
index bb2c5857ae12c972aa1dfdc0435fb2d6295a8f7e..3ad1ee54278a0639eae4d5bbcb38efce5d229b71 100644
--- a/src/npc/interaction/fBellyFuck.tw
+++ b/src/npc/interaction/fBellyFuck.tw
@@ -28,7 +28,7 @@ You have $him brought to you so that you can fuck $his hyperpregnant body.
 		massive belly.
 	<</if>>
 	$He won't be a particularly able lover, but enjoying $his immobility is half the point.
-<<elseif !canWalk(getSlave($AS))>>
+<<elseif !canStand(getSlave($AS))>>
 	$He can't support $himself, so $he is left leaning against $his
 	<<if getSlave($AS).bellyPreg >= 1000000>>
 		impossibly exploded baby bump.
@@ -89,10 +89,3 @@ and then spread $his cheeks for easier access to $his <<if canDoVaginal(getSlave
 <<else>>
 	that you've left $him thoroughly exhausted.
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set _fv = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fv>>
-		<<set $slaves[_fv] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fBoobs.tw b/src/npc/interaction/fBoobs.tw
index 037516b5a9676580d68035ba2ae809abbf06bd03..90e91f2a8a0e17ef2b88d386da4af5d307445237 100644
--- a/src/npc/interaction/fBoobs.tw
+++ b/src/npc/interaction/fBoobs.tw
@@ -240,7 +240,3 @@ tits.
 		$He hurries off to wash $his <<if getSlave($AS).boobs > 1500>>acre of cleavage<<elseif getSlave($AS).boobs > 500>>generous cleavage<<else>>chest<</if>> so that you will be unguarded for as little time as possible.
 	<</switch>>
 <</if>>
-
-<<if passage() !== "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/fButt.tw b/src/npc/interaction/fButt.tw
index 99d02192b0a64675463bcfc2b8e4c63fa1e37850..7d3402ad6850bcc55f5a87d0ac18cd192b5ce949 100644
--- a/src/npc/interaction/fButt.tw
+++ b/src/npc/interaction/fButt.tw
@@ -206,7 +206,7 @@ You call $him over so you can
 	$His still-tight ass keeps your load inside $him.
 <</if>>
 
-<<if canWalk(getSlave($AS)) && $postSexCleanUp > 0>>
+<<if canMove(getSlave($AS)) && $postSexCleanUp > 0>>
 <<switch getSlave($AS).assignment>>
 <<case "whore">>
 	$He heads to the bathroom to clean $his <<if canDoVaginal(getSlave($AS)) && canDoAnal(getSlave($AS))>>holes before returning to selling them publicly.<<elseif canDoVaginal(getSlave($AS)) || canDoAnal(getSlave($AS))>>fuckhole before returning to selling it publicly.<<else>>face before returning to selling $his mouth publicly.<</if>>
@@ -244,7 +244,3 @@ You call $him over so you can
 <</if>>
 
 <</if>>
-
-<<if passage() !== "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/fDance.tw b/src/npc/interaction/fDance.tw
index c094f11af1bbb3bbb48cbfaa09177aa447dd7aa7..d53bee364747b24278f9409f1bae1bc4a4c012dc 100644
--- a/src/npc/interaction/fDance.tw
+++ b/src/npc/interaction/fDance.tw
@@ -1638,10 +1638,3 @@ Once the last piece of clothing has hit the floor you let your little slut dance
 		<<set getSlave($AS).fetish = "humiliation", getSlave($AS).fetishKnown = 1>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set _fl = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fl>>
-		<<set $slaves[_fl] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fDick.tw b/src/npc/interaction/fDick.tw
index 4463b3e59ad556f1d0ebb1555167dc48feca9985..0ff958866d2a08e57586a7b2fc5b55d50eed98c1 100644
--- a/src/npc/interaction/fDick.tw
+++ b/src/npc/interaction/fDick.tw
@@ -217,7 +217,7 @@
 	<</if>>
 <</if>>
 
-<<if canWalk(getSlave($AS)) && getSlave($AS).fetish != "mindbroken" && $postSexCleanUp > 0>>
+<<if canMove(getSlave($AS)) && getSlave($AS).fetish != "mindbroken" && $postSexCleanUp > 0>>
 	<<switch getSlave($AS).assignment>>
 	<<case "whore">>
 		$He heads to the bathroom to clean $his dick before returning to selling $his body publicly.
@@ -248,7 +248,7 @@
 	<<case "work as a servant">>
 		$He rushes to wash $his dick, impatient to get back to $his undiminished chores.
 	<<case "work as a nanny">>
-		$He runs off to wash off $his dick before heading back to the $nurseryName.
+		$He hurries off to wash off $his dick before heading back to the $nurseryName.
 	<</switch>>
 <</if>>
 
@@ -266,7 +266,3 @@
 	Rumors spread that you @@.red;enjoy taking it from slaves.@@
 	<<set $PC.degeneracy += 2>>
 <</if>>
-
-<<if passage() !== "Slave Interact">>
-	<<set $slaves[$slaveIndices[getSlave($AS).ID]] = getSlave($AS)>>
-<</if>>
diff --git a/src/npc/interaction/fKiss.tw b/src/npc/interaction/fKiss.tw
index c708b599c0b01f490dd1c1a72b12203340a52d19..0cd7acad44532fea6b209e12eaafb8192af83302 100644
--- a/src/npc/interaction/fKiss.tw
+++ b/src/npc/interaction/fKiss.tw
@@ -7,27 +7,27 @@
 You tell <<= getSlave($AS).slaveName>> to
 <<switch getSlave($AS).mouthAccessory>>
 <<case "none">>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		approach you.
 	<<else>>
 		have another slave help $him to your desk.
 	<</if>>
 <<case "dildo gag">>
-	<<if hasAnyArms(getSlave($AS))>>
+	<<if hasAnyArms(getSlave($AS)) && canMove(getSlave($AS))>>
 		remove $his dildo gag and approach you.
 	<<else>>
 		have another slave remove $his dildo gag and set $him down on your desk.
 	<</if>>
 	<<set _tempGag = getSlave($AS).mouthAccessory, getSlave($AS).mouthAccessory = "none">>
 <<case "massive dildo gag">>
-	<<if hasAnyArms(getSlave($AS))>>
+	<<if hasAnyArms(getSlave($AS)) && canMove(getSlave($AS))>>
 		pull $his enormous dildo gag out of the depths of $his throat and approach you.
 	<<else>>
 		have another slave pull the enormous dildo gag out of the depths of $his throat and set $him down on your desk.
 	<</if>>
 	<<set _tempGag = getSlave($AS).mouthAccessory, getSlave($AS).mouthAccessory = "none">>
 <<default>>
-	<<if hasAnyArms(getSlave($AS))>>
+	<<if hasAnyArms(getSlave($AS)) && canMove(getSlave($AS))>>
 		undo $his gag and approach you.
 	<<else>>
 		have another slave undo $his gag and set $him down on your desk.
@@ -92,7 +92,7 @@ You tell <<= getSlave($AS).slaveName>> to
 	<<if canSee(getSlave($AS))>>
 		into $his <<= App.Desc.eyesColor(getSlave($AS))>>. $He finds the intense attention from $his <<= WrittenMaster()>> disconcerting, and $he looks down after a moment, blushing.
 	<<else>>
-		upon $his face. $He finds the intense attention from $his <<= WrittenMaster()>>disconcerting, and $he looks down after a moment, blushing.
+		upon $his face. $He finds the intense attention from $his <<= WrittenMaster()>> disconcerting, and $he looks down after a moment, blushing.
 	<</if>>
 <<elseif (getSlave($AS).devotion > 20)>>
 	$He hurriedly complies, happy to be near you. Once $he's close, you take a moment to gaze deeply
diff --git a/src/npc/interaction/fLickPussy.tw b/src/npc/interaction/fLickPussy.tw
index d060cddc6a5c768cbed77a2007ff331791c4e18d..c164ca8caf00f677ddf39f588abd71311e9db44b 100644
--- a/src/npc/interaction/fLickPussy.tw
+++ b/src/npc/interaction/fLickPussy.tw
@@ -6,7 +6,7 @@
 
 <<set _devotion = getSlave($AS).devotion>> /*for brevity*/
 
-<<if canWalk(getSlave($AS))>>
+<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>
 	You summon <<= getSlave($AS).slaveName>> to your office and order $him to lie down on the couch. $He does so
 	<<if _devotion > 95>>
 		with a smile, skipping over to give you a quick kiss before $he complies with your request.
@@ -21,10 +21,10 @@
 	You have <<= getSlave($AS).slaveName>> brought into your office and placed on your couch.
 <</if>>
 
-After $he is situated, you go over to $him<<if getSlave($AS).clothes != "no clothing">> and take off $his clothing, causing $him to <<if _devotion > 50>>give you a soft smile<<else>>give you an angry glare<</if>><</if>>. You suddenly grab $his hips and pull $his crotch to your face, causing $him to give a shriek of surprise. Looking at $him directly in the eyes, you begin to run your tongue along $his labia, drawing a soft moan from $him. The combination of the pleasure and the intense look from the _womanP $he<<if _devotion > 95>> loves <<elseif _devotion > 50>>'s accepted as $his <<= WrittenMaster(getSlave($AS))>><<else>> hates/*not sure what to use for "reluctant"*/<</if>> makes $him blush, but you don't let up.
+After $he is situated, you go over to $him<<if getSlave($AS).clothes != "no clothing">> and take off $his clothing, causing $him to <<if _devotion > 50>>give you a soft smile<<else>>give you an angry glare<</if>><</if>>. You suddenly grab $his hips and pull $his crotch to your face, causing $him to <<if canTalk(getSlave($AS))>>give a shriek of surprise<<else>>shudder in surprise<</if>>. Looking at $him directly in the eyes, you begin to run your tongue along $his labia, drawing a <<if canTalk(getSlave($AS))>>soft<<else>>silent<</if>> moan from $him. The combination of the pleasure and the intense look from the _womanP $he<<if _devotion > 95>> loves <<elseif _devotion > 50>>'s accepted as $his <<= WrittenMaster(getSlave($AS))>><<else>> hates/*not sure what to use for "reluctant"*/<</if>> makes $him blush, but you don't let up.
 <<if getSlave($AS).dick == 0>>
-	You give $his clit a few experimental tweaks, causing $his moans to intensify<<if hasBothLegs(getSlave($AS))>> and $his legs to tighten around your head<</if>>.
+	You give $his clit a few experimental tweaks, causing $his <<if canTalk(getSlave($AS))>>moans<<else>>squirming<</if>> to intensify<<if hasBothLegs(getSlave($AS))>> and $his legs to tighten around your head<</if>>.
 <</if>>
-Before $he cums, though, you pull back, causing $him to whine in frustration. You begin again, then pull back again just before $he orgasms. You repeat this several more times, each time causing $him to become more and more frustrated. Finally, one last stroke of your tongue causes $him to go over the edge, making $him scream out <<if _devotion > 50>>your name<</if>> in ecstasy<<if getSlave($AS).dick > 0>><<if canAchieveErection(getSlave($AS))>> and spray cum across $his chest<<else>> and dribble cum from $his limp dick<</if>><</if>>. You send $him back to $his assignment before calling in another slave to clean up the mess.
+Before $he cums, though, you pull back, causing $him to whine in frustration. You begin again, then pull back again just before $he orgasms. You repeat this several more times, each time causing $him to become more and more frustrated. Finally, one last stroke of your tongue causes $him to go over the edge, making $him <<if canTalk(getSlave($AS))>>scream out <<if _devotion > 50>>your name<</if>><<else>>spasm<</if>> in ecstasy<<if getSlave($AS).dick > 0>><<if canAchieveErection(getSlave($AS))>> and spray cum across $his chest<<else>> and dribble cum from $his limp dick<</if>><</if>>. You send $him back to $his assignment before calling in another slave to clean up the mess.
 
 <<set getSlave($AS).counter.vaginal++, $vaginalTotal++>>
diff --git a/src/npc/interaction/fLips.tw b/src/npc/interaction/fLips.tw
index 907be5732da144be1909ffafda156ff03ed55164..36b55dd9eccfcbc5309c290e897845254a008023 100644
--- a/src/npc/interaction/fLips.tw
+++ b/src/npc/interaction/fLips.tw
@@ -219,10 +219,3 @@ You tell <<= getSlave($AS).slaveName>> to
 		<<set getSlave($AS).fetish = "cumslut", getSlave($AS).fetishKnown = 1>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set _fl = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fl>>
-		<<set $slaves[_fl] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fMaternitySwing.tw b/src/npc/interaction/fMaternitySwing.tw
index 2d3a9eeceab5640382ebcc2629c42e9517755bf2..dedbfd384339d305c2a455e97ee1c74fe48fa72f 100644
--- a/src/npc/interaction/fMaternitySwing.tw
+++ b/src/npc/interaction/fMaternitySwing.tw
@@ -52,10 +52,3 @@ The sight of $his swollen body wobbling in mid-air as you pound away at $him nev
 	and the sight of $his ugly tears rolling down $his face to hit the ground far below is one hell of a bonus.
 <</if>>
 Once finished, you release $him from $his restraints and have $him carried away to clean $himself up before returning to $his duties.
-
-<<if passage() != "Slave Interact">>
-	<<set _fv = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fv>>
-		<<set $slaves[_fv] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fPoolSex.tw b/src/npc/interaction/fPoolSex.tw
index baf3d3a14ea16aba4189841c7d195e5eaf565151..600d22adf2dc7bf9378dc69eba7cb3281f07c961 100644
--- a/src/npc/interaction/fPoolSex.tw
+++ b/src/npc/interaction/fPoolSex.tw
@@ -169,10 +169,3 @@ You order $him to meet you in the spa for some quality time in the penthouse's r
 	<</if>>
 <</if>>
 After you've finished with $him, you escort $him to the spa's attached showers so that the two of you can enjoy a bit more time together as you clean off, then allow $him to return to $his duties as you return to your own.
-
-<<if passage() != "Slave Interact">>
-	<<set _fv = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fv>>
-		<<set $slaves[_fv] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fRelation.tw b/src/npc/interaction/fRelation.tw
index d3863431b65406ebe9269ca18460e1a7617db8c3..f61c686d1d5e27c892f1480e7de73fbbc317d972 100644
--- a/src/npc/interaction/fRelation.tw
+++ b/src/npc/interaction/fRelation.tw
@@ -39,7 +39,7 @@
 
 You call both <<= getSlave($AS).slaveName>> and $slaves[$partner].slaveName to your office.
 
-<<if canWalk(getSlave($AS)) && canWalk($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 50) && canPenetrate(getSlave($AS)) && canPenetrate($slaves[$partner]) && ($slaves[$partner].anus > 0) && (getSlave($AS).anus > 0) && ($PC.dick != 0)>>
+<<if canMove(getSlave($AS)) && canMove($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 50) && canPenetrate(getSlave($AS)) && canPenetrate($slaves[$partner]) && ($slaves[$partner].anus > 0) && (getSlave($AS).anus > 0) && ($PC.dick != 0)>>
 	There are three stiff pricks available. Since <<= getSlave($AS).slaveName>> was already in your office, $he goes on the bottom. $He lies on the floor, spreads $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>buttocks, relaxes $his anus, and then begs $his _partnerRel to buttfuck $him. $slaves[$partner].slaveName does, slipping _his2 cock into _his2 _activeSlaveRel's ass. _He2 then stops and waits for you to buttfuck _him2 in turn. It isn't a particularly convenient way to fuck, but it's got the virtue of being uncommon. $slaves[$partner].slaveName, both fucking and getting fucked, comes in _his2 _activeSlaveRel's rectum first. They switch places so <<= getSlave($AS).slaveName>> can have a turn in the middle, leaving you to finish into <<= getSlave($AS).slaveName>>'s already cum-soaked hole.
 	<<set getSlave($AS).counter.anal += 2, getSlave($AS).counter.penetrative++, $slaves[$partner].counter.anal += 2, $slaves[$partner].counter.penetrative++, $analTotal += 4, $penetrativeTotal += 2>>
 <<elseif ($slaves[$partner].devotion - getSlave($AS).devotion > 20) && ($slaves[$partner].devotion <= 50)>>
@@ -50,12 +50,12 @@ You call both <<= getSlave($AS).slaveName>> and $slaves[$partner].slaveName to y
 	<<= getSlave($AS).slaveName>> is a lot more ready and willing for this than $slaves[$partner].slaveName, so<<if ($PC.dick == 0)>>while getting into a strap-on,<</if>> you sit $him on the couch and make $slaves[$partner].slaveName sit on $his lap, facing $him. In this position, <<= getSlave($AS).slaveName>> can reach around and spread $his _partnerRel's <<if $seeRace == 1>>$slaves[$partner].race <</if>>buttocks for _him2, controlling _him2 all the while in case _he2 has hesitations about this. $slaves[$partner].slaveName knows that _he2's trapped, and lets _his2 _activeSlaveRel hold _his2 ass wide so you can use _him2. They're face to face, and it's not hard to tell that $slaves[$partner].slaveName is glaring daggers at <<= getSlave($AS).slaveName>>. You reward <<= getSlave($AS).slaveName>> for $his obedience and punish $slaves[$partner].slaveName for _his2 resistance by forcing _him2 to suck <<= getSlave($AS).slaveName>> off while you finish using $slaves[$partner].slaveName.
 	<<set getSlave($AS).counter.oral++, $slaves[$partner].counter.oral++, $oralTotal++>>
 	<<= VCheck.Partner()>>
-<<elseif canWalk(getSlave($AS)) && canWalk($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (hasAnyArms(getSlave($AS))) && (["mother", "father", "sole parent"].includes(_activeSlaveRel))>>
+<<elseif canMove(getSlave($AS)) && canMove($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (hasAnyArms(getSlave($AS))) && (["mother", "father", "sole parent"].includes(_activeSlaveRel))>>
 	<<= getSlave($AS).slaveName>> gives you a little smile when $he <<if canHear(getSlave($AS))>>hears<<else>>learns<</if>> you wish to fuck $him and $his _daughter2 $slaves[$partner].slaveName<<if ($PC.dick == 0)>> and <<if canSee(getSlave($AS))>>sees<<else>>acknowledges<</if>> your strap-on<</if>>. On your direction, <<= getSlave($AS).slaveName>> sits on the couch. When $slaves[$partner].slaveName enters, _his2 _activeSlaveRel spreads $his arms and tells _him2 to sit on $his lap. $slaves[$partner].slaveName gets the idea and straddles $him so they're face to face. You take $slaves[$partner].slaveName from behind; _he2 gasps as _he2 feels _his2 _activeSlaveRel's hand<<if (hasBothArms($slaves[$partner]))>>s<</if>> stimulate _him2 from the front. They make out shamelessly while you take your pleasure. When you finish, <<= getSlave($AS).slaveName>> lies down on the couch so $slaves[$partner].slaveName can ride $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>face. As $he sucks the cum out of $his _daughter2's sopping fuckhole, $slaves[$partner].slaveName sucks you hard again. In the mood for something harder this time, you jam yourself into the older <<= getSlave($AS).slaveName>>. $slaves[$partner].slaveName gets off <<= getSlave($AS).slaveName>>'s face so _he2 can offer _himself2 for fondling and groping while you pound <<= getSlave($AS).slaveName>>. After you're done, $slaves[$partner].slaveName returns _his2 _activeSlaveRel's affection and gives $him some gentle oral as the older slave lies there exhausted.
 	<<set getSlave($AS).counter.oral += 2, $slaves[$partner].counter.oral += 2, $oralTotal += 2>>
 	<<= VCheck.Both()>>
 	<<= VCheck.Partner()>>
-<<elseif canWalk(getSlave($AS)) && canWalk($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (["daughter", "son"].includes(_activeSlaveRel))>>
+<<elseif canMove(getSlave($AS)) && canMove($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (["daughter", "son"].includes(_activeSlaveRel))>>
 	<<= getSlave($AS).slaveName>> is enthusiastic when $he <<if canHear(getSlave($AS))>>hears<<else>>notices<</if>> you order $slaves[$partner].slaveName to come over. $His total immersion in sexual slavery has clearly uncovered a willingness to get very close to $his _partnerRel. You<<if ($PC.dick == 0)>> don a strap-on,<</if>> lie on the floor and instruct $slaves[$partner].slaveName to ride you. _He2 complies, and finds _his2 $daughter <<= getSlave($AS).slaveName>>
 	<<if ($slaves[$partner].dick > 0)>>
 		stroking _his2 cock while _he2 humps _his2 <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>butt up and down on your cock.
@@ -65,12 +65,12 @@ You call both <<= getSlave($AS).slaveName>> and $slaves[$partner].slaveName to y
 	Your use of $slaves[$partner].slaveName's <<if $slaves[$partner].physicalAge >= 24>>mature<<else>>surprisingly young<</if>> body is the focus. _He2 finds _himself2 caught up in a miasma of sexual pleasure and perversion, moaning and blushing as your <<if ($PC.dick == 0)>>strap-on and fingers<<else>>cock<</if>> and <<= getSlave($AS).slaveName>>'s mouth tour _his2 body. When you finish in _his2 <<if ($slaves[$partner].dick > 0)>>asshole, _his2 $daughter hastens to lavish attention on $his _partnerRel's well fucked, cum filled butt.<<else>>pussy, _his2 $daughter hastens to lavish attention on $his _partnerRel's well fucked, cum filled cunt.<</if>>
 	<<set getSlave($AS).counter.oral += 2, $slaves[$partner].counter.oral += 2, $oralTotal += 2>>
 	<<= VCheck.Partner()>>
-<<elseif canDoVaginal(getSlave($AS)) && canDoVaginal($slaves[$partner]) && canWalk(getSlave($AS)) && canWalk($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 50) && (["twin brother", "twin sister"].includes(_activeSlaveRel))>>
+<<elseif canDoVaginal(getSlave($AS)) && canDoVaginal($slaves[$partner]) && canMove(getSlave($AS)) && canMove($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 50) && (["twin brother", "twin sister"].includes(_activeSlaveRel))>>
 	<<= getSlave($AS).slaveName>> and $slaves[$partner].slaveName are such devoted sex slaves that they've long since lost any hesitations about their partnership, and generally approach sex as though their bodies were interchangeable. (This means that they almost never masturbate, for one thing, preferring to have sex with each other, instead.) Giggling and kissing each other, they eagerly kneel before your chair and give you simultaneous oral sex, making an effort to play with their symmetry. They kiss around your <<if ($PC.dick == 0)>>pussy<<else>>cock, making a complete seal around you with their lips<</if>>, one on each side. Then they jump up on your desk and press their <<if (getSlave($AS).dick > 0) && ($slaves[$partner].dick > 0)>>cocks<<elseif (getSlave($AS).dick > 0) || ($slaves[$partner].dick > 0)>>cock and pussy<<else>>pussies<</if>> against one another<<if ($PC.dick == 0)>> while you don a strap-on<</if>>, spreading their legs to offer you everything. You switch back and forth, with the twin you're not in rubbing and grinding against the one you are, until both of $slaves[$partner].slaveName and <<= getSlave($AS).slaveName>> are lying on the desk<<if ($PC.dick != 0)>> with cum dripping out of them<</if>>, making out tiredly.
 	<<set $slaves[$partner].counter.oral++, getSlave($AS).counter.oral++, $oralTotal++>>
 	<<= VCheck.Both()>>
 	<<= VCheck.Partner()>>
-<<elseif canWalk(getSlave($AS)) && canWalk($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (["sister", "brother", "half-sister", "half-brother", "twin sister", "twin brother"].includes(_activeSlaveRel))>>
+<<elseif canMove(getSlave($AS)) && canMove($slaves[$partner]) && (getSlave($AS).devotion > 50) && ($slaves[$partner].devotion > 20) && (["sister", "brother", "half-sister", "half-brother", "twin sister", "twin brother"].includes(_activeSlaveRel))>>
 	You call <<= getSlave($AS).slaveName>>'s _partnerRel $slaves[$partner].slaveName in for some incestuous fun, but see no reason to wait for _him2. When _he2 arrives, it's to the <<if canSee($slaves[$partner])>>sight<<else>>scene<</if>> of <<= getSlave($AS).slaveName>> sitting on the couch with $his legs spread with you <<if (getSlave($AS).vagina > -1)>>gently fucking $his pussy<<else>>using $his asshole<</if>><<if ($PC.dick == 0)>> with a strap-on<</if>>. You pull out and order $slaves[$partner].slaveName to orally service _his2 _activeSlaveRel. _He2 gets down before the spread-eagled slave $girl to get to work. After watching <<= getSlave($AS).slaveName>> enjoy the attention for a while, you move behind the busy $slaves[$partner].slaveName and pull _him2 into a good position so you can fuck _him2 while _he2 sucks. After a few thrusts, <<= getSlave($AS).slaveName>>'s eyes roll back. <<if (getSlave($AS).voice == 0) || (getSlave($AS).accent >= 3)>>$He gestures that it feels really good when you make $his _partnerRel moan into $him.<<else>>"Oh <<Master>>," $he squeals, "it feel<<s>> <<s>>o good when you make _him2 moan into me!"<</if>>
 	<<set $slaves[$partner].counter.oral++, getSlave($AS).counter.oral++, $oralTotal++>>
 	<<= VCheck.Both()>>
diff --git a/src/npc/interaction/fSlaveFeed.tw b/src/npc/interaction/fSlaveFeed.tw
index e7d6fe2cc5a86b40a31abd995d9e7e68220a77b5..96d58f158b4403e5b0f928795bf2895b9684a53d 100644
--- a/src/npc/interaction/fSlaveFeed.tw
+++ b/src/npc/interaction/fSlaveFeed.tw
@@ -824,12 +824,12 @@ Next, you see to <<= getSlave($AS).slaveName>>.
 	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))>>$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.
+		<<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))>>$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>>.
+		<<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))>>$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 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>>
diff --git a/src/npc/interaction/fVagina.tw b/src/npc/interaction/fVagina.tw
index 028877f94692bddd85efb3916cc31919964532c3..57efafa30855d587107b8499081e471e5b6a5f2b 100644
--- a/src/npc/interaction/fVagina.tw
+++ b/src/npc/interaction/fVagina.tw
@@ -94,7 +94,7 @@ You call $him over so you can
 	<<set _fSpeed = random(1,100)>>
 <</if>>
 
-<<if canWalk(getSlave($AS)) && getSlave($AS).fetish != "mindbroken">>
+<<if canMove(getSlave($AS)) && getSlave($AS).fetish != "mindbroken">>
 	You decide to fuck $him
 	<<if (_fPosition <= 20)>>
 		in the missionary position.
@@ -513,7 +513,7 @@ You call $him over so you can
 		Your cum slides right out of $his gaping hole.
 	<</if>>
 
-	<<if canWalk(getSlave($AS)) && $postSexCleanUp > 0>>
+	<<if canMove(getSlave($AS)) && $postSexCleanUp > 0>>
 		$He uses <<if getSlave($AS).vagina > 0>>a quick douche to clean $his <<if getSlave($AS).vagina < 2>>tight<<elseif getSlave($AS).vagina > 3>>loose<</if>> pussy<<else>>an enema to clean $his <<if getSlave($AS).anus < 2>>tight<<elseif getSlave($AS).anus < 3>>used<<else>>gaping<</if>> butthole<</if>>,
 		<<switch getSlave($AS).assignment>>
 		<<case "work in the brothel">>
@@ -553,10 +553,3 @@ You call $him over so you can
 		<</switch>>
 	<</if>>
 <</if>>
-
-<<if passage() != "Slave Interact">>
-	<<set _fv = $slaveIndices[getSlave($AS).ID]>>
-	<<if def _fv>>
-		<<set $slaves[_fv] = getSlave($AS)>>
-	<</if>>
-<</if>>
diff --git a/src/npc/interaction/fillUpButt.tw b/src/npc/interaction/fillUpButt.tw
index fb6591c6a9dbaf3b75947e3c07b870080a9b6435..ea27556981a0d9e9288973914e7f901091372dbb 100644
--- a/src/npc/interaction/fillUpButt.tw
+++ b/src/npc/interaction/fillUpButt.tw
@@ -74,7 +74,7 @@ before shoving the equipment into $his
 	<<run deflate(getSlave($AS))>>
 	<<set getSlave($AS).pregKnown = 1>>
 <<elseif getSlave($AS).fetish == "mindbroken">>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to present $his buttocks and anus but $he remains still and unresponsive. Talking to a broken mind is pointless.
 	<</if>>
 	You reach around and grab $his
@@ -129,9 +129,9 @@ before shoving the equipment into $his
 	<<else>>
 		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you push $him onto your desk and admire $his distended, <<= getSlave($AS).skin>> stomach. You give the gut orb a slap, eliciting a slight grunt from the broken slave and touch of wiggling and sloshing from $his middle. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.<</if>>
 <<elseif getSlave($AS).devotion < -20>>
-	<<if canWalk(getSlave($AS))>>
-		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. Opposed to the thought of being filled <<if getSlave($AS).inflation > 0>>more <</if>>with <<= getSlave($AS).inflationType>>, $he tries to step back, but you catch $him and pull $him closer to you as you reach around and grab $his
-	<<elseif (hasAnyLegs(getSlave($AS)))>>
+	<<if canMove(getSlave($AS))>>
+		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. Opposed to the thought of being filled <<if getSlave($AS).inflation > 0>>more <</if>>with <<= getSlave($AS).inflationType>>, $he tries to <<if canWalk(getSlave($AS))>>step back<<else>>crawl away<</if>>, but you catch $him and pull $him closer to you as you reach around and grab $his
+	<<elseif !isAmputee(getSlave($AS))>>
 		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. $He's opposed to the thought of being filled <<if getSlave($AS).inflation > 0>>more <</if>>with <<= getSlave($AS).inflationType>>, but since $he is immobile, can't escape. You reach around and grab $his
 	<<else>>
 		$He's opposed to the thought of being filled with <<if getSlave($AS).inflation > 0>>more <</if>><<= getSlave($AS).inflationType>>, but as an amputee can do nothing about it. You reach around and grab $his
@@ -191,14 +191,14 @@ before shoving the equipment into $his
 	<</if>>
 	butthole, and setting it on low.
 	<<if getSlave($AS).inflation == 2>>
-		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<elseif getSlave($AS).inflation == 1>>
-		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<else>>
-		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.<</if>>
+		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.<</if>>
 <<elseif (getSlave($AS).devotion <= 20)>>
-	<<if canWalk(getSlave($AS))>>
-		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. $He complies without comment, standing in front of you.
+	<<if canMove(getSlave($AS))>>
+		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. $He complies without comment, <<if canStand(getSlave($AS))>>standing in front of you.<<else>>turning $his rear to you.<</if>>
 	<<elseif !isAmputee(getSlave($AS))>>
 		You instruct $him to present $his anus for a <<= getSlave($AS).inflationType>> enema. $He complies without comment, readying $his ass for you.
 	<<else>>
@@ -256,15 +256,15 @@ before shoving the equipment into $his
 	<</if>>
 	butthole, and setting it on low.
 	<<if getSlave($AS).inflation == 2>>
-		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<elseif getSlave($AS).inflation == 1>>
-		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<else>>
-		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<</if>>
 <<elseif getSlave($AS).devotion <= 50>>
-	<<if canWalk(getSlave($AS))>>
-		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He hesitates but eventually stands in front of you showing $his buttocks before presenting $his anus.
+	<<if canMove(getSlave($AS))>>
+		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He hesitates but eventually <<if canStand(getSlave($AS))>>stands<<else>>crawls<</if>> in front of you showing $his buttocks before presenting $his anus.
 	<<elseif !isAmputee(getSlave($AS))>>
 		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He hesitates but eventually shifts $his buttocks to face you before presenting $his anus.
 	<<else>>
@@ -310,15 +310,20 @@ before shoving the equipment into $his
 	<</if>>
 	butthole, and setting it on low.
 	<<if getSlave($AS).inflation == 2>>
-		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<elseif getSlave($AS).inflation == 1>>
-		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<else>>
-		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He sighs contently, hoping you'll give $him more attention. You pat $his belly and tell $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<</if>>
 <<else>>
-	<<if canWalk(getSlave($AS))>>
-		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He eagerly stands in front of you showing $his buttocks before happily presenting $his anus to you, spreading $his butt cheeks and turning to face you.
+	<<if canMove(getSlave($AS))>>
+		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He eagerly
+		<<if canStand(getSlave($AS))>>
+			stands in front of you showing $his buttocks before happily presenting $his anus to you, spreading $his butt cheeks and turning to face you.
+		<<else>>
+			crawls to you, showing $his buttocks in motion before happily presenting $his anus to you.
+		<</if>>
 	<<elseif !isAmputee(getSlave($AS))>>
 		You instruct $him to present $his <<if $seeRace == 1>><<= getSlave($AS).race>> <</if>>anus for a <<= getSlave($AS).inflationType>> enema. $He eagerly shifts around to show $his buttocks before happily presenting $his anus to you, spreading $his butt cheeks and turning to face you.
 	<<else>>
@@ -364,22 +369,22 @@ before shoving the equipment into $his
 	<</if>>
 	butthole, and setting it on low.
 	<<if getSlave($AS).inflation == 2>>
-		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His sloshing belly slowly grows even larger with <<= getSlave($AS).inflationType>>. You rest a hand on $his back as $his swelling belly forces $him to rise off your lap. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his taut, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<elseif getSlave($AS).inflation == 1>>
-		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His bloated belly slowly swells even larger with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his jiggly, soft, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<<else>>
-		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canWalk(getSlave($AS))>>push $him to $his feet<<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
+		$His belly slowly swells with <<= getSlave($AS).inflationType>>. You slip a hand under $his stomach to feel it grow in your palm. Once the enema is complete and $he is plugged, you <<if canStand(getSlave($AS))>>push $him to $his <<if hasBothLegs(getSlave($AS))>>feet<<else>>foot<</if>><<else>>help $him onto your desk<</if>> and admire $his distended, <<= getSlave($AS).skin>> stomach. $He sighs contently, before eagerly begging for more. You pat $his belly and tell $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise.
 	<</if>>
 <</if>>
 <<if _pregDiscovery == 0>>
 	<<set getSlave($AS).inflation += 1>>
 	<<if getSlave($AS).inflation == 3>>
-		<<if canWalk(getSlave($AS))>>$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 @@.red;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
-		<<if canWalk(getSlave($AS))>>$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>>.
+		<<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>>.
 	<<elseif getSlave($AS).inflation == 1>>
-		<<if canWalk(getSlave($AS))>>$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 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>>
 	<<run SetBellySize(getSlave($AS))>>
 <</if>>
diff --git a/src/npc/interaction/fillUpFace.tw b/src/npc/interaction/fillUpFace.tw
index f2986ca9e036c430a48d1c2940d8ee1b38d3ae81..0dceca83318667996d724f31e658a9983a52c9a8 100644
--- a/src/npc/interaction/fillUpFace.tw
+++ b/src/npc/interaction/fillUpFace.tw
@@ -28,7 +28,7 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 	<<run deflate(getSlave($AS))>>
 	<<set getSlave($AS).pregKnown = 1>>
 <<elseif getSlave($AS).fetish == "mindbroken">>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to take a seat on your lap and open $his mouth wide for the hose, but $he remains still and unresponsive. Talking to a broken mind is pointless.
 	<</if>>
 	You reach out and wrap your arms around $his
@@ -63,9 +63,9 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 	<</if>>
 	belly, pulling $him into your lap. You force the hose down $his throat, strapping it to $his head to prevent it from slipping out, and turn the flow to low. With everything in place, you return your hands to $his gurgling stomach.<<if getSlave($AS).inflation == 2>>You can feel $his <<= getSlave($AS).skin>> belly growing taut with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give the firm orb of $his belly a slap, eliciting a large belch from the broken slave and no motion at all from $his gut. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover. After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<elseif getSlave($AS).inflation == 1>>You can feel $his <<= getSlave($AS).skin>> belly growing larger with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give the jiggling orb of $his belly a slap, eliciting a burp from the broken slave and tons of motion from $his gut. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover, After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<else>>You can feel $his <<= getSlave($AS).skin>> belly swelling with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give $his bloated belly a slap, eliciting a small burp from the broken slave and a little jiggle from $his gut. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his sloshing bulk onto the couch to recover. After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the hiccupping <<= getSlave($AS).slaveName>> ready to be moved<</if>>.
 <<elseif getSlave($AS).devotion < -20>>
-	<<if canWalk(getSlave($AS))>>
-		You instruct $him to take a seat on your lap and open $his mouth wide for the hose. Opposed to the thought of being filled <<if getSlave($AS).inflation > 0>>more <</if>>with <<= getSlave($AS).inflationType>>, $he tries to step back, but you catch $him and pull $him into your lap, wrapping your arms around $his
-	<<elseif (hasAnyLegs(getSlave($AS)))>>
+	<<if canMove(getSlave($AS))>>
+		You instruct $him to take a seat on your lap and open $his mouth wide for the hose. Opposed to the thought of being filled <<if getSlave($AS).inflation > 0>>more <</if>>with <<= getSlave($AS).inflationType>>, $he tries to <<if canWalk(getSlave($AS))>>step back<<else>>crawl away<</if>>, but you catch $him and pull $him into your lap, wrapping your arms around $his
+	<<elseif (hasAnyLimbs(getSlave($AS)))>>
 		You inform $him $he'll be taking a seat on your lap, opening $his mouth wide for the hose, and drinking until you tell $him $he is full enough. $He's opposed to the thought of being filled with<<if getSlave($AS).inflation > 0>> more<</if>> <<= getSlave($AS).inflationType>>, but since $he is immobile, $he can't escape. You heft $him onto your lap, wrapping your arms around $his
 	<<else>>
 		$He's opposed to the thought of being filled with <<if getSlave($AS).inflation > 0>>more <</if>><<= getSlave($AS).inflationType>>, but as an amputee can do nothing about it. You heft $him onto your lap, wrapping your arms around $him
@@ -101,9 +101,9 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 	<</if>>
 	belly. You force the hose down $his throat, strapping it to $his head to prevent $him from removing it, and turn the flow to low. With everything in place, you return your hands to $his gurgling stomach.<<if getSlave($AS).inflation == 2>>You can feel $his <<= getSlave($AS).skin>> belly growing taut with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give the firm orb of $his belly a slap, eliciting gagging from the overfilled slave and no motion at all from $his gut. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover. After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<elseif getSlave($AS).inflation == 1>>You can feel $his <<= getSlave($AS).skin>> belly growing larger with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give the jiggling orb of $his belly a slap, eliciting a burp from the full slave and tons of motion from $his gut. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover. After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<else>>You can feel $his <<= getSlave($AS).skin>> belly swelling with <<= getSlave($AS).inflationType>> as it pushes out against your hands. Once you have given $him $his fill, you give $his bloated belly a slap, eliciting a shudder from the bloated slave and a little jiggle from $his gut. $He <<if canSee(getSlave($AS))>>looks in your eyes<<else>>faces you<</if>>, as though almost demanding answers. $He looks apprehensive about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his sloshing bulk onto the couch to recover. After a few minutes of rest, and several glares from you when it appears $his meal might be coming back up, is the hiccupping <<= getSlave($AS).slaveName>> ready to be moved<</if>>.
 <<elseif getSlave($AS).devotion <= 20>>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to take a seat on your lap and open $his mouth wide for the hose. $He complies without comment and settles $himself onto your lap, shuddering slightly as you wrap your arms around $his
-	<<elseif (hasAnyLegs(getSlave($AS)))>>
+	<<elseif (hasAnyLimbs(getSlave($AS)))>>
 		You inform $him $he'll be taking a seat on your lap, opening $his mouth wide for the hose, and drinking until you tell $him $he is full enough. $He complies without comment. You heft $him onto your lap, wrapping your arms around $his
 	<<else>>
 		$He's hesitant of being filled with <<if getSlave($AS).inflation > 0>>more <</if>><<= getSlave($AS).inflationType>>, but as an amputee can do nothing about it. You heft $him onto your lap, wrapping your arms around $his
@@ -139,9 +139,9 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 	<</if>>
 	belly. You insert the hose down $his throat, choosing to hold it place instead of strapping it to $him, and turn the flow to low. With everything in place, you place your free hand on $his gurgling stomach.<<if getSlave($AS).inflation == 2>>You can feel $his <<= getSlave($AS).skin>> belly growing taut with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give the firm orb of $his belly a slap, eliciting a restrained gag from the overfilled slave and no motion at all from $his gut. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover. $His meal attempts to come back up on $his several times, but $he holds it down for fear of punishment should $he vomit. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<elseif getSlave($AS).inflation == 1>>You can feel $his <<= getSlave($AS).skin>> belly growing larger with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give the jiggling orb of $his belly a slap, eliciting a strained burp from the full slave and tons of motion from $his gut. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his hiccupping bulk onto the couch to recover. $His meal attempts to come back up on $his several times, but $he holds it down for fear of punishment should $he vomit. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<else>>You can feel $his <<= getSlave($AS).skin>> belly swelling with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give $his bloated belly a slap, eliciting a small shudder from the bloated slave and a little jiggle from $his gut. $He looks up at you quizzically, unsure about what you will do next. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his sloshing bulk onto the couch to recover. $His meal attempts to come back up on $his several times, but $he holds it down for fear of punishment should $he vomit. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<</if>>.
 <<elseif getSlave($AS).devotion <= 50>>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to take a seat on your lap and open $his mouth wide for the hose. $He hesitates but eventually settles $himself onto your lap as you wrap your arms around $his
-	<<elseif (hasAnyLegs(getSlave($AS)))>>
+	<<elseif (hasAnyLimbs(getSlave($AS)))>>
 		You inform $him $he'll be taking a seat on your lap, opening $his mouth wide for the hose, and drinking until you tell $him $he is full enough. $He hesitates for a moment, before demonstrating acceptance. You heft $him onto your lap, wrapping your arms around $his
 	<<else>>
 		$He's mildly hesitant of being filled with <<if getSlave($AS).inflation > 0>>more <</if>><<= getSlave($AS).inflationType>>, but as an amputee can do nothing about it. You heft $him onto your lap, wrapping your arms around $his
@@ -177,9 +177,9 @@ You attach a hose to $dairyName tap with the pipes set to pump <<= getSlave($AS)
 	<</if>>
 	belly. You <<if hasAnyArms(getSlave($AS))>>hand $him the hose<<else>>place the hose in $his mouth<</if>>, allowing $him to drink from it at $his own pace, as you keep a hand on the tap to control the flow. With everything in place, you place your free hand on $his gurgling stomach.<<if getSlave($AS).inflation == 2>>You can feel $his <<= getSlave($AS).skin>> belly growing taut with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give the firm orb of $his belly a couple gentle pats, eliciting a small hiccup, that $he immediately apologizes for, from the overfilled slave and no motion at all from $his gut. $He sighs contently, hoping you'll give $him more attention. You order $him to keep $himself filled with two gallons of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his heavy body onto the couch to recover. $His meal attempts to come back up on $his several times, but $he dutifully holds it down. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<elseif getSlave($AS).inflation == 1>>You can feel $his <<= getSlave($AS).skin>> belly growing larger with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give the jiggling orb of $his belly a couple pats, eliciting a small hiccup, that $he immediately apologizes for, from the full slave and tons of motion from $his gut. $He sighs contently, hoping you'll give $him more attention. You order $him to keep $himself filled with four liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his heavy body onto the couch to recover. $His meal attempts to come back up on $him several times, but $he dutifully holds it down. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<<else>>You can feel $his <<= getSlave($AS).skin>> belly swelling with <<= getSlave($AS).inflationType>> as it pushes out against your hand. Once you have given $him $his fill, you give $his bloated belly a couple pats, eliciting a small hiccup, that $he immediately apologizes for, from the bloated slave and a little jiggle from $his gut. $He sighs contently, hoping you'll give $him more attention. You order $him to keep $himself filled with two liters of <<= getSlave($AS).inflationType>> until you say otherwise, before helping $his sloshing body onto the couch to recover. $His meal attempts to come back up on $his several times, but $he dutifully holds it down. Only after a few minutes of rest is the groaning <<= getSlave($AS).slaveName>> ready to be moved<</if>>.
 <<else>>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to take a seat on your lap and open $his mouth wide for the hose. $He eagerly settles $himself onto your lap as you wrap your arms around $his
-	<<elseif (hasAnyLegs(getSlave($AS)))>>
+	<<elseif (hasAnyLimbs(getSlave($AS)))>>
 		You inform $him $he'll be taking a seat on your lap, opening $his mouth wide for the hose, and drinking until you tell $him $he is full enough. $He squirms with excitement as you heft $him onto your lap, wrapping your arms around $his
 	<<else>>
 		$He's devotedly <<if canSee(getSlave($AS))>>looks<<else>>smiles<</if>> at you as you heft $him onto your lap, wrapping your arms around $his
@@ -219,12 +219,12 @@ 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))>>$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 @@.red;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
-		<<if canWalk(getSlave($AS))>>$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>>.
+		<<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))>>$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 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>>
 	<<run SetBellySize(getSlave($AS))>>
 <</if>>
diff --git a/src/npc/interaction/fondleButt.tw b/src/npc/interaction/fondleButt.tw
index 2da14590e42757bb51814871e3ceccdb30494bbe..7a0700f3904320c370783601b05fd49f20e40d3f 100644
--- a/src/npc/interaction/fondleButt.tw
+++ b/src/npc/interaction/fondleButt.tw
@@ -67,7 +67,7 @@ as well as $his
 		couch-like
 	<</if>>
 	buttocks. $He looks longingly into your eyes as you continue to move around $his posterior, pinching with your fingers and slapping with your palms before reaching to touch your fingertips against $his sphincter. $He moans and quivers slightly when you rub your fingers around $his virgin anus. $He starts to wiggle $his ass while you circle around $his anus, not breaking contact with $him with your fingers. You keep squeezing $his buttocks firmly — first one, then the other and then finally both. $He can't resist gyrating $his hips in arousal while in your grasp.
-	<<if canWalk(getSlave($AS))>>
+	<<if canStand(getSlave($AS))>>
 		You strongly pull $his body closer towards you by $his buttocks, turn $him around, and push $him down to bend $him over your desk.
 	<<else>>
 		You move closer towards $him, turn $him around and firmly hold $him down on desk, face-down so that $his butt is facing you up into the air.
@@ -94,7 +94,7 @@ as well as $his
 	<</if>>
 	buttocks a few hard smacks for good measure. $His face is in ecstasy as $he stands and turns to face you, gently rubbing the red spots on $his buttocks and looking at you hungrily as if $he wants more.
 <<elseif (getSlave($AS).devotion > 50) && (getSlave($AS).anus == 0)>>
-	<<if canWalk(getSlave($AS))>>
+	<<if canStand(getSlave($AS))>>
 		$He accepts your orders happily and waves $his virgin anus to you, spreading $his butt cheeks in front of you with $his hands before turning to face you.
 	<</if>>
 	You reach around and grab $his
@@ -118,7 +118,7 @@ as well as $his
 		couch-like
 	<</if>>
 	buttocks with both hands before rubbing along them, feeling the shape of $his<<if $seeRace == 1>> <<= getSlave($AS).race>><</if>> ass with your fingers and squeezing gently. $He looks passionately into your eyes as you continue to move around $his posterior gently reaching to touch your fingertips against $his sphincter while rubbing $his ass at the same time. $He sighs as you rub your fingertips around $his virgin anus. $He starts to wiggle $his ass while you circle around $his anus, not breaking contact with $him with your fingers. You keep squeezing $his buttocks tenderly — first one, then the other and then both and $he can't resist gyrating $his hips enticingly while in your grasp.
-	<<if canWalk(getSlave($AS))>>
+	<<if canStand(getSlave($AS))>>
 		You pull $his body closer towards you by $his buttocks, turn $him around, and bend $him over your desk.
 	<<else>>
 		You move closer towards $him, turn $him around and push $him down on desk, face-down so that $his butt is facing you up into the air.
@@ -154,7 +154,7 @@ as well as $his
 		<<Master>>," $he says happily.
 	<</if>>
 <<elseif (getSlave($AS).devotion > 20) && (getSlave($AS).anus == 0)>>
-	<<if canWalk(getSlave($AS))>>
+	<<if canStand(getSlave($AS))>>
 		$He accepts your orders without comment and presents $his virgin anus to you before turning to face you.
 	<</if>>
 	You reach around and grab $his
@@ -282,7 +282,7 @@ as well as $his
 	<</if>>
 	buttocks before rubbing along them, feeling the shape of $his<<if $seeRace == 1>> <<= getSlave($AS).race>><</if>> ass with your fingers and squeezing gently. $He tries to break out of your grasp as you rub fingers around $his virgin anus. $He writhes as you continue to move around $his posterior, gently reaching to touch your fingertips against $his sphincter while rubbing $his ass at the same time. $He struggles to stay still while you circle around $his unbroken anus, not breaking contact with $him with your fingers. You look at $his face and $he has <<if !canSee(getSlave($AS))>>reflexively <</if>>shut $his eyes, trying not to think about what's happening to $his butt. This only encourages you to continue. You keep squeezing $his buttocks tenderly — first one, then the other and then finally both and $he can't help but quiver while in your grasp. You pull $his body closer towards you by $his buttocks, turn $him around, and push $him down, bending $him over your desk while $he tries to push away. You look at $his rear while you squeeze $his cheeks and rub them with your firm hands. You wander along the outline of $his posterior with both your eyes and hands, then look at $his virgin butthole as you trace it with your fingers and thumb. Eventually, you decide to stop. $He slowly stands and looks in your eyes, as though almost demanding answers. $He looks apprehensive about what you will do next.
 <<elseif getSlave($AS).fetish == "mindbroken">>
-	<<if canWalk(getSlave($AS))>>
+	<<if canMove(getSlave($AS))>>
 		You instruct $him to present $his buttocks and anus but $he remains still and unresponsive. Talking to a broken mind is pointless.
 	<</if>>
 	You reach around and grab $his
diff --git a/src/npc/interaction/forceFeeding.tw b/src/npc/interaction/forceFeeding.tw
index 4c7a27e63d93a67806cc30ecc7da60c985774059..89a56c3a1a869d80a8219ff79528b1d988863cbe 100644
--- a/src/npc/interaction/forceFeeding.tw
+++ b/src/npc/interaction/forceFeeding.tw
@@ -33,7 +33,7 @@ buckets overflowing with slave food. $He is going to eat it all and you're going
 	$He grimaces at the <<if canSee(getSlave($AS))>>sight<<elseif canSmell(getSlave($AS))>>smell<<else>>thought<</if>> of the food that will soon be going into $him. $He knows you won't let it come back up later.
 <</if>>
 
-<<if canWalk(getSlave($AS))>>
+<<if canMove(getSlave($AS))>>
 	You instruct $him to take a seat on your lap and open
 	<<if getSlave($AS).fetish == "mindbroken">>
 		wide, but $he remains still and unresponsive. Talking to a broken mind is pointless.
@@ -41,7 +41,7 @@ buckets overflowing with slave food. $He is going to eat it all and you're going
 		wide.
 	<</if>>
 	<<if getSlave($AS).devotion < -20>>
-		Opposed to the thought of being forced to eat all that food, $he tries to step back, but you catch $him and pull $him into your lap, wrapping an arm
+		Opposed to the thought of being forced to eat all that food, $he tries to <<if canWalk(getSlave($AS))>>step back<<else>>crawl away<</if>>, but you catch $him and pull $him into your lap, wrapping an arm
 	<<elseif getSlave($AS).fetish == "submissive">>
 		$He meekly settles into your lap and shudders as $he feels your dominant hand wrap
 	<<elseif getSlave($AS).behavioralFlaw == "gluttonous">>
@@ -527,7 +527,7 @@ and a little jiggle from $his gut.
 	<<set getSlave($AS).pregKnown = 1>>
 <<else>>
 	<<if getSlave($AS).inflation == 3>>
-		<<if canWalk(getSlave($AS))>>
+		<<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.
@@ -535,13 +535,13 @@ and a little jiggle from $his gut.
 		Being filled so full @@.red;surely had negative effects@@ on $his health.
 		<<run healthDamage(getSlave($AS), 10)>>
 	<<elseif getSlave($AS).inflation == 2>>
-		<<if canWalk(getSlave($AS))>>
+		<<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))>>
+		<<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.
diff --git a/src/npc/interaction/passage/fMarry.tw b/src/npc/interaction/passage/fMarry.tw
index 82a603b0b961e64a088c67eb6b4405f66ae2c2ab..37008dddbcad5ae79c04910793442242c1d284ef 100644
--- a/src/npc/interaction/passage/fMarry.tw
+++ b/src/npc/interaction/passage/fMarry.tw
@@ -461,7 +461,7 @@ You tell <<= getSlave($AS).slaveName>> that you're going to marry $him. (A propo
 	<<replace "#result">>
 	You order <<= getSlave($AS).slaveName>> to go change into bridal lingerie.
 	<<if _reactionType == 0>>
-		<<if canWalk(getSlave($AS))>>
+		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>
 			$He hurries off, still crying. When $he returns, $he's wearing
 		<<else>>
 			$He is helped out, still crying. When $he is returned, $he's wearing
@@ -469,13 +469,13 @@ You tell <<= getSlave($AS).slaveName>> that you're going to marry $him. (A propo
 	<<elseif _reactionType == 1>>
 		$He is helped out, completely unaware of $his pending marriage. When $he is returned, $he's wearing
 	<<elseif _reactionType == 2>>
-		<<if canWalk(getSlave($AS))>>
+		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>
 			$He shuffles off, still sobbing. When $he returns, $he's wearing
 		<<else>>
 			$He is helped out, still sobbing. When $he is returned, $he's wearing
 		<</if>>
 	<<else>>
-		<<if canWalk(getSlave($AS))>>
+		<<if canWalk(getSlave($AS)) || (canMove(getSlave($AS)) && getSlave($AS).rules.mobility == "permissive")>>
 			$He hurries off to fulfill your orders. When $he returns, $he's wearing
 		<<else>>
 			$He is helped out to get dressed. When $he is returned, $he's wearing
diff --git a/src/npc/interaction/passage/fSlaveImpreg.tw b/src/npc/interaction/passage/fSlaveImpreg.tw
index ea74e085502754f4fbd5ca0f322f0070f8d88957..e81aeecbf96434e8e1bc6c62cf0954fde2b27012 100644
--- a/src/npc/interaction/passage/fSlaveImpreg.tw
+++ b/src/npc/interaction/passage/fSlaveImpreg.tw
@@ -9,7 +9,8 @@
 
 <h3>Select an eligible slave to serve as the semen donatrix</h3>
 
-<<set _eligibles = $slaves.filter((s) => (s.ID != getSlave($AS).ID) && canImpreg(getSlave($AS), s) && canPenetrate(getSlave($AS), s))>>
+<<set _eligibles = $slaves.filter((s) => (s.ID != getSlave($AS).ID) && canImpreg(getSlave($AS), s) && canPenetrate(s))>>
+<<set _kinship = ibc.kinship_one_many(getSlave($AS), _eligibles)>>
 <<for _i = 0; _i < _eligibles.length; _i++>>
 	<<set _name = SlaveFullName(_eligibles[_i])>>
 	<div>
@@ -23,6 +24,21 @@
 				@@.lightgreen;<<= capFirstChar(_relTerm)>>@@
 			<</if>>
 		<</if>> /* closes extended family mode */
+	<<if $inbreeding && _kinship[_eligibles[_i].ID] > 0>>
+		<<set _thisKinship = _kinship[_eligibles[_i].ID]>>
+		<<if _thisKinship >= 0.5>>
+			(Extreme 
+		<<elseif _thisKinship >= 0.25>>
+			(Major 
+		<<elseif _thisKinship >= 0.125>>
+			(Some 
+		<<elseif _thisKinship >= 0.0625>>
+			(Minor 
+		<<else>>
+			(Slight 
+		<</if>>
+		inbreeding, CoI of <<print _thisKinship>>)
+	<</if>>
 	</div>
 <</for>>
 <<if (_eligibles.length === 0)>>
diff --git a/src/npc/interaction/passage/fSlaveSlaveDick.tw b/src/npc/interaction/passage/fSlaveSlaveDick.tw
index 31db8f4c7b6fd025d05c7f441aa72e88e82105ce..d97314d7e1c6847c7cd5b0b1082ddc2711c6f180 100644
--- a/src/npc/interaction/passage/fSlaveSlaveDick.tw
+++ b/src/npc/interaction/passage/fSlaveSlaveDick.tw
@@ -8,7 +8,7 @@
 
 <h2>Select an eligible slave</h2>
 
-<<set _eligibles = $slaves.filter((s) => (s.ID != $activeSlave.ID) && isSlaveAvailable(s) && canDoVaginal(s) && canWalk(s) /* amp-amp scene is not written */)>>
+<<set _eligibles = $slaves.filter((s) => (s.ID != $activeSlave.ID) && isSlaveAvailable(s) && canDoVaginal(s) && canStand(s) /* amp-amp scene is not written */)>>
 <<for _i = 0; _i < _eligibles.length; _i++>>
 	<div>
 		<<set _name = SlaveFullName(_eligibles[_i])>>
diff --git a/src/npc/interaction/passage/matchmaking.tw b/src/npc/interaction/passage/matchmaking.tw
index aa3ddabef18811f7f779d6810194757e6430ac9b..1bec089c484638da118a33a951883967a2f4808f 100644
--- a/src/npc/interaction/passage/matchmaking.tw
+++ b/src/npc/interaction/passage/matchmaking.tw
@@ -37,7 +37,7 @@ Their relationship would probably be somewhat artificial at first, and they'd ha
 <<if $seeImages == 1>><br style="clear:both"><</if>>
 
 <br><br>__Put $him with another worshipful <<if getSlave($activeSlave.ID).relationship == -2>>emotionally bonded slave<<else>>emotional slut<</if>>:__
-<<print App.UI.SlaveList.slaveSelectionList(
+<<includeDOM App.UI.SlaveList.slaveSelectionList(
 	s => s.devotion >= 100 && s.relationship === $activeSlave.relationship && s.ID !== $activeSlave.ID,
 	App.UI.SlaveList.SlaveInteract.stdInteract,
 	null,
diff --git a/src/npc/interaction/rename.tw b/src/npc/interaction/rename.tw
deleted file mode 100644
index f6a7679c4d8216239c2f5cb1d2c272a11c20923c..0000000000000000000000000000000000000000
--- a/src/npc/interaction/rename.tw
+++ /dev/null
@@ -1,140 +0,0 @@
-:: Rename [nobr]
-
-<<set $nextButton = "Back", $nextLink = "Slave Interact">>
-<<set $AS = $activeSlave.ID>>
-
-<<if getSlave($AS).slaveName === $oldName && getSlave($AS).slaveSurname === $oldSurname>>
-	<<goto "Slave Interact">>
-<<else>>
-/* First time renaming reaction */
-<<if getSlave($AS).slaveName === "" || ndef getSlave($AS).slaveName>>
-	$oldName needs to be called something on the records, so $oldName $he stays.
-	<<set getSlave($AS).slaveName = $oldName>>
-<<elseif getSlave($AS).fetish == "mindbroken">>
-	<<= getSlave($AS).slaveName>> doesn't even recognize that $he's been renamed. It simply does not register.
-<<elseif ($oldName != getSlave($AS).birthName && getSlave($AS).slaveName == getSlave($AS).birthName) || ($oldSurname != getSlave($AS).birthSurname && getSlave($AS).slaveSurname == getSlave($AS).birthSurname)>>
-	<<= getSlave($AS).slaveName>>
-	<<if getSlave($AS).devotion > 50>>
-		cheerfully accepts $his old name back.
-	<<elseif getSlave($AS).devotion >= -20>>
-		obediently accepts $his old name back.
-	<<elseif getSlave($AS).devotion >= -50>>
-		happily accepts $his original name.
-	<<elseif getSlave($AS).trust > 20>>
-		scoffs at the notion that $he was ever anything other than <<= SlaveFullBirthName(getSlave($AS))>>, @@.orangered;weakening your control over $him.@@
-		<<set getSlave($AS).trust += 5>>
-	<<else>>
-		cautiously accepts the return of $his name.
-	<</if>>
-<<elseif ($oldName == getSlave($AS).birthName && getSlave($AS).slaveName != getSlave($AS).birthName) || ($oldSurname == getSlave($AS).birthSurname && getSlave($AS).slaveSurname != getSlave($AS).birthSurname)>>
-	<<= getSlave($AS).slaveName>>
-	<<if getSlave($AS).devotion > 50>>
-		accepts $his new name cheerfully. This attachment to you @@.hotpink; increases $his devotion.@@
-		<<set getSlave($AS).devotion += 4>>
-	<<elseif getSlave($AS).devotion >= -20>>
-		obediently accepts $his new name. This surrender to you @@.hotpink; increases $his obedience.@@
-		<<set getSlave($AS).devotion += 4>>
-	<<elseif getSlave($AS).devotion >= -50>>
-		reluctantly accepts $his new name.
-	<<else>>
-		angrily tries to resist $his new name, insisting that $his name is <<= SlaveFullBirthName(getSlave($AS))>>. This resistance @@.mediumorchid; increases $his rebelliousness.@@
-		<<set getSlave($AS).devotion -= 5>>
-	<</if>>
-<<else>>
-	Since <<= getSlave($AS).slaveName>> has already had a new name <<if getSlave($AS).devotion > 20>>given to<<else>>forced on<</if>> $him before, the mere fact of having it changed again doesn't really affect $him.
-<</if>>
-
-<<set _insultingName = 0>>
-<<if $oldName != getSlave($AS).slaveName && typeof getSlave($AS).slaveName == "string">>
-	<<set _toSearch = getSlave($AS).slaveName.toLowerCase()>>
-	<<for $i = 0; $i < setup.badWords.length; $i++>>
-		<<if _toSearch.indexOf(setup.badWords[$i]) != -1>>
-			<<set _insultingName = 1>>
-			<<break>>
-		<</if>>
-	<</for>>
-<</if>>
-<<if !_insultingName>>
-	<<if $oldSurname != getSlave($AS).slaveSurname && typeof getSlave($AS).slaveSurname == "string">>
-		<<set _toSearch = getSlave($AS).slaveSurname.toLowerCase()>>
-		<<for $i = 0; $i < setup.badWords.length; $i++>>
-			<<if _toSearch.indexOf(setup.badWords[$i]) != -1>>
-				<<set _insultingName = 1>>
-				<<break>>
-			<</if>>
-		<</for>>
-	<</if>>
-<</if>>
-<<if _insultingName == 1>>
-	<<if getSlave($AS).fetish == "mindbroken">>
-		$His new name would be insulting to a normal $girl, but $he dully accepts that it is an accurate description and goes about $his duties.
-	<<elseif getSlave($AS).devotion < -50>>
-		Being given such a degrading name @@.mediumorchid;further increases $his hatred@@ of you.
-		<<set getSlave($AS).devotion -= 5>>
-	<<elseif getSlave($AS).devotion <= 50>>
-		Being given such a degrading name @@.gold;terrifies $him,@@ since $he thinks it's fair warning for what $he'll suffer in the future.
-		<<set getSlave($AS).trust -= 5>>
-	<</if>>
-<</if>>
-
-/* Wife's surname reaction */
-<<if $oldSurname != getSlave($AS).slaveSurname>>
-	<<if getSlave($AS).relationship >= 5>>
-		<<set _i = $slaveIndices[getSlave($AS).relationshipTarget]>>
-		<<if ndef _i>>@@.red;Error, relationshipTarget not found.@@<</if>>
-		<<if $slaves[_i].slaveSurname>>
-			<<setLocalPronouns $slaves[_i] 2>>
-			<<if getSlave($AS).slaveSurname == $slaves[_i].slaveSurname>>
-				$He's touched that $he now shares a surname with $his _wife2 $slaves[_i].slaveName $slaves[_i].slaveSurname, and is @@.mediumaquamarine;more confident than ever@@ that you intend to keep them together in marital bliss.
-				<<set getSlave($AS).trust += 5>>
-			<<elseif $oldSurname == $slaves[_i].slaveSurname>>
-				$He's concerned that $he no longer shares a surname with $his _wife2 $slaves[_i].slaveName $slaves[_i].slaveSurname, and is @@.gold;very worried@@ that you might be considering splitting them up.
-				<<set getSlave($AS).trust -= 5>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-/* PC's surname reaction */
-<<if $oldSurname != getSlave($AS).slaveSurname>>
-	<<if getSlave($AS).relationship == -3>>
-		<<if $PC.slaveSurname>>
-			<<if getSlave($AS).fetish == "mindbroken">>
-				Names are meaningless to $him and it is unlikely $he'll remember it.
-			<<elseif getSlave($AS).devotion+getSlave($AS).trust >= 175>>
-				<<if getSlave($AS).slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as <<= getSlave($AS).slaveName>> <<= getSlave($AS).slaveSurname>> now, $he starts to cry. $He tries to get $himself under control and thank you as best $he can, <<if hasAnyArms(getSlave($AS))>>wiping at the tears running down $his <<= getSlave($AS).skin>> cheeks, <</if>>but $he can't seem to stop weeping as $he thanks you over and over. $He's a sex slave, your property, and it's understandable that some doubts about the permanence of $his place as your slave $wife. This has @@.mediumaquamarine;helped reassure $him,@@ and explains the strength of $his emotional reaction. The next time you make love to $him, $he @@.hotpink;presses $himself as close to you as $he can,@@ eager to drink in as much of your presence as $he can get.
-					<<set getSlave($AS).devotion += 5, getSlave($AS).trust += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He's devastated that you'd rename $him something other than your name. $He's @@.gold;terrified@@ that you intend to discard $him as your slave $wife, and @@.mediumorchid;saddened@@ that you would take away something that was precious to $him.
-					<<set getSlave($AS).devotion -= 5, getSlave($AS).trust -= 5>>
-				<</if>>
-			<<elseif getSlave($AS).devotion < -20 && getSlave($AS).trust > 20>>
-				<<if getSlave($AS).slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as <<= getSlave($AS).slaveName>> <<= getSlave($AS).slaveSurname>> now, $he shows no reaction. You already took everything from $him, what's a name at this point?
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He doesn't care about losing your surname at first, but it quickly sets in that $he may have @@.gold;pushed $his position too far.@@
-					<<set getSlave($AS).trust -= 20>>
-				<</if>>
-			<<elseif getSlave($AS).devotion < -20>>
-				<<if getSlave($AS).slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as <<= getSlave($AS).slaveName>> <<= getSlave($AS).slaveSurname>> now, $he pleads with you not to steal $his name. It matters little to you, and $he is forced to @@.hotpink;accept your will.@@
-					<<set getSlave($AS).devotion += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He's @@.mediumorchid;happy@@ to no longer have to share a name with $his tormentor, but the bliss doesn't last long as @@.gold;dread@@ quickly sets in about what this may mean.
-					<<set getSlave($AS).devotion += 5, getSlave($AS).trust -= 10>>
-				<</if>>
-			<<else>>
-				<<if getSlave($AS).slaveSurname == $PC.slaveSurname>>
-					When you tell $him that $he's to be known as <<= getSlave($AS).slaveName>> <<= getSlave($AS).slaveSurname>> now, $he nods with approval feeling @@.mediumaquamarine;that $he may hold at least some value in your eyes.@@
-					<<set getSlave($AS).trust += 5>>
-				<<elseif $oldSurname == $PC.slaveSurname>>
-					$He accepts that you'd rename $him something other than your name. $He's @@.gold;a little scared@@ that you intend to discard $him as your slave $wife, or worse, but realizes this was not only a possibility, but likely.
-					<<set getSlave($AS).trust -= 5>>
-				<</if>>
-			<</if>>
-		<</if>>
-	<</if>>
-<</if>>
-
-<</if>> /* close new name check */
diff --git a/src/npc/surgery/organFarm.js b/src/npc/surgery/organFarm.js
index 20454e84b151f3e948285185548649209d3ebbe9..fec0a78bd3ace3a06bba19ae1190bb5613820fb3 100644
--- a/src/npc/surgery/organFarm.js
+++ b/src/npc/surgery/organFarm.js
@@ -37,9 +37,10 @@ App.Medicine.OrganFarm.growActions = function(slave) {
 	let wait = [];
 	for (const organ of organOrder) {
 		if (slaveOrgans[organ] === 0) {
-			if (O[organ].canGrow()) {
+			if (O[organ].canGrow(slave)) {
 				grow += `<div>${App.UI.link(O[organ].name, App.Medicine.OrganFarm.growOrgan, [slave, organ], "Remote Surgery")}</div>`;
-				grow += `<div class="detail">Costs ${cashFormat(O[organ].cost)}${O[organ].tooltip !== "" ? ` and ${O[organ].tooltip}` : ""}.</div>`;
+				const tooltip = typeof O[organ].tooltip === "string" ? O[organ].tooltip : O[organ].tooltip(slave);
+				grow += `<div class="detail">Costs ${cashFormat(O[organ].cost)}${tooltip !== "" ? ` and ${tooltip}` : ""}.</div>`;
 			}
 		} else if (slaveOrgans[organ] > 0) {
 			// in growth organs at the end of the list
diff --git a/src/npc/surgery/organs.js b/src/npc/surgery/organs.js
index 467a9747bcd17b0942927548912a6310199621e7..e73f83bec6b9782b98e6c94184eff0546d0d160c 100644
--- a/src/npc/surgery/organs.js
+++ b/src/npc/surgery/organs.js
@@ -216,6 +216,7 @@ App.Medicine.OrganFarm.init = function() {
 
 	new App.Medicine.OrganFarm.Organ({
 		type: "leftEye", name: "Left Eye", cost: 5000, time: 10,
+		tooltip: s => getLeftEyeVision(s) === 2 ? "would not improve this slave" : "",
 		actions: [
 			new App.Medicine.OrganFarm.OrganImplantAction({
 				name: "Implant", healthImpact: 10, surgeryType: "newEyes",
@@ -246,6 +247,7 @@ App.Medicine.OrganFarm.init = function() {
 	});
 	new App.Medicine.OrganFarm.Organ({
 		type: "rightEye", name: "Right Eye", cost: 5000, time: 10,
+		tooltip: s => getRightEyeVision(s) === 2 ? "would not improve this slave" : "",
 		actions: [
 			new App.Medicine.OrganFarm.OrganImplantAction({
 				name: "Implant", healthImpact: 10, surgeryType: "newEyes",
diff --git a/src/personalAssistant/assistant.js b/src/personalAssistant/assistant.js
index 19712cbac6e288288d68d496f898fbbc34304187..c00c4970e6ddd8278e8e47710b54034ed1e7d7ec 100644
--- a/src/personalAssistant/assistant.js
+++ b/src/personalAssistant/assistant.js
@@ -6,45 +6,7 @@ globalThis.assistant = (function() {
 	};
 
 	function BC() {
-		function convert(oldVar, variable, value = null) {
-			if (jsDef(V[oldVar])) {
-				if (typeof V[oldVar] !== "number" || (typeof V[oldVar] === "number" && V[oldVar] > 0)) {
-					V.assistant[variable] = value === null ? V[oldVar] : value;
-				}
-			}
-			delete V[oldVar];
-		}
-
-		const object = Object.assign({},
-		{
-		personality: V.assistant,
-		name: V.assistantName,
-		power: V.assistantPower,
-		appearance: V.assistantAppearance,
-		});
-		V.assistant = object;
-		delete V.assistantName; delete V.assistantPower;
-		delete V.assistantAppearance; delete V.assistantPronouns;
-
-		convert('marketAssistantAnnounced', 'market', {});
-		convert('assistantExtra1', 'Extra1');
-		convert('assistantExtra2', 'Extra2');
-		convert('assistantNameAnnounced', 'announcedName');
-		convert('assistantBodyDesire', 'bodyDesire');
-		convert('assistantOptions', 'options');
-		convert('assistantFSOptions', 'fsOptions');
-		convert('assistantFSAppearance', 'fsAppearance');
-
-		if (V.assistant.market) {
-			Object.assign(V.assistant.market,
-			{
-			relationship: V.marketAssistantRelationship,
-			limit: V.marketAssistantLimit,
-			aggressiveness: V.marketAssistantAggressiveness,
-			});
-		}
-		delete V.marketAssistantRelationship; delete V.marketAssistantLimit;
-		delete V.marketAssistantAggressiveness; delete V.marketAssistantPronouns;
+		App.Update.assistantBC();
 	}
 
 	function pronouns() {
@@ -55,7 +17,7 @@ globalThis.assistant = (function() {
 			if (V.assistant.appearance === "incubus" && V.diversePronouns > 0) {
 				o.main = {pronoun: App.Data.Pronouns.Kind.male};
 				o.market = {pronoun: App.Data.Pronouns.Kind.female};
-			} else if (V.assistantAppearance === "succubus" && V.diversePronouns > 0) {
+			} else if (V.assistant.appearance === "succubus" && V.diversePronouns > 0) {
 				o.main = {pronoun: App.Data.Pronouns.Kind.female};
 				o.market = {pronoun: App.Data.Pronouns.Kind.male};
 			} else {
@@ -67,20 +29,18 @@ globalThis.assistant = (function() {
 
 	function manage() {
 		V.assistant = V.assistant || {};
-		Object.assign(V.assistant,
-		{
-		personality: V.assistant.personality || 0,
-		name: V.assistant.name || "your personal assistant",
-		power: V.assistant.power || 0,
-		appearance: V.assistant.appearance || "normal",
+		Object.assign(V.assistant, {
+			personality: V.assistant.personality || 0,
+			name: V.assistant.name || "your personal assistant",
+			power: V.assistant.power || 0,
+			appearance: V.assistant.appearance || "normal",
 		});
 
 		if (V.assistant.market) {
-			Object.assign(V.assistant.market,
-			{
-			relationship: V.assistant.market.relationship || "cute",
-			limit: V.assistant.market.limit || 0,
-			aggressiveness: V.assistant.market.aggressiveness || 0,
+			Object.assign(V.assistant.market, {
+				relationship: V.assistant.market.relationship || "cute",
+				limit: V.assistant.market.limit || 0,
+				aggressiveness: V.assistant.market.aggressiveness || 0,
 			});
 		}
 		if (V.week < 18) {
diff --git a/src/personalAssistant/assistantBC.js b/src/personalAssistant/assistantBC.js
new file mode 100644
index 0000000000000000000000000000000000000000..c7ef8a4f3d827f4f7e055f08f43f047ee0f126f1
--- /dev/null
+++ b/src/personalAssistant/assistantBC.js
@@ -0,0 +1,39 @@
+// @ts-nocheck
+App.Update.assistantBC = function() {
+	function convert(oldVar, variable, value = null) {
+		if (jsDef(V[oldVar])) {
+			if (typeof V[oldVar] !== "number" || (typeof V[oldVar] === "number" && V[oldVar] > 0)) {
+				V.assistant[variable] = value === null ? V[oldVar] : value;
+			}
+		}
+		delete V[oldVar];
+	}
+
+	V.assistant = Object.assign({}, {
+		personality: V.assistant,
+		name: V.assistantName,
+		power: V.assistantPower,
+		appearance: V.assistantAppearance,
+	});
+	delete V.assistantName; delete V.assistantPower;
+	delete V.assistantAppearance; delete V.assistantPronouns;
+
+	convert('marketAssistantAnnounced', 'market', {});
+	convert('assistantExtra1', 'Extra1');
+	convert('assistantExtra2', 'Extra2');
+	convert('assistantNameAnnounced', 'announcedName');
+	convert('assistantBodyDesire', 'bodyDesire');
+	convert('assistantOptions', 'options');
+	convert('assistantFSOptions', 'fsOptions');
+	convert('assistantFSAppearance', 'fsAppearance');
+
+	if (V.assistant.market) {
+		Object.assign(V.assistant.market, {
+			relationship: V.marketAssistantRelationship,
+			limit: V.marketAssistantLimit,
+			aggressiveness: V.marketAssistantAggressiveness,
+		});
+	}
+	delete V.marketAssistantRelationship; delete V.marketAssistantLimit;
+	delete V.marketAssistantAggressiveness; delete V.marketAssistantPronouns;
+};
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index 4efbdaf320adf2e8d164496d3c6f4216be6eab5f..fe3b61163ee21ecd9c20c22885b90b8f8fec363c 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -1991,6 +1991,8 @@ App.Entity.PlayerState = class PlayerState {
 		 *
 		 * 0: no; 1: yes */
 		this.staminaPills = 0;
+		/** Player's coefficient of inbreeding */
+		this.inbreedingCoeff = 0;
 	}
 
 	/** Creates an object suitable for setting nested attributes as it would be a SlaveState
diff --git a/src/player/js/playerJS.js b/src/player/js/playerJS.js
index dbc4ccb9d42c4c6fae34b48de3a145ae6267103c..bd80ac8d1271e2fd08bc9e7d6a0d666c92c3eaa2 100644
--- a/src/player/js/playerJS.js
+++ b/src/player/js/playerJS.js
@@ -491,6 +491,11 @@ globalThis.PCTitle = function() {
 	} else if (V.TSS.schoolPresent === 1) {
 		schoolsPresent.push("The Slave School");
 	}
+	if (V.TUO.schoolProsperity >= 10) {
+		schoolsPerfected.push("The Utopian Orphanage");
+	} else if (V.TUO.schoolPresent === 1) {
+		schoolsPresent.push("The Utopian Orphanage");
+	}
 	if (V.GRI.schoolProsperity >= 10) {
 		schoolsPerfected.push("The Growth Research Institute");
 	} else if (V.GRI.schoolPresent === 1) {
diff --git a/src/pregmod/FCTV/FCTV.js b/src/pregmod/FCTV/FCTV.js
index 9522066b5ae09a4dcb500d177fa9f6498f12cad7..6d85dd76dd1e27d4ae5b253efc42f87f0bfd92b2 100644
--- a/src/pregmod/FCTV/FCTV.js
+++ b/src/pregmod/FCTV/FCTV.js
@@ -6,7 +6,7 @@ receiver - What is the state of the network box.
 channel[numberAsString] - how many times it has been viewed.
 channel.last - program viewed last week.
 pcViewership.count - How many weeks since the PC last watched FCTV.
-pcViewership.frequency - How oftern should the PC watch FCTV.
+pcViewership.frequency - How often should the PC watch FCTV.
 	-1: Never.
 	1: at least once a week.
 	2: least every two weeks.
@@ -17,7 +17,7 @@ weekEnabled - The week FCTV was installed.
 globalThis.FCTV = (function() {
 	return {
 		channels: channels,
-		manage: manage,
+		initChannels: initChannels,
 		showChannel: showChannel,
 		incrementChannel: incrementChannel,
 		incrementShow: incrementShow,
@@ -30,35 +30,10 @@ globalThis.FCTV = (function() {
 		return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
 	}
 
-	function manage() {
-		function convert(a, b) {
-			return V.FCTVreceiver ? a : b;
-		}
-		delete V.FCTVenable;
-
-		V.FCTV.receiver = V.FCTV.receiver > -1 ? V.FCTV.receiver : -1;
-		if (convert && V.receiverAvailable) {
-			V.FCTV.receiver = V.FCTVreceiver > 0 ? V.FCTVreceiver : 0;
-		}
-
-		if (V.FCTV.receiver > -1) {
-			V.FCTV.channel = V.FCTV.channel || {};
-			V.FCTV.pcViewership = V.FCTV.pcViewership || {};
-
-			for (let i = 0; i < channels().length; i++) {
-				let channel = num(channels()[i], true);
-				let currentChannel = 'show' + capFirstChar(channel);
-				V.FCTV.channel[channel] = convert(V[currentChannel], V.FCTV.channel[channel]) || 0;
-			}
-			V.FCTV.channel.last = convert(V.lastShow, V.FCTV.channel.last) || -1;
-
-			V.FCTV.pcViewership.count = convert(V.FCTVcount, V.FCTV.pcViewership.count) || 0;
-			V.FCTV.pcViewership.frequency = convert(V.FCTVrate, V.FCTV.pcViewership.frequency) || 4;
-			V.FCTV.remote = convert(V.FCTVremote, V.FCTV.remote) || 0;
-
-			if (V.FCTVreceiver && !V.FCTV.weekEnabled) {
-				V.FCTV.weekEnabled = V.receiverAvailable > 1 ? V.receiverAvailable : 0;
-			}
+	function initChannels() {
+		const channelList = FCTV.channels();
+		for (let i = 0; i < channelList.length; i++) {
+			V.FCTV.channel[num(channelList[i], true)] = 0;
 		}
 	}
 
diff --git a/src/pregmod/FCTV/FCTVBC.js b/src/pregmod/FCTV/FCTVBC.js
new file mode 100644
index 0000000000000000000000000000000000000000..820799a07febba8717bc81f9a6f65bd95edfe932
--- /dev/null
+++ b/src/pregmod/FCTV/FCTVBC.js
@@ -0,0 +1,34 @@
+
+// @ts-nocheck
+App.Update.FCTV = function() {
+	function convert(a, b) {
+		return V.FCTVreceiver ? a : b;
+	}
+
+	function initChannels() {
+		const channelList = FCTV.channels();
+		for (let i = 0; i < channelList.length; i++) {
+			const channel = num(channelList[i], true);
+			const currentChannel = 'show' + capFirstChar(channel);
+			V.FCTV.channel[channel] = convert(V[currentChannel], V.FCTV.channel[channel]) || 0;
+		}
+	}
+
+	V.FCTV = V.FCTV || {};
+	V.FCTV.receiver = V.FCTV.receiver > -1 ? V.FCTV.receiver : -1;
+	if (convert && V.receiverAvailable) {
+		V.FCTV.receiver = V.FCTVreceiver > 0 ? V.FCTVreceiver : 0;
+	}
+	V.FCTV.pcViewership = V.FCTV.pcViewership || {};
+	V.FCTV.channel = V.FCTV.channel || {};
+	initChannels();
+	V.FCTV.channel.last = convert(V.lastShow, V.FCTV.channel.last) || -1;
+
+	V.FCTV.pcViewership.count = convert(V.FCTVcount, V.FCTV.pcViewership.count) || 0;
+	V.FCTV.pcViewership.frequency = convert(V.FCTVrate, V.FCTV.pcViewership.frequency) || 4;
+	V.FCTV.remote = convert(V.FCTVremote, V.FCTV.remote) || 0;
+
+	if (V.FCTVreceiver && !V.FCTV.weekEnabled) {
+		V.FCTV.weekEnabled = V.receiverAvailable > 1 ? V.receiverAvailable : 0;
+	}
+};
diff --git a/src/pregmod/FCTV/seFCTVremote.tw b/src/pregmod/FCTV/seFCTVremote.tw
index 2efbd595339dcb710546f8bc99e79b456f3eca97..43f205996d1ddccca8cb6b02b2d3cc737f4c5905 100644
--- a/src/pregmod/FCTV/seFCTVremote.tw
+++ b/src/pregmod/FCTV/seFCTVremote.tw
@@ -57,11 +57,11 @@
 </span>
 
 <p>
-	You have a gap in your schedule in the morning, and decide to watch some FCTV
-	<<if $Concubine != 0>>
+	You have a gap in your schedule in the morning, and decide to watch some
+	<<if _S.Concubine>>
 		FCTV with your concubine,
 		<span class="slave name simple">
-			$Concubine.slaveName.
+			_S.Concubine.slaveName.
 		</span>
 	<<else>>
 	FCTV.
@@ -340,4 +340,4 @@
 			</p>
 		<</replace>>
 	<</link>>
-</p>
\ No newline at end of file
+</p>
diff --git a/src/pregmod/FCTV/seFCTVshows.tw b/src/pregmod/FCTV/seFCTVshows.tw
index 9992c931caa1efe0fdb39fd570d627bb003ba39b..2cbb9bdb45d8cba086cf8fc06f28cb911d275e87 100644
--- a/src/pregmod/FCTV/seFCTVshows.tw
+++ b/src/pregmod/FCTV/seFCTVshows.tw
@@ -148,13 +148,13 @@
 	And here in Canadia, there are no restrictions on fertility drugs for the winner, so the competition this season is fierce! $FCTV.channel.two ladies have already been sent packing, who will be Canadia's... Next Top Breeder!?" The title finally pops up, redundantly labeling the show as 'Next Top Breeder: Canadia'.
 	<<if _MSL >= 1>>
 		<br><br>You don't spend very much time actually watching the show; the randy opening, perverted competitions, and constant talk of creampies quickly has
-		<<if _MSL >= 1 && $Concubine != 0>>
-			<<setLocalPronouns $Concubine 2>>
-			@@.pink;$Concubine.slaveName@@ eager for some attention from _his2 own arcology owner. Of course, the same could be said for the other eager slaves living in your bedroom, and the situation quickly devolves into a lust-filled <<if _MSL > 1>>orgy<<else>>threesome<</if>>.
-		<<elseif _MSL == 0 && $Concubine != 0>>
-			<<setLocalPronouns $Concubine 2>>
-			@@.pink;$Concubine.slaveName@@ eager for some attention from _his2 own arcology owner. You've trained _him2 well, and _he2 knows exactly how to please you. You spend the rest of the evening doing something a lot more fun than watching reality TV.
-		<<elseif _MSL >= 1 && $Concubine == 0>>
+		<<if _MSL >= 1 && _S.Concubine>>
+			<<setLocalPronouns _S.Concubine 2>>
+			@@.pink;_S.Concubine.slaveName@@ eager for some attention from _his2 own arcology owner. Of course, the same could be said for the other eager slaves living in your bedroom, and the situation quickly devolves into a lust-filled <<if _MSL > 1>>orgy<<else>>threesome<</if>>.
+		<<elseif _MSL == 0 && _S.Concubine>>
+			<<setLocalPronouns _S.Concubine 2>>
+			@@.pink;_S.Concubine.slaveName@@ eager for some attention from _his2 own arcology owner. You've trained _him2 well, and _he2 knows exactly how to please you. You spend the rest of the evening doing something a lot more fun than watching reality TV.
+		<<elseif _MSL >= 1 && $ConcubineID == 0>>
 			the pleasure slaves in your bed eager for some attention from their own arcology owner. You spend the rest of the evening doing something a lot more fun than watching reality TV.
 		<</if>>
 	<</if>>
@@ -909,10 +909,10 @@
 	<br><br>
 	<<if $purchasedSagBGone == 1>>
 		You've already made the mistake of ordering the sham of a product. While it gave you a great excuse to fondle breasts, it's not like you needed one in the first place.
-		<<if $Concubine != 0 && canTalk($Concubine)>>
-			<<run Enunciate($Concubine)>>
-			<<setLocalPronouns $Concubine>>
-			"I told you it wouldn't work, <<Master>>. Plu<<s>> you know you can touch the<<s>>e anytime!" $Concubine.slaveName shakes $his chest at you.
+		<<if _S.Concubine && canTalk(_S.Concubine)>>
+			<<run Enunciate(_S.Concubine)>>
+			<<setLocalPronouns _S.Concubine>>
+			"I told you it wouldn't work, <<Master>>. Plu<<s>> you know you can touch the<<s>>e anytime!" _S.Concubine.slaveName shakes $his chest at you.
 		<</if>>
 	<<elseif FCTV.channelCount(1, 'gt')>>
 		You could always order a crate to play around with. Who knows, maybe it'll actually work?<<if $PC.dick != 0>> At the very least it should make for some decent lubricant for a titfuck.<</if>>
@@ -928,18 +928,18 @@
 		</span>
 	<<else>>
 		You could always order a crate to play around with. Who knows, maybe it'll actually work?<<if $PC.dick != 0>> At the very least it should make for some decent lubricant for a titfuck.<</if>>
-		<<if $Concubine != 0>>
-			<<run Enunciate($Concubine)>>
-			<<setLocalPronouns $Concubine>>
-			<<if $Concubine.boobs > 2000 && $Concubine.boobShape == "saggy">>
-				$Concubine.slaveName <<if hasAnyArms($Concubine)>>hefts<<else>>shakes<</if>> $his breasts and lets them flop back into their usual saggy position. <<if canTalk($Concubine)>>"Far too late for the<<s>>e ladie<<s>>. I doubt it will work though, product<<s>> like that never do."<<else>>$He sighs doubtfully.<</if>>
-			<<elseif $Concubine.boobs > 2000>>
-				$Concubine.slaveName massages $his big breasts. <<if canTalk($Concubine)>>"I doubt it will work, but if you're looking for an e<<x>>cu<<s>>e, you don't need one!"<<else>>$He scoffs at the commercial and clearly expresses $his doubt before puffing out $his chest at you.<</if>> $He leans into you so $his bust flops into your lap.
+		<<if _S.Concubine>>
+			<<run Enunciate(_S.Concubine)>>
+			<<setLocalPronouns _S.Concubine>>
+			<<if _S.Concubine.boobs > 2000 && _S.Concubine.boobShape == "saggy">>
+				_S.Concubine.slaveName <<if hasAnyArms(_S.Concubine)>>hefts<<else>>shakes<</if>> $his breasts and lets them flop back into their usual saggy position. <<if canTalk(_S.Concubine)>>"Far too late for the<<s>>e ladie<<s>>. I doubt it will work though, product<<s>> like that never do."<<else>>$He sighs doubtfully.<</if>>
+			<<elseif _S.Concubine.boobs > 2000>>
+				_S.Concubine.slaveName massages $his big breasts. <<if canTalk(_S.Concubine)>>"I doubt it will work, but if you're looking for an e<<x>>cu<<s>>e, you don't need one!"<<else>>$He scoffs at the commercial and clearly expresses $his doubt before puffing out $his chest at you.<</if>> $He leans into you so $his bust flops into your lap.
 			<<else>>
-				$Concubine.slaveName <<if hasAnyArms($Concubine)>>cups<<else>>considers<</if>> $his breasts. <<if canTalk($Concubine)>>"What a joke. I'm <<s>>ure it doe<<s>>n't work, plu<<s>> don't you think they are lovely enough already, <<Master>>?"<<else>>$He scoffs with doubt before proudly sticking out $his chest.<</if>> $He bounces $his tits for you. You'll have to agree with $him; not a bit of sag to them.
+				_S.Concubine.slaveName <<if hasAnyArms(_S.Concubine)>>cups<<else>>considers<</if>> $his breasts. <<if canTalk(_S.Concubine)>>"What a joke. I'm <<s>>ure it doe<<s>>n't work, plu<<s>> don't you think they are lovely enough already, <<Master>>?"<<else>>$He scoffs with doubt before proudly sticking out $his chest.<</if>> $He bounces $his tits for you. You'll have to agree with $him; not a bit of sag to them.
 			<</if>>
 			<<if $PC.boobs >= 1400 && $PC.boobsImplant == 0>>
-				$He slides closer to you, <<if hasAnyArms($Concubine)>>wraps <<if hasBothArms($Concubine)>>an<<else>>$his<</if>> arm around your back, and grabs<<else>>pushing $his head against<</if>> your huge breasts. <<if canTalk($Concubine)>>"Oh <<Master>>! It feel<<s>> like YOU might need it!" $He <<if hasAnyArms($Concubine)>>jiggles your boobs in $his hand<<if hasBothArms($Concubine)>>s<</if>><<else>>nuzzles further into your rack<</if>>.<<else>>$He <<if hasAnyArms($Concubine)>>jiggles your huge boobs in $his hand<<if hasBothArms($Concubine)>>s<</if>><<else>>nuzzles into your huge rack<</if>>. $He's calling you saggy!<</if>> You take that as an open invitation and throw the covers over the two of you so you can have a little fun before bed.
+				$He slides closer to you, <<if hasAnyArms(_S.Concubine)>>wraps <<if hasBothArms(_S.Concubine)>>an<<else>>$his<</if>> arm around your back, and grabs<<else>>pushing $his head against<</if>> your huge breasts. <<if canTalk(_S.Concubine)>>"Oh <<Master>>! It feel<<s>> like YOU might need it!" $He <<if hasAnyArms(_S.Concubine)>>jiggles your boobs in $his hand<<if hasBothArms(_S.Concubine)>>s<</if>><<else>>nuzzles further into your rack<</if>>.<<else>>$He <<if hasAnyArms(_S.Concubine)>>jiggles your huge boobs in $his hand<<if hasBothArms(_S.Concubine)>>s<</if>><<else>>nuzzles into your huge rack<</if>>. $He's calling you saggy!<</if>> You take that as an open invitation and throw the covers over the two of you so you can have a little fun before bed.
 			<</if>>
 		<<elseif $PC.boobs >= 1400 && $PC.boobsImplant == 0>>
 			You cup your huge breasts. They're pretty large and you swear they've been drooping a little lately; maybe you could benefit from this cream...
@@ -1320,13 +1320,12 @@
 			appreciating the life growing within you and what future prospects may await it.
 		<<elseif $PC.dick >= 1>>
 			Stunned and fully erect, you inexplicably feel the urge to creampie a pussy, so you
-			<<if $Concubine != 0 && $Concubine.vagina > 0 && canDoVaginal($Concubine)>>
-				<<setLocalPronouns $Concubine>>
-				grab $Concubine.slaveName and recreate the entire ending with $him.
-				<<set _fctvs = $slaveIndices[$Concubine.ID]>>
-				<<set $slaves[_fctvs].counter.vaginal++, $vaginalTotal++>>
-				<<if canImpreg($Concubine, $PC)>>
-					<<= knockMeUp($slaves[_fctvs], 10, 0, -1, 1)>>
+			<<if _S.Concubine && _S.Concubine.vagina > 0 && canDoVaginal(_S.Concubine)>>
+				<<setLocalPronouns _S.Concubine>>
+				grab _S.Concubine.slaveName and recreate the entire ending with $him.
+				<<set _S.Concubine.counter.vaginal++, $vaginalTotal++>>
+				<<if canImpreg(_S.Concubine, $PC)>>
+					<<= knockMeUp(_S.Concubine, 10, 0, -1, 1)>>
 				<</if>>
 			<<elseif $fuckSlaves > 0>>
 				grab the nearest slave watching with you, who is overjoyed to recreate the entire ending with you.
@@ -1336,12 +1335,11 @@
 			You've never had a more hope-filled orgasm.
 		<<elseif $PC.vagina >= 1>>
 			Stunned and soaking wet, you hurry to
-			<<if $Concubine != 0 && canPenetrate($Concubine) && canImpreg($PC, $Concubine) && ($policies.sexualOpeness == 1 || $Concubine.toyHole == "dick")>>
-				<<setLocalPronouns $Concubine>>
-				get $Concubine.slaveName nice and hard before recreating the entire ending with $him. You've never had a more hope-filled orgasm.
-				<<= knockMeUp($PC, 10, 0, $Concubine.ID, 1)>>
-				<<set _fctvs = $slaveIndices[$Concubine.ID]>>
-				<<set $slaves[_fctvs].counter.penetrative++, $penetrativeTotal++>>
+			<<if _S.Concubine && canPenetrate(_S.Concubine) && canImpreg($PC, _S.Concubine) && ($policies.sexualOpeness == 1 || _S.Concubine.toyHole == "dick")>>
+				<<setLocalPronouns _S.Concubine>>
+				get _S.Concubine.slaveName nice and hard before recreating the entire ending with $him. You've never had a more hope-filled orgasm.
+				<<= knockMeUp($PC, 10, 0, $ConcubineID, 1)>>
+				<<set _S.Concubine.counter.penetrative++, $penetrativeTotal++>>
 			<<elseif $policies.sexualOpeness == 1>>
 				find your favorite cock to get a creampie from. You've never had a more lust-filled orgasm.
 			<<else>>
@@ -1515,7 +1513,7 @@
 
 	/* All actors are at least 18 */
 	<p>
-		As you snuggle in for the night<<if $Concubine != 0>> with your concubine<</if>>, you <<if $usedRemote == 1>>tune to<<else>>begin watching<</if>> the <i>Age of Slavery</i> channel. With so many new types of arcologies emerging, it's sometimes difficult to tell if you are watching events unfolding on a set or in a real arcology with a historical society.
+		As you snuggle in for the night<<if _S.Concubine>> with your concubine<</if>>, you <<if $usedRemote == 1>>tune to<<else>>begin watching<</if>> the <i>Age of Slavery</i> channel. With so many new types of arcologies emerging, it's sometimes difficult to tell if you are watching events unfolding on a set or in a real arcology with a historical society.
 	</p>
 	<<if FCTV.channelCount(1)>>
 		"Well, you did well to win the 'cleanest urchin' contest."
@@ -1573,9 +1571,9 @@
 		<br> The others pause for a moment, then start to laugh. "Ahh, I see where this is going. We could teach her. Yes, let's 'help her out'."
 		<br> Kate pulls out a large makeup kit. "Ordered from Sears and Roebuck by my husband, as a 'makeup' present for his dalliances. For all the times I used it I never got his attention. But you know, maybe I just never used <i>enough</i>."
 		<br> The camera pans away and when it returns, the Indian girl's face is positively caked with the most absurdly overdone makeup you have ever seen. It's both hot and hilarious in the same way.
-		<<if $Concubine.makeup > 0>>
+		<<if _S.Concubine.makeup > 0>>
 			You look at your own concubine's face.
-			<<= App.Desc.makeup($Concubine)>>
+			<<= App.Desc.makeup(_S.Concubine)>>
 			It's just the way you like it, but it's nowhere near as messy and overdone as the girl in the show.
 		<</if>>
 		<br> "Mmmm, good but now the rest of her seems a little plain," Annie frowns. "Don't loose girls wear big boots?"
diff --git a/src/pregmod/FCTV/seFCTVwatch.tw b/src/pregmod/FCTV/seFCTVwatch.tw
index d7306297854d8d108abb66b7e97c9d6430dac1d7..ae22f2b4c656480fd721fce2e8cf3f9a943a7605 100644
--- a/src/pregmod/FCTV/seFCTVwatch.tw
+++ b/src/pregmod/FCTV/seFCTVwatch.tw
@@ -3,7 +3,7 @@
 <<set $nextButton = "Continue", $nextLink = "Scheduled Event", $returnTo = "Scheduled Event", $encyclopedia = "FCTV">>
 <<set $FCTV.pcViewership.count++, _possibleChannels = [0].concat(FCTV.channels())>>
 
-Tired after a long day, you tell <<if $Concubine>>your concubine: @@.pink;$Concubine.slaveName@@<<else>>$assistant.name<</if>> to turn on the TV and
+Tired after a long day, you tell <<if _S.Concubine>>your concubine: @@.pink;_S.Concubine.slaveName@@<<else>>$assistant.name<</if>> to turn on the TV and
 <<if !$usedRemote>>
 	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 $cheatMode > 0 || $debugMode > 0 || $FCTV.remote>>
@@ -72,4 +72,4 @@ Tired after a long day, you tell <<if $Concubine>>your concubine: @@.pink;$Concu
 	</center>
 <</if>>
 
-<<if $FCTV.channel.selected >= -1>> <<include "SE FCTV Shows">> <</if>>
\ No newline at end of file
+<<if $FCTV.channel.selected >= -1>> <<include "SE FCTV Shows">> <</if>>
diff --git a/src/pregmod/beastFucked.tw b/src/pregmod/beastFucked.tw
index 18d07cf11af32b178263e4fc4a714172e2bc1404..95b4fc565194c783cb4914c9a313ba55f5e95c23 100644
--- a/src/pregmod/beastFucked.tw
+++ b/src/pregmod/beastFucked.tw
@@ -144,7 +144,7 @@
 		<</if>>
 			You have $him <<if ($activeSlave.clothes != "no clothing") && (_sexAct != "oral")>>take off $his clothing, then <</if>><<if _sexAct == "oral">>lay <<else>>kneel <</if>>down on the floor <<if _sexAct != "oral">>with $his ass in the air<<else>>on $his back<</if>>
 	<<else>>
-		and $Bodyguard.slaveName has to physically prevent $him from running out of the room<<if random(1,100) > 75>><<set $activeSlave.minorInjury = either("black eye", "bruise", "split lip")>>, resulting in a $activeSlave.minorInjury. <<else>>. <</if>>You <<if ($activeSlave.clothes != "no clothing")>>force $him to take off $his $activeSlave.clothes and <</if>>restrain $him on the floor <<if _sexAct != "oral">>with $his ass in the air<<else>>with a ring gag in $his mouth<</if>> — all the while ignoring $his screams and pleas for mercy —
+		and _S.Bodyguard.slaveName has to physically prevent $him from running out of the room<<if random(1,100) > 75>><<set $activeSlave.minorInjury = either("black eye", "bruise", "split lip")>>, resulting in a $activeSlave.minorInjury. <<else>>. <</if>>You <<if ($activeSlave.clothes != "no clothing")>>force $him to take off $his $activeSlave.clothes and <</if>>restrain $him on the floor <<if _sexAct != "oral">>with $his ass in the air<<else>>with a ring gag in $his mouth<</if>> — all the while ignoring $his screams and pleas for mercy —
 	<</if>>
 <<else>>
 	but $he knows $he is powerless to stop you. You <<if ($activeSlave.clothes != "no clothing")>>take off $his clothes and <</if>><<if _sexAct == "oral">>place a ring gag in $his mouth, then<</if>>place $him on the ground with a cushion under $his hips <<if _sexAct != "oral">>, $his ass in the air,<</if>>
diff --git a/src/pregmod/eliteTakeOver.tw b/src/pregmod/eliteTakeOver.tw
index ae81330e42b6956afa232de5a58b241bc2eedaa8..9cdc8a8314e91d2d1c2a5bf2dd8d39548ffee2db 100644
--- a/src/pregmod/eliteTakeOver.tw
+++ b/src/pregmod/eliteTakeOver.tw
@@ -6,7 +6,7 @@
 /* preg/lobotomization check */
 You knew this day would come. Surrounding yourself with powerful people has its boons, but also poses a distinct threat. You've heard them murmuring when you enter the room, you've seen them excuse themselves from your presence, and lately they've been overtly hostile. The Societal Elite have turned on you.
 <br>
-You look up from your desk as the locked door to your office unseals, and a dozen individuals brazenly walk into your view.<<if $Bodyguard != 0>><<setLocalPronouns $Bodyguard>> $Bodyguard.slaveName stands between you and them. A single glare from the leader of the bunch and $he backs off, eyes to the ground.<</if>>
+You look up from your desk as the locked door to your office unseals, and a dozen individuals brazenly walk into your view.<<if _S.Bodyguard>><<setLocalPronouns _S.Bodyguard>> _S.Bodyguard.slaveName stands between you and them. A single glare from the leader of the bunch and $he backs off, eyes to the ground.<</if>>
 <br>
 <<if $PC.pregSource == -1 || $PC.pregSource == -6>>
 	<<if $PC.refreshmentType == 0>>
@@ -30,8 +30,8 @@ You look up from your desk as the locked door to your office unseals, and a doze
 	<br>
 	What was that last thing he said? It's getting hard to think right. The $PC.refreshment... Drugged... You collapse onto your desk, unconscious.
 	<br>
-	<<if $Bodyguard != 0>>
-		A sharp pain and a rush of adrenaline drags you back to your senses. Sneaking a peak, you witness $Bodyguard.slaveName valiantly trying to protect you, despite <<if hasAnyProstheticLimbs($Bodyguard)>>$his cybernetics being locked down<<else>>the overwhelming odds<</if>>. As $he and their leader struggle closer to your desk, his revolver and a syringe are dislodged onto your desk. It seems drugging you wasn't their first thought. With $Bodyguard.slaveName's risky gamble, your fate has fallen back into your hands.
+	<<if _S.Bodyguard>>
+		A sharp pain and a rush of adrenaline drags you back to your senses. Sneaking a peak, you witness _S.Bodyguard.slaveName valiantly trying to protect you, despite <<if hasAnyProstheticLimbs(_S.Bodyguard)>>$his cybernetics being locked down<<else>>the overwhelming odds<</if>>. As $he and their leader struggle closer to your desk, his revolver and a syringe are dislodged onto your desk. It seems drugging you wasn't their first thought. With _S.Bodyguard.slaveName's risky gamble, your fate has fallen back into your hands.
 	<<else>>
 		You'll spend the rest of your life a breeder to the Societal Elite; blinded and muted. More than a slave, but still less than your master. Life isn't so bad, your two most devoted slaves were taken along to keep you happy, and you are treated quite well, possibilities considered. Though once your eggs dry up, odds are your little remaining usefulness will be at an end. Best not to think too much into it.
 		<<set _pregSurrender = 1>>
@@ -52,15 +52,15 @@ You look up from your desk as the locked door to your office unseals, and a doze
 	<br>
 	You decide to <span id="fate">make a final stand.</span>
 	<br>
-	<<if $Bodyguard != 0>>
+	<<if _S.Bodyguard>>
 		<br>
 		<<if $PC.pregSource == -1 || $PC.pregSource == -6>>
-			$Bodyguard.slaveName is already doing everything in $his power to save you.
+			_S.Bodyguard.slaveName is already doing everything in $his power to save you.
 		<<else>>
-			<<link "Attempt to signal $Bodyguard.slaveName, your bodyguard">>
+			<<link "Attempt to signal _S.Bodyguard.slaveName, your bodyguard">>
 				<<set $finalChoice = "bodyguard">>
 				<<replace "#fate">>
-					attempt to signal $Bodyguard.slaveName, your bodyguard.
+					attempt to signal _S.Bodyguard.slaveName, your bodyguard.
 				<</replace>>
 			<</link>>
 		<</if>>
diff --git a/src/pregmod/eliteTakeOverFight.tw b/src/pregmod/eliteTakeOverFight.tw
index dda107b70bfac4308d353a796ede2180759df7fc..f79d64f85125438702896e1c88e62f948f5c570d 100644
--- a/src/pregmod/eliteTakeOverFight.tw
+++ b/src/pregmod/eliteTakeOverFight.tw
@@ -5,26 +5,26 @@
 <<case "bodyguard">>
 	<<set _roll = random(0,100)>>
 	<<if $career == "mercenary">>
-		As a result of your prior experience as a mercenary you were able to successfully signal $Bodyguard.slaveName without the Elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		As a result of your prior experience as a mercenary you were able to successfully signal _S.Bodyguard.slaveName without the Elites noticing.
+		<<set _BgSignaled = 1>>
 	<<elseif $PC.skill.warfare >= 100 && _roll > 20>>
-		As a result of your mastery of warfare you were able to successfully signal $Bodyguard.slaveName without the Elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		As a result of your mastery of warfare you were able to successfully signal _S.Bodyguard.slaveName without the Elites noticing.
+		<<set _BgSignaled = 1>>
 	<<elseif $PC.skill.warfare >= 60 && _roll > 30>>
-		As a result of your expertness of warfare you were able to successfully signal $Bodyguard.slaveName without the Elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		As a result of your expertness of warfare you were able to successfully signal _S.Bodyguard.slaveName without the Elites noticing.
+		<<set _BgSignaled = 1>>
 	<<elseif $PC.skill.warfare >= 30 && _roll > 40>>
-		As a result of having some skill in warfare you were able to successfully signal $Bodyguard.slaveName without the elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		As a result of having some skill in warfare you were able to successfully signal _S.Bodyguard.slaveName without the elites noticing.
+		<<set _BgSignaled = 1>>
 	<<elseif $PC.skill.warfare >= 10 && _roll > 50>>
-		As a result of having a small amount of skill in warfare you were able to successfully signal $Bodyguard.slaveName without the Elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		As a result of having a small amount of skill in warfare you were able to successfully signal _S.Bodyguard.slaveName without the Elites noticing.
+		<<set _BgSignaled = 1>>
 	<<elseif $PC.skill.warfare < 10 && _roll > 60>>
-		By blind luck and what little you remember of your lessons in warfare are were able to successfully signal $Bodyguard.slaveName without the Elites noticing.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 1>>
+		By blind luck and what little you remember of your lessons in warfare are were able to successfully signal _S.Bodyguard.slaveName without the Elites noticing.
+		<<set _BgSignaled = 1>>
 	<<else>>
 		One of the Elites shouts "What the fuck are you trying to do?" Well, there goes that option.
-		<<set $BodyguardHasSucessfullyRecivedSignal = 0>>
+		<<set _BgSignaled = 0>>
 	<</if>>
 	<br>You then pick up the revolver and quickly take aim.
 <<case "mercenaries">>
@@ -209,10 +209,10 @@
 /*		killed = 0					*/
 /*		enslaved = 1				*/
 /*		had fun (and enslaved) = 2	*/
-<<if $BodyguardHasSucessfullyRecivedSignal == 1>>
-	<<setLocalPronouns $Bodyguard>>
-	<<run Enunciate($Bodyguard)>>
-	$Bodyguard.slaveName asks, "<<Master>>, what <<sh>>ould I do with the <<if $eliteLeft < $eliteTotal>>remaining<</if>> 'Elite' left?"
+<<if _BgSignaled == 1>>
+	<<setLocalPronouns _S.Bodyguard>>
+	<<run Enunciate(_S.Bodyguard)>>
+	_S.Bodyguard.slaveName asks, "<<Master>>, what <<sh>>ould I do with the <<if $eliteLeft < $eliteTotal>>remaining<</if>> 'Elite' left?"
 	<br>
 	<<link "Open fire">>
 		<<set $eliteFate = 0>>
@@ -261,10 +261,10 @@
 		<<set $eliteFate = 2>>
 		<<goto "eliteTakeOverResult">>
 	<</link>>
-<<elseif $Bodyguard != 0>>
-	<<setLocalPronouns $Bodyguard>>
-	<<run Enunciate($Bodyguard)>>
-	It is now that $Bodyguard.slaveName makes $his entrance, pointing $his guns at the <<if $eliteLeft < $eliteTotal>>remaining<</if>> Elite.
+<<elseif $BodyguardID != 0>>
+	<<setLocalPronouns _S.Bodyguard>>
+	<<run Enunciate(_S.Bodyguard)>>
+	It is now that _S.Bodyguard.slaveName makes $his entrance, pointing $his guns at the <<if $eliteLeft < $eliteTotal>>remaining<</if>> Elite.
 	$He then asks "<<Master>>, what <<sh>>ould I do with the <<if $eliteLeft < $eliteTotal>>remaining<</if>> 'Elite' left?"
 	<br>
 	<<link "Open fire">>
diff --git a/src/pregmod/eliteTakeOverResult.tw b/src/pregmod/eliteTakeOverResult.tw
index dfd398604c6ef006b027e5a5120fefdf8c601e4a..2aeb5002fcb9ff3cfa80a277f155d1d20b52df1c 100644
--- a/src/pregmod/eliteTakeOverResult.tw
+++ b/src/pregmod/eliteTakeOverResult.tw
@@ -2,8 +2,8 @@
 
 <<set $nextButton = "Continue">><<UpdateNextButton>> /* unlock Continue button */
 
-<<if $Bodyguard != 0>>
-	<<setLocalPronouns $Bodyguard>>
+<<if _S.Bodyguard>>
+	<<setLocalPronouns _S.Bodyguard>>
 <</if>>
 <<setNonlocalPronouns $seeDicks>>
 
@@ -109,7 +109,7 @@
 			While some citizens respectfully step out, some join you in the fun. The impromptu festivities will go on for hours, getting more and more depraved.
 		<</if>>
 	<<else>>
-		<<if $Bodyguard.fetish == "sadist">>$Bodyguard.slaveName gets out $his melee weapon and starts to give them many small cuts, the look of pleasure on $his face is evident.<<else>>$Bodyguard.slaveName ties them and then gets out $his melee weapon and starts to abuse them; $his face is blank.<</if>>
+		<<if _S.Bodyguard.fetish == "sadist">>_S.Bodyguard.slaveName gets out $his melee weapon and starts to give them many small cuts, the look of pleasure on $his face is evident.<<else>>_S.Bodyguard.slaveName ties them and then gets out $his melee weapon and starts to abuse them; $his face is blank.<</if>>
 		<br>Shortly after, the terrified <<if $eliteLeft < $eliteTotal>>remaining<</if>> Elite are quickly brought away to be officially enslaved.
 	<</if>>
 <</switch>>
diff --git a/src/pregmod/killSlave.tw b/src/pregmod/killSlave.tw
index 79161249e9934f79eef68a671c11ea63e2040de5..9711d44f9beaf40c6edc4561cde107b93fa20503 100644
--- a/src/pregmod/killSlave.tw
+++ b/src/pregmod/killSlave.tw
@@ -86,8 +86,8 @@ you <<if canHear($activeSlave)>>quietly <</if>>reach behind your desk and pull o
 	<span id="killScene">
 	<br><<link "Kill $him">><<set $killChoice = 0>>
 	<<replace "#killScene">>
-		<<if $Bodyguard>>
-			<<setLocalPronouns $Bodyguard 2>>
+		<<if _S.Bodyguard>>
+			<<setLocalPronouns _S.Bodyguard 2>>
 		<</if>>
 
 		<<set _qualifiedFS = []>>
@@ -226,11 +226,11 @@ you <<if canHear($activeSlave)>>quietly <</if>>reach behind your desk and pull o
 		<</if>>
 		<<set $killChoice = -1>>
 	<</replace>><</link>>
-	<<if $activeSlave.assignment != "guard you" && $pit && $Bodyguard && $activeSlave.fuckdoll <= 0 && $activeSlave.fetish != "mindbroken">>
+	<<if $activeSlave.assignment != "guard you" && $pit && _S.Bodyguard && $activeSlave.fuckdoll <= 0 && $activeSlave.fetish != "mindbroken">>
 		<<if ($pitLethal == 1)>>
 			<br><<link "Let $him win $his life in combat">><<set $killChoice = 2>>
 			<<replace "#killScene">>
-				&nbsp;&nbsp;&nbsp;&nbsp;You tell $him that you'll let your bodyguard $Bodyguard.slaveName decide $his fate — if $he wants to live, $he'll have to beat _him2 in hand-to-hand combat in $pitName.
+				&nbsp;&nbsp;&nbsp;&nbsp;You tell $him that you'll let your bodyguard _S.Bodyguard.slaveName decide $his fate — if $he wants to live, $he'll have to beat _him2 in hand-to-hand combat in $pitName.
 				<<if $activeSlave.skill.combat == 0>>
 					The fear on $his face is palpable, though $he nods slowly and agrees, not seeing another choice.
 				<<else>>
@@ -250,5 +250,5 @@ you <<if canHear($activeSlave)>>quietly <</if>>reach behind your desk and pull o
 	<<else>>
 		You change your mind as you suddenly
 	<</if>>
-	realize $slaveFightingBG.slaveName is already fighting your bodyguard $Bodyguard.slaveName for $his life this week. [[Cancel the fight|KillSlave][$killChoice = -1]]
-<</if>>
\ No newline at end of file
+	realize $slaveFightingBG.slaveName is already fighting your bodyguard _S.Bodyguard.slaveName for $his life this week. [[Cancel the fight|KillSlave][$killChoice = -1]]
+<</if>>
diff --git a/src/pregmod/newChildIntro.tw b/src/pregmod/newChildIntro.tw
index 4606fa1f12d5626c7cf55941964bcc04d63308cf..c7aa40b21d10208250bfe81790345fedb679d673 100644
--- a/src/pregmod/newChildIntro.tw
+++ b/src/pregmod/newChildIntro.tw
@@ -144,7 +144,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 <</if>>
 <<if _tempMom != 0>>
 	<<setLocalPronouns _tempMom 2>>
-	<<if _tempMom.ID == $Concubine.ID>>
+	<<if _tempMom.ID == $ConcubineID>>
 		<br><<link "Permit your Concubine to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempMom $activeSlave>>
@@ -166,7 +166,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 				<span class='slave-name'>$activeSlave.slaveName</span>
 			<</replace>>
 		<</link>>
-	<<elseif _tempMom.ID == $Bodyguard.ID>>
+	<<elseif _tempMom.ID == $BodyguardID>>
 		<br><<link "Permit your bodyguard to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempMom $activeSlave>>
@@ -177,7 +177,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 				<span class='slave-name'>$activeSlave.slaveName</span>
 			<</replace>>
 		<</link>>
-	<<elseif _tempMom.ID == $HeadGirl.ID>>
+	<<elseif _tempMom.ID == $HeadGirlID>>
 		<br><<link "Permit your Head Girl to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempMom $activeSlave>>
@@ -203,7 +203,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 <</if>>
 <<if _tempDad != 0 && $activeSlave.father != $activeSlave.mother>>
 	<<setLocalPronouns _tempDad 2>>
-	<<if _tempDad.ID == $Concubine.ID>>
+	<<if _tempDad.ID == $ConcubineID>>
 		<br><<link "Permit your Concubine to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempDad $activeSlave>>
@@ -225,7 +225,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 				<span class='slave-name'>$activeSlave.slaveName</span>
 			<</replace>>
 		<</link>>
-	<<elseif _tempDad.ID == $Bodyguard.ID>>
+	<<elseif _tempDad.ID == $BodyguardID>>
 		<br><<link "Permit your bodyguard to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempDad $activeSlave>>
@@ -236,7 +236,7 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 				<span class='slave-name'>$activeSlave.slaveName</span>
 			<</replace>>
 		<</link>>
-	<<elseif _tempDad.ID == $HeadGirl.ID>>
+	<<elseif _tempDad.ID == $HeadGirlID>>
 		<br><<link "Permit your Head Girl to name _his2 $daughter">>
 			<<replace "#naming">>
 				<<ParentNames _tempDad $activeSlave>>
@@ -317,16 +317,16 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 			and @@.hotpink;notices how obedient@@ the look on _his2 face is. _tempMom.slaveName is eagerly awaiting your reaction to <<if $activeSlave.father == -1>>the result of your union<<else>>_his2 addition to your harem<</if>>.
 			<<set $activeSlave.trust += 25, $activeSlave.devotion += 25>>
 		<</if>>
-	<<elseif _tempMom.ID == $Concubine.ID>>
-		and notices @@.hotpink;how happy@@ the look on _his2 face is. $Concubine.slaveName is positively beaming with pride at _his2 addition to your harem.
+	<<elseif _tempMom.ID == $ConcubineID>>
+		and notices @@.hotpink;how happy@@ the look on _his2 face is. _S.Concubine.slaveName is positively beaming with pride at _his2 addition to your harem.
 		<<set $activeSlave.trust += 30>>
 		<<set $activeSlave.devotion += 30>>
-	<<elseif _tempMom.ID == $Bodyguard.ID>>
-		and @@.mediumaquamarine;notices how _he2 focuses more on you than _himself2.@@ $Bodyguard.slaveName's dedication to your life has @@.hotpink;certainly influenced $him.@@
+	<<elseif _tempMom.ID == $BodyguardID>>
+		and @@.mediumaquamarine;notices how _he2 focuses more on you than _himself2.@@ _S.Bodyguard.slaveName's dedication to your life has @@.hotpink;certainly influenced $him.@@
 		<<set $activeSlave.trust += 40>>
 		<<set $activeSlave.devotion += 20>>
-	<<elseif _tempMom.ID == $HeadGirl.ID>>
-		and notices how much _his2 look takes after your own. $HeadGirl.slaveName's dedication to you has @@.hotpink;certainly influenced $him.@@
+	<<elseif _tempMom.ID == $HeadGirlID>>
+		and notices how much _his2 look takes after your own. _S.HeadGirl.slaveName's dedication to you has @@.hotpink;certainly influenced $him.@@
 		<<set $activeSlave.trust += 20>>
 		<<set $activeSlave.devotion += 20>>
 	<<elseif _tempMom.devotion < -50>>
@@ -402,16 +402,16 @@ You completed the legalities before heading to $incubatorName, knowing the tank
 			and @@.hotpink;notices how obedient@@ the look on _his2 face is. _tempDad.slaveName is eagerly awaiting your reaction to <<if $activeSlave.mother == -1>>the result of your union<<else>>_his2 addition to your harem<</if>>.
 			<<set $activeSlave.trust += 25, $activeSlave.devotion += 25>>
 		<</if>>
-	<<elseif _tempDad.ID == $Concubine.ID>>
-		and notices @@.hotpink;how happy@@ the look on _his2 face is. $Concubine.slaveName is positively beaming with pride at _his2 addition to your harem.
+	<<elseif _tempDad.ID == $ConcubineID>>
+		and notices @@.hotpink;how happy@@ the look on _his2 face is. _S.Concubine.slaveName is positively beaming with pride at _his2 addition to your harem.
 		<<set $activeSlave.trust += 30>>
 		<<set $activeSlave.devotion += 30>>
-	<<elseif _tempDad.ID == $Bodyguard.ID>>
-		and @@.mediumaquamarine;notices how _he2 focuses more on you than _himself2.@@ $Bodyguard.slaveName's dedication to your life has @@.hotpink;certainly influenced $him.@@
+	<<elseif _tempDad.ID == $BodyguardID>>
+		and @@.mediumaquamarine;notices how _he2 focuses more on you than _himself2.@@ _S.Bodyguard.slaveName's dedication to your life has @@.hotpink;certainly influenced $him.@@
 		<<set $activeSlave.trust += 40>>
 		<<set $activeSlave.devotion += 20>>
-	<<elseif _tempDad.ID == $HeadGirl.ID>>
-		and notices how much _his2 look takes after your own. $HeadGirl.slaveName's dedication to you has @@.hotpink;certainly influenced $him.@@
+	<<elseif _tempDad.ID == $HeadGirlID>>
+		and notices how much _his2 look takes after your own. _S.HeadGirl.slaveName's dedication to you has @@.hotpink;certainly influenced $him.@@
 		<<set $activeSlave.trust += 20>>
 		<<set $activeSlave.devotion += 20>>
 	<<elseif _tempDad.devotion < -50>>
@@ -1083,7 +1083,7 @@ You slowly strip down, gauging $his reactions to your show, until you are fully
 				<<set $activeSlave.counter.anal += 10, $analTotal += 10>>
 				<<= assignJob($activeSlave, "dairy")>>
 				<<replace "#result">>
-					You order <<if $HeadGirl == 0>>another slave<<else>>$HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $dairyName. The new slave does not know what $dairyName is, not really, and $he doesn't know what being set up there means, either. If $he knew that you are able to send $him there so blithely only because it is equipped with a special preparatory raper that will seize $him, <<if $activeSlave.vagina > -1>>mercilessly fuck $his pussy and ass until both are gaped<<else>>ream $his anus until it's cavernously gaped<</if>>, and then consign $him to constant fucking by gargantuan machine phalli, $he might resist. But $he doesn't, so $he does not.
+					You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $dairyName. The new slave does not know what $dairyName is, not really, and $he doesn't know what being set up there means, either. If $he knew that you are able to send $him there so blithely only because it is equipped with a special preparatory raper that will seize $him, <<if $activeSlave.vagina > -1>>mercilessly fuck $his pussy and ass until both are gaped<<else>>ream $his anus until it's cavernously gaped<</if>>, and then consign $him to constant fucking by gargantuan machine phalli, $he might resist. But $he doesn't, so $he does not.
 				<</replace>>
 				<</link>>
 			<</if>>
@@ -1128,7 +1128,7 @@ You slowly strip down, gauging $his reactions to your show, until you are fully
 		<<= assignJob($activeSlave, "be confined in the arcade")>>
 		<<set $activeSlave.choosesOwnAssignment = 0>>
 		<<replace "#result">>
-			You order <<if $HeadGirl == 0>>another slave<<else>>$HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $arcadeName. The new slave does not know what $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 tube $he was grown in. 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 $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<<if $activeSlave.vagina > -1>>, parts $his pussylips,<</if>> or presses against $his poor anus.
+			You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $arcadeName. The new slave does not know what $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 tube $he was grown in. 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 $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<<if $activeSlave.vagina > -1>>, parts $his pussylips,<</if>> or presses against $his poor anus.
 			<<if ($arcade <= App.Entity.facilities.arcade.employeesIDs().size)>>Mere <<if $showInches == 2>>yards<<else>>meters<</if>> away, preparations to convert the least appealing Arcade slave into a Fuckdoll begin. As $activeSlave.slaveName is broken in by $his first customers, $he's blissfully unaware that $he's $arcade new slaves away from the same fate.<</if>>
 		<</replace>>
 		<</link>>
diff --git a/src/pregmod/reMaleCitizenHookup.tw b/src/pregmod/reMaleCitizenHookup.tw
index 1e98d3871f96f0e16cae5ca729a9abae2c57ce9e..9e8cef1467eb8ae3bf8c37b9d8cc6a4d2b1b7eb1 100644
--- a/src/pregmod/reMaleCitizenHookup.tw
+++ b/src/pregmod/reMaleCitizenHookup.tw
@@ -222,9 +222,9 @@ He's clearly attracted to you; even the most consummate actor would have difficu
 		You pull his arm around your waist.
 	<</if>>
 	You hear a slight gasp from him as he realizes that his gambit has succeeded with more immediate effect than he expected. He shivers with anticipation as you steer him back through a side door, grabbing a pair of glasses of <<if $PC.refreshmentType == 1>>$PC.refreshment<<else>>liquor<</if>> on the way, and making a discreet exit towards your private suite.
-	<<if $Concubine != 0 && $Concubine.intelligence+$Concubine.intelligenceImplant > 50>>
-		<<setLocalPronouns $Concubine>>
-		$Concubine.slaveName is there, of course, and $he instantly <<if canSee($Concubine)>>sees<<else>>realizes<</if>> that $his continued presence for a ménage à trois is wanted by both you and your guest.
+	<<if _S.Concubine && _S.Concubine.intelligence+_S.Concubine.intelligenceImplant > 50>>
+		<<setLocalPronouns _S.Concubine>>
+		_S.Concubine.slaveName is there, of course, and $he instantly <<if canSee(_S.Concubine)>>sees<<else>>realizes<</if>> that $his continued presence for a ménage à trois is wanted by both you and your guest.
 	<</if>>
 	Your guest restrains his eager praise now that you're in private, but his wide-eyed appreciation of your domain is compliment enough. Once in your suite, you undress him, revealing
 	<<switch _FS>>
@@ -327,13 +327,13 @@ He's clearly attracted to you; even the most consummate actor would have difficu
 		a hot young body, and gently push him back onto your bed. You tease him as you remove your evening dress, crawl over him and impale yourself on his eager shaft, fully taking its length, before beginning to ride him. Even a female arcology owner like yourself appreciates a good hard fuck, since regular submission to a pounding from sex slaves would be a scandal. There's little opprobrium waiting for you if it's known he had you, though, and he is eagerly thrusting into your pussy. You shift into a slightly more comfortable position<<if $PC.belly >= 5000>> (one that forces him to bear the weight of your heavy middle) <</if>>and ride him to orgasm. You follow shortly after, feeling the heat of his seed in the depths of your pussy as it clamps down around his dick. Thankfully, he isn't spent yet and begins anew, quickly carrying your climax to a second orgasm and drawing an adorable moan out of you.
 	<</switch>>
 	<<if _FS == "Asset Expansionist">>
-		<<if $Concubine != 0 && !isAmputee($Concubine)>>
-			$Concubine.slaveName eagerly joins the two of you to help clean the massive cumshot up.
+		<<if _S.Concubine && !isAmputee(_S.Concubine)>>
+			_S.Concubine.slaveName eagerly joins the two of you to help clean the massive cumshot up.
 		<</if>>
 	<<elseif _FS != "Youth Preferentialist">>
-		<<if $Concubine != 0 && !isAmputee($Concubine)>>
-			<<setLocalPronouns $Concubine>>
-			The <<if canSee($Concubine)>>view of your bouncing tits<<elseif canHear($activeSlave)>>sound of lusty sex<<else>>vibrations from your bed bouncing up and down<</if>> is too much for $Concubine.slaveName to resist and so $he crawls over to kiss and caress you as your lover finishes.
+		<<if _S.Concubine && !isAmputee(_S.Concubine)>>
+			<<setLocalPronouns _S.Concubine>>
+			The <<if canSee(_S.Concubine)>>view of your bouncing tits<<elseif canHear($activeSlave)>>sound of lusty sex<<else>>vibrations from your bed bouncing up and down<</if>> is too much for _S.Concubine.slaveName to resist and so $he crawls over to kiss and caress you as your lover finishes.
 		<</if>>
 	<</if>>
 	Sometimes society overlooks that you are a woman and have certain needs, but your lover tonight knows exactly how to treat you. When your guest <<if _FS != "Youth Preferentialist">>is finally spent<<else>>wakes up from against your body<</if>>, he showers, dresses, and leaves discreetly, offering you a proper thank you. This is the kind of thing that @@.green;builds a lasting reputation@@ in the Free Cities.
diff --git a/src/pregmod/sePlayerBirth.tw b/src/pregmod/sePlayerBirth.tw
index 73a46245798295dc62fdf107a4c65fc983042a47..1d01c2d3f5acab66cce3dace6548ef7b78a8fec3 100644
--- a/src/pregmod/sePlayerBirth.tw
+++ b/src/pregmod/sePlayerBirth.tw
@@ -106,46 +106,46 @@ While sitting at your desk planning your day, @@.lime;your water breaks,@@ thoro
 <</if>>
 <br><br>
 <<if $PC.belly < 100000>>
-	<<if $Concubine != 0>><<set _concubinePresent = 1>><<setLocalPronouns $Concubine 2>><</if>>
+	<<if _S.Concubine>><<set _concubinePresent = 1>><<setLocalPronouns _S.Concubine 2>><</if>>
 	After what feels like a <<if $showInches == 2>>mile<<else>>kilometer<</if>> run, you finally waddle into your destination finding
 	<<if _S.Nurse>>
 		<<setLocalPronouns _S.Nurse>>
-		@@.pink;_S.Nurse.slaveName@@ waiting for you with everything $he needs to be a capable midwife to you.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ <<if hasAnyArms($Concubine)>>wraps _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
-	<<elseif $Bodyguard != 0>>
-		<<setLocalPronouns $Bodyguard>>
-		@@.pink;$Bodyguard.slaveName@@ waiting for you. Ever since $he realized $he was protecting for <<if $PC.pregType > 1>>many<<else>>two<</if>>, $he made sure to hone $his skills as a midwife to make sure $he could protect you and your unborn child<<if $PC.pregType > 1>>ren<</if>> from any dangers.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ <<if hasAnyArms($Concubine)>>wraps _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
-	<<elseif $HeadGirl != 0>>
-		<<setLocalPronouns $HeadGirl>>
-		@@.pink;$HeadGirl.slaveName@@ is waiting for you. $He promises to do everything $he can to help you.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ <<if hasAnyArms($Concubine)>>wraps _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
+		@@.pink;_S.Nurse.slaveName@@ waiting for you with everything $he needs to be a capable midwife to you.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ <<if hasAnyArms(_S.Concubine)>>wraps _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
+	<<elseif $BodyguardID != 0>>
+		<<setLocalPronouns _S.Bodyguard>>
+		@@.pink;_S.Bodyguard.slaveName@@ waiting for you. Ever since $he realized $he was protecting for <<if $PC.pregType > 1>>many<<else>>two<</if>>, $he made sure to hone $his skills as a midwife to make sure $he could protect you and your unborn child<<if $PC.pregType > 1>>ren<</if>> from any dangers.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ <<if hasAnyArms(_S.Concubine)>>wraps _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
+	<<elseif $HeadGirlID != 0>>
+		<<setLocalPronouns _S.HeadGirl>>
+		@@.pink;_S.HeadGirl.slaveName@@ is waiting for you. $He promises to do everything $he can to help you.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ <<if hasAnyArms(_S.Concubine)>>wraps _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>leans into<</if>> you from behind, letting you know _he2'll be here to comfort you while you labor.<</if>>
 	<<elseif _concubinePresent == 1>>
-		@@.pink;$Concubine.slaveName@@ is waiting for you. _He2 believes, if anything, that _he2 could at least comfort you while you give birth.
+		@@.pink;_S.Concubine.slaveName@@ is waiting for you. _He2 believes, if anything, that _he2 could at least comfort you while you give birth.
 	<<else>>
 		it quite empty. At least $assistant.name knows where you are should anything go wrong.
 	<</if>>
 <<else>>
-	<<if $Concubine != 0>><<setLocalPronouns $Concubine 2>><<if !canWalk($Concubine)>><<set _concubinePresent = 2>><<else>><<set _concubinePresent = 1>><</if>><</if>>
+	<<if _S.Concubine>><<setLocalPronouns _S.Concubine 2>><<if !canWalk(_S.Concubine)>><<set _concubinePresent = 2>><<else>><<set _concubinePresent = 1>><</if>><</if>>
 	You barely make it half-way down the hall before you feel the first of your many children drop into position at the entrance to your birth canal. You try to keep going, but as it forces its way through your pelvis, spreading it wider, you're forced to the ground. Fortunately the penthouse is littered with supply rooms and closets, so you drag your laboring body into the nearest one instead. Fortunately, $assistant.name discretely directs
 	<<if _S.Nurse>>
 		<<setLocalPronouns _S.Nurse>>
-		@@.pink;_S.Nurse.slaveName@@ to you with everything $he'll need to deliver your child<<if $PC.pregType > 1>>ren<</if>>.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms($Concubine)>>wrapping _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
-	<<elseif $Bodyguard != 0>>
-		<<setLocalPronouns $Bodyguard>>
-		@@.pink;$Bodyguard.slaveName@@ to your location. Ever since $he realized $he was protecting for not only you, but the lives you bear too, $he made sure to hone $his skills as a midwife to make sure $he could protect you and your unborn child<<if $PC.pregType > 1>>ren<</if>> from any dangers. $He apologizes profusely for not being there when you needed $him most; $he wanted to make sure everything was ready for your arrival.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms($Concubine)>>wrapping _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
-	<<elseif $HeadGirl != 0>>
-		<<setLocalPronouns $HeadGirl>>
-		@@.pink;$HeadGirl.slaveName@@ to your location. $He promises to do everything $he can to help you.<<if _concubinePresent == 1>> @@.pink;$Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms($Concubine)>>wrapping _his2 arm<<if hasBothArms($Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
+		@@.pink;_S.Nurse.slaveName@@ to you with everything $he'll need to deliver your child<<if $PC.pregType > 1>>ren<</if>>.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms(_S.Concubine)>>wrapping _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
+	<<elseif $BodyguardID != 0>>
+		<<setLocalPronouns _S.Bodyguard>>
+		@@.pink;_S.Bodyguard.slaveName@@ to your location. Ever since $he realized $he was protecting for not only you, but the lives you bear too, $he made sure to hone $his skills as a midwife to make sure $he could protect you and your unborn child<<if $PC.pregType > 1>>ren<</if>> from any dangers. $He apologizes profusely for not being there when you needed $him most; $he wanted to make sure everything was ready for your arrival.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms(_S.Concubine)>>wrapping _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
+	<<elseif $HeadGirlID != 0>>
+		<<setLocalPronouns _S.HeadGirl>>
+		@@.pink;_S.HeadGirl.slaveName@@ to your location. $He promises to do everything $he can to help you.<<if _concubinePresent == 1>> @@.pink;_S.Concubine.slaveName@@ bursts in after $him and circles around behind you before <<if hasAnyArms(_S.Concubine)>>wrapping _his2 arm<<if hasBothArms(_S.Concubine)>>s<</if>> around<<else>>nuzzling against<</if>> your contraction wracked middle. _He2'll be here to comfort you while you labor.<</if>>
 	<<elseif _concubinePresent == 1>>
-		@@.pink;$Concubine.slaveName@@ to your location. _He2 believes, if anything, that _he2 could at least comfort you while you give birth.
+		@@.pink;_S.Concubine.slaveName@@ to your location. _He2 believes, if anything, that _he2 could at least comfort you while you give birth.
 	<<else>>
 		a camera on the room you crawled into. At least $assistant.name knows where you are should anything go wrong.
 	<</if>>
 <</if>>
 <br><br>
-You arrange yourself to give birth, relaxing until your body urges you to begin bearing down and pushing your child into the world.<<if _concubinePresent == 1>>$Concubine.slaveName settles in beside you, one hand soothing your contraction wracked middle and the other <<if $PC.dick != 0>>stroking your hardening dick<<else>>teasing your stiffening clit<</if>>.<</if>>
+You arrange yourself to give birth, relaxing until your body urges you to begin bearing down and pushing your child into the world.<<if _concubinePresent == 1>>_S.Concubine.slaveName settles in beside you, one hand soothing your contraction wracked middle and the other <<if $PC.dick != 0>>stroking your hardening dick<<else>>teasing your stiffening clit<</if>>.<</if>>
 <<if _badBirth > random(1,100)>> /* shits going wrong */
 	<<if _S.Nurse>>
 		<<setLocalPronouns _S.Nurse>>
-		You keep pushing and pushing, but your child is not coming out.<<if _concubinePresent == 1>> $Concubine.slaveName shifts to massaging your gravid middle<<if canTalk($Concubine)>>, while whispering words of encouragement into your ear<</if>>. Until _S.Nurse.slaveName shoos _him2 away so $he can take over and get this baby out of you.<</if>> _S.Nurse.slaveName was prepared for this possibility, adjusts your position and timings, and before long is holding your @@.lime;new baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ in $his arms.
+		You keep pushing and pushing, but your child is not coming out.<<if _concubinePresent == 1>> _S.Concubine.slaveName shifts to massaging your gravid middle<<if canTalk(_S.Concubine)>>, while whispering words of encouragement into your ear<</if>>. Until _S.Nurse.slaveName shoos _him2 away so $he can take over and get this baby out of you.<</if>> _S.Nurse.slaveName was prepared for this possibility, adjusts your position and timings, and before long is holding your @@.lime;new baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ in $his arms.
 		<<if $PC.pregType >= 8>>
 			But you're far from done; your taut dome of a belly still houses another <<print num($PC.pregType - 1)>> infants. You moan as the next child begins its descent; you'll be at this for awhile. If _S.Nurse.slaveName weren't here, you and your children would likely have perished.
 		<<elseif $PC.pregType >= 4>>
@@ -155,11 +155,11 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 		<<elseif $PC.pregType == 2>>
 			But you aren't done; your rounded belly still houses another infant. You moan as they begin their decent; if _S.Nurse.slaveName weren't here, you and your children would likely have perished.
 		<</if>>
-	<<elseif $Bodyguard != 0>>
-		<<setLocalPronouns $Bodyguard>>
-		You keep pushing and pushing, but your child is not coming out.<<if _concubinePresent == 1>> $Concubine.slaveName shifts to massaging your gravid middle<<if canTalk($Concubine)>>, while whispering words of encouragement into your ear<</if>>. _He2 begins to worry as _his2 lover weakens in front of _him2. $Bodyguard.slaveName quickly pulls _him2 away from you, fearing the worst.<</if>> $Bodyguard.slaveName read about this possibility and tries everything $he can to coax your child out. As time passes, $he notices your consciousness begin to fade as exhaustion kicks in. Fearing for your lives, and desperate to save you, $he draws $his sword from its sheath, hands unsteady from what $he is about to do. Carefully, $he slits your lower abdomen, allowing your baby-<<if $PC.pregType < 3>>filled<<elseif $PC.pregType < 5>>stuffed<<elseif $PC.pregType < 8>>packed<<else>>bursting<</if>> womb to pop out through the incision. Doing $his best, $he cuts open your uterus, pulls your child<<if $PC.pregType > 1>>ren<</if>> from you and severs the umbilical cord<<if $PC.pregType > 1>>s all at once<</if>>.
+	<<elseif $BodyguardID != 0>>
+		<<setLocalPronouns _S.Bodyguard>>
+		You keep pushing and pushing, but your child is not coming out.<<if _concubinePresent == 1>> _S.Concubine.slaveName shifts to massaging your gravid middle<<if canTalk(_S.Concubine)>>, while whispering words of encouragement into your ear<</if>>. _He2 begins to worry as _his2 lover weakens in front of _him2. _S.Bodyguard.slaveName quickly pulls _him2 away from you, fearing the worst.<</if>> _S.Bodyguard.slaveName read about this possibility and tries everything $he can to coax your child out. As time passes, $he notices your consciousness begin to fade as exhaustion kicks in. Fearing for your lives, and desperate to save you, $he draws $his sword from its sheath, hands unsteady from what $he is about to do. Carefully, $he slits your lower abdomen, allowing your baby-<<if $PC.pregType < 3>>filled<<elseif $PC.pregType < 5>>stuffed<<elseif $PC.pregType < 8>>packed<<else>>bursting<</if>> womb to pop out through the incision. Doing $his best, $he cuts open your uterus, pulls your child<<if $PC.pregType > 1>>ren<</if>> from you and severs the umbilical cord<<if $PC.pregType > 1>>s all at once<</if>>.
 		<br><br>
-		You awake some time later in the remote surgery, your stomach extremely sore; you quickly realize you're no longer round with child. As you try to rise, $Bodyguard.slaveName stops you; $he hefts you into a bridal carry and takes you to a recovery room, before gently placing you into a warm bed, tucking you in, and hurrying out of the room. Before you can call out, $he returns carrying
+		You awake some time later in the remote surgery, your stomach extremely sore; you quickly realize you're no longer round with child. As you try to rise, _S.Bodyguard.slaveName stops you; $he hefts you into a bridal carry and takes you to a recovery room, before gently placing you into a warm bed, tucking you in, and hurrying out of the room. Before you can call out, $he returns carrying
 		<<if _curBabies == 1>>
 			@@.lime;your baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@
 		<<elseif _curBabies < 6>>
@@ -169,46 +169,46 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 		<</if>>
 		in $his arms.
 		<<set _wounded = 1>>
-	<<elseif $HeadGirl != 0>>
-		<<setLocalPronouns $HeadGirl>>
+	<<elseif $HeadGirlID != 0>>
+		<<setLocalPronouns _S.HeadGirl>>
 		You keep pushing and pushing, but your child is not coming out.
 		<<if _concubinePresent == 1>>
-			$Concubine.slaveName shifts to massaging your gravid middle<<if canTalk($Concubine)>>, while whispering words of encouragement into your ear<</if>>. _He2 begins to worry as _his2 lover weakens in front of _him2. $HeadGirl.slaveName quickly pulls _him2 to <<if hasBothLegs($Concubine)>>_his2 feet<<else>>upright<</if>> and orders _him2 to help $him carry you to the remote surgery<<if !canWalk($Concubine)>>; in $his rush, $he completely forgot <<if tooBigBreasts($Concubine)>>$Concubine.slaveName's breasts have _him2 pinned to the bed<<elseif tooBigBelly($Concubine)>>$Concubine.slaveName's pregnancy renders _him2 immobile<<elseif tooBigDick($Concubine)>>$Concubine.slaveName can't walk with a dick that big<<elseif tooBigButt($Concubine)>>$Concubine.slaveName's butt pins _him2 to the bed<<elseif tooBigBalls($Concubine)>>$Concubine.slaveName's balls act as a giant anchor<<elseif tooFatSlave($Concubine)>>$Concubine.slaveName is so fat, the bed is _his2 home now<<else>>$Concubine.slaveName can't walk<</if>><</if>>.
+			_S.Concubine.slaveName shifts to massaging your gravid middle<<if canTalk(_S.Concubine)>>, while whispering words of encouragement into your ear<</if>>. _He2 begins to worry as _his2 lover weakens in front of _him2. _S.HeadGirl.slaveName quickly pulls _him2 to <<if hasBothLegs(_S.Concubine)>>_his2 feet<<else>>upright<</if>> and orders _him2 to help $him carry you to the remote surgery<<if !canWalk(_S.Concubine)>>; in $his rush, $he completely forgot <<if tooBigBreasts(_S.Concubine)>>_S.Concubine.slaveName's breasts have _him2 pinned to the bed<<elseif tooBigBelly(_S.Concubine)>>_S.Concubine.slaveName's pregnancy renders _him2 immobile<<elseif tooBigDick(_S.Concubine)>>_S.Concubine.slaveName can't walk with a dick that big<<elseif tooBigButt(_S.Concubine)>>_S.Concubine.slaveName's butt pins _him2 to the bed<<elseif tooBigBalls(_S.Concubine)>>_S.Concubine.slaveName's balls act as a giant anchor<<elseif tooFatSlave(_S.Concubine)>>_S.Concubine.slaveName is so fat, the bed is _his2 home now<<else>>_S.Concubine.slaveName can't walk<</if>><</if>>.
 		<<else>>
-			$HeadGirl.slaveName notices your distress and carries you to the remote surgery<<if $PC.pregType >= 8>>, a daunting task given your extreme gravidity<</if>>.
+			_S.HeadGirl.slaveName notices your distress and carries you to the remote surgery<<if $PC.pregType >= 8>>, a daunting task given your extreme gravidity<</if>>.
 		<</if>>
 		<br><br>
-		You awake some time later in a recovery room<<if _concubinePresent > 0>>, $Concubine.slaveName beside you<</if>>, your stomach extremely sore; a quick glance at the prominent scar tells you everything you need to know. Seeing you're awake, $HeadGirl.slaveName catches your attention. In $his arms
+		You awake some time later in a recovery room<<if _concubinePresent > 0>>, _S.Concubine.slaveName beside you<</if>>, your stomach extremely sore; a quick glance at the prominent scar tells you everything you need to know. Seeing you're awake, _S.HeadGirl.slaveName catches your attention. In $his arms
 		<<if _curBabies == 1>>
-			is @@.lime;your baby <<if _gender == "XX">>girl<<else>>boy<</if>>,@@ <<if $HeadGirl.lactation > 0>>happily nursing from $his breast,<</if>>
+			is @@.lime;your baby <<if _gender == "XX">>girl<<else>>boy<</if>>,@@ <<if _S.HeadGirl.lactation > 0>>happily nursing from $his breast,<</if>>
 		<<else>>
-			are @@.lime;your newborn <<print pregNumberName(_curBabies, 2)>>,@@ <<if $HeadGirl.lactation > 0>>happily nursing from $his breasts,<</if>>
+			are @@.lime;your newborn <<print pregNumberName(_curBabies, 2)>>,@@ <<if _S.HeadGirl.lactation > 0>>happily nursing from $his breasts,<</if>>
 		<</if>>
 		alive and well.
 		<<set _wounded = 1>>
 	<<elseif _concubinePresent == 1>>
-		You keep pushing and pushing, but your child is not coming out. <<if canWalk($Concubine)>>In a panic, $Concubine.slaveName carries you to the remote surgery<<else>>The last thing you remember as you fade is $Concubine.slaveName's look of panic<</if>>.
+		You keep pushing and pushing, but your child is not coming out. <<if canWalk(_S.Concubine)>>In a panic, _S.Concubine.slaveName carries you to the remote surgery<<else>>The last thing you remember as you fade is _S.Concubine.slaveName's look of panic<</if>>.
 		<br><br>
-		You awake some time later in a recovery room, your stomach extremely sore; a quick glance at the prominent scar tells you everything you need to know. A content sigh comes from beside you; $Concubine.slaveName is snuggled next to you, snoozing with
+		You awake some time later in a recovery room, your stomach extremely sore; a quick glance at the prominent scar tells you everything you need to know. A content sigh comes from beside you; _S.Concubine.slaveName is snuggled next to you, snoozing with
 		<<if _curBabies == 1>>
-			@@.lime;your baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ in _his2 arms.<<if $Concubine.lactation > 0>> Your child has managed to free one of $Concubine.slaveName's breasts and is eagerly suckling from its milky nipple.<</if>>
+			@@.lime;your baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ in _his2 arms.<<if _S.Concubine.lactation > 0>> Your child has managed to free one of _S.Concubine.slaveName's breasts and is eagerly suckling from its milky nipple.<</if>>
 		<<else>>
-			@@.lime;your newborn <<print pregNumberName(_curBabies, 2)>>@@ in _his2 arms.<<if $Concubine.lactation > 0>> Your children have managed to free $Concubine.slaveName's breasts and are eagerly suckling from their milky nipples.<</if>>
+			@@.lime;your newborn <<print pregNumberName(_curBabies, 2)>>@@ in _his2 arms.<<if _S.Concubine.lactation > 0>> Your children have managed to free _S.Concubine.slaveName's breasts and are eagerly suckling from their milky nipples.<</if>>
 		<</if>>
-		<<if !canWalk($Concubine)>>
-			<<if tooBigBreasts($Concubine)>>
+		<<if !canWalk(_S.Concubine)>>
+			<<if tooBigBreasts(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _his2 breasts prevent _him2 from walking<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give the pair a gentle caress as thanks.
-			<<elseif tooBigBelly($Concubine)>>
+			<<elseif tooBigBelly(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _he2's so gravid _he2 can't walk<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give the taut dome a gentle caress as thanks.
-			<<elseif tooBigDick($Concubine)>>
+			<<elseif tooBigDick(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _his2 dick prevents _him2 from walking<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give the serpent a gentle caress as thanks.
-			<<elseif tooBigButt($Concubine)>>
+			<<elseif tooBigButt(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _his2 ass prevent _him2 from walking<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give the jiggling giant a gentle caress as thanks.
-			<<elseif tooBigBalls($Concubine)>>
+			<<elseif tooBigBalls(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _his2 balls prevent _him2 from walking<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give the pair a gentle caress as thanks. _He2 returns the kindness with a shudder and a large wet spot forming over _his2 crotch.
-			<<elseif tooFatSlave($Concubine)>>
+			<<elseif tooFatSlave(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _he2's so fat _he2 can't walk<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give _his2 soft body a gentle caress as thanks.
-			<<elseif !hasAnyLegs($Concubine)>>
+			<<elseif !hasAnyLegs(_S.Concubine)>>
 				You don't know how _he2 managed to get you here when _he2 has no legs to walk on<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you give _his2 stumps a gentle caress as thanks.
 			<<else>>
 				You don't know how _he2 managed to get you here, since _he2 can't walk<<if $PC.pregType >= 8>>, especially with how heavy your pregnancy was<</if>>, but you're thankful either way.
@@ -225,10 +225,10 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 		<<setLocalPronouns _S.Nurse>>
 		Under _S.Nurse.slaveName's guidance, childbirth is a breeze for you.
 		<<if $PC.pregType == 1>>
-			<<if _concubinePresent == 1>> Or it would have been, had $Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, _S.Nurse.slaveName, and your newborn getting sprayed with cum.<</if>><</if>> _S.Nurse.slaveName cuts the cord, swaddles your child, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
+			<<if _concubinePresent == 1>> Or it would have been, had _S.Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.Nurse.slaveName, and your newborn getting sprayed with cum.<</if>><</if>> _S.Nurse.slaveName cuts the cord, swaddles your child, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
 		<<else>>
 			<<if _concubinePresent == 1>>
-				Or it would have been, had $Concubine.slaveName not driven you to an intense orgasm right as your first child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, _S.Nurse.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over: before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious. $Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
+				Or it would have been, had _S.Concubine.slaveName not driven you to an intense orgasm right as your first child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.Nurse.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over: before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious. _S.Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
 			<<else>>
 				With one out, you realize
 				<<if $PC.pregType >= 6>>
@@ -252,17 +252,17 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 			<</if>>
 			_S.Nurse.slaveName cuts the cords, swaddles your children, and hands you @@.lime;your new <<print pregNumberName(_curBabies, 2)>>.@@
 		<</if>>
-	<<elseif $Bodyguard != 0>>
-		<<setLocalPronouns $Bodyguard>>
-		With $Bodyguard.slaveName watching over you, you feel safe enough to let your guard down and focus on giving birth. Once you are relaxed, you feel your child begin to inch down your birth canal. Before long you've completed the job without any trouble.
+	<<elseif $BodyguardID != 0>>
+		<<setLocalPronouns _S.Bodyguard>>
+		With _S.Bodyguard.slaveName watching over you, you feel safe enough to let your guard down and focus on giving birth. Once you are relaxed, you feel your child begin to inch down your birth canal. Before long you've completed the job without any trouble.
 		<<if $PC.pregType == 1>>
 			<<if _concubinePresent == 1>>
-				Or it would have, had $Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, $Bodyguard.slaveName, and your newborn getting sprayed with cum.<</if>>
+				Or it would have, had _S.Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.Bodyguard.slaveName, and your newborn getting sprayed with cum.<</if>>
 			<</if>>
-			$Bodyguard.slaveName cuts the cord with $his blade, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
+			_S.Bodyguard.slaveName cuts the cord with $his blade, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
 		<<else>>
 			<<if _concubinePresent == 1>>
-				Or you would have, had $Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, $Bodyguard.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over; before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious, nearly panicking $Bodyguard.slaveName. $Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
+				Or you would have, had _S.Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.Bodyguard.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over; before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious, nearly panicking _S.Bodyguard.slaveName. _S.Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
 			<<else>>
 				With one out, you realize
 				<<if $PC.pregType >= 6>>
@@ -282,17 +282,17 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 				<<else>>
 					they begin their
 				<</if>>
-				descent; you'll be at this for awhile. With $Bodyguard.slaveName around, you feel completely safe.
+				descent; you'll be at this for awhile. With _S.Bodyguard.slaveName around, you feel completely safe.
 			<</if>>
-			$Bodyguard.slaveName cuts the cords with $his blade, and hands you @@.lime;your new <<print pregNumberName(_curBabies, 2)>>.@@
+			_S.Bodyguard.slaveName cuts the cords with $his blade, and hands you @@.lime;your new <<print pregNumberName(_curBabies, 2)>>.@@
 		<</if>>
-	<<elseif $HeadGirl != 0>>
-		With $HeadGirl.slaveName waiting with everything you need, childbirth goes by without a hitch.
+	<<elseif $HeadGirlID != 0>>
+		With _S.HeadGirl.slaveName waiting with everything you need, childbirth goes by without a hitch.
 		<<if $PC.pregType == 1>>
-			<<if _concubinePresent == 1>> Or it would have been, had $Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, $HeadGirl.slaveName, and your newborn getting sprayed with cum.<</if>><</if>> $HeadGirl.slaveName cuts the cord, swaddles your child, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
+			<<if _concubinePresent == 1>> Or it would have been, had _S.Concubine.slaveName not driven you to an intense orgasm right as your child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.HeadGirl.slaveName, and your newborn getting sprayed with cum.<</if>><</if>> _S.HeadGirl.slaveName cuts the cord, swaddles your child, and hands you @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>.@@
 		<<else>>
 			<<if _concubinePresent == 1>>
-				Or it would have been, had $Concubine.slaveName not driven you to an intense orgasm right as your first child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in $Concubine.slaveName, $HeadGirl.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over: before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious. $Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
+				Or it would have been, had _S.Concubine.slaveName not driven you to an intense orgasm right as your first child entered the world.<<if $PC.balls >= 9>> An orgasm that resulted in _S.Concubine.slaveName, _S.HeadGirl.slaveName, and your newborn getting sprayed with cum.<</if>> But it isn't over: before you've even had a chance to come down from your climax, the next infant slips into your birth canal<<if $PC.pregType >= 3>>, followed closely by the <<if $PC.pregType == 3>>last<<else>>next<</if>>,<</if>> and immediately pushes you back over the edge. In minutes, after <<print num(_curBabies)>> children and <<print num(_curBabies)>> intense orgasms, you're barely conscious. _S.Concubine.slaveName slides in behind you to snuggle with you as you return to your senses.
 			<<else>>
 				With one out, you realize
 				<<if $PC.pregType >= 6>>
@@ -312,12 +312,12 @@ You arrange yourself to give birth, relaxing until your body urges you to begin
 				<<else>>
 					they begin their
 				<</if>>
-				descent; you'll be at this for awhile. With $HeadGirl.slaveName around, you know everything is under control.
+				descent; you'll be at this for awhile. With _S.HeadGirl.slaveName around, you know everything is under control.
 			<</if>>
-			$HeadGirl.slaveName cuts the cords, swaddles your children, and hands you @@.lime;your new <<print pregNumberName(_curBabies, 2)>>.@@
+			_S.HeadGirl.slaveName cuts the cords, swaddles your children, and hands you @@.lime;your new <<print pregNumberName(_curBabies, 2)>>.@@
 		<</if>>
 	<<elseif _concubinePresent == 1>>
-		$Concubine.slaveName alternates between calming your nerves and driving your to orgasm. It works fairly well, as your child rapidly enters the world alongside a particularly powerful climax. You reach down and draw @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ into your arms, while $Concubine.slaveName shifts to eagerly "clean" your crotch with _his2 tongue.<<if $PC.pregType > 1>> _His2 over-stimulation of you quickly has _him2 licking the crowning head of your second child. _He2 diligently works you over until all of your children are born, making sure you are thoroughly exhausted; both from the birth and from _his2 ministrations.<<if canPenetrate($Concubine) && canImpreg($PC, $Concubine)>> $Concubine.slaveName <<if canSee($Concubine)>>eyes<<else>>faces<</if>> your spread pussy hungrily as _his2 erection bobs with anticipation. But you're too tired right now and _he2 realizes it.<</if>> _He2 helps gather your child<<if $PC.pregType > 1>>ren<</if>> to<<if $Concubine.lactation > 0>> _his2 and <</if>> your breasts with the hope that you'll reward _him2 when you recover.<</if>>
+		_S.Concubine.slaveName alternates between calming your nerves and driving your to orgasm. It works fairly well, as your child rapidly enters the world alongside a particularly powerful climax. You reach down and draw @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ into your arms, while _S.Concubine.slaveName shifts to eagerly "clean" your crotch with _his2 tongue.<<if $PC.pregType > 1>> _His2 over-stimulation of you quickly has _him2 licking the crowning head of your second child. _He2 diligently works you over until all of your children are born, making sure you are thoroughly exhausted; both from the birth and from _his2 ministrations.<<if canPenetrate(_S.Concubine) && canImpreg($PC, _S.Concubine)>> _S.Concubine.slaveName <<if canSee(_S.Concubine)>>eyes<<else>>faces<</if>> your spread pussy hungrily as _his2 erection bobs with anticipation. But you're too tired right now and _he2 realizes it.<</if>> _He2 helps gather your child<<if $PC.pregType > 1>>ren<</if>> to<<if _S.Concubine.lactation > 0>> _his2 and <</if>> your breasts with the hope that you'll reward _him2 when you recover.<</if>>
 	<<else>>
 		You keep pushing and pushing, your child slowly working its way from your body. With the last of your strength, you bear down, freeing your child from your body at last. Panting, you gather @@.lime;your new baby <<if _gender == "XX">>girl<<else>>boy<</if>>@@ <<if $PC.pregType > 1>>as another contraction ushers your next child into your birth canal<<else>>and drift off into a much deserved rest<</if>>.
 		<<if $PC.pregType >= 2>>
diff --git a/src/pregmod/theBlackMarket.tw b/src/pregmod/theBlackMarket.tw
index f0113cd964357e4ab05158b66ea8a718463cb101..aa035b107c3c5f868e4dc07aa9e4b6b1c93340b5 100644
--- a/src/pregmod/theBlackMarket.tw
+++ b/src/pregmod/theBlackMarket.tw
@@ -2,12 +2,12 @@
 
 <<set $nextButton = "Back", $nextLink = "Main", $returnTo = "Main", $encyclopedia = "The Black Market">>
 
-<<if $Bodyguard != 0>>
-	<<setLocalPronouns $Bodyguard>>
+<<if _S.Bodyguard>>
+	<<setLocalPronouns _S.Bodyguard>>
 <</if>>
 <<setPlayerPronouns>>
 
-You board your VTOL bird for the trip to the current location of the particularly exclusive, and quite lucrative, Black Market knowing full well that the trip may take far longer than anticipated; the market frequently relocates to shake the unwanted attention it inevitably gains. It's far easier than paying off authorities, especially with how outrageous their demands have become with the decline of the old world. You're greeted by a gruff bouncer, already fully aware of just who you are and your permission to be there. "Check your weapons — no firearms allowed inside.<<if $Bodyguard != 0>> $He may keep $his blade," he gestures to $Bodyguard.slaveName, "But keep in mind: if you cause us trouble, you will not be going home in one piece.<<if getLimbCount($Bodyguard, 105) > 0>> Oh, and one more thing." He <<if getArmCount($Bodyguard, 5) + getArmCount($Bodyguard, 6) > 0>> taps $his clearly bionic arm.<<else>> points at $his clearly bionic leg.<</if>> "Disable $his weapons systems. I know what $he is capable of.<</if>><</if>>"
+You board your VTOL bird for the trip to the current location of the particularly exclusive, and quite lucrative, Black Market knowing full well that the trip may take far longer than anticipated; the market frequently relocates to shake the unwanted attention it inevitably gains. It's far easier than paying off authorities, especially with how outrageous their demands have become with the decline of the old world. You're greeted by a gruff bouncer, already fully aware of just who you are and your permission to be there. "Check your weapons — no firearms allowed inside.<<if _S.Bodyguard>> $He may keep $his blade," he gestures to _S.Bodyguard.slaveName, "But keep in mind: if you cause us trouble, you will not be going home in one piece.<<if getLimbCount(_S.Bodyguard, 105) > 0>> Oh, and one more thing." He <<if getArmCount(_S.Bodyguard, 5) + getArmCount(_S.Bodyguard, 6) > 0>> taps $his clearly bionic arm.<<else>> points at $his clearly bionic leg.<</if>> "Disable $his weapons systems. I know what $he is capable of.<</if>><</if>>"
 
 <br><br>
 
diff --git a/src/pregmod/theUtopianOrphanage.tw b/src/pregmod/theUtopianOrphanage.tw
new file mode 100644
index 0000000000000000000000000000000000000000..ea9b894e75fdd5dd715e418272ed3ed1e0d3ea14
--- /dev/null
+++ b/src/pregmod/theUtopianOrphanage.tw
@@ -0,0 +1,28 @@
+:: The Utopian Orphanage [nobr]
+
+<<set $nextButton = "Back", $nextLink = "Buy Slaves", $returnTo = "Buy Slaves", $encyclopedia = "Slave Schools", $slaveMarket = "TUO">>
+/* Multi-Purchase Support */
+<<if ndef $newSlaves>><<set $newSlaves = []>><</if>>
+<<if $newSlaves.length > 0>>
+	<<set $nextButton = "Continue", $nextLink = "Bulk Slave Intro", $returnTo = "Main", $newSlaveIndex = 0>>
+<</if>>
+
+//The Utopian Orphanage is where all slaves dream of being raised. Its business model is to offer intelligent, well educated girls just past their majority. The girls are treated exceptionally well and have no sexual education or experience.
+
+<br><br>The Utopian Orphanage offers a fresh graduate for inspection. The interview takes place in the girl's private room, where she showcases her education and witts.
+
+<<set _marketResult = generateMarketSlave("TUO")>>
+<<set $activeSlave = _marketResult.slave>>
+<<print _marketResult.text>>
+<<set _slaveCost = slaveCost($activeSlave)>>
+<<if $TUO.schoolSale != 0>>
+	<<set _slaveCost = Math.trunc(_slaveCost*0.6)>>
+<<elseif $TUO.schoolUpgrade != 0>>
+	<<set _slaveCost = Math.trunc(_slaveCost*1.2)>>
+<</if>>
+<<if $slavesSeen > $slaveMarketLimit>><<set _slaveCost += Math.trunc(_slaveCost*(($slavesSeen-$slaveMarketLimit)*0.1))>><</if>>
+
+<br><br>The price is <<print cashFormat(_slaveCost)>>.<<if $slavesSeen > $slaveMarketLimit>> You have cast such a wide net for slaves this week that it is becoming more expensive to find more for sale. Your reputation helps determine your reach within the slave market.<</if>>
+
+<<setLocalPronouns $activeSlave>>
+<<buyingFromSchoolControls $TUO "slave" "slaves">>
diff --git a/src/pregmod/widgets/bodyswapWidgets.tw b/src/pregmod/widgets/bodyswapWidgets.tw
index ee9c226b664eca66be129389643e7659a3df1411..4a1ed4eeac6e86a66b17cdfda37e3e0ba63d4cd4 100644
--- a/src/pregmod/widgets/bodyswapWidgets.tw
+++ b/src/pregmod/widgets/bodyswapWidgets.tw
@@ -164,6 +164,7 @@
 <<set $args[0].albinismOverride = $args[1].albinismOverride>>
 <<set $args[0].clone = $args[1].clone>>
 <<set $args[0].cloneID = $args[1].cloneID>>
+<<set $args[0].inbreedingCoeff = $args[1].inbreedingCoeff>>
 
 <<set $args[0].canRecruit = 0>>
 
diff --git a/src/pregmod/widgets/pregmodBirthWidgets.tw b/src/pregmod/widgets/pregmodBirthWidgets.tw
index 399c764a95e788f0c718bea3e4b3b5fd59547660..f0e412c573a94a5ac2712721c96944e6b9bbb8c3 100644
--- a/src/pregmod/widgets/pregmodBirthWidgets.tw
+++ b/src/pregmod/widgets/pregmodBirthWidgets.tw
@@ -381,11 +381,11 @@
 			<<if _birthScene > 50>>
 				While giving a slave oral service, $slaves[$i].slaveName's water breaks. $He disregards this development and continues working.
 				<<ClothingBirth>>
-				The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he draws <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him to move $his useless pregnant ass.
+				The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he draws <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirlID != 0>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him to move $his useless pregnant ass.
 			<<else>>
 				While scrubbing the penthouse floor, $slaves[$i].slaveName's water breaks. $He turns to clean this new spill, disregarding what it means.
 				<<ClothingBirth>>
-				Instead of cleaning the fresh mess $he made, $he draws <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him to move $his useless pregnant ass.
+				Instead of cleaning the fresh mess $he made, $he draws <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif HeadGirlID != 0>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him to move $his useless pregnant ass.
 			<</if>>
 		<</if>>
 	<<else>>
@@ -396,11 +396,11 @@
 				While giving a slave oral service, $slaves[$i].slaveName's water breaks. $He desperately tries to pull away but they grab $his head and force $him back to their crotch.
 				<<set $humiliation = 1>>
 				<<ClothingBirth>>
-				The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. $He hurriedly tries to mop up the mess and collect <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child before <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>><</if>> shouts at $him for dragging $his pregnant ass.
+				The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. $He hurriedly tries to mop up the mess and collect <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child before <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirlID != 0>>_S.HeadGirl.slaveName<<else>><</if>> shouts at $him for dragging $his pregnant ass.
 			<<else>>
 				While scrubbing the penthouse floor, $slaves[$i].slaveName's water breaks. $He panics at the thought of not cleaning up $his spill but $his worsening contractions force $him to find a secluded place to give birth.
 				<<ClothingBirth>>
-				Collecting <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child, $he carefully exits $his hiding place before coming under the eye of <<if _S.Stewardess>>the glaring _S.Stewardess.slaveName. $His child is promptly taken<<elseif $HeadGirl != 0>>the glaring $HeadGirl.slaveName. $His child is promptly taken <<else>>. $His child is promptly taken by other servants<</if>> following a lecture about priorities and time management given $his constant pregnancy and predictable birth cycle.
+				Collecting <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child, $he carefully exits $his hiding place before coming under the eye of <<if _S.Stewardess>>the glaring _S.Stewardess.slaveName. $His child is promptly taken<<elseif $HeadGirlID != 0>>the glaring _S.HeadGirl.slaveName. $His child is promptly taken <<else>>. $His child is promptly taken by other servants<</if>> following a lecture about priorities and time management given $his constant pregnancy and predictable birth cycle.
 			<</if>>
 		<</if>>
 	<</if>>
@@ -418,13 +418,13 @@
 				<<if $masterSuiteUpgradeLuxury == 1>>
 					While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's water breaks thoroughly soaking the sheets. Ignoring the mess, $he shifts into a more comfortable position.
 					<<ClothingBirth>>
-					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 				<<elseif $masterSuiteUpgradeLuxury == 2>>
 					While being the bottom of the master suite's fuckpit, $slaves[$i].slaveName's water breaks. While $he doesn't stop having sex, the fucktoys using $him do and drag $him from the pit to give birth. Instinctively, $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to who may be watching $his naked crotch. Without a second thought about $his child, $he slips back into the fuckpit.
 				<<else>>
 					While awaiting your return in the master suite, $slaves[$i].slaveName's water breaks. $He pays it no heed and continues blankly waiting.
 					<<ClothingBirth>>
-					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass thoroughly cleaned before your return.<</if>>
+					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass thoroughly cleaned before your return.<</if>>
 				<</if>>
 			<</if>>
 		<<else>>
@@ -463,13 +463,13 @@
 				<<if $masterSuiteUpgradeLuxury == 1>>
 					While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's water breaks thoroughly soaking the sheets. Ignoring the mess, $he shifts into a more comfortable position.
 					<<ClothingBirth>>
-					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 				<<elseif $masterSuiteUpgradeLuxury == 2>>
 					While being the bottom of the master suite's fuckpit, $slaves[$i].slaveName's water breaks. While $he doesn't stop having sex, the fucktoys using $him do and drag $him from the pit to give birth. Instinctively, $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to who may be watching $his naked crotch. Without a second thought about $his child, $he slips back into the fuckpit.
 				<<else>>
 					While awaiting your return in the master suite, $slaves[$i].slaveName's water breaks. $He pays it no heed and continues blankly waiting.
 					<<ClothingBirth>>
-					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass thoroughly cleaned before your return.<</if>>
+					$He struggles to reach <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child around $his still gravid figure before bringing it to $his breast and resuming $his wait <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child removed and $his still very pregnant ass thoroughly cleaned before your return.<</if>>
 				<</if>>
 			<</if>>
 		<<else>>
@@ -623,25 +623,25 @@
 	<</if>>
 
 <<case "work as a nanny">>		/* TODO: this needs a rewrite */
-	<<if ($Matron != 0)>>
-		<<setLocalPronouns $Matron 2>>
+	<<if ($MatronID != 0)>>
+		<<setLocalPronouns _S.Matron 2>>
 	<</if>>
 	<<if $slaves[$i].fetish == "mindbroken">>
 		<<if (random(1,20) > $suddenBirth)>>
-			<<if $Matron != 0>>$Matron.slaveName leads $him to a special pool designed to give birth in. Once $he is safely in the water alongside _him2,<<else>>$He is lead to a special pool designed to give birth in. Once $he is safely in the water alongside $his assistant,<</if>> $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to $his watching caretaker. $His child is promptly taken and, following a cleaning of $his still very gravid body, $he is taken back to the nursery.
+			<<if $MatronID != 0>>_S.Matron.slaveName leads $him to a special pool designed to give birth in. Once $he is safely in the water alongside _him2,<<else>>$He is lead to a special pool designed to give birth in. Once $he is safely in the water alongside $his assistant,<</if>> $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to $his watching caretaker. $His child is promptly taken and, following a cleaning of $his still very gravid body, $he is taken back to the nursery.
 		<<else>>
-			While soaking in the nursery's pool, $slaves[$i].slaveName's water breaks. As $he begins to ready $himself for birth, <<if $Matron != 0>>$Matron.slaveName pulls $him out of the pool and glares at $him<<else>>the other bathers watch curiously<</if>>.
+			While soaking in the nursery's pool, $slaves[$i].slaveName's water breaks. As $he begins to ready $himself for birth, <<if $MatronID != 0>>_S.Matron.slaveName pulls $him out of the pool and glares at $him<<else>>the other bathers watch curiously<</if>>.
 			<<ClothingBirth>>
 			$He and <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child are quickly dried off as $he begins nursing it. A servant soon arrives to take $his child away.
 		<</if>>
 	<<else>>
 		<<if (random(1,20) > $suddenBirth)>>
-			<<if $Matron != 0>>$Matron.slaveName escorts $him to a special pool designed to give birth in. Once $he is safely in the water alongside _him2,<<else>>$He is escorted to a special pool designed to give birth in. Once $he is safely in the water alongside $his assistant,<</if>> $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process and being<</if>> aided by $his caretaker. $His child is promptly taken and, following a cleaning of $his still very gravid body, $he is taken back to the nursery.
+			<<if $MatronID != 0>>_S.Matron.slaveName escorts $him to a special pool designed to give birth in. Once $he is safely in the water alongside _him2,<<else>>$He is escorted to a special pool designed to give birth in. Once $he is safely in the water alongside $his assistant,<</if>> $he begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process and being<</if>> aided by $his caretaker. $His child is promptly taken and, following a cleaning of $his still very gravid body, $he is taken back to the nursery.
 		<<else>>
 			<<if !canWalk($slaves[$i])>>While changing in the changing room before a nice soak, $slaves[$i].slaveName's water breaks. $He hurries to find someone to help $him but only finds curious onlookers<<else>>While heading for the changing room before a nice soak, $slaves[$i].slaveName's water breaks. $He hurries into the changing room only to find it unusually crowded<</if>>. Without any choice left, $he assumes a birthing position.
 			<<set $humiliation = 1>>
 			<<ClothingBirth>>
-			Several of other slaves present help $him with <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child while the rest finish pleasuring themselves over the show. <<if $Matron != 0>>$Matron.slaveName, lured in by the commotion, shoos the other slaves out and helps the new mother to a private relaxation room to unwind<<else>>Soon a servant arrives to take $his child away and $he is ushered into the bath to clean up and relax<</if>>.
+			Several of other slaves present help $him with <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child while the rest finish pleasuring themselves over the show. <<if $MatronID != 0>>_S.Matron.slaveName, lured in by the commotion, shoos the other slaves out and helps the new mother to a private relaxation room to unwind<<else>>Soon a servant arrives to take $his child away and $he is ushered into the bath to clean up and relax<</if>>.
 		<</if>>
 	<</if>>
 
@@ -855,47 +855,47 @@
 	<</if>>
 
 <<case "live with your Head Girl">>
-	<<setLocalPronouns $HeadGirl 2>>
-	<<if $slaves[$i].pregSource == $HeadGirl.ID>>
+	<<setLocalPronouns _S.HeadGirl 2>>
+	<<if $slaves[$i].pregSource == $HeadGirlID>>
 		<<if (random(1,20) > $suddenBirth)>>
-			$HeadGirl.slaveName makes sure that the mother of _his2 child is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, convulsing with orgasms in the process<</if>>, and $his child is carefully collected by their father. Once they are out of the way, $HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired, still very gravid body.
+			_S.HeadGirl.slaveName makes sure that the mother of _his2 child is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, convulsing with orgasms in the process<</if>>, and $his child is carefully collected by their father. Once they are out of the way, _S.HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired, still very gravid body.
 		<<else>>
 		<</if>>
 	<<else>>
 		<<if !canWalk($slaves[$i])>>
 			<<if $slaves[$i].fetish == "mindbroken">>
 				<<if (random(1,20) > $suddenBirth)>>
-					$He is aided in finding $HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to _his2 wandering hands. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is taken back to $HeadGirl.slaveName' room.
+					$He is aided in finding _S.HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to _his2 wandering hands. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is taken back to _S.HeadGirl.slaveName' room.
 				<<else>>
 					$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He rolls over and begins to fall back to sleep as another contraction wracks $his body.
 					<<ClothingBirth>>
-					$He fishes around under $his still huge dome of a stomach searching for $his newborn. Once $he finds it, $he draws it to $his breast and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+					$He fishes around under $his still huge dome of a stomach searching for $his newborn. Once $he finds it, $he draws it to $his breast and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 				<</if>>
 			<<else>>
 				<<if (random(1,20) > $suddenBirth)>>
-					$He is aided in seeking out $HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, while convulsing with orgasms in the process<</if>>. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is helped back to $HeadGirl.slaveName's room.
+					$He is aided in seeking out _S.HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, while convulsing with orgasms in the process<</if>>. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is helped back to _S.HeadGirl.slaveName's room.
 				<<else>>
 					$slaves[$i].slaveName is awoken from $his rest by a moist sensation followed by a contraction. $He rolls over and clutches $his gravid belly as another contraction wracks $his body.
 					<<ClothingBirth>>
-					$He collects <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child and places it in the cradle readied for $him. $He is helped to the shower as your servants clean up and remove $his newborn. Freshened up, $he returns to resting knowing full well that $HeadGirl.slaveName will be eager to play with $his still very pregnant body when _he2 finishes _his2 rounds.
+					$He collects <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child and places it in the cradle readied for $him. $He is helped to the shower as your servants clean up and remove $his newborn. Freshened up, $he returns to resting knowing full well that _S.HeadGirl.slaveName will be eager to play with $his still very pregnant body when _he2 finishes _his2 rounds.
 				<</if>>
 			<</if>>
 		<<else>>
 			<<if $slaves[$i].fetish == "mindbroken">>
 				<<if (random(1,20) > $suddenBirth)>>
-					$He wanders until $he finds $HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to _his2 wandering hands. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is lead back to $HeadGirl.slaveName' room.
+					$He wanders until $he finds _S.HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, indifferent to _his2 wandering hands. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is lead back to _S.HeadGirl.slaveName' room.
 				<<else>>
 					$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He rolls over and begins to fall back to sleep as another contraction wracks $his body.
 					<<ClothingBirth>>
-					$He fishes around under $his still huge dome of a stomach searching for $his newborn. Once $he finds it, $he draws it to $his breast and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+					$He fishes around under $his still huge dome of a stomach searching for $his newborn. Once $he finds it, $he draws it to $his breast and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 				<</if>>
 			<<else>>
 				<<if (random(1,20) > $suddenBirth)>>
-					$He seeks out $HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, while convulsing with orgasms in the process<</if>>. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he returns to $HeadGirl.slaveName's room.
+					$He seeks out _S.HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> baby, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>>, while convulsing with orgasms in the process<</if>>. $His child is promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he returns to _S.HeadGirl.slaveName's room.
 				<<else>>
 					$slaves[$i].slaveName is awoken from $his rest by a moist sensation followed by a contraction. $He rolls over and clutches $his gravid belly as another contraction wracks $his body.
 					<<ClothingBirth>>
-					$He collects <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child and places it in the cradle readied for $him. $He waddles off to shower as your servants clean up and remove $his newborn. Freshened up, $he returns to resting knowing full well that $HeadGirl.slaveName will be eager to play with $his still very pregnant body when _he2 finishes _his2 rounds.
+					$He collects <<if $slaves[$i].counter.birthsTotal == 0>>$his first<<else>>this week's<</if>> child and places it in the cradle readied for $him. $He waddles off to shower as your servants clean up and remove $his newborn. Freshened up, $he returns to resting knowing full well that _S.HeadGirl.slaveName will be eager to play with $his still very pregnant body when _he2 finishes _his2 rounds.
 				<</if>>
 			<</if>>
 		<</if>>
@@ -1490,18 +1490,18 @@
 		<<if random(0,1) == 1>>
 			While giving a slave oral service, $slaves[$i].slaveName's body begins to birth another of $his brood. $He disregards this development and continues working.
 			<<ClothingBirth>>
-			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he struggles to bring $his child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him to move $his useless ass<<elseif $HeadGirl != 0>>$HeadGirl.slaveName shouts at $him to move $his useless ass<<else>>shouts at $him to move $his useless ass<</if>>.
+			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he struggles to bring $his child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him to move $his useless ass<<elseif $HeadGirlID != 0>>_S.HeadGirl.slaveName shouts at $him to move $his useless ass<<else>>shouts at $him to move $his useless ass<</if>>.
 		<<else>>
 			While struggling to scrub the penthouse floor, $slaves[$i].slaveName's body begins to birth another of $his brood. $He carries on trying to clean as $he drags $his belly along the floor.
 			<<ClothingBirth>>
-			Instead of carrying on $his task, $he draws $his child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him to move $his useless ass<<elseif $HeadGirl != 0>>$HeadGirl.slaveName shouts at $him to move $his useless ass<<else>>shouts at $him to move $his useless ass<</if>>.
+			Instead of carrying on $his task, $he draws $his child to $his breast until <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him to move $his useless ass<<elseif $HeadGirlID != 0>>_S.HeadGirl.slaveName shouts at $him to move $his useless ass<<else>>shouts at $him to move $his useless ass<</if>>.
 		<</if>>
 	<<else>>
 		<<if random(0,1) == 1>>
 			While giving a slave oral service, $slaves[$i].slaveName's body begins to birth another of $his brood. $He desperately tries to pull away but they grab $his head and force $him back to their crotch.
 			<<set $humiliation = 1>>
 			<<ClothingBirth>>
-			The slave gets off quite strongly to the show and shoves $him to the ground, leaving $him struggling to get up before <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him<<elseif $HeadGirl != 0>>$HeadGirl.slaveName shouts at $him <<else>>shouts at $him<</if>>.
+			The slave gets off quite strongly to the show and shoves $him to the ground, leaving $him struggling to get up before <<if _S.Stewardess>>_S.Stewardess.slaveName shouts at $him<<elseif $HeadGirlID != 0>>_S.HeadGirl.slaveName shouts at $him <<else>>shouts at $him<</if>>.
 		<<else>>
 			While using $his bloated belly to scrub the penthouse floor, $slaves[$i].slaveName's body begins to birth another of $his brood. Since this isn't the first time this week this happened, $he readies $himself for the coming birth.
 			<<ClothingBirth>>
@@ -1515,13 +1515,13 @@
 			<<if $masterSuiteUpgradeLuxury == 1>>
 				While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's body begins to birth another of $his brood. $He struggles to shift into a more comfortable position before giving up.
 				<<ClothingBirth>>
-				Resting in your bed, $he draws $his child to $his breast and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+				Resting in your bed, $he draws $his child to $his breast and resumes waiting for you. <<if _S.Concubine>>_S.Concubine furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 			<<elseif $masterSuiteUpgradeLuxury == 2>>
 				While being the bottom of the master suite's fuckpit, $slaves[$i].slaveName's body begins to birth another of $his brood. While $he doesn't stop having sex, the fucktoys using $him do and attempt to drag $him from the pit to give birth. Instinctively $he begins to push out $his baby, indifferent to who may be watching $his naked crotch. The fucktoys give up their efforts to haul $his bloated form out of the pit and allow $him to settle at the bottom. Without a second thought about $his child, $he attempts to coax the other slaves back into an orgy.
 			<<else>>
 				While awaiting your return in the master suite, $slaves[$i].slaveName's body begins to birth another of $his brood. $He pays it no heed and continues blankly waiting.
 				<<ClothingBirth>>
-				Resting on $his belly, $he draws $his child to $his breast and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
+				Resting on $his belly, $he draws $his child to $his breast and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
 			<</if>>
 		<<else>>
 			<<if $masterSuiteUpgradeLuxury == 1>>
@@ -1544,13 +1544,13 @@
 			<<if $masterSuiteUpgradeLuxury == 1>>
 				While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's body begins to birth another of $his brood. $He shifts into a more comfortable position.
 				<<ClothingBirth>>
-				Resting in your bed, $he draws $his child to $his breast and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+				Resting in your bed, $he draws $his child to $his breast and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 			<<elseif $masterSuiteUpgradeLuxury == 2>>
 				While being the bottom of the master suite's fuckpit, $slaves[$i].slaveName's body begins to birth another of $his brood. While $he doesn't stop having sex, the fucktoys using $him do and attempt to drag $him from the pit to give birth. Instinctively $he begins to push out $his baby, indifferent to who may be watching $his naked crotch. The fucktoys give up their efforts to haul $his bloated form out of the pit and allow $him to settle a the bottom. Without a second thought about $his child, $he attempts to coax the other slaves back into an orgy.
 			<<else>>
 				While awaiting your return in the master suite, $slaves[$i].slaveName's body begins to birth another of $his brood. $He pays it no heed and continues blankly waiting.
 				<<ClothingBirth>>
-				Resting on $his belly, $he draws $his child to $his breast and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
+				Resting on $his belly, $he draws $his child to $his breast and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
 			<</if>>
 		<<else>>
 			<<if $masterSuiteUpgradeLuxury == 1>>
@@ -1752,26 +1752,26 @@
 	<</if>>
 
 <<case "live with your Head Girl">>
-	<<setLocalPronouns $HeadGirl 2>>
+	<<setLocalPronouns _S.HeadGirl 2>>
 	<<if !canWalk($slaves[$i])>>
 		<<if $slaves[$i].fetish == "mindbroken">>
 			$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He attempts to roll over, and failing that, begins to fall back to sleep as another contraction wracks $his body. $His body begins to birth another of $his brood.
 			<<ClothingBirth>>
-			$He draws $his child to $his breast and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+			$He draws $his child to $his breast and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 		<<else>>
 			$slaves[$i].slaveName's body begins to birth another of $his brood. $He attempts to roll over, and failing that, claws at $his massive belly as another contraction wracks $his body.
 			<<ClothingBirth>>
-			$He struggles to collect $his child and brings them to $his breast. $He waits for $HeadGirl.slaveName to return, hoping _he2 will arrive before another baby makes its way out of $him.
+			$He struggles to collect $his child and brings them to $his breast. $He waits for _S.HeadGirl.slaveName to return, hoping _he2 will arrive before another baby makes its way out of $him.
 		<</if>>
 	<<else>>
 		<<if $slaves[$i].fetish == "mindbroken">>
 			$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He rolls over and begins to fall back to sleep as another contraction wracks $his body. $His body begins to birth another of $his brood.
 			<<ClothingBirth>>
-			$He draws $his child to $his breast and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+			$He draws $his child to $his breast and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 		<<else>>
 			$slaves[$i].slaveName's body begins to birth another of $his brood. $He rolls over and claws at $his massive belly as another contraction wracks $his body.
 			<<ClothingBirth>>
-			$He struggles to collect $his child and places them in the cradle readied for $him. $He waddles off to take a bath as your servants clean up and remove $his child. Cleaned up, $he returns to resting knowing full well that $HeadGirl.slaveName will be eager to play with $his body upon returning.
+			$He struggles to collect $his child and places them in the cradle readied for $him. $He waddles off to take a bath as your servants clean up and remove $his child. Cleaned up, $he returns to resting knowing full well that _S.HeadGirl.slaveName will be eager to play with $his body upon returning.
 		<</if>>
 	<</if>>
 
diff --git a/src/pregmod/widgets/seBirthWidgets.tw b/src/pregmod/widgets/seBirthWidgets.tw
index c1872443110e79ad8c2af4a48f0e5c0a2143047c..5285e60e86fdfa52cad050540f3c481d849f30b8 100644
--- a/src/pregmod/widgets/seBirthWidgets.tw
+++ b/src/pregmod/widgets/seBirthWidgets.tw
@@ -123,8 +123,14 @@
 <<if $slaves[$i].induce == 1>>
 	<<set $suddenBirth += 20>>
 <</if>>
-<<if !canWalk($slaves[$i])>>
+<<if !canMove($slaves[$i])>>
 	<<set $suddenBirth += 10>>
+<<elseif !canWalk($slaves[$i])>>
+	<<if $slaves[$i].rules.mobility == "permissive">>
+		<<set $suddenBirth += 3>>
+	<<else>>
+		<<set $suddenBirth += 5>>
+	<</if>>
 <</if>>
 <<if $slaves[$i].fetish == "mindbroken">>
 	<<set $suddenBirth += 18>>
diff --git a/src/societies/aztec/slaveSacrifice.tw b/src/societies/aztec/slaveSacrifice.tw
index 3b848493e18a323165e1992f4ad5824e76aaa5a4..6622dd3335baa2579d3977aad6c8eebd522c5b54 100644
--- a/src/societies/aztec/slaveSacrifice.tw
+++ b/src/societies/aztec/slaveSacrifice.tw
@@ -76,11 +76,11 @@
 		<</if>>
 	<<else>>
 		<<if canWalk($activeSlave)>>
-			$He manages to run halfway to your desk before $he's <<if $Bodyguard != 0>>clotheslined by $Bodyguard.slaveName<<elseif $HeadGirl != 0>> restrained by $HeadGirl.slaveName<<else>>restrained by another, more obedient slave<</if>>. $He kicks and struggles as $he's removed from your office. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
+			$He manages to run halfway to your desk before $he's <<if _S.Bodyguard>>clotheslined by _S.Bodyguard.slaveName<<elseif $HeadGirlID != 0>> restrained by _S.HeadGirl.slaveName<<else>>restrained by another, more obedient slave<</if>>. $He kicks and struggles as $he's removed from your office. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
 		<<elseif !canTalk($activeSlave)>>
-			A long line of slurs and names are screamed at you, none of them particularly pleasant. Your <<if $HeadGirl != 0>>Head Girl $HeadGirl.slaveName <<else>>another, more obedient slave<</if>> removes $him as $he continues $his rant, spitting and resisting as much as $he can without limbs. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
+			A long line of slurs and names are screamed at you, none of them particularly pleasant. Your <<if $HeadGirlID != 0>>Head Girl _S.HeadGirl.slaveName <<else>>another, more obedient slave<</if>> removes $him as $he continues $his rant, spitting and resisting as much as $he can without limbs. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
 		<<else>>
-			$He proceeds to drop into a massive tantrum for the lack of better ways to express $himself. <<if $HeadGirl != 0>>$HeadGirl.slaveName<<else>>Another, more obedient slave<</if>> removes $him as $he continues struggling, spitting and resisting as much as $he can. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
+			$He proceeds to drop into a massive tantrum for the lack of better ways to express $himself. <<if $HeadGirlID != 0>>_S.HeadGirl.slaveName<<else>>Another, more obedient slave<</if>> removes $him as $he continues struggling, spitting and resisting as much as $he can. By the end of the week, it won't matter; $he'll be so hungry and horny that resistance will be the last thing on $his mind.
 		<</if>>
 	<</if>>
 
diff --git a/src/uncategorized/PESS.tw b/src/uncategorized/PESS.tw
index 6c3762e4ba3e20be09c6d6b62e3c4f9f7a322d19..13aed8a89a93a9205821a774d507eb82f54106ce 100644
--- a/src/uncategorized/PESS.tw
+++ b/src/uncategorized/PESS.tw
@@ -44,18 +44,18 @@
 /* special leadership events can be rolled on any eventSlave */
 <<switch $PESSevent>>
 	<<case "madam strategy">>
-		<<set $activeSlave = getSlave($Madam.ID)>>
+		<<set $activeSlave = _S.Madam>>
 	<<case "DJ publicity">>
-		<<set $activeSlave = getSlave($djID)>>
+		<<set $activeSlave = _S.DJ>>
 	<<case "tired milkmaid" "tired collectrix">>
-		<<set $activeSlave = getSlave($Milkmaid.ID)>>
+		<<set $activeSlave = _S.Milkmaid>>
 	<<case "loving concubine">>
-		<<set $activeSlave = getSlave($Concubine.ID)>>
+		<<set $activeSlave = _S.Concubine>>
 	<<case "bodyguard bedtime" "bodyguard beatdown">>
-		<<set $activeSlave = getSlave($Bodyguard.ID)>>
+		<<set $activeSlave = _S.Bodyguard>>
 	<<case "loving headgirl" "headgirl dickgirl" "worried headgirl" "worshipful impregnatrix">>
-		<<set $activeSlave = getSlave($HeadGirl.ID)>>
-		<<set $j = $slaves.findIndex(function(s) { return s.ID != $HeadGirl.ID && s.devotion <= 20; })>>
+		<<set $activeSlave = _S.HeadGirl>>
+		<<set $j = $slaves.findIndex(function(s) { return s.ID != $HeadGirlID && s.devotion <= 20; })>>
 <</switch>>
 
 <<set _clothesTemp = $activeSlave.clothes>>
@@ -395,7 +395,7 @@ $He sees you examining at $him, and looks back at you submissively, too tired to
 <<if ($activeSlave.anus > 0)>>
 	<br><<link "It can wait until after some rough sex with the Concubine">>
 		<<replace "#result">>
-		When you enter your suite, the dim light reveals $activeSlave.slaveName waiting with $his beautiful body laid across the bed. When $he sees <<if ($PC.dick == 0)>>the big strap-on you're wearing<<else>>the kind of sex you're in the mood for<</if>>, $he turns over and pushes $his face down into the sheets. $He <<if hasAnyLegs($Concubine)>>pulls $his knee<<if hasBothLegs($Concubine)>>s<</if>> under $himself and <</if>><<if hasAnyArms($Concubine)>>reaches back to pull<<else>>presents<</if>> $his <<if ($activeSlave.butt > 5)>>huge<<elseif ($activeSlave.butt > 2)>>sizable<<else>>pretty<</if>> buttocks apart, relaxing and then clenching $his <<if ($activeSlave.anus > 2)>>gaping<<elseif ($activeSlave.anus > 1)>>big<<else>>tight<</if>> asshole invitingly. $He starts <<if !canTalk($activeSlave)>>to point an inviting finger at $his backdoor,<<else>>to beg "Plea<<s>>e a<<ss>>rape me, M-" into the sheets,<</if>> but you interrupt $him by shoving $him forward so that $he's all the way face-down on the bed. $He knows how you like it and starts to wriggle, struggle, and whine as you roughly sodomize $him, <<if ($PC.dick == 0)>>the strap-on<<else>>your cock<</if>> ramming straight up $his ass despite the uncomfortable angle, <<if ($activeSlave.butt > 5)>>which $his huge ass makes easier for $him by limiting how deeply you can fuck $him without $his buttocks spread wide.<<elseif ($activeSlave.butt > 2)>>which $his big behind makes easier for $him by limiting how deeply you can fuck $him.<<else>>which $his modest ass makes harder for $him by allowing you to get really deep inside $his anus.<</if>> $He continues to wrestle with you, sometimes even managing to dislodge <<if ($PC.dick == 0)>>the strap-on<<else>>your dick<</if>>, allowing you the <<if ($PC.dick == 0)>>cruel delight<<else>>delicious sensation<</if>> of pushing it back up $his butt each time. Despite the pretense $his enjoyment is obvious;
+		When you enter your suite, the dim light reveals $activeSlave.slaveName waiting with $his beautiful body laid across the bed. When $he sees <<if ($PC.dick == 0)>>the big strap-on you're wearing<<else>>the kind of sex you're in the mood for<</if>>, $he turns over and pushes $his face down into the sheets. $He <<if hasAnyLegs(_S.Concubine)>>pulls $his knee<<if hasBothLegs(_S.Concubine)>>s<</if>> under $himself and <</if>><<if hasAnyArms(_S.Concubine)>>reaches back to pull<<else>>presents<</if>> $his <<if ($activeSlave.butt > 5)>>huge<<elseif ($activeSlave.butt > 2)>>sizable<<else>>pretty<</if>> buttocks apart, relaxing and then clenching $his <<if ($activeSlave.anus > 2)>>gaping<<elseif ($activeSlave.anus > 1)>>big<<else>>tight<</if>> asshole invitingly. $He starts <<if !canTalk($activeSlave)>>to point an inviting finger at $his backdoor,<<else>>to beg "Plea<<s>>e a<<ss>>rape me, M-" into the sheets,<</if>> but you interrupt $him by shoving $him forward so that $he's all the way face-down on the bed. $He knows how you like it and starts to wriggle, struggle, and whine as you roughly sodomize $him, <<if ($PC.dick == 0)>>the strap-on<<else>>your cock<</if>> ramming straight up $his ass despite the uncomfortable angle, <<if ($activeSlave.butt > 5)>>which $his huge ass makes easier for $him by limiting how deeply you can fuck $him without $his buttocks spread wide.<<elseif ($activeSlave.butt > 2)>>which $his big behind makes easier for $him by limiting how deeply you can fuck $him.<<else>>which $his modest ass makes harder for $him by allowing you to get really deep inside $his anus.<</if>> $He continues to wrestle with you, sometimes even managing to dislodge <<if ($PC.dick == 0)>>the strap-on<<else>>your dick<</if>>, allowing you the <<if ($PC.dick == 0)>>cruel delight<<else>>delicious sensation<</if>> of pushing it back up $his butt each time. Despite the pretense $his enjoyment is obvious;
 		<<if ($activeSlave.chastityPenis == 1)>>
 			though $his cock has been placed in a chastity cage, $he's grinding against you as eagerly as if it wasn't there at all.
 		<<elseif ($activeSlave.dick > 0) && ($activeSlave.hormoneBalance >= 100)>>
diff --git a/src/uncategorized/RECI.tw b/src/uncategorized/RECI.tw
deleted file mode 100644
index 7226673a369e0be09effa705a36aba33549fb938..0000000000000000000000000000000000000000
--- a/src/uncategorized/RECI.tw
+++ /dev/null
@@ -1,154 +0,0 @@
-:: RECI [nobr]
-
-/* This is one of several files that contains and organizes many different events.	*/
-/*	genericPlotEvents.tw															*/
-/*	PESS.tw: Player Event, Single Slave												*/
-/*	PETS.tw: Player Event, Two Slaves												*/
-/*	RECI.tw: Random Event, Check In													*/
-/*	REFI.tw: Random Event, Fetish Interest											*/
-/*	REFS.tw: Random Event, Future Societies											*/
-/*	RESS.tw: Random Event, Single Slave												*/
-/*	RESSTR.tw: Random Event, Single Slave (Test Realm, for debugging events)		*/
-/*	RETS.tw: Random Event, Two Slaves												*/
-/*																					*/
-/* Events can also be in a dedicated *.tw file, formatted as follows:				*/
-/*	jeXXXXX.tw: Justice Event														*/
-/*	pXXXXXX.tw: Player event														*/
-/*	peXXXXX.tw: Player Event focused on a slave										*/
-/*	reXXXXX.tw: Random Event														*/
-/*	resXXXX.tw: Random Event, School												*/
-/*	seXXXXX.tw: Slave Event, focuses on slaves coming or going						*/
-/*	securityForceXXXXX.tw: Special (Security) Force event							*/
-/*																					*/
-/* Some scenes are also stored in useGuard.tw, walkPast.tw, and toychest.tw			*/
-
-<<if Array.isArray($RECIevent)>>
-	<<set $activeSlave = $eventSlave>>
-	<<if $cheatMode == 1>>
-		<<set $nextButton = "Back", $nextLink = "Nonrandom Event", $returnTo = "Nonrandom Event">> /* if user just clicks spacebar */
-		''A random check-in event would have been selected from the following:''
-		<br>
-		<<for _i = 0; _i < $RECIevent.length; _i++>>
-			<<print "[[$RECIevent[_i]|RECI][$RECIevent = $RECIevent[" + _i + "]]]">>
-			<br>
-		<</for>>
-		<br><br>[[Go Back to Random Individual Event|Random Individual Event][$activeSlave = 0]]
-	<<else>>
-		<<set $RECIevent = $RECIevent.random()>>
-		<<goto "RECI">>
-	<</if>>
-<<else>>
-
-<<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Next Week">>
-
-<<set _clothesTemp = $activeSlave.clothes>>
-<<switch $RECIevent>>
-	/*Some events start with the slave naked (any event that starts with the daily inspection, for example). Here we switch their clothing just for the image to load, then switch it back quickly so the player's choice is not messed up.*/
-<<case "ugly">>
-	<<set $activeSlave.clothes = "no clothing">>
-<</switch>>
-<span id="artFrame">
-/* 000-250-006 */
-<<if $seeImages == 1>>
-	<<if $imageChoice == 1>>
-		<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
-	<<else>>
-		<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
-	<</if>>
-<</if>>
-/* 000-250-006 */
-</span>
-<<set $activeSlave.clothes = _clothesTemp>>
-
-<<run Enunciate($activeSlave)>>
-<<set $desc = SlaveTitle($activeSlave)>>
-<<set _belly = bellyAdjective($activeSlave)>>
-<<setLocalPronouns $activeSlave>>
-<<setPlayerPronouns>>
-
-<<switch $RECIevent>>
-
-<<case "ugly">>
-
-<<for $i = 0; $i < $REUglyCheckinIDs.length; $i++>>
-	<<if $REUglyCheckinIDs[$i] == $activeSlave.ID>>
-		<<set $REUglyCheckinIDs.deleteAt($i)>>
-		<<break>>
-	<</if>>
-<</for>>
-
-<<= App.UI.slaveDescriptionDialog($activeSlave)>> comes in for an inspection. You have a relaxed day scheduled, so you take the time to do an unusually thorough job. On a whim, you pull up $his complete file, flipping the virtual pages out across your desk. The induction pictures are particularly striking. $He doesn't look much like that, anymore, and that's a good thing. $He was not a pretty girl when you got here, though $he's pretty enough now. With a desk control gesture you flip a full frontal shot of $him on the day of $his enslavement up onto a wallscreen.
-<br><br>
-$His <<= App.Desc.eyesColor($activeSlave)>> track your sudden motion, of course, and $he follows it to the screen. Suddenly, the $desc is eye to eye with a life-size picture of who $he used to be, just a few <<if $showInches == 2>>feet<<else>>meters<</if>> away. $He gasps with recognition, and then $his face clouds inscrutably. $He takes a couple of hesitant steps forward, and then reaches out to touch the cheek of the $girl in the picture. As $his fingertips brush the smooth surface of the wallscreen, $his other hand ghosts along $his own face. $His expression is not sad, so it's surprising when a single tear rolls down $his cheek. You order $him to tell you how the picture makes $him feel.
-<br><br>
-$He looks pensive, and goes through two false starts before $he clears $his throat, wrenches $his gaze away from $his picture, and <<say>>s introspectively, "<<Master>>, it'<<s>> <<s>>trange. We — we don't have picture<<s>> of our<<s>>elve<<s>>, from before we were en<<s>>laved. I didn't really reali<<z>>e how much I'd changed. <<S>>ome day<<s>> being a <<s>>lave i<<s>> hard. But <<s>>eeing that picture, it make<<s>> me feel better about it. You — you're really <<if $PC.title == 1>>hand<<s>>ome<<else>>pretty<</if>>, <<Master>>. I bet you alway<<s>> were." $He inclines $his head towards the homely $girl on the screen. "It'<<s>> hard to be ugly, <<Master>>. Really hard. I feel <<s>>orry for that $girl, and I'm glad I don't look like $him anymore." $He laughs suddenly, a little self-consciously. "The cra<<z>>y thing i<<s>>, if you'd told that $girl that
-<<if $activeSlave.assignment == "whore">>
-	lot<<s>> and lot<<s>> of people would pay to fuck $him
-<<else>>
-	all kind<<s>> of people would happily fuck $him
-<</if>>
-<<s>>omeday, <<he>> wouldn't have believed you. It'<<s>> kind of rea<<ss>>uring, actually. I<<s>> that weird? That came out weird. <<S>>orry."
-
-<<default>>
-	<br>ERROR: bad RECI event $RECIevent
-<</switch>>
-
-<br><br>
-<span id="result">
-<<switch $RECIevent>>
-
-<<case "ugly">>
-
-<<link "$He's pretty enough to decorate your arm for a night out">>
-	<<set _clothesTemp = $activeSlave.clothes, $activeSlave.clothes = "a mini dress">>
-	<<replace "#artFrame">>
-		/* 000-250-006 */
-		<<if $seeImages == 1>>
-			<<if $imageChoice == 1>>
-				<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
-			<<else>>
-				<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt($activeSlave, 2, 0)>></div>
-			<</if>>
-		<</if>>
-		/* 000-250-006 */
-	<</replace>>
-	<<set $activeSlave.clothes = _clothesTemp>>
-
-	<<replace "#result">>
-	<p>
-		You tell $him to head down to the wardrobe and put on the outfit that'll be laid out for $him there. $He obeys promptly, but does not return for some time, having gotten instructions from $assistant.name along the way to put extra effort into $his grooming. When $he finally returns, the effect is striking.
-		<<if $activeSlave.face > 10>>
-			$He's a gorgeous $girl with or without makeup, dressed up or naked, but $he's especially luscious tonight.
-		<<else>>
-			$His face is not flawless, but $he's conscious of $his transformation, and the new confidence in $his beauty adds a special glow that cannot be faked.
-		<</if>>
-		$His evening dress is elegant; it's quite slutty by old world standards, but according to Free Cities fashion, it's just about the most conservative gown a slave can be expected to wear, and quite daring in that it isn't immediately obvious whether $he's a slave or not. The tops of $his areolae are hardly even visible.
-	</p>
-	<p>
-		You take $him out to a nice lounge, with blue-toned light and soft music. $He clings to your arm, pressing $himself against you just the right amount: not enough to demand sex right now, but enough to raise the anticipation of it later. $He's a slave, so $he does not eat or drink the usual fare on offer, but the establishment is used to slaves and offers flavorful variation on liquid slave food. $He drinks the translucent fluid out of a tall glass, carefully maintaining $his poise. You circulate, leaving $him at the bar when acquaintances appear. $He perches on a stool, conscious of and pleased by the discreet admiration of $his body, delectably outlined by the tight dress. Once a new arrival who did not see you with $him introduces himself to $him. He's tall and fit and silver-haired, but he picked $him out of the room to approach, and it's with polite disappointment that he reacts to $his indication of you, across the room: "I'm <<s>>orry, <<S>>ir, that'<<s>> my <<Master>> there." He offers a nonverbal apology without coming over, which you accept with a wave: it's such a common mistake in Free Cities high society that it's universally brushed off without offense. It happens again later in the night, when a slightly tipsy free woman occupies the barstool next to $activeSlave.slaveName's and keeps trying to relax against $him until the flattered slave explains $himself again. When you bring $him home at the end of the night, $his eyes are shining with @@.mediumaquamarine;private assurance,@@ and $he presses $himself against you more closely than ever.
-	</p>
-	<<set $activeSlave.trust += 4>>
-	<</replace>>
-<</link>>
-<br><<link "Show $him off online">>
-	<<replace "#result">>
-	You tell $him that $he's become a very pretty sex slave, and to prove it, you set up a live feed for $him that's available for free in the old world. The Free Cities produce an utter torrent of hardcore video, so much that there's never any profit to be made off it anymore, but viewers begin to join the channel anyway and by the end of the week $he has thousands of eyes watching $him as $he <<if $activeSlave.assignment == "whore">>sells $his body<<else>>has sex with random citizens<</if>> in the hallways of $arcologies[0].name. <<= capFirstChar($assistant.name)>> keeps $him constantly informed of how many people are watching $him get fucked, how many of them are likely masturbating to $his body, and how many inquiries about $him $assistant.name is culling out of your inbox. $He slowly gets used to $his starring role in an impromptu free hardcore stream, but never quite stops stealing wondering glances at the nearest camera, as though $he cannot believe that so many people would sexualize $him. This lends $him a naughtiness that cannot be faked, garnering $him @@.green;much attention@@ by the end of the week.
-	<<if ($activeSlave.fetish == "humiliation") && ($activeSlave.fetishKnown == 1) && ($activeSlave.fetishStrength > 60)>>
-		Of course, since $he's a humiliation fetishist, this experience is like sexual candy for $him. $He @@.hotpink;can't get enough.@@
-		<<set $activeSlave.devotion += 4>>
-	<</if>>
-	<<run repX(1250, "event", $activeSlave)>>
-	<</replace>>
-<</link>>
-
-<<default>>
-	<br>ERROR: bad RECI event $RECIevent
-<</switch>>
-
-<<if $cheatMode == 1>>
-	<br><br>DEBUG: &nbsp;&nbsp;&nbsp;&nbsp;[[Go back to Nonrandom Event|Nonrandom Event][$activeSlave = 0, $eventSlave = 0]]
-<</if>>
-
-</span>
-
-<</if>> /* CLOSES EVENT SELECTION */
diff --git a/src/uncategorized/RESS.tw b/src/uncategorized/RESS.tw
index 6ce35653b7b0d37657a2ccb33357314147aef9e3..4340b937543903e9e88b394418f49515e765e565 100644
--- a/src/uncategorized/RESS.tw
+++ b/src/uncategorized/RESS.tw
@@ -44,7 +44,7 @@
 <<set _clothesTemp = $activeSlave.clothes>>
 <<switch $RESSevent>>
 /*Some events start with the slave naked (any event that starts with the daily inspection, for example). Here we switch their clothing just for the image to load, then switch it back quickly so the player's choice is not messed up.*/
-<<case "age implant" "ara ara" "back stretch" "bad dream" "bed snuggle" "bondage gear" "bonded love" "breast expansion blues" "confident tanning" "devoted educated slave" "devoted exhibition" "devoted lotion" "desperate null" "devoted nympho" "devoted shortstack" "extreme aphrodisiacs" "fearful balls" "fucktoy tribbing" "gaped asshole" "happy dance" "heavy piercing" "huge naturals" "huge tits" "hugely pregnant" "ignorant horny" "implant inspection" "kitchen molestation" "language lesson" "mindbroken morning" "modest clothes" "mods please" "old PC age difference" "orchiectomy please" "PA flirting" "penitent" "permitted masturbation" "plimb help" "rebellious arrogant" "resistant gelding" "resistant shower" "resting amp" "restricted profession" "restricted smart" "sexy succubus" "shaped areolae" "shift masturbation" "shift sleep" "shower slip" "slave clit on slave" "slave dick huge" "slave dick on slave" "sleeping ambivalent" "sore shoulders" "spa boobs" "subjugation blues" "tendon fall" "terrified inspection" "tittymonster inspection" "torpedo squeeze" "transition anxiety" "trusting HG" "unhappy virgin" "used whore" "vocal disobedience" "young PC age difference">>
+<<case "age implant" "ara ara" "back stretch" "bad dream" "bed snuggle" "bondage gear" "bonded love" "breast expansion blues" "confident tanning" "devoted exhibition" "devoted lotion" "desperate null" "devoted nympho" "devoted shortstack" "extreme aphrodisiacs" "fearful balls" "fucktoy tribbing" "gaped asshole" "happy dance" "heavy piercing" "huge naturals" "huge tits" "hugely pregnant" "ignorant horny" "implant inspection" "language lesson" "mindbroken morning" "modest clothes" "mods please" "old PC age difference" "orchiectomy please" "PA flirting" "penitent" "permitted masturbation" "plimb help" "rebellious arrogant" "resistant gelding" "resistant shower" "resting amp" "restricted profession" "restricted smart" "sexy succubus" "shaped areolae" "shift masturbation" "shift sleep" "shower slip" "slave dick huge" "sleeping ambivalent" "sore shoulders" "spa boobs" "subjugation blues" "tendon fall" "terrified inspection" "tittymonster inspection" "torpedo squeeze" "transition anxiety" "trusting HG" "unhappy virgin" "used whore" "vocal disobedience" "young PC age difference">>
 	<<set $activeSlave.clothes = "no clothing">>
 <<case "whore rebellious">>
 	/* this scene states they are wearing "thick, durable latex with temperature regulation and anchor points for restraint" no matter what they are assigned to wear. */
@@ -235,12 +235,12 @@ Finishing your work, you lean back for a well-deserved break. Your peace is quic
 		as $he runs $his hand<<if hasBothArms($activeSlave)>>s<</if>> over your body
 	<</if>>
 	in a desperate attempt at taking control.
-	<<if $HeadGirl != 0>>
-		<<setLocalPronouns $HeadGirl 2>>
-		<<setSpokenLocalPronouns $activeSlave $HeadGirl>>
-		<<run Enunciate($HeadGirl)>>
-		You jump in surprise as $HeadGirl.slaveName tackles $him off you and to the floor
-		<<if $HeadGirl.skill.combat > 0>>
+	<<if $HeadGirlID != 0>>
+		<<setLocalPronouns _S.HeadGirl 2>>
+		<<setSpokenLocalPronouns $activeSlave _S.HeadGirl>>
+		<<run Enunciate(_S.HeadGirl)>>
+		You jump in surprise as _S.HeadGirl.slaveName tackles $him off you and to the floor
+		<<if _S.HeadGirl.skill.combat > 0>>
 			where _he2 twists $his arm in an arm hold until the rebellious slave stops struggling.
 		<<else>>
 			where _he2 settles _his2 weight on the struggling rebel, lacking any better idea of what to do.
@@ -603,7 +603,7 @@ down to meet you. $He's nude, having stripped in your office, and meets your gaz
 
 <<case "permitted masturbation">>
 
-Strolling through the penthouse late at night, thinking over a business problem, you pass <<if ($activeSlave.ID == $HeadGirl.ID) && ($HGSuite > 0)>>the door of your Head Girl's suite<<elseif ($activeSlave.rules.living == "luxurious")>>the door to one of the cozy little slave bedrooms<<else>>through the cavernous slave dormitory<</if>> and see <<= App.UI.slaveDescriptionDialog($activeSlave)>>, alone in bed tonight. $He's nude, of course, and has not pulled the sheets up over $himself. $He's lying face-down,
+Strolling through the penthouse late at night, thinking over a business problem, you pass <<if ($activeSlave.ID == $HeadGirlID) && ($HGSuite > 0)>>the door of your Head Girl's suite<<elseif ($activeSlave.rules.living == "luxurious")>>the door to one of the cozy little slave bedrooms<<else>>through the cavernous slave dormitory<</if>> and see <<= App.UI.slaveDescriptionDialog($activeSlave)>>, alone in bed tonight. $He's nude, of course, and has not pulled the sheets up over $himself. $He's lying face-down,
 <<if ($activeSlave.boobs > 10000)>>
 	though $his titanic tits prop $his torso up awkwardly,
 <<elseif ($activeSlave.boobs > 1600)>>
@@ -755,7 +755,7 @@ One night, you see <<= App.UI.slaveDescriptionDialog($activeSlave)>> <<if (!hasA
 
 <<case "shift doorframe">>
 
-Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if $Concubine != 0>>Your concubine, $Concubine.slaveName<<elseif $HeadGirl != 0>>Your Head Girl, $HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
+Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if _S.Concubine>>Your concubine, _S.Concubine.slaveName<<elseif $HeadGirlID != 0>>Your Head Girl, _S.HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
 <br><br>
 You're at your desk as $he arrives; $his predecessor passes $him on the way out. $activeSlave.slaveName pauses for a moment in the doorway, and then decides to give you a show. $He spreads $his legs until $his
 <<if $activeSlave.shoes == "heels">>
@@ -1258,7 +1258,7 @@ naked ass catching your eye as $he goes.
 
 <<case "shift masturbation">>
 
-Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if $Concubine != 0>>Your concubine, $Concubine.slaveName<<elseif $HeadGirl != 0>>Your Head Girl, $HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
+Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if _S.Concubine>>Your concubine, _S.Concubine.slaveName<<elseif $HeadGirlID != 0>>Your Head Girl, _S.HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
 <br><br>
 And has $he ever come on shift. $He enters your office at something not far removed from a run, displaying evident signs of sexual excitation, a blush visible on $his $activeSlave.skin cheeks. Between $his job, the mild drugs in $his food, and $his life, $he's beside $himself with need. $He realizes you're working and tries to compose $himself, but gives up after a short struggle and flings $himself down on the couch. $He scoots down so $his <<if $activeSlave.butt > 5>>enormous<<elseif $activeSlave.butt > 2>>healthy<<else>>trim<</if>> butt is hanging off the edge of the cushion, and spreads $his legs up and back<<if $activeSlave.belly >= 5000>> to either side of $his _belly <<if $activeSlave.bellyPreg >= 3000>>pregnant <</if>>belly<</if>> as wide as they'll go<<if ($activeSlave.boobs > 1000)>>, hurriedly shoving $his tits out of the way<</if>>. $He uses both hands to frantically
 <<if ($activeSlave.dick > 0) && !canAchieveErection($activeSlave)>>
@@ -1290,9 +1290,9 @@ but after a moment $he clearly decides this isn't enough stimulation. $He <<if $
 
 <<case "shift sleep">>
 
-Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if $Concubine != 0>>Your concubine, $Concubine.slaveName<<elseif $HeadGirl != 0>>Your Head Girl, $HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
+Your fucktoys have to eat, sleep, and look after themselves, just like anyone, so they can't spend every moment offering themselves to you. <<if _S.Concubine>>Your concubine, _S.Concubine.slaveName<<elseif $HeadGirlID != 0>>Your Head Girl, _S.HeadGirl.slaveName<<elseif $assistant.name == "your personal assistant">>Your personal assistant<<else>>Your personal assistant, <<= capFirstChar($assistant.name)>><</if>> manages a schedule for them, constantly changing it up to keep the sluts from getting predictable. <<= App.UI.slaveDescriptionDialog($activeSlave)>> has just come on shift.
 <br><br>
-Though it's late, $he's surprised to find the lights in the master suite off. You had an unusually trying day, so you've retired for the night; you're on the point of sleep when $he comes in<<if $Concubine != 0>>, $Concubine.slaveName nestled under your arm<</if>>. After a moment's hesitation, $activeSlave.slaveName strips quietly and
+Though it's late, $he's surprised to find the lights in the master suite off. You had an unusually trying day, so you've retired for the night; you're on the point of sleep when $he comes in<<if _S.Concubine>>, _S.Concubine.slaveName nestled under your arm<</if>>. After a moment's hesitation, $activeSlave.slaveName strips quietly and
 <<if $activeSlave.belly >= 100000>>
 	gently lowers $his extremely gravid body onto
 <<elseif $activeSlave.belly >= 10000>>
@@ -1761,56 +1761,6 @@ The suit is quite special. It is made of thick, durable latex with temperature r
 <br><br>
 $He will spend the day restrained in public, with your other slaves periodically stopping by to hydrate $him and wash out $his holes.
 
-<<case "serve the public devoted">>
-
-<<= App.UI.slaveDescriptionDialog($activeSlave)>> is a real public servant. This morning, $he rose early, did $his chores, and looked after $himself. $He heads out past your desk toward the arcology's lower floors to offer $himself freely to everyone $he meets.
-<br><br>
-As $he goes, you notice that
-<<if ($activeSlave.chastityPenis == 1)>>
-	precum is starting to dribble out of $his chastity cage.
-<<elseif ($activeSlave.dick > 0) && canAchieveErection($activeSlave)>>
-	$his cock is so hard it's standing out, perfectly straight.
-<<elseif ($activeSlave.dick > 0) && ($activeSlave.balls > 0) && !canAchieveErection($activeSlave)>>
-	$his semi-hard cock is dribbling precum, a sure sign of a bitch who wants to be buttfucked.
-<<elseif ($activeSlave.dick > 0) && !canAchieveErection($activeSlave)>>
-	$his pathetically soft cock is streaming precum, a sure sign of a bitch who wants to be buttfucked.
-<<elseif ($activeSlave.clit > 0)>>
-	$his big clit is obviously engorged.
-<<elseif ($activeSlave.vaginaLube > 0)>>
-	$his pussy is already soaking wet.
-<<else>>
-	$his cheeks are flushed and $he's panting a little.
-<</if>>
-Apparently, $he can't wait to
-<<if ($activeSlave.fetish == "submissive")>>
-	play the submissive.
-<<elseif ($activeSlave.fetish == "humiliation")>>
-	be publicly degraded.
-<<elseif ($activeSlave.fetish == "masochist")>>
-	be roughly handled.
-<<elseif ($activeSlave.fetish == "dom")>>
-	play the dom.
-<<elseif ($activeSlave.fetish == "sadist")>>
-	find a willing masochist.
-<<elseif ($activeSlave.fetish == "pregnancy") && ($activeSlave.preg > $activeSlave.pregData.normalBirth/4)>>
-	have $his pregnancy fondled.
-<<elseif ($activeSlave.fetish == "pregnancy")>>
-	pretend to get knocked up.
-<<elseif ($activeSlave.fetish == "boobs")>>
-	have $his tits fondled.
-<<elseif ($activeSlave.fetish == "cumslut")>>
-	gorge $himself on cum.
-<<elseif ($activeSlave.fetish == "buttslut")>>
-	spend all day getting pounded in the ass.
-<<elseif ($activeSlave.attrXX > 65)>>
-	enjoy all the women.
-<<elseif ($activeSlave.attrXY > 65)>>
-	enjoy all the men.
-<<else>>
-	be a complete slut.
-<</if>>
-If you went with $him, you could certainly show off a little.
-
 <<case "millenary">>
 
 Late one night, <<= App.UI.slaveDescriptionDialog($activeSlave)>> comes to see you. Strangely, several of your other slaves are stealing glances at $him as $he does. $He seems oddly proud of $himself. Asked why, $he says, "It'<<s>> my millenary, <<Master>>. The arcology has logged me getting fucked <<= num(999)>> time<<s>>." The other slaves obviously view it as significant, too.
@@ -2345,16 +2295,6 @@ As you inspect $him with your hands, $he
 
 <<= App.UI.slaveDescriptionDialog($activeSlave)>>'s daily routine includes frequent application of special skin care to $his $activeSlave.skin, hugely swollen belly to prevent $his pregnancy from ruining $his appearance with unsightly stretch marks. Several times a day, $he visits the bathroom to <<if (!hasAnyArms($activeSlave))>>have another slave<<else>>carefully<</if>> coat $his entire _belly stomach in the stuff. $He's so pregnant that it's hard to reach <<if $activeSlave.belly >= 150000>>most of its mass<<else>>the underside<</if>>. The chore keeps $him occupied and stationary for quite a while; there's no need to leave $him sexually idle while $he completes it.
 
-<<case "slave dick on slave">>
-
-<<setNonlocalPronouns 0>>
-Through the glass walls of your office, you see <<= App.UI.slaveDescriptionDialog($activeSlave)>> fucking another slave with $his <<if $activeSlave.dick <= 2>>small penis<<elseif $activeSlave.dick <= 4>>long cock<<elseif $activeSlave.dick > 4>>huge, horselike phallus<</if>>. Since you gave $him orders that permit $him to fuck your other slaves, $he's been fucking them whenever $he can. The other slave is <<if $activeSlave.dick <= 2>>enjoying _himselfU even though the <<if $seeRace == 1>>$activeSlave.race <</if>>dick in _hisU pussy is a little disappointing<<elseif $activeSlave.dick <= 4>>enjoying the sex<<else>>enjoying the big <<if $seeRace == 1>>$activeSlave.race <</if>>dick, even if _heU does wince from time to time<</if>>. <<if $activeSlave.scrotum > 0>><<if $activeSlave.balls > 3>>As $activeSlave.slaveName pounds, $his big balls slap against $his partner.<<elseif $activeSlave.balls > 1>>As $activeSlave.slaveName pounds, $his balls tighten, preparing to empty themselves.<</if>><</if>>
-
-<<case "slave clit on slave">>
-
-<<setNonlocalPronouns 0>>
-Through the glass walls of your office, you see <<= App.UI.slaveDescriptionDialog($activeSlave)>> fucking another slave. Odd, since $he doesn't have a penis: it seems the other slave likes $him enough to try to make clitoral penetration work. $activeSlave.slaveName's <<if $seeRace == 1>>$activeSlave.race <</if>>clit is certainly big enough to make it possible. Since you gave $him orders that permit $him to fuck your other slaves, $he's been having as much sex with them as $he can. The other slave is enjoying _himselfU even though the clit in _hisU pussy is a little disappointing compared to a real cock.
-
 <<case "PA servant">>
 
 As you begin your day one morning, you hear the quiet
@@ -2449,17 +2389,6 @@ To go by $his behavior, the likelihood that $he's actually eager to <<if $PC.dic
 $desc
 has run through the prescribed motions of a normal inspection, and you've reached the part of the routine where you usually ask the slave a few questions, give $him a few orders, or fuck $him. $His <<if canSee($activeSlave)>><<= App.Desc.eyesColor($activeSlave)>> are watching you closely<<else>>face is centered on you<</if>>, and $he's <<if canHear($activeSlave)>>listening carefully<<else>>waiting patiently<</if>> for direction. This is probably due to linguistic anxiety: $he can understand the most straightforward commands, but giving $him orders is often an exercise in <<if canSee($activeSlave)>>pointing and gesturing, or when that fails, simply<<else>>frustration, ending with you simply<</if>> pushing and pulling $his usually-compliant body into the proper place.
 
-<<case "kitchen molestation">>
-
-<<= App.UI.slaveDescriptionDialog($activeSlave)>> is a horny bitch, and $he isn't particularly picky about how $he gets off. Since other slaves are not allowed to resist $his little molestations, $he's taken to haunting the kitchen around mealtimes. Since everyone has to suck their meals out of the phallic food dispensers, every slave has to spend some minutes of every day in the kitchen with their face to the wall and their rear defenseless. $activeSlave.slaveName does $his best to arrive as early as possible and eat as rapidly as possible so $he can then play with anyone who's slower to finish.
-<br><br>
-You decide to stop by to see $his method at work. By the time you arrive, $he's already eaten and orgasmed at least once. As you spectate, $he
-<<if canPenetrate($activeSlave) && $activeSlave.prostate > 0>>
-	pushes a couple of fingers up $his own ass to use prostate stimulation to force $himself hard again, and once this is accomplished, happily turns to select a slow eater to fuck.
-<<else>>
-	languidly plays with $himself, running $his hand<<if hasBothArms($activeSlave)>>s<</if>> over the various vulnerable butts before picking a victim to roughly finger fuck.
-<</if>>
-
 <<case "forbidden masturbation">>
 
 <<if $assistant.personality > 0>>
@@ -3036,7 +2965,7 @@ in time with $his humming, $he runs $his hand<<if hasBothArms($activeSlave)>>s<<
 <<case "plimb help">>
 
 First thing in the morning,
-<<if $activeSlave.ID == $Bodyguard.ID>>
+<<if $activeSlave.ID == $BodyguardID>>
 	before you've even gotten out of bed, you hear <<= App.UI.slaveDescriptionDialog($activeSlave)>>'s voice coming up from where $he sleeps, on $his own bedroll in your suite. There's a distinctly plaintive note in $his voice, and as soon as you sit up and look in $his direction, it's obvious why. $He sleeps out of $his P-Limbs, since it's good to rest the anchor points implanted into $his torso. They're held in actuators over $his bedroll, which can swing down and attach them to $him the instant $he's awake. Except today, they're staying obstinately up near the ceiling. <<if $assistant.personality != 0>>$assistant.name tells you _heA's<<else>>Your personal assistant informs you _heA's<</if>> troubleshooting the problem, but in the meantime, poor
 <<else>>
 	$assistant.name informs you that there's a minor problem with <<= App.UI.slaveDescriptionDialog($activeSlave)>>, and asks whether you'd like it to be handled without you. $He sleeps out of $his P-Limbs, since it's good to rest the anchor points implanted into $his torso. When $he wakes up, $he usually asks whichever slave is nearest to attach $his dominant arm, at which point $he can do the rest $himself. Due to an unexpected change in the slaves' schedules, though, $he's all alone today. Poor
@@ -3262,95 +3191,6 @@ against the edge of the counter as $he leans forward a little to <<if $activeSla
 
 It's been <<print $week-$activeSlave.weekAcquired>> week<<if $week-$activeSlave.weekAcquired > 1>>s<</if>> since you acquired <<= App.UI.slaveDescriptionDialog($activeSlave)>> as your slave. Since then $he has been nothing but trouble. $He has been ungrateful, rebellious and particularly hateful toward you since you acquired $him. $He often needs to be confined or forced to perform $his assignments, and has little regard for your power or authority. You bring $him in for inspection and $he whines and squirms in resistance as you feel up $his $activeSlave.nipples nipples<<if $activeSlave.belly >= 5000>>, rub $his _belly <<if $activeSlave.bellyPreg >= 3000>>pregnant <</if>>belly<<elseif $activeSlave.bellyPreg >= 150>>, feel the curve of $his new pregnancy<</if>>, and grope $his <<if $activeSlave.butt < 2>>narrow <<elseif $activeSlave.butt < 5>>average <<elseif $activeSlave.butt < 8>>plump <<else>>impressive <</if>>butt, coldly appraising $his potential worth as a sexual service provider. <<if $activeSlave.actualAge < 21>>$He is very young, only $activeSlave.actualAge years old, and is still very naïve. $His smooth, $activeSlave.skin skin has yet to experience the hard years of relentless sexual abuse that lie ahead of $him.<<else>>At $activeSlave.actualAge years old, $he should know by now how the world works, but some slaves are just too stubborn for their own good.<</if>> Eventually, of course, even the most headstrong <<print $girl>>s will bend to your will, but the unique social mores of your arcology and the wonders of modern surgical technology offer a prime opportunity to make a potent example of this <<if $activeSlave.actualAge < 21>>young <<else>>obstinate <</if>>slut. $He is $activeSlave.race, and therefore not considered racially inferior or subject to extreme inequities or neglect, but like most slave characteristics, that can change.
 
-<<case "too thin for cum diet">>
-
-You are in your office, tending to the tedious business of requisition orders for your penthouse when you see <<= App.UI.slaveDescriptionDialog($activeSlave)>> appear outside your door. $He hovers outside in the hall, peeking $his head around the entry-way, looking very unsure about disturbing you. You stop what you're doing and call $him in. $He's reticent at first, but then $he obediently steps into your office. $He is
-<<if $activeSlave.clothes == "no clothing">>
-	forced to remain naked at all times, and the most striking thing about $his body is how
-	<<if $activeSlave.belly >= 100000>>
-		$his _belly <<if $activeSlave.bellyPreg >= 3000>>pregnancy<<else>>distended belly<</if>> utterly dwarfs $his skinny body.
-	<<elseif $activeSlave.belly >= 15000>>
-		$his <<if $activeSlave.bellyPreg >= 3000>>full pregnancy<<else>>hugely distended belly<</if>> completely dominates $his skinny body.
-	<<elseif $activeSlave.belly >= 10000>>
-		$his <<if $activeSlave.bellyPreg >= 3000>>advanced pregnancy<<else>>hugely distended belly<</if>> dominates $his skinny body.
-	<<elseif $activeSlave.belly >= 5000>>
-		massive $his <<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>distended<</if>> belly is compared to $his skinny body.
-	<<elseif $activeSlave.belly >= 1500>>
-		noticeable <<if $activeSlave.bellyPreg > 0>>$his growing pregnancy<<else>>the curve of $his belly<</if>> is against $his skinny body.
-	<<elseif $activeSlave.belly >= 150>>
-		noticeable <<if $activeSlave.bellyPreg > 0>>$his early pregnancy<<else>>the curve of $his implant<</if>> is against $his skinny body.
-	<<else>>
-		skinny $he is.
-	<</if>>
-<<else>>
-	forced to wear $activeSlave.clothes all day, but even through $his outfit, it's easy to see how
-	<<if $activeSlave.belly >= 100000>>
-		$his _belly <<if $activeSlave.bellyPreg >= 3000>>pregnancy<<else>>distended belly<</if>> utterly dwarfs $his skinny body.
-	<<elseif $activeSlave.belly >= 15000>>
-		$his <<if $activeSlave.bellyPreg >= 3000>>heavy pregnancy<<else>>hugely distended belly<</if>> completely dominates $his skinny body.
-	<<elseif $activeSlave.belly >= 10000>>
-		$his <<if $activeSlave.bellyPreg >= 3000>>advanced pregnancy<<else>>hugely distended belly<</if>> dominates $his skinny body.
-	<<elseif $activeSlave.belly >= 5000>>
-		massive $his <<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>distended<</if>> belly is compared to $his skinny body.
-	<<elseif $activeSlave.belly >= 1500>>
-		noticeable <<if $activeSlave.bellyPreg > 0>>$his growing pregnancy<<else>>the curve of $his belly<</if>> is against $his skinny body.
-	<<elseif $activeSlave.belly >= 150>>
-		noticeable <<if $activeSlave.bellyPreg > 0>>$his early pregnancy<<else>>the curve of $his implant<</if>> is against $his skinny body.
-	<<else>>
-		impossibly skinny $he is.
-	<</if>>
-<</if>>
-<<if $activeSlave.weight > -80>>
-	$He's not exclusively skin and bones, but $he's close.
-<<else>>
-	$He's so thin that $he doesn't look entirely healthy.
-<</if>>
-$His
-<<if $activeSlave.boobs < 600>>
-	<<if $activeSlave.boobs < 300>>
-		tiny
-	<<elseif $activeSlave.boobs < 600>>
-		small
-	<</if>>
-	boobs are barely noticeable
-	<<if $activeSlave.belly >= 5000>>
-		above $his bloated
-	<<else>>
-		against $his concave
-	<</if>>
-	tummy, and
-	<<if $arcologies[0].FSSlimnessEnthusiast != "unset">>
-		that can be a very appealing look given the predilection your arcology has for slim <<= $girl>>s.
-	<<else>>
-		to most clients, this makes $him a less appealing whore.
-	<</if>>
-<<else>>
-	<<if $activeSlave.boobs < 900>>
-		large
-	<<else>>
-		massive
-	<</if>>
-	tits are a sharp contrast to $his
-	<<if $activeSlave.belly >= 150>>
-		thin, bloated frame,
-	<<else>>
-		concave belly,
-	<</if>>
-	and although impressive, they seem to especially stick out on a $girl who, in all other ways, seems
-	<<if $activeSlave.weight > -80>>
-		slightly
-	<<else>>
-		alarmingly
-	<</if>>
-	malnourished.
-<</if>>
-$His $activeSlave.faceShape face is clearly conflicted, and <<if canSee($activeSlave)>>$his <<= App.Desc.eyesColor($activeSlave)>> shift<<else>>$he glances about<</if>> with nervous energy. $He is clearly unsure whether $he should say what $he came here to say.
-<br><br>
-When you finally ask $him what $he wants, $he hesitates for a moment and then suddenly seems to gain $his courage<<if $activeSlave.accent > 1>>, speaking in an atrociously thick accent<<elseif $activeSlave.accent > 0>>, speaking in a cute little accent<</if>>.
-"Plea<<s>>e, <<Master>> I'm <<s>>o hungry! It <<s>>eem<<s>> like all I get to eat i<<s>> cum. It'<<s>> di<<s>>gu<<s>>ting! Plea<<s>>e! I have to eat <<s>>o much of it. Can I be allowed to eat regular food again? Plea<<s>>e, <<Master>>, I'm <<s>>o hungry. I need real food!"
-<br><br>
-It's true, cum <<if $activeSlave.dietCum == 1>>supplemented <<elseif $activeSlave.dietCum == 2>>based <</if>>food can be hard on <<print $girl>>s who are not fully habituated to being toys for sexual amusement — particularly when you've ordered them to gain weight on it. You look at the<<if $activeSlave.belly >= 1500>> gravid,<</if>> skinny whore and consider your options.
-
 <<case "transition anxiety">>
 
 <<= App.UI.slaveDescriptionDialog($activeSlave)>> stumbles into your office naked for $his weekly inspection, so apprehensive that $he can hardly walk. $He's been like this ever since $he became one of your sex slaves, <<if $week-$activeSlave.weekAcquired == 0>>just this week<<elseif $week-$activeSlave.weekAcquired == 1>>just last week<<else>><<print $week-$activeSlave.weekAcquired>> weeks ago<</if>>. It's not surprising; slaves like $him usually require some time and training to accept that they're slavegirls. $He lacks the natural attraction to men that might have made the idea more comfortable for $him, and the resulting sexual anxiety combined with understandable fear of sexual use makes $him extremely unhappy to be naked in front of someone $he knows can fuck $him at will.
@@ -3587,15 +3427,6 @@ As another long week draws to a close, <<= App.UI.slaveDescriptionDialog($active
 <</if>>
 
 
-<<case "devoted educated slave">>
-
-<<= App.UI.slaveDescriptionDialog($activeSlave)>> comes before you for a routine inspection. The $desc is a well-educated and obedient slave. Though $he performs $his duties devotedly and to the best of $his abilities, slave life is not particularly conducive to straining an individual's brainpower. You happen to run into $activeSlave.slaveName in the hallways of the penthouse, where $he takes the opportunity to wordlessly signal $he wishes to gain your attention.
-<<if !canTalk($activeSlave)>>
-	$He uses gestures to beg your pardon and explains that while $he enjoys life as your slave, $he doesn't feel like $his new role in your arcology allows $him to stimulate $his mind as often as it does $his body.
-<<else>>
-	"<<Master>>," $he <<say>>s. "I really enjoy my role a<<s>> your <<s>>lave, but I ju<<s>>t don't feel like my new life <<s>>timulate<<s>> me." $He blushes prettily at $his choice of words before continuing, "<<S>>timulate my mind, I mean."
-<</if>>
-
 <<case "nice guys">>
 
 One afternoon, $assistant.name informs you $he that $he has a non-urgent matter that you might be interested in overseeing personally.
@@ -4023,7 +3854,7 @@ You are alerted to an issue regarding your
 the poor $girl has gotten lodged in the doorway exiting
 <<if ($activeSlave.rules.living == "luxurious")>>
 	$his room.
-<<elseif ($activeSlave.ID == $HeadGirl.ID) && ($HGSuite == 1)>>
+<<elseif ($activeSlave.ID == $HeadGirlID) && ($HGSuite == 1)>>
 	$his suite.
 <<else>>
 	the dormitory where $he sleeps.
@@ -4047,7 +3878,7 @@ You make a mental note to punish $his caretakers and then set out to see $his pr
 The sight that greets you when you arrive is equal parts incredible and amusing. En route to the baths, the slave attempted to exit
 <<if ($activeSlave.rules.living == "luxurious")>>
 	$his room
-<<elseif ($activeSlave.ID == $HeadGirl.ID) && ($HGSuite == 1)>>
+<<elseif ($activeSlave.ID == $HeadGirlID) && ($HGSuite == 1)>>
 	$his suite
 <<else>>
 	the dorm
@@ -4919,15 +4750,15 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <</link>>
 <br><<link "Call out for help">>
 	<<replace "#result">>
-		$He is far stronger than you expected and has you trapped in a compromising position; you shout out for <<if $Bodyguard != 0>>$Bodyguard.slaveName<<elseif $Concubine != 0 && canWalk($Concubine)>>$Concubine.slaveName<<else>>somebody<</if>> to help you. You've seen $activeSlave.slaveName fuck before; $he's a quick shot, only in it to get $his mate pregnant. You question if anyone is coming as $his gigantic dick pokes at your crotch, eager to find the egg at the end of the tunnel. $He lacks even the basic understanding of foreplay, you realize, as $he drives $his cock deep into your pussy. You groan with pain at the sheer size of the rod stretching out your poor hole and struggle to hold back the tears once $he starts thrusting. There is no pleasure for you here as $he batters your cervix; you barely stop yourself from screaming out as $he slams through your final defense
-		<<if $Bodyguard != 0>>
-			<<setLocalPronouns $Bodyguard 2>>
-			and gets tackled off of you by $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. $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 $slaves[$slaveIndices[$Bodyguard.ID]].devotion += 2>>
-		<<elseif $Concubine != 0 && canWalk($Concubine)>>
-			and gets tackled off of you by $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. $Concubine.slaveName is @@.gold;visibly shaken@@ by the assault and was @@.red;badly beaten@@ by the muscular slave during the fight.
-			<<set $slaves[$slaveIndices[$Concubine.ID]].trust -= 5>>
-			<<run healthDamage($slaves[$slaveIndices[$Concubine.ID]], 40)>>
+		$He is far stronger than you expected and has you trapped in a compromising position; you shout out for <<if _S.Bodyguard>>_S.Bodyguard.slaveName<<elseif _S.Concubine && canWalk(_S.Concubine)>>_S.Concubine.slaveName<<else>>somebody<</if>> to help you. You've seen $activeSlave.slaveName fuck before; $he's a quick shot, only in it to get $his mate pregnant. You question if anyone is coming as $his gigantic dick pokes at your crotch, eager to find the egg at the end of the tunnel. $He lacks even the basic understanding of foreplay, you realize, as $he drives $his cock deep into your pussy. You groan with pain at the sheer size of the rod stretching out your poor hole and struggle to hold back the tears once $he starts thrusting. There is no pleasure for you here as $he batters your cervix; you barely stop yourself from screaming out as $he slams through your final defense
+		<<if _S.Bodyguard>>
+			<<setLocalPronouns _S.Bodyguard 2>>
+			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.
+			<<set _S.Concubine.trust -= 5>>
+			<<run healthDamage(_S.Concubine, 40)>>
 		<<else>>
 			and cums directly into your exposed womb. $He backs off, possibly startled by the shouting, giving you the chance to slip away to safety. 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. Still, $he knew you were fertile and went right for the prize; it would be wise to assume $he's done $his job well.
 			<<= knockMeUp($PC, 50, 0, $activeSlave.ID)>>
@@ -4940,21 +4771,21 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		$He is far stronger than you expected and has you trapped in a compromising position. You've seen $activeSlave.slaveName fuck before; $he's a quick shot, only in it to get $his mate pregnant. You question what you are thinking as $his gigantic dick pokes at your crotch, eager to find the egg at the end of the tunnel. $He lacks even the basic understanding of foreplay, you realize, as $he drives $his cock deep into your pussy. You groan with pain at the sheer size of the rod stretching out your poor hole and struggle to hold back the tears once $he starts thrusting. There is no pleasure for you here as $he batters your cervix; you barely stop yourself from screaming out as $he slams through your final defense and cums directly into your exposed womb.
 		<br><br>
 		You immediately realize you've made a mistake; you feel full already and $he's still cumming. You watch in horror as your belly steadily swells with seed, unable to flow out thanks to your obstructed cunt. $activeSlave.slaveName grunts as $he pushes out the last of $his load before pulling out and releasing you. You try to get up, but the experience has left you drained, so you are left hoping that $his instincts tell $him $he's done $his job. $He lowers $his <<if canSmell($activeSlave)>>nose to your violated pussy and sniffs<<else>>face to your violated pussy to feel the heat emanating from it<</if>>; unsatisfied, $he moves back into position to properly seed you this time. You black out as $his second load joins the first, only to awaken sometime later
-		<<if $Concubine != 0>>
-			<<setLocalPronouns $Concubine 2>>
-			with $Concubine.slaveName trying _his2 best to drain your swollen belly.
-			<<if canTalk($Concubine)>>
-				<<run Enunciate($Concubine)>>
+		<<if _S.Concubine>>
+			<<setLocalPronouns _S.Concubine 2>>
+			with _S.Concubine.slaveName trying _his2 best to drain your swollen belly.
+			<<if canTalk(_S.Concubine)>>
+				<<run Enunciate(_S.Concubine)>>
 				"I'm <<s>>o <<s>>orry <<Master>>... I <<s>>tepped out for a minute and thi<<s>> happen<<s>>." You ask what happened to $activeSlave.slaveName. "$He'<<s>> bound and gagged now, $he won't hurt you anymore. But what are you going to do with that belly? I won't tell anyone what happened, but we've got to get rid of it."
 			<<else>>
 				_He2 gestures _his2 apologies and points to the bound and gagged $activeSlave.slaveName, before expressing _his2 concerns about your visible state.
 			<</if>>
 			You do your best to calm _him2 down; this is your fault after all. $activeSlave.slaveName's a breeding bull and you a fertile _womanP. $He only did what $he was conditioned for; the blame is on you for ignoring the warning signs and not taking the proper precautions. Still, there is no doubt in your mind that @@.lime;you're pregnant@@ with $his child. As for the belly, it'll go down soon, you're sure.
-		<<elseif $Bodyguard != 0>>
-			<<setLocalPronouns $Bodyguard 2>>
-			with $Bodyguard.slaveName sobbing over your swollen belly.
-			<<if canTalk($Bodyguard)>>
-				<<run Enunciate($Bodyguard)>>
+		<<elseif _S.Bodyguard>>
+			<<setLocalPronouns _S.Bodyguard 2>>
+			with _S.Bodyguard.slaveName sobbing over your swollen belly.
+			<<if canTalk(_S.Bodyguard)>>
+				<<run Enunciate(_S.Bodyguard)>>
 				"I'm <<s>>o <<s>>orry <<Master>>... I <<s>>tepped out for a minute and thi<<s>> happen<<s>>." You ask what happened to $activeSlave.slaveName. "$He'<<s>> bound and gagged now, $he won't hurt you again." _He2 sniffles, "But plea<<s>>e puni<<sh>> me too. Thi<<s>> i<<s>> all my fault."
 			<<else>>
 				_He2 gestures _his2 apologies and points to the bound and gagged $activeSlave.slaveName, before begging you to punish _him2 too for _his2 failures as your guardian.
@@ -5438,9 +5269,9 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <br><<link "Bring your good slaves out for a day at the beach">>
 	<<replace "#result">>
 		You direct $assistant.name to bring your slaves who deserve it out for a day at the beach. There might be little sand out on the balcony, but there's plenty of warm sun. And beaches are overrated these days anyway, with the ocean becoming so unpredictable. <<= capFirstChar($assistant.name)>> advises them to get naked, and bring towels and sun lotion appropriate for their individual skin types, so they know they're going to get some sunshine, but they're surprised when they get outside. They're obviously expecting an orgy of some kind, but all they see is you and $activeSlave.slaveName luxuriating on the decking. As they arrive, they take the cue and line up with you one by one. After a long sunbathing session, you send a couple of them inside to bring out cool drinks, a beach ball, and other essentials. The hornier slaves are doubtful for a while, but eventually they all relax and @@.mediumaquamarine;gain confidence@@ from the simple, nonsexual pleasure of a little time in the sun.
-		<<if ($activeSlave.ID != $HeadGirl.ID) && ($HeadGirl != 0)>>
-			<<setLocalPronouns $HeadGirl 2>>
-			Your Head Girl $HeadGirl.slaveName quietly compliments the idea, and asks you how it came to you. You let _him2 know it was $activeSlave.slaveName's idea,
+		<<if ($activeSlave.ID != $HeadGirlID) && ($HeadGirlID != 0)>>
+			<<setLocalPronouns _S.HeadGirl 2>>
+			Your Head Girl _S.HeadGirl.slaveName quietly compliments the idea, and asks you how it came to you. You let _him2 know it was $activeSlave.slaveName's idea,
 		<<else>>
 			Another slave thanks you profusely, and politely asks you what gave you such a wonderful idea. You let them know it was $activeSlave.slaveName,
 		<</if>>
@@ -7666,43 +7497,42 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<set $activeSlave.trust += 4>>
 	<</replace>>
 <</link>>
-<<if ($HeadGirl != 0) && ($HeadGirl.ID != $activeSlave.ID)>>
+<<if $HeadGirlID != $activeSlave.ID>>
 	<br><<link "Call your Head Girl in for double anal">>
-		<<setLocalPronouns $HeadGirl 2>>
+		<<setLocalPronouns _S.HeadGirl 2>>
 		<<replace "#result">>
-			When $HeadGirl.slaveName comes into your office in response to your summons, _he2 finds $activeSlave.slaveName sitting in your lap with your <<if $PC.dick == 0>>strap-on<<else>>dick<</if>> up $his gaping butt<<if $PC.vagina != -1>><<if $PC.dick != 0>>, your bare pussy very visible at the base of your working cock<</if>><</if>>. $HeadGirl.slaveName's expression softens when _he2 realizes _he2's here for pleasure, not business. $activeSlave.slaveName gasps a little when $he <<if canHear($activeSlave)>>hears you tell $HeadGirl.slaveName to join you up $his asshole,<<else>>feels you pull apart $his asscheeks to make some room for $HeadGirl.slaveName,<</if>> but $he doesn't protest.
+			When _S.HeadGirl.slaveName comes into your office in response to your summons, _he2 finds $activeSlave.slaveName sitting in your lap with your <<if $PC.dick == 0>>strap-on<<else>>dick<</if>> up $his gaping butt<<if $PC.vagina != -1>><<if $PC.dick != 0>>, your bare pussy very visible at the base of your working cock<</if>><</if>>. _S.HeadGirl.slaveName's expression softens when _he2 realizes _he2's here for pleasure, not business. $activeSlave.slaveName gasps a little when $he <<if canHear($activeSlave)>>hears you tell _S.HeadGirl.slaveName to join you up $his asshole,<<else>>feels you pull apart $his asscheeks to make some room for _S.HeadGirl.slaveName,<</if>> but $he doesn't protest.
 			<<if ($activeSlave.chastityPenis == 1)>>
 				Since your poor Head Girl can't use _his2 caged cock, _he2 takes a dildo and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
-			<<elseif canAchieveErection() && $HeadGirl.dick > 7>>
+			<<elseif canAchieveErection() && _S.HeadGirl.dick > 7>>
 				Even though your Head Girl is rock-hard and ready to fuck, _his2 cock is far too large to fit into even the most stretched slave's holes. Sighing, _he2 takes a dildo and shoves it up $activeSlave.slaveName's already-filled butt instead.
-			<<elseif canAchieveErection() && $HeadGirl.dick > 6>>
+			<<elseif canAchieveErection() && _S.HeadGirl.dick > 6>>
 				Your lusty Head Girl is already hard and forces _his2 oversized cock up $activeSlave.slaveName's already-filled butt while <<if $activeSlave.nipples != "fuckable">>tweaking<<else>>fingering<</if>> the moaning slave's nipples.
-			<<elseif ($HeadGirl.dick > 0) && ($HeadGirl.hormoneBalance >= 100)>>
+			<<elseif (_S.HeadGirl.dick > 0) && (_S.HeadGirl.hormoneBalance >= 100)>>
 				Since your poor Head Girl can't get hard due to _his2 hormone therapy, _he2 dons a strap-on over _his2 flaccid penis and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
-			<<elseif ($HeadGirl.dick > 0) && ($HeadGirl.balls > 0) && ($HeadGirl.ballType == "sterile")>>
+			<<elseif (_S.HeadGirl.dick > 0) && (_S.HeadGirl.balls > 0) && (_S.HeadGirl.ballType == "sterile")>>
 				Since your poor Head Girl can't get hard due to _his2 chemical castration, _he2 dons a strap-on over _his2 flaccid penis and shoves it up $activeSlave.slaveName's already-filled butt without further ado
-			<<elseif ($HeadGirl.dick > 0) && ($HeadGirl.balls == 0)>>
+			<<elseif (_S.HeadGirl.dick > 0) && (_S.HeadGirl.balls == 0)>>
 				Since your poor Head Girl can't get hard due to _his2 orchiectomy, _he2 dons a strap-on over _his2 flaccid penis and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
-			<<elseif !canAchieveErection($HeadGirl) && $HeadGirl.dick > 6>>
+			<<elseif !canAchieveErection(_S.HeadGirl) && _S.HeadGirl.dick > 6>>
 				Since your poor Head Girl is far too big to get hard, much to $activeSlave.slaveName's disappointment, _he2 dons a strap-on over _his2 flaccid penis and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
-			<<elseif !canAchieveErection($HeadGirl) && $HeadGirl.dick > 0>>
+			<<elseif !canAchieveErection(_S.HeadGirl) && _S.HeadGirl.dick > 0>>
 				Since your poor Head Girl can't get it up for one reason or another, _he2 dons a strap-on over _his2 flaccid penis and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
-			<<elseif $HeadGirl.dick > 0>>
+			<<elseif _S.HeadGirl.dick > 0>>
 				Your lusty Head Girl is already hard and shoves _himself2 up $activeSlave.slaveName's already-filled butt while <<if $activeSlave.nipples != "fuckable">>tweaking<<else>>fingering<</if>> the writhing slave's nipples.
 			<<else>>
 				_He2 dons a strap-on and shoves it up $activeSlave.slaveName's already-filled butt without further ado.
 			<</if>>
-			The two of you jackhammer in and out of $activeSlave.slaveName's ass without mercy; the poor anal whore does $his best to relax, but two phalli at once is a lot, even for $him. $He's only allowed an anal respite when $his sphincter is really fucked out and there's little butthole fun to be had from $him any longer. $He has become @@.hotpink;more submissive to you,@@ and $HeadGirl.slaveName @@.hotpink;enjoyed@@ taking a break to fuck $him with you.
-			<<set $activeSlave.devotion += 4, $HeadGirl.devotion += 4>>
+			The two of you jackhammer in and out of $activeSlave.slaveName's ass without mercy; the poor anal whore does $his best to relax, but two phalli at once is a lot, even for $him. $He's only allowed an anal respite when $his sphincter is really fucked out and there's little butthole fun to be had from $him any longer. $He has become @@.hotpink;more submissive to you,@@ and _S.HeadGirl.slaveName @@.hotpink;enjoyed@@ taking a break to fuck $him with you.
+			<<set $activeSlave.devotion += 4, _S.HeadGirl.devotion += 4>>
 			<<run seX($activeSlave, "anal", $PC, "penetrative")>>
-			<<run seX($activeSlave, "anal", $HeadGirl, "penetrative")>>
+			<<run seX($activeSlave, "anal", _S.HeadGirl, "penetrative")>>
 			<<if canImpreg($activeSlave, $PC)>>
 				<<= knockMeUp($activeSlave, 5, 0, -1, 1)>>
 			<</if>>
-			<<if canImpreg($activeSlave, $HeadGirl)>>
-				<<= knockMeUp($activeSlave, 5, 0, $HeadGirl.ID, 1)>>
+			<<if canImpreg($activeSlave, _S.HeadGirl)>>
+				<<= knockMeUp($activeSlave, 5, 0, $HeadGirlID, 1)>>
 			<</if>>
-			<<set $slaves[$slaveIndices[$HeadGirl.ID]] = $HeadGirl>>
 		<</replace>>
 	<</link>>
 <</if>>
@@ -8629,14 +8459,14 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<run improveCondition($activeSlave, 10)>>
 	<</replace>>
 <</link>>
-<<if ($HeadGirl != 0) && ($HeadGirl.ID != $activeSlave.ID)>>
+<<if $HeadGirlID != $activeSlave.ID>>
 	<br><<link "Give $him an afternoon off for some quality time with your Head Girl">>
-		<<setLocalPronouns $HeadGirl 2>>
+		<<setLocalPronouns _S.HeadGirl 2>>
 		<<replace "#result">>
-			$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 @@.green;feels much better@@ after the rest, too.
 			<<set $activeSlave.devotion += 4>>
 			<<run cashX(forceNeg(500), "event", $activeSlave), improveCondition($activeSlave, 10)>>
-			<<set $slaves[$slaveIndices[$HeadGirl.ID]].devotion += 4>>
+			<<set _S.HeadGirl.devotion += 4>>
 		<</replace>>
 	<</link>> //This option will cost <<print cashFormat(500)>>//
 <</if>>
@@ -10705,115 +10535,6 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<</link>>
 <</if>>
 
-<<case "serve the public devoted">>
-
-<<if canDoAnal($activeSlave)>>
-	<<link "Share $his body with the public">>
-		<<replace "#result">>
-			<<if hasAnyLegs($activeSlave)>>
-				$activeSlave.slaveName is surprised to find you walking beside $him, but obediently falls in behind you as a proper slave should.
-			<<else>>
-				You scoop $activeSlave.slaveName's limbless form up and carry $him out.
-			<</if>>
-			You take $him to a lovely open balcony and seat yourself on a bench surrounded by the lush greenery and flowing water of $arcologies[0].name's food systems. You pull $his ass down onto your <<if $PC.dick == 0>>strap-on<<else>>rigid cock<</if>> so $he can offer everything else to the public.<<if $PC.vagina != -1>><<if $PC.dick != 0>> They know not to presume to use the pussy located beneath your thrusting cock.<</if>><</if>>
-			<<if canDoVaginal($activeSlave)>>
-				For several hours, citizens come and go, most choosing to fuck $his wet and available pussy. You climax repeatedly from the <<if $PC.dick == 0>>titillating nature of<<else>>extra fullness of $his butt during<</if>> double penetration, and by the time you're finished $he's dripping ejaculate from both $his holes.
-			<<elseif $activeSlave.belly >= 120000>>
-				Since
-				<<if $activeSlave.bellyPreg >= 3000>>
-					$he's so enormously pregnant that $his _belly stomach blocks $his crotch
-				<<else>>
-					$his _belly stomach is so massive that is blocks $his crotch
-				<</if>>
-				and you're wearing $his backdoor around your <<if $PC.dick == 0>>strap-on<<else>>cock<</if>>, $his mouth is all that's left. $He gives so many blowjobs by the time you're finished that $his face, hair, chest and belly are liberally spattered with cum.
-			<<elseif ($activeSlave.chastityVagina)>>
-				Since $he's wearing a chastity belt and you're wearing $his backdoor around your <<if $PC.dick == 0>>strap-on<<else>>cock<</if>>, $his mouth is all that's left. $He gives so many blowjobs by the time you're finished that $his face, hair, chest and <<if $activeSlave.belly >= 5000>><<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>_belly<</if>> belly<<else>>even stomach<</if>> are liberally spattered with cum.
-			<<else>>
-				With your cock pumping $his butt as much as you can manage with $him seated in your lap, $his <<if $activeSlave.dick != 0>>dick flops up and down<<if $activeSlave.belly >= 5000>> against the bottom of $his rounded stomach<</if>><<else>>tiny front hole begins to glisten with the promise of a messy little orgasm<</if>>, lewdly advertising $his sexual availability. $He gives so many blowjobs by the time you're finished that $his face, hair, chest and <<if $activeSlave.belly >= 5000>><<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>_belly<</if>> belly<<else>>even stomach<</if>> are liberally spattered with cum.
-			<</if>>
-			The public is sure to remember this spectacle; @@.green;your reputation has increased.@@
-			<<run repX(500, "event", $activeSlave)>>
-			<<if canDoVaginal($activeSlave)>>
-				<<run seX($activeSlave, "vaginal", "public", "penetrative", 5)>>
-				<<if $activeSlave.vagina == 0>>
-					They have also @@.lime;broken in $activeSlave.slaveName's virgin pussy.@@
-					<<set $activeSlave.vagina = 1>>
-				<</if>>
-				<<if canGetPregnant($activeSlave) && $activeSlave.eggType == "human">>
-					<<= knockMeUp($activeSlave, 10, 0, -2)>>
-				<</if>>
-			<<else>>
-				<<run seX($activeSlave, "oral", "public", "penetrative", 5)>>
-			<</if>>
-			<<= VCheck.Anal(5)>>
-		<</replace>>
-	<</link>>
-<</if>>
-<<if canDoVaginal($activeSlave) || canDoAnal($activeSlave)>>
-	<br><<link "Give $him some aftercare">>
-		<<replace "#result">>
-			$activeSlave.slaveName returns many hours later. $He carefully took breaks for $his own safety <<if $activeSlave.preg > $activeSlave.pregData.normalBirth/1.33>>,especially so given $his advanced pregnancy<</if>>, and cleaned $himself periodically, so there isn't much evidence, but $he's obviously bone tired. When you meet $him at the entrance to your penthouse $he's surprised to <<if canSee($activeSlave)>>see<<else>>find<</if>> you, but $he gives you a little smile anyway. You give $him a strong massage and put $him to bed. Afterward you turn to be about your business, but $he wordlessly offers $himself to you as you do.
-			<<if canDoVaginal($activeSlave)>>
-				<<if $activeSlave.vagina == 0>>
-					$His virgin pussy is certainly tempting.
-				<<elseif ($activeSlave.vagina == 1)>>
-					$His tight pussy is certainly tempting.
-				<<elseif ($activeSlave.vagina == 2)>>
-					$His soft pussy is certainly tempting.
-				<<elseif ($activeSlave.labia > 0)>>
-					$His generous petals are certainly tempting.
-				<<elseif ($activeSlave.clit > 0)>>
-					$His prominent clit is certainly eye-catching.
-				<<elseif ($activeSlave.vagina == -1)>>
-					$His relaxed anus is certainly inviting.
-				<<else>>
-					$His capacious pussy is certainly inviting.
-				<</if>>
-			<<else>>
-				<<if $activeSlave.anus == 0>>
-					$His virgin asshole is certainly tempting.
-				<<elseif ($activeSlave.anus == 1)>>
-					$His tight asshole is certainly tempting.
-				<<elseif ($activeSlave.anus == 2)>>
-					$His experienced asshole is certainly tempting.
-				<<else>>
-					$His capacious asshole is certainly inviting.
-				<</if>>
-			<</if>>
-			$He's sore, so you spoon $his<<if $activeSlave.belly >= 5000>> <<if $activeSlave.bellyPreg >= 3000>>gravid<<else>>rounded<</if>> body<</if>> gently in bed, fucking $him slowly to sleep.
-			<<if canDoVaginal($activeSlave)>>
-				<<= VCheck.Vaginal()>>
-			<<else>>
-				<<= VCheck.Anal()>>
-			<</if>>
-			$He falls asleep with a serene expression on $his face. @@.mediumaquamarine;$His trust in you has increased.@@
-			<<set $activeSlave.trust += 4>>
-		<</replace>>
-	<</link>>
-<</if>>
-<br><<link "Chat about $his day">>
-	<<replace "#result">>
-		$activeSlave.slaveName returns many hours later. $He's obviously bone tired. When you meet $him at the entrance to your penthouse $he's surprised to <<if canSee($activeSlave)>>see<<else>>find<</if>> you, but $he gives you a little smile anyway. You bring $him back to your office, and $he's clearly expecting to get fucked, so $he's surprised when you hand $him a hot beverage and sit down on the couch with $him. $He relaxes quickly and chats with you about $his day, gossiping about all the private doings of all the prominent citizens who fucked $his
-		<<if ($activeSlave.vagina > 0) && canDoVaginal($activeSlave)>>
-			cunt
-		<<elseif ($activeSlave.anus > 0) && canDoAnal($activeSlave)>>
-			asshole
-		<<else>>
-			mouth
-		<</if>>
-		today.
-		<<if ($activeSlave.intelligence+$activeSlave.intelligenceImplant > 50)>>
-			$He's witty and holds up $his end of the conversation without straying from $his role as a slave.
-		<<elseif ($activeSlave.intelligence+$activeSlave.intelligenceImplant >= -15)>>
-			$He has a few juicy items to share, and even gossiping, $he's mindful of $his role as a slave.
-		<<else>>
-			$He may be an idiot, but $his babble is amusing enough.
-		<</if>>
-		Time flies, and when you finally stand up to continue with your evening, $he thanks you prettily for listening to $him <<if $activeSlave.belly >= 10000>>as you help $his <<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>heavy<</if>> body off the couch<</if>>. @@.hotpink;$His devotion to you has increased.@@
-		<<set $activeSlave.devotion += 4>>
-	<</replace>>
-<</link>>
-
 <<case "millenary">>
 
 <<if canDoAnal($activeSlave) || canDoVaginal($activeSlave)>>
@@ -11256,10 +10977,10 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <</link>>
 <br><<link "These slaves clearly have too much time on their hands">>
 	<<replace "#result">>
-		<<if $HeadGirl != 0>>
-			<<setLocalPronouns $HeadGirl 2>>
+		<<if $HeadGirlID != 0>>
+			<<setLocalPronouns _S.HeadGirl 2>>
 		<</if>>
-		You thank $him kindly, tell $him to put $his offering on your desk, and go back to your work. $He flees, and once $he's gone, you <<if $HeadGirl != 0>>have $assistant.name pass a note to your Head Girl instructing _him2<<else>>instruct $assistant.name<</if>> to tighten up your slaves' schedules, since you feel that they have too much time on their hands. Then you direct another slave to return the napkin to its proper place. Your slaves find their spare moments occupied by household minutiae during the coming week, @@.yellowgreen;saving you some money@@ on penthouse and slave upkeep.
+		You thank $him kindly, tell $him to put $his offering on your desk, and go back to your work. $He flees, and once $he's gone, you <<if $HeadGirlID != 0>>have $assistant.name pass a note to your Head Girl instructing _him2<<else>>instruct $assistant.name<</if>> to tighten up your slaves' schedules, since you feel that they have too much time on their hands. Then you direct another slave to return the napkin to its proper place. Your slaves find their spare moments occupied by household minutiae during the coming week, @@.yellowgreen;saving you some money@@ on penthouse and slave upkeep.
 		<<run cashX(2000, "event", $activeSlave)>>
 	<</replace>>
 <</link>>
@@ -12566,86 +12287,6 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<</link>><<if canDoVaginal($activeSlave) && ($activeSlave.vagina == 0) && $activeSlave.mpreg == 0>>//This option will take $his virginity//<<elseif !canDoVaginal($activeSlave) && ($activeSlave.anus == 0)>> //This option will take $his anal virginity//<</if>>
 <</if>>
 
-<<case "slave dick on slave">>
-
-<<link "Fuck the bottom">>
-	<<replace "#result">>
-		Since the other slave is riding $activeSlave.slaveName's <<if $seeRace == 1>>$activeSlave.race <</if>>dick, it's a trivial matter to<<if $PC.dick == 0>> don a strap-on,<</if>> come up behind the fucking slaves, stop the other slave's riding for a moment, and insert yourself into _hisU anus. The other slave
-		<<if $activeSlave.dick <= 2>>
-			gasps as your <<if $PC.dick == 0>>strap-on complements the small dick<<else>>big dick complements the small one<</if>> in $his pussy.
-		<<elseif $activeSlave.dick < 4>>
-			shrieks as _heU feels _hisU holes stretched by <<if $PC.dick == 0>>a strap-on and a cock<<else>>two cocks<</if>>.
-		<<else>>
-			struggles and begs for mercy as _hisU holes are brutally stretched.
-		<</if>>
-		The poor slave rides out the sexual storm as you and $activeSlave.slaveName fuck _himU<<if $PC.vagina != -1>>, your pussy sliding against the base of $activeSlave.slaveName's thrusting shaft<</if>>. $activeSlave.slaveName flirts outrageously with you over the other slave's shoulder whenever $he can. @@.mediumaquamarine;$He has become more trusting of you.@@
-		<<set $activeSlave.trust += 4>>
-		<<run seX($activeSlave, "penetrative", "slaves", "anal")>>
-		<<run seX($PC, "penetrative", "slaves", "anal")>>
-	<</replace>>
-<</link>>
-<<if canDoAnal($activeSlave)>>
-	<br><<link "Fuck the top">>
-		<<replace "#result">>
-			Since $activeSlave.slaveName is on top, it's a trivial matter to<<if $PC.dick == 0>> don a strap-on,<</if>> come up behind the fucking slaves, stop $his thrusting for a moment, and penetrate $his butthole.
-			<<if ($activeSlave.fetish == "buttslut") && ($activeSlave.fetishKnown) == 1>>
-				$He shivers with delight as $he feels $his anal ring stretch to accommodate your <<if $PC.dick == 0>>strap-on<<else>>dick<</if>>. <<set $activeSlave.devotion += 1>>
-			<</if>>
-			<<= VCheck.Anal()>>
-			Fucking a slave with <<if $activeSlave.prostate > 0>>prostate <</if>>stimulation from your <<if $PC.dick == 0>>phallus<<else>>cock<</if>> in $his <<if $seeRace == 1>>$activeSlave.race <</if>>ass makes $him cum with indecent speed. You let $him slide down so $he can finish the other slave with $his mouth while you continue using $his anus. The other slave definitely enjoys $activeSlave.slaveName's moaning into _hisU pussy as you use $activeSlave.slaveName's ass. The hard buttfucking $activeSlave.slaveName is getting keeps $his dick stiff all the way through. @@.hotpink;$His submission to you has increased.@@
-			<<set $activeSlave.devotion += 4>>
-			<<run seX($activeSlave, "penetrative", "slaves", "anal")>>
-			<<run seX($activeSlave, "anal", $PC, "penetrative")>>
-		<</replace>>
-	<</link>><<if ($activeSlave.anus == 0)>> //This option will take $his virginity//<</if>>
-<</if>>
-<<if canDoVaginal($activeSlave)>>
-	<br><<link "The slave giving it has a free pussy, so use that">>
-		<<replace "#result">>
-			$activeSlave.slaveName's hermaphroditic genitalia is a little crammed together; it's busy down there. $He obediently stops fucking so you can maneuver into $him.
-			<<= VCheck.Vaginal()>>
-			Having a <<if $PC.dick == 0>>strap-on<<else>>dick<</if>> in $his pussy reduces $his erection a little, so the slave beneath $him helps $his penetration as much as _heU can. It's not the most convenient of fucks, but that's to be expected when a <<= properMaster()>> and two slaves successfully have two separate instances of vaginal intercourse running at once.<<if $PC.vagina != -1>><<if $PC.dick != 0>> You add a third by grabbing a free hand and guiding it to your own pussy; its owner gets the idea and strokes it as best they can.<</if>><</if>> $activeSlave.slaveName's orgasm is general and intense. @@.hotpink;$His devotion to you has increased.@@
-			<<set $activeSlave.devotion += 4>>
-			<<run seX($activeSlave, "penetrative", "slaves", "vaginal")>>
-		<</replace>>
-	<</link>><<if ($activeSlave.vagina == 0)>> //This option will take $his virginity//<</if>>
-<</if>>
-
-<<case "slave clit on slave">>
-
-<<link "The slave taking it has a free anus, so use that">>
-	<<replace "#result">>
-		Since the other slave is riding $activeSlave.slaveName's huge clit, it's a trivial matter to<<if $PC.dick == 0>> don a strap-on,<</if>> come up behind the fucking slaves, stop the other slave's riding for a moment, and insert yourself into _hisU anus. The other slave gasps as your <<if $PC.dick == 0>>strap-on complements the clit<<else>>big dick complements the clit<</if>> in _hisU pussy. The poor slave rides out the sexual storm as you and $activeSlave.slaveName fuck _himU. $activeSlave.slaveName flirts outrageously with you over the other slave's shoulder whenever $he can. @@.mediumaquamarine;$He has become more trusting of you.@@
-		<<set $activeSlave.trust += 4>>
-		<<run seX($activeSlave, "penetrative", "slaves", "anal")>>
-	<</replace>>
-<</link>>
-<<if canDoAnal($activeSlave)>>
-	<br><<link "The slave giving it has a free anus, so use that">>
-		<<replace "#result">>
-			Since $activeSlave.slaveName is on top, it's a trivial matter to<<if $PC.dick == 0>> don a strap-on,<</if>> come up behind the fucking slaves, stop $his thrusting for a moment, and insert yourself into $his anus.
-			<<= VCheck.Anal()>>
-			<<if ($activeSlave.fetish == "buttslut") && ($activeSlave.fetishKnown == 1)>>
-				$He shivers with delight as $he feels $his anal ring stretch to accommodate your <<if $PC.dick == 0>>strap-on<<else>>dick<</if>>. <<set $activeSlave.devotion += 1>>
-			<</if>>
-			Fucking a slave with stimulation from your <<if $PC.dick == 0>>phallus<<else>>cock<</if>> in $his ass makes $him cum with indecent speed. You let $him slide down so $he can finish the other slave with $his mouth while you continue using $his anus. The other slave definitely enjoys $activeSlave.slaveName's moaning into _hisU pussy as you use $activeSlave.slaveName's ass. The hard buttfucking $activeSlave.slaveName is getting keeps $his clit hard all the way through. @@.hotpink;$His submission to you has increased.@@
-			<<set $activeSlave.devotion += 4>>
-			<<run seX($activeSlave, "penetrative", "slaves", "anal")>>
-		<</replace>>
-	<</link>><<if ($activeSlave.anus == 0)>> //This option will take $his anal virginity//<</if>>
-<</if>>
-<<if canDoVaginal($activeSlave)>>
-	<br><<link "The slave giving it has a free pussy, so use that">>
-		<<replace "#result">>
-			Since $activeSlave.slaveName is on top, it's a trivial matter to<<if $PC.dick == 0>> don a strap-on,<</if>> come up behind the fucking slaves, stop $his thrusting for a moment, and insert yourself into $his pussy. $He obediently stops fucking so you can maneuver into $him.
-			<<= VCheck.Vaginal()>>
-			Having a <<if $PC.dick == 0>>strap-on<<else>>dick<</if>> in $his pussy reduces $his ability to use $his engorged clit like a penis a little, so the slave beneath $him helps $his penetration as much as _heU can. It's not the most convenient of fucks, but that's to be expected when a <<= properMaster()>> and two slaves successfully have two separate instances of vaginal intercourse running at once. $His orgasm is general and intense. @@.hotpink;$His devotion to you has increased.@@
-			<<set $activeSlave.devotion += 4>>
-			<<run seX($activeSlave, "penetrative", "slaves", "vaginal")>>
-		<</replace>>
-	<</link>><<if ($activeSlave.vagina == 0)>> //This option will take $his virginity//<</if>>
-<</if>>
-
 <<case "PA servant">>
 
 <<link "Share the slave with your PA">>
@@ -13184,160 +12825,6 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<</replace>>
 <</link>>
 
-<<case "kitchen molestation">>
-
-<<link "Improve on $his abusive little game">>
-	<<setNonlocalPronouns $seeDicks>>
-	<<replace "#result">>
-		<<set $activeSlave.devotion += 4, $activeSlave.trust += 4>>
-		<<set _targetJobs = ["be a servant", "be a subordinate slave", "get milked", "learn in the schoolroom", "please you", "rest in the spa", "rest", "serve in the club", "serve the public", "take classes", "whore", "work a glory hole", "work as a servant", "work in the brothel"]>>
-		You leave for the moment, but appear at the next mealtime before even $he does. You shut off all the phallic feeders but one, and make an announcement. You decree that just for this one meal, $activeSlave.slaveName is to lie in front of the one functional feeder,
-		<<if canPenetrate($activeSlave)>>as
-			$his cock in the air;
-		<<else>>
-			with a dildo jutting up from $his crotch;
-		<</if>>
-		in order to eat, each slave must ride $activeSlave.slaveName for as long as it takes to suck down their meal<<if $activeSlave.belly >= 5000>> (given the _belly mass jutting from $his middle, it should be quite the sight)<</if>>. $activeSlave.slaveName gapes at you openmouthed for a long moment, looking like $he wants to @@.hotpink;declaim a speech of thanks,@@ but you cut $him off by pointing peremptorily at $his place; $he almost runs over, $his @@.mediumaquamarine;trust in your whim@@ nearly absolute. But the true shape of your plan isn't apparent yet. When the first slave seats _himselfU on $activeSlave.slaveName and starts sucking off the dispenser dildo, you crouch behind _himU and insert yourself as well; the bitch is now airtight. _HeU gags and splutters with the discomfort but keeps working away until _heU gets _hisU meal down and struggles off the three phalluses _heU has in _himU. The next in line gets to it with some trepidation: and so it goes, slave by slave.
-		<<for _ress = 0; _ress < $slaves.length; _ress++>>
-			<<if _targetJobs.includes($slaves[_ress].assignment) && hasAnyLegs($slaves[_ress]) && $slaves[_ress].relationship != -3 && _ress.ID != $activeSlave.ID>>
-				<<if canDoAnal($slaves[_ress]) && canDoVaginal($slaves[_ress])>>
-					<<if $slaves[_ress].anus == 0 && $slaves[_ress].vagina == 0>>
-						<<set _virgins = 1>>
-					<<elseif $slaves[_ress].vagina == 0>>
-						<<set _virgins = 1, _anusOnly = 1>>
-						<<run seX($slaves[_ress], "anal", $activeSlave, "penetrative")>>
-						<<run seX($slaves[_ress], "anal", $PC, "penetrative")>>
-						<<if canImpreg($slaves[_ress], $PC)>>
-							<<= knockMeUp($slaves[_ress], 5, 1, -1, 1)>>
-						<</if>>
-						<<if canImpreg($slaves[_ress])>>
-							<<= knockMeUp($slaves[_ress], 5, 1, $activeSlave.ID, 1)>>
-						<</if>>
-						<<if ($slaves[_ress].anus == 1)>>
-							<<set $slaves[_ress].trust -= 2>>
-							<<set _doubleAnal = 1>>
-						<</if>>
-					<<elseif $slaves[_ress].anus == 0>>
-						<<set _virgins = 1, _pussyOnly = 1>>
-						<<run seX($slaves[_ress], "vaginal", $activeSlave, "penetrative")>>
-						<<run seX($slaves[_ress], "vaginal", $PC, "penetrative")>>
-						<<if canImpreg($slaves[_ress], $PC)>>
-							<<= knockMeUp($slaves[_ress], 5, 0, -1, 1)>>
-						<</if>>
-						<<if canImpreg($slaves[_ress])>>
-							<<= knockMeUp($slaves[_ress], 5, 0, $activeSlave.ID, 1)>>
-						<</if>>
-						<<if ($slaves[_ress].vagina == 1)>>
-							<<set $slaves[_ress].trust -= 2>>
-							<<set _doubleVaginal = 1>>
-						<</if>>
-					<<else>>
-						<<run seX($slaves[_ress], "anal", $activeSlave, "penetrative")>>
-						<<run seX($slaves[_ress], "vaginal", $activeSlave, "penetrative")>>
-						<<if canImpreg($slaves[_ress], $PC)>>
-							<<= knockMeUp($slaves[_ress], 5, 2, -1, 1)>>
-						<</if>>
-						<<if canImpreg($slaves[_ress])>>
-							<<= knockMeUp($slaves[_ress], 5, 1, $activeSlave.ID, 1)>>
-						<</if>>
-					<</if>>
-				<<elseif canDoVaginal($slaves[_ress])>>
-					<<set _pussyOnly = 1>>
-					<<run seX($slaves[_ress], "vaginal", $activeSlave, "penetrative")>>
-					<<run seX($slaves[_ress], "vaginal", $PC, "penetrative")>>
-					<<if canImpreg($slaves[_ress], $PC)>>
-						<<= knockMeUp($slaves[_ress], 5, 0, -1, 1)>>
-					<</if>>
-					<<if canImpreg($slaves[_ress])>>
-						<<= knockMeUp($slaves[_ress], 5, 0, $activeSlave.ID, 1)>>
-					<</if>>
-					<<if ($slaves[_ress].vagina == 1)>>
-						<<set $slaves[_ress].trust -= 2>>
-						<<set _doubleVaginal = 1>>
-					<</if>>
-				<<elseif canDoAnal($slaves[_ress])>>
-					<<set _anusOnly = 1>>
-					<<run seX($slaves[_ress], "anal", $activeSlave, "penetrative")>>
-					<<run seX($slaves[_ress], "anal", $PC, "penetrative")>>
-					<<if canImpreg($slaves[_ress], $PC)>>
-						<<= knockMeUp($slaves[_ress], 5, 1, -1, 1)>>
-					<</if>>
-					<<if canImpreg($slaves[_ress])>>
-						<<= knockMeUp($slaves[_ress], 5, 1, $activeSlave.ID, 1)>>
-					<</if>>
-					<<if ($slaves[_ress].anus == 1)>>
-						<<set $slaves[_ress].trust -= 2>>
-						<<set _doubleAnal = 1>>
-					<</if>>
-				<<else>>
-					<<set _chaste = 1>>
-				<</if>>
-				<<if ($slaves[_ress].fetishKnown == 1) && ($slaves[_ress].fetish == "submissive")>>
-					<<set $slaves[_ress].devotion++>>
-					<<set _subLove = 1>>
-				<</if>>
-			<</if>>
-		<</for>>
-		<<if def _virgins>>
-			You let your virgins<<if def _chaste>> and chaste slaves<</if>> hold their thighs tight together for a little frottage rather than deflowering their holes like this.
-		<<elseif def _chaste>>
-			Your chaste slaves hold their thighs tight together for a little frottage should they lack the ability to accommodate.
-		<</if>>
-		<<if def _anusOnly>>
-			Those slaves without pussies are forced to take both you and $activeSlave.slaveName up the butt at once.
-			<<if def _doubleAnal>>
-				Experienced assholes can take the strain just fine, but your tighter-assed slaves are @@.gold;frightened@@ by the anal pain they suffer.
-			<</if>>
-		<</if>>
-		<<if def _pussyOnly>>
-			Slaves with off-limit assholes quickly find both you and $activeSlave.slaveName delving the depths of their cunts.
-			<<if def _doubleVaginal>>
-				Experienced sluts can take the double penetration just fine, but your tighter slaves are @@.gold;frightened@@ by the amount of stretching they are forced to undergo.
-			<</if>>
-		<</if>>
-		<<if def _subLove>>
-			Your subs on the other hand think this is @@.hotpink;a meal worth remembering.@@
-		<</if>>
-	<</replace>>
-<</link>>
-<<if canDoAnal($activeSlave)>>
-	<br><<link "The rule about consent works both ways">>
-		<<replace "#result">>
-			<<set _targetJobs = ["be a servant", "be a subordinate slave", "get milked", "learn in the schoolroom", "please you", "rest in the spa", "rest", "serve in the club", "serve the public", "take classes", "whore", "work a glory hole", "work as a servant", "work in the brothel"]>>
-			You tell $activeSlave.slaveName to get up on the kitchen counter and spread $his legs. $He catches something in the tone of your voice and looks frightened, but obeys. You then make several conversational observations, as though for no particular reason, to the rest of the slaves. First, you point out, the consent rule works for everyone: they, hypothetically, would not need to ask $activeSlave.slaveName $his permission to fuck $his ass, just like $he doesn't have to ask their permission to molest them during meals. (At this $activeSlave.slaveName's fear deepens into obvious @@.gold;terror.@@) Second, you have decided $activeSlave.slaveName will not be getting down off the counter until everyone's done with their meals — and anything else they wish to do in the kitchen. And third, you conclude, any number of large strap-ons and dildos can be found in the kitchen cabinets. There is a general rush for these; you tell $activeSlave.slaveName, whose
-			<<if $activeSlave.lips > 40>>
-				bimbo
-			<<elseif $activeSlave.lips > 20>>
-				big
-			<<elseif $activeSlave.lips > 10>>
-				soft
-			<</if>>
-			lips are quivering, to come see you after $he's done here. About an hour later, $he hobbles into your office, and you tell $him to show you $his anus. $His longtime targets for mealtime molestation were not merciful; they weren't stupid enough to damage $him, but that's one well-gaped butthole. You fuck it anyway, and $he's too tired and desensitized to care. Your less trusting slaves carefully consider the rules, and realize that there's a @@.mediumaquamarine;built-in mechanism for correction:@@ if anyone gets too rapey, they can rape them right back.
-			<<set $activeSlave.trust -= 5>>
-			<<= VCheck.Anal(20)>>
-			<<if canGetPregnant($activeSlave) && $activeSlave.mpreg == 1>>
-				<<set _sourceSeed = random(0,$slaves.length-1)>>
-				<<for _ress = _sourceSeed + 1; _ress != _sourceSeed; _ress++>>
-					<<if _ress == $slaves.length>><<set _ress = 0>><</if>> /* wrap around */
-					<<if canImpreg($activeSlave, $slaves[_ress]) && _targetJobs.includes($slaves[_ress].assignment) && $slaves[_ress].ID != $activeSlave.ID>>
-						<<= knockMeUp($activeSlave, 50, 1, $slaves[_ress].ID, 1)>>
-						<<break>>
-					<</if>>
-				<</for>>
-			<</if>>
-			<<for _ress = 0; _ress < $slaves.length; _ress++>>
-				<<if ($slaves[_ress].trust < 50) && _targetJobs.includes($slaves[_ress].assignment)>>
-					<<set $slaves[_ress].trust++>>
-				<</if>>
-			<</for>>
-			<<if $activeSlave.anus == 1>>
-				Poor $activeSlave.slaveName's butthole @@.lime;isn't quite the same@@ afterward.
-				<<set $activeSlave.anus += 1>>
-			<</if>>
-		<</replace>>
-	<</link>><<if $activeSlave.anus == 0>> //This option will take $his anal virginity//<</if>>
-<</if>>
-
 <<case "forbidden masturbation">>
 
 <<link "Let $him earn relief">>
@@ -15739,7 +15226,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 <<link "Help $him into $his P-Limbs">>
 	<<replace "#result">>
 		You decide to help $him yourself rather than having a slave do it.
-		<<if $activeSlave.ID == $Bodyguard.ID>>Swinging your legs out of bed and heading towards $him<<else>>Heading towards $his part of the sleeping area<</if>>, you're struck by how <<if $activeSlave.belly >= 5000>>round<<else>>small<</if>> $he looks without $his prosthetics. Just a little $activeSlave.skin thing, without the
+		<<if $activeSlave.ID == $BodyguardID>>Swinging your legs out of bed and heading towards $him<<else>>Heading towards $his part of the sleeping area<</if>>, you're struck by how <<if $activeSlave.belly >= 5000>>round<<else>>small<</if>> $he looks without $his prosthetics. Just a little $activeSlave.skin thing, without the
 		<<if getLimbCount($activeSlave, 3) > 2>>
 			sexy artificial limbs that let $him delight anyone who fucks $him with vibrating fingertips.
 		<<elseif getLimbCount($activeSlave, 4) > 2>>
@@ -15751,9 +15238,9 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<else>>
 			artificial limbs that allow $him a semblance of a normal life.
 		<</if>>
-		$He's not frightened, trusting <<if $activeSlave.ID == $Bodyguard.ID>>you to help $him, or detail a slave to do so<<else>>$assistant.name to bring help<</if>>. When $he <<if canSee($activeSlave)>>sees<<else>>notices<</if>> that you mean to assist $him yourself, $he breaks out in a huge grateful smile and thanks you profusely.
+		$He's not frightened, trusting <<if $activeSlave.ID == $BodyguardID>>you to help $him, or detail a slave to do so<<else>>$assistant.name to bring help<</if>>. When $he <<if canSee($activeSlave)>>sees<<else>>notices<</if>> that you mean to assist $him yourself, $he breaks out in a huge grateful smile and thanks you profusely.
 		<br><br>
-		<<if $activeSlave.ID == $Bodyguard.ID>>First, you extricate $his limbs from the malfunctioning machine. <</if>>As you take up $his first arm and kneel down to attach it to $him, $he rolls and hoists that shoulder up towards it, $his
+		<<if $activeSlave.ID == $BodyguardID>>First, you extricate $his limbs from the malfunctioning machine. <</if>>As you take up $his first arm and kneel down to attach it to $him, $he rolls and hoists that shoulder up towards it, $his
 		<<if $activeSlave.belly >= 5000>>
 			<<if $activeSlave.bellyPreg >= 3000>>pregnant<<else>>bloated<</if>> body straining to twist
 		<<elseif $activeSlave.muscles > 30>>
@@ -15779,7 +15266,7 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<br><<link "Fuck $him before you help $him">>
 		<<replace "#result">>
 			You decide to have some fun with the temporarily helpless slave. You
-			<<if $activeSlave.ID == $Bodyguard.ID>>get out of bed<<else>>head to where $he's waiting for help<</if>> and scoop $him up, $his limblessness making it comically easy. With $his torso cradled in your grasp and $his head nestled into the crook of your arm, you carry $his <<if $activeSlave.ID == $Bodyguard.ID>>back towards your bed<<else>>back towards your suite<</if>>. When $he <<if canSee($activeSlave)>>sees<<else>>figures out<</if>> what you're doing, $he turns $his head and gives your <<if $PC.boobs >= 300>>breast on that side<<else>>chest<</if>> an anticipatory kiss. When you set $him down on the bed, $he <<if canSee($activeSlave)>>stares<<else>>smiles<</if>> up at you invitingly and gives $his four P-Limb anchor points a glinting wiggle, a gesture that might mean anything. $His <<if canDoVaginal($activeSlave)>>pussy<<else>>asshole<</if>>, in any case, is presented openly for your use.
+			<<if $activeSlave.ID == $BodyguardID>>get out of bed<<else>>head to where $he's waiting for help<</if>> and scoop $him up, $his limblessness making it comically easy. With $his torso cradled in your grasp and $his head nestled into the crook of your arm, you carry $his <<if $activeSlave.ID == $BodyguardID>>back towards your bed<<else>>back towards your suite<</if>>. When $he <<if canSee($activeSlave)>>sees<<else>>figures out<</if>> what you're doing, $he turns $his head and gives your <<if $PC.boobs >= 300>>breast on that side<<else>>chest<</if>> an anticipatory kiss. When you set $him down on the bed, $he <<if canSee($activeSlave)>>stares<<else>>smiles<</if>> up at you invitingly and gives $his four P-Limb anchor points a glinting wiggle, a gesture that might mean anything. $His <<if canDoVaginal($activeSlave)>>pussy<<else>>asshole<</if>>, in any case, is presented openly for your use.
 			<br><br>
 			After fingering your helpless toy experimentally, you
 			<<if $PC.dick != 0>>
@@ -16184,80 +15671,6 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<</replace>>
 <</link>>
 
-<<case "too thin for cum diet">>
-
-<<link "Give the poor $girl a break and take $him off $his cum diet so $he can safely get to a healthier weight">>
-	<<replace "#result">>
-		You silently appraise <<print $activeSlave.slaveName>>'s desperate face and lean forward in your chair to check your terminal. You confirm that
-		<<if $activeSlave.dietCum == 1>>
-			$his food is supplemented with cum for flavor.
-		<<else>>
-			$he is being fed significant amounts of ejaculate.
-		<</if>>
-		While $his <<if $activeSlave.dietCum == 2>>extreme <</if>>diet is still engineered to <<if $activeSlave.dietCum == 2>>barely <</if>> provide the nutrition $he needs, if $he can't hold the stuff down, $he can't gain weight<<if $activeSlave.pregKnown == 1 && $activeSlave.preg > $activeSlave.pregData.normalBirth/4>>, even more so with $his growing child<<if $activeSlave.pregType > 1>>ren<</if>> taking whatever nutrients <<if $activeSlave.pregType > 1>>they<<else>>it<</if>> can<</if>>. With a few taps on the keyboard you change $his orders so that $he will be fed a much more nutritionally rich diet that is free of ejaculate. However, you make sure to warn $him that $assistant.name will be monitoring $him, and you may change your mind once $he reaches a more attractive weight. $He thanks you profusely, <<if $activeSlave.vagina != 0>>even attempting to offer $his body for use in gratitude,<</if>> but you simply send $him away. You find yourself concerned that you are becoming soft by allowing slaves to dictate what they will and won't eat, but your benevolent decision has already had a @@.hotpink;positive effect on $his attitude.@@ $He @@.mediumaquamarine;trusts you a little more@@ too.
-		<<set $activeSlave.trust += 2, $activeSlave.devotion += 2, $activeSlave.dietCum = 0>>
-	<</replace>>
-<</link>>
-<<if canDoAnal($activeSlave)>>
-	<br><<link "Give the emaciated slut your answer in the form of a rough butt-fuck">>
-		<<replace "#result">>
-			You stand up behind your desk. $He flinches, but holds $his ground, $his lip quivering slightly. You slowly walk toward $him, appraising $his <<if $activeSlave.belly >= 100>>bloated<<else>>smooth<</if>>, skinny body, touching $his shoulder as you disappear behind $him. $He starts to cry as you
-			<<if $PC.dick == 0>>
-				pull a massive rubber dong attachment off the wall and hook it to your strap-on.
-			<<else>>
-				unbuckle your pants and let $him feel your warm dick harden against $his tight, bony thigh.
-			<</if>>
-			Without word or ceremony, you shove $him forward so that $he is bent over and crushed against your desk. The tears start to flow out of $him as $he feels your <<if $PC.dick == 0>>massive rubber dong <<else>>hard, thick cock <</if>>pressing against $his unprotected anus.
-			<br><br>
-			When you offer $him the opportunity to revise $his request, $he does. "Plea<<s>>e, <<Master>>! I'm <<s>>orry! Plea<<s>>e don't fuck my a<<ss>>! I'll eat all the cum you want! Plea<<s>>e!" $He screams <<if $activeSlave.accent > 1>>in $his thick accent <</if>> as you force your<<if $PC.dick == 0>> strap-on <<else>>self <</if>> inside $his butt-hole and subdue $his weak, struggling body against your unforgiving desk. @@.gold;Horrified tears@@ stream down $his face with each brutal thrust as you speak into $his ear and remind $him that slaves are not allowed to dictate their needs to their owners. You know what's best for $him, and right now, $his proper role is as an eager little depository for the products of the free-cities ejaculate market. It's $his job to suck down cum at the whim of $his owner, plain and simple, until $he's told otherwise. You butt-fuck the cum-fed slag until $he simply lays there and accepts $his corrective assrape.
-			<<if $activeSlave.anus == 0>>
-				@@.lime;$His@@
-				<<if $activeSlave.butt < 5>>
-					@@.lime;skinny little@@
-				<<else>>
-					@@.lime;plump little@@
-				<</if>>
-				@@.lime;ass is now broken in.@@
-			<</if>>
-			$He sobs quietly with each thrust of your hips, and when you finally <<if $PC.dick != 0>>make yet another cum deposit into $his <<if $activeSlave.weight < -80>>emaciated <<else>>bony <</if>>body and<</if>> pull out, you instruct
-			<<if $activeSlave.dietCum == 1>>
-				$assistant.name to dramatically increase the amount of cum in $his diet from now on. You won't have a slave telling you what $he eats. $activeSlave.slaveName <<if canHear($activeSlave)>>hears your instructions <<else>>correctly guesses the meaning behind your body language <</if>>and whimpers before rubbing $his sore bottom and
-			<<else>>
-				another slave to drag the broken slut to the kitchen, where $he's to receive an additional feeding of thick, creamy ejaculate-based nutrients before
-			<</if>>
-			proceeding to $his next assignment.
-			<br><br>
-			$He's learned a valuable lesson about what it truly means to be a slave today, and it's one $he @@.orchid;won't soon forget.@@
-			<<set $activeSlave.devotion -= 5, $activeSlave.trust -= 5>>
-			<<if ($activeSlave.anus == 0)>>
-				<<set $activeSlave.anus += 1>>
-			<</if>>
-			<<= VCheck.Anal()>>
-			<<if $activeSlave.dietCum == 1>>
-				<<set $activeSlave.dietCum = 2, $activeSlave.dietMilk = 0>>
-			<</if>>
-		<</replace>>
-	<</link>><<if ($activeSlave.anus == 0) || ($activeSlave.dietMilk == 1)>> //This option will//<<if $activeSlave.anus == 0>> //take anal virginity//<<if $activeSlave.dietMilk == 1>>// and//<</if>><</if>><<if $activeSlave.dietMilk == 1>>// remove milk from $his diet//<</if>><</if>>
-<</if>>
-<br><<link "Use aphrodisiacs and positive reinforcement to make $his cum diet more palatable">>
-	<<replace "#result">>
-		You do a quick check at your terminal. $He is indeed being fed <<if $activeSlave.dietCum == 1>> food that is supplemented with cum for flavor.<<else>>large amounts of human ejaculate.<</if>> You tell $him that it can't be helped. When you assign a slave $his diet, it's for a reason, and in <<print $activeSlave.slaveName>>'s case, you are using $his diet to make $him into a better whore. By the look on $his distressed face, you can tell $he doesn't understand or appreciate your perspective, but you tell $him you have just the thing to help change $his outlook.
-		<br><br>
-		You pull out a heavy dose of aphrodisiacs from the drawer in your desk. They are expensive, but powerful drugs that can be used in a brute force way to manipulate the sexual urges and tastes of even the most frigid sluts. The mild dose included in every slave's food is usually enough to affect a slow, inevitable change in attitude for resistant slaves, but sometimes a more acute dose can be an amusing way to turn disgust into dependence. $activeSlave.slaveName shivers as you reassure $him and inject $his <<if $activeSlave.weight < -80>>bony <<else>>tiny <</if>>body with the powerful drugs. You know they are working when $he begins to perspire a little, and
-		<<if $activeSlave.vagina > -1>>
-			$he starts to leak fresh girl-juice down $his shivering thighs.
-		<<elseif $activeSlave.dick == 0>>
-			starts to turn instinctively to offer you $his only hole.
-		<<else>>
-			$his dick starts to come to attention.
-		<</if>>
-		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. 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 @@.lightcoral;view cum as an inevitable component of $his daily life.@@ $He also manages to @@.green;gain a little weight.@@ Thanks to your manipulation of $his Pavlovian responses through extreme drug therapy, $his @@.red;health has suffered a bit,@@ but $he is also @@.hotpink;more dependent on you@@ thanks to $his <<if $activeSlave.addict == 0>>@@.cyan;new@@<<else>>@@.cyan;developing@@<</if>> @@.cyan;addiction to aphrodisiacs.@@
-		<<set $activeSlave.devotion += 5, $activeSlave.weight += 10, $activeSlave.fetish = "cumslut", $activeSlave.fetishKnown = 1,	$activeSlave.fetishStrength = 10, $activeSlave.addict += 1>>
-		<<run seX($activeSlave, "oral", "public", "penetrative", 50)>>
-		<<run healthDamage($activeSlave, 10)>>
-	<</replace>>
-<</link>>
-
 <<case "transition anxiety">>
 
 <<if canDoAnal($activeSlave)>>
@@ -16652,19 +16065,19 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 		<<EventFetish $activeSlave "masochist">>
 	<</replace>>
 <</link>>
-<<if $HeadGirl != 0>>
+<<if $HeadGirlID != 0>>
 	<br><<link "Double up on $his ass with your Head Girl">>
-		<<setLocalPronouns $HeadGirl 2>>
+		<<setLocalPronouns _S.HeadGirl 2>>
 		<<replace "#result">>
-			You page $HeadGirl.slaveName for some assistance filling up a loose hole, and _he2 comes quickly, <<if canPenetrate($HeadGirl)>>precum already visible at the tip of _his2 erection<<else>>bringing a strap-on<</if>>. When _he2 arrives, it's to the sight of $activeSlave.slaveName sitting on your lap<<if !canDoAnal($activeSlave)>>, anal chastity discarded<</if>>, with your <<if $PC.dick != 0>>cock<<else>>strap-on<</if>> up $his ridiculous anal gape, waiting for a second phallus while your hands tease $his $activeSlave.nipples nipples. Seeing that backup has arrived, you lean back and shift your grip to the back of the receptacle's lower thighs, pulling $him up and shifting $his hips to offer your Head Girl another berth in $activeSlave.slaveName's ass. This pulls $his loose sphincter up, producing a little gape above the top of your invading shaft, and $HeadGirl.slaveName loses no time in fucking it. _He2 gives $activeSlave.slaveName a kiss, but soon breaks the lip lock, since _he2's more interested in making eyes at you over your mutual anal conquest's shuddering shoulder. As
+			You page _S.HeadGirl.slaveName for some assistance filling up a loose hole, and _he2 comes quickly, <<if canPenetrate(_S.HeadGirl)>>precum already visible at the tip of _his2 erection<<else>>bringing a strap-on<</if>>. When _he2 arrives, it's to the sight of $activeSlave.slaveName sitting on your lap<<if !canDoAnal($activeSlave)>>, anal chastity discarded<</if>>, with your <<if $PC.dick != 0>>cock<<else>>strap-on<</if>> up $his ridiculous anal gape, waiting for a second phallus while your hands tease $his $activeSlave.nipples nipples. Seeing that backup has arrived, you lean back and shift your grip to the back of the receptacle's lower thighs, pulling $him up and shifting $his hips to offer your Head Girl another berth in $activeSlave.slaveName's ass. This pulls $his loose sphincter up, producing a little gape above the top of your invading shaft, and _S.HeadGirl.slaveName loses no time in fucking it. _He2 gives $activeSlave.slaveName a kiss, but soon breaks the lip lock, since _he2's more interested in making eyes at you over your mutual anal conquest's shuddering shoulder. As
 			<<if $PC.dick != 0>>
-				<<if canPenetrate($HeadGirl)>>
+				<<if canPenetrate(_S.HeadGirl)>>
 					your dicks slide against each other,
 				<<else>>
 					your dick slides against _his2 strap-on,
 				<</if>>
 			<<else>>
-				<<if canPenetrate($HeadGirl)>>
+				<<if canPenetrate(_S.HeadGirl)>>
 					your strap-on slides against _his2 dick,
 				<<else>>
 					your strap-ons slide against each other,
@@ -16673,10 +16086,10 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 			your trusty Head Girl whispers that @@.hotpink;_he2 loves you.@@ $activeSlave.slaveName makes an inarticulate noise of anal distress that probably means @@.hotpink;approximately the same thing.@@
 			<<set $activeSlave.devotion += 4>>
 			<<= VCheck.Anal(2)>>
-			<<if canImpreg($activeSlave, $HeadGirl)>>
-				<<= knockMeUp($activeSlave, 5, 1, $HeadGirl.ID, 1)>>
+			<<if canImpreg($activeSlave, _S.HeadGirl)>>
+				<<= knockMeUp($activeSlave, 5, 1, $HeadGirlID, 1)>>
 			<</if>>
-			<<set $slaves[$slaveIndices[$HeadGirl.ID]].devotion += 4>>
+			<<set _S.HeadGirl.devotion += 4>>
 			<<EventFetish $activeSlave "buttslut">>
 			<<EventFetish $activeSlave "submissive">>
 		<</replace>>
@@ -17197,76 +16610,6 @@ brought in to you. This time <<= App.UI.slaveDescriptionDialog($activeSlave)>> h
 	<</link>> //This option will render $activeSlave.slaveName pregnant//<<if canDoVaginal($activeSlave) && ($activeSlave.vagina == 0)>> //and take $his virginity//<<elseif !canDoVaginal($activeSlave) && ($activeSlave.anus == 0)>> //and take $his anal virginity//<</if>>
 <</if>>
 
-<<case "devoted educated slave">>
-
-<<link "Have a conversation with $him">>
-	<<replace "#result">>
-		You linger in the hallway a while with $activeSlave.slaveName and enjoy a brief but verbose discussion. It's clear $he hasn't had the chance to engage in any meaningful conversations in a while, so $he relishes the opportunity energetically.
-		<br><br>
-		Though the two of you only touch upon a handful of sophisticated topics, by the time you leave $him to tend to other matters, $activeSlave.slaveName is beaming happily as $he continues on to $his duties for the day. $He @@.mediumaquamarine;trusts you more@@ for taking the time to engage with $him intellectually.
-		<<set $activeSlave.trust += 4>>
-	<</replace>>
-<</link>>
-<<if canDoAnal($activeSlave) || canDoVaginal($activeSlave)>>
-	<br><<link "Stimulate $his mind and body">>
-		<<replace "#result">>
-			You find a comfortable seat to sit on <<if $PC.dick == 0>>,don a strap-on,<</if>>as $he seats $his
-			<<if $activeSlave.butt < 2>>
-				narrow
-			<<elseif $activeSlave.butt < 5>>
-				average
-			<<elseif $activeSlave.butt < 8>>
-				plump
-			<<elseif $activeSlave.butt < 12>>
-				impressive
-			<<else>>
-				mind-blowing
-			<</if>>
-			ass on your <<if $PC.dick == 0>>strap-on<<else>>dick<</if>> and rides you. While your cock plumbs the depths of $his
-			<<if canDoVaginal($activeSlave)>>
-				pussy,
-			<<else>>
-				butt,
-			<</if>>
-			the two of you engage in a casual debate over a number of topics of sophistry.
-			<br><br>
-			At the crux of one of $his concluding arguments, the intense pleasure from the prolonged
-			<<if canDoVaginal($activeSlave)>>
-				fucking
-			<<else>>
-				buttfucking
-			<</if>>
-			pushes $him over the edge into orgasm, robbing the thrust of $his defense of any gravitas it once had. $He doesn't seem to mind however, choosing to instead wrap up $his remarks by @@.hotpink;blowing you a kiss.@@
-			<<set $activeSlave.devotion += 4>>
-			<<if canDoVaginal($activeSlave)>>
-				<<= VCheck.Vaginal()>>
-			<<else>>
-				<<= VCheck.Anal()>>
-			<</if>>
-		<</replace>>
-	<</link>><<if canDoVaginal($activeSlave) && ($activeSlave.vagina == 0)>>//This option will take $his virginity//<<elseif !canDoVaginal($activeSlave) && ($activeSlave.anus == 0)>> //This option will take $his anal virginity//<</if>>
-<</if>>
-<<if $arcologies[0].FSDegradationist !== "unset">>
-	<br><<link "A public blowbang will show $him what a Degradationist arcology thinks about 'educated' slaves">>
-		<<replace "#result">>
-			You tell $him $he'll get fucked in the mouth until $he either gets over the idea of being special for $his education or until all $his learning is fucked out of $his head. You drag the protesting $activeSlave.slaveName out into a public plaza, restrain $him in stocks so that $his mouth is available, and inform the gathering crowd of citizens that this particular slave thinks $himself more than a fuckhole because of some fancy 'education'.
-			<br><br>
-			When you return later in the evening, it becomes abundantly clear that your citizenry taught $activeSlave.slaveName a harsh lesson about a slave's place in $arcologies[0].name. $activeSlave.slaveName has certainly @@.green;learned to keep any pretentious thoughts about $his education in $his head.@@ $He did, however, @@.red;have quite a rough time@@ sucking all those dicks<<if $activeSlave.skill.oral <= 30>>, though $he did learn about sucking dick, so $he can't claim enslavement isn't educational.<<= SkillIncrease.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.
-			<<run seX($activeSlave, "oral", "public", "penetrative", random(65,80))>>
-			<<run repX(500, "event", $activeSlave), healthDamage($activeSlave, 10)>>
-		<</replace>>
-	<</link>>
-<<elseif $arcologies[0].FSPaternalist !== "unset">>
-	<br><<link "Allow $him to audit some advanced classes at Paternalist slave schools">>
-		<<replace "#result">>
-			You tell $him to take the day off for a trip to audit classes at $arcologies[0].name's most renowned Paternalist slave schools. $activeSlave.slaveName is shocked into a reverie by your words before running to wrap $his arms around you in a tight hug. When $he eventually untangles $himself from you, $he races off to get ready for $his day of intellectualism while you direct $assistant.name to make the necessary arrangements.
-			<br><br>
-			When $activeSlave.slaveName returns, $he looks exhausted but happier than you've ever seen $him. It seems $he benefited greatly from $arcologies[0].name's Paternalist institutions, and has grown to @@.mediumaquamarine;trust you more@@ while also @@.hotpink;deepening $his acceptance of slavery.@@
-			<<set $activeSlave.trust += 4, $activeSlave.devotion += 4>>
-		<</replace>>
-	<</link>>
-<</if>>
-
 <<case "nice guys">>
 
 <<link "Let the boys be">>
diff --git a/src/uncategorized/RETS.tw b/src/uncategorized/RETS.tw
index 444e78514975d567c7a5282dd1c00bc06b78a633..e3391e5b081e454b75aefc80affffe6a1b02e35f 100644
--- a/src/uncategorized/RETS.tw
+++ b/src/uncategorized/RETS.tw
@@ -115,13 +115,6 @@
 
 <<set $subSlave = $slaves[$slaveIndices[$eventSlave.relationshipTarget]]>>
 
-<<case "simple assault">>
-
-<<if Array.isArray($RESimpleAssaultIDs)>>
-	<<set $RESimpleAssaultIDs = $RESimpleAssaultIDs.random()>>
-	<<set $subSlave = $slaves[$slaveIndices[$RESimpleAssaultIDs]]>>
-<</if>>
-
 <<case "cockmilk interception">>
 
 <<if Array.isArray($RECockmilkInterceptionIDs)>>
@@ -166,7 +159,7 @@
 	<<case "anal cowgirl" "boob collision" "cockmilk interception" "if you enjoy it" "incestuous nursing" "interslave begging" "repressed anal virgin" "shower force" "taste test" "top exhaustion">>
 		<<set $activeSlave.clothes = "no clothing">>
 		<<set $subSlave.clothes = "no clothing">>
-	<<case "date please" "simple assault">>
+	<<case "date please">>
 		<<set $activeSlave.clothes = "no clothing">>
 	<<case "sadistic description">>
 		<<set $subSlave.clothes = "no clothing">>
@@ -405,8 +398,8 @@ $He clearly held off on climaxing in case you wanted $his libido undiminished fo
 <<case "boob collision">>
 
 <<set _headGirlPresent = 1>>
-<<if $HeadGirl != 0>>
-	<<if $activeSlave.ID == $HeadGirl.ID>>
+<<if $HeadGirlID != 0>>
+	<<if $activeSlave.ID == $HeadGirlID>>
 		<<set _headGirlPresent = 0>>
 	<</if>>
 <</if>>
@@ -738,41 +731,6 @@ Being the <<if $activeSlave.relationship > 4>>$wife<<else>>lover<</if>> of a lus
 
 Mere moments after you absorb this arresting scene, $subSlave.slaveName thrusts <<if canPenetrate($subSlave)>>_his2 cock<<else>>the strap-on<</if>> all the way inside $activeSlave.slaveName's <<if _notVirgin == 1>>ass<<else>>womanhood<</if>> and shudders, <<if canPenetrate($subSlave)>>filling it with _his2 cum<<else>>orgasming<</if>>. Then _he2 collapses, utterly spent. $activeSlave.slaveName <<if canPenetrate($subSlave)>>gasps at the sensation of the ejaculate shooting into $his body<<else>>smiles a little wider as $he feels $subSlave.slaveName's muscles tense with climax<</if>>, and then grunts a little as $subSlave.slaveName lies down on top of $him.<<if $subSlave.boobs > 5000>> The enormous weight of $his lover's boobs squashes $him.<</if>><<if $subSlave.belly >= 5000>> _His2 _belly <<if $subSlave.bellyPreg >= 3000>>pregnancy<<else>>belly<</if>> pushing into the small of $his back.<</if>> After a few seconds, $he wiggles $his hips a little as a wordless question. The sensation <<if canPenetrate($subSlave)>>against $subSlave.slaveName's softening, overstimulated member<<else>>is transmitted through the phallus and its harness to $subSlave.slaveName's overstimulated clit, and this<</if>> makes the exhausted slave on top quiver, eliciting a giggle from the slave underneath _him2. "I love you, $subSlave.slaveName," $he whispers, and receives a mumbled "I love you too" in breathy response, right next to $his ear.
 
-<<case "simple assault">>
-
-<<set _vaginal = 0>>
-<<if $subSlave.vagina != 0 && canDoVaginal($subSlave)>>
-	<<set _vaginal = 1>>
-	<<run seX($subSlave, "vaginal", $activeSlave, "penetrative")>>
-<<else>>
-	<<run seX($subSlave, "anal", $activeSlave, "penetrative")>>
-<</if>>
-You round a corner and almost trip over <<= App.UI.slaveDescriptionDialog($activeSlave)>>. $He's on top of another slave, humping away; $his <<if $activeSlave.butt > 8>>monstrous, naked ass jiggles lewdly<<elseif $activeSlave.anus > 2>>loose asspussy winks lewdly<<elseif $activeSlave.muscles > 30>>heavily muscled butt flexes powerfully<<elseif $activeSlave.butt > 3>>big butt pumps energetically<<else>>nice little butt flexes cutely<</if>> as $he thrusts. You can't see much of the slave <<if _vaginal>>lying on _his2 back<<else>>face-down<</if>> underneath $activeSlave.slaveName, but you recognize _him2 as <<= App.UI.slaveDescriptionDialog($subSlave)>> by _his2 sobbing. _He2's struggling a little, but $activeSlave.slaveName has _him2 pinned to the floor by _his2 wrists, and $activeSlave.slaveName is quickly raping the resistance out of the <<print SlaveTitle($subSlave)>>.
-<br><br>
-$activeSlave.slaveName senses your presence above and behind $him, and twists $his $activeSlave.hColor head around to <<if canSee($activeSlave)>>see who it is. $He sees that it's you<<else>>discern who it is. $He realizes that it's you<</if>>, and greets you cheerfully. "Hi, <<Master>>," $he trills.
-<<if $activeSlave.fetishKnown && $activeSlave.fetish == "sadist">>
-	"Thi<<s>> i<<s>> <<s>>o awe<<s>>ome," the sadistic $desc gushes. "The crying, the <<s>>truggling. Thank you for letting u<<s>> do thi<<s>>."
-<<elseif $activeSlave.fetishKnown && $activeSlave.fetish == "dom">>
-	"The crying almo<<s>>t make<<s>> me feel bad, but fucking a bitch feel<<s>> <<s>>o, <<s>>o good," the dominant $desc admits conversationally.
-<<elseif $activeSlave.fetishKnown && $activeSlave.fetish == "pregnancy" && _vaginal == 1 && canImpreg($subSlave, $activeSlave)>>
-	"I couldn't help my<<s>>elf," the $desc admits. "<<He 2>>'d look <<s>>o pretty with a pregnant belly and I ju<<s>>t couldn't re<<s>>i<<s>>t giving _him2 one. <<He 2>> tried to <<s>>ay <<he 2>> didn't want to be a mother, <<s>>o..."
-<<elseif $activeSlave.energy > 95>>
-	"I can't help my<<s>>elf," the nymphomaniac $desc admits breathlessly. "Thank you for letting me take what I need from the other girl<<s>>."
-<<elseif $activeSlave.energy > 60>>
-	"I couldn't help my<<s>>elf," the $desc admits. "I wa<<s>> really, really horny and <<he 2>> was ju<<s>>t, um, there. And <<he 2>> tried to <<s>>ay no."
-<<else>>
-	"I know it'<<s>> not like me," the $desc admits. "But I a<<s>>ked _him2, like, mo<<s>>tly joking, and <<he 2>> tried to <<s>>ay no."
-<</if>>
-<br><br>
-<<run Enunciate($subSlave)>>
-$subSlave.slaveName <<if _vaginal>>looks out from under $activeSlave.slaveName<<else>>turns _his2 head<</if>> and <<if canSee($subSlave)>>looks at<<else>>faces<</if>> you too. "<<Master $subSlave>>, plea<<s>>e," _he2 begs. "P-plea<<s>>e, make $him <<s>>-<<s>>top — mhhh —" $activeSlave.slaveName shuts _him2 up by <<if _vaginal>>kissing _his2 unwilling mouth<<else>>shoving _his2 face back against the floor<</if>>. Once $he has $subSlave.slaveName back under control, $activeSlave.slaveName slows $his thrusting, reaches around behind $himself, and <<if $activeSlave.vagina != 0 && canDoVaginal($activeSlave)>>spreads $his futa pussy for you.<<else>>pulls one asscheek aside to offer you $his anus. To make the offer extra clear, $he starts winking it lewdly.<</if>>
-<br><br>
-<<run Enunciate($activeSlave)>>
-"Plea<<s>>e fuck me while I rape _him2, <<Master>>," $activeSlave.slaveName <<say>>s in a mockery of $subSlave.slaveName's <<if $subSlave.voice > 2>>high-pitched whining<<elseif $subSlave.voice > 1>>begging<<else>>deep-voiced begging<</if>>. "Ooh, or, plea<<s>>e, <<Master>>, may I flip _him2 over? I'd love to feel <<if $PC.dick>>your cock in<<s>>ide _him2 along<<s>>ide mine<<else>>that <<s>>trap-on you u<<s>>e in<<s>>ide _him2 along<<s>>ide my cock<</if>>!"
-<br><br>
-<<run Enunciate($subSlave)>>
-"Plea<<s>>e, no," sobs $subSlave.slaveName.
-
 <<case "cockmilk interception">>
 
 <<run seX($subSlave, "oral", $activeSlave, "oral")>>
@@ -871,7 +829,7 @@ voice. It's <<= contextualIntro($activeSlave, $subSlave, true)>>.
 _His2 objections sound a bit feigned, and _he2's obviously in no hurry to put an end to
 <<if areRelated($activeSlave, $subSlave)>>
 	the unmatched perversion of having _his2 own <<= relativeTerm($subSlave, $activeSlave)>> beg _him2 for sex.
-<<elseif $activeSlave.ID == $HeadGirl.ID>>
+<<elseif $activeSlave.ID == $HeadGirlID>>
 	<<if canHear($subSlave)>>listening to<<else>>having<</if>> the Head Girl beg to be allowed to put $his cock inside _him2. Usually, it's $activeSlave.slaveName giving the orders.
 <<elseif $activeSlave.fetishKnown && ($activeSlave.fetish == "dom" || $activeSlave.fetish == "sadist")>>
 	having a dominant $desc like $activeSlave.slaveName beg _him2 for sex.
@@ -1760,101 +1718,6 @@ $he adds impishly. <<if canHear($subSlave)>>Hearing this<<else>>Realizing your p
 	<</replace>>
 <</link>>
 
-<<case "simple assault">>
-
-<<link "Slide in behind">>
-	<<replace "#result">>
-	You order $activeSlave.slaveName to go back to what $he was doing. $He's a little disappointed you're not joining in, but $he obeys, pounding the crying $subSlave.slaveName without mercy. Then $activeSlave.slaveName feels the head of <<if $PC.dick>>your dick<<else>>a strap-on<</if>> brush $his butt.
-	<<run Enunciate($activeSlave)>>
-	"Ooh!" $he squeals, @@.hotpink;pleased $he was wrong after all.@@ "Ye<<s>>, thank you, <<Master>>! Fuck me! Fuck me while I rape _him2!" Underneath $him, $subSlave.slaveName cries harder, even though $activeSlave.slaveName has to stop $his thrusting for a moment to let you inside. In fact, you reflect as you hammer $activeSlave.slaveName's <<if $activeSlave.vagina != 0 && canDoVaginal($activeSlave)>><<if $activeSlave.vagina > 2>>roomy<<elseif $activeSlave.vagina > 1>>delectable<<else>>tight little<</if>> cunt<<else>><<if $activeSlave.anus > 2>>gaping<<elseif $activeSlave.anus > 1>>relaxed<<else>>poor little<</if>> asspussy<</if>>, it's a little strange that $subSlave.slaveName @@.gold;seems to think this is worse@@ than just being raped by $activeSlave.slaveName. After all, having your <<if $PC.dick>>turgid cock<<else>>formidable strap-on<</if>> sliding energetically in and out of $his <<if $activeSlave.vagina != 0 && canDoVaginal($activeSlave)>>womanhood<<else>>rectum<</if>> is cramping $activeSlave.slaveName's style a bit. Maybe it's that $subSlave.slaveName is a little squashed under there.
-	<<set $activeSlave.devotion += 4>>
-	<<if $activeSlave.vagina != 0 && canDoVaginal($activeSlave)>>
-		<<run seX($activeSlave, "vaginal", $PC, "penetrative")>>
-		<<if canImpreg($activeSlave, $PC)>>
-			<<= knockMeUp($activeSlave, 5, 0, -1, 1)>>
-		<</if>>
-	<<else>>
-		<<run seX($activeSlave, "anal", $PC, "penetrative")>>
-		<<if canImpreg($activeSlave, $PC)>>
-			<<= knockMeUp($activeSlave, 5, 1, -1, 1)>>
-		<</if>>
-	<</if>>
-	<<set $subSlave.trust -= 4>>
-	<<if _vaginal>>
-		<<run seX($subSlave, "vaginal", $activeSlave, "penetrative")>>
-		<<if canPenetrate($activeSlave) && canImpreg($subSlave, $activeSlave)>>
-			<<= knockMeUp($subSlave, 5, 0, $activeSlave.ID, 1)>>
-		<</if>>
-	<<else>>
-		<<run seX($subSlave, "anal", $activeSlave, "penetrative")>>
-		<<if canPenetrate($activeSlave) && canImpreg($subSlave, $activeSlave)>>
-			<<= knockMeUp($subSlave, 5, 1, $activeSlave.ID, 1)>>
-		<</if>>
-	<</if>>
-	<<set $slaves[$slaveIndices[$subSlave.ID]] = $subSlave>>
-	<</replace>>
-<</link>>
-<br><<link "Slide in alongside">>
-	<<replace "#result">>
-	You order $activeSlave.slaveName to flip $subSlave.slaveName over and let you in too. Just as you expected, $activeSlave.slaveName responds with a vicious giggle, and $subSlave.slaveName cries even harder.
-	<<run Enunciate($subSlave)>>
-	"Plea<<s>>e!" _he2 screams. "<<Master $subSlave>>, it'll hurt! Plea<<s>>e don't!"
-	<<set _fit = 0>>
-	<<if _vaginal>><<if $subSlave.vagina > 2>><<set _fit = 1>><</if>><<else>><<if $subSlave.anus > 2>><<set _fit = 1>><</if>><</if>>
-	<<if _fit>>
-		It's not clear what _he2's so worked up about. _His2 cavernous <<if _vaginal>>cunt<<else>>asshole<</if>> should be able to take two dicks without trouble.
-	<<else>>
-		<<if $activeSlave.dick < 5>>
-			It's not clear what _he2's so worked up about. <<if $PC.dick>>You're quite large<<else>>You use a big strap-on<</if>>, but $activeSlave.slaveName's penis is reasonably sized. It's not like $subSlave.slaveName's <<if _vaginal>>cunt<<else>>asshole<</if>> is going to be permanently damaged or anything.
-		<<else>>
-			_He2's right to be concerned. <<if $PC.dick>>You're quite large<<else>>You use a big strap-on<</if>>, and $activeSlave.slaveName's penis is huge too. $subSlave.slaveName's <<if _vaginal>>cunt<<else>>asshole<</if>> is in serious peril.
-		<</if>>
-	<</if>>
-	$activeSlave.slaveName pulls out, sits $his bare butt down on the floor, and hauls a struggling $subSlave.slaveName onto $his lap, shoving $his stiff prick back where it belongs. Then $activeSlave.slaveName hauls $subSlave.slaveName's legs back, offering you _his2 already-occupied hole.
-	<<if $subSlave.vagina != 0 && _vaginal == 0>>$subSlave.slaveName has another hole, and _he2 tearfully begs you to use it, but in vain.<</if>>
-	You jam yourself inside, enjoying $subSlave.slaveName's wriggling<<if !_fit>> and the extreme tightness of _his2 overfilled insides. _He2 spasms with pain as you force your way inside _him2<</if>>. $activeSlave.slaveName can't thrust much from where $he is, and serves mostly to tighten $subSlave.slaveName for you, but $he <<if canSee($activeSlave)>>stares into your eyes lovingly<<else>>lovingly smiles at you<</if>>. Playing such an equal sexual role with you definitely @@.mediumaquamarine;builds $his trust@@ in $his role. For _his2 part, $subSlave.slaveName is @@.gold;thoroughly degraded,@@ <<if _fit>>but physically unhurt.<<else>>and @@.orange;stretched out.@@<</if>>
-	<<set $activeSlave.trust += 4>>
-	<<set $subSlave.trust -= 4>>
-	<<if _vaginal>>
-		<<run seX($subSlave, "vaginal", $activeSlave, "penetrative")>>
-		<<if canImpreg($activeSlave, $PC)>>
-			<<= knockMeUp($activeSlave, 5, 0, -1, 1)>>
-		<</if>>
-		<<if canPenetrate($activeSlave) && canImpreg($subSlave, $activeSlave)>>
-			<<= knockMeUp($subSlave, 5, 0, $activeSlave.ID, 1)>>
-		<</if>>
-	<<else>>
-		<<run seX($subSlave, "anal", $activeSlave, "penetrative")>>
-		<<if canImpreg($activeSlave, $PC)>>
-			<<= knockMeUp($activeSlave, 5, 1, -1, 1)>>
-		<</if>>
-		<<if canPenetrate($activeSlave) && canImpreg($subSlave, $activeSlave)>>
-			<<= knockMeUp($subSlave, 5, 1, $activeSlave.ID, 1)>>
-		<</if>>
-	<</if>>
-	<<if !_fit>><<if _vaginal>><<set $subSlave.vagina++>><<else>><<set $subSlave.anus++>><</if>><</if>>
-	<<set $slaves[$slaveIndices[$subSlave.ID]] = $subSlave>>
-	<</replace>>
-<</link>>
-<br><<link "Put a stop to it">>
-	<<replace "#result">>
-	You order $activeSlave.slaveName to stop raping $subSlave.slaveName.
-	<<run Enunciate($activeSlave)>>
-	"Ye<<s>>, <<Master>>," $he <<say>>s automatically, and gets up, pulling $his dick out of $subSlave.slaveName's poor <<if _vaginal>>pussy<<else>>asshole<</if>>. $activeSlave.slaveName doesn't understand, and $his prick softens quickly with $his confusion. $He thought $he didn't need consent to fuck other slaves, and $he @@.gold;doubts $himself.@@
-	<br><br>
-	<<run Enunciate($subSlave)>>
-	$subSlave.slaveName gets to _his2 feet too, using a hand to massage _his2 outraged hole. "Thank you, <<Master $subSlave>>, thank you," _he2 repeats over and over, @@.mediumaquamarine;weeping with relief.@@
-	<<set $activeSlave.trust -= 4>>
-	<<set $subSlave.trust += 4>>
-	<<if _vaginal>>
-		<<run seX($subSlave, "vaginal", $activeSlave, "penetrative")>>
-	<<else>>
-		<<run seX($subSlave, "anal", $activeSlave, "penetrative")>>
-	<</if>>
-	<<set $slaves[$slaveIndices[$subSlave.ID]] = $subSlave>>
-	<</replace>>
-<</link>>
-
 <<case "cockmilk interception">>
 
 <<link "Step in for the stimulator">>
diff --git a/src/uncategorized/arcmgmt.tw b/src/uncategorized/arcmgmt.tw
index e965a3ea34493b0b04c59941461dbaba8150e567..f04a755a5ced275f9927b5c1117fe67029688513 100644
--- a/src/uncategorized/arcmgmt.tw
+++ b/src/uncategorized/arcmgmt.tw
@@ -1413,11 +1413,11 @@ You own
 	<</if>>
 	<<if $arcologies[0].FSNull != "unset">>
 		Your cultural openness is a powerful driver of economic activity.
-		<<set _AWeekGrowth += Math.trunc($arcologies[0].FSNull/25)>>
+		<<set _AWeekGrowth += Math.max(1,Math.trunc($arcologies[0].FSNull/25))>>
 	<</if>>
 	<<if $arcologies[0].FSRestart != "unset">>
 		Your powerful connections open many avenues of economic expansion.
-		<<set _AWeekGrowth += Math.trunc($arcologies[0].FSRestart/10)>>
+		<<set _AWeekGrowth += Math.max(1,Math.trunc($arcologies[0].FSRestart/10))>>
 	<</if>>
 	<<if $arcologies[0].FSPaternalist >= random(1,100)>>
 		This week, the careful attention to slave welfare your new society emphasizes has been a driver of prosperity.
@@ -1441,7 +1441,7 @@ You own
 		This week, intense interest in your project to revive Roman values has driven prosperity.
 		<<set _AWeekGrowth++>>
 	<<elseif $arcologies[0].FSChineseRevivalist != "unset">>
-		<<if ($HeadGirl != 0) && ($Recruiter != 0) && ($Bodyguard != 0)>>
+		<<if ($HeadGirlID != 0) && ($RecruiterID != 0) && ($BodyguardID != 0)>>
 			This week, your imperial administration, staffed with a Head Girl, a Recruiter, and a Bodyguard, has improved prosperity.
 			<<set _AWeekGrowth += 2>>
 		<</if>>
@@ -1603,6 +1603,24 @@ You own
 	<</if>>
 <</if>>
 
+<<if $TUO.schoolPresent == 1>>
+	<br>
+	The Utopian Orphanage has a <<if $TUO.schoolProsperity > 4>>very prosperous<<elseif $TUO.schoolProsperity < -4>>struggling<<else>>thriving<</if>> branch campus in $arcologies[0].name.
+	<<if $TUO.schoolProsperity >= 10>>
+		It is one of the finest slave schools in the world<<if $rep > 19000>>.<<else>>, @@.green;improving your reputation.@@<<run repX(200, "policies")>><</if>>
+		<<set $TUO.subsidize = 0>>
+		<<set $TUO.schoolProsperity = 10>>
+	<</if>>
+	<<if $TUO.subsidize == 1>>
+		You have a policy of subsidizing them.
+		<<set $TUO.schoolProsperity++>>
+	<<elseif $TUO.subsidize == -1>>
+		You have a policy of covertly undermining them.
+		<<set $TUO.schoolProsperity-->>
+	<</if>>
+<</if>>
+
+
 <<if $GRI.schoolPresent == 1>>
 	<br>
 	The Growth Research Institute has a <<if $GRI.schoolProsperity > 4>>very prosperous<<elseif $GRI.schoolProsperity < -4>>struggling<<else>>thriving<</if>> subsidiary lab in $arcologies[0].name.
diff --git a/src/uncategorized/bulkSlaveGenerate.tw b/src/uncategorized/bulkSlaveGenerate.tw
index 6e40cfd7f2c63dccd776f17e7c1b57fa58699c2a..69ec4dae50edca772c7b5257afe564b58c630dd1 100644
--- a/src/uncategorized/bulkSlaveGenerate.tw
+++ b/src/uncategorized/bulkSlaveGenerate.tw
@@ -14,6 +14,11 @@
 		<<set $discount = 375>>
 	<</if>>
 
+<<case "TUO">>
+	<<if $TUO.schoolUpgrade != 0>>
+		<<set $discount = 375>>
+	<</if>>
+
 <<case "GRI">>
 	<<if $GRI.schoolUpgrade != 0>>
 		<<set $discount = 375>>
@@ -115,6 +120,8 @@
 <<switch $slaveMarket>>
 <<case "TSS">>
 	<<set $TSS.studentsBought += $newSlaves.length>>
+<<case "TUO">>
+	<<set $TUO.studentsBought += $newSlaves.length>>
 <<case "GRI">>
 	<<set $GRI.studentsBought += $newSlaves.length>>
 <<case "SCP">>
diff --git a/src/uncategorized/buySlaves.tw b/src/uncategorized/buySlaves.tw
index 5b696027dbc552b85ec5b4927607e1a1d06c0aa0..04f8e072cdc758f48c877747cbcfb21c1abcb590 100644
--- a/src/uncategorized/buySlaves.tw
+++ b/src/uncategorized/buySlaves.tw
@@ -204,6 +204,17 @@
 			//Straightforward slaves with good training.//<<if $TSS.schoolSale == 1>> //@@.yellow;Offering your first purchase at half price this week.@@//<</if>>
 		</div>
 
+		<div class="indent">
+			[[The Utopian Orphanage][$slavesSeen += 1]] |
+			<<if $cash > _minimumFive>>
+				[[(x5)|Bulk Slave Generate][$slaveMarket = "TUO", $introType = "bulk", $numSlaves = 5]] |
+			<</if>>
+			<<if $cash > _minimumTen>>
+				[[(x10)|Bulk Slave Generate][$slaveMarket = "TUO", $introType = "bulk", $numSlaves = 10]] |
+			<</if>>
+			//Intelligent, unspoiled slaves just past their majority.//<<if $TUO.schoolSale == 1>> //@@.yellow;Offering your first purchase at half price this week.@@//<</if>>
+		</div>
+
 		<div class="indent">
 			[[Growth Research Institute][$slavesSeen += 1]] |
 			<<if $cash > _minimumFive>>
diff --git a/src/uncategorized/costsBudget.js b/src/uncategorized/costsBudget.js
index 99e0a69c50b07255a3680793125387601e4a5a67..3f68e52de47fae26131a844e55ee7078d8fc3019 100644
--- a/src/uncategorized/costsBudget.js
+++ b/src/uncategorized/costsBudget.js
@@ -1,11 +1,4 @@
 App.UI.Budget.Cost = function() {
-	let el = document.createElement('p');
-	let table = document.createElement("table");
-	table.classList.add("budget");
-	let node;
-	let subNode;
-	let cell;
-	let array;
 	let coloredRow = true;
 
 	// Set up object to track calculated displays
@@ -14,52 +7,38 @@ App.UI.Budget.Cost = function() {
 	const profits = "lastWeeksCashProfits";
 	const F = V.lastWeeksGatheredTotals;
 
+	const table = document.createElement("table");
+	table.classList.add("budget");
+
 	// HEADER
-	let header = table.createTHead();
-	let row = header.insertRow(0);
-	let th = document.createElement("th");
-	let pent = document.createElement("h1");
-	pent.textContent = "Budget Overview";
-	th.appendChild(pent);
-	row.appendChild(th);
-
-	array = [
-		"Income",
-		"Expense",
-		"Totals"
-	];
-	for (let th of array) {
-		let cell = document.createElement("th");
-		cell.textContent = th;
-		row.appendChild(cell);
-	}
+	generateHeader();
 
-	let body = document.createElement('tbody');
-	table.appendChild(body);
-	row = body.insertRow();
+	// BODY
+	table.createTBody();
 
 	// HEADER: FACILITIES
 	createSectionHeader("Facilities");
 
 	// PENTHOUSE
-	generateRowGroup("Penthouse", "PENTHOUSE");
-	generateRowCategory("Rest", "slaveAssignmentRest");
-	generateRowCategory("RestVign", "slaveAssignmentRestVign");
-	generateRowCategory("Fucktoy", "slaveAssignmentFucktoy");
-	generateRowCategory("Classes", "slaveAssignmentClasses");
-	generateRowCategory("House", "slaveAssignmentHouse");
-	generateRowCategory("HouseVign", "slaveAssignmentHouseVign");
-	generateRowCategory("Whore", "slaveAssignmentWhore");
-	generateRowCategory("WhoreVign", "slaveAssignmentWhoreVign");
-	generateRowCategory("Public", "slaveAssignmentPublic");
-	generateRowCategory("PublicVign", "slaveAssignmentPublicVign");
-	generateRowCategory("Subordinate", "slaveAssignmentSubordinate");
-	generateRowCategory("Milked", "slaveAssignmentMilked");
-	generateRowCategory("MilkedVign", "slaveAssignmentMilkedVign");
-	generateRowCategory("ExtraMilk", "slaveAssignmentExtraMilk");
-	generateRowCategory("ExtraMilkVign", "slaveAssignmentExtraMilkVign");
-	generateRowCategory("Gloryhole", "slaveAssignmentGloryhole");
-	generateRowCategory("Confinement", "slaveAssignmentConfinement");
+	addToggle(generateRowGroup("Penthouse", "PENTHOUSE"), [
+		generateRowCategory("Rest", "slaveAssignmentRest"),
+		generateRowCategory("RestVign", "slaveAssignmentRestVign"),
+		generateRowCategory("Fucktoy", "slaveAssignmentFucktoy"),
+		generateRowCategory("Classes", "slaveAssignmentClasses"),
+		generateRowCategory("House", "slaveAssignmentHouse"),
+		generateRowCategory("HouseVign", "slaveAssignmentHouseVign"),
+		generateRowCategory("Whore", "slaveAssignmentWhore"),
+		generateRowCategory("WhoreVign", "slaveAssignmentWhoreVign"),
+		generateRowCategory("Public", "slaveAssignmentPublic"),
+		generateRowCategory("PublicVign", "slaveAssignmentPublicVign"),
+		generateRowCategory("Subordinate", "slaveAssignmentSubordinate"),
+		generateRowCategory("Milked", "slaveAssignmentMilked"),
+		generateRowCategory("MilkedVign", "slaveAssignmentMilkedVign"),
+		generateRowCategory("ExtraMilk", "slaveAssignmentExtraMilk"),
+		generateRowCategory("ExtraMilkVign", "slaveAssignmentExtraMilkVign"),
+		generateRowCategory("Gloryhole", "slaveAssignmentGloryhole"),
+		generateRowCategory("Confinement", "slaveAssignmentConfinement")
+	]);
 	// Other
 	generateRowCategory("Chosing Own Assignment", "slaveAssignmentChoice");
 
@@ -67,281 +46,327 @@ App.UI.Budget.Cost = function() {
 
 	// HEAD GIRL
 	// find passage name for HGSuite
-	generateRowGroup(V.HGSuiteNameCaps, "HEADGIRLSUITE");
-	generateRowCategory("Head Girl", "slaveAssignmentHeadgirl");
-	generateRowCategory("Head Girl Fucktoys", "slaveAssignmentHeadgirlsuite");
+	addToggle(generateRowGroup(V.HGSuiteNameCaps, "HEADGIRLSUITE"), [
+		generateRowCategory("Head Girl", "slaveAssignmentHeadgirl"),
+		generateRowCategory("Head Girl Fucktoys", "slaveAssignmentHeadgirlsuite")
+	]);
 
 	// RECRUITER
-	generateRowGroup("Recruiter", "RECRUITER");
-	generateRowCategory("Recruiter", "slaveAssignmentRecruiter");
+	addToggle(generateRowGroup("Recruiter", "RECRUITER"), [
+		generateRowCategory("Recruiter", "slaveAssignmentRecruiter")
+	]);
 
 	// BODYGUARD
 	// find passage name for Armory
-	generateRowGroup("Armory", "DOJO");
-	generateRowCategory("Bodyguard", "slaveAssignmentBodyguard");
+	addToggle(generateRowGroup("Armory", "DOJO"), [
+		generateRowCategory("Bodyguard", "slaveAssignmentBodyguard")
+	]);
 
 	// CONCUBINE
-	generateRowGroup("Master Suite", "MASTERSUITE");
-	generateRowCategory("Master Suite Operation", "masterSuite");
-	generateRowCategory("Master Suite Concubine", "slaveAssignmentConcubine");
-	generateRowCategory("Master Suite Fucktoys", "slaveAssignmentMastersuite");
+	addToggle(generateRowGroup("Master Suite", "MASTERSUITE"), [
+		generateRowCategory("Master Suite Operation", "masterSuite"),
+		generateRowCategory("Master Suite Concubine", "slaveAssignmentConcubine"),
+		generateRowCategory("Master Suite Fucktoys", "slaveAssignmentMastersuite")
+	]);
 
 	// AGENT
-	generateRowGroup("Agent", "AGENT");
-	generateRowCategory("Agent", "slaveAssignmentAgent");
-	generateRowCategory("Agent's Partner", "slaveAssignmentAgentPartner");
+	addToggle(generateRowGroup("Agent", "AGENT"), [
+		generateRowCategory("Agent", "slaveAssignmentAgent"),
+		generateRowCategory("Agent's Partner", "slaveAssignmentAgentPartner")
+	]);
 
 	// ARCADE
-	generateRowGroup(V.arcadeNameCaps, "ARCADE");
-	generateRowCategory("Arcade Operation", "arcade");
-	generateRowCategory("Arcade Fuckdolls", "slaveAssignmentArcade");
+	addToggle(generateRowGroup(V.arcadeNameCaps, "ARCADE"), [
+		generateRowCategory("Arcade Operation", "arcade"),
+		generateRowCategory("Arcade Fuckdolls", "slaveAssignmentArcade")
+	]);
 
 	// BROTHEL
-	generateRowGroup(V.brothelNameCaps, "BROTHEL");
-	generateRowCategory("Brothel Operation", "brothel");
-	generateRowCategory("Brothel Madam", "slaveAssignmentMadam");
-	generateRowCategory("Brothel MadamVign", "slaveAssignmentMadamVign");
-	generateRowCategory("Brothel Whore", "slaveAssignmentBrothel");
-	generateRowCategory("Brothel WhoreVign", "slaveAssignmentBrothelVign");
-	generateRowCategory("Brothel Ads", "brothelAds");
+	addToggle(generateRowGroup(V.brothelNameCaps, "BROTHEL"), [
+		generateRowCategory("Brothel Operation", "brothel"),
+		generateRowCategory("Brothel Madam", "slaveAssignmentMadam"),
+		generateRowCategory("Brothel MadamVign", "slaveAssignmentMadamVign"),
+		generateRowCategory("Brothel Whore", "slaveAssignmentBrothel"),
+		generateRowCategory("Brothel WhoreVign", "slaveAssignmentBrothelVign"),
+		generateRowCategory("Brothel Ads", "brothelAds")
+	]);
 
 	// CELLBLOCK
-	generateRowGroup(V.cellblockNameCaps, "CELLBLOCK");
-	generateRowCategory("Cellblock Operation", "cellblock");
-	generateRowCategory("Cellblock Warden", "slaveAssignmentWarden");
-	generateRowCategory("Cellblock Slaves", "slaveAssignmentCellblock");
+	addToggle(generateRowGroup(V.cellblockNameCaps, "CELLBLOCK"), [
+		generateRowCategory("Cellblock Operation", "cellblock"),
+		generateRowCategory("Cellblock Warden", "slaveAssignmentWarden"),
+		generateRowCategory("Cellblock Slaves", "slaveAssignmentCellblock")
+	]);
 
 	// CLUB
-	generateRowGroup(V.clubNameCaps, "CLUB");
-	generateRowCategory("Club Operation", "club");
-	generateRowCategory("Club DJ", "slaveAssignmentDj");
-	generateRowCategory("Club DJVign", "slaveAssignmentDjVign");
-	generateRowCategory("Club Public", "slaveAssignmentClub");
-	generateRowCategory("Club PublicVign", "slaveAssignmentClubVign");
-	generateRowCategory("Club Ads", "clubAds");
+	addToggle(generateRowGroup(V.clubNameCaps, "CLUB"), [
+		generateRowCategory("Club Operation", "club"),
+		generateRowCategory("Club DJ", "slaveAssignmentDj"),
+		generateRowCategory("Club DJVign", "slaveAssignmentDjVign"),
+		generateRowCategory("Club Public", "slaveAssignmentClub"),
+		generateRowCategory("Club PublicVign", "slaveAssignmentClubVign"),
+		generateRowCategory("Club Ads", "clubAds")
+	]);
 
 	// CLINIC
-	generateRowGroup(V.clinicNameCaps, "CLINIC");
-	generateRowCategory("Clinic Operation", "clinic");
-	generateRowCategory("Clinic Nurse", "slaveAssignmentNurse");
-	generateRowCategory("Clinic Slaves", "slaveAssignmentClinic");
+	addToggle(generateRowGroup(V.clinicNameCaps, "CLINIC"), [
+		generateRowCategory("Clinic Operation", "clinic"),
+		generateRowCategory("Clinic Nurse", "slaveAssignmentNurse"),
+		generateRowCategory("Clinic Slaves", "slaveAssignmentClinic")
+	]);
 
 	// DAIRY
-	generateRowGroup(V.dairyNameCaps, "DAIRY");
-	generateRowCategory("Dairy Operation", "dairy");
-	generateRowCategory("Dairy Milkmaid", "slaveAssignmentMilkmaid");
-	generateRowCategory("Dairy Cows", "slaveAssignmentDairy");
-	generateRowCategory("Dairy Cows", "slaveAssignmentDairyVign");
+	addToggle(generateRowGroup(V.dairyNameCaps, "DAIRY"), [
+		generateRowCategory("Dairy Operation", "dairy"),
+		generateRowCategory("Dairy Milkmaid", "slaveAssignmentMilkmaid"),
+		generateRowCategory("Dairy Cows", "slaveAssignmentDairy"),
+		generateRowCategory("Dairy Cows", "slaveAssignmentDairyVign")
+	]);
 
 	// FARMYARD
-	generateRowGroup(V.farmyardNameCaps, "FARMYARD");
-	generateRowCategory("Farmyard Operation", "farmyard");
-	generateRowCategory("Farmyard Farmer", "slaveAssignmentFarmer");
-	generateRowCategory("Farmyard Farmhands", "slaveAssignmentFarmyard");
-	generateRowCategory("Farmyard FarmhandsVign", "slaveAssignmentFarmyardVign");
+	addToggle(generateRowGroup(V.farmyardNameCaps, "FARMYARD"), [
+		generateRowCategory("Farmyard Operation", "farmyard"),
+		generateRowCategory("Farmyard Farmer", "slaveAssignmentFarmer"),
+		generateRowCategory("Farmyard Farmhands", "slaveAssignmentFarmyard"),
+		generateRowCategory("Farmyard FarmhandsVign", "slaveAssignmentFarmyardVign")
+	]);
 
 	// INCUBATOR
-	generateRowGroup(V.incubatorNameCaps, "INCUBATOR");
-	generateRowCategory("Incubator Operation", "incubator");
-	generateRowCategory("Incubator Babies", "incubatorSlaves");
+	addToggle(generateRowGroup(V.incubatorNameCaps, "INCUBATOR"), [
+		generateRowCategory("Incubator Operation", "incubator"),
+		generateRowCategory("Incubator Babies", "incubatorSlaves")
+	]);
 
 	// NURSERY
-	generateRowGroup(V.nurseryNameCaps, "NURSERY");
-	generateRowCategory("Nursery Operation", "nursery");
-	generateRowCategory("Nursery Matron", "slaveAssignmentMatron");
-	generateRowCategory("Nursery Nannies", "slaveAssignmentNursery");
-	generateRowCategory("Nursery NanniesVign", "slaveAssignmentNurseryVign");
+	addToggle(generateRowGroup(V.nurseryNameCaps, "NURSERY"), [
+		generateRowCategory("Nursery Operation", "nursery"),
+		generateRowCategory("Nursery Matron", "slaveAssignmentMatron"),
+		generateRowCategory("Nursery Nannies", "slaveAssignmentNursery"),
+		generateRowCategory("Nursery NanniesVign", "slaveAssignmentNurseryVign")
+	]);
 
 	// PIT
-	generateRowGroup(V.pitNameCaps, "PIT");
-	generateRowCategory("Pit Operation", "pit");
+	addToggle(generateRowGroup(V.pitNameCaps, "PIT"), [
+		generateRowCategory("Pit Operation", "pit")
+	]);
 
 	// PROSTHETIC LAB
-	generateRowGroup("Prosthetic Lab", "PROSTHETICLAB");
-	generateRowCategory("Prosthetic Lab Operation", "lab");
-	generateRowCategory("Prostethic Lab Research", "labResearch");
-	generateRowCategory("Prostethic Lab Scientists", "labScientists");
-	generateRowCategory("Prostethic Lab Menials", "labMenials");
+	addToggle(generateRowGroup("Prosthetic Lab", "PROSTHETICLAB"), [
+		generateRowCategory("Prosthetic Lab Operation", "lab"),
+		generateRowCategory("Prostethic Lab Research", "labResearch"),
+		generateRowCategory("Prostethic Lab Scientists", "labScientists"),
+		generateRowCategory("Prostethic Lab Menials", "labMenials")
+	]);
 
 	// SCHOOLROOM
-	generateRowGroup(V.schoolroomNameCaps, "SCHOOLROOM");
-	generateRowCategory("Schoolroom Operation", "school");
-	generateRowCategory("Schoolroom Teacher", "slaveAssignmentTeacher");
-	generateRowCategory("Schoolroom Students", "slaveAssignmentSchool");
+	addToggle(generateRowGroup(V.schoolroomNameCaps, "SCHOOLROOM"), [
+		generateRowCategory("Schoolroom Operation", "school"),
+		generateRowCategory("Schoolroom Teacher", "slaveAssignmentTeacher"),
+		generateRowCategory("Schoolroom Students", "slaveAssignmentSchool")
+	]);
 
 	// SERVANTS' QUARTERS
-	generateRowGroup(V.servantsQuartersNameCaps, "SERVANTSQUARTERS");
-	generateRowCategory("Servants' Quarters Operation", "servantsQuarters");
-	generateRowCategory("Servants' Quarters Steward", "slaveAssignmentSteward");
-	generateRowCategory("Servants' Quarters Servants", "slaveAssignmentQuarter");
-	generateRowCategory("Servants' Quarters ServantsVign", "slaveAssignmentQuarterVign");
+	addToggle(generateRowGroup(V.servantsQuartersNameCaps, "SERVANTSQUARTERS"), [
+		generateRowCategory("Servants' Quarters Operation", "servantsQuarters"),
+		generateRowCategory("Servants' Quarters Steward", "slaveAssignmentSteward"),
+		generateRowCategory("Servants' Quarters Servants", "slaveAssignmentQuarter"),
+		generateRowCategory("Servants' Quarters ServantsVign", "slaveAssignmentQuarterVign")
+	]);
 
 	// SPA
-	generateRowGroup(V.spaNameCaps, "SPA");
-	generateRowCategory("Spa Operation", "spa");
-	generateRowCategory("Spa Attendant", "slaveAssignmentAttendant");
-	generateRowCategory("Spa Slaves", "slaveAssignmentSpa");
+	addToggle(generateRowGroup(V.spaNameCaps, "SPA"), [
+		generateRowCategory("Spa Operation", "spa"),
+		generateRowCategory("Spa Attendant", "slaveAssignmentAttendant"),
+		generateRowCategory("Spa Slaves", "slaveAssignmentSpa")
+	]);
 
 	// HEADER: ARCOLOGY
 	createSectionHeader("Arcology");
 
 	// SLAVES
-	generateRowGroup("Miscellaneous Slave Income and Expenses", "SLAVES");
-	generateRowCategory("Slave Porn", "porn");
-	generateRowCategory("Slave Modifcations", "slaveMod");
-	generateRowCategory("Slave Surgery", "slaveSurgery");
-	generateRowCategory("Slave Birhting", "birth");
+	addToggle(generateRowGroup("Miscellaneous Slave Income and Expenses", "SLAVES"), [
+		generateRowCategory("Slave Porn", "porn"),
+		generateRowCategory("Slave Modifications", "slaveMod"),
+		generateRowCategory("Slave Surgery", "slaveSurgery"),
+		generateRowCategory("Slave Birthing", "birth")
+	]);
 
 	// MENIAL LABOR
-	generateRowGroup("Menial Labor", "LABOR");
-	generateRowCategory("Menials: Slaves", "menialTrades");
-	generateRowCategory("Menials: Fuckdolls", "fuckdolls");
-	generateRowCategory("Menials: Bioreactors", "menialBioreactors");
+	addToggle(generateRowGroup("Menial Labor", "LABOR"), [
+		generateRowCategory("Menials: Slaves", "menialTrades"),
+		generateRowCategory("Menials: Fuckdolls", "fuckdolls"),
+		generateRowCategory("Menials: Bioreactors", "menialBioreactors")
+	]);
 
 	// FLIPPING
-	generateRowGroup("Flipping", "FLIPPING");
-	generateRowCategory("Slave Transfer", "slaveTransfer");
-	generateRowCategory("Menials", "menialTransfer");
-	generateRowCategory("Fuckdolls", "fuckdollsTransfer");
-	generateRowCategory("Bioreactors", "menialBioreactorsTransfer");
-	generateRowCategory("Assistant: Menials", "menialTransferA");
-	generateRowCategory("Assistant: Fuckdolls", "fuckdollsTransferA");
-	generateRowCategory("Assistant: Bioreactors", "menialBioreactorsTransferA");
-	generateRowCategory("Menial Retirement", "menialRetirement");
-	generateRowCategory("Scientist Transfer", "labScientistsTransfer");
-	generateRowCategory("Slave Babies", "babyTransfer");
+	addToggle(generateRowGroup("Flipping", "FLIPPING"), [
+		generateRowCategory("Slave Transfer", "slaveTransfer"),
+		generateRowCategory("Menials", "menialTransfer"),
+		generateRowCategory("Fuckdolls", "fuckdollsTransfer"),
+		generateRowCategory("Bioreactors", "menialBioreactorsTransfer"),
+		generateRowCategory("Assistant: Menials", "menialTransferA"),
+		generateRowCategory("Assistant: Fuckdolls", "fuckdollsTransferA"),
+		generateRowCategory("Assistant: Bioreactors", "menialBioreactorsTransferA"),
+		generateRowCategory("Menial Retirement", "menialRetirement"),
+		generateRowCategory("Scientist Transfer", "labScientistsTransfer"),
+		generateRowCategory("Slave Babies", "babyTransfer")
+	]);
 
 	// FINANCIALS
-	generateRowGroup("Financials", "FINANCIALS");
-	generateRowCategory("Weather", "weather");
-	generateRowCategory("Rents", "rents");
-	generateRowCategory("Fines", "fines");
-	generateRowCategory("Events", "event");
-	generateRowCategory("Capital Expenses", "capEx");
-	generateRowCategory("Future Society Shaping", "futureSocieties");
-	generateRowCategory("School Subsidy", "schoolBacking");
-	generateRowCategory("Arcology conflict", "war");
-	generateRowCategory("Cheating", "cheating");
+	addToggle(generateRowGroup("Financials", "FINANCIALS"), [
+		generateRowCategory("Weather", "weather"),
+		generateRowCategory("Rents", "rents"),
+		generateRowCategory("Fines", "fines"),
+		generateRowCategory("Events", "event"),
+		generateRowCategory("Capital Expenses", "capEx"),
+		generateRowCategory("Future Society Shaping", "futureSocieties"),
+		generateRowCategory("School Subsidy", "schoolBacking"),
+		generateRowCategory("Arcology conflict", "war"),
+		generateRowCategory("Cheating", "cheating")
+	]);
 
 	// POLICIES
-	generateRowGroup("Policies", "POLICIES");
-	generateRowCategory("Policies", "policies");
-	generateRowCategory("Subsidies and Barriers", "subsidiesAndBarriers");
+	addToggle(generateRowGroup("Policies", "POLICIES"), [
+		generateRowCategory("Policies", "policies"),
+		generateRowCategory("Subsidies and Barriers", "subsidiesAndBarriers")
+	]);
 
 	// EDICTS
-	generateRowGroup("Edicts", "EDICTS");
-	generateRowCategory("Edicts", "edicts");
+	addToggle(generateRowGroup("Edicts", "EDICTS"), [
+		generateRowCategory("Edicts", "edicts")
+	]);
 
 	// PERSONAL FINANCE
-	generateRowGroup("Personal Finance", "PERSONALFINANCE");
-	generateRowCategory("Personal Business", "personalBusiness");
-	generateRowCategory("Personal Living Expenses", "personalLivingExpenses");
-	generateRowCategory("Your skills", "PCSkills");
-	generateRowCategory("Your training expenses", "PCtraining");
-	generateRowCategory("Your medical expenses", "PCmedical");
-	generateRowCategory("Citizen Orphanage", "citizenOrphanage");
-	generateRowCategory("Private Orphanage", "privateOrphanage");
-	generateRowCategory("Stock dividents", "stocks");
-	generateRowCategory("Stock trading", "stocksTraded");
+	addToggle(generateRowGroup("Personal Finance", "PERSONALFINANCE"), [
+		generateRowCategory("Personal Business", "personalBusiness"),
+		generateRowCategory("Personal Living Expenses", "personalLivingExpenses"),
+		generateRowCategory("Your skills", "PCSkills"),
+		generateRowCategory("Your training expenses", "PCtraining"),
+		generateRowCategory("Your medical expenses", "PCmedical"),
+		generateRowCategory("Citizen Orphanage", "citizenOrphanage"),
+		generateRowCategory("Private Orphanage", "privateOrphanage"),
+		generateRowCategory("Stock dividents", "stocks"),
+		generateRowCategory("Stock trading", "stocksTraded")
+	]);
 
 	// SECURITY
-	generateRowGroup("Security", "SECURITY");
-	generateRowCategory("Mercenaries", "mercenaries");
-	generateRowCategory("Security Expansion", "securityExpansion");
-	generateRowCategory("Special Forces", "specialForces");
-	generateRowCategory("Peacekeepers", "peacekeepers");
+	addToggle(generateRowGroup("Security", "SECURITY"), [
+		generateRowCategory("Mercenaries", "mercenaries"),
+		generateRowCategory("Security Expansion", "securityExpansion"),
+		generateRowCategory("Special Forces", "specialForces"),
+		generateRowCategory("Peacekeepers", "peacekeepers")
+	]);
 
 	// BUDGET REPORT
-	createSectionHeader("Budget Report");
-
-	row = table.insertRow();
-	cell = row.insertCell();
-	cell.append("Tracked totals");
-
-	cell = row.insertCell();
-	V.lastWeeksCashIncome.Total = hashSum(V.lastWeeksCashIncome);
-	cell.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashIncome.Total)));
-
-	cell = row.insertCell();
-	V.lastWeeksCashExpenses.Total = hashSum(V.lastWeeksCashExpenses);
-	cell.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashExpenses.Total)));
-
-	cell = row.insertCell();
-
-	V.lastWeeksCashProfits.Total = (V.lastWeeksCashIncome.Total + V.lastWeeksCashExpenses.Total);
-	// each "profit" item is calculated on this sheet, and there's two ways to generate a profit total: the difference of the income and expense totals, and adding all the profit items. If they aren't the same, I probably forgot to properly add an item's profit calculation to this sheet.
-	node = new DocumentFragment();
-	let total = hashSum(V.lastWeeksCashProfits) - V.lastWeeksCashProfits.Total;
-	if (V.lastWeeksCashProfits.Total !== total) { // Profits includes the total number of profits, so we have to subtract it back out
-		node.append(cashFormatColorDOM(Math.trunc(total)));
-		subNode = document.createElement('span');
-		subNode.className = "red";
-		subNode.textContent = "Fix profit calc";
-		node.append(subNode);
+	generateSummary();
+
+	return table;
+
+	function generateHeader() {
+		const header = table.createTHead();
+		const row = header.insertRow();
+		const cell = row.insertCell();
+		let pent = document.createElement("h1");
+		pent.textContent = "Budget Overview";
+		cell.appendChild(pent);
+
+		for (let column of ["Income", "Expense", "Totals"]) {
+			let cell = document.createElement("th");
+			cell.textContent = column;
+			row.appendChild(cell);
+		}
 	}
-	node.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashProfits.Total)));
-	cell.append(node);
-	flipColors(row);
-
-	row = table.insertRow();
-	cell = row.insertCell();
-	cell.append(`Expenses budget for week ${V.week + 1}`);
-	cell = row.insertCell();
-	cell = row.insertCell();
-	cell.append(cashFormatColorDOM(-V.costs));
-	flipColors(row);
-
-	row = table.insertRow();
-	cell = row.insertCell();
-	cell.append(`Last week actuals`);
-	cell = row.insertCell();
-	cell = row.insertCell();
-	cell = row.insertCell();
-	cell.append(cashFormatColorDOM(V.cash - V.cashLastWeek));
-	flipColors(row);
-
-	row = table.insertRow();
-	if ((V.cash - V.cashLastWeek) === V.lastWeeksCashProfits.Total) {
+
+	function generateSummary() {
+		let row, cell;
+		createSectionHeader("Budget Report");
+
+		row = table.insertRow();
+		cell = row.insertCell();
+		cell.append("Tracked totals");
+
+		cell = row.insertCell();
+		V.lastWeeksCashIncome.Total = hashSum(V.lastWeeksCashIncome);
+		cell.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashIncome.Total)));
+
 		cell = row.insertCell();
-		node = document.createElement('span');
-		node.className = "green";
-		node.textContent = `The books are balanced, ${properTitle()}!`;
-		cell.append(node);
-	} else {
+		V.lastWeeksCashExpenses.Total = hashSum(V.lastWeeksCashExpenses);
+		cell.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashExpenses.Total)));
+
 		cell = row.insertCell();
-		cell.append("Transaction tracking off by:");
+
+		V.lastWeeksCashProfits.Total = (V.lastWeeksCashIncome.Total + V.lastWeeksCashExpenses.Total);
+		// each "profit" item is calculated on this sheet, and there's two ways to generate a profit total: the
+		// difference of the income and expense totals, and adding all the profit items. If they aren't the same, I
+		// probably forgot to properly add an item's profit calculation to this sheet.
+		const total = hashSum(V.lastWeeksCashProfits) - V.lastWeeksCashProfits.Total;
+		if (V.lastWeeksCashProfits.Total !== total) { // Profits includes the total number of profits, so we have to subtract it back out
+			cell.append(cashFormatColorDOM(Math.trunc(total)));
+			const span = document.createElement('span');
+			span.className = "red";
+			span.textContent = "Fix profit calc";
+			cell.append(span);
+		}
+		cell.append(cashFormatColorDOM(Math.trunc(V.lastWeeksCashProfits.Total)));
+		flipColors(row);
+
+		row = table.insertRow();
 		cell = row.insertCell();
+		cell.append(`Expenses budget for week ${V.week + 1}`);
 		cell = row.insertCell();
 		cell = row.insertCell();
-		cell.append(cashFormatColorDOM((V.cash - V.cashLastWeek) - V.lastWeeksCashProfits.Total));
-	}
-	flipColors(row);
+		cell.append(cashFormatColorDOM(-V.costs));
+		flipColors(row);
+
+		row = table.insertRow();
+		cell = row.insertCell();
+		cell.append(`Last week actuals`);
+		cell = row.insertCell();
+		cell = row.insertCell();
+		cell = row.insertCell();
+		cell.append(cashFormatColorDOM(V.cash - V.cashLastWeek));
+		flipColors(row);
 
-	el.appendChild(table);
-	return jQuery('#costTable').empty().append(el);
+		row = table.insertRow();
+		if ((V.cash - V.cashLastWeek) === V.lastWeeksCashProfits.Total) {
+			cell = row.insertCell();
+			const span = document.createElement('span');
+			span.className = "green";
+			span.textContent = `The books are balanced, ${properTitle()}!`;
+			cell.append(span);
+		} else {
+			cell = row.insertCell();
+			cell.append("Transaction tracking off by:");
+			cell = row.insertCell();
+			cell = row.insertCell();
+			cell = row.insertCell();
+			cell.append(cashFormatColorDOM((V.cash - V.cashLastWeek) - V.lastWeeksCashProfits.Total));
+		}
+		flipColors(row);
+	}
 
 	function createSectionHeader(text) {
 		coloredRow = true; // make sure the following section begins with color.
-		row = table.insertRow();
-		cell = row.insertCell();
-		let headline = document.createElement('h2');
+		const row = table.insertRow();
+		const cell = row.insertCell();
+		const headline = document.createElement('h2');
 		headline.textContent = text;
 		cell.append(headline);
 	}
 
 	function generateRowCategory(node, category) {
 		if (category === "") {
-			row = table.insertRow();
+			const row = table.insertRow();
 			row.append(document.createElement('br'));
 			row.insertCell();
 			row.insertCell();
 			row.insertCell();
 			flipColors(row);
-			return;
+			return row;
 		}
 
 		if (V[income][category] || V[expenses][category] || V.showAllEntries.costsBudget) {
-			row = table.insertRow();
+			const row = table.insertRow();
 			let cell = row.insertCell();
 			cell.append(node);
 			cell = row.insertCell();
@@ -351,49 +376,67 @@ App.UI.Budget.Cost = function() {
 			flipColors(row);
 			cell = row.insertCell();
 			cell.append(cashFormatColorDOM(V[profits][category]));
+			return row;
 		}
 	}
 
 	function generateRowGroup(title, group) {
 		if (F[group].income || F[group].expenses || V.showAllEntries.costsBudget) {
-			row = table.insertRow();
-			cell = row.insertCell();
-			let headline = document.createElement('h3');
+			const row = table.insertRow();
+			let cell = row.insertCell();
+			const headline = document.createElement('h3');
 			headline.textContent = title;
 			cell.append(headline);
 			cell = row.insertCell();
-			cell.append(cashFormatColorDOM(F[group].income, null, true));
+			cell.append(cashFormatColorDOM(F[group].income, null));
 			cell = row.insertCell();
-			cell.append(cashFormatColorDOM(F[group].expenses, null, true));
+			cell.append(cashFormatColorDOM(F[group].expenses, null));
 			cell = row.insertCell();
-			cell.append(cashFormatColorDOM(F[group].profits, null, true));
+			cell.append(cashFormatColorDOM(F[group].profits, null));
+			return row;
+		}
+	}
+
+	/**
+	 * @param {HTMLTableRowElement} head
+	 * @param {Array<HTMLTableRowElement>} content
+	 */
+	function addToggle(head, content) {
+		if (!head) {
+			return;
+		}
+		content = content.filter(e => !!e);
+		if (content.length === 0) {
+			return;
 		}
+		App.UI.DOM.elementToggle(head, content);
 	}
 
-	function cashFormatColorDOM(s, invert = false, temp) {
+	function cashFormatColorDOM(cash, invert = false) {
 		if (invert) {
-			s = -1 * s;
+			cash = -1 * cash;
 		}
-		let el = document.createElement('span');
-		el.textContent = cashFormat(s);
-		if (temp === true) {
-			// Gray display for totals when expanded
-			el.className = "gray";
+		let span = document.createElement('span');
+		span.textContent = cashFormat(cash);
+		if (cash === 0) {
+			// cash overwrites gray, so we don't use it here.
+			span.classList.add("gray");
 		} else {
+			span.classList.add("cash");
 			// Display red if the value is negative, unless invert is true
-			if (s < 0) {
-				el.className = "red";
+			if (cash < 0) {
+				span.classList.add("dec");
 				// Yellow for positive
-			} else if (s > 0) {
-				el.className = "yellowgreen";
-				// White for exactly zero
+			} else if (cash > 0) {
+				span.classList.add("inc");
+				// Gray for exactly zero
 			}
 		}
-		return el;
+		return span;
 	}
 
 	function flipColors(row) {
-		if (coloredRow === true) {
+		if (coloredRow) {
 			row.classList.add("colored");
 		}
 		coloredRow = !coloredRow;
diff --git a/src/uncategorized/costsBudget.tw b/src/uncategorized/costsBudget.tw
index 8af2335e3e9bf82a23307de7ebd2671c596c2e0c..fa9d10999900d0ebcacae93fc1d792000fe41c83 100644
--- a/src/uncategorized/costsBudget.tw
+++ b/src/uncategorized/costsBudget.tw
@@ -82,14 +82,13 @@
 .addValue("Normal", 0).on().addValue("Show Empty Entries", 1).neutral()>>
 <<includeDOM _options.render()>>
 
-<p>
 /* Table of Totals */
-<<if ndef $lastWeeksCashIncome>>
-	Financial data currently unavailable.
-<<else>>
-	<span id="costTable"></span>
-	<script>App.UI.Budget.Cost()</script>
-<</if>>
+<p>
+	<<if ndef $lastWeeksCashIncome>>
+		Financial data currently unavailable.
+	<<else>>
+		<<includeDOM App.UI.Budget.Cost()>>
+	<</if>>
 </p>
 
 <<if ndef $lastWeeksCashErrors>>
diff --git a/src/uncategorized/costsReport.tw b/src/uncategorized/costsReport.tw
index 5b365daa246306f026575687ddd11c9cb3f83d64..ff255584896ea0722f7a034e51a9111abd1f657b 100644
--- a/src/uncategorized/costsReport.tw
+++ b/src/uncategorized/costsReport.tw
@@ -7,7 +7,7 @@
 	<<capture $i>>
 		<br style="clear:both"><<if $lineSeparations == 0>><br><<else>><hr style="margin:0"><</if>>
 		[[$slaves[$i].slaveName|Slave Interact][$activeSlave = $slaves[$i]]] will $slaves[$i].assignment.
-		<<includeDOM SlaveExpenses (V.slaves[V.i])>>
+		<<includeDOM slaveExpenses($slaves[$i])>>
 	<</capture>>
 <</for>>
 
@@ -295,7 +295,7 @@ $researchLab.level > 0>>
 	<br>__Society Shaping Expenses__: @@.yellowgreen;<<print cashFormat($FSSpending)>>@@
 	<<set _arcologyCosts += $FSSpending>>
 <</if>>
-<<if $Recruiter != 0>>
+<<if $RecruiterID != 0>>
 	<br>__Recruiting Expenses__: @@.yellowgreen;<<print cashFormat(250)>>@@
 	<<set _arcologyCosts += 250>>
 <</if>>
@@ -308,6 +308,13 @@ $researchLab.level > 0>>
 		<<set _schoolSubsidy = 1>>
 	<</if>>
 <</if>>
+<<if ($TUO.schoolPresent > 0)>>
+	<<set _school = "The Utopian Orphanage">>
+	<<set _schoolPresent = 1>>
+	<<if $TUO.subsidize != 0>>
+		<<set _schoolSubsidy = 1>>
+	<</if>>
+<</if>>
 <<if ($GRI.schoolPresent > 0)>>
 	<<set _school = "Growth Research Institute">>
 	<<set _schoolPresent = 1>>
diff --git a/src/uncategorized/costsReportSlaves.tw b/src/uncategorized/costsReportSlaves.tw
index ae0467edc474f629deb9c74255662f840c8890c3..0bfb98f93d160dc90419775e5e848ad647063f67 100644
--- a/src/uncategorized/costsReportSlaves.tw
+++ b/src/uncategorized/costsReportSlaves.tw
@@ -8,6 +8,6 @@
 	<<capture $i>>
 	<br style="clear:both"><<if $lineSeparations == 0>><br><<else>><hr style="margin:0"><</if>>
 	[[$slaves[$i].slaveName|Slave Interact][$activeSlave = $slaves[$i]]] will $slaves[$i].assignment.
-	<<includeDOM SlaveExpenses (V.slaves[V.i])>>
+	<<includeDOM slaveExpenses($slaves[$i])>>
 	<</capture>>
 <</for>>
diff --git a/src/uncategorized/coursingAssociation.tw b/src/uncategorized/coursingAssociation.tw
index a1d356c18ad9c96810ca230b75a0f323c046e479..1e6819683375a15a1b098aab61baca1d1ac5591a 100644
--- a/src/uncategorized/coursingAssociation.tw
+++ b/src/uncategorized/coursingAssociation.tw
@@ -16,13 +16,13 @@ The chasing slaves are known as lurchers, the term once used for the sighthounds
 
 <<if $LurcherID != 0>>
 	<br><br>''Fire your Lurcher:''
-	<<= App.UI.SlaveList.render.listMarkup([$LurcherID], [],
+	<<includeDOM App.UI.SlaveList.render([$LurcherID], new Array(),
 		(slave) => App.UI.DOM.passageLink(SlaveFullName(slave), 'Coursing Association', () => removeJob(slave, Job.LURCHER)))>>
 <</if>>
 
 <br><br>''Select a slave to course as a Lurcher:''
 <br><br>
-<<= App.UI.SlaveList.slaveSelectionList(
+<<includeDOM App.UI.SlaveList.slaveSelectionList(
 		s => $LurcherID !== s.ID && canWalk(s) && canHold(s) && (canSee(s) || canHear(s)) && s.fuckdoll === 0 && isSlaveAvailable(s),
 		(slave) => App.UI.DOM.link(SlaveFullName(slave), () => assignmentTransition(slave, Job.LURCHER, "Coursing Association"))
 	)>>
diff --git a/src/uncategorized/fsDevelopments.tw b/src/uncategorized/fsDevelopments.tw
index e5b0d7399b3cd1db4646bc22917488ed97f51fb2..ce6e951d44804928066a6c78b620c23d7828f70d 100644
--- a/src/uncategorized/fsDevelopments.tw
+++ b/src/uncategorized/fsDevelopments.tw
@@ -76,19 +76,19 @@
 <<if $secExpEnabled == 1>>
 	<<if $SecExp.buildings.propHub.active > 0 && $SecExp.buildings.propHub.campaign >= 1 && $SecExp.buildings.propHub.focus == "social engineering">>
 		Your propaganda campaign helps further your societal engineering efforts.
-		<<if $SecExp.buildings.propHub.recuriterOffice == 0 || $Recruiter == 0>>
+		<<if $SecExp.buildings.propHub.recuriterOffice == 0 || $RecruiterID == 0>>
 			<<if $propCampaignBoost == 1>>
 				<<set _broadProgress += 2>>
 			<<else>>
 				<<set _broadProgress += 1>>
 			<</if>>
-		<<elseif $SecExp.buildings.propHub.recuriterOffice && $Recruiter > 0>>
-			<<setLocalPronouns $Recruiter>>
-			<span class='slave-name'><<= SlaveFullName($Recruiter)>></span> is able to further boost your societal engineering campaign from $his PR hub office.
+		<<elseif $SecExp.buildings.propHub.recuriterOffice && $RecruiterID > 0>>
+			<<setLocalPronouns _S.Recruiter>>
+			<span class='slave-name'><<= SlaveFullName(_S.Recruiter)>></span> is able to further boost your societal engineering campaign from $his PR hub office.
 			<<if $propCampaignBoost == 1>>
-				<<set _broadProgress += $SecExp.buildings.propHub.campaign + Math.floor(($Recruiter.intelligence+$Recruiter.intelligenceImplant)/32)>>
+				<<set _broadProgress += $SecExp.buildings.propHub.campaign + Math.floor((_S.Recruiter.intelligence+_S.Recruiter.intelligenceImplant)/32)>>
 			<<else>>
-				<<set _broadProgress += 1 + Math.floor(($Recruiter.intelligence+$Recruiter.intelligenceImplant)/32)>>
+				<<set _broadProgress += 1 + Math.floor((_S.Recruiter.intelligence+_S.Recruiter.intelligenceImplant)/32)>>
 			<</if>>
 		<</if>>
 	<</if>>
diff --git a/src/uncategorized/fullReport.tw b/src/uncategorized/fullReport.tw
index f409b5fa69d4a2d6e0ef27ad4377c17843f38493..c04175c99395c3c419f24b4fb9f3dba13f195255 100644
--- a/src/uncategorized/fullReport.tw
+++ b/src/uncategorized/fullReport.tw
@@ -83,7 +83,7 @@
 	<br>
 <</if>>
 
-<<if Array.isArray($personalAttention) && $personalAttention.findIndex(function(s) { return s.ID == $slaves[$i].ID; }) != -1>>
+<<if $PC.health.shortDamage < 30 && Array.isArray($personalAttention) && $personalAttention.findIndex(function(s) { return s.ID == $slaves[$i].ID; }) != -1>>
 	<<set $activeSlave = $slaves[$i]>>
 	<<include "PT Workaround">>
 	<br>
diff --git a/src/uncategorized/initRules.tw b/src/uncategorized/initRules.tw
index d4689a3d95bcc075ad9f0a004608c911470d080d..3c67fed8e1e596f18e5eeeefcb83a344a7962e39 100644
--- a/src/uncategorized/initRules.tw
+++ b/src/uncategorized/initRules.tw
@@ -129,6 +129,7 @@
 		holes: "no default setting"
 	},
 	lactationRules: "no default setting",
+	mobilityRules: "no default setting",
 	underArmHColor: "no default setting",
 	underArmHStyle: "no default setting",
 	eyebrowHColor: "no default setting",
diff --git a/src/uncategorized/manageArcology.tw b/src/uncategorized/manageArcology.tw
index 0b76b7221c2b427d8e8044ed7049d83adc368824..c27d239ff46364b1dc9bcdc4a2ae909ac9e108c4 100644
--- a/src/uncategorized/manageArcology.tw
+++ b/src/uncategorized/manageArcology.tw
@@ -107,14 +107,36 @@
 	</div>
 
 	<div>
-	<<run FCTV.manage()>>
 	<<if $FCTV.receiver > -1 && !$FCTV.weekEnabled>>
-		You have not installed an FCTV receiver.
-		Installing this receiver yourself will cost <<print cashFormat(Math.trunc(25000*$upgradeMultiplierArcology*$HackingSkillMultiplier))>>.
-		You can have your citizens pay for the fiberoptic upgrades, reducing the cost to <<print cashFormat(Math.trunc(20000*$upgradeMultiplierArcology*$HackingSkillMultiplier))>>.
-		You can also have them heavily subsidize installation, they will be upset about it, but it will only cost <<print cashFormat(Math.trunc(10000*$upgradeMultiplierArcology*$HackingSkillMultiplier))>>.
+		<<set _noSub = Math.trunc(25000*$upgradeMultiplierArcology*$HackingSkillMultiplier)>>
+		<<set _lightSub = Math.trunc(20000*$upgradeMultiplierArcology*$HackingSkillMultiplier)>>
+		<<set _heavySub = Math.trunc(10000*$upgradeMultiplierArcology*$HackingSkillMultiplier)>>
 
-		[[No subsidy|Manage Arcology][cashX(forceNeg(Math.trunc(25000*$upgradeMultiplierArcology*$HackingSkillMultiplier)), "capEx"), $FCTV.receiver = 1, $FCTV.weekEnabled = $week, repX(500, "capEx"), $PC.skill.hacking += 1]] | [[Light subsidy|Manage Arcology][cashX(forceNeg(Math.trunc(20000*$upgradeMultiplierArcology*$HackingSkillMultiplier)), "capEx"), $FCTV.receiver = 1, $FCTV.weekEnabled= $week, $PC.skill.hacking += 1]] | [[Heavy subsidy|Manage Arcology][cashX(forceNeg(Math.trunc(10000*$upgradeMultiplierArcology*$HackingSkillMultiplier)), "capEx"), $FCTV.receiver = 1, $FCTV.weekEnabled = $week, repX(-1500, "capEx"), $PC.skill.hacking += 1]]
+		You have not installed an FCTV receiver.
+		Installing this receiver yourself will cost <<print cashFormat(_noSub)>>.
+		You can have your citizens pay for the fiberoptic upgrades, reducing the cost to <<print cashFormat(_lightSub)>>.
+		You can also have them heavily subsidize installation, they will be upset about it, but it will only cost <<print cashFormat(_heavySub)>>.
+
+		<<link "No subsidy" "Manage Arcology">>
+			<<set $FCTV.receiver = 1, $FCTV.weekEnabled = $week, $PC.skill.hacking += 1>>
+			<<run FCTV.initChannels()>>
+			<<run cashX(forceNeg(_noSub), "capEx")>>
+			<<run repX(500, "capEx")>>
+		<</link>>
+		|
+		<<link "Light subsidy" "Manage Arcology">>
+			<<set $FCTV.receiver = 1, $FCTV.weekEnabled = $week, $PC.skill.hacking += 1>>
+			<<run FCTV.initChannels()>>
+			<<run cashX(forceNeg(_lightSub), "capEx")>>
+			/* No rep change */
+		<</link>>
+		|
+		<<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 repX(-1500, "capEx")>>
+		<</link>>
 	<<elseif ($FCTV.receiver == 3)>>
 		You have installed the FCTV receiver and have access to the full range of FCTV's programs. High viewership rates amongst your citizens makes it easier to pursue your societal goals.
 	<<elseif ($FCTV.receiver == 2)>>
@@ -516,7 +538,7 @@
 	<<elseif $SF.Toggle && $SF.Active >= 1>>
 		Your army counts <<print num(App.SecExp.Manpower.employedOverall + $SF.ArmySize)>> total soldiers of which <<print num($SF.ArmySize)>> under the special force command and the rest under your direct control.
 	<</if>>
-	<<if $hasFoughtOnce == 1>>
+	<<if $battlesCount > 0>>
 		Your troops were involved in <<print num($battlesCount)>> battles of which <<print num($majorBattlesCount)>> were major engagements. You won
 		<<if $battlesCount == $PCvictories>>
 			all of them.
diff --git a/src/uncategorized/newGamePlus.tw b/src/uncategorized/newGamePlus.tw
index ed8e42b62e09064ac4ca23b74b50995e39b6a931..f582693fbfdbe64819a34ad5ddee809f29d225c5 100644
--- a/src/uncategorized/newGamePlus.tw
+++ b/src/uncategorized/newGamePlus.tw
@@ -1,10 +1,6 @@
 :: New Game Plus [nobr]
 
 <<set $ui = "start">> <<unset $SlaveSummaryFiler>>
-<<if ndef $slavesToImport>>
-	<<silently>><<include "Backwards Compatibility">><</silently>>
-<</if>>
-<<if ndef $slavesToImport>><<set $slavesToImport = 0>><</if>>
 <<if ndef $slavesToImportMax>><<set $slavesToImportMax = 5>><</if>>
 
 <<set _fee = 50000+($slavesToImportMax*10000)>>
@@ -54,7 +50,7 @@ You <<if $cash >= _fee>>have<<else>>lack<</if>> the funds to bring more than $sl
 
 <<if $cheatMode == 1>> <<link "DEBUG: Add all slaves to import list" "New Game Plus">> <<for _ngi = 0; _ngi < $slaves.length; _ngi++>> <<= assignJob($slaves[_ngi], Job.IMPORTED)>> <</for>> <</link>> <br> <</if>>
 
-Select up to $slavesToImportMax slaves to be imported into a new game and then click [[here.|init][$saveImported = 1,$oldCareer = "undefined",$slavesToImport = 0]]
+Select up to $slavesToImportMax slaves to be imported into a new game and then click [[here.|init][$saveImported = 1,$oldCareer = "undefined"]]
 
 <br>
-<<print App.UI.SlaveList.listNGPSlaves()>>
+<<includeDOM App.UI.SlaveList.listNGPSlaves()>>
diff --git a/src/uncategorized/newSlaveIntro.tw b/src/uncategorized/newSlaveIntro.tw
index f8f47d1f01d3a3383bb874dcf7f4b28499633603..b7cb402cc5f2683c8cc1fe3dfae0db99af2f0001 100644
--- a/src/uncategorized/newSlaveIntro.tw
+++ b/src/uncategorized/newSlaveIntro.tw
@@ -95,7 +95,7 @@
 <</if>>
 
 <p>
-The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlave)>></span> <<if !hasAnyLegs($activeSlave)>><<if isAmputee($activeSlave)>>lies<<else>>rests<</if>> helplessly on your desk,<<elseif !canWalk($activeSlave)>>kneels before your desk,<<else>>stands before your desk,<</if>>
+The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlave)>></span> <<if !hasAnyLegs($activeSlave)>><<if isAmputee($activeSlave)>>lies<<else>>rests<</if>> helplessly on your desk,<<elseif !canStand($activeSlave)>>kneels before your desk,<<else>>stands before your desk,<</if>>
 <<if $activeSlave.devotion < -50>>
 	<<if canSee($activeSlave)>>looking you in the eyes<<else>>directly facing you<</if>> with defiance.
 <<elseif $activeSlave.devotion < -20>>
@@ -1526,7 +1526,7 @@ The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlav
 							You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple. With a sharp intake of breath $he begins to squirm, and $his breast fetish soon has $him moaning and writhing as $he fights an intense orgasm with tears of shame rolling down $his flushed cheeks.
 						<</if>>
 					<</if>>
-					As you summon <<if $HeadGirl != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment, $he slumps dejectedly in $his restraints. $He is torn between the knowledge that $he is little more than livestock to you, and the disturbing realization that @@.hotpink;$he might enjoy $his new life as a human cow.@@
+					As you summon <<if $HeadGirlID != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment, $he slumps dejectedly in $his restraints. $He is torn between the knowledge that $he is little more than livestock to you, and the disturbing realization that @@.hotpink;$he might enjoy $his new life as a human cow.@@
 					<<set $activeSlave.devotion += 4>>
 				<<else>>
 					<<if $servantMilkers == 1>>
@@ -1534,7 +1534,7 @@ The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlav
 					<<else>>
 						You kneel behind $him and begin to gently massage $his swollen breasts. A steady stream of milk begins to erupt from each nipple as a look of confusion washes over $his features. $He then closes $his eyes tight and does $his best to ignore what's happening in a futile attempt to deny $his new reality, but you continue $his milking until $he is drained.
 					<</if>>
-					As you summon <<if $HeadGirl != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment $he glares at you with disgust. $His resentment at the new burden you've saddled $him with is evident, and the regular milkings $he'll require will serve to remind $him of the @@.gold;disturbing fact that $he is little more than livestock to you.@@
+					As you summon <<if $HeadGirlID != 0>>your Head Girl<<else>>another slave<</if>> to escort the $girl to $his first assignment $he glares at you with disgust. $His resentment at the new burden you've saddled $him with is evident, and the regular milkings $he'll require will serve to remind $him of the @@.gold;disturbing fact that $he is little more than livestock to you.@@
 					<<set $activeSlave.devotion -= 4>>
 				<</if>>
 			<</if>>
@@ -2299,7 +2299,7 @@ The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlav
 				<<set $activeSlave.counter.anal += 10, $analTotal += 10>>
 				<<= assignJob($activeSlave, "dairy")>>
 				<<replace "#introResult">>
-					You order <<if $HeadGirl == 0>>another slave<<else>>$HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $dairyName. The new slave does not know what $dairyName is, not really, and $he doesn't know what being set up there means, either. If $he knew that you are able to send $him there so blithely only because it is equipped with a special preparatory raper that will seize $him, <<if $activeSlave.vagina > -1>>mercilessly fuck $his pussy and ass until both are gaped<<else>>ream $his anus until it's cavernously gaped<</if>>, and then consign $him to constant fucking by gargantuan machine phalli, $he might resist. But $he doesn't, so $he does not.
+					You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $dairyName. The new slave does not know what $dairyName is, not really, and $he doesn't know what being set up there means, either. If $he knew that you are able to send $him there so blithely only because it is equipped with a special preparatory raper that will seize $him, <<if $activeSlave.vagina > -1>>mercilessly fuck $his pussy and ass until both are gaped<<else>>ream $his anus until it's cavernously gaped<</if>>, and then consign $him to constant fucking by gargantuan machine phalli, $he might resist. But $he doesn't, so $he does not.
 				<</replace>>
 			<</link>>
 		<</if>>
@@ -2410,7 +2410,7 @@ The legalities completed, <span class='slave-name'><<= SlaveFullName($activeSlav
 			<<set $activeSlave.assignment = "be confined in the arcade">>
 			<<set $activeSlave.choosesOwnAssignment = 0>>
 			<<replace "#introResult">>
-				You order <<if $HeadGirl == 0>>another slave<<else>>$HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $arcadeName. The new slave does not know what $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 $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<<if $activeSlave.vagina > -1>>, parts $his pussylips,<</if>> or presses against $his poor anus.
+				You order <<if $HeadGirlID == 0>>another slave<<else>>_S.HeadGirl.slaveName<</if>> to get $activeSlave.slaveName set up in $arcadeName. The new slave does not know what $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 $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<<if $activeSlave.vagina > -1>>, parts $his pussylips,<</if>> or presses against $his poor anus.
 				<<if ($arcade <= App.Entity.facilities.arcade.employeesIDs().size)>>Mere <<if $showInches == 2>>yards<<else>>meters<</if>> away, preparations to convert the least appealing Arcade slave into a Fuckdoll begin. As $activeSlave.slaveName is broken in by $his first customers, $he's blissfully unaware that $he's $arcade new slaves away from the same fate.<</if>>
 			<</replace>>
 		<</link>>
diff --git a/src/uncategorized/nextWeek.tw b/src/uncategorized/nextWeek.tw
index 25f8249827c013185df19ac7747724b752bc9624..d5f85cffb7c5877d830756a3e79bd8fb332f103f 100644
--- a/src/uncategorized/nextWeek.tw
+++ b/src/uncategorized/nextWeek.tw
@@ -198,10 +198,10 @@
 	<<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) && ($HeadGirl != 0) && ($slaves[_i].devotion > 50)>>
+	<<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 == $HeadGirl.ID)>>
+		<<elseif ($slaves[_i].ID == $HeadGirlID)>>
 			<<set _OldHG = _i>>
 		<</if>>
 	<</if>>
@@ -339,7 +339,7 @@
 <<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 = [], $RESimpleAssaultIDs = [], $RECockmilkInterceptionIDs = [], $REInterslaveBeggingIDs = [], $bedSlaves = [], $eligibleSlaves = []>>
+<<set $events = [], $RESSevent = [], $RESSTRevent = [], $RETSevent = [], $RECIevent = [], $RecETSevent = [], $REFIevent = [], $REFSevent = [], $PESSevent = [], $PETSevent = [], $FSAcquisitionEvents = [], $FSNonconformistEvents = [], $REAnalCowgirlSubIDs = [], $REButtholeCheckinIDs = [], $recruit = [], $RETasteTestSubIDs = [], $rebelSlaves = [], $REBoobCollisionSubIDs = [], $REIfYouEnjoyItSubIDs = [], $RESadisticDescriptionSubIDs = [], $REShowerForceSubIDs = [], $RECockmilkInterceptionIDs = [], $REInterslaveBeggingIDs = [], $bedSlaves = [], $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>>
@@ -348,7 +348,7 @@
 <<set $beforeGingering = null>>
 
 /% Strings Memory varies. %/
-<<set $buyer = "", $desc = "", $event = "", $goto = "", $malefactor = "", $oldName = "", $oldSurname = "">>
+<<set $buyer = "", $desc = "", $event = "", $goto = "", $malefactor = "">>
 /% Done with zeroing out, what should be for the most part Temps %/
 
 <<if $autosave != 0>>
diff --git a/src/uncategorized/nonRandomEvent.tw b/src/uncategorized/nonRandomEvent.tw
index 2f8e6418f85ef3f95c934c6c419839a87ea46a2d..a6d7540fa34d7edb07ec73b5c8cb228c84cd9eca 100644
--- a/src/uncategorized/nonRandomEvent.tw
+++ b/src/uncategorized/nonRandomEvent.tw
@@ -19,7 +19,7 @@
 <<elseif (_effectiveWeek == 11)>>
 	<<set $Event = "assistant">>
 	<<goto "Assistant Events">>
-<<elseif (_effectiveWeek == 12) && $raped == -1 && $arcologyUpgrade.drones != 1 && $Bodyguard == 0 && $PC.career != "arcology owner">>
+<<elseif (_effectiveWeek == 12) && $raped == -1 && $arcologyUpgrade.drones != 1 && $BodyguardID == 0 && $PC.career != "arcology owner">>
 	<<goto "P raped">>
 <<elseif (_effectiveWeek == 14) && $badC != 1>>
 	<<set _valid = $slaves.find(function(s) { return s.curatives > 1 || s.inflationType == "curative"; })>>
@@ -96,19 +96,17 @@
 	<<goto "P collaboration choice">>
 <<elseif (_effectiveWeek == 69)>>
 	<<goto "P hacker support">>
-<<elseif (_effectiveWeek == 70) && ($collaboration == 1)>>
-	<<if $traitorType != "trapper">>
-		<<goto "P coup collaboration">>
-	<<else>>
-		<<goto "P coup betrayal">>
-	<</if>>
+<<elseif (_effectiveWeek == 70) && ($collaboration == 1) && $traitorType != "trapper">>
+	<<goto "P coup collaboration">>
 <<elseif (_effectiveWeek == 71)>>
 	<<if $traitorType != "agent" && $traitorType != "trapper">>
 		<<set _doubleAgent = 0>>
 	<<else>>
 		<<set _doubleAgent = 1>>
 	<</if>>
-	<<if $mercenaries + $personalArms + $hackerSupport + _doubleAgent < 5>>
+	<<if $traitorType == "trapper">>
+		<<goto "P coup betrayal">>
+	<<elseif $mercenaries + $personalArms + $hackerSupport + _doubleAgent < 5>>
 		<<goto "P coup loss">>
 	<<else>>
 		<<goto "P coup attempt">>
@@ -171,7 +169,7 @@
 	<<goto "Generic Plot Events">>
 <<elseif ($slaveMedic > 0)>>
 	<<goto "P slave medic">>
-<<elseif ($PPit == 1) && ($Bodyguard != 0)>>
+<<elseif ($PPit == 1) && ($BodyguardID != 0)>>
 	<<goto "PE pit fight">>
 <<elseif $rivalOwner == 0 && $smilingManProgress == 0 && _effectiveWeek >= 74 && $secExpEnabled == 1>>
 	<<goto "secExpSmilingMan">>
diff --git a/src/uncategorized/officeDescription.tw b/src/uncategorized/officeDescription.tw
index 1c2bf5c9aaccb9bbbe6626d6873070a5640e2168..d65e275d37d2728335e46fbc03428e18518e71b7 100644
--- a/src/uncategorized/officeDescription.tw
+++ b/src/uncategorized/officeDescription.tw
@@ -7,11 +7,11 @@
 
 <<if $clubAdsSpending >= 5000>>
 	A corner of your desk is piled with sample merchandise from the campaign promoting your club.
-	<<set _eventSlaves = $slaves.filter(function(s) { return s.assignment == "serve in the club" && canWalk(s); })>>
+	<<set _eventSlaves = $slaves.filter(function(s) { return s.assignment == "serve in the club"; })>>
 	<<if _eventSlaves.length > 0>>
 		<<set $activeSlave = _eventSlaves.random()>>
 		<<setLocalPronouns $activeSlave>>
-		<<if random(1,2) == 1>>
+		<<if random(1,2) == 1 || !canStand($activeSlave)>>
 			This includes a sex toy based on $activeSlave.slaveName's
 			<<if (random(1,3) == 1) && ($activeSlave.skill.vaginal >= 100)>>
 				<<if $activeSlave.vagina > 3>>massive vagina<<elseif $activeSlave.vagina > 2>>gaping cunt<<elseif $activeSlave.vagina > 1>>hot cunt<<elseif $activeSlave.vagina > 0>>tight pussy<<else>>virgin pussy<</if>>.
@@ -38,11 +38,11 @@
 	<<else>>
 		A corner of your desk is piled with sample merchandise from the campaign promoting $brothelName.
 	<</if>>
-	<<set _eventSlaves = $slaves.filter(function(s) { return s.assignment == "work in the brothel" && canWalk(s); })>>
+	<<set _eventSlaves = $slaves.filter(function(s) { return s.assignment == "work in the brothel"; })>>
 	<<if _eventSlaves.length > 0>>
 		<<set $activeSlave = _eventSlaves.random()>>
 		<<setLocalPronouns $activeSlave>>
-		<<if random(1,2) == 1>>
+		<<if random(1,2) == 1 || !canStand($activeSlave)>>
 			This includes a sex toy based on $activeSlave.slaveName's
 			<<if (random(1,3) == 1) && ($activeSlave.skill.vaginal >= 100)>>
 				<<if $activeSlave.vagina > 3>>massive vagina<<elseif $activeSlave.vagina > 2>>gaping cunt<<elseif $activeSlave.vagina > 1>>hot cunt<<elseif $activeSlave.vagina > 0>>tight pussy<<else>>virgin pussy<</if>>.
diff --git a/src/uncategorized/pBombing.tw b/src/uncategorized/pBombing.tw
index 92b5569be2b67e6fcb5e8262e43c23d8bc1db794..4b2ee2408738518c0b286d657b25451773a906a7 100644
--- a/src/uncategorized/pBombing.tw
+++ b/src/uncategorized/pBombing.tw
@@ -3,15 +3,15 @@
 <<set $nextButton = "Continue">>
 <<set $nextLink = "Random Nonindividual Event">>
 
-<<if $Bodyguard != 0>>
-	<<setLocalPronouns $Bodyguard>>
+<<if _S.Bodyguard>>
+	<<setLocalPronouns _S.Bodyguard>>
 	<span id="artFrame">
 	/* 000-250-006 */
 	<<if $seeImages == 1>>
 		<<if $imageChoice == 1>>
-			<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt($Bodyguard, 2, 0)>></div>
+			<div class="imageRef lrgVector"><div class="mask">&nbsp;</div><<= SlaveArt(_S.Bodyguard, 2, 0)>></div>
 		<<else>>
-			<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt($Bodyguard, 2, 0)>></div>
+			<div class="imageRef lrgRender"><div class="mask">&nbsp;</div><<= SlaveArt(_S.Bodyguard, 2, 0)>></div>
 		<</if>>
 	<</if>>
 	/* 000-250-006 */
@@ -19,22 +19,22 @@
 <</if>>
 <<set $fcnn.push("...new report on advances in security screening designed to sniff out small explosives like...")>>
 
-One fine day, you're strolling down the main promenade, making your usual combined inspection and public rounds. These walks are triply useful, since they allow you to keep a finger on the pulse of your demesne, identify any problems, and display yourself to the population. <<if $Bodyguard != 0>>$Bodyguard.slaveName is walking with you, of course; $his presence protection, intimidation, and ostentation, all in one.<</if>>
+One fine day, you're strolling down the main promenade, making your usual combined inspection and public rounds. These walks are triply useful, since they allow you to keep a finger on the pulse of your demesne, identify any problems, and display yourself to the population. <<if _S.Bodyguard>>_S.Bodyguard.slaveName is walking with you, of course; $his presence protection, intimidation, and ostentation, all in one.<</if>>
 
 <<= capFirstChar($assistant.name)>> suddenly relays a silent alarm, highlighting a scruffy individual twenty <<if $showInches == 2>>yards<<else>>meters<</if>> ahead. This person is hooded and acting oddly. You catch a glance from beneath the hood in your direction: and the figure obviously decides that it can get no closer to you. Your assistant's scanning detects the presence of an implanted bomb moments before the suicide bomber detonates themselves.
 
-The implant is small, and went off too far ahead to do anything more than stun. <<if $Bodyguard != 0>>Since $he was behind you, $Bodyguard.slaveName is in much better shape than you, $his ears clear and $his senses sharp. <</if>>Three other assailants run at you from where they were waiting in the confusion, wielding improvised weapons that apparently did not set off the security scanners.
+The implant is small, and went off too far ahead to do anything more than stun. <<if _S.Bodyguard>>Since $he was behind you, _S.Bodyguard.slaveName is in much better shape than you, $his ears clear and $his senses sharp. <</if>>Three other assailants run at you from where they were waiting in the confusion, wielding improvised weapons that apparently did not set off the security scanners.
 
 <br><br>
 
-<<if $Bodyguard != 0>>
-	<<if getLimbCount($Bodyguard, 105) === 4>>
-		$Bodyguard.slaveName glances at you to check that $his principal is alive, $his P-Limbs already entering combat mode. $He becomes more inhuman by the moment,
-		<<if ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 5>>
+<<if _S.Bodyguard>>
+	<<if getLimbCount(_S.Bodyguard, 105) === 4>>
+		_S.Bodyguard.slaveName glances at you to check that $his principal is alive, $his P-Limbs already entering combat mode. $He becomes more inhuman by the moment,
+		<<if (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 5>>
 			$his calves unfolding to reveal stabilizers that slide down and broaden $his ground contact. The targeting programs in $his arms allow $him to fire $his light machine gun from the hip, and $he places a precise burst in each target.
-		<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 4>>
+		<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 4>>
 			the arm that holds $his battle rifle extending to lock to its foregrip and stock. With it fully attached to $him, $he raises that arm at each attacker in turn.
-		<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 3>>
+		<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 3>>
 			stabilizers extending from the forearm that holds $his submachine gun. $He fires it one-handed, the weapons barely moving at all as $he flicks from target to target.
 		<<else>>
 			$his hand extending fully around $his machine pistol even as $he brings it up to meet the first target. When its first magazine is expended, $his forearm unfolds to reveal another.
@@ -42,37 +42,37 @@ The implant is small, and went off too far ahead to do anything more than stun.
 		Your bodyguard's enhancements are not exactly secret, however, and the attackers are numerous, sent to overwhelm $him. $He drops $his weapon, ammunition expended, producing a scream of triumph from one of the few remaining assailants. Nothing daunted, $he swings $his dominant hand down to $his thigh on that side. It unfolds and places a pistol in $his grip, which $he brings up and uses to end the engagement.
 		<br><br>
 		After reloading $his pistol from the ammunition compartment in $his other thigh, $he strides mechanically from body to body, a few of which are still moving. Stopping by each one, $he places $his palm against the base of each head with obscene care before extending the blade in $his forearm through it. This done, $he scans for further threats. There are none, and due to $his terrifying display there is @@.green;an increase in confidence in the security of $arcologies[0].name@@ and @@.green;public applause for your skill in training $him.@@ There is some @@.red;minor damage@@ to repair, but no lasting harm.
-		<<run cashX(-1000, "event", $Bodyguard)>>
-		<<run repX(2500, "event", $Bodyguard)>>
+		<<run cashX(-1000, "event", _S.Bodyguard)>>
+		<<run repX(2500, "event", _S.Bodyguard)>>
 		<<set $arcologies[0].prosperity += 1>>
 
-	<<elseif $Bodyguard.skill.combat > 0>>
-		$Bodyguard.slaveName glances at you to check that $his principal is alive for the moment, already
-		<<if ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 5>>
+	<<elseif _S.Bodyguard.skill.combat > 0>>
+		_S.Bodyguard.slaveName glances at you to check that $his principal is alive for the moment, already
+		<<if (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 5>>
 			getting $his light machine gun into action. Since they're so close, $he doesn't even bother to shoulder the weapon, simply using $his weapon's firepower to dump rounds into each attacker.
-		<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 4>>
+		<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 4>>
 			bringing $his battle rifle up. $He swivels from target to target, placing a single round in each, using the weapon's devastating stopping power to good effect.
-		<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 3>>
-			bringing $his submachine gun up. $He cradles the compact weapon in <<if hasBothArms($Bodyguard)>>both hands<<else>>$his hand<</if>> and swivels from target to target, placing a long burst of fire in each, using the weapon's large ammunition capacity to good effect.
+		<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 3>>
+			bringing $his submachine gun up. $He cradles the compact weapon in <<if hasBothArms(_S.Bodyguard)>>both hands<<else>>$his hand<</if>> and swivels from target to target, placing a long burst of fire in each, using the weapon's large ammunition capacity to good effect.
 		<<else>>
-			drawing $his machine pistol. $He cradles the diminutive weapon in <<if hasBothArms($Bodyguard)>>both hands<<else>>$his hand<</if>> and swivels from target to target, placing a disciplined burst of fire in each at its center of mass.
+			drawing $his machine pistol. $He cradles the diminutive weapon in <<if hasBothArms(_S.Bodyguard)>>both hands<<else>>$his hand<</if>> and swivels from target to target, placing a disciplined burst of fire in each at its center of mass.
 		<</if>>
-		None of them gets within five <<if $showInches == 2>>yards<<else>>meters<</if>>. $Bodyguard.slaveName dispassionately places a round in the head of each body, reloads, and takes up a position from which $he can watch for further threats. There are none, and due to $his sterling conduct there is @@.green;an increase in confidence in the security of $arcologies[0].name@@ and @@.green;public applause for your skill in training $him.@@ There is some @@.red;minor damage@@ to repair, but no lasting harm.
-		<<run cashX(-1000, "event", $Bodyguard)>>
-		<<run repX(2500, "event", $Bodyguard)>>
+		None of them gets within five <<if $showInches == 2>>yards<<else>>meters<</if>>. _S.Bodyguard.slaveName dispassionately places a round in the head of each body, reloads, and takes up a position from which $he can watch for further threats. There are none, and due to $his sterling conduct there is @@.green;an increase in confidence in the security of $arcologies[0].name@@ and @@.green;public applause for your skill in training $him.@@ There is some @@.red;minor damage@@ to repair, but no lasting harm.
+		<<run cashX(-1000, "event", _S.Bodyguard)>>
+		<<run repX(2500, "event", _S.Bodyguard)>>
 		<<set $arcologies[0].prosperity += 1>>
 
-	<<elseif $Bodyguard.muscles > 5>>
-		$Bodyguard.slaveName hurriedly <<if ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 5>>gets $his light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls $his aim off target.<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 4>>brings up $his battle rifle. $He hits the nearest assailant with $his first round and then fires again, but the weapon's powerful recoil sends it over $his target.<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 3>>brings up $his submachine gun. $He points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<<else>>draws $his machine pistol. $He points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<</if>> By the time $he pulls the muzzle back down the others are on $him. Fortunately, $he is equal to the challenge, getting $his sword into gory action. $He is physically powerful but unskilled, so the result is simple, unlovely butcher's work. This spectacle produces @@.green;an increase in confidence in the security of $arcologies[0].name.@@ There is some @@.red;minor property damage@@ to repair, but no lasting harm.
-		<<run cashX(-1000, "event", $Bodyguard)>>
+	<<elseif _S.Bodyguard.muscles > 5>>
+		_S.Bodyguard.slaveName hurriedly <<if (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 5>>gets $his light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls $his aim off target.<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 4>>brings up $his battle rifle. $He hits the nearest assailant with $his first round and then fires again, but the weapon's powerful recoil sends it over $his target.<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 3>>brings up $his submachine gun. $He points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<<else>>draws $his machine pistol. $He points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<</if>> By the time $he pulls the muzzle back down the others are on $him. Fortunately, $he is equal to the challenge, getting $his sword into gory action. $He is physically powerful but unskilled, so the result is simple, unlovely butcher's work. This spectacle produces @@.green;an increase in confidence in the security of $arcologies[0].name.@@ There is some @@.red;minor property damage@@ to repair, but no lasting harm.
+		<<run cashX(-1000, "event", _S.Bodyguard)>>
 		<<set $arcologies[0].prosperity += 1>>
 
 	<<else>>
-		$Bodyguard.slaveName hurriedly <<if ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 5>>gets $his light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls $his aim off target.<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 4>>brings up $his battle rifle. $He hits the nearest assailant with $his first round and then fires again, but the weapon's powerful recoil sends it over $his target.<<elseif ($Bodyguard.muscles+$Bodyguard.height-100)/25 > 3>>brings up $his submachine gun. $He points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<<else>>draws $his machine pistol. $He points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<</if>> By the time $he pulls the muzzle back down the others are on $him. $His sword is very sharp, but so are $his assailants' weapons, and everyone is unskilled. As often happens with bladed fights between unarmored novices, everyone dies, and so fast that you cannot intervene. $Bodyguard.slaveName expires gasping in agony over multiple deep wounds to $his abdomen, but still manages to lock eyes with you as $he does. There is some @@.red;minor property damage@@ to repair, but no lasting harm.
-		<<run cashX(-1000, "event", $Bodyguard), healthDamage($Bodyguard, 1000)>>
-		<<set $activeSlave = $Bodyguard>>
+		_S.Bodyguard.slaveName hurriedly <<if (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 5>>gets $his light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls $his aim off target.<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 4>>brings up $his battle rifle. $He hits the nearest assailant with $his first round and then fires again, but the weapon's powerful recoil sends it over $his target.<<elseif (_S.Bodyguard.muscles+_S.Bodyguard.height-100)/25 > 3>>brings up $his submachine gun. $He points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<<else>>draws $his machine pistol. $He points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls $his aim off target.<</if>> By the time $he pulls the muzzle back down the others are on $him. $His sword is very sharp, but so are $his assailants' weapons, and everyone is unskilled. As often happens with bladed fights between unarmored novices, everyone dies, and so fast that you cannot intervene. _S.Bodyguard.slaveName expires gasping in agony over multiple deep wounds to $his abdomen, but still manages to lock eyes with you as $he does. There is some @@.red;minor property damage@@ to repair, but no lasting harm.
+		<<run cashX(-1000, "event", _S.Bodyguard), healthDamage(_S.Bodyguard, 1000)>>
+		<<set $activeSlave = _S.Bodyguard>>
 		<<= removeActiveSlave() >>
-	<</if>> /* closes $Bodyguard != 0 */
+	<</if>> /* closes if _S.Bodyguard */
 
 <<elseif $personalArms > 0>>
 	You are prepared for this exigency. Your handsome clothing conceals a small but very powerful pistol. This fact comes slowly back to you as your ears and mind shake off the blast effects. There is something important about this fact, but you are having trouble thinking of it. Eventually, you remember, and groggily draw and level the weapon. Its rocket-assisted slugs detonate on contact, assisting your impaired marksmanship by causing fatal damage regardless of shot placement. You are alive, and not permanently damaged, which is more than can be said for your assailants, who have, with the assistance of your weapon, redecorated this area of the club in red tones. There is some @@.red;minor property damage@@ to repair, but no lasting harm.
diff --git a/src/uncategorized/pCoupBetrayal.tw b/src/uncategorized/pCoupBetrayal.tw
index 4b388663b06e1c4762eec16285d530e433f06ec1..8fbf09d33914d84d5cc10c90aa09dbcb624e3333 100644
--- a/src/uncategorized/pCoupBetrayal.tw
+++ b/src/uncategorized/pCoupBetrayal.tw
@@ -29,9 +29,8 @@ You are awakened in the middle of the night by a jolt that shakes the entire arc
 @@.yellow;The Daughters of Liberty are utterly crushed.@@ $arcologies[0].name has been slightly damaged in the crossfire, but even as the last pockets of resistance are cleaned up, your citizens begin repairs themselves. The effect on your reputation is @@.green;immensely positive,@@ since you won without lifting a finger and the arcology's prosperity, if anything, was benefited. However, the PMCs took the lion's share of the loot.
 
 <<run cashX(-10000, "war")>>
-<<run cashX(forceNeg(Math.trunc($cash*0.3)), "war")>>
-<<set $arcologies[0].prosperity = Math.trunc($arcologies[0].prosperity*0.5)>>
-<<run repX(500, "war")>>
+<<set $arcologies[0].prosperity = Math.trunc($arcologies[0].prosperity*0.9)>>
+<<run repX(5000, "war")>>
 <<set $daughtersVictory = 3>>
 <<set $desc = "a leftover banner bearing Daughters of Liberty insignia">>
 <<set $trinkets.push($desc)>>
diff --git a/src/uncategorized/pSchoolSuggestion.tw b/src/uncategorized/pSchoolSuggestion.tw
index 229d28cac2d47140afc48995f82b27cd26b19666..c29dca1d41fff7f9f2022fe6de85b05f4f0d69cc 100644
--- a/src/uncategorized/pSchoolSuggestion.tw
+++ b/src/uncategorized/pSchoolSuggestion.tw
@@ -12,6 +12,8 @@ When you do appear, you get an even greater acclamation than usual. It seems you
 
 <br><br>
 It seems a young, thin woman in a modern business suit was concluding her own argument when you arrived. "In summation, I propose we offer our support for Nueva Universidad de Libertad," says the woman, who you now realize is a very feminine man. "Nullification may seem extreme, but serves to expand the potential market amongst more... traditional slaveowners," says the man, who you <i>now</i> realize is a woman who merely looks like a very feminine man.
+<br><br>
+"I believe what you need in a slave is a good base. As such, The Utopian Orphanage is the best. They offer slaves who were raised with careful attention; They're beautiful, smart, well-educated and unspoiled." The young surgeon continues. "No traumas, a happy childhood, obedient and trusting. You can then mold them to your will as you please, I have enough faith in my skills to achieve the results I desire myself."
 <<if $seeDicks != 100>>
 <br><br>
 "The Slave School for me," says a portly man with a thriving slave breaking business down in the markets. "Their girls are pretty, skilled, and innocent, without any of that weird crap the other schools go in for. Besides, all that special stuff drives up the prices. TSS girls are cheap for what you get. When you're tired of one, just buy another." He turns to his friend and business partner, a much thinner man. "Though I'm sure you disagree with me."
@@ -44,6 +46,12 @@ The older gentleman who seems to have been acting as unofficial moderator before
 			<<set $TSS.schoolPresent = 1>><<run cashX(-10000, "policies")>>
 		<</replace>>
 	<</link>>
+	<br><<link "The Utopian Orphanage">>
+		<<replace "#result">>
+			You thank your leading citizens and announce your decision: you'll be contacting The Utopian Orphanage about opening a branch campus here, immediately.
+			<<set $TUO.schoolPresent = 1>><<run cashX(-10000, "policies")>>
+		<</replace>>
+	<</link>>
 	<br><<link "The Growth Research Institute">>
 		<<replace "#result">>
 			You thank your leading citizens and announce your decision: you'll be contacting the Growth Research Institute about opening a branch campus here, immediately.
diff --git a/src/uncategorized/peCombatTraining.tw b/src/uncategorized/peCombatTraining.tw
index 21662d1ec38d2750492d5cbc2d5b630514b837fd..b8d5bc9452148237f8beda5767b3db64bc7cd2a4 100644
--- a/src/uncategorized/peCombatTraining.tw
+++ b/src/uncategorized/peCombatTraining.tw
@@ -2,7 +2,7 @@
 
 <<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "RIE Eligibility Check">>
 
-<<set $activeSlave = $Bodyguard>>
+<<set $activeSlave = _S.Bodyguard>>
 <<setLocalPronouns $activeSlave>>
 
 <span id="artFrame">
diff --git a/src/uncategorized/peConcubineInterview.tw b/src/uncategorized/peConcubineInterview.tw
index c4232a18dd2eec67cdc5fb2a80a6079db0318f57..bb85625c4f900354a789e191213f3bdb003e3f6e 100644
--- a/src/uncategorized/peConcubineInterview.tw
+++ b/src/uncategorized/peConcubineInterview.tw
@@ -1,6 +1,6 @@
 :: PE concubine interview [nobr]
 
-<<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "RIE Eligibility Check", $activeSlave = $Concubine, _oldRep = $rep>>
+<<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "RIE Eligibility Check", $activeSlave = _S.Concubine, _oldRep = $rep>>
 <<run Enunciate($activeSlave)>>
 <<setLocalPronouns $activeSlave>>
 <<set _belly = bellyAdjective($activeSlave)>>
diff --git a/src/uncategorized/peHeadgirlConcubine.tw b/src/uncategorized/peHeadgirlConcubine.tw
index 1ce3fc86a9c8ad2c58b0c950186958ae2fb13b90..4bc9ea2986100693642d89d3082ce0ca8ee42450 100644
--- a/src/uncategorized/peHeadgirlConcubine.tw
+++ b/src/uncategorized/peHeadgirlConcubine.tw
@@ -2,12 +2,10 @@
 
 <<set $nextButton = "Continue", $nextLink = "RIE Eligibility Check", $returnTo = "RIE Eligibility Check">>
 
-<<set _headGirl = $slaveIndices[$HeadGirl.ID]>>
-<<set _concubine = $slaveIndices[$Concubine.ID]>>
-<<setLocalPronouns $HeadGirl>>
-<<set _belly = bellyAdjective($HeadGirl)>>
-<<setLocalPronouns $Concubine 2>>
-<<set _bellyCon = bellyAdjective($Concubine)>>
+<<setLocalPronouns _S.HeadGirl>>
+<<set _belly = bellyAdjective(_S.HeadGirl)>>
+<<setLocalPronouns _S.Concubine 2>>
+<<set _bellyCon = bellyAdjective(_S.Concubine)>>
 <<setAssistantPronouns>>
 
 <span id="artFrame">
@@ -15,27 +13,27 @@
 	<<if $seeImages == 1>>
 		<div class="imageColumn">
 			<div class="imageRef medImg">
-				<<= SlaveArt($HeadGirl, 2, 0)>>
+				<<= SlaveArt(_S.HeadGirl, 2, 0)>>
 			</div>
 			<div class="imageRef medImg">
-				<<= SlaveArt($Concubine, 2, 0)>>
+				<<= SlaveArt(_S.Concubine, 2, 0)>>
 			</div>
 		</div>
 	<</if>>
 	/* 000-250-006 */
 </span>
 
-You wake up one morning to find $HeadGirl.slaveName and $Concubine.slaveName waiting next to your bed. It's immediately obvious that they both have a minor item of business for you, since nothing was urgent enough to wake you early, and they evidently both came in to catch you at your normal hour of rising. They're kneeling side by side next to the
-<<if $HeadGirl.boobs >= 10000 && $Concubine.boobs >= 10000>>
+You wake up one morning to find _S.HeadGirl.slaveName and _S.Concubine.slaveName waiting next to your bed. It's immediately obvious that they both have a minor item of business for you, since nothing was urgent enough to wake you early, and they evidently both came in to catch you at your normal hour of rising. They're kneeling side by side next to the
+<<if _S.HeadGirl.boobs >= 10000 && _S.Concubine.boobs >= 10000>>
 	bed (a position that smashes their massive breasts together on one side),
-<<elseif $HeadGirl.belly >= 45000 && $Concubine.belly >= 45000>>
+<<elseif _S.HeadGirl.belly >= 45000 && _S.Concubine.belly >= 45000>>
 	bed (a position that forces their swollen middles to compete for space),
-<<elseif $HeadGirl.butt > 5 && $Concubine.butt > 5>>
+<<elseif _S.HeadGirl.butt > 5 && _S.Concubine.butt > 5>>
 	bed (a position that smashes their massive asses together on one side),
 <<else>>
 	bed,
 <</if>>
-and it's also obvious that they've been filling the few minutes they've been waiting for you to wake up by making out. Their lips are moist, $Concubine.slaveName's $Concubine.skin cheeks are a little flushed, and there's even a little unnoticed strand of saliva running from _his2 mouth to $HeadGirl.slaveName's. They both look at you expectantly, wondering whether to speak up or save their questions for after you take a moment to get ready for your day.
+and it's also obvious that they've been filling the few minutes they've been waiting for you to wake up by making out. Their lips are moist, _S.Concubine.slaveName's _S.Concubine.skin cheeks are a little flushed, and there's even a little unnoticed strand of saliva running from _his2 mouth to _S.HeadGirl.slaveName's. They both look at you expectantly, wondering whether to speak up or save their questions for after you take a moment to get ready for your day.
 
 <br><br>
 
@@ -43,12 +41,12 @@ and it's also obvious that they've been filling the few minutes they've been wai
 <<link "Get right to work">>
 	<<replace "#result">>
 	You didn't get to where you are by lying around on the job. You <<if $PC.belly < 5000>>spring<<elseif $PC.belly < 10000>>pull yourself<<else>>slowly slip your gravid bulk<</if>> out of bed and head straight towards your office and its desk to get some work done with your first rush of morning energy before your ablutions. Your Head Girl and Concubine follow along behind you;
-	$HeadGirl.slaveName does $his best to keep up, because the sight of your
-	<<if $HeadGirl.fetish == "pregnancy" && $PC.belly >= 10000>>
+	_S.HeadGirl.slaveName does $his best to keep up, because the sight of your
+	<<if _S.HeadGirl.fetish == "pregnancy" && $PC.belly >= 10000>>
 		increasingly large baby bump and motherly waddle
-	<<elseif $HeadGirl.fetish == "cumslut" && $PC.balls >= 20>>
+	<<elseif _S.HeadGirl.fetish == "cumslut" && $PC.balls >= 20>>
 		over-burdened scrotum in motion as you walk
-	<<elseif $HeadGirl.attrXX > 65 && $HeadGirl.attrXY > 65 && $PC.dick != -1 && ($PC.boobs >= 650 || $PC.title == 0 || $PC.vagina != -1)>>
+	<<elseif _S.HeadGirl.attrXX > 65 && _S.HeadGirl.attrXY > 65 && $PC.dick != -1 && ($PC.boobs >= 650 || $PC.title == 0 || $PC.vagina != -1)>>
 		dangling penis
 		<<if $PC.boobs >= 650>>
 			and bouncing boobs
@@ -58,7 +56,7 @@ and it's also obvious that they've been filling the few minutes they've been wai
 			on such a feminine body
 		<</if>>
 		in motion as you walk
-	<<elseif $HeadGirl.attrXY > 65>>
+	<<elseif _S.HeadGirl.attrXY > 65>>
 		<<if $PC.dick != 0>>
 			dangling penis
 		<<elseif $PC.title == 1>>
@@ -67,7 +65,7 @@ and it's also obvious that they've been filling the few minutes they've been wai
 			beloved body nude
 		<</if>>
 		in motion as you walk
-	<<elseif $HeadGirl.attrXX > 65>>
+	<<elseif _S.HeadGirl.attrXX > 65>>
 		<<if $PC.boobs >= 300>>
 			<<if $PC.boobs >= 1400>>
 				huge boobs from behind
@@ -87,7 +85,7 @@ and it's also obvious that they've been filling the few minutes they've been wai
 	<<else>>
 		beloved body nude in motion as you walk
 	<</if>>
-	is something $he openly enjoys, while $Concubine.slaveName at least tries to be a little more modest. You sit down at your desk, taking in the situation in your domain at a glance as $assistant.name
+	is something $he openly enjoys, while _S.Concubine.slaveName at least tries to be a little more modest. You sit down at your desk, taking in the situation in your domain at a glance as $assistant.name
 	<<switch $assistant.appearance>>
 	<<case "monstergirl">>
 		springs into monstrous life on the glass desktop, eyes flashing and hair writhing.
@@ -127,41 +125,41 @@ and it's also obvious that they've been filling the few minutes they've been wai
 		appears as a glowing icon on the glass desktop, awaiting instructions.
 	<</switch>>
 	<br><br>
-	You look at $HeadGirl.slaveName, standing across the desk from you, and $he takes the cue and goes first. $He asks your permission to make a couple of last-minute changes to slaves' assignments for the day, and you ask about a couple of minor efficiency issues before giving $him the go ahead. Meanwhile, $Concubine.slaveName kneels beside your chair, resting _his2 <<if $Concubine.bald == 0 && $Concubine.hLength > 0>>$Concubine.hColor<<else>>smooth<</if>> head on your knee, _his2
-	<<if $Concubine.dick > 8>>
+	You look at _S.HeadGirl.slaveName, standing across the desk from you, and $he takes the cue and goes first. $He asks your permission to make a couple of last-minute changes to slaves' assignments for the day, and you ask about a couple of minor efficiency issues before giving $him the go ahead. Meanwhile, _S.Concubine.slaveName kneels beside your chair, resting _his2 <<if _S.Concubine.bald == 0 && _S.Concubine.hLength > 0>>_S.Concubine.hColor<<else>>smooth<</if>> head on your knee, _his2
+	<<if _S.Concubine.dick > 8>>
 		limp, monstrous cock resting softly against one of your ankles.
-	<<elseif $Concubine.boobs > 10000>>
+	<<elseif _S.Concubine.boobs > 10000>>
 		titanic udders a nice cushion against your calves.
-	<<elseif $Concubine.weight > 30>>
+	<<elseif _S.Concubine.weight > 30>>
 		soft body cushioning nicely against your leg.
-	<<elseif $Concubine.belly >= 100000>>
+	<<elseif _S.Concubine.belly >= 100000>>
 		_bellyCon belly resting warmly against your leg.
-	<<elseif $Concubine.boobs > 1000>>
+	<<elseif _S.Concubine.boobs > 1000>>
 		heavy breast resting against your calf on that side.
 	<<else>>
 		chest warm against your calf on that side.
 	<</if>>
-	_He2 places a <<if $Concubine.muscles > 50>>strong<<else>>soft<</if>> hand on the inside of your shin and hugs your lower leg, sensual, but companionable rather than sexually insistent.
+	_He2 places a <<if _S.Concubine.muscles > 50>>strong<<else>>soft<</if>> hand on the inside of your shin and hugs your lower leg, sensual, but companionable rather than sexually insistent.
 	<br><br>
 	Your Head Girl exits, performing the proper formalities and then hurrying off to make sure the reassigned slaves get going. As $he withdraws $he
-	<<if $HeadGirl.relationship <= -2>>
+	<<if _S.HeadGirl.relationship <= -2>>
 		has eyes only for you; $he
-	<<elseif $HeadGirl.relationship == -1>>
-		gives $Concubine.slaveName a look up and down, which $his slutty nature obliges $him to give everyone; then $he
-	<<elseif $HeadGirl.relationshipTarget == $Concubine.ID>>
-		<<if $HeadGirl.relationship == 1>>
-			gives $his friend $Concubine.slaveName a smile; then $he
-		<<elseif $HeadGirl.relationship == 2>>
-			gives $his best friend $Concubine.slaveName a smile and a little wave; then $he
-		<<elseif $HeadGirl.relationship == 3>>
-			gives $his fuckbuddy $Concubine.slaveName a flirty wink; then $he
-		<<elseif $HeadGirl.relationship == 4>>
-			blows $his lover $Concubine.slaveName a kiss; then $he
+	<<elseif _S.HeadGirl.relationship == -1>>
+		gives _S.Concubine.slaveName a look up and down, which $his slutty nature obliges $him to give everyone; then $he
+	<<elseif _S.HeadGirl.relationshipTarget == $ConcubineID>>
+		<<if _S.HeadGirl.relationship == 1>>
+			gives $his friend _S.Concubine.slaveName a smile; then $he
+		<<elseif _S.HeadGirl.relationship == 2>>
+			gives $his best friend _S.Concubine.slaveName a smile and a little wave; then $he
+		<<elseif _S.HeadGirl.relationship == 3>>
+			gives $his fuckbuddy _S.Concubine.slaveName a flirty wink; then $he
+		<<elseif _S.HeadGirl.relationship == 4>>
+			blows $his lover _S.Concubine.slaveName a kiss; then $he
 		<<else>>
-			mouths "I love you" at $his _wife2 $Concubine.slaveName; then $he
+			mouths "I love you" at $his _wife2 _S.Concubine.slaveName; then $he
 		<</if>>
 	<</if>>
-	<<switch $HeadGirl.fetish>>
+	<<switch _S.HeadGirl.fetish>>
 	<<case "submissive">>
 		catches $his lower lip behind $his teeth and gives you a blushing, sexually submissive look
 	<<case "cumslut">>
@@ -169,7 +167,7 @@ and it's also obvious that they've been filling the few minutes they've been wai
 	<<case "humiliation">>
 		flashes you and blushes artfully
 	<<case "buttslut">>
-		<<if canDoAnal($HeadGirl)>>
+		<<if canDoAnal(_S.HeadGirl)>>
 			cocks $his hips, pulls one buttock aside, and winks $his asshole in your direction
 		<<else>>
 			looks you in the eye and gives $his booty a slap
@@ -187,20 +185,20 @@ and it's also obvious that they've been filling the few minutes they've been wai
 	<<default>>
 		looks at you and adds a bit of extra sway to $his hips
 	<</switch>>
-	on $his way out the door. When $he's gone, your Concubine speaks up from _his2 adoring embrace of your leg. _He2 asks a little aesthetic question about one of your other bedslaves, _his2 <<if $Concubine.voice < 2>>deep voice rumbling against your knee<<elseif $Concubine.voice > 2>>high voice humming against your knee<<else>>feminine voice purring against your knee<</if>>. You chat with _him2 about how you'd like the bedslave to look and act, ideally, and do other work while running your fingers
-	<<if $Concubine.bald == 0 && $Concubine.hLength > 0>>
-		through your Concubine's<<if $Concubine.hLength < 10>> short<</if>> hair.
+	on $his way out the door. When $he's gone, your Concubine speaks up from _his2 adoring embrace of your leg. _He2 asks a little aesthetic question about one of your other bedslaves, _his2 <<if _S.Concubine.voice < 2>>deep voice rumbling against your knee<<elseif _S.Concubine.voice > 2>>high voice humming against your knee<<else>>feminine voice purring against your knee<</if>>. You chat with _him2 about how you'd like the bedslave to look and act, ideally, and do other work while running your fingers
+	<<if _S.Concubine.bald == 0 && _S.Concubine.hLength > 0>>
+		through your Concubine's<<if _S.Concubine.hLength < 10>> short<</if>> hair.
 	<<else>>
-		along your Concubine's <<if $Concubine.bald == 1>>bald<<else>>shaved<</if>> head.
+		along your Concubine's <<if _S.Concubine.bald == 1>>bald<<else>>shaved<</if>> head.
 	<</if>>
 	Your Head Girl's confident assistance managing your chattel and your Concubine's devoted comfort as you work allows you to @@.yellowgreen;be unusually productive@@ all morning.
-	<<run cashX(5000, "event", $HeadGirl)>>
+	<<run cashX(5000, "event", _S.HeadGirl)>>
 	<</replace>>
 <</link>>
 <br><<link "Tease them while they meet with you">>
 	<<replace "#result">>
-	<<run Enunciate($HeadGirl)>>
-	You tell them to go ahead, $HeadGirl.slaveName first, but you flip the sheet back and pat the soft mattress on either side of you while you do. They laugh and clamber on up, $HeadGirl.slaveName first, and $he manages to drag a $HeadGirl.nipples nipple across your
+	<<run Enunciate(_S.HeadGirl)>>
+	You tell them to go ahead, _S.HeadGirl.slaveName first, but you flip the sheet back and pat the soft mattress on either side of you while you do. They laugh and clamber on up, _S.HeadGirl.slaveName first, and $he manages to drag a _S.HeadGirl.nipples nipple across your
 	<<if $PC.boobs >= 300>>
 		own bare breasts
 	<<elseif $PC.title == 0>>
@@ -208,35 +206,35 @@ and it's also obvious that they've been filling the few minutes they've been wai
 	<<else>>
 		chest
 	<</if>>
-	as $he <<if $PC.belly >= 10000 || $HeadGirl.boobs > 10000 || $HeadGirl.belly >= 5000 || $HeadGirl.dick > 20 || $HeadGirl.balls > 20>>struggles to get<<else>>climbs<</if>> over you. Once $he's situated under the crook of your arm,
-	<<if $HeadGirl.boobs > 25000>>
+	as $he <<if $PC.belly >= 10000 || _S.HeadGirl.boobs > 10000 || _S.HeadGirl.belly >= 5000 || _S.HeadGirl.dick > 20 || _S.HeadGirl.balls > 20>>struggles to get<<else>>climbs<</if>> over you. Once $he's situated under the crook of your arm,
+	<<if _S.HeadGirl.boobs > 25000>>
 		the huge weight of $his boobs pressing against your ribcage,
-	<<elseif $HeadGirl.belly >= 10000>>
+	<<elseif _S.HeadGirl.belly >= 10000>>
 		the huge weight of $his _belly middle pressing against your body,
-	<<elseif $HeadGirl.dick > 4 && canAchieveErection($HeadGirl)>>
+	<<elseif _S.HeadGirl.dick > 4 && canAchieveErection(_S.HeadGirl)>>
 		which presses $his absurd erection against your hip,
-	<<elseif $HeadGirl.dick > 5>>
+	<<elseif _S.HeadGirl.dick > 5>>
 		which leaves $his absurd dick resting across your lap,
-	<<elseif $HeadGirl.energy > 95>>
+	<<elseif _S.HeadGirl.energy > 95>>
 		making the eager heat between the nympho's legs quite noticeable against your hip,
 	<<else>>
 		$his body warm against you,
 	<</if>>
 	$he clears $his throat and asks your permission to make a couple of last-minute changes to slaves' assignments for the day.
 	<br><br>
-	As $he did so, you were encircling your <<if $girl == _girl2>>$girl<<else>>lover<</if>>s in your <<if $arcologies[0].FSPhysicalIdealist != "unset">>powerful arms<<elseif $PC.title == 1>>dominant grip<<else>>loving grip<</if>>. Cupping your Head Girl's butt in your left hand and your Concubine's in your right, you give them a good squeeze as $HeadGirl.slaveName finishes $his question. $His voice rises cutely as your marauding hand moves over the
-	<<if $HeadGirl.butt > 10>>
-		endless expanse of $his backside. $He manages to finish, but trails off into a groan, the <<if Math.floor($HeadGirl.buttImplant/$HeadGirl.butt) > .60>>firm<<else>>soft<</if>> flesh quivering against your palm.
+	As $he did so, you were encircling your <<if $girl == _girl2>>$girl<<else>>lover<</if>>s in your <<if $arcologies[0].FSPhysicalIdealist != "unset">>powerful arms<<elseif $PC.title == 1>>dominant grip<<else>>loving grip<</if>>. Cupping your Head Girl's butt in your left hand and your Concubine's in your right, you give them a good squeeze as _S.HeadGirl.slaveName finishes $his question. $His voice rises cutely as your marauding hand moves over the
+	<<if _S.HeadGirl.butt > 10>>
+		endless expanse of $his backside. $He manages to finish, but trails off into a groan, the <<if Math.floor(_S.HeadGirl.buttImplant/_S.HeadGirl.butt) > .60>>firm<<else>>soft<</if>> flesh quivering against your palm.
 	<<else>>
-		place between $his <<if $HeadGirl.butt > 6>>huge<<elseif $HeadGirl.butt > 3>>healthy<<else>>cute<</if>> buttocks and you slowly
-		<<if canDoAnal($HeadGirl)>>
-			press a single malicious finger in against $his <<if $HeadGirl.anus > 2>>loose asspussy<<elseif $HeadGirl.butt > 0>>practiced asshole<<else>>virgin pucker<</if>>. $He manages to finish, but trails off into a groan, the warm, crinkled skin spasming against the teasing digit.
+		place between $his <<if _S.HeadGirl.butt > 6>>huge<<elseif _S.HeadGirl.butt > 3>>healthy<<else>>cute<</if>> buttocks and you slowly
+		<<if canDoAnal(_S.HeadGirl)>>
+			press a single malicious finger in against $his <<if _S.HeadGirl.anus > 2>>loose asspussy<<elseif _S.HeadGirl.butt > 0>>practiced asshole<<else>>virgin pucker<</if>>. $He manages to finish, but trails off into a groan, the warm, crinkled skin spasming against the teasing digit.
 		<<else>>
 			trace the perimeter of $his chastity. $He manages to finish, but trails off into a groan, $his rump tightening around the invading hand.
 		<</if>>
 	<</if>>
 	<br><br>
-	$Concubine.slaveName laughs at $him and asks _his2 own question, a little aesthetic question about one of your other bedslaves, but something you like to exercise artistic control over. _His2 giggling is cut off as _he2 has to concentrate on getting _his2 words out as you use two fingers to massage and tease the sensitive skin between _his2 ass and <<if $Concubine.vagina > -1>>the start of _his2 cunt<<elseif $Concubine.scrotum > 0>>_his2 ballsack<<else>>the base of _his2 dick<</if>>. It's $HeadGirl.slaveName's turn to laugh, which $he does throatily, $his warm breath gusting deliciously across your <<if $PC.boobs >= 300 || $PC.title == 0>>hardening nipples<<else>>pecs<</if>>. "<<S>>ee, it'<<s>> not ea<<s>>y," $he breathes across you at your Concubine.
+	_S.Concubine.slaveName laughs at $him and asks _his2 own question, a little aesthetic question about one of your other bedslaves, but something you like to exercise artistic control over. _His2 giggling is cut off as _he2 has to concentrate on getting _his2 words out as you use two fingers to massage and tease the sensitive skin between _his2 ass and <<if _S.Concubine.vagina > -1>>the start of _his2 cunt<<elseif _S.Concubine.scrotum > 0>>_his2 ballsack<<else>>the base of _his2 dick<</if>>. It's _S.HeadGirl.slaveName's turn to laugh, which $he does throatily, $his warm breath gusting deliciously across your <<if $PC.boobs >= 300 || $PC.title == 0>>hardening nipples<<else>>pecs<</if>>. "<<S>>ee, it'<<s>> not ea<<s>>y," $he breathes across you at your Concubine.
 	<br><br>
 	They soon get their revenge as you give them their orders, though, moving their expert hands between your legs to give you a handjob. They do it cooperatively as you continue to play with them, letting you enjoy the delightful sensation of
 	<<if $PC.dick != 0>>
@@ -250,142 +248,142 @@ and it's also obvious that they've been filling the few minutes they've been wai
 		a hand massaging your petals on either side while another delves two fingers inside you and a fourth pleasures your clit.
 	<</if>>
 	They crane their necks to rain kisses on your chin, neck, <<if $PC.boobs >= 300>>breasts, and nipples,<<elseif $PC.title == 0>>and nipples,<<else>>and chest,<</if>> and on each other's mouths. You <<if $PC.belly < 5000>>spring<<elseif $PC.belly < 10000>>pull yourself<<else>>slowly tip your gravid bulk<</if>> out of bed when you're satisfied, pulling your leadership with you towards the shower, and they @@.mediumaquamarine;trustingly bounce up to follow.@@
-	<<set $slaves[_headGirl].trust += 4, $slaves[_concubine].trust += 4>>
-	<<set $slaves[_headGirl].counter.oral++, $slaves[_concubine].counter.oral++>>
+	<<set _S.HeadGirl.trust += 4, _S.Concubine.trust += 4>>
+	<<set _S.HeadGirl.counter.oral++, _S.Concubine.counter.oral++>>
 	<<set $oralTotal += 2>>
 	<</replace>>
 <</link>>
-<<if ((canDoAnal($HeadGirl) && $HeadGirl.anus > 0) || (canDoVaginal($HeadGirl) && $HeadGirl.vagina > 0)) && ((canDoAnal($Concubine) && $Concubine.anus > 0) || (canDoVaginal($Concubine) && $Concubine.vagina > 0)) && $PC.belly+$Concubine.belly < 50000 && $PC.belly+$HeadGirl.belly < 50000>>
+<<if ((canDoAnal(_S.HeadGirl) && _S.HeadGirl.anus > 0) || (canDoVaginal(_S.HeadGirl) && _S.HeadGirl.vagina > 0)) && ((canDoAnal(_S.Concubine) && _S.Concubine.anus > 0) || (canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0)) && $PC.belly+_S.Concubine.belly < 50000 && $PC.belly+_S.HeadGirl.belly < 50000>>
 	<br><<link "Double penetrate them while they meet with you">>
 		<<replace "#result">>
-		You sit up and order $HeadGirl.slaveName up onto the bed, on $his back, and while $he's <<if $HeadGirl.energy > 95>>eagerly obeying<<else>>obediently getting situated<</if>>, you tell $Concubine.slaveName to ride $him, reverse cowgirl. $He straddles your Head Girl,
-		<<if $Concubine.butt > 6>>
+		You sit up and order _S.HeadGirl.slaveName up onto the bed, on $his back, and while $he's <<if _S.HeadGirl.energy > 95>>eagerly obeying<<else>>obediently getting situated<</if>>, you tell _S.Concubine.slaveName to ride $him, reverse cowgirl. $He straddles your Head Girl,
+		<<if _S.Concubine.butt > 6>>
 			using _his2 hands to situate _his2 huge buttocks.
-		<<elseif $Concubine.belly >= 10000>>
+		<<elseif _S.Concubine.belly >= 10000>>
 			making sure _his2 _bellyCon belly is comfortably positioned.
-		<<elseif $Concubine.boobs > 4000>>
+		<<elseif _S.Concubine.boobs > 4000>>
 			making _his2 heavy boobs sway deliciously.
 		<<else>>
-			carefully situating _his2 legs astride $HeadGirl.slaveName's hips.
+			carefully situating _his2 legs astride _S.HeadGirl.slaveName's hips.
 		<</if>>
-		You plant a hard kiss on $Concubine.slaveName's mouth, pressing _him2 backwards <<if $HeadGirl.belly >= 10000>>into $HeadGirl.slaveName's taut belly <</if>>as you maneuver yourself into place<<if $PC.dick == 0>> and don a strap-on, since there isn't enough space for tribbing to work here<</if>>.
-		<<if (canDoVaginal($Concubine) && $Concubine.vagina > 0)>>
+		You plant a hard kiss on _S.Concubine.slaveName's mouth, pressing _him2 backwards <<if _S.HeadGirl.belly >= 10000>>into _S.HeadGirl.slaveName's taut belly <</if>>as you maneuver yourself into place<<if $PC.dick == 0>> and don a strap-on, since there isn't enough space for tribbing to work here<</if>>.
+		<<if (canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0)>>
 			/* this is up here to give the PC priority on pregnancy */
-			<<if canImpreg($slaves[_concubine], $PC)>>
-				<<= knockMeUp($slaves[_concubine], 10, 0, -1, 1)>>
+			<<if canImpreg(_S.Concubine, $PC)>>
+				<<= knockMeUp(_S.Concubine, 10, 0, -1, 1)>>
 			<</if>>
-			<<set $slaves[_concubine].counter.vaginal++, $vaginalTotal++>>
-			_He2 moans into you as _he2 feels you run <<if $PC.dick == 0>>the cool head of the phallus<<else>>your cockhead<</if>> along _his2<<if $Concubine.labia > 0>> generous<</if>> labia before plunging it inside _him2. Getting the idea, $HeadGirl.slaveName
-			<<if (canPenetrate($HeadGirl))>>
-				<<if (canDoAnal($Concubine) && $Concubine.anus > 0)>>
-					<<if $HeadGirl.dick - $Concubine.anus > 2>>
-						gently pushes $his cock up $Concubine.slaveName's ass, since $he knows your Concubine's anus will be a little tight around $his imposing dick.
-					<<elseif $HeadGirl.dick - $Concubine.anus > 0>>
-						pushes $his cock up $Concubine.slaveName's ass, since $he knows your Concubine can take $his dick without too much trouble.
+			<<set _S.Concubine.counter.vaginal++, $vaginalTotal++>>
+			_He2 moans into you as _he2 feels you run <<if $PC.dick == 0>>the cool head of the phallus<<else>>your cockhead<</if>> along _his2<<if _S.Concubine.labia > 0>> generous<</if>> labia before plunging it inside _him2. Getting the idea, _S.HeadGirl.slaveName
+			<<if (canPenetrate(_S.HeadGirl))>>
+				<<if (canDoAnal(_S.Concubine) && _S.Concubine.anus > 0)>>
+					<<if _S.HeadGirl.dick - _S.Concubine.anus > 2>>
+						gently pushes $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine's anus will be a little tight around $his imposing dick.
+					<<elseif _S.HeadGirl.dick - _S.Concubine.anus > 0>>
+						pushes $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine can take $his dick without too much trouble.
 					<<else>>
-						shoves $his cock up $Concubine.slaveName's ass, since $he knows your Concubine can take $his dick with ease.
+						shoves $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine can take $his dick with ease.
 					<</if>>
-					<<if $PC.dick != 0>>You feel the penetration through $Concubine.slaveName's vaginal walls, an incredible sensation.<</if>>
-					<<if canImpreg($slaves[_concubine], $slaves[_headGirl])>>
-						<<= knockMeUp($slaves[_concubine], 10, 1, $slaves[_headGirl].ID, 1)>>
+					<<if $PC.dick != 0>>You feel the penetration through _S.Concubine.slaveName's vaginal walls, an incredible sensation.<</if>>
+					<<if canImpreg(_S.Concubine, _S.HeadGirl)>>
+						<<= knockMeUp(_S.Concubine, 10, 1, _S.HeadGirl.ID, 1)>>
 					<</if>>
-					<<set $slaves[_concubine].counter.anal++, $analTotal++>>
+					<<set _S.Concubine.counter.anal++, $analTotal++>>
 				<<else>>
-					gently eases $his cock in beside you.<<if $PC.dick != 0>> The added friction against you in $Concubine.slaveName's vagina feels incredible.<</if>>
-					<<set $slaves[_concubine].counter.vaginal++, $vaginalTotal++>>
-					<<if canImpreg($slaves[_concubine], $slaves[_headGirl])>>
-						<<= knockMeUp($slaves[_concubine], 10, 0, $slaves[_headGirl].ID, 1)>>
+					gently eases $his cock in beside you.<<if $PC.dick != 0>> The added friction against you in _S.Concubine.slaveName's vagina feels incredible.<</if>>
+					<<set _S.Concubine.counter.vaginal++, $vaginalTotal++>>
+					<<if canImpreg(_S.Concubine, _S.HeadGirl)>>
+						<<= knockMeUp(_S.Concubine, 10, 0, _S.HeadGirl.ID, 1)>>
 					<</if>>
 				<</if>>
-				<<set $slaves[_headGirl].counter.penetrative++, $penetrativeTotal++>>
+				<<set _S.HeadGirl.counter.penetrative++, $penetrativeTotal++>>
 			<<else>>
 				slides a hand down and
-				<<if (canDoAnal($Concubine) && $Concubine.anus > 0)>>
-					<<if $Concubine.anus > 3>>
-						fists $Concubine.slaveName's loose ass, since that's what it takes to fill your Concubine's gaping asspussy.
-					<<elseif $Concubine.anus == 3>>
-						starts to fuck $Concubine.slaveName's soft anus with three fingers.
-					<<elseif $Concubine.anus == 2>>
-						starts to fuck $Concubine.slaveName's asspussy with two fingers.
+				<<if (canDoAnal(_S.Concubine) && _S.Concubine.anus > 0)>>
+					<<if _S.Concubine.anus > 3>>
+						fists _S.Concubine.slaveName's loose ass, since that's what it takes to fill your Concubine's gaping asspussy.
+					<<elseif _S.Concubine.anus == 3>>
+						starts to fuck _S.Concubine.slaveName's soft anus with three fingers.
+					<<elseif _S.Concubine.anus == 2>>
+						starts to fuck _S.Concubine.slaveName's asspussy with two fingers.
 					<<else>>
-						starts to fuck $Concubine.slaveName's tight little backdoor with a finger.
+						starts to fuck _S.Concubine.slaveName's tight little backdoor with a finger.
 					<</if>>
-					<<if $PC.dick != 0>>You feel the penetration through $Concubine.slaveName's vaginal walls, an incredible sensation.<</if>>
-					<<set $slaves[_concubine].counter.anal++, $analTotal++>>
+					<<if $PC.dick != 0>>You feel the penetration through _S.Concubine.slaveName's vaginal walls, an incredible sensation.<</if>>
+					<<set _S.Concubine.counter.anal++, $analTotal++>>
 				<<else>>
-					gently eases a few fingers in beside you.<<if $PC.dick != 0>> The added sensation in $Concubine.slaveName's vagina feels incredible.<</if>>
-					<<set $slaves[_concubine].counter.vaginal++, $vaginalTotal++>>
+					gently eases a few fingers in beside you.<<if $PC.dick != 0>> The added sensation in _S.Concubine.slaveName's vagina feels incredible.<</if>>
+					<<set _S.Concubine.counter.vaginal++, $vaginalTotal++>>
 				<</if>>
 			<</if>>
 		<<else>>
 			/* this is up here to give the PC priority on pregnancy */
-			<<if canImpreg($slaves[_concubine], $PC)>>
-				<<= knockMeUp($slaves[_concubine], 10, 1, -1, 1)>>
+			<<if canImpreg(_S.Concubine, $PC)>>
+				<<= knockMeUp(_S.Concubine, 10, 1, -1, 1)>>
 			<</if>>
-			<<set $slaves[_concubine].counter.anal += 2, $analTotal += 2>>
-			Getting the idea, $HeadGirl.slaveName
-			<<if (canPenetrate($HeadGirl))>>
-				<<if $HeadGirl.dick - $Concubine.anus > 2>>
-					gently pushes $his cock up $Concubine.slaveName's ass, since $he knows your Concubine's anus will be a little tight around $his imposing dick.
-				<<elseif $HeadGirl.dick - $Concubine.anus > 0>>
-					pushes $his cock up $Concubine.slaveName's ass, since $he knows your Concubine can take $his dick without too much trouble.
+			<<set _S.Concubine.counter.anal += 2, $analTotal += 2>>
+			Getting the idea, _S.HeadGirl.slaveName
+			<<if (canPenetrate(_S.HeadGirl))>>
+				<<if _S.HeadGirl.dick - _S.Concubine.anus > 2>>
+					gently pushes $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine's anus will be a little tight around $his imposing dick.
+				<<elseif _S.HeadGirl.dick - _S.Concubine.anus > 0>>
+					pushes $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine can take $his dick without too much trouble.
 				<<else>>
-					shoves $his cock up $Concubine.slaveName's ass, since $he knows your Concubine can take $his dick with ease.
+					shoves $his cock up _S.Concubine.slaveName's ass, since $he knows your Concubine can take $his dick with ease.
 				<</if>>
-				Then $he grabs $Concubine.slaveName <<if !hasAnyLegs($Concubine)>>by the stumps<<else>>behind the knee<<if hasBothLegs($Concubine)>>s<</if>><</if>> and spreads _his2 <<if hasBothLegs($Concubine)>>legs<<else>>cheeks<</if>> as far as they'll go, rotating _his2 hips to position _him2 for another phallus up the butt.
-				<<set $slaves[_headGirl].counter.penetrative++, $penetrativeTotal++>>
-				<<if canImpreg($slaves[_concubine], $slaves[_headGirl])>>
-					<<= knockMeUp($slaves[_concubine], 10, 1, $slaves[_headGirl].ID, 1)>>
+				Then $he grabs _S.Concubine.slaveName <<if !hasAnyLegs(_S.Concubine)>>by the stumps<<else>>behind the knee<<if hasBothLegs(_S.Concubine)>>s<</if>><</if>> and spreads _his2 <<if hasBothLegs(_S.Concubine)>>legs<<else>>cheeks<</if>> as far as they'll go, rotating _his2 hips to position _him2 for another phallus up the butt.
+				<<set _S.HeadGirl.counter.penetrative++, $penetrativeTotal++>>
+				<<if canImpreg(_S.Concubine, _S.HeadGirl)>>
+					<<= knockMeUp(_S.Concubine, 10, 1, _S.HeadGirl.ID, 1)>>
 				<</if>>
 			<<else>>
 				slides a hand down and
-				<<if $Concubine.anus > 3>>
-					fists $Concubine.slaveName's loose ass, since that's what it takes to fill your Concubine's gaping asspussy.
-				<<elseif $Concubine.anus == 3>>
-					starts to fuck $Concubine.slaveName's soft anus with three fingers.
-				<<elseif $Concubine.anus == 2>>
-					starts to fuck $Concubine.slaveName's asspussy with two fingers.
+				<<if _S.Concubine.anus > 3>>
+					fists _S.Concubine.slaveName's loose ass, since that's what it takes to fill your Concubine's gaping asspussy.
+				<<elseif _S.Concubine.anus == 3>>
+					starts to fuck _S.Concubine.slaveName's soft anus with three fingers.
+				<<elseif _S.Concubine.anus == 2>>
+					starts to fuck _S.Concubine.slaveName's asspussy with two fingers.
 				<<else>>
-					starts to fuck $Concubine.slaveName's tight little backdoor with a finger.
+					starts to fuck _S.Concubine.slaveName's tight little backdoor with a finger.
 				<</if>>
-				Then $he grabs $Concubine.slaveName behind a knee with $his other hand and rotates _his2 hips to position _him2 for additional anal penetration.
+				Then $he grabs _S.Concubine.slaveName behind a knee with $his other hand and rotates _his2 hips to position _him2 for additional anal penetration.
 			<</if>>
 			Your Concubine shivers and moans as _his2 sphincter accommodates <<if $PC.dick != 0>>your cock<<else>>the strap-on<</if>>, too.
 		<</if>>
 		<br><br>
-		You start to fuck _him2, and then ask _him2 what _he2 needed from you. The poor _girl2 has to concentrate hard to remember why _he2's here through the waterfall of sensations, but _he2 manages it eventually, gasping out a little aesthetic question about one of your other bedslaves. You give _him2 _his2 answer and make _him2 repeat it back. When you're satisfied, you grab _his2 <<if !hasAnyLegs($Concubine)>>hips<<else>>ankle<<if hasBothLegs($Concubine)>>s<</if>><</if>> and pull them up, sliding _him2 off your <<if $PC.dick != 0>>pole<<else>>strap-on<</if>> and $HeadGirl.slaveName's <<if (canPenetrate($HeadGirl))>>dick<<else>>penetrating fingers<</if>> with a lovely lewd noise. After appreciating the sight of _his2 well-fucked <<if ((canDoAnal($Concubine) && $Concubine.anus > 0) && (canDoVaginal($Concubine) && $Concubine.vagina > 0))>>holes<<else>>hole<</if>>, you swing _his2 ass over and deposit it on the bed, producing a grunt as the drop forces the wind out of _him2. Grabbing your Head Girl in turn, you yank $him towards you, give the giggling slave a kiss, seize $his
-		<<if $HeadGirl.hips > 2>>
+		You start to fuck _him2, and then ask _him2 what _he2 needed from you. The poor _girl2 has to concentrate hard to remember why _he2's here through the waterfall of sensations, but _he2 manages it eventually, gasping out a little aesthetic question about one of your other bedslaves. You give _him2 _his2 answer and make _him2 repeat it back. When you're satisfied, you grab _his2 <<if !hasAnyLegs(_S.Concubine)>>hips<<else>>ankle<<if hasBothLegs(_S.Concubine)>>s<</if>><</if>> and pull them up, sliding _him2 off your <<if $PC.dick != 0>>pole<<else>>strap-on<</if>> and _S.HeadGirl.slaveName's <<if (canPenetrate(_S.HeadGirl))>>dick<<else>>penetrating fingers<</if>> with a lovely lewd noise. After appreciating the sight of _his2 well-fucked <<if ((canDoAnal(_S.Concubine) && _S.Concubine.anus > 0) && (canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0))>>holes<<else>>hole<</if>>, you swing _his2 ass over and deposit it on the bed, producing a grunt as the drop forces the wind out of _him2. Grabbing your Head Girl in turn, you yank $him towards you, give the giggling slave a kiss, seize $his
+		<<if _S.HeadGirl.hips > 2>>
 			breeding
-		<<elseif $HeadGirl.weight > 95>>
+		<<elseif _S.HeadGirl.weight > 95>>
 			padded
-		<<elseif $HeadGirl.weight > 10>>
+		<<elseif _S.HeadGirl.weight > 10>>
 			plush
-		<<elseif $HeadGirl.hips > 1>>
+		<<elseif _S.HeadGirl.hips > 1>>
 			broad
-		<<elseif $HeadGirl.hips >= 0>>
+		<<elseif _S.HeadGirl.hips >= 0>>
 			curvy
 		<<else>>
 			trim
 		<</if>>
-		hips and lift $him onto $Concubine.slaveName's lap. $He takes
-		<<if (canDoVaginal($HeadGirl) && $HeadGirl.vagina > 0)>>
-			<<if (canDoVaginal($Concubine) && $Concubine.vagina > 0)>>
-				<<if (canDoAnal($HeadGirl) && $HeadGirl.anus > 0)>>
+		hips and lift $him onto _S.Concubine.slaveName's lap. $He takes
+		<<if (canDoVaginal(_S.HeadGirl) && _S.HeadGirl.vagina > 0)>>
+			<<if (canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0)>>
+				<<if (canDoAnal(_S.HeadGirl) && _S.HeadGirl.anus > 0)>>
 					$his own double pounding, pussy and ass,
 				<<else>>
 					$his own double pounding,
 				<</if>>
 			<<else>>
-				<<if (canDoAnal($HeadGirl) && $HeadGirl.anus > 0)>>
+				<<if (canDoAnal(_S.HeadGirl) && _S.HeadGirl.anus > 0)>>
 					double penetration instead, since unlike your Concubine $he has two lower fuckholes,
 				<<else>>
 					$his own double pounding,
 				<</if>>
 			<</if>>
 		<<else>>
-			<<if (canDoVaginal($Concubine) && $Concubine.vagina > 0)>>
+			<<if (canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0)>>
 				double anal instead, since unlike your Concubine
-				<<if $HeadGirl.vagina == -1>>
+				<<if _S.HeadGirl.vagina == -1>>
 					$he only has the one lower fuckhole,
 				<<else>>
 					$his pussy is off limits,
@@ -395,55 +393,55 @@ and it's also obvious that they've been filling the few minutes they've been wai
 			<</if>>
 		<</if>>
 		while taking $his turn getting instructions. $He manages to ask your permission to make a couple of last-minute changes to slaves' assignments for the day. Having thus given your leadership guidance, you jump up for a shower, crooking a finger to indicate that they should follow. They @@.hotpink;trail obediently after you,@@ a bit sore, their holes dripping
-		<<if ($HeadGirl.vagina > -1) || ($HeadGirl.vagina > -1)>>
-			<<if ($PC.balls > 0) || ($HeadGirl.balls > 0) || ($Concubine.balls > 0)>>
+		<<if (_S.HeadGirl.vagina > -1) || (_S.HeadGirl.vagina > -1)>>
+			<<if ($PC.balls > 0) || (_S.HeadGirl.balls > 0) || (_S.Concubine.balls > 0)>>
 				cum, pussyjuice, and lube.
 			<<else>>
 				pussyjuice and lube.
 			<</if>>
 		<<else>>
-			<<if ($PC.balls > 0) || ($HeadGirl.balls > 0) || ($Concubine.balls > 0)>>
+			<<if ($PC.balls > 0) || (_S.HeadGirl.balls > 0) || (_S.Concubine.balls > 0)>>
 				cum and lube.
 			<<else>>
 				lube.
 			<</if>>
 		<</if>>
-		<<set $slaves[_headGirl].devotion += 4, $slaves[_concubine].devotion += 4>>
-		/* sex count cleanup for $HeadGirl since the above block is not pretty */
-		<<if (canDoVaginal($HeadGirl) && $HeadGirl.vagina > 0)>>
-			<<if canImpreg($slaves[_headGirl], $PC)>>
-				<<= knockMeUp($slaves[_headGirl], 10, 0, -1, 1)>>
+		<<set _S.HeadGirl.devotion += 4, _S.Concubine.devotion += 4>>
+		/* sex count cleanup for _S.HeadGirl since the above block is not pretty */
+		<<if (canDoVaginal(_S.HeadGirl) && _S.HeadGirl.vagina > 0)>>
+			<<if canImpreg(_S.HeadGirl, $PC)>>
+				<<= knockMeUp(_S.HeadGirl, 10, 0, -1, 1)>>
 			<</if>>
-			<<set $slaves[_headGirl].counter.vaginal++, $vaginalTotal++>>
-			<<if (canPenetrate($Concubine))>>
-				<<if (canDoAnal($HeadGirl) && $HeadGirl.anus > 0)>>
-					<<if canImpreg($slaves[_headGirl], $slaves[_concubine])>>
-						<<= knockMeUp($slaves[_headGirl], 10, 1, $slaves[_concubine].ID, 1)>>
+			<<set _S.HeadGirl.counter.vaginal++, $vaginalTotal++>>
+			<<if (canPenetrate(_S.Concubine))>>
+				<<if (canDoAnal(_S.HeadGirl) && _S.HeadGirl.anus > 0)>>
+					<<if canImpreg(_S.HeadGirl, _S.Concubine)>>
+						<<= knockMeUp(_S.HeadGirl, 10, 1, $ConcubineID, 1)>>
 					<</if>>
-					<<set $slaves[_headGirl].counter.anal++, $analTotal++>>
+					<<set _S.HeadGirl.counter.anal++, $analTotal++>>
 				<<else>>
-					<<set $slaves[_headGirl].counter.vaginal++, $vaginalTotal++>>
-					<<if canImpreg($slaves[_headGirl], $slaves[_concubine])>>
-						<<= knockMeUp($slaves[_headGirl], 10, 0, $slaves[_concubine].ID, 1)>>
+					<<set _S.HeadGirl.counter.vaginal++, $vaginalTotal++>>
+					<<if canImpreg(_S.HeadGirl, _S.Concubine)>>
+						<<= knockMeUp(_S.HeadGirl, 10, 0, $ConcubineID, 1)>>
 					<</if>>
 				<</if>>
-				<<set $slaves[_concubine].counter.penetrative++, $penetrativeTotal++>>
+				<<set _S.Concubine.counter.penetrative++, $penetrativeTotal++>>
 			<<else>>
-				<<if (canDoAnal($HeadGirl) && $HeadGirl.anus > 0)>>
-					<<set $slaves[_headGirl].counter.anal++, $analTotal++>>
+				<<if (canDoAnal(_S.HeadGirl) && _S.HeadGirl.anus > 0)>>
+					<<set _S.HeadGirl.counter.anal++, $analTotal++>>
 				<<else>>
-					<<set $slaves[_headGirl].counter.vaginal++, $vaginalTotal++>>
+					<<set _S.HeadGirl.counter.vaginal++, $vaginalTotal++>>
 				<</if>>
 			<</if>>
 		<<else>>
-			<<if canImpreg($slaves[_headGirl], $PC)>>
-				<<= knockMeUp($slaves[_headGirl], 10, 1, -1, 1)>>
+			<<if canImpreg(_S.HeadGirl, $PC)>>
+				<<= knockMeUp(_S.HeadGirl, 10, 1, -1, 1)>>
 			<</if>>
-			<<set $slaves[_headGirl].counter.anal += 2, $analTotal += 2>>
-			<<if (canPenetrate($Concubine))>>
-				<<set $slaves[_headGirl].counter.penetrative++, $penetrativeTotal++>>
-				<<if canImpreg($slaves[_headGirl], $slaves[_concubine])>>
-					<<= knockMeUp($slaves[_headGirl], 10, 1, $slaves[_concubine].ID, 1)>>
+			<<set _S.HeadGirl.counter.anal += 2, $analTotal += 2>>
+			<<if (canPenetrate(_S.Concubine))>>
+				<<set _S.HeadGirl.counter.penetrative++, $penetrativeTotal++>>
+				<<if canImpreg(_S.HeadGirl, _S.Concubine)>>
+					<<= knockMeUp(_S.HeadGirl, 10, 1, $ConcubineID, 1)>>
 				<</if>>
 			<</if>>
 		<</if>>
diff --git a/src/uncategorized/peLonelyBodyguard.tw b/src/uncategorized/peLonelyBodyguard.tw
index 3b2f4f7b991c29b61a5403376cf65a0fd9deecc5..bccd428a8b32e8adf289d122e9ee45c06a15b608 100644
--- a/src/uncategorized/peLonelyBodyguard.tw
+++ b/src/uncategorized/peLonelyBodyguard.tw
@@ -2,9 +2,9 @@
 
 <<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Random Nonindividual Event">>
 
-<<set $activeSlave = $Bodyguard, _targetSlaveIndex = []>>
+<<set $activeSlave =_S.Bodyguard, _targetSlaveIndex = []>>
 <<for _i = 0; _i < $slaves.length; _i++>>
-	<<if $slaves[_i].devotion >= -20 && $slaves[_i].relationship == 0 && $slaves[_i].ID != $Bodyguard.ID>>
+	<<if $slaves[_i].devotion >= -20 && $slaves[_i].relationship == 0 && $slaves[_i].ID != $BodyguardID>>
 		<<set _targetSlaveIndex.push(_i)>>
 	<</if>>
 <</for>>
diff --git a/src/uncategorized/persBusiness.tw b/src/uncategorized/persBusiness.tw
index f64939884ac8d646c88fa8749d4c64e1dd5b30dd..7bd43749694133292e3a651c4f35fe9421b7f3a6 100644
--- a/src/uncategorized/persBusiness.tw
+++ b/src/uncategorized/persBusiness.tw
@@ -29,16 +29,14 @@
 	<</if>>
 <</if>>
 <<if $PC.health.shortDamage >= 30>>
-	<<if $PC.health.shortDamage >= 30>>
-		<<run endWeekHealthDamage($PC)>>
-		The injuries received in the recent battle prevents you from engaging in tiring endeavors.
-		<<if $PC.health.shortDamage >= 51>>
-			Your trusted physician believes it will still take a few weeks to fully recover.
-		<<elseif $PC.health.shortDamage >= 39>>
-			You are starting to feel better. It's very likely you will be back to full working order within the next week.
-		<<else>>
-			You have finally recovered from your injuries.
-		<</if>>
+	<<run endWeekHealthDamage($PC)>>
+	The injuries received in the recent battle prevents you from engaging in tiring endeavors.
+	<<if $PC.health.shortDamage >= 51>>
+		Your trusted physician believes it will still take a few weeks to fully recover.
+	<<elseif $PC.health.shortDamage >= 39>>
+		You are starting to feel better. It's very likely you will be back to full working order within the next week.
+	<<else>>
+		You have finally recovered from your injuries.
 	<</if>>
 <<elseif ($personalAttention == "whoring")>>
 	<<set _income = random(2000,4500)>>
@@ -320,7 +318,7 @@
 		During important meetings with higher society, it is wise to have a lot of slaves to put at the disposition of others. But some slaveowners grow really attached to their slaves, and so they'd much rather rent out unknown slaves from an anonymous owner's stock than use their own. This is a good opportunity to make some money, as shown by the @@.yellowgreen;<<print cashFormat(_income)>>@@ you managed to make.
 	<<case "chinese law">>
 		<<set _income += random(2000,3000)>>
-		<<setLocalPronouns $HeadGirl>>
+		<<setLocalPronouns _S.HeadGirl>>
 		This time, you have a good idea that will also make use of your Head Girl. You coax $him into thinking $he should accept bribes for the time being, making up a good reason on the spot, and $he ends up bringing back @@.yellowgreen;<<print cashFormat(_income)>>@@ from all the bribes people gave for $him to turn the other way.
 	<<case "chinese">>
 		<<set _income += random(1500,2500)>>
diff --git a/src/uncategorized/personalAttentionSelect.tw b/src/uncategorized/personalAttentionSelect.tw
index 74c8f24c04d6af32566e9a6f75b94aa9946b215a..1f52bae005cb55866918141e98b2ce52e542ad4d 100644
--- a/src/uncategorized/personalAttentionSelect.tw
+++ b/src/uncategorized/personalAttentionSelect.tw
@@ -23,7 +23,7 @@
 <<if $PC.skill.hacking > 25>>
 	<br>[[Sell your intrusion services to the highest bidder|Main][$personalAttention = "technical accidents"]]
 <</if>>
-<<if $HeadGirl != 0>>
+<<if $HeadGirlID != 0>>
 	<br>[[Support your Head Girl|Main][$personalAttention = "HG"]]
 <</if>>
 <br>[[Have as much sex with your slaves as possible|Main][$personalAttention = "sex"]]
@@ -359,7 +359,7 @@
 <</if>> /* CLOSES NO SLAVE SELECTED */
 
 <br><br>__Select a slave to train:__ <<if $PC.skill.slaving >= 100>>//Your @@.springgreen;slaving experience@@ allows you to divide your attention between more than one slave each week, with slightly reduced efficiency//<</if>>
-<<= App.UI.SlaveList.slaveSelectionList(
+<<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)
 	)>>
diff --git a/src/uncategorized/randomIndividualEvent.tw b/src/uncategorized/randomIndividualEvent.tw
index 67596866e79a6530ba9adde4b3cf7269d3661a22..cbe9fa27a51bbc5e5ea7209c4810e70c99532a75 100644
--- a/src/uncategorized/randomIndividualEvent.tw
+++ b/src/uncategorized/randomIndividualEvent.tw
@@ -28,7 +28,7 @@
 
 	/* SUB CHECKS */
 
-	<<set $REAnalCowgirlSubIDs = [], $RETasteTestSubIDs = [], $RESimpleAssaultIDs = [], $REInterslaveBeggingIDs = [], $RECockmilkInterceptionIDs = [], $REShowerForceSubIDs = [], $RESadisticDescriptionSubIDs = [], $REIfYouEnjoyItSubIDs = [], $REBoobCollisionSubIDs = [], $RERepressedAnalVirginSubIDs = []>>
+	<<set $REAnalCowgirlSubIDs = [], $RETasteTestSubIDs = [], $REInterslaveBeggingIDs = [], $RECockmilkInterceptionIDs = [], $REShowerForceSubIDs = [], $RESadisticDescriptionSubIDs = [], $REIfYouEnjoyItSubIDs = [], $REBoobCollisionSubIDs = [], $RERepressedAnalVirginSubIDs = []>>
 
 	<<for $i = 0; $i < $slaves.length; $i++>>
 		<<if $slaves[$i].fetish != "mindbroken">>
@@ -44,17 +44,6 @@
 									<<set $RESadisticDescriptionSubIDs.push($slaves[$i].ID)>>
 								<</if>>
 							<</if>>
-							<<if $slaves[$i].anus != 0 && canDoAnal($slaves[$i])>>
-								<<if $slaves[$i].vagina != 0 && canDoVaginal($slaves[$i])>>
-									<<if $slaves[$i].belly < 2000>>
-										<<if $slaves[$i].skill.combat == 0>>
-											<<if $slaves[$i].muscles <= 30>>
-												<<set $RESimpleAssaultIDs.push($slaves[$i].ID)>>
-											<</if>>
-										<</if>>
-									<</if>>
-								<</if>>
-							<</if>>
 						<<else>>
 							<<if $slaves[$i].boobs > 3000>>
 								<<set $REBoobCollisionSubIDs.push($slaves[$i].ID)>>
diff --git a/src/uncategorized/randomNonindividualEvent.tw b/src/uncategorized/randomNonindividualEvent.tw
index fe1403fdd8e2953f137c2b85076c5be249e88c39..96bb896e4303effd606daaf1cdab1e49df9496bc 100644
--- a/src/uncategorized/randomNonindividualEvent.tw
+++ b/src/uncategorized/randomNonindividualEvent.tw
@@ -329,44 +329,44 @@
 		<<set $events.push("RE busy arcade")>>
 	<</if>>
 	<<if App.Entity.facilities.masterSuite.employeesIDs().size > 3>>
-		<<if $Concubine != 0>>
+		<<if $ConcubineID != 0>>
 			<<set $events.push("RE busy master suite")>>
 		<</if>>
 	<</if>>
 
 	/* Position Events */
 
-	<<if ($HeadGirl != 0)>>
+	<<if _S.HeadGirl>>
 
-		<<if ($Concubine != 0)>>
+		<<if (_S.Concubine)>>
 			<<if $fuckSlaves > 1>>
-				<<if canTalk($Concubine) && canSee($Concubine) && canHear($Concubine) && $Concubine.fetish != "mindbroken" && canHold($Concubine) && canWalk($Concubine)>>
+				<<if canTalk(_S.Concubine) && canSee(_S.Concubine) && canHear(_S.Concubine) && _S.Concubine.fetish != "mindbroken" && canHold(_S.Concubine) && canWalk(_S.Concubine)>>
 					<<set $events.push("PE headgirl concubine")>>
 				<</if>>
 			<</if>>
 		<</if>>
 
-		<<set $j = $slaves.findIndex(function(s) { return s.ID != $HeadGirl.ID && s.devotion <= 20; })>>
+		<<set $j = $slaves.findIndex(function(s) { return s.ID != $HeadGirlID && s.devotion <= 20; })>>
 
-		<<if canPenetrate($HeadGirl) && ($HeadGirl.balls > 0)>>
+		<<if canPenetrate(_S.HeadGirl) && (_S.HeadGirl.balls > 0)>>
 			<<if $j != -1>>
 				<<set $PESSevent.push("headgirl dickgirl")>> /* requires valid $slaves[$j] */
 			<</if>>
 
 			<<if ($universalRulesImpregnation == "HG")>>
-				<<if ($HeadGirl.devotion > 95)>>
+				<<if (_S.HeadGirl.devotion > 95)>>
 					<<set $PESSevent.push("worshipful impregnatrix")>> /* does not use $j */
 				<</if>>
 			<</if>>
 		<</if>>
 
-		<<if ($HeadGirl.devotion > 95)>>
+		<<if (_S.HeadGirl.devotion > 95)>>
 			<<if ($slaves.length > 4)>>
 				<<set $PESSevent.push("loving headgirl")>> /* uses $j if valid, but not required */
 			<</if>>
 			<<if ($week > 20)>>
-				<<if ($HeadGirl.trust > 95)>>
-					<<if ($HeadGirl.intelligence+$HeadGirl.intelligenceImplant > 50)>>
+				<<if (_S.HeadGirl.trust > 95)>>
+					<<if (_S.HeadGirl.intelligence+_S.HeadGirl.intelligenceImplant > 50)>>
 						<<set $PESSevent.push("worried headgirl")>> /* does not use $j */
 					<</if>>
 				<</if>>
@@ -375,22 +375,22 @@
 
 	<</if>>
 
-	<<if ($Bodyguard != 0)>>
+	<<if _S.Bodyguard>>
 
 		<<set $events.push("PE pit fight invite")>>
 
-		<<if ($Bodyguard.skill.combat != 0)>>
+		<<if (_S.Bodyguard.skill.combat != 0)>>
 			<<set $PESSevent.push("bodyguard bedtime")>>
 		<</if>>
 
-		<<if ($Bodyguard.devotion > 45)>>
+		<<if (_S.Bodyguard.devotion > 45)>>
 			<<set $PESSevent.push("bodyguard beatdown")>>
 		<</if>>
 
-		<<if ($Bodyguard.relationship == 0)>>
-			<<if ($Bodyguard.rules.relationship == "permissive")>>
-				<<if ($Bodyguard.fetish != "mindbroken")>>
-					<<set $j = $slaves.findIndex(function(s) { return s.ID != $Bodyguard.ID && s.relationship == 0 && s.devotion >= -20; })>>
+		<<if (_S.Bodyguard.relationship == 0)>>
+			<<if (_S.Bodyguard.rules.relationship == "permissive")>>
+				<<if (_S.Bodyguard.fetish != "mindbroken")>>
+					<<set $j = $slaves.findIndex(function(s) { return s.ID != $BodyguardID && s.relationship == 0 && s.devotion >= -20; })>>
 					<<if $j != -1>>
 						<<set $events.push("PE lonely bodyguard")>> /* requires valid $slaves[$j] */
 					<</if>>
@@ -398,7 +398,7 @@
 			<</if>>
 		<</if>>
 
-		<<if ($Bodyguard.skill.combat == 0)>>
+		<<if (_S.Bodyguard.skill.combat == 0)>>
 			<<set $events.push("PE combat training")>>
 			<<set $events.push("PE combat training")>>
 			<<set $events.push("PE combat training")>>
@@ -406,11 +406,11 @@
 
 	<</if>>
 
-	<<if ($Milkmaid != 0) && (_L.dairy >= 5) && ($Milkmaid.health.condition >= 70)>>
-		<<if ($Milkmaid.muscles > 5)>>
+	<<if _S.Milkmaid && (_L.dairy >= 5) && (_S.Milkmaid.health.condition >= 70)>>
+		<<if (_S.Milkmaid.muscles > 5)>>
 			<<set $PESSevent.push("tired milkmaid")>>
 		<</if>>
-		<<if canAchieveErection($Milkmaid)>>
+		<<if canAchieveErection(_S.Milkmaid)>>
 			<<set $PESSevent.push("tired collectrix")>>
 		<</if>>
 	<</if>>
@@ -419,7 +419,7 @@
 		<<set $PETSevent.push("stewardess beating")>>
 	<</if>>
 
-	<<if ($Madam != 0) && (_L.brothel >= 5) && ($Madam.actualAge >= 35 || $AgePenalty == 0) && ($Madam.skill.whoring >= 100)>>
+	<<if _S.Madam && (_L.brothel >= 5) && (_S.Madam.actualAge >= 35 || $AgePenalty == 0) && (_S.Madam.skill.whoring >= 100)>>
 		<<set $PESSevent.push("madam strategy")>>
 	<</if>>
 
@@ -446,12 +446,12 @@
 		<</if>>
 	<</if>>
 
-	<<if ($Concubine != 0)>>
-		<<if canWalk($Concubine)>>
-			<<if canSee($Concubine)>>
+	<<if (_S.Concubine)>>
+		<<if canWalk(_S.Concubine)>>
+			<<if canSee(_S.Concubine)>>
 				<<set $PESSevent.push("loving concubine")>>
 			<</if>>
-			<<if canTalk($Concubine) && canHear($Concubine)>>
+			<<if canTalk(_S.Concubine) && canHear(_S.Concubine)>>
 				<<set $events.push("PE concubine interview")>>
 			<</if>>
 		<</if>>
@@ -543,10 +543,10 @@
 		<<if ($cash > 20000)>>
 			<<set $recruit.push("school sale")>>
 		<</if>>
-		<<if ($HeadGirl.devotion > 50) && ($HeadGirl.skill.entertainment >= 100)>>
+		<<if _S.HeadGirl && (_S.HeadGirl.devotion > 50) && (_S.HeadGirl.skill.entertainment >= 100)>>
 			<<set $recruit.push("female recruit")>>
 		<</if>>
-		<<if ($HeadGirl.devotion > 50) && ($HeadGirl.skill.whoring >= 100)>>
+		<<if _S.HeadGirl && (_S.HeadGirl.devotion > 50) && (_S.HeadGirl.skill.whoring >= 100)>>
 			<<set $recruit.push("whore recruit")>>
 		<</if>>
 		<<if $PC.skill.hacking >= 50 && (random(0,100) < $PC.skill.hacking) || $cheatMode>>
@@ -601,11 +601,11 @@
 		<<if ($cash > 20000)>>
 			<<set $recruit.push("school trap")>>
 		<</if>>
-		<<if ($HeadGirl.devotion > 50) && ($HeadGirl.dick > 0)>>
+		<<if _S.HeadGirl && (_S.HeadGirl.devotion > 50) && (_S.HeadGirl.dick > 0)>>
 			<<set $recruit.push("male recruit")>>
 		<</if>>
 		<<if $seeDicks != 100>>
-			<<if ($HeadGirl.devotion > 50)>>
+			<<if (_S.HeadGirl && _S.HeadGirl.devotion > 50)>>
 				<<set $RecETSevent.push("mismatched pair")>>
 			<</if>>
 			<<if ($rep/400) > (random(1,100) || $cheatMode)>>
@@ -1042,6 +1042,15 @@
 				<<set $RESEndowment.push("TSS")>>
 			<</if>>
 		<</if>>
+		<<if $TUO.schoolUpgrade == 0>>
+			<<if ($TUO.studentsBought > 1) || ($TUO.schoolPresent != 0)>>
+				<<set $events.push("RES Endowment")>>
+				<<if $TUO.studentsBought > 2>>
+					<<set $events.push("RES Endowment")>>
+				<</if>>
+				<<set $RESEndowment.push("TUO")>>
+			<</if>>
+		<</if>>
 		<<if $GRI.schoolUpgrade == 0>>
 			<<if ($GRI.studentsBought > 1) || ($GRI.schoolPresent != 0)>>
 				<<set $events.push("RES Endowment")>>
@@ -1132,6 +1141,15 @@
 				<</if>>
 			<</if>>
 		<</if>>
+		<<if $TUO.schoolUpgrade != 0>>
+			<<if $TUO.schoolPresent == 0>>
+				<<if $TUO.schoolAnnexed == 0>>
+					<<set $events.push("RES Move")>>
+					<<set $events.push("RES Move")>>
+					<<set $RESMove.push("TUO")>>
+				<</if>>
+			<</if>>
+		<</if>>
 		<<if $GRI.schoolUpgrade != 0>>
 			<<if $GRI.schoolPresent == 0>>
 				<<if $GRI.schoolAnnexed == 0>>
@@ -1218,6 +1236,10 @@
 					<<set $events.push("RES Sale")>>
 					<<set $RESSale.push("TSS")>>
 				<</if>>
+				<<if $TUO.studentsBought == 0>>
+					<<set $events.push("RES Sale")>>
+					<<set $RESSale.push("TUO")>>
+				<</if>>
 				<<if $GRI.studentsBought == 0>>
 					<<set $events.push("RES Sale")>>
 					<<set $RESSale.push("GRI")>>
diff --git a/src/uncategorized/reAnalPunishment.tw b/src/uncategorized/reAnalPunishment.tw
index 0e6494fa54c21ccb26f0530171c8b21a8eddbd9a..36aff4000c744ec3b85af74f2360c5d77349420e 100644
--- a/src/uncategorized/reAnalPunishment.tw
+++ b/src/uncategorized/reAnalPunishment.tw
@@ -4,18 +4,18 @@
 
 <<set $activeSlave = $eventSlave>>
 <<setLocalPronouns $activeSlave>>
-<<setLocalPronouns $HeadGirl 2>>
+<<setLocalPronouns _S.HeadGirl 2>>
 
 <<run Enunciate($activeSlave)>>
 
 <<set _clothesTemp = $activeSlave.clothes, $activeSlave.clothes = "no clothing">>
-<<set _clothesTemp2 = $HeadGirl.clothes, $HeadGirl.clothes = "no clothing">>
+<<set _clothesTemp2 = _S.HeadGirl.clothes, _S.HeadGirl.clothes = "no clothing">>
 <span id="artFrame">
 /* 000-250-006 */
 <<if $seeImages == 1>>
 	<div class="imageColumn">
 		<div class="imageRef medImg">
-			<<= SlaveArt($HeadGirl, 2, 0)>>
+			<<= SlaveArt(_S.HeadGirl, 2, 0)>>
 		</div>
 		<div class="imageRef medImg">
 			<<= SlaveArt($activeSlave, 2, 0)>>
@@ -25,32 +25,31 @@
 /* 000-250-006 */
 </span>
 <<set $activeSlave.clothes = _clothesTemp>>
-<<set $HeadGirl.clothes = _clothesTemp2>>
+<<set _S.HeadGirl.clothes = _clothesTemp2>>
 
-As you're making the rounds through your penthouse, you hear $HeadGirl.slaveName speaking in the tones _he2 uses to castigate misbehaving slaves in the next room. When you appear in the doorway, you have little chance to survey the situation before <<= App.UI.slaveDescriptionDialog($activeSlave)>>, apparently the miscreant, flings $himself at your feet. $He clings to one of your legs convulsively, choking on tears as $he stares up at you and tries to muster an explanation. After two false starts, $he manages to start begging. "Plea<<s>>e, <<Master>>," $he wails miserably. "Plea<<s>>e don't let _him2 rape my butt."
+As you're making the rounds through your penthouse, you hear _S.HeadGirl.slaveName speaking in the tones _he2 uses to castigate misbehaving slaves in the next room. When you appear in the doorway, you have little chance to survey the situation before <<= App.UI.slaveDescriptionDialog($activeSlave)>>, apparently the miscreant, flings $himself at your feet. $He clings to one of your legs convulsively, choking on tears as $he stares up at you and tries to muster an explanation. After two false starts, $he manages to start begging. "Plea<<s>>e, <<Master>>," $he wails miserably. "Plea<<s>>e don't let _him2 rape my butt."
 <br><br>
-You shoot an amused glance at $HeadGirl.slaveName, who smiles back as _he2 explains the slave's minor sin and _his2 intention to sodomize the malefactor. _He2 does not bother to keep an edge of anticipation out of _his2 voice, and $activeSlave.slaveName cries harder and clings to you with renewed force as your Head Girl pronounces _his2 intention with cruel clarity.<<if $activeSlave.boobs > 4000>> The supplicant's breasts are so huge that $his embrace of your leg has completely surrounded it in deliciously heaving breastflesh.<<elseif $activeSlave.boobs > 1000>> The weight of the supplicant's breasts is quite noticeable as $his embrace of your leg presses them against it.<</if>> You look down at $activeSlave.slaveName. $He stares back with huge wet <<= App.Desc.eyesColor($activeSlave)>>, doing $his best to implore you with $his gaze, and scooting $his rear in towards your foot in an unconscious effort to protect it from the promised assrape. $He's quite authentically terrified; $his whole body is shaking.
+You shoot an amused glance at _S.HeadGirl.slaveName, who smiles back as _he2 explains the slave's minor sin and _his2 intention to sodomize the malefactor. _He2 does not bother to keep an edge of anticipation out of _his2 voice, and $activeSlave.slaveName cries harder and clings to you with renewed force as your Head Girl pronounces _his2 intention with cruel clarity.<<if $activeSlave.boobs > 4000>> The supplicant's breasts are so huge that $his embrace of your leg has completely surrounded it in deliciously heaving breastflesh.<<elseif $activeSlave.boobs > 1000>> The weight of the supplicant's breasts is quite noticeable as $his embrace of your leg presses them against it.<</if>> You look down at $activeSlave.slaveName. $He stares back with huge wet <<= App.Desc.eyesColor($activeSlave)>>, doing $his best to implore you with $his gaze, and scooting $his rear in towards your foot in an unconscious effort to protect it from the promised assrape. $He's quite authentically terrified; $his whole body is shaking.
 <br><br>
-$HeadGirl.slaveName is very much acting within _his2 duties, and $activeSlave.slaveName has now misbehaved twice by trying to go over your Head Girl's head by appealing to you. $HeadGirl.slaveName is ready to carry out the sentence: <<if canPenetrate($HeadGirl) && ($HeadGirl.dick > 2)>>_his2 cock is fully erect, and _he2's keeping it hard with one hand. _He2 slaps its head against _his2 other palm<<elseif $HeadGirl.dick > 0>>since _his2 dick isn't an appropriate instrument for inflicting anal pain, _he2's got an elephantine dildo ready. _He2 slaps it against _his2 palm<<else>>_He2's got an elephantine dildo ready, and _he2 slaps it against _his2 palm<</if>>, forcing a frightened moan from $activeSlave.slaveName.
+_S.HeadGirl.slaveName is very much acting within _his2 duties, and $activeSlave.slaveName has now misbehaved twice by trying to go over your Head Girl's head by appealing to you. _S.HeadGirl.slaveName is ready to carry out the sentence: <<if canPenetrate(_S.HeadGirl) && (_S.HeadGirl.dick > 2)>>_his2 cock is fully erect, and _he2's keeping it hard with one hand. _He2 slaps its head against _his2 other palm<<elseif _S.HeadGirl.dick > 0>>since _his2 dick isn't an appropriate instrument for inflicting anal pain, _he2's got an elephantine dildo ready. _He2 slaps it against _his2 palm<<else>>_He2's got an elephantine dildo ready, and _he2 slaps it against _his2 palm<</if>>, forcing a frightened moan from $activeSlave.slaveName.
 
 <br><br>
 
 <span id="result">
 <<link "Carry on">>
 	<<replace "#result">>
-	You ignore $activeSlave.slaveName — no small feat, since the poor <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> is clinging to your leg — and tell $HeadGirl.slaveName to carry on. Your Head Girl @@.mediumaquamarine;puffs up a bit with pride,@@ and orders the weeping slave to present $his anus. The <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> does not resist, but nor does $he comply. $HeadGirl.slaveName jabs a thumb into $activeSlave.slaveName's side, right above $his kidney, driving the wind out of the slave with a pained grunt. $He arches $his back involuntarily and $his grip on you loosens, and $HeadGirl.slaveName drags $him off you. _He2 jabs $him again, depriving _his2 victim of breath completely, and then takes _him2 by the ankle, dragging the slave across the floor with comic effect. The slave leaves a trail of tears across the flooring as $he vanishes into the room. As you continue making your rounds, you hear a drawn-out howl followed by rhythmic screaming.
+	You ignore $activeSlave.slaveName — no small feat, since the poor <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> is clinging to your leg — and tell _S.HeadGirl.slaveName to carry on. Your Head Girl @@.mediumaquamarine;puffs up a bit with pride,@@ and orders the weeping slave to present $his anus. The <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> does not resist, but nor does $he comply. _S.HeadGirl.slaveName jabs a thumb into $activeSlave.slaveName's side, right above $his kidney, driving the wind out of the slave with a pained grunt. $He arches $his back involuntarily and $his grip on you loosens, and _S.HeadGirl.slaveName drags $him off you. _He2 jabs $him again, depriving _his2 victim of breath completely, and then takes _him2 by the ankle, dragging the slave across the floor with comic effect. The slave leaves a trail of tears across the flooring as $he vanishes into the room. As you continue making your rounds, you hear a drawn-out howl followed by rhythmic screaming.
 	<<set $activeSlave.counter.anal += 1>>
 	<<set $analTotal += 1>>
-	<<set $HeadGirl.trust += 4, $HeadGirl.counter.penetrative += 1>>
+	<<set _S.HeadGirl.trust += 4, _S.HeadGirl.counter.penetrative += 1>>
 	<<set $penetrativeTotal += 1>>
-	<<set $slaves[$slaveIndices[$HeadGirl.ID]] = $HeadGirl>>
 	<</replace>>
 <</link>>
 <br><<link "Take part">>
 	<<replace "#result">>
-	You explain $activeSlave.slaveName's double crime to $him, and tell $HeadGirl.slaveName to get started. Your Head Girl orders the weeping slave to present $his anus. The <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> does not resist, but nor does $he comply. $HeadGirl.slaveName jabs a thumb into $activeSlave.slaveName's side, right above $his kidney, driving the wind out of the slave with a pained grunt. $He arches $his back involuntarily and $his grip on you loosens, so $HeadGirl.slaveName drags $him off you, telling $activeSlave.slaveName that it'll hurt less if $he cooperates and assumes the proper position. $He doesn't, so $HeadGirl.slaveName assfucks $him lying flat on the floor, with the poor $girl sobbing loudly as $HeadGirl.slaveName <<if canPenetrate($HeadGirl) && ($HeadGirl.dick > 2)>>pistons _his2 cock in and out of $his rectum<<else>>rams the massive dildo up $his butt<</if>>.
+	You explain $activeSlave.slaveName's double crime to $him, and tell _S.HeadGirl.slaveName to get started. Your Head Girl orders the weeping slave to present $his anus. The <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>> does not resist, but nor does $he comply. _S.HeadGirl.slaveName jabs a thumb into $activeSlave.slaveName's side, right above $his kidney, driving the wind out of the slave with a pained grunt. $He arches $his back involuntarily and $his grip on you loosens, so _S.HeadGirl.slaveName drags $him off you, telling $activeSlave.slaveName that it'll hurt less if $he cooperates and assumes the proper position. $He doesn't, so _S.HeadGirl.slaveName assfucks $him lying flat on the floor, with the poor $girl sobbing loudly as _S.HeadGirl.slaveName <<if canPenetrate(_S.HeadGirl) && (_S.HeadGirl.dick > 2)>>pistons _his2 cock in and out of $his rectum<<else>>rams the massive dildo up $his butt<</if>>.
 	<br><br>
-	After enjoying the spectacle for a while, you judge that the slave's sphincter is loose enough and tell $HeadGirl.slaveName to flip the bitch over. @@.hotpink;_He2 obeys, chuckling,@@ sitting _himself2 down and hauling the reluctant slave onto _his2 lap by seizing a nipple and pulling it into position so the agonized slave is forced to follow. <<if canPenetrate($HeadGirl) && ($HeadGirl.dick > 2)>>$HeadGirl.slaveName reinserts _his2 dick, <<else>>$HeadGirl.slaveName maneuvers the dildo down over _his2 own crotch, approximating the position of a natural cock and using its base to stimulate _himself2. _He2 reinserts it,<</if>> intentionally missing twice to keep the experience unpleasant despite _his2 victim's well-fucked backdoor.
+	After enjoying the spectacle for a while, you judge that the slave's sphincter is loose enough and tell _S.HeadGirl.slaveName to flip the bitch over. @@.hotpink;_He2 obeys, chuckling,@@ sitting _himself2 down and hauling the reluctant slave onto _his2 lap by seizing a nipple and pulling it into position so the agonized slave is forced to follow. <<if canPenetrate(_S.HeadGirl) && (_S.HeadGirl.dick > 2)>>_S.HeadGirl.slaveName reinserts _his2 dick, <<else>>_S.HeadGirl.slaveName maneuvers the dildo down over _his2 own crotch, approximating the position of a natural cock and using its base to stimulate _himself2. _He2 reinserts it,<</if>> intentionally missing twice to keep the experience unpleasant despite _his2 victim's well-fucked backdoor.
 	<br><br>
 	$activeSlave.slaveName, now facing upward rather than having $his face ground into the floor, notices for the first time that <<if $PC.dick != 0>>you've got your dick out and hard<<else>>you've donned one of your punishment-sized strap-ons<</if>>. $His <<= App.Desc.eyesColor($activeSlave)>> @@.gold;fly open with horror@@ as you kneel down and smack its head against $his <<if $activeSlave.vagina > -1>>poor pussy<<else>>stretched taint<</if>>, but $he doesn't realize how comprehensively fucked $he is until you press it against the top of $his already-stretched anal sphincter. "Plea<<s>>e no, <<Master>>! It won't fit! Plea<<s>>e <<if $activeSlave.vagina > 0>>put it in my pu<<ss>>y<<else>>let me <<s>>uck it<</if>> in<<s>>tead," $he begs desperately. "I p-promi<<s>>e I'll be a g-good
 	<<if $girl == "girl">>
@@ -65,16 +64,15 @@ $HeadGirl.slaveName is very much acting within _his2 duties, and $activeSlave.sl
 	$he howls. $He gasps for air, tears streaming down $his $activeSlave.skin cheeks, and then continues: "AAAH! FUCK! TAKE IT OUUUT! N-NOOO, PLEA<<S>>E DON'T THRU<<S>>T — AAAH! AAAH! AAAH!"
 	<<set $activeSlave.trust -= 5, $activeSlave.counter.anal += 1>>
 	<<set $analTotal += 1>>
-	<<set $HeadGirl.devotion += 4, $HeadGirl.counter.penetrative += 1>>
+	<<set _S.HeadGirl.devotion += 4, _S.HeadGirl.counter.penetrative += 1>>
 	<<set $penetrativeTotal += 1>>
-	<<set $slaves[$slaveIndices[$HeadGirl.ID]] = $HeadGirl>>
 	<</replace>>
 <</link>>
 <br><<link "Take pity">>
 	<<replace "#result">>
-	You tell $HeadGirl.slaveName you've decided to be merciful, just this once. $activeSlave.slaveName holds your leg even harder, @@.mediumaquamarine;sobbing $his thanks@@ over and over until you reach down, pat $his head, and tell $him it will be all right, calming the hysterical <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>>. $HeadGirl.slaveName, meanwhile, stammers an apology. _He2 hurries about _his2 business, @@.gold;badly puzzled@@ and more than a little shaken. _He2 thought _he2 had the authority to anally rape misbehaving slaves, but _he2's no longer so sure of _his2 rights and responsibilities.
+	You tell _S.HeadGirl.slaveName you've decided to be merciful, just this once. $activeSlave.slaveName holds your leg even harder, @@.mediumaquamarine;sobbing $his thanks@@ over and over until you reach down, pat $his head, and tell $him it will be all right, calming the hysterical <<if $activeSlave.physicalAge > 30>>$woman<<else>>$girl<</if>>. _S.HeadGirl.slaveName, meanwhile, stammers an apology. _He2 hurries about _his2 business, @@.gold;badly puzzled@@ and more than a little shaken. _He2 thought _he2 had the authority to anally rape misbehaving slaves, but _he2's no longer so sure of _his2 rights and responsibilities.
 	<<set $activeSlave.trust += 4>>
-	<<set $slaves[$slaveIndices[$HeadGirl.ID]].trust -= 15>>
+	<<set _S.HeadGirl.trust -= 15>>
 	<</replace>>
 <</link>>
 </span>
diff --git a/src/uncategorized/reBusyMasterSuite.tw b/src/uncategorized/reBusyMasterSuite.tw
index 6d3a70940bc0d2fbde3b54c45bc672deab284b17..03185e81da49bf4f9e9395284172b3a3a3bc56a2 100644
--- a/src/uncategorized/reBusyMasterSuite.tw
+++ b/src/uncategorized/reBusyMasterSuite.tw
@@ -25,10 +25,10 @@
 	<<goto "RIE Eligibility Check">>
 <<else>>
 
-<<setLocalPronouns $Concubine>>
+<<setLocalPronouns _S.Concubine>>
 <<setLocalPronouns _bottomSlave 2>>
 
-<p>You have an extended meeting with a prominent citizen planned, from the start of business in the morning until you're done. That's likely to be in the late evening, since he's probably going to get into technical business proposals, and $Concubine.slaveName knows it. $He is surprised, therefore, when a minor business emergency calls your would-be interlocutor away, canceling the meeting and sending you home hours earlier than you'd planned. $He <<if canTalk($Concubine)>>giggles helplessly<<else>>signs humorously<</if>> at the surprise when you walk into your suite. Apparently, $he decided to while away the hours until you got back by having some truly grandiose group sex with all the slaves you have in the suite.</p>
+<p>You have an extended meeting with a prominent citizen planned, from the start of business in the morning until you're done. That's likely to be in the late evening, since he's probably going to get into technical business proposals, and _S.Concubine.slaveName knows it. $He is surprised, therefore, when a minor business emergency calls your would-be interlocutor away, canceling the meeting and sending you home hours earlier than you'd planned. $He <<if canTalk(_S.Concubine)>>giggles helplessly<<else>>signs humorously<</if>> at the surprise when you walk into your suite. Apparently, $he decided to while away the hours until you got back by having some truly grandiose group sex with all the slaves you have in the suite.</p>
 
 <p>$He had to turn to greet you as you entered, since $he was facing away from the entry, and the reason why is rather obvious. Up near the opposite wall, _bottomSlave.slaveName is on the floor with _his2 face down and _his2 ass up.
 
@@ -36,7 +36,7 @@
 <<print _msSlaves.filter((s) => s.mode !== "none").reduce((acc, cur, i, arr) => {
 	let r = ``;
 	const {mode, slave} = cur;
-	const nextSlave = (i+1 >= arr.length) ? V.Concubine : arr[i+1].slave;
+	const nextSlave = (i+1 >= arr.length) ? S.Concubine : arr[i+1].slave;
 	let hole = () => {
 		if (mode === "vaginal") {
 			if (slave.vagina > 2) {
@@ -116,7 +116,7 @@
 <<set _nonparticipants = _msSlaves.filter((s) => s.mode === "none").map((s) => s.slave)>>
 <<if _nonparticipants.length > 0>>
 	<p>
-		<<print _nonparticipants.map((s) => s.slaveName).reduce((res, ch, i, arr) => res + (i === arr.length - 1 ? ' and ' : ', ') + ch)>> can't participate in the train, so $Concubine.slaveName has them busy lying under the slaves who are, offering what oral stimulation they can manage.
+		<<print _nonparticipants.map((s) => s.slaveName).reduce((res, ch, i, arr) => res + (i === arr.length - 1 ? ' and ' : ', ') + ch)>> can't participate in the train, so _S.Concubine.slaveName has them busy lying under the slaves who are, offering what oral stimulation they can manage.
 	</p>
 	<<run _nonparticipants.forEach((s) => actX(s, "oral"))>>
 <</if>>
@@ -124,35 +124,35 @@
 <<set _top = _msSlaves.slice().reverse().find((s) => s.mode !== "none")>>
 
 <<set _concubineMode = "none">>
-<<if canDoAnal($Concubine) && $Concubine.anus > 0>>
+<<if canDoAnal(_S.Concubine) && _S.Concubine.anus > 0>>
 	<<set _concubineMode = "anal">>
-	<<if ($Concubine.anus > 2)>><<set _concubineHole = "loose anus">><<elseif ($Concubine.anus > 1)>><<set _concubineHole = "asshole">><<else>><<set _concubineHole = "tight little asshole">><</if>>
-<<elseif canDoVaginal($Concubine) && $Concubine.vagina > 0>>
+	<<if (_S.Concubine.anus > 2)>><<set _concubineHole = "loose anus">><<elseif (_S.Concubine.anus > 1)>><<set _concubineHole = "asshole">><<else>><<set _concubineHole = "tight little asshole">><</if>>
+<<elseif canDoVaginal(_S.Concubine) && _S.Concubine.vagina > 0>>
 	<<set _concubineMode = "vaginal">>
-	<<if ($Concubine.vagina > 2)>><<set _concubineHole = "loose pussy">><<elseif ($Concubine.vagina > 1)>><<set _concubineHole = "pussy">><<else>><<set _concubineHole = "tight little pussy">><</if>>
+	<<if (_S.Concubine.vagina > 2)>><<set _concubineHole = "loose pussy">><<elseif (_S.Concubine.vagina > 1)>><<set _concubineHole = "pussy">><<else>><<set _concubineHole = "tight little pussy">><</if>>
 <</if>>
 <span id="result">
 <<if _concubineMode !== "none">>
 	<<link "Slide in behind the concubine for some action">>
 		<<replace "#result">>
 		<<setLocalPronouns _top.slave 2>>
-		$Concubine.slaveName anticipates you, and is already sliding $himself partway out of _top.slave.slaveName and cocking $his hips to spread $his
+		_S.Concubine.slaveName anticipates you, and is already sliding $himself partway out of _top.slave.slaveName and cocking $his hips to spread $his
 		<<if $activeSlave.butt > 15>>
 			immeasurable
 		<<elseif $activeSlave.butt > 10>>
 			expansive
 		<<elseif $activeSlave.butt > 7>>
 			enormous
-		<<elseif ($Concubine.butt > 5)>>
+		<<elseif (_S.Concubine.butt > 5)>>
 			huge
-		<<elseif ($Concubine.butt > 2)>>
+		<<elseif (_S.Concubine.butt > 2)>>
 			healthy
 		<<else>>
 			trim
 		<</if>>
-		buttocks as wide as $he can without disentangling $himself from the sex train. Up on the bed $he's at just the right height, and $he winks $his _concubineHole invitingly<<if canTalk($Concubine)>>, laughing at the sheer decadence of it<</if>>. <<if ($PC.dick == 0)>>You pull on a strap-on and push it<<else>>You push yourself<</if>> home with some force, your concubine's extreme state of arousal leaving $his ass very relaxed and welcoming; the thrust shoves $him forward to hilt $himself in _top.slave.slaveName, and so on down the line, producing more giggling, some squealing, and much scrabbling for balance. It takes a while to find the rhythm, and while you wait for the inevitable tangles to be fixed you decide to challenge yourself. You reach around and <<if ($Concubine.boobs > 10000)>>sink your hands into $Concubine.slaveName's massive boobs<<elseif ($Concubine.boobs > 1000)>>heft $Concubine.slaveName's heavy boobs<<elseif ($Concubine.boobs > 300)>>tease $Concubine.slaveName's healthy breasts<<else>>massage $Concubine.slaveName's flat chest<</if>>, nibbling $his $Concubine.skin neck, and generally torturing $him with stimulation until $he climaxes to $his beloved <<= WrittenMaster($Concubine)>>. When $he does, you extract yourself and pull $him unceremoniously off _top.slave.slaveName, replacing $him in _top.slave.slaveName's <<if _top.mode === "anal">>butt<<else>>pussy<</if>>. You work your way down the line, orgasm by orgasm, delaying your own climax until the exhausted _bottomSlave.slaveName manages yet another orgasm by heroic efforts, and you're done. As you roll off _him2, panting, there is scattered applause and much congratulation from your harem of @@.mediumaquamarine;trusting slaves.@@
-		<<set getSlave($Concubine.ID).trust += 5>>
-		<<run seX($PC, "penetrative", getSlave($Concubine.ID), _concubineMode)>>
+		buttocks as wide as $he can without disentangling $himself from the sex train. Up on the bed $he's at just the right height, and $he winks $his _concubineHole invitingly<<if canTalk(_S.Concubine)>>, laughing at the sheer decadence of it<</if>>. <<if ($PC.dick == 0)>>You pull on a strap-on and push it<<else>>You push yourself<</if>> home with some force, your concubine's extreme state of arousal leaving $his ass very relaxed and welcoming; the thrust shoves $him forward to hilt $himself in _top.slave.slaveName, and so on down the line, producing more giggling, some squealing, and much scrabbling for balance. It takes a while to find the rhythm, and while you wait for the inevitable tangles to be fixed you decide to challenge yourself. You reach around and <<if (_S.Concubine.boobs > 10000)>>sink your hands into _S.Concubine.slaveName's massive boobs<<elseif (_S.Concubine.boobs > 1000)>>heft _S.Concubine.slaveName's heavy boobs<<elseif (_S.Concubine.boobs > 300)>>tease _S.Concubine.slaveName's healthy breasts<<else>>massage _S.Concubine.slaveName's flat chest<</if>>, nibbling $his _S.Concubine.skin neck, and generally torturing $him with stimulation until $he climaxes to $his beloved <<= WrittenMaster(_S.Concubine)>>. When $he does, you extract yourself and pull $him unceremoniously off _top.slave.slaveName, replacing $him in _top.slave.slaveName's <<if _top.mode === "anal">>butt<<else>>pussy<</if>>. You work your way down the line, orgasm by orgasm, delaying your own climax until the exhausted _bottomSlave.slaveName manages yet another orgasm by heroic efforts, and you're done. As you roll off _him2, panting, there is scattered applause and much congratulation from your harem of @@.mediumaquamarine;trusting slaves.@@
+		<<set _S.Concubine.trust += 5>>
+		<<run seX($PC, "penetrative", _S.Concubine, _concubineMode)>>
 		<<run _msSlaves.forEach((s) => {
 			s.slave.trust += 1;
 			seX($PC, "penetrative", s.slave, s.mode);
@@ -176,23 +176,23 @@
 	<<else>>
 		lips
 	<</if>>
-	around your <<if ($PC.dick == 0)>>clit<<else>>cock<<if $PC.vagina != -1>> and starts stroking your pussy<</if>><</if>> eagerly enough, even as _msSlaves[1].slave.slaveName goes back to fucking _him2. The sex train is fairly gentle, since anything too fast would disintegrate the gymnastic arrangement, but _bottomSlave.slaveName is still getting enough stimulation that _he2 whimpers quietly into your <<if ($PC.vagina != -1)>>pussy<<else>>dick<</if>>, a nice feeling. The blowjob is <<if (_bottomSlave.skill.oral >= 100)>>masterful, despite the distraction<<elseif (_bottomSlave.skill.oral > 10)>>serviceable, despite the distraction<<else>>only mediocre, but serviceable enough<</if>>, so you let _him2 work for a while before gently shoving _him2 off the side of the bed and telling _him2 to get to the back of the line. The slaves all shuffle forward awkwardly, and inadvertently block your view so that you hear rather than see _bottomSlave.slaveName start groping your concubine $Concubine.slaveName's
+	around your <<if ($PC.dick == 0)>>clit<<else>>cock<<if $PC.vagina != -1>> and starts stroking your pussy<</if>><</if>> eagerly enough, even as _msSlaves[1].slave.slaveName goes back to fucking _him2. The sex train is fairly gentle, since anything too fast would disintegrate the gymnastic arrangement, but _bottomSlave.slaveName is still getting enough stimulation that _he2 whimpers quietly into your <<if ($PC.vagina != -1)>>pussy<<else>>dick<</if>>, a nice feeling. The blowjob is <<if (_bottomSlave.skill.oral >= 100)>>masterful, despite the distraction<<elseif (_bottomSlave.skill.oral > 10)>>serviceable, despite the distraction<<else>>only mediocre, but serviceable enough<</if>>, so you let _him2 work for a while before gently shoving _him2 off the side of the bed and telling _him2 to get to the back of the line. The slaves all shuffle forward awkwardly, and inadvertently block your view so that you hear rather than see _bottomSlave.slaveName start groping your concubine _S.Concubine.slaveName's
 	<<if $activeSlave.butt > 15>>
 		immeasurable
 	<<elseif $activeSlave.butt > 10>>
 		expansive
 	<<elseif $activeSlave.butt > 7>>
 		enormous
-	<<elseif ($Concubine.butt > 5)>>
+	<<elseif (_S.Concubine.butt > 5)>>
 		huge
-	<<elseif ($Concubine.butt > 2)>>
+	<<elseif (_S.Concubine.butt > 2)>>
 		healthy
 	<<else>>
 		trim
 	<</if>>
 	ass down near the foot of the bed. You climax, on occasion, but are enjoying yourself so immensely that you let the slaves continue the rotation until you're entirely spent, and they're entirely exhausted. You reach for a tablet to get some work done, in the center of a pile of sweaty, tired slaves, all of whom are resting with at least one body part in contact with their @@.hotpink;beloved@@ <<= properMaster()>>.
-	<<set getSlave($Concubine.ID).devotion += 5>>
-	<<run seX($PC, "penetrative", getSlave($Concubine.ID), "oral")>>
+	<<set _S.Concubine.devotion += 5>>
+	<<run seX($PC, "penetrative", _S.Concubine, "oral")>>
 	<<run _msSlaves.forEach((s) => {
 		s.slave.devotion += 1;
 		seX($PC, "penetrative", s.slave, "oral", 2);
diff --git a/src/uncategorized/reCitizenHookup.tw b/src/uncategorized/reCitizenHookup.tw
index b710e363344bc58cf66915c40fda9a7301ac01da..5d8dd99b878b2f75f5e6fadc558eda31304438f5 100644
--- a/src/uncategorized/reCitizenHookup.tw
+++ b/src/uncategorized/reCitizenHookup.tw
@@ -235,8 +235,8 @@ She's clearly attracted to you; even the most consummate actress would have diff
 <br><<link "To them that hath, it shall be given">>
 	<<replace "#result">>
 	You're not exactly starved for casual sex, but you've never thought there was any such thing as too much of a good thing. You place a <<if $PC.title == 1>>masculine<<else>>feminine<</if>> hand against the small of her back, feeling the warmth of her through the material of her evening wear. You hear a slight gasp from her as she realizes that her gambit has succeeded with more immediate effect than she expected. She shivers with anticipation as you steer her back through a side door, making a discreet exit towards your private suite.
-	<<if $Concubine != 0 && $Concubine.intelligence+$Concubine.intelligenceImplant > 50>>
-		$Concubine.slaveName is there, of course, and she instantly sees that her continued presence for a ménage à trois is wanted by both you and your guest.
+	<<if _S.Concubine && _S.Concubine.intelligence+_S.Concubine.intelligenceImplant > 50>>
+		_S.Concubine.slaveName is there, of course, and she instantly sees that her continued presence for a ménage à trois is wanted by both you and your guest.
 	<</if>>
 	Your guest restrains her eager praise now that you're in private, but her wide-eyed appreciation of your domain is compliment enough. Once in your suite, she strips, revealing
 	<<switch _FS>>
@@ -311,9 +311,9 @@ She's clearly attracted to you; even the most consummate actress would have diff
 		a hot young body.
 	<</switch>>
 	Citizens like her often appreciate a good hard fuck, since regular submission to a pounding from sex slaves would be a scandal. There's little opprobrium waiting for you if it's known you had her, though, and she's deliciously eager as you press her down onto the bed<<if $PC.dick == 0>> and pull on your usual harness<</if>>. She ruts herself hard back against you as you thrust into her, moaning, and after a short while she begs for it even harder, so you flip her over and mount her like a bitch, making her scream.
-	<<if $Concubine != 0 && $Concubine.intelligence > 1>>
-		<<setLocalPronouns $Concubine>>
-		The view of your conquest's rutting back is nice, of course, but after enjoying it for a few moments you pull $Concubine.slaveName in and kiss your favorite deeply, playing with $him as you fuck.
+	<<if _S.Concubine && _S.Concubine.intelligence > 1>>
+		<<setLocalPronouns _S.Concubine>>
+		The view of your conquest's rutting back is nice, of course, but after enjoying it for a few moments you pull _S.Concubine.slaveName in and kiss your favorite deeply, playing with $him as you fuck.
 	<</if>>
 	When your guest is finally spent, she showers, dresses, and leaves discreetly, offering you a proper thank you. This is the kind of thing that @@.green;builds a lasting reputation@@ in the Free Cities.
 	<<run repX(5000, "event")>>
diff --git a/src/uncategorized/reHGReplacement.tw b/src/uncategorized/reHGReplacement.tw
index c4be84321a12b2401f1591b9a35ae98a4ea84747..dc36a1781077a82e0318506c935b86a4ad520550 100644
--- a/src/uncategorized/reHGReplacement.tw
+++ b/src/uncategorized/reHGReplacement.tw
@@ -3,11 +3,11 @@
 <<set $nextButton = "Continue", $nextLink = "AS Dump", $returnTo = "Next Week", $activeSlave = $eventSlave>>
 <<run Enunciate($activeSlave)>>
 <<setLocalPronouns $activeSlave>>
-<<setLocalPronouns $HeadGirl 2>>
-<<setSpokenLocalPronouns $activeSlave $HeadGirl>>
+<<setLocalPronouns _S.HeadGirl 2>>
+<<setSpokenLocalPronouns $activeSlave _S.HeadGirl>>
 
 <<set _clothesTemp = $activeSlave.clothes, $activeSlave.clothes = "no clothing">>
-<<set _clothesTemp2 = $HeadGirl.clothes>>
+<<set _clothesTemp2 = _S.HeadGirl.clothes>>
 <span id="artFrame">
 /* 000-250-006 */
 <<if $seeImages == 1>>
@@ -16,21 +16,21 @@
 			<<= SlaveArt($activeSlave, 2, 0)>>
 		</div>
 		<div class="imageRef medImg">
-			<<= SlaveArt($HeadGirl, 2, 0)>>
+			<<= SlaveArt(_S.HeadGirl, 2, 0)>>
 		</div>
 	</div>
 <</if>>
 /* 000-250-006 */
 </span>
 <<set $activeSlave.clothes = _clothesTemp>>
-<<set $HeadGirl.clothes = _clothesTemp2>>
+<<set _S.HeadGirl.clothes = _clothesTemp2>>
 
-There's a constant traffic of slaves in and out of your office as your chattel comes in and out for instructions, inspections, and sex. Your Head Girl $HeadGirl.slaveName is one of the most frequent visitors, since although you trust _him2, _he2's still a slave and has to check with you before acting on some matters.<<if $HeadGirl.relationship == -3>> (_He2's also your _wife2, making _his2 visits a pleasant diversion.)<<elseif $arcologies[0].FSEgyptianRevivalistLaw == 1>> (_He2's also your Consort, making _his2 visits a pleasant diversion.)<</if>> During one of _his2 visits, <<= App.UI.slaveDescriptionDialog($activeSlave)>> happens to come in for $his scheduled inspection. $He perches politely on the couch, waiting $his turn like a good $girl. $His <<= App.Desc.eyesColor($activeSlave)>> are watchful, however, and $he seems to be paying very close attention to your conversation with your Head Girl.
+There's a constant traffic of slaves in and out of your office as your chattel comes in and out for instructions, inspections, and sex. Your Head Girl _S.HeadGirl.slaveName is one of the most frequent visitors, since although you trust _him2, _he2's still a slave and has to check with you before acting on some matters.<<if _S.HeadGirl.relationship == -3>> (_He2's also your _wife2, making _his2 visits a pleasant diversion.)<<elseif $arcologies[0].FSEgyptianRevivalistLaw == 1>> (_He2's also your Consort, making _his2 visits a pleasant diversion.)<</if>> During one of _his2 visits, <<= App.UI.slaveDescriptionDialog($activeSlave)>> happens to come in for $his scheduled inspection. $He perches politely on the couch, waiting $his turn like a good $girl. $His <<= App.Desc.eyesColor($activeSlave)>> are watchful, however, and $he seems to be paying very close attention to your conversation with your Head Girl.
 
-When $HeadGirl.slaveName leaves, $he clears $his throat nervously. $He gathers $his courage, and <<say>>s, "<<Master>>, I think I would make a better Head Girl than $him." $He takes a deep breath and plunges on. "I'm ju<<s>>t a<<s>> <<s>>mart a<<s>> <<he 2>> i<<s>>. And, <<Master>>, I'm <<s>>ure you've noti<<c>>ed <<he 2>> i<<s>>n't very good at teaching other girl<<s>> vaginal <<s>>kill<<s>>.
-<<if $HeadGirl.vagina > 0>>
+When _S.HeadGirl.slaveName leaves, $he clears $his throat nervously. $He gathers $his courage, and <<say>>s, "<<Master>>, I think I would make a better Head Girl than $him." $He takes a deep breath and plunges on. "I'm ju<<s>>t a<<s>> <<s>>mart a<<s>> <<he 2>> i<<s>>. And, <<Master>>, I'm <<s>>ure you've noti<<c>>ed <<he 2>> i<<s>>n't very good at teaching other girl<<s>> vaginal <<s>>kill<<s>>.
+<<if _S.HeadGirl.vagina > 0>>
 	I could do it much better than <<he 2>> doe<<s>>,
-<<elseif $HeadGirl.vagina == 0>>
+<<elseif _S.HeadGirl.vagina == 0>>
 	How could <<he 2>> be? <<He 2>>'<<s>> a virgin! I can manage it,
 <<else>>
 	How could <<he 2>> be? <<He 2>> doe<<s>>n't have a pu<<ss>>y! I can manage it,
@@ -42,7 +42,7 @@ When $HeadGirl.slaveName leaves, $he clears $his throat nervously. $He gathers $
 <span id="result">
 	<<link "$He's right">>
 		<<replace "#result">>
-			After giving the matter some consideration, you agree, instructing $assistant.name that $activeSlave.slaveName is now the Head Girl. $activeSlave.slaveName is @@.hotpink;overjoyed,@@ and bounces to $his feet, clearly very eager to begin. You caution $him that $he is not to undermine $HeadGirl.slaveName by letting anyone know why you decided to change Head Girls. $He nods obediently. As far as $HeadGirl.slaveName knows, it's just a standard change of Head Girls, and _he2's too good a slave to let it affect _him2 seriously.
+			After giving the matter some consideration, you agree, instructing $assistant.name that $activeSlave.slaveName is now the Head Girl. $activeSlave.slaveName is @@.hotpink;overjoyed,@@ and bounces to $his feet, clearly very eager to begin. You caution $him that $he is not to undermine _S.HeadGirl.slaveName by letting anyone know why you decided to change Head Girls. $He nods obediently. As far as _S.HeadGirl.slaveName knows, it's just a standard change of Head Girls, and _he2's too good a slave to let it affect _him2 seriously.
 			<<set $activeSlave.devotion += 5>>
 			<<= assignJob($activeSlave, "be your Head Girl")>>
 		<</replace>>
@@ -55,14 +55,13 @@ When $HeadGirl.slaveName leaves, $he clears $his throat nervously. $He gathers $
 	<</link>>
 	<br><<link "Let your Head Girl sort this out however _he2 sees fit">>
 		<<replace "#result">>
-			<<run Enunciate($HeadGirl)>>
-			You page $HeadGirl.slaveName, telling _him2 to get back up to your office. $activeSlave.slaveName hears you, of course, and stiffens with fear. $He knows $he's in trouble, but $he doesn't yet know how much. You tell $HeadGirl.slaveName to have a seat next to $activeSlave.slaveName on the couch, and instruct $assistant.name to replay the last five minutes of the feeds from your office on a wallscreen. $activeSlave.slaveName's<<if $activeSlave.lips > 40>> pillowlike<</if>> lower lip begins to tremble at this, and $he can barely manage to keep $his gaze on the fatal replay of $his attempt to convince you to replace $HeadGirl.slaveName with $him. $HeadGirl.slaveName, who is no fool, is not surprised by this, but the replay still angers _him2. As it rolls on, _his2 hands begin to roam across $activeSlave.slaveName, who winces at _his2 touch. About halfway through, $HeadGirl.slaveName looks straight at you and cruelly pinches one of $activeSlave.slaveName's nipples. You respond to the nonverbal question with a silent nod, and $HeadGirl.slaveName smiles briefly at you. Then, _he2 sticks _his2 tongue in $activeSlave.slaveName's ear. The poor slave starts at the warm, wet intrusion, but knows better than to try to resist. $He closes $his eyes tight.
+			<<run Enunciate(_S.HeadGirl)>>
+			You page _S.HeadGirl.slaveName, telling _him2 to get back up to your office. $activeSlave.slaveName hears you, of course, and stiffens with fear. $He knows $he's in trouble, but $he doesn't yet know how much. You tell _S.HeadGirl.slaveName to have a seat next to $activeSlave.slaveName on the couch, and instruct $assistant.name to replay the last five minutes of the feeds from your office on a wallscreen. $activeSlave.slaveName's<<if $activeSlave.lips > 40>> pillowlike<</if>> lower lip begins to tremble at this, and $he can barely manage to keep $his gaze on the fatal replay of $his attempt to convince you to replace _S.HeadGirl.slaveName with $him. _S.HeadGirl.slaveName, who is no fool, is not surprised by this, but the replay still angers _him2. As it rolls on, _his2 hands begin to roam across $activeSlave.slaveName, who winces at _his2 touch. About halfway through, _S.HeadGirl.slaveName looks straight at you and cruelly pinches one of $activeSlave.slaveName's nipples. You respond to the nonverbal question with a silent nod, and _S.HeadGirl.slaveName smiles briefly at you. Then, _he2 sticks _his2 tongue in $activeSlave.slaveName's ear. The poor slave starts at the warm, wet intrusion, but knows better than to try to resist. $He closes $his eyes tight.
 			<br><br>
-			$HeadGirl.slaveName hops off the couch, gets down in front of _his2 victim, and shoves $his legs apart. $activeSlave.slaveName scrunches up $his face, expecting something agonizing to happen to $his <<if $activeSlave.dick == 0>>pussy, but $HeadGirl.slaveName shocks $him by starting to hungrily eat $him out.<<else>>cock, but $HeadGirl.slaveName shocks $him by giving $him a lush blowjob.<</if>> $activeSlave.slaveName clearly expects a trap, but $HeadGirl.slaveName's attentions slowly distract $him. When $he's on the very verge of orgasm, your Head Girl suddenly stops and <<if $activeSlave.dick == 0>>pinches the $girl's pussylips,<<else>>tugs the $girl's cock downward by its head,<</if>> not painfully, but hard enough to communicate threat. $activeSlave.slaveName, shocked out of $his pleasure, looks down at _him2. $HeadGirl.slaveName <<say>>s menacingly, "Bitch, if you orga<<s>>m, I'm going to fucking de<<s>>troy you." Then _he2 goes back to giving $activeSlave.slaveName oral. $activeSlave.slaveName begins to cry, realizing how comprehensively fucked $he is.
+			_S.HeadGirl.slaveName hops off the couch, gets down in front of _his2 victim, and shoves $his legs apart. $activeSlave.slaveName scrunches up $his face, expecting something agonizing to happen to $his <<if $activeSlave.dick == 0>>pussy, but _S.HeadGirl.slaveName shocks $him by starting to hungrily eat $him out.<<else>>cock, but _S.HeadGirl.slaveName shocks $him by giving $him a lush blowjob.<</if>> $activeSlave.slaveName clearly expects a trap, but _S.HeadGirl.slaveName's attentions slowly distract $him. When $he's on the very verge of orgasm, your Head Girl suddenly stops and <<if $activeSlave.dick == 0>>pinches the $girl's pussylips,<<else>>tugs the $girl's cock downward by its head,<</if>> not painfully, but hard enough to communicate threat. $activeSlave.slaveName, shocked out of $his pleasure, looks down at _him2. _S.HeadGirl.slaveName <<say>>s menacingly, "Bitch, if you orga<<s>>m, I'm going to fucking de<<s>>troy you." Then _he2 goes back to giving $activeSlave.slaveName oral. $activeSlave.slaveName begins to cry, realizing how comprehensively fucked $he is.
 			<br><br>
-			Eventually, $he climaxes. $HeadGirl.slaveName has a talented tongue. $HeadGirl.slaveName does not begrudge the slave $his pleasure, <<if $activeSlave.dick == 0>>tonguing $his clit all the way through $his orgasm.<<else>>industriously sucking $his cock until _he2's swallowed every last drop of cum.<</if>> _He2 stands up and seizes $activeSlave.slaveName by the ear, <<say>>ing, "And now, <<s>>lut, you are fucked." $activeSlave.slaveName's weeping stopped during $his climax, but now $he starts to @@.gold;sob with terror,@@ and $he cries harder as your Head Girl hauls $him out of your office by $his ear. $HeadGirl.slaveName @@.mediumaquamarine;waves cheerfully@@ to you as _he2 leaves with _his2 victim.
-			<<set $i = $slaveIndices[$HeadGirl.ID]>>
-			<<set $slaves[$i].trust += 4, $slaves[$i].counter.oral += 1>>
+			Eventually, $he climaxes. _S.HeadGirl.slaveName has a talented tongue. _S.HeadGirl.slaveName does not begrudge the slave $his pleasure, <<if $activeSlave.dick == 0>>tonguing $his clit all the way through $his orgasm.<<else>>industriously sucking $his cock until _he2's swallowed every last drop of cum.<</if>> _He2 stands up and seizes $activeSlave.slaveName by the ear, <<say>>ing, "And now, <<s>>lut, you are fucked." $activeSlave.slaveName's weeping stopped during $his climax, but now $he starts to @@.gold;sob with terror,@@ and $he cries harder as your Head Girl hauls $him out of your office by $his ear. _S.HeadGirl.slaveName @@.mediumaquamarine;waves cheerfully@@ to you as _he2 leaves with _his2 victim.
+			<<set _S.HeadGirl.trust += 4, _S.HeadGirl.counter.oral += 1>>
 			<<set $oralTotal += 1>>
 			<<set $activeSlave.trust -= 4, $activeSlave.counter.oral += 1>>
 			<<set $oralTotal += 1>>
diff --git a/src/uncategorized/reNickname.tw b/src/uncategorized/reNickname.tw
index 71426e5e0846d12ecfc05562564736d0ab81ea1e..a3d528b4c977e4ce92fa78df0d82933dea28db11 100644
--- a/src/uncategorized/reNickname.tw
+++ b/src/uncategorized/reNickname.tw
@@ -104,40 +104,40 @@
 <<if ($activeSlave.skill.oral <= 30) && ($activeSlave.skill.anal <= 30)>>
 	<<set _qualifiedNicknames.push("novice")>>
 <</if>>
-<<if ($activeSlave.ID == $HeadGirl.ID)>>
+<<if ($activeSlave.ID == $HeadGirlID)>>
 	<<set _qualifiedNicknames.push("Head Girl")>>
 <</if>>
-<<if ($activeSlave.ID == $Bodyguard.ID)>>
+<<if ($activeSlave.ID == $BodyguardID)>>
 	<<set _qualifiedNicknames.push("Bodyguard")>>
 <</if>>
-<<if ($activeSlave.ID == $Concubine.ID)>>
+<<if ($activeSlave.ID == $ConcubineID)>>
 	<<set _qualifiedNicknames.push("Concubine")>>
 <</if>>
 <<if ($activeSlave.ID == $AttendantID)>>
 	<<set _qualifiedNicknames.push("Attendant")>>
 <</if>>
-<<if ($activeSlave.ID == $Matron.ID)>>
+<<if ($activeSlave.ID == $MatronID)>>
 	<<set _qualifiedNicknames.push("Matron")>>
 <</if>>
-<<if ($activeSlave.ID == $Madam.ID)>>
+<<if ($activeSlave.ID == $MadamID)>>
 	<<set _qualifiedNicknames.push("Madam")>>
 <</if>>
 <<if ($activeSlave.ID == $djID)>>
 	<<set _qualifiedNicknames.push("DJ")>>
 <</if>>
-<<if ($activeSlave.ID == $Recruiter.ID)>>
+<<if ($activeSlave.ID == $RecruiterID)>>
 	<<set _qualifiedNicknames.push("Recruiter")>>
 <</if>>
 <<if ($activeSlave.ID == $WardenessID)>>
 	<<set _qualifiedNicknames.push("Wardeness")>>
 <</if>>
-<<if ($activeSlave.ID == $Milkmaid.ID)>>
+<<if ($activeSlave.ID == $MilkmaidID)>>
 	<<set _qualifiedNicknames.push("Milkmaid")>>
 <</if>>
-<<if ($activeSlave.ID == $Farmer.ID)>>
+<<if ($activeSlave.ID == $FarmerID)>>
 	<<set _qualifiedNicknames.push("Farmer")>>
 <</if>>
-<<if ($activeSlave.ID == $Milkmaid.ID) && $activeSlave.dick > 5 && canPenetrate($activeSlave) && $cumSlaves > 3>>
+<<if ($activeSlave.ID == $MilkmaidID) && $activeSlave.dick > 5 && canPenetrate($activeSlave) && $cumSlaves > 3>>
 	<<set _qualifiedNicknames.push("Collectrix")>>
 <</if>>
 <<if ($activeSlave.ID == $StewardessID)>>
diff --git a/src/uncategorized/reRecruit.tw b/src/uncategorized/reRecruit.tw
index 331070fcb62fdf9213996ea2d9f616c6c2bf1239..5af04f9b5389af46df5fc4034299d4a345f5948a 100644
--- a/src/uncategorized/reRecruit.tw
+++ b/src/uncategorized/reRecruit.tw
@@ -2059,30 +2059,30 @@
 <<switch $recruit>>
 <<case "female recruit">>
 
-<<setLocalPronouns $HeadGirl 2>>
-<<setSpokenLocalPronouns $activeSlave $HeadGirl>>
+<<setLocalPronouns _S.HeadGirl 2>>
+<<setSpokenLocalPronouns $activeSlave _S.HeadGirl>>
 
-Your Head Girl sends you a discreet message that _he2 may have found a slave for you. $HeadGirl.slaveName duly ushers a $girl into your office. $He looks very young, like a dissolute party $girl. $He bites $his lip nervously when $he sees you, and looks to $HeadGirl.slaveName for guidance. $HeadGirl.slaveName nods at $him reassuringly, so $he explains $himself.
+Your Head Girl sends you a discreet message that _he2 may have found a slave for you. _S.HeadGirl.slaveName duly ushers a $girl into your office. $He looks very young, like a dissolute party $girl. $He bites $his lip nervously when $he sees you, and looks to _S.HeadGirl.slaveName for guidance. _S.HeadGirl.slaveName nods at $him reassuringly, so $he explains $himself.
 <br><br>
-"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm, um, bored, I gue<<ss>>. I go to club<<s>> and get drunk and fuck guy<<s>> and it'<<s>> ju<<s>>t kinda boring. I thought it would be different when I turned <<= num($activeSlave.actualAge)>>, but that wa<<s>> a couple month<<s>> ago and, well, nothing'<<s>> different. I <<s>>aw $HeadGirl.slaveName and <<he 2>> wa<<s>> ju<<s>>t <<s>>o gra<<c>>eful and beautiful and <<he 2>> <<s>>eemed <<s>>o confident in what <<he 2>> wa<<s>> doing and who <<he 2>> wa<<s>> and I talked to _him2 and <<he 2>> <<s>>aid <<he 2>> wa<<s>> your Head Girl and... I want to be like _him2. Can I be your <<s>>lave? I'd be good, I'm good at <<s>>ucking dick<<s>> and <<s>>tuff." $He seems to be a little naïve about sexual slavery, but there's no need to tell $him that.
+"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm, um, bored, I gue<<ss>>. I go to club<<s>> and get drunk and fuck guy<<s>> and it'<<s>> ju<<s>>t kinda boring. I thought it would be different when I turned <<= num($activeSlave.actualAge)>>, but that wa<<s>> a couple month<<s>> ago and, well, nothing'<<s>> different. I <<s>>aw _S.HeadGirl.slaveName and <<he 2>> wa<<s>> ju<<s>>t <<s>>o gra<<c>>eful and beautiful and <<he 2>> <<s>>eemed <<s>>o confident in what <<he 2>> wa<<s>> doing and who <<he 2>> wa<<s>> and I talked to _him2 and <<he 2>> <<s>>aid <<he 2>> wa<<s>> your Head Girl and... I want to be like _him2. Can I be your <<s>>lave? I'd be good, I'm good at <<s>>ucking dick<<s>> and <<s>>tuff." $He seems to be a little naïve about sexual slavery, but there's no need to tell $him that.
 
 <<case "male recruit">>
 
-<<setLocalPronouns $HeadGirl 2>>
-<<setSpokenLocalPronouns $activeSlave $HeadGirl>>
+<<setLocalPronouns _S.HeadGirl 2>>
+<<setSpokenLocalPronouns $activeSlave _S.HeadGirl>>
 
-Your Head Girl sends you a discreet message that _he2 may have found a slave for you. $HeadGirl.slaveName duly ushers an androgynous young person into your office. $He's dressed as a girl and acts like one. $He looks very young, like a dissolute party girl. $He bites $his lip nervously when $he sees you, and looks to $HeadGirl.slaveName for guidance. $HeadGirl.slaveName nods at $him reassuringly, so $he explains $himself.
+Your Head Girl sends you a discreet message that _he2 may have found a slave for you. _S.HeadGirl.slaveName duly ushers an androgynous young person into your office. $He's dressed as a girl and acts like one. $He looks very young, like a dissolute party girl. $He bites $his lip nervously when $he sees you, and looks to _S.HeadGirl.slaveName for guidance. _S.HeadGirl.slaveName nods at $him reassuringly, so $he explains $himself.
 <br><br>
-"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm, um, bored, I gue<<ss>>. I go to club<<s>> and get drunk and fuck guy<<s>> and it'<<s>> ju<<s>>t kinda boring. I thought it would be different when I turned <<= num($activeSlave.actualAge)>>, but that wa<<s>> a couple month<<s>> ago and, well, nothing'<<s>> different. I <<s>>aw $HeadGirl.slaveName and <<he 2>> wa<<s>> ju<<s>>t <<s>>o beautiful and ha<<s>> a dick like me and <<he 2>> <<s>>eemed <<s>>o confident in what <<he 2>> wa<<s>> doing and who <<he 2>> wa<<s>> and I talked to _him2 and <<he 2>> <<s>>aid <<he 2>> wa<<s>> your Head Girl and... I want to be like _him2. Can I be your <<s>>lave? I'd be good, I'm good at <<s>>ucking dick<<s>>." $He seems to be a little naïve about sexual slavery, but there's no need to tell $him that.
+"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm, um, bored, I gue<<ss>>. I go to club<<s>> and get drunk and fuck guy<<s>> and it'<<s>> ju<<s>>t kinda boring. I thought it would be different when I turned <<= num($activeSlave.actualAge)>>, but that wa<<s>> a couple month<<s>> ago and, well, nothing'<<s>> different. I <<s>>aw _S.HeadGirl.slaveName and <<he 2>> wa<<s>> ju<<s>>t <<s>>o beautiful and ha<<s>> a dick like me and <<he 2>> <<s>>eemed <<s>>o confident in what <<he 2>> wa<<s>> doing and who <<he 2>> wa<<s>> and I talked to _him2 and <<he 2>> <<s>>aid <<he 2>> wa<<s>> your Head Girl and... I want to be like _him2. Can I be your <<s>>lave? I'd be good, I'm good at <<s>>ucking dick<<s>>." $He seems to be a little naïve about sexual slavery, but there's no need to tell $him that.
 
 <<case "whore recruit">>
 
-<<setLocalPronouns $HeadGirl 2>>
-<<setSpokenLocalPronouns $activeSlave $HeadGirl>>
+<<setLocalPronouns _S.HeadGirl 2>>
+<<setSpokenLocalPronouns $activeSlave _S.HeadGirl>>
 
-Your Head Girl sends you a discreet message that _he2 may have found a slave for you. $HeadGirl.slaveName duly ushers a working $girl into your office. $He looks like a classic street hooker, and $he's clearly had a rough time recently. $He needs no prompting to explain $himself, and $he does so without embarrassment.
+Your Head Girl sends you a discreet message that _he2 may have found a slave for you. _S.HeadGirl.slaveName duly ushers a working $girl into your office. $He looks like a classic street hooker, and $he's clearly had a rough time recently. $He needs no prompting to explain $himself, and $he does so without embarrassment.
 <br><br>
-"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm a <<s>>treet whore. Working <<= $girl>><<s>> get to know each other, <<s>>o I know $HeadGirl.slaveName pretty well. I've been having a bad time on the <<s>>treet<<s>>, and la<<s>>t night a john beat on me pretty good. I'm broke and I'm <<s>>ick of thi<<s>>. Being free i<<s>>n't worth shit. $HeadGirl.slaveName like<<s>> you and <<he 2>> <<s>>eem<<s>> to do OK. <<S>>o, can I be your <<s>>lave? I'm a good bet, <<if $PC.title != 0>><<s>>ir<<else>>ma'am<</if>>. I'd be happy enough working for you a<<s>> a <<s>>lave whore if you keep me healthy and <<s>>afe, and I'm a good fuck."
+"<<if $PC.title != 0>><<S>>ir<<else>>Ma'am<</if>>, my name i<<s>> $activeSlave.slaveName. I'm a <<s>>treet whore. Working <<= $girl>><<s>> get to know each other, <<s>>o I know _S.HeadGirl.slaveName pretty well. I've been having a bad time on the <<s>>treet<<s>>, and la<<s>>t night a john beat on me pretty good. I'm broke and I'm <<s>>ick of thi<<s>>. Being free i<<s>>n't worth shit. _S.HeadGirl.slaveName like<<s>> you and <<he 2>> <<s>>eem<<s>> to do OK. <<S>>o, can I be your <<s>>lave? I'm a good bet, <<if $PC.title != 0>><<s>>ir<<else>>ma'am<</if>>. I'd be happy enough working for you a<<s>> a <<s>>lave whore if you keep me healthy and <<s>>afe, and I'm a good fuck."
 
 <<case "female debtor">>
 
@@ -2600,9 +2600,9 @@ He explains that one of his servant <<= $girl>>s was impregnated by his fool of
 		<<case "rogue cyborg">>
 			With your order received the target quickly seeks you out. After a huge sum of credits changes hands to render it penthouse safe, the legalities and biometric scanning are complete and you are now the proud owner of a cyber<<= $loli>>.
 		<<case "female recruit">>
-			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if $HeadGirl.clothes != "no clothing">>"Can I have <<if $HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>$HeadGirl.clothes<</if>> like $HeadGirl.slaveName?"<<else>>"Can I work with $HeadGirl.slaveName?"<</if>> Your sternly tell $him to call you <<= properMaster()>>, to ask questions only with permission, and to strip. $He looks at $HeadGirl.slaveName, but $HeadGirl.slaveName returns $his look with no compassion at all and tells $him to do what you say. Looking suddenly fearful, $activeSlave.slaveName strips. Once your inspection of $his body reaches $his anus, $he becomes suddenly nervous. "A-am I going to have t-to do butt <<s>>tuff, <<Master>>?"
+			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if _S.HeadGirl.clothes != "no clothing">>"Can I have <<if _S.HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>_S.HeadGirl.clothes<</if>> like _S.HeadGirl.slaveName?"<<else>>"Can I work with _S.HeadGirl.slaveName?"<</if>> Your sternly tell $him to call you <<= properMaster()>>, to ask questions only with permission, and to strip. $He looks at _S.HeadGirl.slaveName, but _S.HeadGirl.slaveName returns $his look with no compassion at all and tells $him to do what you say. Looking suddenly fearful, $activeSlave.slaveName strips. Once your inspection of $his body reaches $his anus, $he becomes suddenly nervous. "A-am I going to have t-to do butt <<s>>tuff, <<Master>>?"
 		<<case "male recruit">>
-			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if $HeadGirl.clothes != "no clothing">>"Can I have <<if $HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>$HeadGirl.clothes<</if>> like $HeadGirl.slaveName?"<<else>>"Can I work with $HeadGirl.slaveName?"<</if>> Your sternly tell $him to call you <<= properMaster()>>, to ask questions only with permission, and to strip. $He looks at $HeadGirl.slaveName, but $HeadGirl.slaveName returns $his look with no compassion at all and tells $him to do what you say. Looking suddenly fearful, $activeSlave.slaveName strips. $His cock is totally flaccid as $he looks at the floor, shivering. Once your inspection of $his body reaches $his anus, $he becomes suddenly nervous. "A-am I going to have t-to do butt <<s>>tuff, <<Master>>?"
+			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if _S.HeadGirl.clothes != "no clothing">>"Can I have <<if _S.HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>_S.HeadGirl.clothes<</if>> like _S.HeadGirl.slaveName?"<<else>>"Can I work with _S.HeadGirl.slaveName?"<</if>> Your sternly tell $him to call you <<= properMaster()>>, to ask questions only with permission, and to strip. $He looks at _S.HeadGirl.slaveName, but _S.HeadGirl.slaveName returns $his look with no compassion at all and tells $him to do what you say. Looking suddenly fearful, $activeSlave.slaveName strips. $His cock is totally flaccid as $he looks at the floor, shivering. Once your inspection of $his body reaches $his anus, $he becomes suddenly nervous. "A-am I going to have t-to do butt <<s>>tuff, <<Master>>?"
 		<<case "whore recruit">>
 			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName participates matter-of-factly, and there's a certain sense of relief about $him. $His fate is out of $his hands now, and you get the impression $he isn't sorry about that at all.
 		<<case "desperate birth">>
@@ -2752,11 +2752,11 @@ He explains that one of his servant <<= $girl>>s was impregnated by his fool of
 		<<case "rogue cyborg">>
 			With your order received the target quickly seeks you out. After a huge sum of credits changes hands to render it safe, the legalities and biometric scanning are complete and it is sold off to a very eager robophile.
 		<<case "female recruit">>
-			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if $HeadGirl.clothes != "no clothing">>"Can I have <<if $HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>$HeadGirl.clothes<</if>> like $HeadGirl.slaveName?"<<else>>"Can I work with $HeadGirl.slaveName?"<</if>> Your answer appears in the form of a purchasing agent, here to take $him away. As he restrains the disbelieving $girl, you tell $him $he's been purchased by a brothel, so $he's going to be fucked about 70,000 times before $he gets to be too old and is retired, so $he can be sure $he won't be bored. $He releases a wail of utter despair, quickly cut off by a sturdy bag being fastened over $his head.
+			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if _S.HeadGirl.clothes != "no clothing">>"Can I have <<if _S.HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>_S.HeadGirl.clothes<</if>> like _S.HeadGirl.slaveName?"<<else>>"Can I work with _S.HeadGirl.slaveName?"<</if>> Your answer appears in the form of a purchasing agent, here to take $him away. As he restrains the disbelieving $girl, you tell $him $he's been purchased by a brothel, so $he's going to be fucked about 70,000 times before $he gets to be too old and is retired, so $he can be sure $he won't be bored. $He releases a wail of utter despair, quickly cut off by a sturdy bag being fastened over $his head.
 		<<case "male recruit">>
-			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if $HeadGirl.clothes != "no clothing">>"Can I have <<if $HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>$HeadGirl.clothes<</if>> like $HeadGirl.slaveName?"<<else>>"Can I work with $HeadGirl.slaveName?"<</if>> Your answer appears in the form of a purchasing agent, here to take $him away. As he restrains the disbelieving bitch, you tell $him $he's been purchased by a brothel, so $he's going to be fucked about 70,000 times before $he gets to be too old and is retired, so $he can be sure $he won't be bored. $He releases a wail of utter despair, quickly cut off by a sturdy bag being fastened over $his head.
+			You complete the legalities and biometric scanning quickly and without fuss. $activeSlave.slaveName bounces nervously on $his heels. Finally $he works up $his courage and asks, <<if _S.HeadGirl.clothes != "no clothing">>"Can I have <<if _S.HeadGirl.clothes == "choosing her own clothes">>a cute outfit<<else>>_S.HeadGirl.clothes<</if>> like _S.HeadGirl.slaveName?"<<else>>"Can I work with _S.HeadGirl.slaveName?"<</if>> Your answer appears in the form of a purchasing agent, here to take $him away. As he restrains the disbelieving bitch, you tell $him $he's been purchased by a brothel, so $he's going to be fucked about 70,000 times before $he gets to be too old and is retired, so $he can be sure $he won't be bored. $He releases a wail of utter despair, quickly cut off by a sturdy bag being fastened over $his head.
 		<<case "whore recruit">>
-			When the purchasing agent appears to take $him away, $he looks rueful. "God fucking damn it," $he <<say>>s, less angrily than you expected. "I <<sh>>ould have known. $HeadGirl.slaveName played me good. Treat _him2 right, _he2'<<s>> got <<s>>kill<<s>>."
+			When the purchasing agent appears to take $him away, $he looks rueful. "God fucking damn it," $he <<say>>s, less angrily than you expected. "I <<sh>>ould have known. _S.HeadGirl.slaveName played me good. Treat _him2 right, _he2'<<s>> got <<s>>kill<<s>>."
 		<<case "female debtor">>
 			$He sobs as the biometric scanners scrupulously record $his every particular as belonging not to a person but to a piece of human property. $He tries to resist placing $his biometric signature in testament to the truth of $his debt, but when you observe that the alternative is death, $he complies. A purchasing agent appears to take $him away.
 		<<case "male debtor">>
diff --git a/src/uncategorized/reShelterInspection.tw b/src/uncategorized/reShelterInspection.tw
index 58cf6af091d2f7489f10cf4d6d90b720cd986b90..b43b73611cf03f0a117a51375280f534ce49da9c 100644
--- a/src/uncategorized/reShelterInspection.tw
+++ b/src/uncategorized/reShelterInspection.tw
@@ -118,12 +118,12 @@ Not waiting to be greeted, the inspector looks up at the nearest camera and dema
 	<<include "New Slave Intro">>
 	<</replace>>
 <</link>> //This will cost <<print cashFormat(_ContractCost)>>//
-<<if $Bodyguard != 0 || $mercenaries > 1 || ($SF.Toggle && $SF.Active >= 1)>>
+<<if _S.Bodyguard || $mercenaries > 1 || ($SF.Toggle && $SF.Active >= 1)>>
 	<br><<link "Escort _him2 off your property">>
 	<<replace "#result">>
 		You along with
-		<<if $Bodyguard != 0>>
-			your bodyguard <<print SlaveFullName($Bodyguard)>>
+		<<if _S.Bodyguard>>
+			your bodyguard <<print SlaveFullName(_S.Bodyguard)>>
 			<<if $mercenaries > 1 || ($SF.Toggle && $SF.Active >= 1)>>
 				and
 			<</if>>
diff --git a/src/uncategorized/reShowerPunishment.tw b/src/uncategorized/reShowerPunishment.tw
index 3123fc2027f83e620379be56db2b11fa797a98e4..8dfbfbc377f6ae0797762800a66340e2ab3f6be8 100644
--- a/src/uncategorized/reShowerPunishment.tw
+++ b/src/uncategorized/reShowerPunishment.tw
@@ -4,18 +4,18 @@
 
 <<set $activeSlave = $eventSlave>>
 <<setLocalPronouns $activeSlave>>
-<<setLocalPronouns $HeadGirl 2>>
+<<setLocalPronouns _S.HeadGirl 2>>
 <<setPlayerPronouns>>
-<<run Enunciate($HeadGirl)>>
+<<run Enunciate(_S.HeadGirl)>>
 
 <<set _clothesTemp = $activeSlave.clothes, $activeSlave.clothes = "no clothing">>
-<<set _clothesTemp2 = $HeadGirl.clothes, $HeadGirl.clothes = "no clothing">>
+<<set _clothesTemp2 = _S.HeadGirl.clothes, _S.HeadGirl.clothes = "no clothing">>
 <span id="artFrame">
 /* 000-250-006 */
 <<if $seeImages == 1>>
 	<div class="imageColumn">
 		<div class="imageRef medImg">
-			<<= SlaveArt($HeadGirl, 2, 0)>>
+			<<= SlaveArt(_S.HeadGirl, 2, 0)>>
 		</div>
 		<div class="imageRef medImg">
 			<<= SlaveArt($activeSlave, 2, 0)>>
@@ -25,90 +25,87 @@
 /* 000-250-006 */
 </span>
 <<set $activeSlave.clothes = _clothesTemp>>
-<<set $HeadGirl.clothes = _clothesTemp2>>
+<<set _S.HeadGirl.clothes = _clothesTemp2>>
 
 <<if $HGSuite == 1>>
 	Looking in on your Head Girl in _his2 suite, you hear _his2 private shower running and head that way. Through the thick steam the shower makes on its hottest setting, you see
 <<else>>
 	Passing by the showers, you see, through the steam of a very hot shower,
 <</if>>
-a <<if $activeSlave.height > 180>>tall, <<elseif $activeSlave.height < 150>>tiny, <</if>>$activeSlave.skin form moving busily around a $HeadGirl.skin figure, which is standing confidently in the middle of the warm, moist space. As you draw nearer, you identify the stationary slave as your Head Girl, <<= App.UI.slaveDescriptionDialog($HeadGirl)>>. _His2 attendant is <<= contextualIntro($HeadGirl, $activeSlave, true)>>, and $he's washing $his superior with a big sponge.
+a <<if $activeSlave.height > 180>>tall, <<elseif $activeSlave.height < 150>>tiny, <</if>>$activeSlave.skin form moving busily around a _S.HeadGirl.skin figure, which is standing confidently in the middle of the warm, moist space. As you draw nearer, you identify the stationary slave as your Head Girl, <<= App.UI.slaveDescriptionDialog(_S.HeadGirl)>>. _His2 attendant is <<= contextualIntro(_S.HeadGirl, $activeSlave, true)>>, and $he's washing $his superior with a big sponge.
 <<if $HGSeverity > 0>>
 	By virtue of not being rape, this is an unusually mild punishment by your Head Girl, if indeed that's what it is. But perhaps _he2's saving that for later. And to go by the cringing, frightened mien of the busy little bath bitch, that's probably it.
 <<elseif $HGSeverity == 0>>
-	Your Head Girl does _his2 best to fit the punishment to the crime, so $activeSlave.slaveName's failure was likely minor. With $HeadGirl.slaveName's penchant for poetic justice, probably some little deficiency of personal cleanliness.
+	Your Head Girl does _his2 best to fit the punishment to the crime, so $activeSlave.slaveName's failure was likely minor. With _S.HeadGirl.slaveName's penchant for poetic justice, probably some little deficiency of personal cleanliness.
 <<else>>
 	This is the sort of mild punishment that your regime of respect and dignity requires _him2 to use for all but the most egregious fault. Thus restricted, _he2 does _his2 best to come up with novel little degradations to keep _his2 charges on their toes.
 <</if>>
 <br><br>
-$activeSlave.slaveName is being very thorough. When you first appeared, $he was working $his way up $HeadGirl.slaveName's <<if $HeadGirl.muscles > 30>>muscle-corded<<elseif $HeadGirl.weight > 10>>soft<<elseif $HeadGirl.vagina > -1>>feminine<<else>>pretty<</if>> thighs, having obviously started at the bottom. $He skips over $HeadGirl.slaveName's crotch, probably under instructions to leave it for last. It's late in your Head Girl's busy day, and you hear _his2 groan of relaxation over the running water when the stiff sponge begins to scrub back and forth across _his2 <<if $HeadGirl.belly >= 60000>>enormously <<if $HeadGirl.preg > 0>>pregnant<<else>>rounded<</if>> belly<<elseif $HeadGirl.weight > 190>>massively fat gut<<elseif $HeadGirl.belly >= 10000>>hugely <<if $HeadGirl.preg > 0>>pregnant<<else>>rounded<</if>> belly<<elseif $HeadGirl.weight > 95>>big soft belly<<elseif $HeadGirl.belly >= 5000>><<if $HeadGirl.preg > 0>>pregnant<<else>>round<</if>> belly<<elseif $HeadGirl.weight > 30>>soft belly<<elseif $HeadGirl.belly >= 1500>>bloated belly<<elseif $HeadGirl.muscles > 30>>shredded abs<<elseif $HeadGirl.weight > 10>>plush belly<<elseif $HeadGirl.navelPiercing > 0>>pierced belly button<<elseif $HeadGirl.waist < -10>><<if $HeadGirl.waist < -95>>absurdly <</if>>narrow waist<<else>>belly<</if>>.
+$activeSlave.slaveName is being very thorough. When you first appeared, $he was working $his way up _S.HeadGirl.slaveName's <<if _S.HeadGirl.muscles > 30>>muscle-corded<<elseif _S.HeadGirl.weight > 10>>soft<<elseif _S.HeadGirl.vagina > -1>>feminine<<else>>pretty<</if>> thighs, having obviously started at the bottom. $He skips over _S.HeadGirl.slaveName's crotch, probably under instructions to leave it for last. It's late in your Head Girl's busy day, and you hear _his2 groan of relaxation over the running water when the stiff sponge begins to scrub back and forth across _his2 <<if _S.HeadGirl.belly >= 60000>>enormously <<if _S.HeadGirl.preg > 0>>pregnant<<else>>rounded<</if>> belly<<elseif _S.HeadGirl.weight > 190>>massively fat gut<<elseif _S.HeadGirl.belly >= 10000>>hugely <<if _S.HeadGirl.preg > 0>>pregnant<<else>>rounded<</if>> belly<<elseif _S.HeadGirl.weight > 95>>big soft belly<<elseif _S.HeadGirl.belly >= 5000>><<if _S.HeadGirl.preg > 0>>pregnant<<else>>round<</if>> belly<<elseif _S.HeadGirl.weight > 30>>soft belly<<elseif _S.HeadGirl.belly >= 1500>>bloated belly<<elseif _S.HeadGirl.muscles > 30>>shredded abs<<elseif _S.HeadGirl.weight > 10>>plush belly<<elseif _S.HeadGirl.navelPiercing > 0>>pierced belly button<<elseif _S.HeadGirl.waist < -10>><<if _S.HeadGirl.waist < -95>>absurdly <</if>>narrow waist<<else>>belly<</if>>.
 
 <br><br>
 
 <span id="result">
 <<link "Just spectate">>
 	<<replace "#result">>
-	You could strip off your suit, walk into the steam, and enjoy your slaves' ministrations, but sometimes the artistry of tastefully nude bodies is a welcome change of pace. You lean against the wall, far enough away that they remain unaware of your presence, and take in the sight. $HeadGirl.slaveName makes the penitent $girl do the job with Brahmanical thoroughness, cleaning $his superior's $HeadGirl.race body down to its very last pore. As $activeSlave.slaveName circles the Head Girl laboriously, doing $his best to ingratiate $himself by diligence, the pair of naked <<if $girl == _girl2>>$women<<else>>slaves<</if>> present a fascinating contrast. They are unclothed alike, the water streaming off their bodies without any distinction, but even an old world fool could not mistake the immense gulf between them.
+	You could strip off your suit, walk into the steam, and enjoy your slaves' ministrations, but sometimes the artistry of tastefully nude bodies is a welcome change of pace. You lean against the wall, far enough away that they remain unaware of your presence, and take in the sight. _S.HeadGirl.slaveName makes the penitent $girl do the job with Brahmanical thoroughness, cleaning $his superior's _S.HeadGirl.race body down to its very last pore. As $activeSlave.slaveName circles the Head Girl laboriously, doing $his best to ingratiate $himself by diligence, the pair of naked <<if $girl == _girl2>>$women<<else>>slaves<</if>> present a fascinating contrast. They are unclothed alike, the water streaming off their bodies without any distinction, but even an old world fool could not mistake the immense gulf between them.
 	<br><br>
-	When $activeSlave.slaveName is finally done, $HeadGirl.slaveName's
+	When $activeSlave.slaveName is finally done, _S.HeadGirl.slaveName's
 	<<if $HGSeverity > 0>>
 		hands seize $him by the ears and pull $his head in for a kiss that is dominance distilled into the form of a loving gesture. Then _he2 pokes _his2 bitch in the side, forcing the slave to collapse in just the right way.
 	<<elseif $HGSeverity == 0>>
 		arms encircle $him in an embrace that is simultaneously controlling, comforting, and sexually insistent. The slave does not resist, allowing the Head Girl to run _his2 hands over the warm, wet sex slave.
 	<<else>>
-		arousal is obvious. Though the respectful regime you require secures $him from the fear of being used, $activeSlave.slaveName nonverbally offers $his superior oral, out of obvious gratitude that whatever $he did is being treated so leniently, and perhaps out of a desire to be in $HeadGirl.slaveName's good graces.
+		arousal is obvious. Though the respectful regime you require secures $him from the fear of being used, $activeSlave.slaveName nonverbally offers $his superior oral, out of obvious gratitude that whatever $he did is being treated so leniently, and perhaps out of a desire to be in _S.HeadGirl.slaveName's good graces.
 	<</if>>
-	In no time at all, $activeSlave.slaveName's $activeSlave.hColor head descends to obscure $HeadGirl.slaveName's groin. The <<if $HeadGirl.face > 95>>heartrendingly gorgeous<<elseif $HeadGirl.face <= 95>>wonderfully pretty<<elseif $HeadGirl.face <= 40>>approachably lovely<<elseif $HeadGirl.face <= 10>>not unattractive<<else>>homely<</if>> <<if $HeadGirl.physicalAge > 25>>_woman2's<<else>>_girl2's<</if>> head cranes back with orgasm before long; that diligent scrub must have been quite stimulating.
+	In no time at all, $activeSlave.slaveName's $activeSlave.hColor head descends to obscure _S.HeadGirl.slaveName's groin. The <<if _S.HeadGirl.face > 95>>heartrendingly gorgeous<<elseif _S.HeadGirl.face <= 95>>wonderfully pretty<<elseif _S.HeadGirl.face <= 40>>approachably lovely<<elseif _S.HeadGirl.face <= 10>>not unattractive<<else>>homely<</if>> <<if _S.HeadGirl.physicalAge > 25>>_woman2's<<else>>_girl2's<</if>> head cranes back with orgasm before long; that diligent scrub must have been quite stimulating.
 	<br><br>
-	$activeSlave.slaveName stays in the shower to clean $himself, so $HeadGirl.slaveName exits to see you watching the denouement. _He2 @@.hotpink;smiles,@@ murmuring a greeting, and hurries over to give you a peck on the cheek, leaning in as best _he2 can to keep _his2 moist body away from your suit. "Thi<<s>> i<<s>> the life, <<Master>>," _he2 whispers.
+	$activeSlave.slaveName stays in the shower to clean $himself, so _S.HeadGirl.slaveName exits to see you watching the denouement. _He2 @@.hotpink;smiles,@@ murmuring a greeting, and hurries over to give you a peck on the cheek, leaning in as best _he2 can to keep _his2 moist body away from your suit. "Thi<<s>> i<<s>> the life, <<Master>>," _he2 whispers.
 	<<set $activeSlave.counter.oral += 1>>
 	<<set $oralTotal += 1>>
-	<<set $i = $slaveIndices[$HeadGirl.ID]>>
-	<<set $slaves[$i].devotion += 4>>
-	<<set $slaves[$i].counter.penetrative += 1>>
+	<<set _S.HeadGirl.devotion += 4>>
+	<<set _S.HeadGirl.counter.penetrative += 1>>
 	<<set $penetrativeTotal += 1>>
 	<</replace>>
 <</link>>
 <br><<link "Get a scrub down too">>
 	<<replace "#result">>
-	You strip off your suit and enter the shower. By the time you get in, $HeadGirl.slaveName's sponge scrub is almost done. _He2 turns to greet you with half-lidded eyes, well pleased with _his2 thorough scrubbing. _His2 $HeadGirl.skin skin shines with wet cleanliness, and _his2 $HeadGirl.nipples nipples begin to <<if $HeadGirl.nipples == "fuckable">>swell with arousal<<else>>stiffen<</if>> as _he2 sees your gaze take in _his2 nude body. _He2 brusquely orders $activeSlave.slaveName to scrub you, too, anticipating your intention. The rough, exfoliating sensation of the sponge is indeed delightful, and you close your eyes to savor the feeling as the slave rubs it up and down your calves and then the backs of your knees.
+	You strip off your suit and enter the shower. By the time you get in, _S.HeadGirl.slaveName's sponge scrub is almost done. _He2 turns to greet you with half-lidded eyes, well pleased with _his2 thorough scrubbing. _His2 _S.HeadGirl.skin skin shines with wet cleanliness, and _his2 _S.HeadGirl.nipples nipples begin to <<if _S.HeadGirl.nipples == "fuckable">>swell with arousal<<else>>stiffen<</if>> as _he2 sees your gaze take in _his2 nude body. _He2 brusquely orders $activeSlave.slaveName to scrub you, too, anticipating your intention. The rough, exfoliating sensation of the sponge is indeed delightful, and you close your eyes to savor the feeling as the slave rubs it up and down your calves and then the backs of your knees.
 	<br><br>
 	<<if $HGSeverity > 0>>
 		You detect tremors of fear in the slave<<if hasAnyArms($activeSlave)>>'s hand<<if hasBothArms($activeSlave)>>s<</if>><</if>>; $he knows that $he hasn't extirpated $his misbehavior, whatever it was, just yet. You let your Head Girl manage that, however, and _he2 does. When $activeSlave.slaveName is stuck in one position for a short time by the need to wash your thighs, you hear a gasp and open your eyes to the sight of your Head Girl crouched behind $him, giving $him an anal fingerfuck. When $activeSlave.slaveName is done washing you, your Head Girl holds the slave's head to your
 	<<else>>
-		When the washing reaches your shoulders, it becomes clumsier, and $activeSlave.slaveName's wet body begins to bump gently against your torso. Opening your eyes, you see that your Head Girl is taking $him as $he finishes your bath. $activeSlave.slaveName is doing $his best to do a good job as $he's fucked, and $he manages it reasonably well. When $he's done, $HeadGirl.slaveName pushes $his head down towards your
+		When the washing reaches your shoulders, it becomes clumsier, and $activeSlave.slaveName's wet body begins to bump gently against your torso. Opening your eyes, you see that your Head Girl is taking $him as $he finishes your bath. $activeSlave.slaveName is doing $his best to do a good job as $he's fucked, and $he manages it reasonably well. When $he's done, _S.HeadGirl.slaveName pushes $his head down towards your
 	<</if>>
 	<<if $PC.dick != 0>>groin so $he can suck you off<<if $PC.vagina != -1>> and stroke your cunt<</if>><<else>>cunt so $he can eat you out<</if>>. $activeSlave.slaveName complies, and afterward, $he seems to feel that @@.mediumaquamarine;$he came off reasonably well;@@ it could have been worse.
 	<<set $activeSlave.counter.anal += 1>>
 	<<set $analTotal += 1>>
-	<<= knockMeUp($activeSlave, 10, 1, $HeadGirl.ID)>>
+	<<= knockMeUp($activeSlave, 10, 1, $HeadGirlID)>>
 	<<set $activeSlave.counter.oral += 1>>
 	<<set $oralTotal += 1>>
 	<<set $activeSlave.trust += 4>>
-	<<set $i = $slaveIndices[$HeadGirl.ID]>>
-	<<set $slaves[$i].counter.penetrative += 1>>
+	<<set _S.HeadGirl.counter.penetrative += 1>>
 	<<set $penetrativeTotal += 1>>
 	<</replace>>
 <</link>>
 <br><<link "Focus on your Head Girl">>
 	<<replace "#result">>
-	You strip off your suit and walk into the steam, producing a surprised but welcoming greeting from your Head Girl and a muffled, submissive noise from $activeSlave.slaveName. $HeadGirl.slaveName is held more or less stationary by the slave _he2's straddling, so you step in, hook a dominant arm around _his2 waist, and kiss _him2. There's precisely one person in this arcology who's allowed to treat _him2 as _hersP, and it's you. _He2 relaxes into you with gratitude as you shoulder the burden of being the leader in this little area of your empire, lifting it from _his2 shoulders for now.
+	You strip off your suit and walk into the steam, producing a surprised but welcoming greeting from your Head Girl and a muffled, submissive noise from $activeSlave.slaveName. _S.HeadGirl.slaveName is held more or less stationary by the slave _he2's straddling, so you step in, hook a dominant arm around _his2 waist, and kiss _him2. There's precisely one person in this arcology who's allowed to treat _him2 as _hersP, and it's you. _He2 relaxes into you with gratitude as you shoulder the burden of being the leader in this little area of your empire, lifting it from _his2 shoulders for now.
 	<br><br>
-	You run a hand up the side of _his2 neck, bringing it to rest with your fingers cupping _him2 under the ear and your thumb running up along _his2 temple. _He2 shivers, unable to concentrate despite all _his2 poise, the ongoing oral service blending into your intense closeness. Right now, _he2's the <<if $HeadGirl.physicalAge > 25>>_woman2<<else>>_girl2<</if>> for you, so you snap your fingers next to the ear of the slave <<if $HeadGirl.vagina > -1>>eating _him2 out<<else>>blowing _him2<</if>>, point at the dropped sponge, and then point at yourself. The oral stops as $activeSlave.slaveName hurries to scrub you, starting at your feet, but your Head Girl doesn't care. You're kissing _him2.
+	You run a hand up the side of _his2 neck, bringing it to rest with your fingers cupping _him2 under the ear and your thumb running up along _his2 temple. _He2 shivers, unable to concentrate despite all _his2 poise, the ongoing oral service blending into your intense closeness. Right now, _he2's the <<if _S.HeadGirl.physicalAge > 25>>_woman2<<else>>_girl2<</if>> for you, so you snap your fingers next to the ear of the slave <<if _S.HeadGirl.vagina > -1>>eating _him2 out<<else>>blowing _him2<</if>>, point at the dropped sponge, and then point at yourself. The oral stops as $activeSlave.slaveName hurries to scrub you, starting at your feet, but your Head Girl doesn't care. You're kissing _him2.
 	<br><br>
 	_He2 gently strokes your <<if $PC.dick != 0>>rapidly hardening member, smiling into your mouth at the speed with which it stiffens<<if $PC.vagina != -1>>, and teases your pussylips with mischievous fingers<</if>><<else>>flushed cunt, smiling into your mouth at the moisture that instantly coats _his2 fingertips<</if>>. You reach out in turn,
-	<<if $HeadGirl.vagina > -1>>
-		caressing _his2 pussylips before slowly inserting a digit inside _his2 warmth while nuzzling _his2 clit with the knuckle of your thumb. At the first real brush against _his2 clitoris, the overstimulated $HeadGirl.slaveName climaxes, pulling _his2 mouth away from you to shout your name and then sobbing thanks into your ear.
+	<<if _S.HeadGirl.vagina > -1>>
+		caressing _his2 pussylips before slowly inserting a digit inside _his2 warmth while nuzzling _his2 clit with the knuckle of your thumb. At the first real brush against _his2 clitoris, the overstimulated _S.HeadGirl.slaveName climaxes, pulling _his2 mouth away from you to shout your name and then sobbing thanks into your ear.
 	<<else>>
-		hooking your fingers up underneath _his2 taint to grope _his2 anus. After teasing _his2 asspussy for a moment you bring your hand slowly across _his2 perineum<<if $HeadGirl.scrotum > 0>> until _his2 ballsack rests against your wrist<</if>>. The overstimulated $HeadGirl.slaveName cums the instant the butt of your hand touches the base of _his2 cock. _He2 screams your name.
+		hooking your fingers up underneath _his2 taint to grope _his2 anus. After teasing _his2 asspussy for a moment you bring your hand slowly across _his2 perineum<<if _S.HeadGirl.scrotum > 0>> until _his2 ballsack rests against your wrist<</if>>. The overstimulated _S.HeadGirl.slaveName cums the instant the butt of your hand touches the base of _his2 cock. _He2 screams your name.
 	<</if>>
 	<br><br>
-	_He2 isn't terribly affected by loving shower sex with you; after all, it isn't exactly novel for _him2. $activeSlave.slaveName was there to bear witness, though, scrubbing your back as $HeadGirl.slaveName clung to it with orgasm. $He can't help but be @@.hotpink;impressed.@@ Maybe, just maybe, that could be $him someday. $He seems distinctly uncomfortable.
+	_He2 isn't terribly affected by loving shower sex with you; after all, it isn't exactly novel for _him2. $activeSlave.slaveName was there to bear witness, though, scrubbing your back as _S.HeadGirl.slaveName clung to it with orgasm. $He can't help but be @@.hotpink;impressed.@@ Maybe, just maybe, that could be $him someday. $He seems distinctly uncomfortable.
 	<<set $activeSlave.counter.oral += 1>>
 	<<set $oralTotal += 1>>
 	<<set $activeSlave.devotion += 4>>
-	<<set $i = $slaveIndices[$HeadGirl.ID]>>
-	<<set $slaves[$i].counter.oral += 1>>
+	<<set _S.HeadGirl.counter.oral += 1>>
 	<<set $oralTotal += 1>>
 	<</replace>>
 <</link>>
diff --git a/src/uncategorized/reStandardPunishment.tw b/src/uncategorized/reStandardPunishment.tw
index e8c184aaa1b2236d9f195e3470af1391a7d1ae70..753539203771632259ff9593da8329a4f84fd7d6 100644
--- a/src/uncategorized/reStandardPunishment.tw
+++ b/src/uncategorized/reStandardPunishment.tw
@@ -9,8 +9,8 @@
 	<div class="imageColumn">
 		<<if $imageChoice == 1>>
 			<div class="imageRef medImg"><<= SlaveArt($activeSlave, 2, 0)>></div>
-			<<if $HeadGirl != 0>>
-				<div class="imageRef medImg"><<= SlaveArt($HeadGirl, 2, 0)>></div>
+			<<if $HeadGirlID != 0>>
+				<div class="imageRef medImg"><<= SlaveArt(_S.HeadGirl, 2, 0)>></div>
 			<<else>>
 				<<= assistantArt(3)>>
 			<</if>>
@@ -74,9 +74,9 @@
 	ERROR: bad sin
 <</switch>>
 
-<<if $HeadGirl != 0>>
-	<<setLocalPronouns $HeadGirl 2>>
-	$HeadGirl.slaveName is <<if $activeSlave.rules.punishment == "situational">>assessing an appropriate punishment<<else>>sentencing $him to $his standard punishment, <<switch $activeSlave.rules.punishment>><<case "confinement">>several hours in confinement<<case "whipping">>a whipping<<case "chastity">>several hours in chastity<</switch>><</if>>. _He2 immediately defers to you, should you wish to take over.
+<<if $HeadGirlID != 0>>
+	<<setLocalPronouns _S.HeadGirl 2>>
+	_S.HeadGirl.slaveName is <<if $activeSlave.rules.punishment == "situational">>assessing an appropriate punishment<<else>>sentencing $him to $his standard punishment, <<switch $activeSlave.rules.punishment>><<case "confinement">>several hours in confinement<<case "whipping">>a whipping<<case "chastity">>several hours in chastity<</switch>><</if>>. _He2 immediately defers to you, should you wish to take over.
 <<else>>
 	<<setAssistantPronouns>>
 	$assistant.name is <<if $activeSlave.rules.punishment == "situational">>assessing an appropriate punishment<<else>>sentencing $him to $his standard punishment, <<switch $activeSlave.rules.punishment>><<case "confinement">>several hours in confinement<<case "whipping">>a whipping<<case "chastity">>several hours in chastity<</switch>><</if>>. _HeA immediately defers to you, should you wish to take over.
diff --git a/src/uncategorized/recETS.tw b/src/uncategorized/recETS.tw
index 9564b26d47729380892ee0f36f16b57ffe7159d6..655855adc5dad1b7bb2849eac349f726c1e09bfb 100644
--- a/src/uncategorized/recETS.tw
+++ b/src/uncategorized/recETS.tw
@@ -1314,12 +1314,12 @@
 					This call is coming from a public kiosk, which is usually an indication that the person on the other end is a transient individual who has decided to take slavery over homelessness. In this case, however, the story is more unusual — the callers seem stressed, but otherwise normal. They haltingly and quietly explain that they are a father and _daughter2 who had to flee their home after $his wife found out $he was having sex with their _daughter2. They feel that life in an arcology together, even as slaves, would be better than their current life on the streets.
 				</p>
 			<<case "mismatched pair">>
-				<<setLocalPronouns $HeadGirl 3>>
+				<<setLocalPronouns _S.HeadGirl 3>>
 				<p>
 					Your Head Girl comes to see you. _He3 flags a slave posted for sale on your desk. The posting seems completely unimpressive — just a bitch barely past $his <<= ordinalSuffix($activeSlave.actualAge)>> birthday with basic implants and a pathetic little dick — until _he3 points out that the person posting $him for sale is $his _sister2. $His slightly older, naturally female _sister2. Who, to go by the pictures, the younger sibling has desperately been trying to mold $himself to look more like.
 				</p>
 				<p>
-					<<run Enunciate ($HeadGirl)>>
+					<<run Enunciate (_S.HeadGirl)>>
 					"It won't <<sh>>ow on the de<<s>>k yet, but rumor i<<s>> the little cunt'<<s>> gotten them both in bad debt, <<Master>>. You could grab them both cheap."
 				</p>
 				<p>
diff --git a/src/uncategorized/recruiterSelect.tw b/src/uncategorized/recruiterSelect.tw
index 49ad10e6d6d3c9f3b5720de175404330b1533935..23c96f0d8cc23063cdd6b7620c8c611222adee15 100644
--- a/src/uncategorized/recruiterSelect.tw
+++ b/src/uncategorized/recruiterSelect.tw
@@ -1,10 +1,9 @@
 :: Recruiter Select [nobr jump-to-safe jump-from-safe]
 
 <<set $nextButton = "Back to Main", $nextLink = "Main", $encyclopedia = "Recruiter">>
-<<if ($Recruiter != 0)>>
-	<<set $Recruiter = getSlave($Recruiter.ID)>>
-	<<setLocalPronouns $Recruiter>>
-	<span class='slave-name'><<= SlaveFullName($Recruiter)>></span> is
+<<if ($RecruiterID != 0)>>
+	<<setLocalPronouns _S.Recruiter>>
+	<span class='slave-name'><<= SlaveFullName(_S.Recruiter)>></span> is
 	<<if $recruiterTarget != "other arcologies">>
 		recruiting girls.
 	<<else>>
@@ -63,6 +62,6 @@
 <br>&nbsp; //"Facilities" doesn't include training slots in cellblock, schoolroom, spa, clinic (but does include those leaders)//
 
 <br><br>''Appoint a recruiter from among your devoted slaves:''
-<br>[[None|Main][removeJob($Recruiter, "recruit girls")]]
+<br>[[None|Main][removeJob(_S.Recruiter, "recruit girls")]]
 <br><br>
-<<print App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.penthouse, "Main")>>
+<<includeDOM App.UI.SlaveList.facilityManagerSelection(App.Entity.facilities.penthouse, "Main")>>
diff --git a/src/uncategorized/remFluctuations.tw b/src/uncategorized/remFluctuations.tw
index 839a0e94d02c58c0f49cfb0ba71a83f30af9a647..2f7b903629ff1144aa6c3ebb84d390ee812a4524 100644
--- a/src/uncategorized/remFluctuations.tw
+++ b/src/uncategorized/remFluctuations.tw
@@ -82,43 +82,46 @@
 /* The events reducing slave prices are all supply sided. Without events reducing demand this is a little unbalanced. A minor issue */
 <<if $REM == "revel">>
 	Something is happening in one of the Free Cities' richest arcologies. It's not clear what, exactly, it is, since its owner is making skillful use of the arcology's advanced surveillance and media systems to keep its internal affairs quite secret. The truth will get out eventually, and it's probably not going to do much for old world opinions of the Free Cities. After all, cheap slaves go into that arcology at a prodigious rate, and they don't seem to ever come out again. The unexpected demand for slaves, any slaves, has produced a temporary tightening of the entire slave market. Projections suggest price increases of up to five percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialDemandFactor += 12000>>
 <<elseif $REM == "tainted drugs">>
 	The Free Cities are anarcho-capitalist paradises — or 'paradises,' depending on one's station and assets. You can't complain personally, as one of the Free Cities' richest citizens, master of your own arcology and owner of sexual slaves. Unfortunately quite a few slaves in the markets are in a position to complain today, as are their owners. Many slave markets use long-lasting stimulants to pep their wares up for auction; dull-eyed slaves earn low bids. Corner-cutting at one of the major suppliers of these stimulants led to a number of slaves being prepared for auction being damaged today. Relatively few were permanently lost, but slaves are going to be a little scarce for a while, which will drive up the going rate. Projections suggest increases of up to five percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialSupplyFactor -= 12000>>
 <<elseif $REM == "antislavery terrorism">>
 	Antislavery activism in the old world has grown to match the spread of slavery in the Free Cities. Unfortunately for the activists, they are confronted with a fundamental problem: the independence of the Free Cities. There is very little they can do without resorting to violence, and so, predictably, they often do. A major slave induction center in one of the more open Free Cities has just suffered a suicide bombing. The actual damage was slight, but a wave of increased import security is sweeping the Free Cities in reaction to the incident. Slave prices will be driven up by the cost of checking imported merchandise for explosive devices until the market adjusts. Projections suggest price increases of up to five percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialSupplyFactor -= 12000>>
 <<elseif $REM == "new free city">>
 	New Free Cities arise unpredictably. They require either carving out a slice of the old world, emancipating it from whichever inattentive or corrupt country previously owned the land, or reclaiming new land from barren or uninhabitable areas, efforts which are often kept secret. The unpredictable happened today; the world has a new Free City. As usual, immigration rights are being offered cheaply to deserving persons. Many of the remaining rich and talented of the old world are staking claims in the new city, and they'll be buying slaves when they get to their new home. It's a sellers' market out there; projections show the price of slaves rising as much as ten percent in the short term. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialDemandFactor += 20000>>
 <<elseif $REM == "speculation">>
 	The Free Cities are almost totally unregulated. Prices and interest rates can spike and plummet with speeds not seen since the South Sea Bubble, and for the most silly or corrupt of reasons. Today, it's the latter. A massive attempt to rig the slave market was uncovered this morning. Ultimately, the culprits were caught and much of the damage reversed, but confidence in the marketplace has been shaken. Many great slave vendors are holding onto their stock until they're sure the water's calm again. It's a sellers' market out there; projections show the price of slaves rising as much as ten percent in the short term. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialSupplyFactor -= 20000>>
 <<elseif $REM == "medical breakthrough">>
 	There has been a breakthrough in gene therapy. More accurately, there was a breakthrough in gene therapy several years ago — you already knew all about it, and some of the more advanced slave medical upgrades available to you use the technology. However, it's finally gotten out of the prototype stage, and is becoming available to the Free Cities middle class, citizens with one or two slaves. The average citizen is more able today than he was yesterday to turn his chattel housekeeper into the girl he's always dreamed of. Aspirational stuff like this always causes a major price shock. It's a sellers' market out there; projections show the price of slaves rising as much as ten percent in the short term. There will be no immediate impact on you or your slaves, but the coming weeks will be a great time to sell stock, and a bad time to buy. <span class="noteworthy">The market price of slaves has increased.</span>
+	<<set $menialDemandFactor += 20000>>
 <<elseif $REM == "bankruptcy">>
 	The economy of the Free Cities is a rough-and-tumble place. The absence of old world regulations and institutions, and the often gold-rush atmosphere of the new cities, lead to fortunes being made and lost overnight. Last night, one of the Free Cities' greatest fortunes was lost. A great slave trading house unexpectedly went bankrupt, and its huge stable of slaves are being sold at fire-sale prices. The unforeseen sell off is driving down the market price of slaves all across the Free Cities. Projections show a short-term price drop of up to five percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 12000>>
 <<elseif $REM == "refugee boat">>
 	Periodic refugee crises sweep the old world, and sometimes the human flotsam shaken loose from its moorings in the old world is brought up on the shores of the Free Cities. This week, that was no metaphor. A floating Free City has been inundated by refugees in boats. Naturally, the boats have been discarded and the refugees enslaved. It is unclear whether they somehow did not know that this was their inevitable fate, or their lot in the old world was so desperate that they were willing to accept it. Projections show a short-term slave price drop of up to five percent as the market digests the influx. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 12000>>
 <<elseif $REM == "arcology change">>
 	All across the Free Cities, arcology owners are experimenting with new society models and new ways of enforcing them. A nearby arcology has just undergone a major internal struggle as its owner forced through a radical program of changes and harsh measures to enforce them. All but a handful of its inhabitants have been enslaved and placed under the control of a chosen few. With harems of hundreds and little experience or infrastructure to manage them, the new overlords are selling off stock to raise funds to make the transition. Projections show a short-term price drop of up to five percent as they flood the market with mediocre slaves. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 12000>>
 <<elseif $REM == "war">>
 	The old world outside the Free Cities took another step towards its final decline today. A relatively prosperous third world city fell to a regional warlord, and it seems the remaining great powers lack either the money or the will to do anything about it. The victors seem to be following the standard procedure for modern conquerors. Anything valuable, they steal. Among the population, they recruit the willing, shoot the unwilling, and enslave everyone else. The slave markets are going to be glutted with new stock soon. Projections show a short-term price drop of up to ten percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 20000>>
 <<elseif $REM == "empty prisons">>
 	A small, impoverished old world country defaulted on its currency today. Its beleaguered government is taking every available step to raise funds. Among other things, it has sold every inmate in its prisons who would fetch a price worth the trouble of sale into Free Cities slavery. Though most of the influx is going to be of abominably low quality, the sudden addition of so much new meat is going to have a big impact on the slave economy. Projections show a short-term price drop of up to ten percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 20000>>
 <<elseif $REM == "unemployment">>
 	A leading old world nation has just suffered a major economic downturn. Old world nations suffer economic downturns all the time, of course, but to those with interests in the slave market, news like this can be very important. Slave market shocks from catastrophes get all the headlines, but a change that affects millions will often be more impactful. As unemployment in the old world rises, the number of people faced with the choice between starvation and voluntary enslavement rises. Social safety nets just aren't what they used to be. Projections show a short-term slave price drop of up to ten percent due to the sharp increase in desperate people immigrating to the Free Cities for enslavement. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialSupplyFactor += 20000>>
 <<elseif $REM == "anti-slavery coup">>
 	For months there were strong indications that an old world nation was quietly taking steps towards legalizing slavery. The market began to anticipate a serious increase in the demand for slaves in earnest but this week a month of protests against the country's leaders ended in a violent coup. The new government, claiming to only follow the will of the people, has made several promises, including a very vocal rebuke of even the slightest possibility of legal slavery within their borders. The slave market was shocked to find the previous government to be so weak and even more shocked at how unwilling the new one is to accept the times we live in. The panicked market quickly adjusted to greatly lowered slave demand and projections show a short-term price drop of up to ten percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
+	<<set $menialDemandFactor -= 20000>>
 <<elseif $REM == "hostilities ended">>
 	The Free Cities make a real effort to avoid armed conflict, especially amongst themselves, as such endeavors almost never have any real winners. But tensions grew so high in their trade conflict that the likelihood of a full blown war between two Free Cities became not just a possibility but a near certainty for months. As skirmishes commenced and slave armies were quickly drilled for action on both sides, the slave market anticipated a boost to demand as soon as the fighting intensified. Miraculously, cooler heads prevailed and the Free Cities agreed to disband their armies. While many people sighed with relief the slave market was forced into a shock adjustment, projections show a short-term price drop of up to five percent. There will be no immediate impact on you or your slaves, but the coming weeks will be a fine time to buy new stock, and a terrible time to sell. <span class="noteworthy">The market price of slaves has dropped.</span>
-<</if>>
-
-<<if ["arcology change", "bankruptcy", "refugee boat", "revel"].includes($REM)>>
-	<<set $menialSupplyFactor += 12000>>
-<<elseif ["antislavery terrorism", "hostilities ended", "tainted drugs"].includes($REM)>>
 	<<set $menialDemandFactor -= 12000>>
-<<elseif ["empty prisons", "medical breakthrough", "new free city", "unemployment", "war"].includes($REM)>>
-	<<set $menialSupplyFactor += 20000>>
-<<elseif ["anti-slavery coup", "speculation"].includes($REM)>>
-	<<set $menialDemandFactor -= 20000>>
 <</if>>
-
 <<set $menialDemandFactor = Math.clamp($menialDemandFactor, -50000, 50000)>>
-<<set $slaveCostFactor = menialSlaveCost()/1000>>
\ No newline at end of file
+<<set $slaveCostFactor = menialSlaveCost()/1000>>
diff --git a/src/uncategorized/reputation.tw b/src/uncategorized/reputation.tw
index 5e99ce9a76f37bea098ddef72ec0967e549cd8fa..3ff287fcf7ff7307b8e4c1884871584e6251af74 100644
--- a/src/uncategorized/reputation.tw
+++ b/src/uncategorized/reputation.tw
@@ -525,7 +525,7 @@ _enduringRep = $enduringRep>>
 		Society @@.green;approves@@ of your advancing age, which advances the ancient Aztec ideal of an experienced leader of the people.
 		<<= FutureSocieties.Change("AztecRevivalist", 1)>>
 	<</if>>
-	<<if $HeadGirl == 0>>
+	<<if $HeadGirlID == 0>>
 		Society @@.red;disapproves@@ of you not having a Head Girl as an advisor and assistant.
 		<<= FutureSocieties.Change("AztecRevivalist", -2)>>
 	<<else>>
@@ -582,21 +582,21 @@ _enduringRep = $enduringRep>>
 		<<= FutureSocieties.Change("ArabianRevivalist", -2)>>
 	<</if>>
 <<elseif $arcologies[0].FSChineseRevivalist != "unset">>
-	<<if $HeadGirl == 0>>
+	<<if $HeadGirlID == 0>>
 		Society @@.red;disapproves@@ of your failure to rely on a Head Girl, as proper imperial administration requires,
 		<<= FutureSocieties.Change("ChineseRevivalist", -2)>>
 	<<else>>
 		Society @@.green;approves@@ of your reliance on a Head Girl, as proper imperial administration requires,
 		<<= FutureSocieties.Change("ChineseRevivalist", 2)>>
 	<</if>>
-	<<if $Recruiter == 0>>
+	<<if $RecruiterID == 0>>
 		@@.red;disapproves@@ of your failure to maintain a Recruiter to expand the Middle Kingdom,
 		<<= FutureSocieties.Change("ChineseRevivalist", -2)>>
 	<<else>>
 		@@.green;approves@@ of your maintaining a Recruiter to expand the Middle Kingdom,
 		<<= FutureSocieties.Change("ChineseRevivalist", 2)>>
 	<</if>>
-	<<if $Bodyguard == 0>>
+	<<if !_S.Bodyguard>>
 		and @@.red;disapproves@@ of your failure to keep a Bodyguard as befits a proper imperial palace.
 		<<= FutureSocieties.Change("ChineseRevivalist", -2)>>
 	<<else>>
diff --git a/src/uncategorized/resEndowment.tw b/src/uncategorized/resEndowment.tw
index 9c55bdde15b27b013af04f81234f502de3af2c3c..580c3895d9fd4c3b005008105064d994873cdbae 100644
--- a/src/uncategorized/resEndowment.tw
+++ b/src/uncategorized/resEndowment.tw
@@ -8,6 +8,10 @@
 	You receive a personal call from a senior representative of The Slavegirl School. It seems the school is raising funds, and since you've already <<if $TSS.schoolPresent == 0>>purchased $TSS.studentsBought of its graduates<<else>>encouraged them to open a branch campus in your arcology<</if>> and are known to be wealthy, you are an obvious potential donor. Though the Free Cities' slave schools are of course for-profit institutions, they do their best to maintain a veneer of public service, and cast their efforts to improve their product as a benefit to slaveowning society as a whole.
 	<br><br>
 	"A generous donation," the representative insists, "would help us advance our mission to provide good quality slaves at competitive prices." Getting down to the business advantages, he adds that "donors receive considerable price advantage on future graduates," 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.
+<<elseif $RESEndowment == "TUO">>
+	You receive a personal call from a senior representative of The Utopian Orphanage. It seems the institute is raising funds, and since you've already <<if $TUO.schoolPresent == 0>>purchased $TUO.studentsBought of its test subjects<<else>>encouraged them to open a branch campus in your arcology<</if>> and are known to be wealthy, you are an obvious potential donor. Though the Free Cities' slave schools and research institutions are of course for-profit organizations, they do their best to maintain a veneer of public service, and cast their efforts to improve their product as a benefit to slaveowning society as a whole.
+	<br><br>
+	"A generous donation," the representative insists, "would help us advance our mission to make slave children's dreams come true." Getting down to the business advantages, he adds that "donors receive considerable price advantage on future transactions," 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 institute policy to an extent.
 <<elseif $RESEndowment == "GRI">>
 	You receive a personal call from a senior representative of the Growth Research Institute. It seems the institute is raising funds, and since you've already <<if $GRI.schoolPresent == 0>>purchased $GRI.studentsBought of its test subjects<<else>>encouraged them to open a branch campus in your arcology<</if>> and are known to be wealthy, you are an obvious potential donor. Though the Free Cities' slave schools and research institutions are of course for-profit organizations, they do their best to maintain a veneer of public service, and cast their efforts to improve their product as a benefit to slaveowning society as a whole.
 	<br><br>
@@ -79,6 +83,32 @@
 			<<run repX(5000, "event")>>
 		<</replace>>
 	<</link>> //This will cost <<print cashFormat(10000)>>//
+<<elseif $RESEndowment == "TUO">>
+	<<link "Stricter admission criteria">>
+		<<replace "#result">>
+			You express your interest in making the orphanage's admission criteria stricter. You want them to only teach the most beautiful and intelligent children, and focus all attention on raising these select few. It spreads @@.green;word of your generosity,@@ using you as an example of investment in the future of slaveowning.
+			<<set $TUO.schoolUpgrade = 1>>
+			<<run cashX(-50000, "capEx")>>
+			<<run repX(7500, "event")>>
+		<</replace>>
+	<</link>> //This will cost <<print cashFormat(50000)>>//
+	<br>
+	<<link "Encourage basic, theoretical sexual education">>
+		<<replace "#result">>
+			You propose giving the children a theoretical sex education, claiming that without it, their education is incomplete. The representative considers it, and agrees that it would be beneficial. It spreads @@.green;word of your generosity,@@ using you as an example of investment in the future of slaveowning.
+			<<set $TUO.schoolUpgrade = 2>>
+			<<run cashX(-50000, "capEx")>>
+			<<run repX(7500, "event")>>
+		<</replace>>
+	<</link>> //This will cost <<print cashFormat(50000)>>//
+	<br>
+	<<link "Make a generous contribution">>
+		<<replace "#result">>
+			You make a generous contribution to the institute. It spreads @@.green;word of your generosity,@@ using you as an example of investment in the future of slaveowning.
+			<<run cashX(-10000, "capEx")>>
+			<<run repX(5000, "event")>>
+		<</replace>>
+	<</link>> //This will cost <<print cashFormat(10000)>>//
 <<elseif $RESEndowment == "GRI">>
 	<<link "Focus on curative research">>
 		<<replace "#result">>
diff --git a/src/uncategorized/resFailure.tw b/src/uncategorized/resFailure.tw
index 2f8fea7aadfd0654ce278c17875c801c9fa22bdc..f3f3b0ea9543403d665771c6e77b75502c1442fb 100644
--- a/src/uncategorized/resFailure.tw
+++ b/src/uncategorized/resFailure.tw
@@ -86,6 +86,62 @@
 		<<set $activeSlave.custom.tattoo = "$He has the simple logo of the corporation that operates the Slavegirl School tattooed on $his left cheek.">>
 		<<run newSlave($activeSlave)>> /* skip New Slave Intro */
 	<</for>>
+<<elseif $RESFailure == "TUO">>
+	<<set $TUO.schoolPresent = 0>>
+	<<set $TUO.subsidy = 0>>
+	<<set $TUO.schoolProsperity = 0>>
+	<<set $TUO.schoolAnnexed = 1>>
+	<<for $i = 0; $i < $slavesToAdd; $i++>>
+		<<set $activeSlaveOneTimeMinAge = $minimumSlaveAge>>
+		<<set $activeSlaveOneTimeMaxAge = $fertilityAge>>
+		<<set $oneTimeDisableDisability = 1>>
+		<<set $activeSlave = GenerateNewSlave()>>
+		<<set $activeSlave.origin = "$He was given to you by a failed branch of The Utopian Orphanage right after $his graduation.">>
+		<<set $activeSlave.career = "a slave">>
+		<<run setHealth($activeSlave, jsRandom(60, 80), 0, 0, 0)>>
+		<<set $activeSlave.devotion = random(50,75)>>
+		<<set $activeSlave.trust = random(50,75)>>
+		<<if $TUO.schoolUpgrade == 1>>
+			<<set $activeSlave.face = random(30,100)>>
+			<<set $activeSlave.intelligence = random(55,100)>>
+			<<set $activeSlave.intelligenceImplant = 30>>
+			<<set $activeSlave.accent = 1>>
+			<<set $activeSlave.skill.entertainment = 75>>
+			<<set $activeSlave.skill.combat = 1>>
+		<<else>>
+			<<set $activeSlave.face = random(10,65)>>
+			<<set $activeSlave.intelligenceImplant = 15>>
+			<<set $activeSlave.intelligence = random(35,75)>>
+			<<set $activeSlave.accent = either(0,1)>>
+			<<set $activeSlave.skill.entertainment = 45>>
+			<<set $activeSlave.skill.combat = 0>>
+		<</if>>
+		<<if $TUO.schoolUpgrade == 2>>
+			<<set $activeSlave.skill.vaginal = 15>>
+			<<set $activeSlave.skill.oral = 15>>
+			<<set $activeSlave.skill.anal = 15>>
+			<<set $activeSlave.skill.whoring = 15>>
+			<<set $activeSlave.energy = random(40,95)>>
+		<<else>>
+			<<set $activeSlave.skill.vaginal = 0>>
+			<<set $activeSlave.skill.oral = 0>>
+			<<set $activeSlave.skill.anal = 0>>
+			<<set $activeSlave.skill.whoring = 0>>
+			<<set $activeSlave.energy = random(15,65)>>
+		<</if>>
+		<<set $activeSlave.faceImplant = 0>>
+		<<set $activeSlave.weight = random(-17,17)>>
+		<<set $activeSlave.muscles = random(0,20)>>
+		<<set $activeSlave.lips = random(10,40)>>
+		<<set $activeSlave.lipsImplant = 0>>
+		<<set $activeSlave.boobs = 50>>
+		<<set $activeSlave.boobsImplant = 0>>
+		<<set $activeSlave.butt = random(0,2)>>
+		<<set $activeSlave.buttImplant = 0>>
+		<<set $activeSlave.vagina = 0>>
+		<<set $activeSlave.anus = 0>>
+		<<run newSlave($activeSlave)>> /* skip New Slave Intro */
+	<</for>>
 <<elseif $RESFailure == "TCR">>
 	<<set $TCR.schoolPresent = 0>>
 	<<set $TCR.subsidy = 0>>
diff --git a/src/uncategorized/resMove.tw b/src/uncategorized/resMove.tw
index e2b5bd826499af05e76ba46ea1184cd46f3d7bbb..d80a617256722647e0637513924a1511b7d345a9 100644
--- a/src/uncategorized/resMove.tw
+++ b/src/uncategorized/resMove.tw
@@ -17,6 +17,8 @@
 
 <<if $RESMove == "TSS">>
 	You receive a personal call from a senior representative of The Slavegirl School. "_playerName," he says without preamble, "The Slavegirl School would like to open a branch campus in $arcologies[0].name. We'd like to ask for a significant reduction in rent from your standard rate, since we could bring significant benefits to you through our presence." After some further pleasantries, he urges you to consider the offer, wishes you a pleasant day, and ends the call.
+<<elseif $RESMove == "TUO">>
+	You receive a personal call from a senior representative of The Utopian Orphanage. "_playerName," she says without preamble, "The Utopian Orphanage would like to open a branch campus in $arcologies[0].name. We'd like to ask for a significant reduction in rent from your standard rate, since we could bring significant benefits to you through our presence." After some further pleasantries, he urges you to consider the offer, wishes you a pleasant day, and ends the call.
 <<elseif $RESMove == "GRI">>
 	You receive a personal call from a senior representative of the Growth Research Institute. "_playerName," he says without preamble, "GRI would like to open a subsidiary lab in $arcologies[0].name. We'd like to ask for a significant reduction in rent from your standard rate, since we could bring significant benefits to you through our presence." After some further pleasantries, he urges you to consider the offer, wishes you a pleasant day, and ends the call.
 <<elseif $RESMove == "SCP">>
@@ -298,6 +300,8 @@ as you suspected,
 Your assistant is a powerful analytic tool, but _heA lacks the capacity for true creativity. _HeA did not mention the long term. It would be possible to support the
 <<if ($RESMove == "TSS") || ($RESMove == "SCP") || ($RESMove == "LDE") || ($RESMove == "TGA") || ($RESMove == "HA") || ($RESMove == "NUL")>>
 	school's
+<<elseif $RESMove == "TUO">>
+	orphanage's
 <<elseif $RESMove == "GRI">>
 	lab's
 <<elseif $RESMove == "TCR">>
@@ -315,6 +319,8 @@ presence in the arcology, reaping all sorts of benefits. Alternatively, you migh
 		You call them back and accept the offer. They forward their specifications for an appropriate space and the modifications it needs to host them, along with their thanks.
 		<<if $RESMove == "TSS">>
 			<<set $TSS.schoolPresent = 1>>
+		<<elseif $RESMove == "TUO">>
+			<<set $TUO.schoolPresent = 1>>
 		<<elseif $RESMove == "GRI">>
 			<<set $GRI.schoolPresent = 1>>
 		<<elseif $RESMove == "SCP">>
diff --git a/src/uncategorized/resSale.tw b/src/uncategorized/resSale.tw
index 3b8c23b87268e7d78003aaa220446bbe4be36407..b28f0f1e705083a17ada73e12755cfbea70d0cd6 100644
--- a/src/uncategorized/resSale.tw
+++ b/src/uncategorized/resSale.tw
@@ -11,6 +11,12 @@
 	<<set $activeSlave = _marketResult.slave>>
 	<<print _marketResult.text>>
 	<<set $activeSlave.clothes = "a schoolgirl outfit">>
+<<elseif $RESSale == "TUO">>
+	<<set $TUO.schoolSale = 1>>
+	<<set _marketResult = generateMarketSlave("TUO")>>
+	<<set $activeSlave = _marketResult.slave>>
+	<<print _marketResult.text>>
+	<<set $activeSlave.clothes = "nice business attire">>
 <<elseif $RESSale == "GRI">>
 	<<set $GRI.schoolSale = 1>>
 	<<set _marketResult = generateMarketSlave("GRI")>>
@@ -77,6 +83,8 @@
 You receive a personal call from
 <<if $RESSale == "TSS">>
 	The Slavegirl School, a rising slave training organization known for its reliably unremarkable products, offered at a competitive price.
+<<elseif $RESSale == "TUO">>
+	The Utopian Orphanage, a school that educates intelligent slaves until they reach their majority.
 <<elseif $RESSale == "GRI">>
 	The Growth Research Institute, a drug development group breaking new ground on human experimentation.
 <<elseif $RESSale == "SCP">>
@@ -106,6 +114,8 @@ It's from a piece of merchandise, not leadership, and $he's offering $himself fo
 You pause for a moment, and $he plunges on: "<<if $PC.title != 0>>Sir<<else>>Ma'am<</if>>, @@.yellow;your first slave purchase from us this week is half price.@@ And, <<if $PC.title != 0>>sir<<else>>ma'am<</if>>, I'm one of the slaves for sale.
 <<if $RESSale == "TSS">>
 	I've got the very best skills I could learn as a virgin. I'm healthy, obedient, and educated. And I'm fresh, and willing, and really eager." $He rips $his white school<<= $girl>> blouse open to show off a fresh pair of tits, and shakes them for you. "I would love to be your sex slave, <<if $PC.title != 0>>sir<<else>>ma'am<</if>>," $he says, doing $his very best to sound appealing, like $he's been trained.
+<<elseif $RESSale == "TUO">>
+	I'm smart and cute. I'm well educated and obedient, I will serve you well." $He blushes and maintains eye contact as he continues. "I'm unexperienced when it comes to sexual matters, I hope you will treat me well." $He bows to you and finished. "I would love to be your slave, <<if $PC.title != 0>>sir<<else>>ma'am<</if>>," $he says, doing $his very best to sound appealing, like $he's been taught.
 <<elseif $RESSale == "GRI">>
 	I've, uh, been trained to obey." $His eyes flick to one side, like $he's reading $his cues. "And, um, I have really big boobs." $He's wearing a bathrobe, and $he suddenly jerks it open to reveal a bigger pair of breasts than anyone that age could possibly have grown $himself. "I would love to be your sex slave, <<if $PC.title != 0>>sir<<else>>ma'am<</if>>," $he says, doing $his very best to sound appealing. No doubt $he'd prefer not to be part of any more testing.
 <<elseif $RESSale == "SCP">>
diff --git a/src/uncategorized/saDiet.tw b/src/uncategorized/saDiet.tw
index cb3412be07d4a3be4387af25431f46716d977615..d970d99900e42e86a0daa6d952ae296344623946 100644
--- a/src/uncategorized/saDiet.tw
+++ b/src/uncategorized/saDiet.tw
@@ -495,11 +495,7 @@
 		$He is no longer capable of working out in any plausible way. $His special diet @@.yellow;has ended.@@
 		<<set $slaves[$i].diet = "healthy">>
 	<<else>>
-		<<if $slaves[$i].muscles > 90>>
-			$His heavy workouts focus on lifting. $He has plateaued at a state of @@.lime;goddess-like musculature.@@
-			<<set $slaves[$i].muscles = 100>>
-			<<set $slaves[$i].diet = "healthy">>
-		<<elseif ($slaves[$i].geneticQuirks.mGain == 2)>>
+		<<if ($slaves[$i].geneticQuirks.mGain == 2)>>
 			$His heavy workouts focus on lifting,
 			<<if $geneticMappingUpgrade >= 1>>
 				and with $his myotonic hypertrophy,
@@ -507,7 +503,7 @@
 				and despite run-of-the-mill routines,
 			<</if>>
 			$he @@.lime;gains muscle rapidly.@@
-			<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles+10, -100, 100)>>
+			<<set $slaves[$i].muscles += 10>>
 		<<elseif ($slaves[$i].drugs == "steroids")>>
 			$His heavy workouts focus on lifting, and since $he's on so much gear, $he @@.lime;gains muscle rapidly.@@
 			<<set $slaves[$i].muscles += 8+$slaves[$i].geneticQuirks.mGain>>
@@ -576,17 +572,19 @@
 				<<set $slaves[$i].weight -= 2>>
 			<</if>>
 		<</if>>
+		<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles, -100, 100)>>
+		<<if $slaves[$i].muscles >= 100>>
+			$He has plateaued at a state of @@.lime;goddess-like musculature.@@
+			<<set $slaves[$i].muscles = 100>>
+			<<set $slaves[$i].diet = "healthy">>
+		<</if>>
 	<</if>>
 <<case "slimming">> /* Muscle Loss */
 	<<if !canWalk($slaves[$i])>>
 		$He is no longer capable of actively working out. $His special diet @@.yellow;has ended.@@
 		<<set $slaves[$i].diet = "healthy">>
-	<<elseif $slaves[$i].muscles > 5>>
-		<<if $slaves[$i].muscles <= 10>>
-			$His long workouts focus on cardio. $He has finally @@.orange;lost all visible musculature.@@
-			<<set $slaves[$i].muscles = 0>>
-			<<set $slaves[$i].diet = "healthy">>
-		<<elseif ($slaves[$i].geneticQuirks.mLoss == 2)>>
+	<<elseif $slaves[$i].muscles > 0>>
+		<<if ($slaves[$i].geneticQuirks.mLoss == 2)>>
 			$His long workouts focus on cardio,
 			<<if $geneticMappingUpgrade >= 1>>
 				and with $his myotonic dystrophy,
@@ -663,6 +661,12 @@
 				<<set $slaves[$i].weight -= 2>>
 			<</if>>
 		<</if>>
+		<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles, 0, 100)>>
+		<<if $slaves[$i].muscles <= 0>>
+			$He has finally @@.orange;lost all visible musculature.@@
+			<<set $slaves[$i].muscles = 0>>
+			<<set $slaves[$i].diet = "healthy">>
+		<</if>>
 	<<else>>
 		$His long workouts focus on cardio to keep $his body lithe.
 		<<if ($slaves[$i].behavioralQuirk == "fitness")>>
@@ -1304,13 +1308,9 @@
 	<</if>>
 <<case "muscle building">> /* Muscle Gain */
 	Fuckdoll suits can force their inhabitants to <<if hasAnyArms($slaves[$i])>>lift weights<<else>>exercise<</if>> until they drop.
-	<<if $slaves[$i].muscles > 90>>
-		$He has plateaued at a state of @@.lime;perfect musculature.@@
-		<<set $slaves[$i].muscles = 100>>
-		<<set $slaves[$i].diet = "healthy">>
-	<<elseif ($slaves[$i].geneticQuirks.mGain == 2)>>
+	<<if ($slaves[$i].geneticQuirks.mGain == 2)>>
 		$He @@.lime;explosively builds muscle<<if $geneticMappingUpgrade >= 1>>@@ aided by $his myotonic hypertrophy.<<else>>.@@<</if>>
-		<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles+15, 0, 100)>>
+		<<set $slaves[$i].muscles += 15>>
 	<<elseif ($slaves[$i].geneticQuirks.mLoss == 2)>>
 		$He @@.lime;slowly gains muscle<<if $geneticMappingUpgrade >= 1>>@@ due to $his myotonic dystrophy.<<else>>.@@<</if>>
 		<<set $slaves[$i].muscles += 4>>
@@ -1334,14 +1334,16 @@
 			<<set $slaves[$i].weight -= 2>>
 		<</if>>
 	<</if>>
+	<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles, -100, 100)>>
+	<<if $slaves[$i].muscles >= 100>>
+		$He has plateaued at a state of @@.lime;perfect musculature.@@
+		<<set $slaves[$i].muscles = 100>>
+		<<set $slaves[$i].diet = "healthy">>
+	<</if>>
 	The stress of forced exercise is trivial compared to everything else $he experiences, and $he's unaffected mentally.
 <<case "slimming">> /* Muscle Loss */
 	Fuckdoll suits can force their inhabitants to <<if hasAnyArms($slaves[$i])>>lift weights<<else>>exercise<</if>> until they drop.
-	<<if $slaves[$i].muscles <= 10>>
-		$He has finally @@.lime;lost all visible musculature.@@
-		<<set $slaves[$i].muscles = 0>>
-		<<set $slaves[$i].diet = "healthy">>
-	<<elseif ($slaves[$i].geneticQuirks.mLoss == 2)>>
+	<<if ($slaves[$i].geneticQuirks.mLoss == 2)>>
 		$He @@.lime;rapidly sheds muscle<<if $geneticMappingUpgrade >= 1>>@@ aided by $his myotonic dystrophy.<<else>>.@@<</if>>
 		<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles-15, -100, 100)>>
 	<<elseif ($slaves[$i].geneticQuirks.mGain == 2)>>
@@ -1367,6 +1369,12 @@
 			<<set $slaves[$i].weight -= 2>>
 		<</if>>
 	<</if>>
+	<<set $slaves[$i].muscles = Math.clamp($slaves[$i].muscles, 0, 100)>>
+	<<if $slaves[$i].muscles <= 0>>
+		$He has finally @@.lime;lost all visible musculature.@@
+		<<set $slaves[$i].muscles = 0>>
+		<<set $slaves[$i].diet = "healthy">>
+	<</if>>
 	The stress of forced exercise is trivial compared to everything else $he experiences, and $he's unaffected mentally.
 
 <<case "cum production">>
diff --git a/src/uncategorized/saLiveWithHG.tw b/src/uncategorized/saLiveWithHG.tw
index 0bcfe68a2dab80763b62519d42ee48de3170cdf3..32f698d9ce680470d37e41a763df0b0e8bdf736d 100644
--- a/src/uncategorized/saLiveWithHG.tw
+++ b/src/uncategorized/saLiveWithHG.tw
@@ -1,6 +1,5 @@
 :: SA live with HG [nobr]
 
-<<set $HeadGirl = $slaves[_iTemp]>>
 <<set _oralUse = 0, _analUse = 0, _vaginalUse = 0, _mammaryUse = 0, _penetrativeUse = 0>>
 
 <<if $seeImages && $seeReportImages>>
@@ -10,7 +9,7 @@
 <</if>>
 
 <<setLocalPronouns $slaves[$i]>>
-<<setLocalPronouns $HeadGirl 2>>
+<<setLocalPronouns _S.HeadGirl 2>>
 
 /* run this up here so that it affects things better */
 <<if $arcologies[0].FSSlimnessEnthusiast != "unset">>
@@ -29,7 +28,7 @@
 	<<set $HGTastes = 2>>
 <</if>>
 <<set _diet = $slaves[$i].diet>>
-<<if ($arcologies[0].FSRepopulationFocusPregPolicy == 1 || ($arcologies[0].FSRestart == "unset" && $HeadGirl.fetish == "pregnancy")) && canImpreg($slaves[$i], $HeadGirl) && ($dietFertility == 1)>>
+<<if ($arcologies[0].FSRepopulationFocusPregPolicy == 1 || ($arcologies[0].FSRestart == "unset" && _S.HeadGirl.fetish == "pregnancy")) && canImpreg($slaves[$i], _S.HeadGirl) && ($dietFertility == 1)>>
 	<<if $slaves[$i].diet != "fertility">>
 		<<set $slaves[$i].diet = "fertility">>
 	<</if>>
@@ -41,11 +40,11 @@
 	<<if $slaves[$i].diet != "restricted">>
 		<<set $slaves[$i].diet = "restricted">>
 	<</if>>
-<<elseif (($slaves[$i].muscles <= 95 && (($arcologies[0].FSPhysicalIdealist > 0 && $arcologies[0].FSPhysicalIdealistLaw == 0) || (($HeadGirl.fetish == "submissive" || $HeadGirl.fetish == "masochist") && $HeadGirl.fetishKnown == 1))) || ($slaves[$i].muscles <= 45 && ($arcologies[0].FSPhysicalIdealist > 0 && $arcologies[0].FSPhysicalIdealistLaw == 1)) || $slaves[$i].muscles <= 5) && !isAmputee($slaves[$i])>>
+<<elseif (($slaves[$i].muscles <= 95 && (($arcologies[0].FSPhysicalIdealist > 0 && $arcologies[0].FSPhysicalIdealistLaw == 0) || ((_S.HeadGirl.fetish == "submissive" || _S.HeadGirl.fetish == "masochist") && _S.HeadGirl.fetishKnown == 1))) || ($slaves[$i].muscles <= 45 && ($arcologies[0].FSPhysicalIdealist > 0 && $arcologies[0].FSPhysicalIdealistLaw == 1)) || $slaves[$i].muscles <= 5) && !isAmputee($slaves[$i])>>
 	<<if $slaves[$i].diet != "muscle building">>
 		<<set $slaves[$i].diet = "muscle building">>
 	<</if>>
-<<elseif $slaves[$i].balls > 0 && $HeadGirl.fetish == "cumslut" && $HeadGirl.fetishKnown == 1 && $cumProDiet == 1>>
+<<elseif $slaves[$i].balls > 0 && _S.HeadGirl.fetish == "cumslut" && _S.HeadGirl.fetishKnown == 1 && $cumProDiet == 1>>
 	<<if $slaves[$i].diet != "cum production">>
 		<<set $slaves[$i].diet = "cum production">>
 	<</if>>
@@ -66,23 +65,23 @@
 		<<set $slaves[$i].drugs = "lip injections">>
 	<<elseif $arcologies[0].FSSlaveProfessionalismResearch == 1 && $HGSuiteEquality != 0 && canImproveIntelligence($slaves[$i])>>
 		<<set $slaves[$i].drugs = "psychostimulants">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 18>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 18>>
 		<<set $slaves[$i].drugs = "hyper butt injections">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 8>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 8>>
 		<<set $slaves[$i].drugs = "butt injections">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "cumslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].balls < 100 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "cumslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].balls < 100 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
 		<<set $slaves[$i].drugs = "hyper testicle enhancement">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "cumslut") && $slaves[$i].balls < 10 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "cumslut") && $slaves[$i].balls < 10 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
 		<<set $slaves[$i].drugs = "testicle enhancement">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "masochist" || $HeadGirl.fetish == "submissive") && canImproveHeight($slaves[$i])>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "masochist" || _S.HeadGirl.fetish == "submissive") && canImproveHeight($slaves[$i])>>
 		<<set $slaves[$i].drugs = "growth stimulants">>
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "boobs") && ($slaves[$i].boobs < 8000)>>
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "boobs") && ($slaves[$i].boobs < 8000)>>
 		<<if $arcologies[0].FSAssetExpansionistResearch == 1>>
 			<<set $slaves[$i].drugs = "hyper breast injections">>
 		<<else>>
 			<<set $slaves[$i].drugs = "breast injections">>
 		<</if>>
-	<<elseif canImpreg($slaves[$i], $HeadGirl) && $arcologies[0].FSRestart == "unset" && ($HGTastes == 4 || $HeadGirl.fetish == "pregnancy" || $arcologies[0].FSRepopulationFocusPregPolicy == 1)>>
+	<<elseif canImpreg($slaves[$i], _S.HeadGirl) && $arcologies[0].FSRestart == "unset" && ($HGTastes == 4 || _S.HeadGirl.fetish == "pregnancy" || $arcologies[0].FSRepopulationFocusPregPolicy == 1)>>
 		<<set $slaves[$i].drugs = "fertility drugs">>
 	<<elseif $HGTastes > 1>>
 		<<if ($slaves[$i].lips <= 70)>>
@@ -105,26 +104,26 @@
 
 <<if $slaves[$i].devotion > 20>>
 	<<if $HGSlaveSuccess == 1>>
-		looks after your Head Girl <<= SlaveFullName($HeadGirl)>> domestically and sexually, freeing _him2 to devote more of _his2 time to looking after your other slaves.
+		looks after your Head Girl <<= SlaveFullName(_S.HeadGirl)>> domestically and sexually, freeing _him2 to devote more of _his2 time to looking after your other slaves.
 	<<else>>
-		does $his best to look after your Head Girl <<= SlaveFullName($HeadGirl)>> domestically and sexually, but doesn't free up much of _his2 time this week.
+		does $his best to look after your Head Girl <<= SlaveFullName(_S.HeadGirl)>> domestically and sexually, but doesn't free up much of _his2 time this week.
 	<</if>>
 <<elseif $slaves[$i].trust < -20>>
 	<<if $HGSlaveSuccess == 1>>
-		obeys Head Girl <<= SlaveFullName($HeadGirl)>> domestically and sexually, freeing _him2 to devote more of _his2 time to looking after your other slaves.
+		obeys Head Girl <<= SlaveFullName(_S.HeadGirl)>> domestically and sexually, freeing _him2 to devote more of _his2 time to looking after your other slaves.
 	<<else>>
-		tries to look after your Head Girl <<= SlaveFullName($HeadGirl)>> domestically and sexually, but doesn't free up much of _his2 time this week.
+		tries to look after your Head Girl <<= SlaveFullName(_S.HeadGirl)>> domestically and sexually, but doesn't free up much of _his2 time this week.
 	<</if>>
 <<else>>
 	<<if $HGSlaveSuccess == 1>>
-		resists Head Girl <<= SlaveFullName($HeadGirl)>>'s domestic and sexual use, but still frees _him2 to devote more of _his2 time to looking after your other slaves.
+		resists Head Girl <<= SlaveFullName(_S.HeadGirl)>>'s domestic and sexual use, but still frees _him2 to devote more of _his2 time to looking after your other slaves.
 	<<else>>
-		resists Head Girl <<= SlaveFullName($HeadGirl)>>'s domestic and sexual use, and doesn't free up much of _his2 time this week.
+		resists Head Girl <<= SlaveFullName(_S.HeadGirl)>>'s domestic and sexual use, and doesn't free up much of _his2 time this week.
 	<</if>>
 <</if>>
 
-<<if ($HeadGirl.fetish == "sadist") && ($HeadGirl.fetishStrength > 60) && ($HeadGirl.fetishKnown == 1)>>
-	Since $HeadGirl.slaveName is a sadist, poor $slaves[$i].slaveName lives a life of fear and pain.
+<<if (_S.HeadGirl.fetish == "sadist") && (_S.HeadGirl.fetishStrength > 60) && (_S.HeadGirl.fetishKnown == 1)>>
+	Since _S.HeadGirl.slaveName is a sadist, poor $slaves[$i].slaveName lives a life of fear and pain.
 	<<if ($slaves[$i].energy > 95)>>
 		Though it's hard, $he manages to find @@.hotpink;sexual satisfaction@@ being a pain slave.
 		<<set $slaves[$i].devotion += 4>>
@@ -152,39 +151,39 @@
 	<</if>>
 <<elseif ($slaves[$i].devotion < -20)>>
 	<<if ($slaves[$i].trust >= -20)>>
-		Since $he is not yet obedient, $HeadGirl.slaveName @@.gold;punishes@@ $him constantly, training $him to be a decent assistant or suffer pain.
+		Since $he is not yet obedient, _S.HeadGirl.slaveName @@.gold;punishes@@ $him constantly, training $him to be a decent assistant or suffer pain.
 		<<set $slaves[$i].trust -= 5>>
 	<<else>>
-		Since $he isn't suitably broken into slavery yet, $HeadGirl.slaveName trains $him to be @@.hotpink;obedient@@ or suffer pain.
+		Since $he isn't suitably broken into slavery yet, _S.HeadGirl.slaveName trains $him to be @@.hotpink;obedient@@ or suffer pain.
 		<<set $slaves[$i].devotion += 5>>
 	<</if>>
 <<elseif ($slaves[$i].devotion <= 20)>>
-	Since $he is not yet well trained, $HeadGirl.slaveName instructs $him, @@.hotpink;training@@ $him to be a proper assistant.
+	Since $he is not yet well trained, _S.HeadGirl.slaveName instructs $him, @@.hotpink;training@@ $him to be a proper assistant.
 	<<set $slaves[$i].devotion += 4>>
 <<elseif ($slaves[$i].devotion <= 50)>>
-	Since $he is not yet a devoted slave, $HeadGirl.slaveName works with $him, @@.hotpink;training@@ $him to be a good assistant.
+	Since $he is not yet a devoted slave, _S.HeadGirl.slaveName works with $him, @@.hotpink;training@@ $him to be a good assistant.
 	<<set $slaves[$i].devotion += 2>>
 <<else>>
-	Since $he is a devoted assistant, $HeadGirl.slaveName @@.hotpink;praises@@ and encourages $him.
+	Since $he is a devoted assistant, _S.HeadGirl.slaveName @@.hotpink;praises@@ and encourages $him.
 	<<set $slaves[$i].devotion += 1>>
 <</if>>
 
-<<if ($HeadGirl.rivalry != 0) && ($HeadGirl.rivalryTarget == $slaves[$i].ID)>>
-	$HeadGirl.slaveName @@.lightgreen;resolves the rivalry@@ between _him2 and $slaves[$i].slaveName, mostly by the expedient of punishing $slaves[$i].slaveName savagely whenever $he gives any trouble.
-	<<set $HeadGirl.rivalry = 0, $HeadGirl.rivalryTarget = 0, $slaves[$i].rivalry = 0, $slaves[$i].rivalryTarget = 0>>
-<<elseif ($HeadGirl.relationship == 0)>>
-	<<if areRelated($slaves[$i], $HeadGirl)>>
-		$HeadGirl.slaveName
-		<<if $HeadGirl.devotion > 95>>
+<<if (_S.HeadGirl.rivalry != 0) && (_S.HeadGirl.rivalryTarget == $slaves[$i].ID)>>
+	_S.HeadGirl.slaveName @@.lightgreen;resolves the rivalry@@ between _him2 and $slaves[$i].slaveName, mostly by the expedient of punishing $slaves[$i].slaveName savagely whenever $he gives any trouble.
+	<<set _S.HeadGirl.rivalry = 0, _S.HeadGirl.rivalryTarget = 0, $slaves[$i].rivalry = 0, $slaves[$i].rivalryTarget = 0>>
+<<elseif (_S.HeadGirl.relationship == 0)>>
+	<<if areRelated($slaves[$i], _S.HeadGirl)>>
+		_S.HeadGirl.slaveName
+		<<if _S.HeadGirl.devotion > 95>>
 			is so inured to the perversities of slavery that _he2 @@.hotpink;enjoys@@ the prospect of sexual service from _his2
-			<<set $HeadGirl.devotion += 1>>
-		<<elseif $HeadGirl.devotion > 60>>
+			<<set _S.HeadGirl.devotion += 1>>
+		<<elseif _S.HeadGirl.devotion > 60>>
 			is so devoted to you that _he2 accepts the prospect of sexual service from _his2
 		<<else>>
 			is somewhat @@.mediumorchid;disturbed@@ by the prospect of sexual service from _his2
-			<<set $HeadGirl.devotion -= 2>>
+			<<set _S.HeadGirl.devotion -= 2>>
 		<</if>>
-		<<print relativeTerm($HeadGirl, $slaves[$i])>>.
+		<<print relativeTerm(_S.HeadGirl, $slaves[$i])>>.
 		$slaves[$i].slaveName
 		<<if $slaves[$i].devotion > 95>>
 			is so inured to the perversities of slavery that $he @@.hotpink;enjoys@@ the prospect of sexually servicing $his
@@ -204,109 +203,109 @@
 			is @@.mediumorchid;revolted@@ by the prospect of sexually servicing $his
 			<<set $slaves[$i].devotion -= 10>>
 		<</if>>
-		<<print relativeTerm($slaves[$i], $HeadGirl)>>.
+		<<print relativeTerm($slaves[$i], _S.HeadGirl)>>.
 	<</if>>
 	<<if ($slaves[$i].relationship == 0) && ($slaves[$i].devotion > 20)>>
-		$slaves[$i].slaveName sees $HeadGirl.slaveName constantly, and does $his best to @@.lightgreen;become _his2 friend.@@
-		<<set $HeadGirl.relationship = 1, $HeadGirl.relationshipTarget = $slaves[$i].ID, $slaves[$i].relationship = 1, $slaves[$i].relationshipTarget = $HeadGirl.ID>>
+		$slaves[$i].slaveName sees _S.HeadGirl.slaveName constantly, and does $his best to @@.lightgreen;become _his2 friend.@@
+		<<set _S.HeadGirl.relationship = 1, _S.HeadGirl.relationshipTarget = $slaves[$i].ID, $slaves[$i].relationship = 1, $slaves[$i].relationshipTarget = $HeadGirlID>>
 	<<elseif ($slaves[$i].relationship == -2) && ($slaves[$i].devotion > 20) && ($slaves[$i].devotion > 50)>>
-		$slaves[$i].slaveName sees $HeadGirl.slaveName so much more than $he sees you and begins to warm up to _him2 instead of you, @@.lightgreen;becoming _his2 friend.@@
-		<<set $HeadGirl.relationship = 1, $HeadGirl.relationshipTarget = $slaves[$i].ID, $slaves[$i].relationship = 1, $slaves[$i].relationshipTarget = $HeadGirl.ID>>
-	<<elseif ($slaves[$i].relationship != 5) && ($slaves[$i].relationship > 0) && ($slaves[$i].relationshipTarget != $HeadGirl.ID)>>
+		$slaves[$i].slaveName sees _S.HeadGirl.slaveName so much more than $he sees you and begins to warm up to _him2 instead of you, @@.lightgreen;becoming _his2 friend.@@
+		<<set _S.HeadGirl.relationship = 1, _S.HeadGirl.relationshipTarget = $slaves[$i].ID, $slaves[$i].relationship = 1, $slaves[$i].relationshipTarget = $HeadGirlID>>
+	<<elseif ($slaves[$i].relationship != 5) && ($slaves[$i].relationship > 0) && ($slaves[$i].relationshipTarget != $HeadGirlID)>>
 		<<set _j = $slaves.findIndex(function(s) { return s.ID == $slaves[$i].relationshipTarget; })>>
-		$slaves[$i].slaveName, as $HeadGirl.slaveName's bitch, knows $slaves[_j].slaveName cannot compete with the Head Girl, and so their relationship fades.
+		$slaves[$i].slaveName, as _S.HeadGirl.slaveName's bitch, knows $slaves[_j].slaveName cannot compete with the Head Girl, and so their relationship fades.
 		<<set $slaves[$i].relationship = 0, $slaves[$i].relationshipTarget = 0, $slaves[_j].relationship = 0, $slaves[_j].relationshipTarget = 0>>
 	<</if>>
 <<else>>
-	<<if ($HeadGirl.relationshipTarget == $slaves[$i].ID)>>
-		<<set $slaves[$i].trust += 1, $HeadGirl.trust += 1>>
+	<<if (_S.HeadGirl.relationshipTarget == $slaves[$i].ID)>>
+		<<set $slaves[$i].trust += 1, _S.HeadGirl.trust += 1>>
 		<<if $slaves[$i].relationship <= 2>>
-			Since $HeadGirl.slaveName and $slaves[$i].slaveName are already friends, they're @@.mediumaquamarine;thankful@@ to you for allowing it. After a few nights of offering each other sexual comfort, they consider themselves @@.lightgreen;friends with benefits.@@
-			<<set $HeadGirl.relationship = 3, $HeadGirl.relationshipTarget = $slaves[$i].ID>>
-			<<set $slaves[$i].relationship = 3, $slaves[$i].relationshipTarget = $HeadGirl.ID>>
+			Since _S.HeadGirl.slaveName and $slaves[$i].slaveName are already friends, they're @@.mediumaquamarine;thankful@@ to you for allowing it. After a few nights of offering each other sexual comfort, they consider themselves @@.lightgreen;friends with benefits.@@
+			<<set _S.HeadGirl.relationship = 3, _S.HeadGirl.relationshipTarget = $slaves[$i].ID>>
+			<<set $slaves[$i].relationship = 3, $slaves[$i].relationshipTarget = $HeadGirlID>>
 		<<elseif $slaves[$i].relationship <= 3>>
-			Since $HeadGirl.slaveName and $slaves[$i].slaveName are already lovers, they both @@.hotpink;enjoy@@ living together and are @@.mediumaquamarine;thankful@@ to you for allowing it. After living together for a few days, they draw closer emotionally, too, and consider themselves @@.lightgreen;lovers.@@
-			<<set $HeadGirl.relationship = 4, $HeadGirl.relationshipTarget = $slaves[$i].ID>>
-			<<set $slaves[$i].relationship = 4, $slaves[$i].relationshipTarget = $HeadGirl.ID>>
+			Since _S.HeadGirl.slaveName and $slaves[$i].slaveName are already lovers, they both @@.hotpink;enjoy@@ living together and are @@.mediumaquamarine;thankful@@ to you for allowing it. After living together for a few days, they draw closer emotionally, too, and consider themselves @@.lightgreen;lovers.@@
+			<<set _S.HeadGirl.relationship = 4, _S.HeadGirl.relationshipTarget = $slaves[$i].ID>>
+			<<set $slaves[$i].relationship = 4, $slaves[$i].relationshipTarget = $HeadGirlID>>
 		<<elseif $slaves[$i].relationship <= 4>>
-			<<if ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetishStrength <= 60) && ($HeadGirl.fetish == "sadist") && ($slaves[$i].fetish != "masochist")>>
-				Since their relationship is very abusive, only $HeadGirl.slaveName @@.hotpink;enjoys@@ living together with $slaves[$i].slaveName.
+			<<if (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetishStrength <= 60) && (_S.HeadGirl.fetish == "sadist") && ($slaves[$i].fetish != "masochist")>>
+				Since their relationship is very abusive, only _S.HeadGirl.slaveName @@.hotpink;enjoys@@ living together with $slaves[$i].slaveName.
 				<<set $slaves[$i].devotion -= 5, $slaves[$i].trust -= 5>>
 			<<else>>
-				Since $HeadGirl.slaveName and $slaves[$i].slaveName are lovers, they @@.hotpink;enjoy@@ living together.
+				Since _S.HeadGirl.slaveName and $slaves[$i].slaveName are lovers, they @@.hotpink;enjoy@@ living together.
 			<</if>>
 		<<else>>
-			Since $HeadGirl.slaveName and $slaves[$i].slaveName are <<if $wife == _wife2>>slave $wives<<else>>married<</if>>, they @@.hotpink;enjoy@@ living together.
+			Since _S.HeadGirl.slaveName and $slaves[$i].slaveName are <<if $wife == _wife2>>slave $wives<<else>>married<</if>>, they @@.hotpink;enjoy@@ living together.
 		<</if>>
 	<</if>>
 <</if>>
 
 <<set _skillIncrease = 10>>
 <<if ($slaves[$i].skill.oral <= 30)>>
-	$HeadGirl.slaveName wants to come home to a talented tongue, so _he2 trains $slaves[$i].slaveName's oral skills.
+	_S.HeadGirl.slaveName wants to come home to a talented tongue, so _he2 trains $slaves[$i].slaveName's oral skills.
 	<<= SkillIncrease.Oral($slaves[$i], _skillIncrease)>>
 <<elseif ($slaves[$i].vagina > 0) && ($slaves[$i].skill.vaginal <= 30)>>
-	$HeadGirl.slaveName wants _his2 personal sex slave to be as skilled with $his pussy as possible, so _he2 trains $slaves[$i].slaveName's vaginal skills.
+	_S.HeadGirl.slaveName wants _his2 personal sex slave to be as skilled with $his pussy as possible, so _he2 trains $slaves[$i].slaveName's vaginal skills.
 	<<= SkillIncrease.Vaginal($slaves[$i], _skillIncrease)>>
 <<elseif ($slaves[$i].anus > 0) && ($slaves[$i].skill.anal <= 30)>>
-	$HeadGirl.slaveName wants _his2 personal sex slave to be a skillful backdoor whore, so _he2 trains $slaves[$i].slaveName's anal skills.
+	_S.HeadGirl.slaveName wants _his2 personal sex slave to be a skillful backdoor whore, so _he2 trains $slaves[$i].slaveName's anal skills.
 	<<= SkillIncrease.Anal($slaves[$i], _skillIncrease)>>
 <<elseif ($slaves[$i].skill.entertainment <= 10)>>
-	$HeadGirl.slaveName wants to be kept amused in _his2 occasional free time, so _he2 trains $slaves[$i].slaveName's entertainment skills.
+	_S.HeadGirl.slaveName wants to be kept amused in _his2 occasional free time, so _he2 trains $slaves[$i].slaveName's entertainment skills.
 	<<= SkillIncrease.Entertain($slaves[$i], _skillIncrease)>>
 <<elseif ($slaves[$i].sexualFlaw != "none")>>
-	$HeadGirl.slaveName is annoyed by $slaves[$i].slaveName's sexual flaws, so _he2 @@.green;trains@@ $him out of them.
+	_S.HeadGirl.slaveName is annoyed by $slaves[$i].slaveName's sexual flaws, so _he2 @@.green;trains@@ $him out of them.
 	<<set $slaves[$i].sexualFlaw = "none">>
 <<elseif ($slaves[$i].behavioralFlaw != "none")>>
-	$HeadGirl.slaveName is irritated by $slaves[$i].slaveName's flaws, so _he2 @@.green;trains@@ $him out of them.
+	_S.HeadGirl.slaveName is irritated by $slaves[$i].slaveName's flaws, so _he2 @@.green;trains@@ $him out of them.
 	<<set $slaves[$i].behavioralFlaw = "none">>
 <</if>>
 
-<<if canPenetrate($HeadGirl)>>
+<<if canPenetrate(_S.HeadGirl)>>
 	<<if canDoVaginal($slaves[$i])>>
 		<<if ($slaves[$i].vagina == 0)>>
-			$HeadGirl.slaveName @@.hotpink;loves@@ @@.lime;taking@@ $slaves[$i].slaveName's virginity, and spends much of the week ogling _his2 conquest complacently.
-			<<set $slaves[$i].vagina = 1, $HeadGirl.devotion += 4>>
+			_S.HeadGirl.slaveName @@.hotpink;loves@@ @@.lime;taking@@ $slaves[$i].slaveName's virginity, and spends much of the week ogling _his2 conquest complacently.
+			<<set $slaves[$i].vagina = 1, _S.HeadGirl.devotion += 4>>
 		<<elseif ($slaves[$i].vagina == 1)>>
-			<<if ($HeadGirl.dick > 4) && ($HeadGirl.energy > 95)>>
-				$HeadGirl.slaveName has such a ferocious sex drive and such a big dick that _his2 constant pounding of $slaves[$i].slaveName @@.lime;loosens@@ the poor $girl's pussy.
+			<<if (_S.HeadGirl.dick > 4) && (_S.HeadGirl.energy > 95)>>
+				_S.HeadGirl.slaveName has such a ferocious sex drive and such a big dick that _his2 constant pounding of $slaves[$i].slaveName @@.lime;loosens@@ the poor $girl's pussy.
 				<<set $slaves[$i].vagina += 1>>
-			<<elseif ($HeadGirl.dick > 5)>>
-				Serving $HeadGirl.slaveName's monster cock @@.lime;loosens@@ $slaves[$i].slaveName's pussy.
+			<<elseif (_S.HeadGirl.dick > 5)>>
+				Serving _S.HeadGirl.slaveName's monster cock @@.lime;loosens@@ $slaves[$i].slaveName's pussy.
 				<<set $slaves[$i].vagina += 1>>
 			<</if>>
 		<<elseif ($slaves[$i].vagina == 2)>>
-			<<if ($HeadGirl.dick > 5) && ($HeadGirl.energy > 95)>>
-				$HeadGirl.slaveName has such a ferocious sex drive and such a monster cock that _his2 constant pounding of $slaves[$i].slaveName @@.lime;loosens@@ the poor $girl's pussy.
+			<<if (_S.HeadGirl.dick > 5) && (_S.HeadGirl.energy > 95)>>
+				_S.HeadGirl.slaveName has such a ferocious sex drive and such a monster cock that _his2 constant pounding of $slaves[$i].slaveName @@.lime;loosens@@ the poor $girl's pussy.
 				<<set $slaves[$i].vagina += 1>>
 			<</if>>
 		<<elseif ($slaves[$i].vagina == 3)>>
-			<<if ($HeadGirl.dick > 5) && ($HeadGirl.energy > 95) && random(1,100) > 80>>
-				$HeadGirl.slaveName has such a ferocious sex drive and such a monster cock that _his2 constant pounding of $slaves[$i].slaveName's loose pussy inevitably leaves it @@.lime;gaping.@@
+			<<if (_S.HeadGirl.dick > 5) && (_S.HeadGirl.energy > 95) && random(1,100) > 80>>
+				_S.HeadGirl.slaveName has such a ferocious sex drive and such a monster cock that _his2 constant pounding of $slaves[$i].slaveName's loose pussy inevitably leaves it @@.lime;gaping.@@
 				<<set $slaves[$i].vagina += 1>>
 			<</if>>
 		<</if>>
 	<</if>>
 	<<if canDoAnal($slaves[$i])>>
 		<<if ($slaves[$i].anus == 0)>>
-			$HeadGirl.slaveName @@.hotpink;relishes@@ @@.lime;taking@@ $slaves[$i].slaveName's anal virginity, and spends much of the week groping _his2 conquest's rear end possessively.
-			<<set $slaves[$i].anus = 1, $HeadGirl.devotion += 4>>
+			_S.HeadGirl.slaveName @@.hotpink;relishes@@ @@.lime;taking@@ $slaves[$i].slaveName's anal virginity, and spends much of the week groping _his2 conquest's rear end possessively.
+			<<set $slaves[$i].anus = 1, _S.HeadGirl.devotion += 4>>
 		<<elseif ($slaves[$i].anus == 1)>>
-			<<if ($HeadGirl.dick > 4) && ($HeadGirl.energy > 95)>>
-				$HeadGirl.slaveName uses $slaves[$i].slaveName's anus to vent _his2 sexual addiction. Constant assrape from such a huge dick @@.lime;loosens@@ $his sphincter.
+			<<if (_S.HeadGirl.dick > 4) && (_S.HeadGirl.energy > 95)>>
+				_S.HeadGirl.slaveName uses $slaves[$i].slaveName's anus to vent _his2 sexual addiction. Constant assrape from such a huge dick @@.lime;loosens@@ $his sphincter.
 				<<set $slaves[$i].anus += 1>>
-			<<elseif ($HeadGirl.dick > 5)>>
-				Taking $HeadGirl.slaveName's monster cock up the butt @@.lime;loosens@@ $slaves[$i].slaveName's anal sphincter.
+			<<elseif (_S.HeadGirl.dick > 5)>>
+				Taking _S.HeadGirl.slaveName's monster cock up the butt @@.lime;loosens@@ $slaves[$i].slaveName's anal sphincter.
 				<<set $slaves[$i].anus += 1>>
 			<</if>>
 		<<elseif ($slaves[$i].anus == 2)>>
-			<<if ($HeadGirl.dick > 5) && ($HeadGirl.energy > 95)>>
-				$HeadGirl.slaveName uses $slaves[$i].slaveName's anus to vent _his2 sexual addiction. Constant assrape from the Head Girl's monster cock @@.lime;loosens@@ $his sphincter.
+			<<if (_S.HeadGirl.dick > 5) && (_S.HeadGirl.energy > 95)>>
+				_S.HeadGirl.slaveName uses $slaves[$i].slaveName's anus to vent _his2 sexual addiction. Constant assrape from the Head Girl's monster cock @@.lime;loosens@@ $his sphincter.
 				<<set $slaves[$i].anus += 1>>
 			<</if>>
 		<<elseif ($slaves[$i].anus == 3)>>
-			<<if ($HeadGirl.dick > 5) && ($HeadGirl.energy > 95) && random(1,100) > 80>>
-				$HeadGirl.slaveName uses $slaves[$i].slaveName's loose anus to vent _his2 sexual addiction. Constant assrape from the Head Girl's monster cock eventually leaves it @@.lime;gaping.@@
+			<<if (_S.HeadGirl.dick > 5) && (_S.HeadGirl.energy > 95) && random(1,100) > 80>>
+				_S.HeadGirl.slaveName uses $slaves[$i].slaveName's loose anus to vent _his2 sexual addiction. Constant assrape from the Head Girl's monster cock eventually leaves it @@.lime;gaping.@@
 				<<set $slaves[$i].anus += 1>>
 			<</if>>
 		<</if>>
@@ -314,49 +313,49 @@
 <</if>>
 
 <<if $seePreg != 0>>
-	<<if ($HeadGirl.fetish == "pregnancy") && canImpreg($slaves[$i], $HeadGirl)>>
+	<<if (_S.HeadGirl.fetish == "pregnancy") && canImpreg($slaves[$i], _S.HeadGirl)>>
 		<<if $arcologies[0].FSRestart == "unset">>
-			$HeadGirl.slaveName promptly @@.lime;impregnates@@
-			<<if $HeadGirl.fetishKnown == 1>>
+			_S.HeadGirl.slaveName promptly @@.lime;impregnates@@
+			<<if _S.HeadGirl.fetishKnown == 1>>
 				$slaves[$i].slaveName, to your Head Girl's considerable @@.hotpink;satisfaction.@@ $slaves[$i].slaveName spent the week regularly getting held down and ejaculated into anytime $his superior had cum to spare.
-				<<set $HeadGirl.devotion += 4>>
+				<<set _S.HeadGirl.devotion += 4>>
 			<<else>>
 				$slaves[$i].slaveName. _His2 eagerness completely exposes _his2 hidden @@.lightcoral;pregnancy kink.@@
-				<<set $HeadGirl.fetishKnown = 1>>
+				<<set _S.HeadGirl.fetishKnown = 1>>
 			<</if>>
-			<<= knockMeUp($slaves[$i], 100, 2, $HeadGirl.ID, 1)>>
-			<<if ($HeadGirl.fetishStrength > 70) && canImpreg($HeadGirl, $slaves[$i])>>
+			<<= knockMeUp($slaves[$i], 100, 2, $HeadGirlID, 1)>>
+			<<if (_S.HeadGirl.fetishStrength > 70) && canImpreg(_S.HeadGirl, $slaves[$i])>>
 				Unsurprisingly, _his2 gives in to _his2 own cravings and also takes $slaves[$i].slaveName's loads until _he2 @@.lime;gets pregnant@@ too.
-				<<= knockMeUp($HeadGirl, 100, 2, $slaves[$i].ID, 1)>>
+				<<= knockMeUp(_S.HeadGirl, 100, 2, $slaves[$i].ID, 1)>>
 			<</if>>
-		<<elseif $HeadGirl.fetishKnown == 1>>
-			$HeadGirl.slaveName knows better than to even consider knocking up $slaves[$i].slaveName.
+		<<elseif _S.HeadGirl.fetishKnown == 1>>
+			_S.HeadGirl.slaveName knows better than to even consider knocking up $slaves[$i].slaveName.
 		<</if>>
-	<<elseif ($HeadGirl.fetish == "pregnancy") && canImpreg($HeadGirl, $slaves[$i])>>
-		<<if $arcologies[0].FSRestart == "unset" && ($HeadGirl.fetishStrength > 70)>>
-			$HeadGirl.slaveName promptly @@.lime;knocks _himself2 up@@ with $slaves[$i].slaveName's
-			<<if $HeadGirl.fetishKnown == 1>>
+	<<elseif (_S.HeadGirl.fetish == "pregnancy") && canImpreg(_S.HeadGirl, $slaves[$i])>>
+		<<if $arcologies[0].FSRestart == "unset" && (_S.HeadGirl.fetishStrength > 70)>>
+			_S.HeadGirl.slaveName promptly @@.lime;knocks _himself2 up@@ with $slaves[$i].slaveName's
+			<<if _S.HeadGirl.fetishKnown == 1>>
 				seed, to your Head Girl's considerable @@.hotpink;satisfaction.@@
-				<<set $HeadGirl.devotion += 4>>
+				<<set _S.HeadGirl.devotion += 4>>
 			<<else>>
 				seed. _His2 @@.hotpink;pride@@ over _his2 new pregnancy and eagerness to get pregnant completely exposes _his2 hidden, and powerful, @@.lightcoral;pregnancy fetish.@@
-				<<set $HeadGirl.fetishKnown = 1, $HeadGirl.devotion += 4>>
+				<<set _S.HeadGirl.fetishKnown = 1, _S.HeadGirl.devotion += 4>>
 			<</if>>
-			<<= knockMeUp($HeadGirl, 100, 2, $slaves[$i].ID, 1)>>
-		<<elseif $HeadGirl.fetishKnown == 1>>
-			$HeadGirl.slaveName knows better than to even consider getting knocked up by $slaves[$i].slaveName.
+			<<= knockMeUp(_S.HeadGirl, 100, 2, $slaves[$i].ID, 1)>>
+		<<elseif _S.HeadGirl.fetishKnown == 1>>
+			_S.HeadGirl.slaveName knows better than to even consider getting knocked up by $slaves[$i].slaveName.
 		<</if>>
-	<<elseif $arcologies[0].FSRepopulationFocusPregPolicy == 1 && canImpreg($slaves[$i], $HeadGirl)>>
-		$HeadGirl.slaveName wastes no time in chasing the current trend and hurries to @@.lime;impregnate@@ $slaves[$i].slaveName.
-		<<= knockMeUp($slaves[$i], 100, 2, $HeadGirl.ID, 1)>>
+	<<elseif $arcologies[0].FSRepopulationFocusPregPolicy == 1 && canImpreg($slaves[$i], _S.HeadGirl)>>
+		_S.HeadGirl.slaveName wastes no time in chasing the current trend and hurries to @@.lime;impregnate@@ $slaves[$i].slaveName.
+		<<= knockMeUp($slaves[$i], 100, 2, $HeadGirlID, 1)>>
 	<<elseif $slaves[$i].pregKnown == 1>>
 		<<if $slaves[$i].breedingMark == 1>>
-			Since $slaves[$i].slaveName is marked as a breeding slave, $HeadGirl.slaveName feels it is _his2 duty to keep $his pregnancy safe.
+			Since $slaves[$i].slaveName is marked as a breeding slave, _S.HeadGirl.slaveName feels it is _his2 duty to keep $his pregnancy safe.
 		<<elseif $slaves[$i].pregSource == -1>>
-			Since $slaves[$i].slaveName is bearing your child<<if $slaves[$i].pregType > 1>>ren<</if>>, $HeadGirl.slaveName feels _he2 has been tasked with keeping $his pregnancy safe.
-		<<elseif $HeadGirl.fetish == "pregnancy">>
-			$HeadGirl.slaveName
-			<<if $HeadGirl.fetishKnown == 1>>
+			Since $slaves[$i].slaveName is bearing your child<<if $slaves[$i].pregType > 1>>ren<</if>>, _S.HeadGirl.slaveName feels _he2 has been tasked with keeping $his pregnancy safe.
+		<<elseif _S.HeadGirl.fetish == "pregnancy">>
+			_S.HeadGirl.slaveName
+			<<if _S.HeadGirl.fetishKnown == 1>>
 				<<if $slaves[$i].bellyPreg > 500>>
 					enjoys the swell of $slaves[$i].slaveName's baby bump, giving _him2 plenty of reason to keep $his pregnancy.
 				<<else>>
@@ -367,10 +366,10 @@
 			<</if>>
 		<<elseif $arcologies[0].FSRepopulationFocusPregPolicy == 0 && $arcologies[0].FSRepopulationFocus == "unset" && $HGSuiteDrugs == 1>>
 			<<if $slaves[$i].preg > $slaves[$i].pregData.normalBirth-2 && $slaves[$i].preg > $slaves[$i].pregData.minLiveBirth && $slaves[$i].broodmother === 0 && $slaves[$i].labor === 0>>
-				$HeadGirl.slaveName has no interest in a slut stuffed full of unwanted spawn, so _he2 promptly doses slaves[$i].slaveName with labor inducing agents to get $him back in fucking shape.
+				_S.HeadGirl.slaveName has no interest in a slut stuffed full of unwanted spawn, so _he2 promptly doses slaves[$i].slaveName with labor inducing agents to get $him back in fucking shape.
 				<<set $slaves[$i].labor = 1, $slaves[$i].induce = 1, $birthee = 1>>
 			<<else>>
-				$HeadGirl.slaveName promptly aborts the child growing in $slaves[$i].slaveName since _he2 prefers _his2 <<= $girl>>s not harboring someone else's child or loaded down with _his2 own unwanted spawn.
+				_S.HeadGirl.slaveName promptly aborts the child growing in $slaves[$i].slaveName since _he2 prefers _his2 <<= $girl>>s not harboring someone else's child or loaded down with _his2 own unwanted spawn.
 				<<run TerminatePregnancy($slaves[$i])>>
 				<<set $slaves[$i].counter.abortions++>>
 				<<set $abortionsTotal++>>
@@ -380,68 +379,68 @@
 <</if>>
 
 <<if ($slaves[$i].devotion > 50)>>
-	<<if (($slaves[$i].fetishStrength <= 95) || ($slaves[$i].fetishKnown == 0)) && ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetishStrength > 60) && ($slaves[$i].fetish != $HeadGirl.fetish) && ($slaves[$i].fetish != "mindbroken")>>
-		<<if ($HeadGirl.fetish == "submissive")>>
-			$slaves[$i].slaveName slowly gets used to $HeadGirl.slaveName's need to be sexually dominated, and begins to @@.pink;enjoy being a top@@ within the confines of the suite.
+	<<if (($slaves[$i].fetishStrength <= 95) || ($slaves[$i].fetishKnown == 0)) && (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetishStrength > 60) && ($slaves[$i].fetish != _S.HeadGirl.fetish) && ($slaves[$i].fetish != "mindbroken")>>
+		<<if (_S.HeadGirl.fetish == "submissive")>>
+			$slaves[$i].slaveName slowly gets used to _S.HeadGirl.slaveName's need to be sexually dominated, and begins to @@.pink;enjoy being a top@@ within the confines of the suite.
 			<<set $slaves[$i].fetish = "dom", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "dom")>>
+		<<elseif (_S.HeadGirl.fetish == "dom")>>
 			<<if ($slaves[$i].fetish != "submissive") || ($slaves[$i].fetish == 0)>>
-				$slaves[$i].slaveName slowly gets used to being tied up and fucked regularly, and accepts that it's how $HeadGirl.slaveName's sexual attraction to $him expresses itself. $He begins to @@.pink;enjoy being a sub,@@ especially for $HeadGirl.slaveName.
+				$slaves[$i].slaveName slowly gets used to being tied up and fucked regularly, and accepts that it's how _S.HeadGirl.slaveName's sexual attraction to $him expresses itself. $He begins to @@.pink;enjoy being a sub,@@ especially for _S.HeadGirl.slaveName.
 				<<set $slaves[$i].fetish = "submissive", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
 			<</if>>
-		<<elseif ($HeadGirl.fetish == "boobs")>>
-			$HeadGirl.slaveName lavishes so much attention on $slaves[$i].slaveName's boobs that $slaves[$i].slaveName really starts to @@.pink;fetishize tits@@ too.
+		<<elseif (_S.HeadGirl.fetish == "boobs")>>
+			_S.HeadGirl.slaveName lavishes so much attention on $slaves[$i].slaveName's boobs that $slaves[$i].slaveName really starts to @@.pink;fetishize tits@@ too.
 			<<set $slaves[$i].fetish = "boobs", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "pregnancy")>>
-			$slaves[$i].slaveName and $HeadGirl.slaveName spend so much time talking about pregnancy and fertility that $slaves[$i].slaveName really starts to @@.pink;fetishize pregnancy@@ too.
+		<<elseif (_S.HeadGirl.fetish == "pregnancy")>>
+			$slaves[$i].slaveName and _S.HeadGirl.slaveName spend so much time talking about pregnancy and fertility that $slaves[$i].slaveName really starts to @@.pink;fetishize pregnancy@@ too.
 			<<set $slaves[$i].fetish = "pregnancy", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "cumslut")>>
-			$slaves[$i].slaveName participates in enough of $HeadGirl.slaveName's cum games that $he can't help but @@.pink;enjoy oral sex@@ $himself.
+		<<elseif (_S.HeadGirl.fetish == "cumslut")>>
+			$slaves[$i].slaveName participates in enough of _S.HeadGirl.slaveName's cum games that $he can't help but @@.pink;enjoy oral sex@@ $himself.
 			<<set $slaves[$i].fetish = "cumslut", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "humiliation")>>
-			$slaves[$i].slaveName has so much public sex with $HeadGirl.slaveName that $he really starts to @@.pink;enjoy humiliation@@ $himself.
+		<<elseif (_S.HeadGirl.fetish == "humiliation")>>
+			$slaves[$i].slaveName has so much public sex with _S.HeadGirl.slaveName that $he really starts to @@.pink;enjoy humiliation@@ $himself.
 			<<set $slaves[$i].fetish = "humiliation", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "buttslut")>>
+		<<elseif (_S.HeadGirl.fetish == "buttslut")>>
 			<<if canDoAnal($slaves[$i])>>
-				$slaves[$i].slaveName takes $HeadGirl.slaveName's <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on and dildos<</if>> up $his poor anus so often that $he starts to @@.pink;enjoy buttsex@@ $himself.
+				$slaves[$i].slaveName takes _S.HeadGirl.slaveName's <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on and dildos<</if>> up $his poor anus so often that $he starts to @@.pink;enjoy buttsex@@ $himself.
 			<<else>>
-				$HeadGirl.slaveName lavishes so much attention on $slaves[$i].slaveName's butt, since _he2 can't use it, that $slaves[$i].slaveName really starts to @@.pink;fetishize rears@@ too.
+				_S.HeadGirl.slaveName lavishes so much attention on $slaves[$i].slaveName's butt, since _he2 can't use it, that $slaves[$i].slaveName really starts to @@.pink;fetishize rears@@ too.
 			<</if>>
 			<<set $slaves[$i].fetish = "buttslut", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
-		<<elseif ($HeadGirl.fetish == "masochist")>>
+		<<elseif (_S.HeadGirl.fetish == "masochist")>>
 			<<if ($slaves[$i].fetish != "submissive") || ($slaves[$i].fetish == 0)>>
-				$slaves[$i].slaveName is initially shocked to be asked to cause $HeadGirl.slaveName pain, but $he slowly begins to understand the honestly sexual nature of the arrangement. $He starts to @@.pink;enjoy causing pain,@@ since $HeadGirl.slaveName is clearly enjoying it so much.
+				$slaves[$i].slaveName is initially shocked to be asked to cause _S.HeadGirl.slaveName pain, but $he slowly begins to understand the honestly sexual nature of the arrangement. $He starts to @@.pink;enjoy causing pain,@@ since _S.HeadGirl.slaveName is clearly enjoying it so much.
 				<<set $slaves[$i].fetish = "sadist", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
 			<</if>>
 		<<else>>
-			$slaves[$i].slaveName often takes $HeadGirl.slaveName's <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on<</if>> in the missionary position, after some light foreplay and before bed; $he honestly enjoys the respectful lovemaking, and begins to @@.pink;enjoy vanilla sex@@ $himself.
+			$slaves[$i].slaveName often takes _S.HeadGirl.slaveName's <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on<</if>> in the missionary position, after some light foreplay and before bed; $he honestly enjoys the respectful lovemaking, and begins to @@.pink;enjoy vanilla sex@@ $himself.
 			<<set $slaves[$i].fetish = "none", $slaves[$i].fetishKnown = 1, $slaves[$i].fetishStrength = 10>>
 		<</if>>
 	<</if>>
-	<<if ($HeadGirl.attrKnown == 1)>>
-		<<if ($HeadGirl.attrXX > 85) && ($slaves[$i].attrXX > 65) && ($slaves[$i].attrXX <= 85)>>
-			$slaves[$i].slaveName and $HeadGirl.slaveName spend so much time giggling over which of your other slaves they think is prettiest (typically followed by giggling sex) that $slaves[$i].slaveName really starts to @@.green;fetishize girls@@ too.
+	<<if (_S.HeadGirl.attrKnown == 1)>>
+		<<if (_S.HeadGirl.attrXX > 85) && ($slaves[$i].attrXX > 65) && ($slaves[$i].attrXX <= 85)>>
+			$slaves[$i].slaveName and _S.HeadGirl.slaveName spend so much time giggling over which of your other slaves they think is prettiest (typically followed by giggling sex) that $slaves[$i].slaveName really starts to @@.green;fetishize girls@@ too.
 			<<set $slaves[$i].attrXX += 2, $slaves[$i].attrKnown = 1>>
 		<</if>>
-		<<if ($HeadGirl.attrXY > 85) && ($slaves[$i].attrXY > 65) && ($slaves[$i].attrXY <= 85)>>
-			$slaves[$i].slaveName and $HeadGirl.slaveName have so much fun debating which male citizens and slave dickgirls are cutest (typically followed by <<if canDoAnal($slaves[$i]) || canDoVaginal($slaves[$i])>>$HeadGirl.slaveName banging _his2 girltoy <<if canPenetrate($HeadGirl)>>with a strap-on<<else>>senseless<</if>><<else>>giggling sex<</if>>) that $slaves[$i].slaveName really starts to @@.green;fetishize boys@@ too.
+		<<if (_S.HeadGirl.attrXY > 85) && ($slaves[$i].attrXY > 65) && ($slaves[$i].attrXY <= 85)>>
+			$slaves[$i].slaveName and _S.HeadGirl.slaveName have so much fun debating which male citizens and slave dickgirls are cutest (typically followed by <<if canDoAnal($slaves[$i]) || canDoVaginal($slaves[$i])>>_S.HeadGirl.slaveName banging _his2 girltoy <<if canPenetrate(_S.HeadGirl)>>with a strap-on<<else>>senseless<</if>><<else>>giggling sex<</if>>) that $slaves[$i].slaveName really starts to @@.green;fetishize boys@@ too.
 			<<set $slaves[$i].attrXY += 2, $slaves[$i].attrKnown = 1>>
 		<</if>>
-		<<if ($HeadGirl.energy > 95) && ($slaves[$i].energy <= 95)>>
-			$slaves[$i].slaveName has so much fun sex with $HeadGirl.slaveName that @@.green;$his sex drive is slowly enhanced.@@
+		<<if (_S.HeadGirl.energy > 95) && ($slaves[$i].energy <= 95)>>
+			$slaves[$i].slaveName has so much fun sex with _S.HeadGirl.slaveName that @@.green;$his sex drive is slowly enhanced.@@
 			<<set $slaves[$i].energy += 2>>
 		<</if>>
 	<</if>>
 <</if>>
 
-<<if ($HeadGirl.fetishKnown == 1)>>
-	<<if ($HeadGirl.energy > 95)>>
-		$slaves[$i].slaveName spends a lot of time getting fucked in whichever hole or cranny $HeadGirl.slaveName decides to shove _his2 <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on<</if>> into next.
+<<if (_S.HeadGirl.fetishKnown == 1)>>
+	<<if (_S.HeadGirl.energy > 95)>>
+		$slaves[$i].slaveName spends a lot of time getting fucked in whichever hole or cranny _S.HeadGirl.slaveName decides to shove _his2 <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on<</if>> into next.
 		<<set _oralUse += 5>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 5>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 5, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 5, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 500>>
 			<<set _mammaryUse += 5>>
@@ -450,25 +449,25 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse += 5>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 5, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 5, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse += 5>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 5, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 5, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse += 5>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 15, $penetrativeTotal += 15>>
-	<<elseif ($HeadGirl.fetish == "submissive")>>
-		$HeadGirl.slaveName walks a fine line with $slaves[$i].slaveName. They work out a sexual life in which $HeadGirl.slaveName is in charge, but $slaves[$i].slaveName takes the sexual lead: $slaves[$i].slaveName serves $his superior by taking _him2 firmly.
+		<<set _S.HeadGirl.counter.penetrative += 15, $penetrativeTotal += 15>>
+	<<elseif (_S.HeadGirl.fetish == "submissive")>>
+		_S.HeadGirl.slaveName walks a fine line with $slaves[$i].slaveName. They work out a sexual life in which _S.HeadGirl.slaveName is in charge, but $slaves[$i].slaveName takes the sexual lead: $slaves[$i].slaveName serves $his superior by taking _him2 firmly.
 		<<set _oralUse += 4>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 500>>
 			<<set _mammaryUse += 3>>
@@ -477,25 +476,25 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse += 3>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-	<<elseif ($HeadGirl.fetish == "dom")>>
-		$slaves[$i].slaveName serves $HeadGirl.slaveName as _his2 sexual and moral inferior, taking the Head Girl's <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on<</if>> in all $his holes. Though it isn't necessary with such an obedient partner, $slaves[$i].slaveName is often tied up for use.
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+	<<elseif (_S.HeadGirl.fetish == "dom")>>
+		$slaves[$i].slaveName serves _S.HeadGirl.slaveName as _his2 sexual and moral inferior, taking the Head Girl's <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on<</if>> in all $his holes. Though it isn't necessary with such an obedient partner, $slaves[$i].slaveName is often tied up for use.
 		<<set _oralUse += 4>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 500>>
 			<<set _mammaryUse += 3>>
@@ -504,25 +503,25 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse += 3>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-	<<elseif ($HeadGirl.fetish == "humiliation")>>
-		$HeadGirl.slaveName is such an exhibitionist that $slaves[$i].slaveName finds $himself taking $HeadGirl.slaveName's <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on<</if>> in public quite often.
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+	<<elseif (_S.HeadGirl.fetish == "humiliation")>>
+		_S.HeadGirl.slaveName is such an exhibitionist that $slaves[$i].slaveName finds $himself taking _S.HeadGirl.slaveName's <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on<</if>> in public quite often.
 		<<set _oralUse += 4>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 500>>
 			<<set _mammaryUse += 3>>
@@ -531,55 +530,55 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse += 3>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-	<<elseif ($HeadGirl.fetish == "boobs")>>
-		$HeadGirl.slaveName is such a boob fetishist that $slaves[$i].slaveName sometimes wonders whether $HeadGirl.slaveName thinks there are four clits in the suite: the four nipples between the two of them.
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+	<<elseif (_S.HeadGirl.fetish == "boobs")>>
+		_S.HeadGirl.slaveName is such a boob fetishist that $slaves[$i].slaveName sometimes wonders whether _S.HeadGirl.slaveName thinks there are four clits in the suite: the four nipples between the two of them.
 		<<set _oralUse += 4>>
 		<<set _mammaryUse += 6>>
-		<<set $HeadGirl.counter.mammary += 10, $mammaryTotal += 10>>
-	<<elseif ($HeadGirl.fetish == "buttslut")>>
-		$HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName; _he2 indulges _his2 <<if canDoAnal($slaves[$i])>>anal addiction by constantly buttfucking $slaves[$i].slaveName<<else>>ass obsession by constantly molesting and using $slaves[$i].slaveName's buttcheeks as a sex toy<</if>> while wearing a vibrating plug _himself2.
+		<<set _S.HeadGirl.counter.mammary += 10, $mammaryTotal += 10>>
+	<<elseif (_S.HeadGirl.fetish == "buttslut")>>
+		_S.HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName; _he2 indulges _his2 <<if canDoAnal($slaves[$i])>>anal addiction by constantly buttfucking $slaves[$i].slaveName<<else>>ass obsession by constantly molesting and using $slaves[$i].slaveName's buttcheeks as a sex toy<</if>> while wearing a vibrating plug _himself2.
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 10>>
-			<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 10, 1, $HeadGirl.ID)>>
+			<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 10, 1, $HeadGirlID)>>
 			<</if>>
 		<</if>>
-	<<elseif ($HeadGirl.fetish == "cumslut")>>
-		$HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName; _he2 indulges _his2 oral fixation with constant oral sex. _He2 certainly applies _his2 mouth to $slaves[$i].slaveName when _he2 gets the chance, but $slaves[$i].slaveName does most of the sucking.
+	<<elseif (_S.HeadGirl.fetish == "cumslut")>>
+		_S.HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName; _he2 indulges _his2 oral fixation with constant oral sex. _He2 certainly applies _his2 mouth to $slaves[$i].slaveName when _he2 gets the chance, but $slaves[$i].slaveName does most of the sucking.
 		<<set _oralUse += 20>>
-		<<set $HeadGirl.counter.oral += 10, $oralTotal += 10>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-	<<elseif ($HeadGirl.fetish == "masochist")>>
-		$HeadGirl.slaveName carefully structures _his2 sexual games with $slaves[$i].slaveName to gratify _his2 deep need to be physically hurt during sex without damaging _his2 leadership. Usually, $slaves[$i].slaveName tortures $HeadGirl.slaveName until _he2's quite sated, and then takes a rough <<if canDoAnal($slaves[$i])>>buttfuck<<else>>facefucking<</if>> from the aroused $HeadGirl.slaveName's <<if canPenetrate($HeadGirl)>>dick<<else>>strap-on<</if>> to restore their relative positions.
+		<<set _S.HeadGirl.counter.oral += 10, $oralTotal += 10>>
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+	<<elseif (_S.HeadGirl.fetish == "masochist")>>
+		_S.HeadGirl.slaveName carefully structures _his2 sexual games with $slaves[$i].slaveName to gratify _his2 deep need to be physically hurt during sex without damaging _his2 leadership. Usually, $slaves[$i].slaveName tortures _S.HeadGirl.slaveName until _he2's quite sated, and then takes a rough <<if canDoAnal($slaves[$i])>>buttfuck<<else>>facefucking<</if>> from the aroused _S.HeadGirl.slaveName's <<if canPenetrate(_S.HeadGirl)>>dick<<else>>strap-on<</if>> to restore their relative positions.
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 10>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 10, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 10, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse += 10>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
-	<<elseif ($HeadGirl.attrXX > 65)>>
-		$HeadGirl.slaveName expects $slaves[$i].slaveName to be girlish sexually, and $slaves[$i].slaveName spends a lot of time <<if hasBothLegs($slaves[$i])>>on $his knees<<else>>servicing _him2<</if>>.
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+	<<elseif (_S.HeadGirl.attrXX > 65)>>
+		_S.HeadGirl.slaveName expects $slaves[$i].slaveName to be girlish sexually, and $slaves[$i].slaveName spends a lot of time <<if hasBothLegs($slaves[$i])>>on $his knees<<else>>servicing _him2<</if>>.
 		<<set _oralUse += 6>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse++>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 1, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 1, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 500>>
 			<<set _mammaryUse++>>
@@ -588,25 +587,25 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse++>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 1, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 1, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse++>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 1, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 1, 1, $HeadGirlID)>>
 			<</if>>
 		<<else>>
 			<<set _oralUse++>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 8, $penetrativeTotal += 8>>
+		<<set _S.HeadGirl.counter.penetrative += 8, $penetrativeTotal += 8>>
 	<<else>>
-		$HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName, mostly getting oral<<if canPenetrate($HeadGirl) && (canDoVaginal($slaves[$i]) || canDoAnal($slaves[$i]))>> and fucking $him in the missionary position<<elseif $slaves[$i] >= 300>> and molesting $his breasts<</if>>.
+		_S.HeadGirl.slaveName takes a dominant sexual role with $slaves[$i].slaveName, mostly getting oral<<if canPenetrate(_S.HeadGirl) && (canDoVaginal($slaves[$i]) || canDoAnal($slaves[$i]))>> and fucking $him in the missionary position<<elseif $slaves[$i] >= 300>> and molesting $his breasts<</if>>.
 		<<set _oralUse += 4>>
 		<<if canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 300>>
 			<<set _mammaryUse += 3>>
@@ -615,20 +614,20 @@
 		<</if>>
 		<<if ($slaves[$i].vagina > 0) && canDoVaginal($slaves[$i])>>
 			<<set _vaginalUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 0, $HeadGirlID)>>
 			<</if>>
 		<<elseif canDoAnal($slaves[$i])>>
 			<<set _analUse += 3>>
-			<<if canImpreg($slaves[$i], $HeadGirl)>>
-				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirl.ID)>>
+			<<if canImpreg($slaves[$i], _S.HeadGirl)>>
+				<<= knockMeUp($slaves[$i], 3, 1, $HeadGirlID)>>
 			<</if>>
 		<<elseif $slaves[$i] >= 300>>
 			<<set _mammaryUse += 3>>
 		<<else>>
 			<<set _oralUse += 3>>
 		<</if>>
-		<<set $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+		<<set _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
 	<</if>>
 <</if>>
 
@@ -685,7 +684,7 @@
 <</if>>
 
 <<set $slaves[$i].need -= ((_vaginalUse+_penetrativeUse+_analUse)*5)>>
-<<set $HeadGirl.need = 0>>
+<<set _S.HeadGirl.need = 0>>
 
 <<if _cervixPump > 0>>
 	$slaves[$i].slaveName notices $his @@.lime;belly swell@@ after
@@ -699,22 +698,22 @@
 <</if>>
 
 <<if $slaves[$i].health.tired > 60>>
-	$HeadGirl.slaveName sees $he is becoming exhausted and gives $him lighter duties so $he doesn't break.
+	_S.HeadGirl.slaveName sees $he is becoming exhausted and gives $him lighter duties so $he doesn't break.
 <</if>>
 <<run tired($slaves[$i])>>
 
-<<if ($HeadGirl.fetishKnown == 1)>>
-	<<if ($HeadGirl.energy > 95)>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName naked so _he2 doesn't have to waste any time tearing $his clothes off.
+<<if (_S.HeadGirl.fetishKnown == 1)>>
+	<<if (_S.HeadGirl.energy > 95)>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName naked so _he2 doesn't have to waste any time tearing $his clothes off.
 		<<set $slaves[$i].clothes = "no clothing">>
 	<<elseif $slaves[$i].devotion <= 20>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName in a torturous habit since $he still clings to $his dignity and remains defiant.
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName in a torturous habit since $he still clings to $his dignity and remains defiant.
 		<<set $slaves[$i].clothes = "a penitent nuns habit">>
-	<<elseif ($HeadGirl.fetish == "submissive")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName dressed up in slutty power clothing, since _he2's attracted to competence.
+	<<elseif (_S.HeadGirl.fetish == "submissive")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName dressed up in slutty power clothing, since _he2's attracted to competence.
 		<<set $slaves[$i].clothes = "slutty business attire">>
-	<<elseif ($HeadGirl.fetish == "masochist")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName
+	<<elseif (_S.HeadGirl.fetish == "masochist")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName
 		<<if isItemAccessible.entry("battledress", "clothing")>>
 			in battledress, since _he2 likes the fantasy of being raped by a soldier $girl.
 			<<set $slaves[$i].clothes = "battledress">>
@@ -722,82 +721,82 @@
 			in a scalemail bikini, since _he2 likes the fantasy of being raped by a horny barbarian.
 			<<set $slaves[$i].clothes = "a scalemail bikini">>
 		<</if>>
-	<<elseif ($HeadGirl.fetish == "dom")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName dressed up as a school<<= $girl>>
+	<<elseif (_S.HeadGirl.fetish == "dom")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName dressed up as a school<<= $girl>>
 		<<if $slaves[$i].physicalAge > 16>>
 			to infantilize _his2 sub.
 		<<else>>
 			to highlight _his2 authority over _his2 young sub.
 		<</if>>
 		<<set $slaves[$i].clothes = "a schoolgirl outfit">>
-	<<elseif ($HeadGirl.fetish == "boobs")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName wearing a string bikini, since _he2 likes seeing those tits.
+	<<elseif (_S.HeadGirl.fetish == "boobs")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName wearing a string bikini, since _he2 likes seeing those tits.
 		<<set $slaves[$i].clothes = "a string bikini">>
-	<<elseif ($HeadGirl.fetish == "cumslut")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName in restrictive latex, because _he2 likes how it draws attention to $his mouth.
+	<<elseif (_S.HeadGirl.fetish == "cumslut")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName in restrictive latex, because _he2 likes how it draws attention to $his mouth.
 		<<set $slaves[$i].clothes = "restrictive latex">>
-	<<elseif ($HeadGirl.fetish == "humiliation")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName in humiliating bondage gear, because _he2 likes to see others blush, too.
+	<<elseif (_S.HeadGirl.fetish == "humiliation")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName in humiliating bondage gear, because _he2 likes to see others blush, too.
 		<<set $slaves[$i].clothes = "uncomfortable straps">>
-	<<elseif ($HeadGirl.fetish == "sadist")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName in humiliating bondage gear, because it can easily be tightened to cause considerable anguish.
+	<<elseif (_S.HeadGirl.fetish == "sadist")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName in humiliating bondage gear, because it can easily be tightened to cause considerable anguish.
 		<<set $slaves[$i].clothes = "uncomfortable straps">>
-	<<elseif ($HeadGirl.fetish == "buttslut")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName wearing nothing but slutty bangles, since _he2 likes dressing $him up but wants that butt bare.
+	<<elseif (_S.HeadGirl.fetish == "buttslut")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName wearing nothing but slutty bangles, since _he2 likes dressing $him up but wants that butt bare.
 		<<set $slaves[$i].clothes = "slutty jewelry">>
-	<<elseif ($HeadGirl.fetish == "pregnancy" && isItemAccessible.entry("a huge empathy belly", "bellyAccessory", $slaves[$i]) == true)>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName wearing an empathy belly, since _he2 likes _his2 <<= $girl>>s with a bun in the oven.
+	<<elseif (_S.HeadGirl.fetish == "pregnancy" && isItemAccessible.entry("a huge empathy belly", "bellyAccessory", $slaves[$i]) == true)>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName wearing an empathy belly, since _he2 likes _his2 <<= $girl>>s with a bun in the oven.
 		<<set $slaves[$i].bellyAccessory = "a large empathy belly">>
-	<<elseif ($HeadGirl.fetish == "pregnancy") && ($slaves[$i].preg > 0) && ($slaves[$i].pregSource == $HeadGirl.ID)>>
-		$HeadGirl.slaveName keeps the pregnant $slaves[$i].slaveName dressed as a slutty maid, since _he2 wants $him to look motherly yet fuckable while _he2 fantasizes about fucking $him pregnant again.
+	<<elseif (_S.HeadGirl.fetish == "pregnancy") && ($slaves[$i].preg > 0) && ($slaves[$i].pregSource == $HeadGirlID)>>
+		_S.HeadGirl.slaveName keeps the pregnant $slaves[$i].slaveName dressed as a slutty maid, since _he2 wants $him to look motherly yet fuckable while _he2 fantasizes about fucking $him pregnant again.
 		<<set $slaves[$i].clothes = "a slutty maid outfit">>
-	<<elseif ($HeadGirl.fetish == "pregnancy")>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 wants $him to look motherly yet fuckable while _he2 fantasizes about putting a bun in $his oven.
+	<<elseif (_S.HeadGirl.fetish == "pregnancy")>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 wants $him to look motherly yet fuckable while _he2 fantasizes about putting a bun in $his oven.
 		<<set $slaves[$i].clothes = "a slutty maid outfit">>
-	<<elseif ($HeadGirl.attrXX > 85)>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName wearing nice lingerie, since girls and girliness turn _him2 on.
+	<<elseif (_S.HeadGirl.attrXX > 85)>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName wearing nice lingerie, since girls and girliness turn _him2 on.
 		<<set $slaves[$i].clothes = "attractive lingerie">>
-	<<elseif ($HeadGirl.attrXY > 85) && ($slaves[$i].dick > 0)>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName wearing slutty netting with a hole for $his dick to stick through, since _he2 likes it bare.
+	<<elseif (_S.HeadGirl.attrXY > 85) && ($slaves[$i].dick > 0)>>
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName wearing slutty netting with a hole for $his dick to stick through, since _he2 likes it bare.
 		<<set $slaves[$i].clothes = "clubslut netting">>
 	<<else>>
-		$HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 likes seeing $his body as $he looks after their suite.
+		_S.HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 likes seeing $his body as $he looks after their suite.
 		<<set $slaves[$i].clothes = "a slutty maid outfit">>
 	<</if>>
 <<else>>
-	$HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 likes seeing $his body as $he looks after their suite.
+	_S.HeadGirl.slaveName keeps $slaves[$i].slaveName dressed as a slutty maid, since _he2 likes seeing $his body as $he looks after their suite.
 	<<set $slaves[$i].clothes = "a slutty maid outfit">>
 <</if>>
 <<set $slaves[$i].devotion += 1>>
 
-<<if $HeadGirl.energy > 95>>
-	<<if $HeadGirl.bald != 1&& $HeadGirl.hLength > 0>>
+<<if _S.HeadGirl.energy > 95>>
+	<<if _S.HeadGirl.bald != 1&& _S.HeadGirl.hLength > 0>>
 		<<if $slaves[$i].bald != 1>>
 			<<if !hasHColorRule($slaves[$i],$defaultRules)>>
-				<<if ($slaves[$i].hColor != $HeadGirl.hColor)>>
-					$HeadGirl.slaveName dyes $slaves[$i].slaveName's hair $HeadGirl.hColor, just like _his2 own, since with nymphomania comes narcissism.
-					<<set $slaves[$i].hColor = $HeadGirl.hColor>>
+				<<if ($slaves[$i].hColor != _S.HeadGirl.hColor)>>
+					_S.HeadGirl.slaveName dyes $slaves[$i].slaveName's hair _S.HeadGirl.hColor, just like _his2 own, since with nymphomania comes narcissism.
+					<<set $slaves[$i].hColor = _S.HeadGirl.hColor>>
 				<</if>>
 			<</if>>
 			<<if !hasHStyleRule($slaves[$i],$defaultRules)>>
-				<<if ($slaves[$i].hStyle != $HeadGirl.hStyle)>>
-					$HeadGirl.slaveName styles $slaves[$i].slaveName's hair just like _his2 own, since with nymphomania comes narcissism.
-					<<set $slaves[$i].hStyle = $HeadGirl.hStyle>>
-					<<set $slaves[$i].hLength = $HeadGirl.hLength>>
+				<<if ($slaves[$i].hStyle != _S.HeadGirl.hStyle)>>
+					_S.HeadGirl.slaveName styles $slaves[$i].slaveName's hair just like _his2 own, since with nymphomania comes narcissism.
+					<<set $slaves[$i].hStyle = _S.HeadGirl.hStyle>>
+					<<set $slaves[$i].hLength = _S.HeadGirl.hLength>>
 				<</if>>
 			<</if>>
 		<<else>>
 			<<if !hasHColorRule($slaves[$i],$defaultRules)>>
-				<<if ($slaves[$i].hColor != $HeadGirl.hColor)>>
-					$HeadGirl.slaveName changes $slaves[$i].slaveName's wig to $HeadGirl.hColor, just like _his2 own hair, since with nymphomania comes narcissism.
-					<<set $slaves[$i].hColor = $HeadGirl.hColor>>
+				<<if ($slaves[$i].hColor != _S.HeadGirl.hColor)>>
+					_S.HeadGirl.slaveName changes $slaves[$i].slaveName's wig to _S.HeadGirl.hColor, just like _his2 own hair, since with nymphomania comes narcissism.
+					<<set $slaves[$i].hColor = _S.HeadGirl.hColor>>
 				<</if>>
 			<</if>>
 			<<if !hasHStyleRule($slaves[$i],$defaultRules)>>
-				<<if ($slaves[$i].hStyle != $HeadGirl.hStyle)>>
-					$HeadGirl.slaveName changes $slaves[$i].slaveName's wig style to look just like _his2 own hair, since with nymphomania comes narcissism.
-					<<set $slaves[$i].hStyle = $HeadGirl.hStyle>>
-					<<set $slaves[$i].hLength = $HeadGirl.hLength>>
+				<<if ($slaves[$i].hStyle != _S.HeadGirl.hStyle)>>
+					_S.HeadGirl.slaveName changes $slaves[$i].slaveName's wig style to look just like _his2 own hair, since with nymphomania comes narcissism.
+					<<set $slaves[$i].hStyle = _S.HeadGirl.hStyle>>
+					<<set $slaves[$i].hLength = _S.HeadGirl.hLength>>
 				<</if>>
 			<</if>>
 		<</if>>
@@ -814,37 +813,37 @@
 	<</if>>
 
 	/* get eye colors, HG can be expected to have at least one working eye due to job requirements */
-	<<set _left = hasLeftEye($HeadGirl) ? getLeftEyeColor($HeadGirl) : getRightEyeColor($HeadGirl)>>
-	<<set _right = hasRightEye($HeadGirl) ? getRightEyeColor($HeadGirl) : _left>>
+	<<set _left = hasLeftEye(_S.HeadGirl) ? getLeftEyeColor(_S.HeadGirl) : getRightEyeColor(_S.HeadGirl)>>
+	<<set _right = hasRightEye(_S.HeadGirl) ? getRightEyeColor(_S.HeadGirl) : _left>>
 
 	/* make sure the slave has the same eye color as the HG */
 	<<if getLeftEyeColor($slaves[$i]) !== _left>>
 		<<run setEyeColor($slaves[$i], _left, "left")>>
-		$HeadGirl.slaveName <<if getLeftEyeType($slaves[$i] === 3)>> changes <<else>> gives <</if>> $slaves[$i].slaveName _left <<if getLeftEyeType($slaves[$i]) === 3>> eye lenses <<elseif getLeftEyeType($slaves[$i]) === 2>> glass eyes <<else>> contact lenses <</if>>, to match _his2 own left eye, since with nymphomania comes narcissism.
+		_S.HeadGirl.slaveName <<if getLeftEyeType($slaves[$i] === 3)>> changes <<else>> gives <</if>> $slaves[$i].slaveName _left <<if getLeftEyeType($slaves[$i]) === 3>> eye lenses <<elseif getLeftEyeType($slaves[$i]) === 2>> glass eyes <<else>> contact lenses <</if>>, to match _his2 own left eye, since with nymphomania comes narcissism.
 	<</if>>
 	<<if getRightEyeColor($slaves[$i]) !== _right>>
 		<<run setEyeColor($slaves[$i], _right, "right")>>
-		$HeadGirl.slaveName <<if getRightEyeType($slaves[$i] === 3)>> changes <<else>> gives <</if>> $slaves[$i].slaveName _right <<if getLeftRightType($slaves[$i]) === 3>> eye lenses <<elseif getLeftRightType($slaves[$i]) === 2>> glass eyes <<else>> contact lenses <</if>>, to match _his2 own right eye, since with nymphomania comes narcissism.
+		_S.HeadGirl.slaveName <<if getRightEyeType($slaves[$i] === 3)>> changes <<else>> gives <</if>> $slaves[$i].slaveName _right <<if getLeftRightType($slaves[$i]) === 3>> eye lenses <<elseif getLeftRightType($slaves[$i]) === 2>> glass eyes <<else>> contact lenses <</if>>, to match _his2 own right eye, since with nymphomania comes narcissism.
 	<</if>>
 	<</if>>
 <</if>>
 
-<<if ($arcologies[0].FSRepopulationFocusPregPolicy == 1 || ($arcologies[0].FSRestart == "unset" && $HeadGirl.fetish == "pregnancy")) && canImpreg($slaves[$i], $HeadGirl) && ($dietFertility == 1)>>
-	$HeadGirl.slaveName thinks $slaves[$i].slaveName could do with a bigger bump <<if $arcologies[0].FSRepopulationFocusPregPolicy>>since pregnant <<= $girl>>s are all the rage in your arcology <</if>>
+<<if ($arcologies[0].FSRepopulationFocusPregPolicy == 1 || ($arcologies[0].FSRestart == "unset" && _S.HeadGirl.fetish == "pregnancy")) && canImpreg($slaves[$i], _S.HeadGirl) && ($dietFertility == 1)>>
+	_S.HeadGirl.slaveName thinks $slaves[$i].slaveName could do with a bigger bump <<if $arcologies[0].FSRepopulationFocusPregPolicy>>since pregnant <<= $girl>>s are all the rage in your arcology <</if>>
 	<<if _diet != "fertility">>
 		and assigns $him a diet to promote ovulation.
 	<<else>>
 		and keeps $him on $his fertility diet.
 	<</if>>
 <<elseif ($slaves[$i].weight < 100 && $arcologies[0].FSHedonisticDecadence >= 20) || ($slaves[$i].weight < -10)>>
-	$HeadGirl.slaveName thinks $slaves[$i].slaveName could use a little extra weight <<if $arcologies[0].FSHedonisticDecadence >= 20>>since plump <<= $girl>>s are all the rage in your arcology <</if>>
+	_S.HeadGirl.slaveName thinks $slaves[$i].slaveName could use a little extra weight <<if $arcologies[0].FSHedonisticDecadence >= 20>>since plump <<= $girl>>s are all the rage in your arcology <</if>>
 	<<if _diet != "fattening">>
 		and assigns $him a diet to fatten $him up.
 	<<else>>
 		and keeps $him on $his fattening diet.
 	<</if>>
 <<elseif $slaves[$i].weight > 30 && $arcologies[0].FSHedonisticDecadence == "unset">>
-	$HeadGirl.slaveName thinks $slaves[$i].slaveName is a little too porky
+	_S.HeadGirl.slaveName thinks $slaves[$i].slaveName is a little too porky
 	<<if _diet != "restricted">>
 		and puts $him on a diet.
 	<<else>>
@@ -856,8 +855,8 @@
 		(
 			($arcologies[0].FSPhysicalIdealist > 0 && $arcologies[0].FSPhysicalIdealistLaw == 0) ||
 			(
-				($HeadGirl.fetish == "submissive" || $HeadGirl.fetish == "masochist") &&
-				$HeadGirl.fetishKnown == 1)
+				(_S.HeadGirl.fetish == "submissive" || _S.HeadGirl.fetish == "masochist") &&
+				_S.HeadGirl.fetishKnown == 1)
 			)
 		)
 		||
@@ -872,10 +871,10 @@
 			!isAmputee($slaves[$i])
 		)
 	)>>
-	$HeadGirl.slaveName thinks $slaves[$i].slaveName
-	<<if $HeadGirl.fetish == "submissive">>
+	_S.HeadGirl.slaveName thinks $slaves[$i].slaveName
+	<<if _S.HeadGirl.fetish == "submissive">>
 		could use bigger muscles to better dominate _him2 with in bed
-	<<elseif $HeadGirl.fetish == "masochist">>
+	<<elseif _S.HeadGirl.fetish == "masochist">>
 		could use bigger muscles to better spank _him2
 	<<elseif $arcologies[0].FSPhysicalIdealist > 0>>
 		could use bigger muscles to support your societal goals
@@ -887,8 +886,8 @@
 	<<else>>
 		and keeps $him working out.
 	<</if>>
-<<elseif $slaves[$i].balls > 0 && $HeadGirl.fetish == "cumslut" && $HeadGirl.fetishKnown == 1 && $cumProDiet == 1>>
-	$HeadGirl.slaveName loves cum, so _he2
+<<elseif $slaves[$i].balls > 0 && _S.HeadGirl.fetish == "cumslut" && _S.HeadGirl.fetishKnown == 1 && $cumProDiet == 1>>
+	_S.HeadGirl.slaveName loves cum, so _he2
 	<<if _diet != "cum production">>
 		puts
 	<<else>>
@@ -896,7 +895,7 @@
 	<</if>>
 	$slaves[$i].slaveName on a diet designed to make $him cum harder and stronger.
 <<else>>
-	$HeadGirl.slaveName thinks $slaves[$i].slaveName is fine as is
+	_S.HeadGirl.slaveName thinks $slaves[$i].slaveName is fine as is
 	<<if _diet != "healthy">>
 		and puts $him on a normal diet.
 	<<else>>
@@ -905,100 +904,100 @@
 <</if>>
 
 <<if $HGSuiteHormones != 0 && _hormones != 2>>
-	$HeadGirl.slaveName puts $slaves[$i].slaveName on intensive female hormones, since _he2 expects $him to act as the submissive, feminine partner.
+	_S.HeadGirl.slaveName puts $slaves[$i].slaveName on intensive female hormones, since _he2 expects $him to act as the submissive, feminine partner.
 <</if>>
 
 <<if $HGSuiteDrugs != 0>>
 	<<if ($slaves[$i].health.condition < 60)>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName curatives, since _he2 wants a shiningly healthy living partner.
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName curatives, since _he2 wants a shiningly healthy living partner.
 	<</if>>
 	<<if ($slaves[$i].lips <= 20)>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName lip injections, since _he2 wants to get oral from plump, luscious lips.
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName lip injections, since _he2 wants to get oral from plump, luscious lips.
 	<<elseif $arcologies[0].FSSlaveProfessionalismResearch == 1 && $HGSuiteEquality != 0 && canImproveIntelligence($slaves[$i])>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName psychostimulants, since $him being smarter will benefit them both when $he is in charge.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 18>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName hyper ass injections, since _he2 never once thought it possible to fuck an ass as big as _he2 is.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 8>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName ass injections, since _he2 likes comfortable padding as _he2 fucks a butt.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "cumslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].balls < 100 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName hyper testicle injections, since _he2 wants to swim in a river of cum.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "cumslut") && $slaves[$i].balls < 10 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName testicle injections, since _he2 wants $slaves[$i].slaveName shooting bigger loads.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "masochist" || $HeadGirl.fetish == "submissive") && canImproveHeight($slaves[$i])>>
-		$HeadGirl.slaveName has a subconscious need to be hurt by the biggest, strongest $girl possible, so $he gives $slaves[$i].slaveName injections of growth stimulants to make $him grow taller.
-	<<elseif ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetish == "boobs") && ($slaves[$i].boobs < 8000)>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName <<if $arcologies[0].FSAssetExpansionistResearch == 1>>hyper <</if>> tit injections, since as far as _he2's concerned there's no such thing as too much boob.
-	<<elseif ($HGTastes == 4 || $HeadGirl.fetish == "pregnancy") && canImpreg($slaves[$i], $HeadGirl) && $arcologies[0].FSRestart == "unset">>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName fertility enhancers, since _he2 wants to see $slaves[$i].slaveName heavy with child.
-	<<elseif $arcologies[0].FSRepopulationFocusPregPolicy == 1 && canImpreg($slaves[$i], $HeadGirl)>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName fertility enhancers, since pregnancy is popular and _he2 wants $slaves[$i].slaveName to look hot.
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName psychostimulants, since $him being smarter will benefit them both when $he is in charge.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 18>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName hyper ass injections, since _he2 never once thought it possible to fuck an ass as big as _he2 is.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "buttslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].butt < 8>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName ass injections, since _he2 likes comfortable padding as _he2 fucks a butt.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "cumslut") && $arcologies[0].FSAssetExpansionistResearch == 1 && $slaves[$i].balls < 100 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName hyper testicle injections, since _he2 wants to swim in a river of cum.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "cumslut") && $slaves[$i].balls < 10 && $slaves[$i].balls > 0 && $slaves[$i].dick > 0>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName testicle injections, since _he2 wants $slaves[$i].slaveName shooting bigger loads.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "masochist" || _S.HeadGirl.fetish == "submissive") && canImproveHeight($slaves[$i])>>
+		_S.HeadGirl.slaveName has a subconscious need to be hurt by the biggest, strongest $girl possible, so $he gives $slaves[$i].slaveName injections of growth stimulants to make $him grow taller.
+	<<elseif (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetish == "boobs") && ($slaves[$i].boobs < 8000)>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName <<if $arcologies[0].FSAssetExpansionistResearch == 1>>hyper <</if>> tit injections, since as far as _he2's concerned there's no such thing as too much boob.
+	<<elseif ($HGTastes == 4 || _S.HeadGirl.fetish == "pregnancy") && canImpreg($slaves[$i], _S.HeadGirl) && $arcologies[0].FSRestart == "unset">>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName fertility enhancers, since _he2 wants to see $slaves[$i].slaveName heavy with child.
+	<<elseif $arcologies[0].FSRepopulationFocusPregPolicy == 1 && canImpreg($slaves[$i], _S.HeadGirl)>>
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName fertility enhancers, since pregnancy is popular and _he2 wants $slaves[$i].slaveName to look hot.
 	<<elseif $HGTastes > 1>>
 		<<if ($slaves[$i].lips <= 70)>>
-			$HeadGirl.slaveName gives $slaves[$i].slaveName lip injections, since _he2 thinks $slaves[$i].slaveName should have lips so big $he can barely speak.
+			_S.HeadGirl.slaveName gives $slaves[$i].slaveName lip injections, since _he2 thinks $slaves[$i].slaveName should have lips so big $he can barely speak.
 		<<elseif ($slaves[$i].boobs > (125*Math.pow($slaves[$i].butt, 2)))>>
-			$HeadGirl.slaveName gives $slaves[$i].slaveName ass injections, since _he2 loves curves and thinks $slaves[$i].slaveName's butt needs the most work.
+			_S.HeadGirl.slaveName gives $slaves[$i].slaveName ass injections, since _he2 loves curves and thinks $slaves[$i].slaveName's butt needs the most work.
 		<<else>>
-			$HeadGirl.slaveName gives $slaves[$i].slaveName boob injections, since _he2 loves curves and thinks $slaves[$i].slaveName's tits need the most work.
+			_S.HeadGirl.slaveName gives $slaves[$i].slaveName boob injections, since _he2 loves curves and thinks $slaves[$i].slaveName's tits need the most work.
 		<</if>>
 	<</if>>
 <<else>>
 	<<if ($slaves[$i].health.condition < 100)>>
-		$HeadGirl.slaveName gives $slaves[$i].slaveName curatives, since _his2 assistant could be healthier.
+		_S.HeadGirl.slaveName gives $slaves[$i].slaveName curatives, since _his2 assistant could be healthier.
 	<</if>>
 <</if>>
 
 <<if $HGSuiteSurgery != 0>>
 	<<if $arcologies[0].FSBodyPurist == "unset">>
-		$HeadGirl.slaveName sends $assistant.name notes on what surgical modifications _he2 wants for $slaves[$i].slaveName.
+		_S.HeadGirl.slaveName sends $assistant.name notes on what surgical modifications _he2 wants for $slaves[$i].slaveName.
 		<<= rulesAutosurgery($slaves[$i])>>
 	<</if>>
-	<<if $HeadGirl.fetishKnown == 1>>
-		<<if ($HeadGirl.fetish == "sadist") && ($HeadGirl.fetishStrength > 95) && ($seeExtreme == 1) && ($slaves[$i].fetish != "mindbroken")>>
+	<<if _S.HeadGirl.fetishKnown == 1>>
+		<<if (_S.HeadGirl.fetish == "sadist") && (_S.HeadGirl.fetishStrength > 95) && ($seeExtreme == 1) && ($slaves[$i].fetish != "mindbroken")>>
 			<<if ($slaves[$i].health.condition > 40)>>
 				<<if hasAnyNaturalLimbs($slaves[$i]) && ($slaves[$i].devotion < -20)>>
-					$HeadGirl.slaveName decides to extract the ultimate in sadistic pleasure from $slaves[$i].slaveName, and directs the autosurgery to @@.red;amputate $his <<if hasAnyArms($slaves[$i])>>arm<<if hasBothArms($slaves[$i])>>s<</if>> <<if hasAnyLegs($slaves[$i])>>and<</if>><</if>><<if hasAnyLegs($slaves[$i])>> leg<<if hasBothLegs($slaves[$i])>>s<</if>><</if>>.@@ _He2 carefully describes the entire process to the sobbing slave @@.gold;in minute detail@@ before allowing $him to be sedated.
+					_S.HeadGirl.slaveName decides to extract the ultimate in sadistic pleasure from $slaves[$i].slaveName, and directs the autosurgery to @@.red;amputate $his <<if hasAnyArms($slaves[$i])>>arm<<if hasBothArms($slaves[$i])>>s<</if>> <<if hasAnyLegs($slaves[$i])>>and<</if>><</if>><<if hasAnyLegs($slaves[$i])>> leg<<if hasBothLegs($slaves[$i])>>s<</if>><</if>>.@@ _He2 carefully describes the entire process to the sobbing slave @@.gold;in minute detail@@ before allowing $him to be sedated.
 					<<set $slaves[$i].devotion -= 20, $slaves[$i].trust -= 20>>
 					<<run removeLimbs($slaves[$i], "all"), surgeryDamage($slaves[$i], 40)>>
 				<</if>>
 				<<if ($slaves[$i].balls > 0) && ($slaves[$i].devotion <= 50)>>
-					$HeadGirl.slaveName decides to have some once-only sadistic pleasure, and carefully straps $slaves[$i].slaveName into the surgery until $he's completely immobile. $HeadGirl.slaveName situates <<if canSee($slaves[$i])>>a mirror so the terrified $girl can see $his own crotch<<else>>$him so that the terrified $girl can sense what part of $his body is being manipulated even through the anesthetics<</if>>, gets behind $him, and carefully sodomizes $him during the entire process of @@.red;castration.@@ $slaves[$i].slaveName is anesthetized down there and can't feel the brutal anal rape, but $his abuser orgasms repeatedly to $him @@.gold;weeping at the <<if canSee($slaves[$i])>>sight<<else>>muted sensation<</if>> of being gelded and raped at once.@@
+					_S.HeadGirl.slaveName decides to have some once-only sadistic pleasure, and carefully straps $slaves[$i].slaveName into the surgery until $he's completely immobile. _S.HeadGirl.slaveName situates <<if canSee($slaves[$i])>>a mirror so the terrified $girl can see $his own crotch<<else>>$him so that the terrified $girl can sense what part of $his body is being manipulated even through the anesthetics<</if>>, gets behind $him, and carefully sodomizes $him during the entire process of @@.red;castration.@@ $slaves[$i].slaveName is anesthetized down there and can't feel the brutal anal rape, but $his abuser orgasms repeatedly to $him @@.gold;weeping at the <<if canSee($slaves[$i])>>sight<<else>>muted sensation<</if>> of being gelded and raped at once.@@
 					<<set $slaves[$i].devotion -= 20, $slaves[$i].trust -= 20, $slaves[$i].balls = 0>>
 					<<run surgeryDamage($slaves[$i], 20)>>
 				<</if>>
 			<</if>>
 			<<if ($slaves[$i].devotion < -90) && ($slaves[$i].fetish != "mindbroken")>>
-				$HeadGirl.slaveName tires of $slaves[$i].slaveName's protests that $HeadGirl.slaveName is a monster for hurting $him this way and $his complaints that $he deserves better. $HeadGirl.slaveName decides that it'll be just as much fun and a lot less trouble to mistreat $slaves[$i].slaveName once $he's mindbroken, and sends $him in for @@.red;chemical lobotomization.@@
+				_S.HeadGirl.slaveName tires of $slaves[$i].slaveName's protests that _S.HeadGirl.slaveName is a monster for hurting $him this way and $his complaints that $he deserves better. _S.HeadGirl.slaveName decides that it'll be just as much fun and a lot less trouble to mistreat $slaves[$i].slaveName once $he's mindbroken, and sends $him in for @@.red;chemical lobotomization.@@
 				<<set $slaves[$i].fetish = "mindbroken">>
 				<<run surgeryDamage($slaves[$i], 20)>>
 			<</if>>
-		<<elseif ($HeadGirl.fetish == "dom") && ($HeadGirl.fetishStrength > 60) && ($seeExtreme == 1) && ($slaves[$i].fetish != "mindbroken") && ($slaves[$i].devotion <= 50)>>
+		<<elseif (_S.HeadGirl.fetish == "dom") && (_S.HeadGirl.fetishStrength > 60) && ($seeExtreme == 1) && ($slaves[$i].fetish != "mindbroken") && ($slaves[$i].devotion <= 50)>>
 			<<if ($slaves[$i].health.condition > 40)>>
 				<<if ($slaves[$i].heels == 0) && hasAnyNaturalLegs($slaves[$i])>>
-					$HeadGirl.slaveName is not satisfied with $slaves[$i].slaveName's submissiveness, and directs the autosurgery to @@.red;clip $his Achilles tendons.@@ Once the slave is recovered from surgery, $HeadGirl.slaveName removes all shoes from the suite, too, so $slaves[$i].slaveName is forced to crawl like a good little bitch. $slaves[$i].slaveName is @@.gold;angry@@ and @@.gold;frightened.@@
+					_S.HeadGirl.slaveName is not satisfied with $slaves[$i].slaveName's submissiveness, and directs the autosurgery to @@.red;clip $his Achilles tendons.@@ Once the slave is recovered from surgery, _S.HeadGirl.slaveName removes all shoes from the suite, too, so $slaves[$i].slaveName is forced to crawl like a good little bitch. $slaves[$i].slaveName is @@.gold;angry@@ and @@.gold;frightened.@@
 					<<set $slaves[$i].devotion -= 5, $slaves[$i].heels = 1, $slaves[$i].shoes = "none">>
 					<<run surgeryDamage($slaves[$i], 20)>>
 				<</if>>
 				<<if ($slaves[$i].balls > 0)>>
-					$HeadGirl.slaveName decides that it's counterproductive for $slaves[$i].slaveName to be able to get hard, and sends $him in to have $his @@.red;balls removed.@@ $slaves[$i].slaveName is horrified, but $HeadGirl.slaveName uses $his anus with such persistence and mercilessness that the poor gelding doesn't have much time to mourn. $slaves[$i].slaveName is badly @@.gold;frightened@@ by $his new role as nothing but a recipient of anal.
+					_S.HeadGirl.slaveName decides that it's counterproductive for $slaves[$i].slaveName to be able to get hard, and sends $him in to have $his @@.red;balls removed.@@ $slaves[$i].slaveName is horrified, but _S.HeadGirl.slaveName uses $his anus with such persistence and mercilessness that the poor gelding doesn't have much time to mourn. $slaves[$i].slaveName is badly @@.gold;frightened@@ by $his new role as nothing but a recipient of anal.
 					<<set $slaves[$i].trust -= 10, $slaves[$i].balls = 0>>
 					<<run surgeryDamage($slaves[$i], 20)>>
 				<</if>>
 			<</if>>
-		<<elseif $HeadGirl.energy > 95>>
+		<<elseif _S.HeadGirl.energy > 95>>
 			<<if ($slaves[$i].health.condition > 40) && ($slaves[$i].heels == 0) && hasAnyNaturalLegs($slaves[$i]) && ($slaves[$i].devotion <= 50) && ($slaves[$i].fetish != "mindbroken")>>
-				$HeadGirl.slaveName wants a good little bimbo who sticks $his butt out all the time, and directs the autosurgery to @@.red;clip $slaves[$i].slaveName's Achilles tendons.@@ Once the slave is recovered from surgery, $HeadGirl.slaveName presents $slaves[$i].slaveName with a new pair of extra-tall heels to totter around in like a good little slut. $slaves[$i].slaveName is @@.gold;angry@@ and @@.gold;frightened.@@
+				_S.HeadGirl.slaveName wants a good little bimbo who sticks $his butt out all the time, and directs the autosurgery to @@.red;clip $slaves[$i].slaveName's Achilles tendons.@@ Once the slave is recovered from surgery, _S.HeadGirl.slaveName presents $slaves[$i].slaveName with a new pair of extra-tall heels to totter around in like a good little slut. $slaves[$i].slaveName is @@.gold;angry@@ and @@.gold;frightened.@@
 				<<set $slaves[$i].devotion -= 5, $slaves[$i].trust -= 5, $slaves[$i].heels = 1, $slaves[$i].shoes = "heels">>
 				<<run surgeryDamage($slaves[$i], 20)>>
 			<</if>>
-		<<elseif $HeadGirl.fetish == "masochist">>
+		<<elseif _S.HeadGirl.fetish == "masochist">>
 			<<if ($slaves[$i].health.condition > 40) && ($slaves[$i].heightImplant == 0) && ($slaves[$i].height < 185) && hasAllLimbs($slaves[$i])>>
-				$HeadGirl.slaveName has a subconscious need to be hurt by the biggest, strongest $girl possible, and directs the autosurgery to extend $slaves[$i].slaveName's arm and leg bones to make $him a little @@.lime;taller.@@
+				_S.HeadGirl.slaveName has a subconscious need to be hurt by the biggest, strongest $girl possible, and directs the autosurgery to extend $slaves[$i].slaveName's arm and leg bones to make $him a little @@.lime;taller.@@
 				<<set $slaves[$i].heightImplant = 1, $slaves[$i].height += 10>>
 				<<run surgeryDamage($slaves[$i], 20)>>
 			<</if>>
-		<<elseif $HeadGirl.fetish == "submissive">>
+		<<elseif _S.HeadGirl.fetish == "submissive">>
 			<<if ($slaves[$i].health.condition > 40) && ($slaves[$i].heightImplant == 0) && ($slaves[$i].height < 185) && hasAllLimbs($slaves[$i])>>
-				$HeadGirl.slaveName wants to be topped by the biggest, strongest $girl possible, and directs the autosurgery to extend $slaves[$i].slaveName's arm and leg bones to make $him a little @@.lime;taller.@@
+				_S.HeadGirl.slaveName wants to be topped by the biggest, strongest $girl possible, and directs the autosurgery to extend $slaves[$i].slaveName's arm and leg bones to make $him a little @@.lime;taller.@@
 				<<set $slaves[$i].heightImplant = 1, $slaves[$i].height += 10>>
 				<<run surgeryDamage($slaves[$i], 20)>>
 			<</if>>
@@ -1008,29 +1007,28 @@
 
 <<if $HGSuiteEquality != 0>>
 	<<if $slaves[$i].devotion > 50>>
-		$HeadGirl.slaveName and $slaves[$i].slaveName @@.hotpink;really appreciate@@ the equal, alternating Head Girl system, and @@.yellow;switch places at the end of the week.@@
-		<<set $HeadGirl.devotion += 1, $slaves[$i].devotion += 1>>
+		_S.HeadGirl.slaveName and $slaves[$i].slaveName @@.hotpink;really appreciate@@ the equal, alternating Head Girl system, and @@.yellow;switch places at the end of the week.@@
+		<<set _S.HeadGirl.devotion += 1, $slaves[$i].devotion += 1>>
 	<<else>>
-		$slaves[$i].slaveName is not a decent Head Girl candidate, so $HeadGirl.slaveName keeps the position for now.
+		$slaves[$i].slaveName is not a decent Head Girl candidate, so _S.HeadGirl.slaveName keeps the position for now.
 	<</if>>
 <<else>>
 	<<set _hgEnjoyment = $HGSuiteSurgery + $HGSuiteDrugs + $HGSuiteHormones>>
 	<<if _hgEnjoyment > 2>>
 		Your Head Girl @@.hotpink;really enjoys@@ being given total freedom to customize _his2 very own bitch.
-		<<set $HeadGirl.devotion += 2>>
+		<<set _S.HeadGirl.devotion += 2>>
 	<<elseif _hgEnjoyment == 2>>
 		Your Head Girl @@.hotpink;enjoys@@ being given freedom to customize _his2 very own $girl.
-		<<set $HeadGirl.devotion += 1>>
+		<<set _S.HeadGirl.devotion += 1>>
 	<<elseif _hgEnjoyment == 1>>
 		Your Head Girl @@.hotpink;appreciates@@ being given some freedom to customize _his2 own servant.
-		<<set $HeadGirl.devotion += 1>>
+		<<set _S.HeadGirl.devotion += 1>>
 	<</if>>
 <</if>>
 
-<<if ($HeadGirl.relationshipTarget == $slaves[$i].ID)>>
-	<<set ($HeadGirl.relationship = $slaves[$i].relationship)>>
+<<if (_S.HeadGirl.relationshipTarget == $slaves[$i].ID)>>
+	<<set (_S.HeadGirl.relationship = $slaves[$i].relationship)>>
 <</if>>
-<<set $slaves[_iTemp] = $HeadGirl>>
 
 <<if $showEWD == 0>>
 	<<silently>>
diff --git a/src/uncategorized/saLongTermEffects.tw b/src/uncategorized/saLongTermEffects.tw
index 17f1785363d4cb98d9102f8b3a395fde3980e2a5..9996d62b4caa79f07ab29c91b7f666700afe8bef 100644
--- a/src/uncategorized/saLongTermEffects.tw
+++ b/src/uncategorized/saLongTermEffects.tw
@@ -1247,7 +1247,7 @@
 				<<elseif ($slaves[$i].assignment == "be the Schoolteacher")>>
 					$His paraphilia is satisfied by $his work as the Schoolteacher.
 					<<set _para = 1>>
-				<<elseif canWalk($slaves[$i]) && ($slaves[$i].rules.release.slaves === 1 || App.Utils.hasFamilySex($slaves[$i])) && $universalRulesConsent == 0>>
+				<<elseif canMove($slaves[$i]) && ($slaves[$i].rules.release.slaves === 1 || App.Utils.hasFamilySex($slaves[$i])) && $universalRulesConsent == 0>>
 					$His paraphilia makes pinning down and raping other slaves very satisfying.
 					<<set _para = 1>>
 				<<else>>
@@ -1278,7 +1278,7 @@
 				<<elseif ($slaves[$i].assignment == "be the Wardeness")>>
 					$His paraphilia is satisfied by $his work as your Wardeness.
 					<<set _para = 1>>
-				<<elseif canWalk($slaves[$i]) && ($slaves[$i].rules.release.slaves === 1 || App.Utils.hasFamilySex($slaves[$i])) && $universalRulesConsent == 0>>
+				<<elseif canMove($slaves[$i]) && ($slaves[$i].rules.release.slaves === 1 || App.Utils.hasFamilySex($slaves[$i])) && $universalRulesConsent == 0>>
 					$His paraphilia makes pinning down and brutally raping other slaves very satisfying.
 					<<set _para = 1>>
 				<<else>>
@@ -2890,9 +2890,9 @@
 						<</if>>
 					<<case "dom">>
 						<<if $slaves[$i].sexualFlaw != "abusive">>
-							<<if $HeadGirl != 0>>
+							<<if $HeadGirlID != 0>>
 								<<if $HGSeverity >= 0>>
-									<<if $slaves[$i].ID == $HeadGirl.ID>>
+									<<if $slaves[$i].ID == $HeadGirlID>>
 										As Head Girl, $he's plentifully provided with misbehaving slaves to dominate sexually. $He becomes more and more eager until $he's actively @@.yellow;sexually abusive, getting off on the thrill of forcing $himself on other slaves.@@
 										<<set $slaves[$i].sexualFlaw = "abusive">>
 										<<set $slaves[$i].fetishStrength = 100>>
@@ -4464,7 +4464,7 @@
 	<<if canGetPregnant($slaves[$i]) && (($slaves[$i].assignment == "work in the dairy" && $dairyPregSetting == 0) || $slaves[$i].assignment != "work in the dairy")>>
 
 		<<set _conceptionSeed = random(1,100)>>
-		<<set _studIgnoresRules = (_Stud && $universalRulesImpregnation == "Stud" && _Stud.career == "a breeding bull" && _Stud.fetish == "mindbroken" && canWalk(_Stud)) ? 1 : 0>>
+		<<set _studIgnoresRules = (_Stud && $universalRulesImpregnation == "Stud" && _Stud.career == "a breeding bull" && _Stud.fetish == "mindbroken" && canMove(_Stud)) ? 1 : 0>>
 
 		<<if ($universalRulesImpregnation == "PC") && canImpreg($slaves[$i], $PC)>>
 			$slaves[$i].slaveName is ripe for breeding, so you ejaculate inside $him often. When you bore of $his fertile <<if $slaves[$i].mpreg == 1>>ass<<else>>cunt<</if>>, you keep $him around as you fuck other slaves so you can pull out of them, shove your cock into $him, and fill $him with your seed anyway.
@@ -4501,31 +4501,31 @@
 			<<set $activeSlave = $slaves[$i]>><<if $slaves[$i].mpreg == 1>><<= VCheck.Anal(10)>><<else>><<= VCheck.Vaginal(10)>><</if>><<set $slaves[$i] = $activeSlave>>
 
 		<<elseif (($slaves[$i].vagina == 0) || (($slaves[$i].anus == 0) && ($slaves[$i].mpreg > 0))) && !_studIgnoresRules>>
-		<<elseif ($HeadGirl != 0) && ($HeadGirl.dick > 0) && ($slaves[$i].ID != $HeadGirl.ID) && ($universalRulesImpregnation == "HG") && canPenetrate($HeadGirl)>>
-			<<setLocalPronouns $HeadGirl 2>>
+		<<elseif ($HeadGirlID != 0) && (_S.HeadGirl.dick > 0) && ($slaves[$i].ID != $HeadGirlID) && ($universalRulesImpregnation == "HG") && canPenetrate(_S.HeadGirl)>>
+			<<setLocalPronouns _S.HeadGirl 2>>
 			<<if $slaves[$i].HGExclude == 1>>
-				It's $HeadGirl.slaveName's responsibility to impregnate fertile slaves, but your Head Girl is forbidden from impregnating $slaves[$i].slaveName.
+				It's _S.HeadGirl.slaveName's responsibility to impregnate fertile slaves, but your Head Girl is forbidden from impregnating $slaves[$i].slaveName.
 			<<elseif $universalHGImpregnateMasterSuiteToggle == 1 && ["serve in the master suite", "be your Concubine"].includes($slaves[$i].assignment)>>
-				It's $HeadGirl.slaveName's responsibility to impregnate fertile slaves, but $slaves[$i].slaveName is off-limits.
+				It's _S.HeadGirl.slaveName's responsibility to impregnate fertile slaves, but $slaves[$i].slaveName is off-limits.
 			<<elseif $HGCum == 0>>
-				It's $HeadGirl.slaveName's responsibility to impregnate fertile slaves, but your Head Girl can only fuck a limited number of slaves enough to ensure impregnation each week.
-			<<elseif !canBreed($slaves[$i], $HeadGirl)>>
-				It's $HeadGirl.slaveName's responsibility to impregnate fertile slaves, but $HeadGirl.slaveName's sperm is unable to fertilize $slaves[$i].slaveName's ova, so _he2 doesn't waste _his2 seed trying.
+				It's _S.HeadGirl.slaveName's responsibility to impregnate fertile slaves, but your Head Girl can only fuck a limited number of slaves enough to ensure impregnation each week.
+			<<elseif !canBreed($slaves[$i], _S.HeadGirl)>>
+				It's _S.HeadGirl.slaveName's responsibility to impregnate fertile slaves, but _S.HeadGirl.slaveName's sperm is unable to fertilize $slaves[$i].slaveName's ova, so _he2 doesn't waste _his2 seed trying.
 			<<else>>
-				It's $HeadGirl.slaveName's responsibility to get $him pregnant, a task your
-				<<if ($HeadGirl.fetish == "pregnancy") && ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetishStrength > 60)>>
+				It's _S.HeadGirl.slaveName's responsibility to get $him pregnant, a task your
+				<<if (_S.HeadGirl.fetish == "pregnancy") && (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetishStrength > 60)>>
 					pregnancy fetishist Head Girl is @@.hotpink;extremely pleased@@ to take on.
-					<<set $HeadGirl.devotion += 2>>
-					<<if ($HeadGirl.fetishStrength <= 95)>>
+					<<set _S.HeadGirl.devotion += 2>>
+					<<if (_S.HeadGirl.fetishStrength <= 95)>>
 						The opportunity @@.lightcoral;strengthens _his2 pregnancy fetish@@ by indulgence.
-						<<set $HeadGirl.fetishStrength += 4>>
+						<<set _S.HeadGirl.fetishStrength += 4>>
 					<</if>>
-				<<elseif $HeadGirl.career == "a breeding bull">>
+				<<elseif _S.HeadGirl.career == "a breeding bull">>
 					Head Girl feels @@.hotpink;_he2 was meant for.@@
-					<<set $HeadGirl.devotion += 1>>
-				<<elseif ($HeadGirl.attrXX > 65) && ($HeadGirl.attrKnown == 1)>>
+					<<set _S.HeadGirl.devotion += 1>>
+				<<elseif (_S.HeadGirl.attrXX > 65) && (_S.HeadGirl.attrKnown == 1)>>
 					<<if $slaves[$i].mpreg == 1>>ass<<else>>pussy<</if>>-hungry Head Girl is @@.hotpink;happy@@ to take on.
-					<<set $HeadGirl.devotion += 1>>
+					<<set _S.HeadGirl.devotion += 1>>
 				<<else>>
 					Head Girl approaches dutifully.
 				<</if>>
@@ -4533,11 +4533,11 @@
 					<<if $slaves[$i].career == "a dairy cow" && ($slaves[$i].devotion <= 20)>>
 						$slaves[$i].slaveName feels a need to be bred by the Head Girl, and submits $himself to $his superior's virile cock until @@.lime;conception@@ is verified.
 					<<elseif ($slaves[$i].devotion <= 20) && ($slaves[$i].trust >= -20)>>
-						<<if (($HeadGirl.fetish == "sadist") || ($HeadGirl.fetish == "dom")) && ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetishStrength > 60)>>
-							_His2 interest is piqued, however, when $slaves[$i].slaveName shows signs of resistance. $HeadGirl.slaveName @@.hotpink;enthusiastically@@ @@.mediumorchid;rapes the poor $girl@@ pregnant, ejaculating inside _his2 victim more often than is really necessary for @@.lime;conception.@@
-							<<set $HeadGirl.devotion += 2, $slaves[$i].devotion -= 5>>
+						<<if ((_S.HeadGirl.fetish == "sadist") || (_S.HeadGirl.fetish == "dom")) && (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetishStrength > 60)>>
+							_His2 interest is piqued, however, when $slaves[$i].slaveName shows signs of resistance. _S.HeadGirl.slaveName @@.hotpink;enthusiastically@@ @@.mediumorchid;rapes the poor $girl@@ pregnant, ejaculating inside _his2 victim more often than is really necessary for @@.lime;conception.@@
+							<<set _S.HeadGirl.devotion += 2, $slaves[$i].devotion -= 5>>
 						<<else>>
-							$slaves[$i].slaveName tries to resist _him2, so $HeadGirl.slaveName is forced to @@.mediumorchid;rape the poor $girl@@ pregnant, regularly ejaculating inside $him until @@.lime;conception@@ is confirmed.
+							$slaves[$i].slaveName tries to resist _him2, so _S.HeadGirl.slaveName is forced to @@.mediumorchid;rape the poor $girl@@ pregnant, regularly ejaculating inside $him until @@.lime;conception@@ is confirmed.
 							<<set $slaves[$i].devotion -= 4>>
 						<</if>>
 						<<if ($slaves[$i].sexualFlaw == "none")>>
@@ -4549,11 +4549,11 @@
 							<</if>>
 						<</if>>
 					<<elseif ($slaves[$i].devotion <= 20)>>
-						<<if (($HeadGirl.fetish == "sadist") || ($HeadGirl.fetish == "dom")) && ($HeadGirl.fetishKnown == 1) && ($HeadGirl.fetishStrength > 60)>>
-							_His2 interest is piqued, however, when it becomes clear that $slaves[$i].slaveName, though fearfully obedient, is not happy with being bred. $HeadGirl.slaveName @@.hotpink;enthusiastically@@ ensures that _his2 victim @@.mediumorchid;does not enjoy@@ a week of being @@.lime;raped pregnant.@@
-							<<set $HeadGirl.devotion += 2, $slaves[$i].devotion -= 3>>
+						<<if ((_S.HeadGirl.fetish == "sadist") || (_S.HeadGirl.fetish == "dom")) && (_S.HeadGirl.fetishKnown == 1) && (_S.HeadGirl.fetishStrength > 60)>>
+							_His2 interest is piqued, however, when it becomes clear that $slaves[$i].slaveName, though fearfully obedient, is not happy with being bred. _S.HeadGirl.slaveName @@.hotpink;enthusiastically@@ ensures that _his2 victim @@.mediumorchid;does not enjoy@@ a week of being @@.lime;raped pregnant.@@
+							<<set _S.HeadGirl.devotion += 2, $slaves[$i].devotion -= 3>>
 						<<else>>
-							$slaves[$i].slaveName, though fearfully obedient, is not happy with being bred, but $HeadGirl.slaveName @@.mediumorchid;rapes the poor $girl@@ pregnant anyway, regularly ejaculating inside $him until @@.lime;conception@@ is confirmed.
+							$slaves[$i].slaveName, though fearfully obedient, is not happy with being bred, but _S.HeadGirl.slaveName @@.mediumorchid;rapes the poor $girl@@ pregnant anyway, regularly ejaculating inside $him until @@.lime;conception@@ is confirmed.
 							<<set $slaves[$i].devotion -= 2>>
 						<</if>>
 						<<if ($slaves[$i].sexualFlaw == "none")>>
@@ -4591,18 +4591,18 @@
 						<</if>>
 					<</if>>
 				<</if>> /* closes not Fuckdoll not mindbroken */
-				<<set $slaves[$i].preg = 1, $slaves[$i].pregSource = $HeadGirl.ID, $slaves[$i].pregWeek = 1, $slaves[$i].pregKnown = 1, $HGCum -= 1, $HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
+				<<set $slaves[$i].preg = 1, $slaves[$i].pregSource = $HeadGirlID, $slaves[$i].pregWeek = 1, $slaves[$i].pregKnown = 1, $HGCum -= 1, _S.HeadGirl.counter.penetrative += 10, $penetrativeTotal += 10>>
 				<<set $slaves[$i].pregType = setPregType($slaves[$i])>>
-				<<set WombImpregnate($slaves[$i], $slaves[$i].pregType, $HeadGirl.ID, 1)>>
+				<<set WombImpregnate($slaves[$i], $slaves[$i].pregType, $HeadGirlID, 1)>>
 				<<set $activeSlave = $slaves[$i]>><<if $slaves[$i].mpreg == 1>><<= VCheck.Anal(10)>><<else>><<= VCheck.Vaginal(10)>><</if>><<set $slaves[$i] = $activeSlave>>
-				<<set _saLTE = $slaveIndices[$HeadGirl.ID]>>
+				<<set _saLTE = $slaveIndices[$HeadGirlID]>>
 				<<if def _saLTE>>
-					<<set $slaves[_saLTE] = $HeadGirl>>
+					<<set $slaves[_saLTE] = _S.HeadGirl>>
 				<</if>>
 			<</if>> /* closes HG impregnation */
 		<<elseif _Stud && $slaves[$i].ID != $StudID && $universalRulesImpregnation == "Stud">>
 			<<setLocalPronouns _Stud 2>>
-			<<if ($slaves[$i].StudExclude == 1 || $slaves[$i].breedingMark == 1) && (_Stud.career != "a breeding bull" || _Stud.fetish != "mindbroken" || !canWalk(_Stud))>>
+			<<if ($slaves[$i].StudExclude == 1 || $slaves[$i].breedingMark == 1) && (_Stud.career != "a breeding bull" || _Stud.fetish != "mindbroken" || !canMove(_Stud))>>
 				It's _Stud.slaveName's role to provide sperm for fertile slaves, but $slaves[$i].slaveName is not included on the list.
 			<<elseif $universalHGImpregnateMasterSuiteToggle == 1 && ["serve in the master suite", "be your Concubine"].includes($slaves[$i].assignment)>>
 				It's _Stud.slaveName's role to provide sperm for fertile slaves, but $slaves[$i].slaveName is off-limits.
@@ -4617,7 +4617,7 @@
 				<<if _Stud.fuckdoll > 0>>
 					provide sperm for $him; all $he needs to do is signal the suit to stimulate an ejaculation.
 				<<elseif _Stud.fetish == "mindbroken">>
-					<<if _Stud.career == "a breeding bull" && canWalk(_Stud)>>
+					<<if _Stud.career == "a breeding bull" && canMove(_Stud)>>
 						impregnate fertile slaves, a task the amorous Stud performs with gusto and little regard for rules.
 						<<set _studRandiness = 1>>
 					<<else>>
@@ -6752,8 +6752,6 @@
 	<</if>>
 <</if>>
 
-/* LONG TERM PHYSICAL EFFECTS */
-
 <<if $slaves[$i].fuckdoll == 0>>
 	<<if (!jQuery.isEmptyObject($slaves[$i].brand))>>
 		<<if ($slaves[$i].assignment == "serve the public") || ($slaves[$i].assignment == "whore") || ($slaves[$i].assignment == "work in the brothel") || ($slaves[$i].assignment == "serve in the club")>>
@@ -6771,6 +6769,10 @@
 	<</if>>
 <</if>>
 
+/* LONG TERM PHYSICAL EFFECTS */
+/* start here!
+<<= App.SlaveAssignment.longTermPhysicalEffects($slaves[$i])>>
+*/
 <<if $slaves[$i].fuckdoll == 0 && $slaves[$i].fetish != "mindbroken">>
 	<<set _visionFlag = 0>>
 	<<if getBestVision($slaves[$i]) === 0>>
@@ -7669,67 +7671,250 @@
 <</if>>
 
 <<if $slaves[$i].fuckdoll == 0>>
-	<<if !canWalk($slaves[$i])>>
-		$He is immobilized by $his
+	<<if !canWalk($slaves[$i]) || !canMove($slaves[$i])>>
+		<<set _mobility = 1>>
 		<<set _anchors = []>>
-		<<if tooBigBreasts($slaves[$i])>>
-			<<set _anchors.push("tits")>>
-		<</if>>
-		<<if tooBigBelly($slaves[$i])>>
-			<<set _anchors.push("belly")>>
-		<</if>>
-		<<if tooBigDick($slaves[$i])>>
-			<<set _anchors.push("dick")>>
-		<</if>>
-		<<if tooBigButt($slaves[$i])>>
-			<<set _anchors.push("butt")>>
-		<</if>>
-		<<if tooBigBalls($slaves[$i])>>
-			<<set _anchors.push("balls")>>
-		<</if>>
-		<<if tooFatSlave($slaves[$i])>>
-			<<set _anchors.push("fat body")>>
+		<<if !canMove($slaves[$i])>>
+			<<set _mobility = 0>>
+			<<if tooFatSlave($slaves[$i])>>
+				<<set _anchors.push("fat body")>>
+			<</if>>
+			<<if ($slaves[$i].physicalAge <= 3)>>
+				<<if $slaves[$i].boobs > 25000 + ($slaves[$i].muscles * 20)>>
+					<<set _anchors.push("tits")>>
+				<</if>>
+				<<if $slaves[$i].belly >= 150000>>
+					<<set _anchors.push("belly")>>
+				<</if>>
+				<<if $slaves[$i].balls >= 30 + ($slaves[$i].muscles * 0.3)>>
+					<<set _anchors.push("balls")>>
+				<</if>>
+			<<elseif ($slaves[$i].physicalAge <= 12)>>
+				<<if $slaves[$i].boobs > 100000 + ($slaves[$i].muscles * 50)>>
+					<<set _anchors.push("tits")>>
+				<</if>>
+				<<if $slaves[$i].belly >= 300000>>
+					<<set _anchors.push("belly")>>
+				<</if>>
+				<<if $slaves[$i].balls >= 60 + ($slaves[$i].muscles * 0.5)>>
+					<<set _anchors.push("balls")>>
+				<</if>>
+			<<elseif ($slaves[$i].physicalAge < 18)>>
+				<<if $slaves[$i].boobs > 250000 + ($slaves[$i].muscles * 100)>>
+					<<set _anchors.push("tits")>>
+				<</if>>
+				<<if $slaves[$i].belly >= 600000>>
+					<<set _anchors.push("belly")>>
+				<</if>>
+				<<if $slaves[$i].balls >= 90 + ($slaves[$i].muscles * 0.7)>>
+					<<set _anchors.push("balls")>>
+				<</if>>
+			<<else>>
+				<<if $slaves[$i].boobs > 500000 + ($slaves[$i].muscles * 200)>>
+					<<set _anchors.push("tits")>>
+				<</if>>
+				<<if $slaves[$i].belly >= 1000000>>
+					<<set _anchors.push("belly")>>
+				<</if>>
+				<<if $slaves[$i].balls >= 90 + ($slaves[$i].muscles * 0.7)>>
+					<<set _anchors.push("balls")>>
+				<</if>>
+			<</if>>
+		<<else>>
+			<<if tooBigBreasts($slaves[$i])>>
+				<<set _anchors.push("tits")>>
+			<</if>>
+			<<if tooBigBelly($slaves[$i])>>
+				<<set _anchors.push("belly")>>
+			<</if>>
+			<<if tooBigDick($slaves[$i])>>
+				<<set _anchors.push("dick")>>
+			<</if>>
+			<<if tooBigButt($slaves[$i])>>
+				<<set _anchors.push("butt")>>
+			<</if>>
+			<<if tooBigBalls($slaves[$i])>>
+				<<set _anchors.push("balls")>>
+			<</if>>
+			<<if tooFatSlave($slaves[$i])>>
+				<<set _anchors.push("fat body")>>
+			<</if>>
 		<</if>>
-		<<if _anchors.length > 0>>
-			<<print _anchors.reduce(function(res, ch, i, arr) { return res + (i === arr.length - 1 ? ' and ' : ', ') + ch; })>>
-			<<if (isAmputee($slaves[$i]))>>
-				<<if _anchors.length > 1>>but ultimately $his limblessness<<else>>or would be if $he had limbs<</if>>,
-			<<elseif (!hasAnyLegs($slaves[$i]))>>
-				<<if _anchors.length > 1>>but ultimately $his leglessness<<else>>or would be if $he had legs<</if>>,
-			<<elseif (!hasBothLegs($slaves[$i]))>>
-				<<if _anchors.length > 1>>but ultimately $his missing leg<<else>>or would be if $he had both legs<</if>>,
-			<<elseif $slaves[$i].heels == 1 && !["boots", "extreme heels", "heels", "pumps"].includes($slaves[$i].shoes)>>
-				<<if _anchors.length > 1>>but ultimately $his heels having been clipped<<else>>or would be if $his heels weren't clipped<</if>>,
+		<<if _mobility>>
+			$His
+			<<if _anchors.length > 0>>
+				<<print _anchors.reduce(function(res, ch, i, arr) { return res + (i === arr.length - 1 ? ' and ' : ', ') + ch; })>>
+				<<if (isAmputee($slaves[$i]))>>
+					<<if _anchors.length > 1>>but ultimately $his limblessness<<else>>or would be if $he had limbs<</if>>,
+				<<elseif (!hasAnyLegs($slaves[$i]))>>
+					<<if _anchors.length > 1>>but ultimately $his leglessness<<else>>or would be if $he had legs<</if>>,
+				<<elseif (!hasBothLegs($slaves[$i]))>>
+					<<if _anchors.length > 1>>but ultimately $his missing leg<<else>>or would be if $he had both legs<</if>>,
+				<<elseif $slaves[$i].heels == 1 && !setup.highHeels.includes($slaves[$i].shoes)>>
+					<<if _anchors.length > 1>>but ultimately $his heels having been clipped<<else>>or would be if $his heels weren't clipped<</if>>,
+				<</if>>
+				render<<if _anchors.length == 1>>s<</if>>
+			<<else>>
+				<<if (isAmputee($slaves[$i]))>>
+					limblessness renders
+				<<elseif (!hasAnyLegs($slaves[$i]))>>
+					leglessness renders
+				<<elseif (!hasBothLegs($slaves[$i]))>>
+					missing leg renders
+				<<elseif $slaves[$i].heels == 1 && !["boots", "extreme heels", "heels"].includes($slaves[$i].shoes)>>
+					clipped heels render
+				<</if>>
 			<</if>>
+			$him incapable of walking,
 		<<else>>
-			<<if (isAmputee($slaves[$i]))>>
-				limblessness,
-			<<elseif (!hasAnyLegs($slaves[$i]))>>
-				leglessness,
-			<<elseif (!hasBothLegs($slaves[$i]))>>
-				missing leg,
-			<<elseif $slaves[$i].heels == 1 && !["boots", "extreme heels", "heels", "pumps"].includes($slaves[$i].shoes)>>
-				heels having been clipped,
+			$He is immobilized by $his
+			<<if _anchors.length > 0>>
+				<<print _anchors.reduce(function(res, ch, i, arr) { return res + (i === arr.length - 1 ? ' and ' : ', ') + ch; })>>
+				<<if (isAmputee($slaves[$i]))>>
+					<<if _anchors.length > 1>>but ultimately $his limblessness<<else>>or would be if $he had limbs<</if>>,
+				<<elseif (!hasAnyLegs($slaves[$i]))>>
+					<<if _anchors.length > 1>>but ultimately $his leglessness<<else>>or would be if $he had legs<</if>>,
+				<</if>>
+			<<else>>
+				<<if (isAmputee($slaves[$i]))>>
+					limblessness,
+				<<elseif (!hasAnyLegs($slaves[$i]))>>
+					leglessness,
+				<</if>>
 			<</if>>
 		<</if>>
-		making $his life more difficult. Other slaves have to help $him at times, which
+		making $his life more difficult.
 		<<if $slaves[$i].fetish != "mindbroken">>
-			<<if $slaves[$i].devotion > 50>>
+			<<if _mobility>>
+				<<if $slaves[$i].rules.mobility == "restrictive">>
+					<<if $slaves[$i].devotion < -20>>
+						$He <span class="mediumorchid">hates being forced</span> to crawl around like some kind of animal, but it keeps $him properly <span class="gold">afraid.</span>
+						<<set $slaves[$i].devotion -= 2>>
+						<<set $slaves[$i].trust -= 4>>
+					<<elseif $slaves[$i].devotion >= -20>>
+						<<if $slaves[$i].fetish == "submissive">>
+							<<if $slaves[$i].fetishKnown == 1>>
+								$He <span class="hotpink">loves being forced</span> to crawl around like some kind of animal.
+								<<set $slaves[$i].devotion += 1>>
+							<<else>>
+								$He seems to enjoy $his forced prostration to anyone that comes $his way. @@.lightcoral;$He's a natural submissive!@@
+								<<set $slaves[$i].fetishKnown = 1>>
+							<</if>>
+							<<set _likesImmobility = 1>>
+						<<elseif $slaves[$i].fetish == "humiliation">>
+							<<if $slaves[$i].fetishKnown == 1>>
+								$He <span class="hotpink">revels in the humiliation</span> of being forced to crawl around in the dirt.
+								<<set $slaves[$i].devotion += 1>>
+							<<else>>
+								$He seems to be excited by the humiliation not being able to walk brings. @@.lightcoral;$He's enjoys being degraded!@@
+								<<set $slaves[$i].fetishKnown = 1>>
+							<</if>>
+							<<set _likesImmobility = 1>>
+						<<elseif $slaves[$i].devotion > 50>>
+							It is your will that $he crawl around on
+							<<if (hasAllLimbs($slaves[$i]))>>
+								all fours,
+							<<else>>
+								the ground,
+							<</if>>
+							so $he embraces it as normality.
+						<<elseif $slaves[$i].devotion > 20>>
+							$He accepts being forced to crawl around on
+							<<if (hasAllLimbs($slaves[$i]))>>
+								all fours
+							<<else>>
+								the ground
+							<</if>>
+							as part of slave life.
+						<<else>>
+							Being forced to crawl around on
+							<<if (hasAllLimbs($slaves[$i]))>>
+								all fours
+							<<else>>
+								the ground
+							<</if>>
+							@@.hotpink;breaks down any remaining dignity@@ $he may have and keeps $him @@.gold;well aware of $his total vulnerability@@ to punishment.
+							<<set $slaves[$i].devotion += 2>>
+							<<set $slaves[$i].trust -= 1>>
+						<</if>>
+					<</if>>
+				<<else>>
+					<<if $slaves[$i].devotion > 50>>
+						@@.mediumaquamarine;$He is thankful@@ that $he serves a <<= WrittenMaster($slaves[$i])>> so kind as to permit $him the tools $he needs to not be reduced to crawling.
+						<<set $slaves[$i].trust += 1>>
+					<<elseif $slaves[$i].devotion > 20>>
+						@@.mediumaquamarine;$He appreciates@@ being given the means to maintain some semblance of mobility.
+						<<set $slaves[$i].trust += 2>>
+					<<elseif $slaves[$i].devotion >= -20>>
+						@@.mediumaquamarine;$He takes full advantage@@ of the provided mobility aids to avoid being forced to crawl everywhere.
+						<<set $slaves[$i].trust += 3>>
+					<<else>>
+						Anything is better than crawling on the floor, so $he makes full use of the provided mobility aids @@.mediumaquamarine;to avoid such an indignity.@@
+					<</if>>
+				<</if>>
+			<<else>>
+				<<if $slaves[$i].devotion < -20>>
+					$He can't can't escape those looking to prey upon $him, <span class="gold">terrifying $him.</span>
+					<<set $slaves[$i].trust -= 4>>
+				<<elseif $slaves[$i].devotion >= -20>>
+					<<if $slaves[$i].fetish == "submissive">>
+						<<if $slaves[$i].fetishKnown == 1>>
+							$He <span class="hotpink">loves having no choice but to be submissive</span> if $he wants assistance.
+							<<set $slaves[$i].devotion += 1>>
+						<<else>>
+							$He seems to enjoy being at the mercy of you and your chattel. @@.lightcoral;$He's a natural submissive!@@
+							<<set $slaves[$i].fetishKnown = 1>>
+						<</if>>
+						<<set _likesImmobility = 1>>
+					<<elseif $slaves[$i].fetish == "humiliation">>
+						<<if $slaves[$i].fetishKnown == 1>>
+							$He <span class="hotpink">revels in the humiliation</span> of being completely stripped of any ability to do things on $his own.
+							<<set $slaves[$i].devotion += 1>>
+						<<else>>
+							$He seems to be excited by the humiliation complete immobility brings. @@.lightcoral;$He's enjoys being degraded!@@
+							<<set $slaves[$i].fetishKnown = 1>>
+						<</if>>
+						<<set _likesImmobility = 1>>
+					<<elseif $slaves[$i].devotion > 50>>
+						Since you want to keep $him from moving, $he's perfectly happy to sit around and wait for your attention.
+					<<elseif $slaves[$i].devotion > 20>>
+						$He accepts $his inability to move as part of slave life.
+					<<else>>
+						Being completely unable to move leaves $him at the mercy of others, @@.hotpink;destroying any semblance of self-reliance $he has@@ and keeps $him @@.gold;well aware of $his total vulnerability@@ to punishment.
+						<<set $slaves[$i].devotion += 3>>
+						<<set $slaves[$i].trust -= 1>>
+					<</if>>
+				<</if>>
+			<</if>>
+		<</if>>
+		<<if _mobility>>
+			Other slaves have to help $him at times, which
+		<<else>>
+			$He requires help to do nearly everything, which
+		<</if>>
+		<<if $slaves[$i].fetish != "mindbroken">>
+			<<if _likesImmobility>>
+				suits $his fetish just fine.
+			<<elseif $slaves[$i].devotion > 50>>
 				the devoted slave bears with equanimity.
 			<<elseif $slaves[$i].devotion > 20>>
 				$he can't help @@.mediumorchid;resenting@@ a little.
 				<<set $slaves[$i].devotion -= 1>>
 			<<elseif $slaves[$i].devotion >= -20>>
-				$he @@.mediumorchid;actively resents.@@
-				<<set $slaves[$i].devotion -= 2>>
+				<<if $slaves[$i].trust >= -20>>
+					$he @@.mediumorchid;actively resents.@@
+					<<set $slaves[$i].devotion -= 2>>
+				<<else>>
+					$he appreciates in $his frightening life.
+					<<set $slaves[$i].trust += 1>>
+				<</if>>
 			<<else>>
-				$he @@.mediumorchid;utterly detests.@@
-				<<set $slaves[$i].devotion -= 2>>
-			<</if>>
-			<<if $slaves[$i].devotion < 10>>
 				<<if $slaves[$i].trust >= -20>>
-					$He's @@.gold;aware of $his total vulnerability@@ to punishment.
-					<<set $slaves[$i].trust -= 1>>
+					$he @@.mediumorchid;utterly detests.@@
+					<<set $slaves[$i].devotion -= 2>>
+				<<else>>
+					$he needs in these dark times.
+					<<set $slaves[$i].trust += 2>>
 				<</if>>
 			<</if>>
 		<<else>>
@@ -7745,7 +7930,7 @@
 				<</if>>
 				<<set $slaves[$i].muscles -= 3>>
 			<</if>>
-		<<elseif !canWalk($slaves[$i]) && ($slaves[$i].diet != "muscle building")>>
+		<<elseif !canMove($slaves[$i]) && ($slaves[$i].diet != "muscle building")>>
 			<<if $slaves[$i].muscles > -80>>
 				Since $he is effectively incapable of moving $himself,
 				<<if $universalRulesImmobileSlavesMaintainMuscles == 1 && $slaves[$i].muscles >= 0>>
@@ -7880,13 +8065,15 @@
 				$His titanic breasts are so massive they dwarf $his body. $He <<if $slaves[$i].sexualFlaw == "breast growth">>happily embraces<<else>>has no choice but to accept<</if>> immobility.
 			<<elseif ($slaves[$i].boobs > 20000)>>
 				$His immense breasts are so huge they rest upon the floor even when $he tries to stand, taking the weight off $his tiny body.
-				<<if $slaves[$i].sexualFlaw == "breast growth">>
-					$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds pulling them along @@.mediumorchid;mildly annoying.@@
-					<<set $slaves[$i].devotion -= 1>>
-				<<else>>
-					$He finds pulling them along a big burden, but $his devotion to you is far stronger than $his discomfort.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breast growth">>
+						$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds pulling them along @@.mediumorchid;mildly annoying.@@
+						<<set $slaves[$i].devotion -= 1>>
+					<<else>>
+						$He finds pulling them along a big burden, but $his devotion to you is far stronger than $his discomfort.
+					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].boobs > 4700 + ($slaves[$i].muscles * 20))>>
 				<<if canWalk($slaves[$i])>>
@@ -7907,7 +8094,7 @@
 					<<else>>
 						$His giant tits are debilitatingly big. $He does everything $he can to keep comfortable and pain free as to not bother you.
 					<</if>>
-				<<else>>
+				<<elseif canMove($slaves[$i])>>
 					$His giant breasts are so big they pin $him to the floor, taking the weight off $his tiny body.
 					<<if $slaves[$i].sexualFlaw == "breast growth">>
 						$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
@@ -7983,13 +8170,15 @@
 		<<if isSlaveAvailable($slaves[$i])>>
 			<<if ($slaves[$i].boobs > 40000)>>
 				$His immense breasts are so huge they rest upon the floor even when $he tries to stand, taking the weight off $his small body.
-				<<if $slaves[$i].sexualFlaw == "breast growth">>
-					$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds pulling them along @@.mediumorchid;mildly annoying.@@
-					<<set $slaves[$i].devotion -= 1>>
-				<<else>>
-					$He finds pulling them along a burden, but $his devotion to you is far stronger than $his discomfort.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breast growth">>
+						$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds pulling them along @@.mediumorchid;mildly annoying.@@
+						<<set $slaves[$i].devotion -= 1>>
+					<<else>>
+						$He finds pulling them along a burden, but $his devotion to you is far stronger than $his discomfort.
+					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].boobs > 9500 + ($slaves[$i].muscles * 50))>>
 				<<if canWalk($slaves[$i])>>
@@ -8013,13 +8202,15 @@
 					<</if>>
 				<<else>>
 					$His giant breasts are so big they pin $him to the floor, taking the weight off $his young body.
-					<<if $slaves[$i].sexualFlaw == "breast growth">>
-						$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
-					<<elseif $slaves[$i].devotion <= 50>>
-						$He finds pulling them along @@.mediumorchid;mildly annoying.@@
-						<<set $slaves[$i].devotion -= 1>>
-					<<else>>
-						$He finds pulling them along a little annoying, but your will is far more important to $him.
+					<<if canMove($slaves[$i])>>
+						<<if $slaves[$i].sexualFlaw == "breast growth">>
+							$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
+						<<elseif $slaves[$i].devotion <= 50>>
+							$He finds pulling them along @@.mediumorchid;mildly annoying.@@
+							<<set $slaves[$i].devotion -= 1>>
+						<<else>>
+							$He finds pulling them along a little annoying, but your will is far more important to $him.
+						<</if>>
 					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].boobs > 5000)>>
@@ -8089,24 +8280,28 @@
 		<<if isSlaveAvailable($slaves[$i])>>
 			<<if ($slaves[$i].boobs > 25000 + ($slaves[$i].muscles * 100))>>
 				$His immense breasts are so big they pin $him to the floor, taking the weight off $his youthful body.
-				<<if $slaves[$i].sexualFlaw == "breast growth">>
-					$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds pulling them along @@.mediumorchid;mildly annoying.@@
-					<<set $slaves[$i].devotion -= 1>>
-				<<else>>
-					$He finds pulling them along a little annoying, but your will is far more important to $him.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breast growth">>
+						$He's perfectly fine with dragging them along as a testament to how much $he's managed to grow.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds pulling them along @@.mediumorchid;mildly annoying.@@
+						<<set $slaves[$i].devotion -= 1>>
+					<<else>>
+						$He finds pulling them along a little annoying, but your will is far more important to $him.
+					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].boobs > 17000)>>
 				$His giant tits are debilitatingly big.
-				<<if $slaves[$i].sexualFlaw == "breast growth">>
-					No amount of discomfort or pain could dissuade $him from going bigger.
-				<<elseif $slaves[$i].devotion <= 50>>
-					They are @@.mediumorchid;very uncomfortable@@ and @@.red;painful@@ for $his youthful form.
-					<<set $slaves[$i].devotion -= 3>>
-					<<run healthDamage($slaves[$i], 10)>>
-				<<else>>
-					$He finds them painfully uncomfortable, but finds ways to mitigate it to not distract you with $his worries.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breast growth">>
+						No amount of discomfort or pain could dissuade $him from going bigger.
+					<<elseif $slaves[$i].devotion <= 50>>
+						They are @@.mediumorchid;very uncomfortable@@ and @@.red;painful@@ for $his youthful form.
+						<<set $slaves[$i].devotion -= 3>>
+						<<run healthDamage($slaves[$i], 10)>>
+					<<else>>
+						$He finds them painfully uncomfortable, but finds ways to mitigate it to not distract you with $his worries.
+					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].boobs > 10000)>>
 				<<if ($slaves[$i].muscles <= 30)>>
@@ -8367,13 +8562,15 @@
 				$His _belly belly is so large it dwarfs $his body. $He <<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>enjoys<<else>>has no choice but to accept<</if>> being an accessory to $his belly.
 			<<elseif $slaves[$i].belly >= 100000>>
 				$His _belly belly is so large it reaches the floor <<if hasAnyLegs($slaves[$i])>>even when $he stands<<else>>no matter how $he rests<</if>>, taking the weight off $his tiny body.
-				<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
-					Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds dragging it along @@.mediumorchid;mildly annoying.@@
-					<<set $slaves[$i].devotion -= 1>>
-				<<else>>
-					$He's fine with this though, as it is much easier than trying to waddle along.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
+						Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds dragging it along @@.mediumorchid;mildly annoying.@@
+						<<set $slaves[$i].devotion -= 1>>
+					<<else>>
+						$He's fine with this though, as it is much easier than trying to waddle along.
+					<</if>>
 				<</if>>
 			<<elseif $slaves[$i].belly >= 30000>>
 				<<if canWalk($slaves[$i])>>
@@ -8401,17 +8598,19 @@
 				<</if>>
 			<</if>>
 		<<elseif $slaves[$i].physicalAge < 13>>
-			<<if $slaves[$i].belly >= 600000>>
+			<<if $slaves[$i].belly >= 300000>>
 				$His _belly belly is so large it dwarfs $his body. $He <<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>enjoys<<else>>has no choice but to accept<</if>> being an accessory to $his belly.
 			<<elseif $slaves[$i].belly >= 300000>>
 				$His _belly belly is so large it reaches the floor <<if hasAnyLegs($slaves[$i])>>even when $he stands<<else>>no matter how $he rests<</if>>, taking the weight off $his young figure.
-				<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
-					Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds dragging it along @@.mediumorchid;mildly annoying.@@
-					<<set $slaves[$i].devotion -= 1>>
-				<<else>>
-					While it is quite a nuisance, your will outweighs $his opinions.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
+						Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds dragging it along @@.mediumorchid;mildly annoying.@@
+						<<set $slaves[$i].devotion -= 1>>
+					<<else>>
+						While it is quite a nuisance, your will outweighs $his opinions.
+					<</if>>
 				<</if>>
 			<<elseif $slaves[$i].belly >= 120000>>
 				<<if canWalk($slaves[$i])>>
@@ -8451,17 +8650,19 @@
 				<</if>>
 			<</if>>
 		<<elseif $slaves[$i].physicalAge < 18>>
-			<<if $slaves[$i].belly >= 750000>>
+			<<if $slaves[$i].belly >= 60000>>
 				$His _belly belly is so large it dwarfs $his body. $He <<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>enjoys<<else>>has no choice but to accept<</if>> being an accessory to $his belly.
 			<<elseif $slaves[$i].belly >= 300000>>
 				$His _belly belly is so large it reaches the floor <<if hasAnyLegs($slaves[$i])>>even when $he stands<<else>>no matter how $he rests<</if>>, taking the weight off $his body.
-				<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
-					Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds dragging it along @@.mediumorchid;a painful problem.@@
-					<<set $slaves[$i].devotion -= 5>>
-				<<else>>
-					While moving it is a problem, $he struggles through it for you.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
+						Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds dragging it along @@.mediumorchid;a painful problem.@@
+						<<set $slaves[$i].devotion -= 5>>
+					<<else>>
+						While moving it is a problem, $he struggles through it for you.
+					<</if>>
 				<</if>>
 			<<elseif $slaves[$i].belly >= 200000>>
 				<<if canWalk($slaves[$i])>>
@@ -8501,17 +8702,19 @@
 				<</if>>
 			<</if>>
 		<<else>>
-			<<if $slaves[$i].belly >= 1000000>>
+			<<if $slaves[$i].belly >= 1500000>>
 				$His _belly belly is so large it dwarfs $his body. $He <<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>enjoys<<else>>has no choice but to accept<</if>> being an accessory to $his belly.
-			<<elseif $slaves[$i].belly >= 450000>>
+			<<elseif $slaves[$i].belly >= 1000000>>
 				$His _belly belly is so large it reaches the floor <<if hasAnyLegs($slaves[$i])>>even when $he stands<<else>>no matter how $he rests<</if>>, taking the weight off $his body.
-				<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
-					Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
-				<<elseif $slaves[$i].devotion <= 50>>
-					$He finds dragging it along @@.mediumorchid;a painful problem.@@
-					<<set $slaves[$i].devotion -= 5>>
-				<<else>>
-					While moving it is a problem, $he struggles through it for you.
+				<<if canMove($slaves[$i])>>
+					<<if $slaves[$i].sexualFlaw == "breeder" && $slaves[$i].preg > 0>>
+						Such an inconvenience means little when you're living the dream of being a baby-filled breeder.
+					<<elseif $slaves[$i].devotion <= 50>>
+						$He finds dragging it along @@.mediumorchid;a painful problem.@@
+						<<set $slaves[$i].devotion -= 5>>
+					<<else>>
+						While moving it is a problem, $he struggles through it for you.
+					<</if>>
 				<</if>>
 			<<elseif $slaves[$i].belly >= 300000>>
 				<<if canWalk($slaves[$i])>>
@@ -8983,7 +9186,7 @@
 				<</if>>
 			<</if>>
 
-			<<if canWalk($slaves[$i])>>
+			<<if canMove($slaves[$i])>>
 				<<if ($slaves[$i].dick >= 30)>>
 					<<if ($slaves[$i].devotion <= 50)>>
 						$His titanic penis rests upon the floor even when $he stands. $He finds moving @@.mediumorchid;terrifying@@ since $he drags $his cock along.
@@ -9016,7 +9219,7 @@
 						$He's @@.mediumaquamarine;thankful@@ to you for providing living spaces adapted to life with an enormous dick.
 						<<set $slaves[$i].trust += 1>>
 					<</if>>
-				<<else>>
+				<<elseif canMove($slaves[$i])>>
 					$His giant penis makes life a struggle: $he has to drag it along as $he moves, has trouble fitting into beds and sitting in chairs, and constantly has to make sure to not get $his dick caught in doors.
 					<<if ($slaves[$i].devotion > 50)>>
 						Since $he's devoted to you, $he just does $his best.
@@ -9030,7 +9233,7 @@
 				<</if>>
 			<</if>>
 
-			<<if canWalk($slaves[$i])>>
+			<<if canMove($slaves[$i])>>
 				<<if ($slaves[$i].balls > 90)>>
 					$His titanic balls rest upon the floor even when $he stands.
 					<<if ($slaves[$i].devotion <= 50)>>
@@ -9062,7 +9265,7 @@
 						$He's @@.mediumaquamarine;thankful@@ to you for providing living spaces adapted to life with enormous testicles.
 						<<set $slaves[$i].trust += 1>>
 					<</if>>
-				<<else>>
+				<<elseif canMove($slaves[$i])>>
 					$His giant balls make life a struggle: <<if $buttAccessibility == 1 || $pregAccessibility == 1 || $boobAccessibility == 1>>$he has trouble using appliances and furniture, and has to be constantly mindful of things striking $his oversensitive testicles, but at least the doors have already been widened for your other slaves<<else>>$he barely fits through doors, has trouble using appliances and furniture, and has to be constantly mindful of things striking $his oversensitive testicles<</if>>.
 					<<if ($slaves[$i].devotion > 50)>>
 						Since $he's devoted to you, $he just does $his best.
@@ -9093,8 +9296,8 @@
 					<</if>>
 				<</if>>
 			<<elseif ($slaves[$i].sexualFlaw == "breeder") && ($slaves[$i].hips > 2)>>
-					$He @@.hotpink;loves@@ how $his wide body basically screams "fertility goddess".
-					<<set $slaves[$i].devotion += 1>>
+				$He @@.hotpink;loves@@ how $his wide body basically screams "fertility goddess".
+				<<set $slaves[$i].devotion += 1>>
 			<</if>>
 
 			<<if canWalk($slaves[$i])>>
@@ -9102,10 +9305,10 @@
 					$His butt is a massive burden to $him. $He dreads walking down narrow hallways and getting dressed in the morning.
 					<<if $arcologies[0].FSAssetExpansionist != "unset" && ($slaves[$i].behavioralFlaw == "arrogant" || $slaves[$i].behavioralQuirk == "confident")>>
 						<<if $slaves[$i].behavioralFlaw == "arrogant">>
-							However, your arcology can't help but worship an ass as epxansive as $his, @@.mediumaquamarine;giving $him an ego@@ that rivals $his rear.
+							However, your arcology can't help but worship an ass as expansive as $hers, @@.mediumaquamarine;giving $him an ego@@ that rivals $his rear.
 							<<set $slaves[$i].trust += 3>>
 						<<elseif $slaves[$i].behavioralQuirk == "confident">>
-							However, your arcology can't help but worship an ass as epxansive as $his, so it mostly balances out.
+							However, your arcology can't help but worship an ass as expansive as $hers, so it mostly balances out.
 						<</if>>
 					<<elseif ($slaves[$i].devotion <= 50)>>
 						$He lives a life of @@.mediumorchid;annoyance@@ over knocking things over, bumping people, and getting stuck in chairs with $his godly ass.
@@ -9154,6 +9357,7 @@
 	<</if>>
 <</if>>
 
+/* health blips */
 <<if ($curativeSideEffects != 0)>>
 	<<if ($slaves[$i].chem > 10)>>
 		<<if random(1,200) < $slaves[$i].chem + $slaves[$i].physicalAge - $slaves[$i].health.condition - (5*$slaves[$i].curatives)>>
@@ -9198,7 +9402,7 @@
 			<<run healthDamage($slaves[$i], 2)>>
 		<</if>>
 		<<if random(1,200) < 5>>
-			$He experienced @@.red;severe@@ health complications due to $his weight this week. After extensive surgery, $his health was stabilized, though $his survival is not assured.
+			$He experienced @@.red;severe health complications@@ due to $his weight this week. After extensive surgery, $his health was stabilized, though $his survival is not assured.
 			<<run healthDamage($slaves[$i], 80)>>
 		<</if>>
 	<<elseif $slaves[$i].weight > 160>>
@@ -9207,21 +9411,22 @@
 			<<run healthDamage($slaves[$i], 1)>>
 		<</if>>
 		<<if random(1,200) < 5>>
-			$He experienced @@.red;severe@@ health complications due to $his weight this week. After extensive surgery, $his health was stabilized.
+			$He experienced @@.red;severe health complications@@ due to $his weight this week. After extensive surgery, $his health was stabilized.
 			<<run healthDamage($slaves[$i], 40)>>
 		<</if>>
 	<<elseif $slaves[$i].weight > 130>>
 		<<if random(1,200) < 5>>
-			$He experienced @@.red;severe@@ health complications due to $his weight this week. After some rest, $he recovered.
+			$He experienced @@.red;severe health complications@@ due to $his weight this week. After some rest, $he recovered.
 			<<run healthDamage($slaves[$i], 20)>>
 		<</if>>
 	<<elseif $slaves[$i].weight > 95>>
 		<<if random(1,200) < 5>>
-			$He experienced @@.red;minor@@ health complications due to $his weight this week. After some rest, $he recovered.
+			$He experienced @@.red;minor health complications@@ due to $his weight this week. After some rest, $he recovered.
 			<<run healthDamage($slaves[$i], 5)>>
 		<</if>>
 	<</if>>
 <</if>>
+/* end here! */
 
 /* LANGUAGE */
 
diff --git a/src/uncategorized/saRecruitGirls.tw b/src/uncategorized/saRecruitGirls.tw
index 665fa092ab1b923dde5a19c3d10a0bc66b2b39af..1bd1f8859de4d19d2b6ce6756ee76bc3c34608bf 100644
--- a/src/uncategorized/saRecruitGirls.tw
+++ b/src/uncategorized/saRecruitGirls.tw
@@ -396,15 +396,15 @@
 	<</if>>
 
 	<<if $recruiterTarget == "desperate whores">>
-		<<if ($Recruiter.counter.anal + $Recruiter.counter.oral + $Recruiter.counter.vaginal) > 200>>
+		<<if (_S.Recruiter.counter.anal + _S.Recruiter.counter.oral + _S.Recruiter.counter.vaginal) > 200>>
 			As a veteran slut, $he speaks from experience when $he says that being your slave whore will be safer and healthier than streetwalking.
 			<<set $recruiterProgress += 1>>
 		<<else>>
 			$He does $his best to convince them that being your slave whore will be safer and healthier than streetwalking.
 		<</if>>
 	<<elseif $recruiterTarget == "expectant mothers">> /* now you can ginger too */
-		<<if (($Recruiter.belly >= 1500 || setup.fakeBellies.includes(_bellyAccessory) && $Recruiter.weight < 130) || $Recruiter.belly >= 100000)>>
-			<<if $Recruiter.preg > $Recruiter.pregData.normalBirth/8>>
+		<<if ((_S.Recruiter.belly >= 1500 || setup.fakeBellies.includes(_bellyAccessory) && _S.Recruiter.weight < 130) || _S.Recruiter.belly >= 100000)>>
+			<<if _S.Recruiter.preg > _S.Recruiter.pregData.normalBirth/8>>
 				Since $he's visibly pregnant $himself, $he speaks with authority when $he says that Free Cities medicine can keep them and their pregnancies safe and healthy.
 			<<else>>
 				Since $he looks visibly pregnant, $he's more convincing when $he says that Free Cities medicine can keep them and their pregnancies safe and healthy.
@@ -414,21 +414,21 @@
 			$He does $his best to convince them that Free Cities medicine can keep them and their pregnancies safe and healthy.
 		<</if>>
 	<<elseif $recruiterTarget == "young migrants">>
-		<<if ($Recruiter.health.condition >= 80) && ($Recruiter.face > 10)>>
+		<<if (_S.Recruiter.health.condition >= 80) && (_S.Recruiter.face > 10)>>
 			$His lovely face and shining health go a long way to convince them that being your slave promises a better life.
 			<<set $recruiterProgress += 1>>
 		<<else>>
 			$He does $his best to convince them that being your slave promises a better life.
 		<</if>>
 	<<elseif $recruiterTarget == "dissolute sissies">>
-		<<if ($Recruiter.dick > 1) && canAchieveErection($Recruiter) && !($Recruiter.chastityPenis)>>
+		<<if (_S.Recruiter.dick > 1) && canAchieveErection(_S.Recruiter) && !(_S.Recruiter.chastityPenis)>>
 			$He giggles and shows off $his erection, making it easy to convince them that your slaves with dicks enjoy a sexually satisfying life.
 			<<set $recruiterProgress += 1>>
 		<<else>>
 			$He does $his best to convince them that your slaves with dicks enjoy a sexually satisfying life.
 		<</if>>
 	<<elseif $recruiterTarget == "reassignment candidates">>
-		<<if (($Recruiter.balls > 0 && $Recruiter.ovaries == 0 && $Recruiter.genes == "XX") || ($Recruiter.ovaries == 1 && $Recruiter.scrotum == 0 && $Recruiter.genes == "XY")) && ($Recruiter.face > 10)>>
+		<<if ((_S.Recruiter.balls > 0 && _S.Recruiter.ovaries == 0 && _S.Recruiter.genes == "XX") || (_S.Recruiter.ovaries == 1 && _S.Recruiter.scrotum == 0 && _S.Recruiter.genes == "XY")) && (_S.Recruiter.face > 10)>>
 			$He shows off $his lovely face and describes $his unusual biological situation under your care, convincing them that you'll turn them into happy little slave girls.
 			<<set $recruiterProgress += 1>>
 		<<else>>
@@ -766,8 +766,8 @@
 			<<set _seed += 6, _FSmatch++, $arcologies[0].FSArabianRevivalist += 0.02*$FSSingleSlaveRep>>
 		<</if>>
 	<<elseif $arcologies[0].FSChineseRevivalist != "unset">>
-		<<if $slaves[$i].intelligence+$slaves[$i].intelligenceImplant > 15 && $HeadGirl != 0 && $Bodyguard != 0 && $HGSuite > 0>>
-			<<set _seed += (Math.min((($HeadGirl.skill.entertainment/30)+($HeadGirl.intelligenceImplant/10)+$HeadGirl.prestige), 4)+Math.min($Bodyguard.prestige, 1)), _FSdefend++, $arcologies[0].FSChineseRevivalist += 0.03*$FSSingleSlaveRep>>
+		<<if $slaves[$i].intelligence+$slaves[$i].intelligenceImplant > 15 && $HeadGirlID != 0 && $BodyguardID != 0 && $HGSuite > 0>>
+			<<set _seed += (Math.min(((_S.HeadGirl.skill.entertainment/30)+(_S.HeadGirl.intelligenceImplant/10)+_S.HeadGirl.prestige), 4)+Math.min(_S.Bodyguard.prestige, 1)), _FSdefend++, $arcologies[0].FSChineseRevivalist += 0.03*$FSSingleSlaveRep>>
 		<</if>>
 	<</if>>
 	/* and then there's Aztec revivalist, completely forgotten */
@@ -1169,19 +1169,19 @@
 				$He makes a short video essay about your elaborate master suite, but there's no sex going on while $he films.
 			<</if>>
 		<<elseif $arcologies[0].FSChineseRevivalist != "unset">>
-			<<if $slaves[$i].intelligence+$slaves[$i].intelligenceImplant > 50 && $HeadGirl != 0 && $Bodyguard != 0 && $HGSuite > 0>>
-				<<if (($HeadGirl.skill.entertainment/30)+($HeadGirl.intelligenceImplant/10)+$HeadGirl.prestige) >= 4>>
-					<<if $Bodyguard.prestige >= 1>>
-						$He deferentially chronicles the administration of your Imperial household by Head Girl $HeadGirl.slaveName and Bodyguard $Bodyguard.slaveName. The piece explains points of Chinese Revivalist protocol where new slaves or visitors to the Forbidden Penthouse might inadvertently stumble.
+			<<if $slaves[$i].intelligence+$slaves[$i].intelligenceImplant > 50 && $HeadGirlID != 0 && $BodyguardID != 0 && $HGSuite > 0>>
+				<<if ((_S.HeadGirl.skill.entertainment/30)+(_S.HeadGirl.intelligenceImplant/10)+_S.HeadGirl.prestige) >= 4>>
+					<<if _S.Bodyguard.prestige >= 1>>
+						$He deferentially chronicles the administration of your Imperial household by Head Girl _S.HeadGirl.slaveName and Bodyguard _S.Bodyguard.slaveName. The piece explains points of Chinese Revivalist protocol where new slaves or visitors to the Forbidden Penthouse might inadvertently stumble.
 					<<else>>
-						$He interviews your Head Girl about points of protocol and household administration for broadcast to the arcology. Your Bodyguard, $Bodyguard.slaveName, is not accustomed to fame and prefers to remain off-screen.
+						$He interviews your Head Girl about points of protocol and household administration for broadcast to the arcology. Your Bodyguard, _S.Bodyguard.slaveName, is not accustomed to fame and prefers to remain off-screen.
 					<</if>>
-				<<elseif $HeadGirl.intelligenceImplant < 15>>
+				<<elseif _S.HeadGirl.intelligenceImplant < 15>>
 					$He edits a documentary broadcast about the Revivalist protocols that drive your household, and in the process uncovers small but annoying lapses due to the Head Girl's lack of formal education.
 				<<else>>
-					$He broadcasts a documentary about life inside your Imperial Chinese household, but the Head Girl's segment comes out flat: $HeadGirl.slaveName needs more experience working in front of a camera.
+					$He broadcasts a documentary about life inside your Imperial Chinese household, but the Head Girl's segment comes out flat: _S.HeadGirl.slaveName needs more experience working in front of a camera.
 				<</if>>
-			<<elseif $HeadGirl == 0 || $Bodyguard == 0>>
+			<<elseif $HeadGirlID == 0 || $BodyguardID == 0>>
 				$He can't document the benefits of your Imperial Chinese administration because of unfilled posts in its leadership.
 			<<elseif $slaves[$i].intelligence+$slaves[$i].intelligenceImplant <= 15>>
 				Your household is a well-run model for the arcology at large, but your recruiter doesn't completely understand its intricate Revivalist protocols and can't explain it for the masses.
diff --git a/src/uncategorized/saRelationships.tw b/src/uncategorized/saRelationships.tw
index 455f0ed014784845f43423d1ec317bb6b5af5b15..db67b0c96e46d3355a15493502617ba4b33cfc37 100644
--- a/src/uncategorized/saRelationships.tw
+++ b/src/uncategorized/saRelationships.tw
@@ -75,7 +75,7 @@
 				<<set _SlaveJ = $slaves[_j]>>
 				<<setLocalPronouns _SlaveJ 2>>
 				<<if (_SlaveJ.ID != _SlaveI.ID) && (_SlaveJ.relationship == 0) && (_SlaveI.rivalryTarget != _SlaveJ.ID) && (_SlaveJ.assignment != "stay confined") && (_SlaveJ.fetish != "mindbroken")>>
-				<<if (_SlaveJ.ID == $Madam.ID) && (_SlaveI.assignment == "work in the brothel")>>
+				<<if (_SlaveJ.ID == $MadamID) && (_SlaveI.assignment == "work in the brothel")>>
 					<<if _SlaveJ.rules.relationship != "restrictive">>
 						_SlaveI.slaveName manages to ingratiate $himself with the Madam, _SlaveJ.slaveName. The two slaves have @@.lightgreen;struck up a friendship.@@
 						<<set _SlaveJ.relationship = 1, _SlaveJ.relationshipTarget = _SlaveI.ID, _SlaveI.relationship = 1, _SlaveI.relationshipTarget = _SlaveJ.ID>>
@@ -86,7 +86,7 @@
 							<<set _SlaveI.trust -= 4>>
 						<</if>>
 					<</if>>
-				<<elseif (_SlaveJ.ID == $djID) && (_SlaveI.assignment == "serve in the club")>>
+				<<elseif (_SlaveJ.ID == $djID) && (_SlaveI.assignment == Job.CLUB)>>
 					<<if _SlaveJ.rules.relationship != "restrictive">>
 						_SlaveI.slaveName manages to ingratiate $himself with the DJ, _SlaveJ.slaveName. The two slaves have @@.lightgreen;struck up a friendship.@@
 						<<set _SlaveJ.relationship = 1, _SlaveJ.relationshipTarget = _SlaveI.ID, _SlaveI.relationship = 1, _SlaveI.relationshipTarget = _SlaveJ.ID>>
@@ -97,7 +97,7 @@
 							<<set _SlaveI.trust -= 4>>
 						<</if>>
 					<</if>>
-				<<elseif (_SlaveJ.ID == $Milkmaid.ID) && (_SlaveI.assignment == "work in the dairy")>>
+				<<elseif (_SlaveJ.ID == $MilkmaidID) && (_SlaveI.assignment == "work in the dairy")>>
 					<<if _SlaveJ.rules.relationship != "restrictive">>
 						_SlaveI.slaveName manages to ingratiate $himself with the Milkmaid, _SlaveJ.slaveName. The two slaves have @@.lightgreen;struck up a friendship.@@
 						<<set _SlaveJ.relationship = 1, _SlaveJ.relationshipTarget = _SlaveI.ID, _SlaveI.relationship = 1, _SlaveI.relationshipTarget = _SlaveJ.ID>>
@@ -108,7 +108,7 @@
 							<<set _SlaveI.trust -= 4>>
 						<</if>>
 					<</if>>
-				<<elseif (_SlaveJ.ID == $Farmer.ID) && (_SlaveI.assignment == "work as a farmhand")>>
+				<<elseif (_SlaveJ.ID == $FarmerID) && (_SlaveI.assignment == "work as a farmhand")>>
 					<<if _SlaveJ.rules.relationship != "restrictive">>
 						_SlaveI.slaveName manages to ingratiate $himself with the Farmer, _SlaveJ.slaveName. The two slaves have @@.lightgreen;struck up a friendship.@@
 						<<set _SlaveJ.relationship = 1, _SlaveJ.relationshipTarget = _SlaveI.ID, _SlaveI.relationship = 1, _SlaveI.relationshipTarget = _SlaveJ.ID>>
@@ -163,7 +163,7 @@
 							<<set _SlaveI.trust -= 5>>
 						<</if>>
 					<</if>>
-				<<elseif (_SlaveJ.ID == $Concubine.ID) && (_SlaveI.assignment == "serve in the master suite")>>
+				<<elseif (_SlaveJ.ID == $ConcubineID) && (_SlaveI.assignment == "serve in the master suite")>>
 					<<if _SlaveJ.rules.relationship != "restrictive">>
 						_SlaveI.slaveName becomes especially close to the Concubine, _SlaveJ.slaveName. The two slaves have @@.lightgreen;struck up a friendship.@@
 						<<set _SlaveJ.relationship = 1, _SlaveJ.relationshipTarget = _SlaveI.ID, _SlaveI.relationship = 1, _SlaveI.relationshipTarget = _SlaveJ.ID>>
diff --git a/src/uncategorized/saRules.tw b/src/uncategorized/saRules.tw
index e47fed9f833329a931f0ac70d5f2be0b179bb949..be87a0c0cecd73d8132238b7632c794a20dff4f5 100644
--- a/src/uncategorized/saRules.tw
+++ b/src/uncategorized/saRules.tw
@@ -1417,14 +1417,14 @@
 		<<case "get treatment in the clinic">>
 			<<if $slaves[$i].relationship > 2>>
 				<<set _lover = $slaveIndices[$slaves[$i].relationshipTarget]>>
-				<<if !canWalk($slaves[_lover]) || !isSlaveAvailable($slaves[_lover])>>
+				<<if !canMove($slaves[_lover]) || !isSlaveAvailable($slaves[_lover])>>
 					<<set _lover = 0>>
 				<</if>>
 			<<else>>
 				<<set _lover = 0>>
 			<</if>>
 			<<if _lover == 0 && $seeIncest == 1>>
-				<<set _famVisitor = randomRelatedSlave($slaves[$i], (s) => { return canWalk(s) && isSlaveAvailable(s) && App.Utils.sexAllowed($slaves[$i], s); } )>>
+				<<set _famVisitor = randomRelatedSlave($slaves[$i], (s) => { return canMove(s) && isSlaveAvailable(s) && App.Utils.sexAllowed($slaves[$i], s); } )>>
 			<</if>>
 			<<if $slaves[$i].devotion < -50>>
 				is so unhappy that $he has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
@@ -4721,14 +4721,14 @@
 					<<if ($universalRulesConsent == 0)>>
 						<<if ($slaves[$i].devotion <= 20)>>
 							<<if ($slaves[$i].trust > -10)>>
-								<<if _L.dairy > 2 || $Milkmaid != 0>>
+								<<if _L.dairy > 2 || $MilkmaidID != 0>>
 									Under the rules, $he finds $himself constantly molested by other slaves, and lives $his life constantly @@.gold;afraid.@@
 								<<else>>
 									Under the rules, $he is free game for other slaves to molest, and lives $his life constantly @@.gold;afraid@@ of the day you assign more cows to $dairyName.
 								<</if>>
 								<<set $slaves[$i].trust -= 2>>
 							<<else>>
-								<<if _L.dairy > 2 || $Milkmaid != 0>>
+								<<if _L.dairy > 2 || $MilkmaidID != 0>>
 									Under the rules, $he finds $himself constantly molested by other slaves, but $he's already in such constant terror it doesn't seriously affect $him.
 								<<else>>
 									Under the rules, $he will someday find $himself constantly molested by other slaves, but $he's already in such constant terror it that it doesn't cross $his mind.
@@ -4762,7 +4762,7 @@
 						<</if>>
 					<<else>>
 						<<if ($slaves[$i].devotion <= 20) && ($slaves[$i].devotion >= -20)>>
-							<<if _L.dairy > 2 || $Milkmaid != 0>>
+							<<if _L.dairy > 2 || $MilkmaidID != 0>>
 								Since $he's low in the slave hierarchy, @@.mediumaquamarine;$he knows@@ that the rule that slaves must get consent before having sex with $him are all that protect $him from abuse.
 							<<else>>
 								Since $he's low in the slave hierarchy, @@.mediumaquamarine;$he knows@@ that the rule that slaves must get consent before having sex with $him are all that protect $him from abuse. Well, that and the fact $dairyName is rather empty.
@@ -5294,14 +5294,14 @@
 			<<if ($universalRulesConsent == 0)>>
 				<<if ($slaves[$i].devotion <= 20)>>
 					<<if ($slaves[$i].trust > -10)>>
-						<<if _L.farmyard > 2 || $Farmer != 0>>
+						<<if _L.farmyard > 2 || $FarmerID != 0>>
 							Under the rules, $he finds $himself constantly molested by other slaves, and lives $his life constantly @@.gold;afraid.@@
 						<<else>>
 							Under the rules, $he is free game for other slaves to molest, and lives $his life constantly @@.gold;afraid@@ of the day you assign more cows to $farmyardName.
 						<</if>>
 						<<set $slaves[$i].trust -= 2>>
 					<<else>>
-						<<if _L.farmyard > 2 || $Farmer != 0>>
+						<<if _L.farmyard > 2 || $FarmerID != 0>>
 							Under the rules, $he finds $himself constantly molested by other slaves, but $he's already in such constant terror it doesn't seriously affect $him.
 						<<else>>
 							Under the rules, $he will someday find $himself constantly molested by other slaves, but $he's already in such constant terror it that it doesn't cross $his mind.
@@ -5335,7 +5335,7 @@
 				<</if>>
 			<<else>>
 				<<if ($slaves[$i].devotion <= 20) && ($slaves[$i].devotion >= -20)>>
-					<<if _L.farmyard > 2 || $Farmer != 0>>
+					<<if _L.farmyard > 2 || $FarmerID != 0>>
 						Since $he's low in the slave hierarchy, @@.mediumaquamarine;$he knows@@ that the rule that slaves must get consent before having sex with $him are all that protect $him from abuse.
 					<<else>>
 						Since $he's low in the slave hierarchy, @@.mediumaquamarine;$he knows@@ that the rule that slaves must get consent before having sex with $him are all that protect $him from abuse. Well, that and the fact $farmyardName is rather empty.
@@ -5713,14 +5713,14 @@
 			<</if>>
 		<<case "live with your Head Girl">>
 			<<if $slaves[$i].devotion < -50>>
-				is so unhappy that $he has little interest in getting off, not that $HeadGirl.slaveName cares.
+				is so unhappy that $he has little interest in getting off, not that _S.HeadGirl.slaveName cares.
 				<<set $slaves[$i].need = 0>>
 			<<elseif $slaves[$i].energy <= 20>>
-				is frigid and has little interest in getting off, though it doesn't stop $HeadGirl.slaveName.
+				is frigid and has little interest in getting off, though it doesn't stop _S.HeadGirl.slaveName.
 				<<set $slaves[$i].need = 0>>
 			<<elseif $slaves[$i].need < $slaves[$i].needCap*0.5>>
 				<<if ($slaves[$i].devotion <= 20)>>
-					gets off with $HeadGirl.slaveName despite $his reluctance, @@.hotpink;habituating $him to sexual slavery.@@
+					gets off with _S.HeadGirl.slaveName despite $his reluctance, @@.hotpink;habituating $him to sexual slavery.@@
 					<<set $slaves[$i].devotion += 1>>
 					<<if ($slaves[$i].trust >= -20) && ($slaves[$i].devotion <= 20)>>
 						$He hates $himself for climaxing, and knows the mild aphrodisiacs in the food are forcing $his arousal, @@.gold;frightening $him.@@
@@ -5728,14 +5728,14 @@
 					<</if>>
 					<<set $slaves[$i].need -= 20>>
 				<<elseif (_release.masturbation === 0)>>
-					gets off with $HeadGirl.slaveName, so being forbidden to masturbate doesn't affect $him seriously.
+					gets off with _S.HeadGirl.slaveName, so being forbidden to masturbate doesn't affect $him seriously.
 					<<set $slaves[$i].need -= 20>>
 				<<else>>
-					gets off with $HeadGirl.slaveName, so $he doesn't feel the need for release that often.
+					gets off with _S.HeadGirl.slaveName, so $he doesn't feel the need for release that often.
 					<<set $slaves[$i].need -= 20>>
 				<</if>>
 			<<else>>
-				either gets off with $HeadGirl.slaveName or gets to put up with sexual frustration.
+				either gets off with _S.HeadGirl.slaveName or gets to put up with sexual frustration.
 			<</if>>
 
 			<<if ($slaves[$i].devotion <= 20)>>
@@ -5752,9 +5752,9 @@
 			<</if>>
 
 			<<if ($slaves[$i].devotion <= 20)>>
-				$He shares a room, and sometimes bed, with $HeadGirl.slaveName. Your Head Girl keeps it from going to $his head, however.
+				$He shares a room, and sometimes bed, with _S.HeadGirl.slaveName. Your Head Girl keeps it from going to $his head, however.
 			<<else>>
-				$He loves sharing a room, and sometimes bed, with $HeadGirl.slaveName.
+				$He loves sharing a room, and sometimes bed, with _S.HeadGirl.slaveName.
 			<</if>>
 
 			<<if ($slaves[$i].attrKnown == 0)>>
@@ -5766,7 +5766,7 @@
 			<</if>>
 
 			<<if $slaves[$i].rules.lactation == "induce">>
-				<<if $HeadGirl.fetish == "boobs">>
+				<<if _S.HeadGirl.fetish == "boobs">>
 					Your Head Girl enjoys playing with $his tits, making it an inevitability that $he'll begin lactating.
 				<<else>>
 					$He carries out $his daily tasks with a pair of automatic breast pumps attached to $his chest to help bring in $his lactation.
@@ -5775,7 +5775,7 @@
 				<<= induceLactation($slaves[$i])>>
 				<<if $slaves[$i].lactation == 1>><<set $slaves[$i].rules.lactation = "maintain">><</if>>
 			<<elseif $slaves[$i].rules.lactation == "maintain">>
-				<<if $HeadGirl.fetish == "boobs">>
+				<<if _S.HeadGirl.fetish == "boobs">>
 					Your Head Girl enjoys playing with $his tits, thoroughly draining $him of milk and encouraging $his continued lactation.
 				<<else>>
 					$He utilizes $his lactation as your Head Girl demands, and if it should not be needed, spends the evenings with a pair of automatic breast pumps.
@@ -5784,7 +5784,7 @@
 			<</if>>
 
 			<<if $slaves[$i].devotion > 50>>
-				$He does $his best for $HeadGirl.slaveName, and thus you, so $he frequently deserves a reward and never needs to be punished.
+				$He does $his best for _S.HeadGirl.slaveName, and thus you, so $he frequently deserves a reward and never needs to be punished.
 				<<set _punishments = 0>>
 				<<set _rewards = 3>>
 			<<elseif $slaves[$i].devotion > 20>>
@@ -5807,7 +5807,7 @@
 					<<set _punishments = 1>>
 					<<set _rewards = 0>>
 				<<else>>
-					$He hates you and $HeadGirl.slaveName too much to obey, so $he needs constant punishment.
+					$He hates you and _S.HeadGirl.slaveName too much to obey, so $he needs constant punishment.
 					<<set _punishments = 3>>
 					<<set _rewards = 0>>
 				<</if>>
@@ -5871,7 +5871,7 @@
 					<<run healthDamage($slaves[$i], _punishments)>>
 					<<set $slaves[$i].trust -= 2*_punishments>>
 				<<case "chastity">>
-					When $he disobeys, $HeadGirl.slaveName @@.gold;ruins $his next orgasm,@@ @@.red;reducing $his libido@@ but breaking $him to @@.hotpink;sexual obedience.@@
+					When $he disobeys, _S.HeadGirl.slaveName @@.gold;ruins $his next orgasm,@@ @@.red;reducing $his libido@@ but breaking $him to @@.hotpink;sexual obedience.@@
 					<<if $slaves[$i].energy > 2>><<set $slaves[$i].energy -= 2*_punishments>><</if>>
 					<<set $slaves[$i].devotion += _punishments>>
 					<<set $slaves[$i].trust -= _punishments>>
@@ -6067,10 +6067,10 @@
 					<<set $slaves[$i].trust += 2>>
 				<</if>>
 			<<else>>
-				<<if ($slaves[$i].ID == $HeadGirl.ID) && ($HGSuite == 1)>>
+				<<if ($slaves[$i].ID == $HeadGirlID) && ($HGSuite == 1)>>
 					$He is @@.hotpink;very happy@@ with $his suite and @@.mediumaquamarine;trusts@@ you a bit more for providing it.
 					<<set $slaves[$i].devotion += 1, $slaves[$i].trust += 1>>
-				<<elseif ($slaves[$i].ID == $Bodyguard.ID) && ($dojo <= 1)>>
+				<<elseif ($slaves[$i].ID == $BodyguardID) && ($dojo <= 1)>>
 					$He rarely leaves your company enough to make use of $his living area.
 				<<elseif ($slaves[$i].rules.living == "luxurious")>>
 					$He is @@.hotpink;very happy@@ with $his little room and @@.mediumaquamarine;trusts@@ you a bit more for providing it.
diff --git a/src/uncategorized/salon.tw b/src/uncategorized/salon.tw
index 057c9ec21c72d376abc5d4a3ab3e56d4e832b70e..2d0ef9efd71a2d4bd67968bdb7d759e3af25611a 100644
--- a/src/uncategorized/salon.tw
+++ b/src/uncategorized/salon.tw
@@ -17,13 +17,15 @@
 </span>
 
 /* 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>
+<span id="artFrame">
+	<<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>>
-<</if>>
+</span>
 /* 000-250-006 */
 
 /* EYES */
@@ -71,75 +73,10 @@
 
 /* EARS */
 <h3>Ears</h3>
-<div>
-	<<if getSlave($AS).earT != "none" && getSlave($AS).earTColor != "hairless">>
-		$His fluffy ears are <<= getSlave($AS).earTColor>>.
-		<div class="choices">
-			<<if getSlave($AS).earTColor != getSlave($AS).hColor>>
-				[[Match current hair|Salon][getSlave($AS).earTColor = getSlave($AS).hColor]] or <span class="note">choose a new one:</span>
-			<<else>>
-				<span class="note">Choose a dye color before dyeing $his ears:</span>
-			<</if>>
-		</div>
-		<div class="choices">
-			Colors:
-				[[Auburn|Salon][$primaryEarColor = "auburn"]]
-				| [[Black|Salon][$primaryEarColor = "black"]]
-				| [[Blazing Red|Salon][$primaryEarColor = "blazing red"]]
-				| [[Blonde|Salon][$primaryEarColor = "blonde"]]
-				| [[Blue-Violet|Salon][$primaryEarColor = "blue-violet"]]
-				| [[Blue|Salon][$primaryEarColor = "blue"]]
-				| [[Brown|Salon][$primaryEarColor = "brown"]]
-				| [[Burgundy|Salon][$primaryEarColor = "burgundy"]]
-				| [[Chestnut|Salon][$primaryEarColor = "chestnut"]]
-				| [[Chocolate|Salon][$primaryEarColor = "chocolate brown"]]
-				| [[Copper|Salon][$primaryEarColor = "copper"]]
-				| [[Dark Blue|Salon][$primaryEarColor = "dark blue"]]
-				| [[Dark Brown|Salon][$primaryEarColor = "dark brown"]]
-				| [[Dark Orchid|Salon][$primaryEarColor = "dark orchid"]]
-				| [[Deep Red|Salon][$primaryEarColor = "deep red"]]
-				| [[Ginger|Salon][$primaryEarColor = "ginger"]]
-				| [[Golden|Salon][$primaryEarColor = "golden"]]
-				| [[Green-yellow|Salon][$primaryEarColor = "green-yellow"]]
-				| [[Green|Salon][$primaryEarColor = "green"]]
-				| [[Grey|Salon][$primaryEarColor = "grey"]]
-				| [[Hazel|Salon][$primaryEarColor = "hazel"]]
-				| [[Jet Black|Salon][$primaryEarColor = "jet black"]]
-				| [[Neon Blue|Salon][$primaryEarColor = "neon blue"]]
-				| [[Neon Green|Salon][$primaryEarColor = "neon green"]]
-				| [[Neon Pink|Salon][$primaryEarColor = "neon pink"]]
-				| [[Pink|Salon][$primaryEarColor = "pink"]]
-				| [[Platinum Blonde|Salon][$primaryEarColor = "platinum blonde"]]
-				| [[Purple|Salon][$primaryEarColor = "purple"]]
-				| [[Red|Salon][$primaryEarColor = "red"]]
-				| [[Sea Green|Salon][$primaryEarColor = "sea green"]]
-				| [[Silver|Salon][$primaryEarColor = "silver"]]
-				| [[Strawberry-Blonde|Salon][$primaryEarColor = "strawberry-blonde"]]
-				| [[White|Salon][$primaryEarColor = "white"]]
-		</div>
-
-		<div class="choices">
-			Highlights:
-				[[None|Salon][$secondaryEarColor = ""]]
-				| [[Black|Salon][$secondaryEarColor = " with black highlights"]]
-				| [[Blazing Red|Salon][$secondaryEarColor = " with blazing red highlights"]]
-				| [[Blonde|Salon][$secondaryEarColor = " with blonde highlights"]]
-				| [[Grey|Salon][$secondaryEarColor = " with grey highlights"]]
-				| [[Neon Blue|Salon][$secondaryEarColor = " with neon blue highlights"]]
-				| [[Neon Green|Salon][$secondaryEarColor = " with neon green highlights"]]
-				| [[Neon Pink|Salon][$secondaryEarColor = " with neon pink highlights"]]
-				| [[Rainbow|Salon][$secondaryEarColor = " with rainbow highlights"]]
-				| [[Silver|Salon][$secondaryEarColor = " with silver highlights"]]
-				| [[White|Salon][$secondaryEarColor = " with white highlights"]]
-			</div>
-
-		<<if $primaryEarColor != 0>>
-			<div class="choices">
-				[["Dye " + $his + " ears"|Salon][getSlave($AS).earTColor = ($primaryEarColor + $secondaryEarColor),cashX(forceNeg($modCost), "slaveMod", getSlave($AS)), $primaryEarColor = 0, $secondaryEarColor = ""]] $primaryEarColor $secondaryEarColor now?
-			</div>
-		<</if>>
-	<</if>>
-</div>
+<p id="salonEars"></p>
+<script>
+	App.Medicine.Salon.ears(getSlave(V.AS));
+</script>
 
 <div>
 	<<if getSlave($AS).earImplant == 1>>
@@ -219,267 +156,11 @@
 /* HAIR */
 
 <h3>Hair</h3>
-<div>
-	<<if getSlave($AS).bald != 1>>
-		$His hair is <<= getSlave($AS).hColor>>.
-		<<if getSlave($AS).origHColor != getSlave($AS).hColor>>
-			[[Restore natural color|Salon][getSlave($AS).hColor = getSlave($AS).origHColor]] or <span class="note">choose a new one:</span>
-		<<else>>
-			<span class="note">Choose a dye color before dyeing $his hair:</span>
-		<</if>>
-		<div class="choices">
-			Colors:
-				[[Auburn|Salon][$primaryHairColor = "auburn"]]
-				| [[Black|Salon][$primaryHairColor = "black"]]
-				| [[Blazing Red|Salon][$primaryHairColor = "blazing red"]]
-				| [[Blonde|Salon][$primaryHairColor = "blonde"]]
-				| [[Blue-Violet|Salon][$primaryHairColor = "blue-violet"]]
-				| [[Blue|Salon][$primaryHairColor = "blue"]]
-				| [[Brown|Salon][$primaryHairColor = "brown"]]
-				| [[Burgundy|Salon][$primaryHairColor = "burgundy"]]
-				| [[Chestnut|Salon][$primaryHairColor = "chestnut"]]
-				| [[Chocolate|Salon][$primaryHairColor = "chocolate brown"]]
-				| [[Copper|Salon][$primaryHairColor = "copper"]]
-				| [[Dark Blue|Salon][$primaryHairColor = "dark blue"]]
-				| [[Dark Brown|Salon][$primaryHairColor = "dark brown"]]
-				| [[Dark Orchid|Salon][$primaryHairColor = "dark orchid"]]
-				| [[Deep Red|Salon][$primaryHairColor = "deep red"]]
-				| [[Ginger|Salon][$primaryHairColor = "ginger"]]
-				| [[Golden|Salon][$primaryHairColor = "golden"]]
-				| [[Green-yellow|Salon][$primaryHairColor = "green-yellow"]]
-				| [[Green|Salon][$primaryHairColor = "green"]]
-				| [[Grey|Salon][$primaryHairColor = "grey"]]
-				| [[Hazel|Salon][$primaryHairColor = "hazel"]]
-				| [[Jet Black|Salon][$primaryHairColor = "jet black"]]
-				| [[Neon Blue|Salon][$primaryHairColor = "neon blue"]]
-				| [[Neon Green|Salon][$primaryHairColor = "neon green"]]
-				| [[Neon Pink|Salon][$primaryHairColor = "neon pink"]]
-				| [[Pink|Salon][$primaryHairColor = "pink"]]
-				| [[Platinum Blonde|Salon][$primaryHairColor = "platinum blonde"]]
-				| [[Purple|Salon][$primaryHairColor = "purple"]]
-				| [[Red|Salon][$primaryHairColor = "red"]]
-				| [[Sea Green|Salon][$primaryHairColor = "sea green"]]
-				| [[Silver|Salon][$primaryHairColor = "silver"]]
-				| [[Strawberry-Blonde|Salon][$primaryHairColor = "strawberry-blonde"]]
-				| [[White|Salon][$primaryHairColor = "white"]]
-		</div>
-
-		<div class="choices">
-			Highlights:
-				[[None|Salon][$secondaryHairColor = ""]]
-				| [[Black|Salon][$secondaryHairColor = " with black highlights"]]
-				| [[Blazing Red|Salon][$secondaryHairColor = " with blazing red highlights"]]
-				| [[Blonde|Salon][$secondaryHairColor = " with blonde highlights"]]
-				| [[Grey|Salon][$secondaryHairColor = " with grey highlights"]]
-				| [[Neon Blue|Salon][$secondaryHairColor = " with neon blue highlights"]]
-				| [[Neon Green|Salon][$secondaryHairColor = " with neon green highlights"]]
-				| [[Neon Pink|Salon][$secondaryHairColor = " with neon pink highlights"]]
-				| [[Rainbow|Salon][$secondaryHairColor = " with rainbow highlights"]]
-				| [[Silver|Salon][$secondaryHairColor = " with silver highlights"]]
-				| [[White|Salon][$secondaryHairColor = " with white highlights"]]
-		</div>
-
-		<<if $primaryHairColor != 0>>
-			<div class="choices">
-				[["Dye " + $his + " hair"|Salon][getSlave($AS).hColor = ($primaryHairColor + $secondaryHairColor),cashX(forceNeg($modCost), "slaveMod", getSlave($AS)), $primaryHairColor = 0, $secondaryHairColor = ""]] $primaryHairColor $secondaryHairColor now?
-			</div>
-		<</if>>
-
-		<div>
-			<<if getSlave($AS).hStyle !== "shaved">>
-				$His <<= getSlave($AS).hStyle>> hair is <<= lengthToEitherUnit(getSlave($AS).hLength)>> long.
-			<<else>>
-				$His hair is shaved smooth.
-			<</if>>
-			<span class="note">General hairstyles will conform to hair length and clothing choices.</span>
-		</div>
-		<div class="choices">
-			<<if getSlave($AS).hLength > 1>>
-				Style $his hair:
-				[[Afro|Salon][getSlave($AS).hStyle = "afro",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Braided|Salon][getSlave($AS).hStyle = "braided",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Cornrows|Salon][getSlave($AS).hStyle = "cornrows",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Curled|Salon][getSlave($AS).hStyle = "curled",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Dreadlocks|Salon][getSlave($AS).hStyle = "dreadlocks",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Eary|Salon][getSlave($AS).hStyle = "eary",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a bun|Salon][getSlave($AS).hStyle = "bun",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a messy bun|Salon][getSlave($AS).hStyle = "messy bun",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a ponytail|Salon][getSlave($AS).hStyle = "ponytail",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In tails|Salon][getSlave($AS).hStyle = "tails",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Luxurious|Salon][getSlave($AS).hStyle = "luxurious",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Messy|Salon][getSlave($AS).hStyle = "messy",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Neat|Salon][getSlave($AS).hStyle = "neat",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Permed|Salon][getSlave($AS).hStyle = "permed",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Shaved sides|Salon][getSlave($AS).hStyle = "strip",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Up|Salon][getSlave($AS).hStyle = "up",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<<else>>
-				<span class="note">$His hair is too short to style meaningfully</span>
-			<</if>>
-		</div>
-
-		<div class="choices">
-			<<if getSlave($AS).hLength > 1>>
-				Cut and style $his hair:
-				[[Shaved|Salon][getSlave($AS).hStyle = "shaved",getSlave($AS).hLength = 0,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Trimmed short|Salon][getSlave($AS).hStyle = "trimmed",getSlave($AS).hLength = 10,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Buzzcut|Salon][getSlave($AS).hStyle = "buzzcut",getSlave($AS).hLength = 1,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-		</div>
+<p id="salonHair"></p>
+<script>
+	App.Medicine.Salon.hair(getSlave(V.AS));
+</script>
 
-		<div class="choices">
-			Cut or lengthen $his hair:
-			<<if getSlave($AS).hLength > 5>>
-				[[Very short|Salon][getSlave($AS).hLength = 5,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-			<<if getSlave($AS).hLength > 10>>
-				| [[Short|Salon][getSlave($AS).hLength = 10,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-			<<if getSlave($AS).hLength > 30>>
-				| [[Shoulder length|Salon][getSlave($AS).hLength = 30,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-			<<if getSlave($AS).hLength > 60>>
-				| [[Long|Salon][getSlave($AS).hLength = 60,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-			<<if getSlave($AS).hLength > 100>>
-				| [[Very long|Salon][getSlave($AS).hLength = 100,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-			<<if getSlave($AS).hLength == 1>>
-				| [[Apply hair growth stimulating treatment|Salon][getSlave($AS).hLength = 1,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<<elseif getSlave($AS).hLength < 150>>
-				| [[Apply extensions|Salon][getSlave($AS).hLength += 10,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			<</if>>
-				| Custom length: <<textbox "_newHLength" _oldHLength "Salon">>
-			<<if $showInches == 1>>
-				cm (<<= cmToInchString(getSlave($AS).hLength)>>)
-			<<elseif $showInches == 2>>
-				inches
-			<</if>>
-			[[Apply|Salon][cashX(forceNeg($modCost), "slaveMod", getSlave($AS)), getSlave($AS).hLength = (Number(_newHLength) || getSlave($AS).hLength)]]
-		</div>
-
-		<div class="choices">
-			Have $his hair carefully maintained at its current length:
-			<<if getSlave($AS).haircuts == 1>>
-				[[Cease maintenance|Salon][getSlave($AS).haircuts = 0]]
-			<<else>>
-				[[Begin maintenance|Salon][getSlave($AS).haircuts = 1]]
-			<</if>>
-		</div>
-	<<else>>
-		<<if getSlave($AS).hStyle == "bald">>
-			$He is completely bald.
-		<<else>>
-			$His current wig is <<= getSlave($AS).hColor>>.
-			<<if getSlave($AS).hStyle != "bald">>
-				[[Remove wig|Salon][getSlave($AS).hStyle = "bald",getSlave($AS).hLength = 0,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]] or <span class="note">choose a new one:</span>
-			<<else>>
-				<span class="note">Choose a wig color:</span>
-			<</if>>
-
-			<div class="choices">
-				Colors:
-					[[Auburn|Salon][$primaryHairColor = "auburn"]]
-					| [[Black|Salon][$primaryHairColor = "black"]]
-					| [[Blazing Red|Salon][$primaryHairColor = "blazing red"]]
-					| [[Blonde|Salon][$primaryHairColor = "blonde"]]
-					| [[Blue-Violet|Salon][$primaryHairColor = "blue-violet"]]
-					| [[Blue|Salon][$primaryHairColor = "blue"]]
-					| [[Brown|Salon][$primaryHairColor = "brown"]]
-					| [[Burgundy|Salon][$primaryHairColor = "burgundy"]]
-					| [[Chestnut|Salon][$primaryHairColor = "chestnut"]]
-					| [[Chocolate|Salon][$primaryHairColor = "chocolate brown"]]
-					| [[Copper|Salon][$primaryHairColor = "copper"]]
-					| [[Dark Blue|Salon][$primaryHairColor = "dark blue"]]
-					| [[Dark Brown|Salon][$primaryHairColor = "dark brown"]]
-					| [[Dark Orchid|Salon][$primaryHairColor = "dark orchid"]]
-					| [[Deep Red|Salon][$primaryHairColor = "deep red"]]
-					| [[Ginger|Salon][$primaryHairColor = "ginger"]]
-					| [[Golden|Salon][$primaryHairColor = "golden"]]
-					| [[Green-yellow|Salon][$primaryHairColor = "green-yellow"]]
-					| [[Green|Salon][$primaryHairColor = "green"]]
-					| [[Grey|Salon][$primaryHairColor = "grey"]]
-					| [[Hazel|Salon][$primaryHairColor = "hazel"]]
-					| [[Jet Black|Salon][$primaryHairColor = "jet black"]]
-					| [[Neon Blue|Salon][$primaryHairColor = "neon blue"]]
-					| [[Neon Green|Salon][$primaryHairColor = "neon green"]]
-					| [[Neon Pink|Salon][$primaryHairColor = "neon pink"]]
-					| [[Pink|Salon][$primaryHairColor = "pink"]]
-					| [[Platinum Blonde|Salon][$primaryHairColor = "platinum blonde"]]
-					| [[Purple|Salon][$primaryHairColor = "purple"]]
-					| [[Red|Salon][$primaryHairColor = "red"]]
-					| [[Sea Green|Salon][$primaryHairColor = "sea green"]]
-					| [[Silver|Salon][$primaryHairColor = "silver"]]
-					| [[Strawberry-Blonde|Salon][$primaryHairColor = "strawberry-blonde"]]
-					| [[White|Salon][$primaryHairColor = "white"]]
-			</div>
-
-			<div class="choices">
-				Highlights:
-					[[None|Salon][$secondaryHairColor = ""]]
-					| [[Black|Salon][$secondaryHairColor = " with black highlights"]]
-					| [[Blazing Red|Salon][$secondaryHairColor = " with blazing red highlights"]]
-					| [[Blonde|Salon][$secondaryHairColor = " with blonde highlights"]]
-					| [[Grey|Salon][$secondaryHairColor = " with grey highlights"]]
-					| [[Neon Blue|Salon][$secondaryHairColor = " with neon blue highlights"]]
-					| [[Neon Green|Salon][$secondaryHairColor = " with neon green highlights"]]
-					| [[Neon Pink|Salon][$secondaryHairColor = " with neon pink highlights"]]
-					| [[Rainbow|Salon][$secondaryHairColor = " with rainbow highlights"]]
-					| [[Silver|Salon][$secondaryHairColor = " with silver highlights"]]
-					| [[White|Salon][$secondaryHairColor = " with white highlights"]]
-			</div>
-
-			<<if $primaryHairColor != 0>>
-				<div class="choices">
-					[[Change|Salon][getSlave($AS).hColor = ($primaryHairColor + $secondaryHairColor),cashX(forceNeg($modCost), "slaveMod", getSlave($AS)), $primaryHairColor = 0, $secondaryHairColor = ""]] $his wig color to $primaryHairColor $secondaryHairColor
-				</div>
-			<</if>>
-		<</if>>
-
-		<div>
-			<<if getSlave($AS).hStyle != "bald">>
-				$His <<= getSlave($AS).hStyle>> wig is <<= lengthToEitherUnit(getSlave($AS).hLength)>> long.
-				<span class="note">General hairstyles will conform to hair length and clothing choices.</span>
-
-				<<if getSlave($AS).hLength == 0>><<set getSlave($AS).hLength = 10>><</if>>
-
-				<div class="choices">
-					Set wig length to:
-					<<if getSlave($AS).hLength != 10>>[[lengthToEitherUnit(10)|Salon][getSlave($AS).hLength = 10,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-					<<if getSlave($AS).hLength != 30>>[[lengthToEitherUnit(30)|Salon][getSlave($AS).hLength = 30,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-					<<if getSlave($AS).hLength != 60>>[[lengthToEitherUnit(60)|Salon][getSlave($AS).hLength = 60,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-					<<if getSlave($AS).hLength != 90>>[[lengthToEitherUnit(90)|Salon][getSlave($AS).hLength = 90,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-					<<if getSlave($AS).hLength != 120>>[[lengthToEitherUnit(120)|Salon][getSlave($AS).hLength = 120,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-					<<if getSlave($AS).hLength != 150>>[[lengthToEitherUnit(150)|Salon][getSlave($AS).hLength = 150,cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]<</if>>
-				</div>
-			<<else>>
-				$He is not wearing a wig.
-			<</if>>
-			<div class="choices">
-				<<if getSlave($AS).hStyle != "bald">>
-					Set wig style:
-				<<else>>
-					Give $him a wig:
-				<</if>>
-				[[Afro|Salon][getSlave($AS).hStyle = "afro",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Braided|Salon][getSlave($AS).hStyle = "braided",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Cornrows|Salon][getSlave($AS).hStyle = "cornrows",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Curled|Salon][getSlave($AS).hStyle = "curled",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Dreadlocks|Salon][getSlave($AS).hStyle = "dreadlocks",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Eary|Salon][getSlave($AS).hStyle = "eary",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a bun|Salon][getSlave($AS).hStyle = "bun",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a messy bun|Salon][getSlave($AS).hStyle = "messy bun",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In a ponytail|Salon][getSlave($AS).hStyle = "ponytail",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[In tails|Salon][getSlave($AS).hStyle = "tails",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Luxurious|Salon][getSlave($AS).hStyle = "luxurious",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Messy|Salon][getSlave($AS).hStyle = "messy",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Neat|Salon][getSlave($AS).hStyle = "neat",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Permed|Salon][getSlave($AS).hStyle = "permed",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Shaved sides|Salon][getSlave($AS).hStyle = "strip",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-				| [[Up|Salon][getSlave($AS).hStyle = "up",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			</div>
-		</div>
-	<</if>>
-</div>
 /* MAKEUP */
 <h3>Makeup</h3>
 
@@ -605,71 +286,10 @@
 	| [[Tiger Stripes|Salon][getSlave($AS).skin = "tiger striped",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 </div>
 
-<<if getSlave($AS).tail != "none">>
-	$His tail is <<= getSlave($AS).tailColor>>.
-	<<if getSlave($AS).tailColor != getSlave($AS).hColor>>
-		[[Match current hair|Salon][getSlave($AS).tailColor = getSlave($AS).hColor]] or <span class="note">choose a new one:</span>
-	<<else>>
-		<span class="note">Choose a dye color before dyeing $his tail:</span>
-	<</if>>
-	<div class="choices">
-		Colors:
-			[[Auburn|Salon][$primaryTailColor = "auburn"]]
-			| [[Black|Salon][$primaryTailColor = "black"]]
-			| [[Blazing Red|Salon][$primaryTailColor = "blazing red"]]
-			| [[Blonde|Salon][$primaryTailColor = "blonde"]]
-			| [[Blue-Violet|Salon][$primaryTailColor = "blue-violet"]]
-			| [[Blue|Salon][$primaryTailColor = "blue"]]
-			| [[Brown|Salon][$primaryTailColor = "brown"]]
-			| [[Burgundy|Salon][$primaryTailColor = "burgundy"]]
-			| [[Chestnut|Salon][$primaryTailColor = "chestnut"]]
-			| [[Chocolate|Salon][$primaryTailColor = "chocolate brown"]]
-			| [[Copper|Salon][$primaryTailColor = "copper"]]
-			| [[Dark Blue|Salon][$primaryTailColor = "dark blue"]]
-			| [[Dark Brown|Salon][$primaryTailColor = "dark brown"]]
-			| [[Dark Orchid|Salon][$primaryTailColor = "dark orchid"]]
-			| [[Deep Red|Salon][$primaryTailColor = "deep red"]]
-			| [[Ginger|Salon][$primaryTailColor = "ginger"]]
-			| [[Golden|Salon][$primaryTailColor = "golden"]]
-			| [[Green-yellow|Salon][$primaryTailColor = "green-yellow"]]
-			| [[Green|Salon][$primaryTailColor = "green"]]
-			| [[Grey|Salon][$primaryTailColor = "grey"]]
-			| [[Hazel|Salon][$primaryTailColor = "hazel"]]
-			| [[Jet Black|Salon][$primaryTailColor = "jet black"]]
-			| [[Neon Blue|Salon][$primaryTailColor = "neon blue"]]
-			| [[Neon Green|Salon][$primaryTailColor = "neon green"]]
-			| [[Neon Pink|Salon][$primaryTailColor = "neon pink"]]
-			| [[Pink|Salon][$primaryTailColor = "pink"]]
-			| [[Platinum Blonde|Salon][$primaryTailColor = "platinum blonde"]]
-			| [[Purple|Salon][$primaryTailColor = "purple"]]
-			| [[Red|Salon][$primaryTailColor = "red"]]
-			| [[Sea Green|Salon][$primaryTailColor = "sea green"]]
-			| [[Silver|Salon][$primaryTailColor = "silver"]]
-			| [[Strawberry-Blonde|Salon][$primaryTailColor = "strawberry-blonde"]]
-			| [[White|Salon][$primaryTailColor = "white"]]
-	</div>
-
-	<div class="choices">
-		Highlights:
-			[[None|Salon][$secondaryTailColor = ""]]
-			| [[Black|Salon][$secondaryTailColor = " with black highlights"]]
-			| [[Blazing Red|Salon][$secondaryTailColor = " with blazing red highlights"]]
-			| [[Blonde|Salon][$secondaryTailColor = " with blonde highlights"]]
-			| [[Grey|Salon][$secondaryTailColor = " with grey highlights"]]
-			| [[Neon Blue|Salon][$secondaryTailColor = " with neon blue highlights"]]
-			| [[Neon Green|Salon][$secondaryTailColor = " with neon green highlights"]]
-			| [[Neon Pink|Salon][$secondaryTailColor = " with neon pink highlights"]]
-			| [[Rainbow|Salon][$secondaryTailColor = " with rainbow highlights"]]
-			| [[Silver|Salon][$secondaryTailColor = " with silver highlights"]]
-			| [[White|Salon][$secondaryTailColor = " with white highlights"]]
-	</div>
-
-	<<if $primaryTailColor != 0>>
-		<div class="choices">
-			[["Dye " + $his + " tail"|Salon][getSlave($AS).tailColor = ($primaryTailColor + $secondaryTailColor),cashX(forceNeg($modCost), "slaveMod", getSlave($AS)), $primaryTailColor = 0, $secondaryTailColor = ""]] $primaryTailColor $secondaryTailColor now?
-		</div>
-	<</if>>
-<</if>>
+<p id="salonTail"></p>
+<script>
+	App.Medicine.Salon.tail(getSlave(V.AS));
+</script>
 
 /* MARKS */
 <div>
@@ -731,7 +351,7 @@
 			| [[Dark Blue|Salon][getSlave($AS).eyebrowHColor = "dark blue",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Brown|Salon][getSlave($AS).eyebrowHColor = "dark brown",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Orchid|Salon][getSlave($AS).eyebrowHColor = "dark orchid",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			| [[Deep Red|Salon][$primaryHairColor = "deep red"]]
+			| [[Deep Red|Salon][getSlave($AS).eyebrowHColor = "deep red",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Ginger|Salon][getSlave($AS).eyebrowHColor = "ginger",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Golden|Salon][getSlave($AS).eyebrowHColor = "golden",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Green-yellow|Salon][getSlave($AS).eyebrowHColor = "green-yellow",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
@@ -822,7 +442,7 @@
 			| [[Dark Blue|Salon][getSlave($AS).pubicHColor = "dark blue",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Brown|Salon][getSlave($AS).pubicHColor = "dark brown",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Orchid|Salon][getSlave($AS).pubicHColor = "dark orchid",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			| [[Deep Red|Salon][$primaryHairColor = "deep red"]]
+			| [[Deep Red|Salon][getSlave($AS).pubicHColor = "deep red",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Ginger|Salon][getSlave($AS).pubicHColor = "ginger",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Golden|Salon][getSlave($AS).pubicHColor = "golden",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Green-yellow|Salon][getSlave($AS).pubicHColor = "green-yellow",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
@@ -896,7 +516,7 @@
 			| [[Dark Blue|Salon][getSlave($AS).underArmHColor = "dark blue",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Brown|Salon][getSlave($AS).underArmHColor = "dark brown",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Dark Orchid|Salon][getSlave($AS).underArmHColor = "dark orchid",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
-			| [[Deep Red|Salon][$primaryHairColor = "deep red"]]
+			| [[Deep Red|Salon][getSlave($AS).underArmHColor = "deep red",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Ginger|Salon][getSlave($AS).underArmHColor = "ginger",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Golden|Salon][getSlave($AS).underArmHColor = "golden",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
 			| [[Green-yellow|Salon][getSlave($AS).underArmHColor = "green-yellow",cashX(forceNeg($modCost), "slaveMod", getSlave($AS))]]
diff --git a/src/uncategorized/scheduledEvent.tw b/src/uncategorized/scheduledEvent.tw
index 45ae65c25e58740425d03e981382b1b232652ba1..38cadbed2828240f27d28a34f78366533cd2e474 100644
--- a/src/uncategorized/scheduledEvent.tw
+++ b/src/uncategorized/scheduledEvent.tw
@@ -129,9 +129,9 @@
 	<<if $week > 50 && $FCTV.remote < 2>> <<goto "SE FCTV Remote">>
 	<<else>> <<goto "SE FCTV Watch">>
 	<</if>>
-<<elseif ($Recruiter != 0 && $recruiterEugenics == 0 && $recruiterProgress >= 13)>>
+<<elseif ($RecruiterID != 0 && $recruiterEugenics == 0 && $recruiterProgress >= 13)>>
 	<<goto "SE recruiter success">>
-<<elseif $Recruiter != 0 && $recruiterEugenics == 1 && $recruiterProgress >= (13 + (policies.countEugenicsSMRs() * 6))>>
+<<elseif $RecruiterID != 0 && $recruiterEugenics == 1 && $recruiterProgress >= (13 + (policies.countEugenicsSMRs() * 6))>>
 	<<goto "SE recruiter success">>
 <<elseif ($customSlaveOrdered == 1 && $customSlaveOrderedReorder != 1)>>
 	<<goto "SE custom slave delivery">>
@@ -143,7 +143,7 @@
 	<<goto "SE coursing">>
 <<elseif $policies.raidingMercenaries == 1 && ($week > ($raided + 6))>>
 	<<goto "SE raiding">>
-<<elseif ((($fighterIDs.length > 1) && ($pitBG == 0)) || (($fighterIDs.length > 0) && ($Bodyguard != 0) && ($pitBG == 1)) || (($pitAnimal > 0) && ($fighterIDs.length > 0)) || ($killChoice == 2)) && ($pitFought == 0)>>
+<<elseif ((($fighterIDs.length > 1) && ($pitBG == 0)) || (($fighterIDs.length > 0) && ($BodyguardID != 0) && ($pitBG == 1)) || (($pitAnimal > 0) && ($fighterIDs.length > 0)) || ($killChoice == 2)) && ($pitFought == 0)>>
 	<<if $pitLethal == 1>><<goto "SE lethal pit">><<else>><<goto "SE nonlethal pit">><</if>>
 <<elseif ($bioreactorPerfectedID != 0) && ($bioreactorsAnnounced != 1)>>
 	<<goto "P bioreactor perfected">>
diff --git a/src/uncategorized/seNicaeaPreperation.tw b/src/uncategorized/seNicaeaPreperation.tw
index 5bacd93f81f5eb50de6124cbf1a58e438444e0bf..6cb571b353caa1e34d1dec07d511e15c64c17f02 100644
--- a/src/uncategorized/seNicaeaPreperation.tw
+++ b/src/uncategorized/seNicaeaPreperation.tw
@@ -17,12 +17,12 @@ But you did win. Chattel Religionism will remain a free and open faith that acce
 <<else>>
 
 You have a busy week preparing for the $nicaeaName ahead of you. Planning the event itself is the easy part. Modern arcologies are designed to host big events, and it's not like you don't have a lot of labor available to assist you. Furthermore, one of $assistant.name's standard functions is event planning. _HeA helps manage the thousand details of hosting a group of rich, powerful, opinionated people, letting you focus on the most important matters.
-<<if $HeadGirl != 0>>
-	<<setLocalPronouns $HeadGirl>>
-	$HeadGirl.slaveName is also a great help. $He does $his best to supervise your other slaves even more thoroughly than usual, giving you precious time.
+<<if $HeadGirlID != 0>>
+	<<setLocalPronouns _S.HeadGirl>>
+	_S.HeadGirl.slaveName is also a great help. $He does $his best to supervise your other slaves even more thoroughly than usual, giving you precious time.
 <</if>>
-<<if $Concubine != 0>>
-	Of course, $Concubine.slaveName is always there to help you relax when you need it.
+<<if _S.Concubine>>
+	Of course, _S.Concubine.slaveName is always there to help you relax when you need it.
 <</if>>
 <br><br>
 The first major choice you have to make concerns the seats on the Council. There are some Chattel Religionists you simply have to invite; they're so prominent that if you don't, the Council's decisions will lack any weight. But that doesn't mean you don't have room to manipulate the list of people invited in order to enhance your influence in the council. It wouldn't be difficult to selectively invite Chattel Religionist slaveowners that have the reputation to support attendance, who you know to be philosophically aligned with your thoughts about the faith. On the other hand, managing the seats on the Council with perfect impartiality would definitely help acceptance of any creed the Council agrees upon.
diff --git a/src/uncategorized/seRecruiterSuccess.tw b/src/uncategorized/seRecruiterSuccess.tw
index 00ab9d919853b0e4b12e57ca342db07628118a11..57535f22625eaf8097d1b1db6592e37c6687156d 100644
--- a/src/uncategorized/seRecruiterSuccess.tw
+++ b/src/uncategorized/seRecruiterSuccess.tw
@@ -169,31 +169,31 @@
 <<set _slaveCost = slaveCost($activeSlave)>>
 <<set _slaveCost -= _ContractCost>>
 <<setLocalPronouns $activeSlave>>
-<<setLocalPronouns $Recruiter 2>>
+<<setLocalPronouns _S.Recruiter 2>>
 
 <<if $recruiterTarget == "young migrants">>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced a starving young migrant from the old world that $he'll have a better chance at survival as one of your slaves.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced a starving young migrant from the old world that $he'll have a better chance at survival as one of your slaves.
 
 <<elseif $recruiterTarget == "recent divorcees">>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced a recent divorcee from the old world that $he'll have a better quality of life as one of your slaves.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced a recent divorcee from the old world that $he'll have a better quality of life as one of your slaves.
 
 <<elseif $recruiterTarget == "reassignment candidates">>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced an old world person desperate for modern reassignment surgery that you'll provide it if $he agrees to be a sex slave after $he's recreated as a female.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced an old world person desperate for modern reassignment surgery that you'll provide it if $he agrees to be a sex slave after $he's recreated as a female.
 
 <<elseif $recruiterTarget == "dissolute sissies">>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced an old world sissy that $he'd be better off as a slave in a society that is so totally sexually libertine as to accept as female and respectable anyone who takes cock.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced an old world sissy that $he'd be better off as a slave in a society that is so totally sexually libertine as to accept as female and respectable anyone who takes cock.
 
 <<elseif $recruiterTarget == "expectant mothers">>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced an unhealthy expectant mother that you'll provide $him with modern medicine in return for $his enslavement.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced an unhealthy expectant mother that you'll provide $him with modern medicine in return for $his enslavement.
 
 <<else>>
 
-Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced a desperate old world whore that $he'd be better off as a slave in $arcologies[0].name than as a free prostitute on the rough streets of a traditional city.
+Your recruiter _S.Recruiter.slaveName has succeeded; _he2's convinced a desperate old world whore that $he'd be better off as a slave in $arcologies[0].name than as a free prostitute on the rough streets of a traditional city.
 
 <</if>>
 
@@ -212,7 +212,7 @@ Your recruiter $Recruiter.slaveName has succeeded; _he2's convinced a desperate
 	<</replace>>
 <</link>> //This will cost <<print cashFormat(_ContractCost)>>//
 <br><<link "Sell $him immediately">>
-	<<run cashX(_slaveCost, "slaveTransfer", $Recruiter)>>
+	<<run cashX(_slaveCost, "slaveTransfer", _S.Recruiter)>>
 	<<replace "#result">>
 	$activeSlave.slaveName accepts being resold without much fuss. $He's merely exchanged one unknown owner for another. For all $he knows $his new buyer will be less abusive than you would have been. $He would be less complacent if $he knew who $his buyers are; $he'll be immured in an arcade within the hour.
 	<</replace>>
diff --git a/src/uncategorized/seRetirement.tw b/src/uncategorized/seRetirement.tw
index c38103479688aa2a09fe03b2a6431168674a2252..aac5a08c8d2d665431110ce41feaafd5afcf0c3a 100644
--- a/src/uncategorized/seRetirement.tw
+++ b/src/uncategorized/seRetirement.tw
@@ -90,7 +90,7 @@
 	<<else>>
 		<<set _toSearchAlt = $activeSlave.porn.prestigeDesc>>
 	<</if>>
-	<<if (_toSearch.indexOf("Head Girl") != -1) || ($HeadGirl.ID == $activeSlave.ID)>>
+	<<if (_toSearch.indexOf("Head Girl") != -1) || ($HeadGirlID == $activeSlave.ID)>>
 		<br><br>
 		$He has a reputation from $his long service as your Head Girl. To $his bemusement, and considerable satisfaction, $he has multiple job offers from slaving operations without even having to circulate $his resume.
 		<<if $activeSlave.fetish == "sadist">>
@@ -399,4 +399,3 @@
 	<<set $activeSlave = getSlave($retiree), $retiree = 0, $retired = 0>>
 	<<= removeActiveSlave()>>
 <</if>>
-
diff --git a/src/uncategorized/subordinateTargeting.tw b/src/uncategorized/subordinateTargeting.tw
index 8f357c015289900cdfc99f7a2fbcff653b40e205..7f9a8f115e9311fead3ccc60b7f63ff1158b773a 100644
--- a/src/uncategorized/subordinateTargeting.tw
+++ b/src/uncategorized/subordinateTargeting.tw
@@ -32,7 +32,7 @@
 	<</if>>
 	<br><br>[[None|Subordinate Targeting][$activeSlave.subTarget = 0]]
 	<br><br>__Select a slave for $him to submit to, sexually:__
-	<<= App.UI.SlaveList.slaveSelectionList(
+	<<includeDOM App.UI.SlaveList.slaveSelectionList(
 			s => s.devotion >= -20 && s.fuckdoll === 0 && State.variables.activeSlave.ID !== s.ID &&
 				(!isAmputee(State.variables.activeSlave) || !isAmputee(s)),
 			(s) => App.UI.DOM.passageLink(SlaveFullName(s), 'Subordinate Targeting', () => { V.activeSlave.subTarget = s.ID; }),
diff --git a/src/uncategorized/surgeryDegradation.tw b/src/uncategorized/surgeryDegradation.tw
index ea91b902be936fcf1f073f7a4aac7ec332f39665..5d3d7c8ae1643e9348b168af56d57eae35995ad6 100644
--- a/src/uncategorized/surgeryDegradation.tw
+++ b/src/uncategorized/surgeryDegradation.tw
@@ -958,7 +958,7 @@ As the remote surgery's long recovery cycle completes,
 	Even though creating an artificial scar is a trivial process, @@.red;$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 == $Bodyguard.ID>>
+		<<if getSlave($AS).ID == $BodyguardID>>
 			and upon <<if canSee(getSlave($AS))>>throwing a glance at the mirror<<else>>being informed of $his new scar<</if>>, $he frightens $himself, but is @@.hotpink;happier@@ with $his new look and @@.mediumaquamarine;more confident@@ that $he's the $girl for the job.
 			<<set getSlave($AS).devotion += 5>>
 			<<set getSlave($AS).trust += 5>>
diff --git a/src/uncategorized/underperformingSlaves.tw b/src/uncategorized/underperformingSlaves.tw
index 2b874d7244561fcf367584c74101a3c0be465769..5fc39bb79b88343d17662c48e4678fa42608a6a9 100644
--- a/src/uncategorized/underperformingSlaves.tw
+++ b/src/uncategorized/underperformingSlaves.tw
@@ -24,58 +24,15 @@
 	<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'highSale')" id="tab highSale">Worth much but earning little</button>
 	<button class="tablinks" onclick="App.UI.tabbar.openTab(event, 'expensive')" id="tab expensive">Costing vs earning</button>
 </div>
+
 <div id="highSale" class="tabcontent">
 	<div class="content">
-		<div style="font-style:italic">
-			Take the rough value of a slave and divide it by how much they made overall last week. This will tell you how many weeks it might take them to earn the same amount you'd get for selling them right now.
-		</div>
-		<<print App.UI.SlaveList.render.listMarkup(
-			getBestSlavesIDs(
-				{
-					part:(slave) => {
-						const ratio = slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave));
-						return ratio > 0 ? ratio : 100000000 + ratio;
-					},
-					count: 7,
-					filter:(slave) => (
-						["be confined in the arcade", "get milked", "whore", "work a glory hole", "work in the brothel", "work in the dairy"].includes(slave.assignment)
-						&& ((slave.weekAcquired + 1) < V.week)
-						&& slave.lastWeeksCashIncome
-					)
-				}
-			),
-			[],
-			App.UI.SlaveList.SlaveInteract.stdInteract,
-			(slave) => $(document.createDocumentFragment()).append(
-				`Worth ${cashFormatColor(slaveCost(slave))} / Nets ${cashFormatColor(slave.lastWeeksCashIncome - getSlaveCost(slave))} a week = ${(Math.trunc(slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave)))) > 0 ? (Math.trunc(slaveCost(slave) / (slave.lastWeeksCashIncome - getSlaveCost(slave)))) : "infinite"} weeks`
-			).get(0)
-		)>>
+		<<includeDOM App.Underperformers.highSale()>>
 	</div>
 </div>
 
 <div id="expensive" class="tabcontent">
 	<div class="content">
-		<div style="font-style:italic">
-			This list looks for moochers by weighing their weekly income against the weekly cost of providing for them.
-		</div>
-		<<print App.UI.SlaveList.render.listMarkup(
-			getBestSlavesIDs(
-				{
-					part:(slave) => (slave.lastWeeksCashIncome - getSlaveCost(slave)),
-					largest: false,
-					count: 7,
-					filter:(slave) => (
-						["be confined in the arcade", "get milked", "whore", "work a glory hole", "work in the brothel", "work in the dairy"].includes(slave.assignment)
-						&& ((slave.weekAcquired + 1) < V.week)
-						&& slave.lastWeeksCashIncome
-					)
-				}
-			),
-			[],
-			App.UI.SlaveList.SlaveInteract.stdInteract,
-			(slave) => $(document.createDocumentFragment()).append(
-				`${cashFormatColor(Math.trunc(slave.lastWeeksCashIncome - getSlaveCost(slave)))} net last week`
-			).get(0)
-		)>>
+		<<includeDOM App.Underperformers.expensive()>>
 	</div>
 </div>
diff --git a/src/uncategorized/universalRules.tw b/src/uncategorized/universalRules.tw
index d042ff535231b81f534657a0b0b928a6b7c4aa16..d07f4819b2d5cadcb4a1d9142ef8f90a28f9958c 100644
--- a/src/uncategorized/universalRules.tw
+++ b/src/uncategorized/universalRules.tw
@@ -516,7 +516,7 @@ Slave nicknames are
 ''Pregnant slaves''
 <br>
 <<if $universalRulesImpregnation == "HG">>
-	<<setLocalPronouns $HeadGirl>>
+	<<setLocalPronouns _S.HeadGirl>>
 	Fertile slaves will be ''systematically impregnated by your Head Girl,'' if $he is able to do so.
 	[[Cancel insemination regime|Universal Rules][($universalRulesImpregnation = "none"),($universalHGImpregnateMasterSuiteToggle = 0)]]
 	| [[Use a Stud for insemination|Universal Rules][($universalRulesImpregnation = "Stud")]]
diff --git a/src/utility/birthWidgets.tw b/src/utility/birthWidgets.tw
index d2e119dedf843a5aa16e21d350fb076c9114f5f0..ddd3a99f7f5172639120351544d0466c60207a69 100644
--- a/src/utility/birthWidgets.tw
+++ b/src/utility/birthWidgets.tw
@@ -144,7 +144,7 @@
 			<<set _roll = random(0, _animals.length)>>
 			$He shows no interest in $his coming birth as $he continues being rutted by a <<print _animals[_roll].species>>, so <<if _S.Farmer > 0>>_S.Farmer.slaveName<<else>>another slave<</if>> has to pull the animal off of $him and drag $him into the nearest birthing stall. $He also shows almost no reaction when $his child<<if $slaves[$i].pregType > 1>>ren<</if>> begin<<if $slaves[$i].pregType <= 1>>s<</if>> to come out, or when $he is subsequently hosed off before being led back to $farmyardName.
 		<<else>>
-			$He shows no interest in $his coming birth as $he continues <<print either("loading hay bales", "milking a cow", "pulling weeds")>>, until $his contractions become so strong that $he collapses to $his knees. <<if $Farmer > 0>>$Farmer.name<<else>>Another slave<</if>> is at $his side in an instant, pulling $him to $his feet and into a birthing stall. $He also shows almost no reaction when $his child<<if $slaves[$i].pregType > 1>>ren<</if>> begin<<if $slaves[$i].pregType <= 1>>s<</if>> to come out, or when $he is subsequently hosed off before being led back to $farmyardName.
+			$He shows no interest in $his coming birth as $he continues <<print either("loading hay bales", "milking a cow", "pulling weeds")>>, until $his contractions become so strong that $he collapses to $his knees. <<if $FarmerID > 0>>_S.Farmer.name<<else>>Another slave<</if>> is at $his side in an instant, pulling $him to $his feet and into a birthing stall. $He also shows almost no reaction when $his child<<if $slaves[$i].pregType > 1>>ren<</if>> begin<<if $slaves[$i].pregType <= 1>>s<</if>> to come out, or when $he is subsequently hosed off before being led back to $farmyardName.
 		<</if>>
 	<<else>>
 		<<if $farmyardShows && $seeBestiality>>
@@ -304,20 +304,20 @@
 	<</if>>
 
 <<case "live with your Head Girl">>
-	<<setLocalPronouns $HeadGirl 2>>
-	<<if $slaves[$i].pregSource == $HeadGirl.ID>>
-		$HeadGirl.slaveName makes sure that the mother of _his2 child<<if $slaves[$i].pregType > 1>>ren<</if>> is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process,<</if>> and $his child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> carefully collected by their father. Once they are out of the way, $HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired body.
+	<<setLocalPronouns _S.HeadGirl 2>>
+	<<if $slaves[$i].pregSource == $HeadGirlID>>
+		_S.HeadGirl.slaveName makes sure that the mother of _his2 child<<if $slaves[$i].pregType > 1>>ren<</if>> is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process,<</if>> and $his child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> carefully collected by their father. Once they are out of the way, _S.HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired body.
 	<<elseif !canWalk($slaves[$i])>>
 		<<if $slaves[$i].fetish == "mindbroken">>
-			$He is aided in finding $HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is taken back to $HeadGirl.slaveName's room.
+			$He is aided in finding _S.HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is taken back to _S.HeadGirl.slaveName's room.
 		<<else>>
-			$He is aided in seeking out $HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> and convulsing with orgasms in the process<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is helped back to $HeadGirl.slaveName's room.
+			$He is aided in seeking out _S.HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> and convulsing with orgasms in the process<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is helped back to _S.HeadGirl.slaveName's room.
 		<</if>>
 	<<else>>
 		<<if $slaves[$i].fetish == "mindbroken">>
-			$He wanders until $he finds $HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is lead back to $HeadGirl.slaveName's room.
+			$He wanders until $he finds _S.HeadGirl.slaveName, who undresses $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is lead back to _S.HeadGirl.slaveName's room.
 		<<else>>
-			$He seeks out $HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> and convulsing with orgasms in the process<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he returns to $HeadGirl.slaveName's room.
+			$He seeks out _S.HeadGirl.slaveName, who undresses $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, enjoying _his2 wandering hands and attention<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> and convulsing with orgasms in the process<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he returns to _S.HeadGirl.slaveName's room.
 		<</if>>
 	<</if>>
 
@@ -580,13 +580,13 @@
 	<</if>>
 
 <<case "live with your Head Girl">>
-	<<setLocalPronouns $HeadGirl 2>>
-	<<if $slaves[$i].pregSource == $HeadGirl.ID>>
-		$HeadGirl.slaveName makes sure that the mother of _his2 child<<if $slaves[$i].pregType > 1>>ren<</if>> is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process,<</if>> and $his child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> carefully collected by their father. Once they are out of the way, $HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired body.
+	<<setLocalPronouns _S.HeadGirl 2>>
+	<<if $slaves[$i].pregSource == $HeadGirlID>>
+		_S.HeadGirl.slaveName makes sure that the mother of _his2 child<<if $slaves[$i].pregType > 1>>ren<</if>> is happy and comfortable for the upcoming birth, even if they won't be spending much time with their offspring. _He2 carefully undresses $slaves[$i].slaveName, all the while whispering sweet nothings in $his ear. $He begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process,<</if>> and $his child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> carefully collected by their father. Once they are out of the way, _S.HeadGirl.slaveName moves in to fondle $slaves[$i].slaveName's tired body.
 	<<elseif $slaves[$i].fetish == "mindbroken">>
-		$He is placed with $HeadGirl.slaveName. _He2 unwraps $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is carried back to $HeadGirl.slaveName's room.
+		$He is placed with _S.HeadGirl.slaveName. _He2 unwraps $him as $he instinctively begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to _his2 wandering hands. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is carried back to _S.HeadGirl.slaveName's room.
 	<<else>>
-		$He is placed with $HeadGirl.slaveName. _He2 unwraps $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process and<</if>> enjoying _his2 wandering hands and attention. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is carried back to $HeadGirl.slaveName's room.
+		$He is placed with _S.HeadGirl.slaveName. _He2 unwraps $him as $he dutifully begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>,<<if $slaves[$i].geneticQuirks.uterineHypersensitivity == 2>> convulsing with orgasms in the process and<</if>> enjoying _his2 wandering hands and attention. $His child<<if $slaves[$i].pregType > 1>>ren are<<else>> is<</if>> promptly taken and, following a cleaning, a fresh change of clothes, and some private time with your Head Girl, $he is carried back to _S.HeadGirl.slaveName's room.
 	<</if>>
 
 <<case "be confined in the arcade">>
@@ -861,22 +861,22 @@
 		<<if _birthScene > 50>>
 			While giving a slave oral service, $slaves[$i].slaveName's water breaks. $He disregards this development and continues working.
 			<<ClothingBirth>>
-			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>>shouts at $him to move $his useless ass.
+			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. Instead, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif _S.HeadGirl>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>>shouts at $him to move $his useless ass.
 		<<else>>
 			While scrubbing the penthouse floor, $slaves[$i].slaveName's water breaks. $He turns to clean this new spill, disregarding what it means.
 			<<ClothingBirth>>
-			Instead of cleaning the fresh mess $he made, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>>shouts at $him to move $his useless ass.
+			Instead of cleaning the fresh mess $he made, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> until <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif _S.HeadGirl>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>>shouts at $him to move $his useless ass.
 		<</if>>
 	<<else>>
 		<<if _birthScene > 50>>
 			While giving a slave oral service, $slaves[$i].slaveName's water breaks. $He desperately tries to pull away but they grab $his head and force $him back to their crotch.
 			<<set $humiliation = 1>>
 			<<ClothingBirth>>
-			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. $He hurriedly tries to mop up the mess and collect $his child<<if $slaves[$i].pregType > 1>>ren<</if>> before <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif $HeadGirl != 0>>$HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him.
+			The slave gets off quite strongly to the show and shoves $him out of the way, leaving $him to clean up $his mess. $He hurriedly tries to mop up the mess and collect $his child<<if $slaves[$i].pregType > 1>>ren<</if>> before <<if _S.Stewardess>>_S.Stewardess.slaveName<<elseif _S.HeadGirl>>_S.HeadGirl.slaveName<<else>>$assistant.name<</if>> shouts at $him.
 		<<else>>
 			While scrubbing the penthouse floor, $slaves[$i].slaveName's water breaks. $He panics at the thought of not cleaning up $his spill but $his worsening contractions force $him to find a secluded place to give birth.
 			<<ClothingBirth>>
-			Collecting $his child<<if $slaves[$i].pregType > 1>>ren<</if>>, $he exits $his hiding place before coming under the eye of <<if _S.Stewardess>>the glaring _S.Stewardess.slaveName<<elseif $HeadGirl != 0>>the glaring $HeadGirl.slaveName<<else>>$assistant.name<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren<</if>> are promptly taken by other servants following a lecture about priorities and time management.
+			Collecting $his child<<if $slaves[$i].pregType > 1>>ren<</if>>, $he exits $his hiding place before coming under the eye of <<if _S.Stewardess>>the glaring _S.Stewardess.slaveName<<elseif $HeadGirlID != 0>>the glaring _S.HeadGirl.slaveName<<else>>$assistant.name<</if>>. $His child<<if $slaves[$i].pregType > 1>>ren<</if>> are promptly taken by other servants following a lecture about priorities and time management.
 		<</if>>
 	<</if>>
 
@@ -886,13 +886,13 @@
 			<<if $masterSuiteUpgradeLuxury == 1>>
 				While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's water breaks thoroughly soaking the sheets. Ignoring the mess, $he shifts into a more comfortable position.
 				<<ClothingBirth>>
-				Resting in your bed, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if $Concubine != 0>>$Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+				Resting in your bed, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 			<<elseif $masterSuiteUpgradeLuxury == 2>>
 				While at the bottom of the master suite's fuckpit, $slaves[$i].slaveName's water breaks. While $he doesn't stop having sex, the fucktoys using $him do and drag $him from the pit to give birth. Instinctively, $he begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to who may be watching $his naked crotch. Without a second thought about $his child<<if $slaves[$i].pregType > 1>>ren<</if>>, $he slips back into the fuckpit.
 			<<else>>
 				While awaiting your return in the master suite, $slaves[$i].slaveName's water breaks. $He pays it no heed and continues blankly waiting.
 				<<ClothingBirth>>
-				Resting on the floor, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if $Concubine != 0>>$Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed and $him thoroughly cleaned before your return.<</if>>
+				Resting on the floor, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed and $him thoroughly cleaned before your return.<</if>>
 			<</if>>
 		<<else>>
 			<<if $masterSuiteUpgradeLuxury == 1>>
@@ -915,13 +915,13 @@
 			<<if $masterSuiteUpgradeLuxury == 1>>
 				While awaiting your return on the big bed in the master suite, $slaves[$i].slaveName's water breaks thoroughly soaking the sheets. Ignoring the mess, $he shifts into a more comfortable position.
 				<<ClothingBirth>>
-				Resting in your bed, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
+				Resting in your bed, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him forcefully ejected from the bed so it can be cleaned before your return.<</if>>
 			<<elseif $masterSuiteUpgradeLuxury == 2>>
 				While being the bottom of the master suite's fuckpit, $slaves[$i].slaveName's water breaks. While $he doesn't stop having sex, the fucktoys using $him do and drag $him from the pit to give birth. Instinctively, $he begins to push out $his bab<<if $slaves[$i].pregType > 1>>ies<<else>>y<</if>>, indifferent to who may be watching $his naked crotch. Without a second thought about $his child<<if $slaves[$i].pregType > 1>>ren<</if>>, $he slips back into the fuckpit.
 			<<else>>
 				While awaiting your return in the master suite, $slaves[$i].slaveName's water breaks. $He pays it no heed and continues blankly waiting.
 				<<ClothingBirth>>
-				Resting on the floor, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if $Concubine != 0>>Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
+				Resting on the floor, $he draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes waiting for you. <<if _S.Concubine>>_S.Concubine.slaveName furiously orders $his child<<if $slaves[$i].pregType > 1>>ren<</if>> removed, and $him thoroughly cleaned before your return.<</if>>
 			<</if>>
 		<<else>>
 			<<if $masterSuiteUpgradeLuxury == 1>>
@@ -1169,26 +1169,26 @@
 	<</if>>
 
 <<case "live with your Head Girl">>
-	<<setLocalPronouns $HeadGirl 2>>
+	<<setLocalPronouns _S.HeadGirl 2>>
 	<<if !canWalk($slaves[$i])>>
 		<<if $slaves[$i].fetish == "mindbroken">>
 			$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He rolls over and begins to fall back to sleep as another wracks $his body.
 			<<ClothingBirth>>
-			$He draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+			$He draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 		<<else>>
 			$slaves[$i].slaveName is awoken from $his rest by a moist sensation followed by a contraction. $He rolls over and clutches $his gravid belly as another wracks $his body.
 			<<ClothingBirth>>
-			$He collects $his newborn<<if $slaves[$i].pregType > 1>>s<</if>> and places them in the cradle readied for $him. $He is helped to the shower as your servants clean up and remove $his child<<if $slaves[$i].pregType > 1>>ren<</if>>. Freshened up, $he returns to resting knowing full well that $HeadGirl.slaveName will be eager to play with $his body upon returning.
+			$He collects $his newborn<<if $slaves[$i].pregType > 1>>s<</if>> and places them in the cradle readied for $him. $He is helped to the shower as your servants clean up and remove $his child<<if $slaves[$i].pregType > 1>>ren<</if>>. Freshened up, $he returns to resting knowing full well that _S.HeadGirl.slaveName will be eager to play with $his body upon returning.
 		<</if>>
 	<<else>>
 		<<if $slaves[$i].fetish == "mindbroken">>
 			$slaves[$i].slaveName is awoken from $his rest by a strong contraction. $He rolls over and begins to fall back to sleep as another wracks $his body.
 			<<ClothingBirth>>
-			$He draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes resting before $HeadGirl.slaveName returns from _his2 duties.
+			$He draws $his child<<if $slaves[$i].pregType > 1>>ren<</if>> to $his breast<<if $slaves[$i].pregType > 1>>s<</if>> and resumes resting before _S.HeadGirl.slaveName returns from _his2 duties.
 		<<else>>
 			$slaves[$i].slaveName is awoken from $his rest by a moist sensation followed by a contraction. $He rolls over and clutches $his gravid belly as another wracks $his body.
 			<<ClothingBirth>>
-			$He collects $his newborn<<if $slaves[$i].pregType > 1>>s<</if>> and places them in the cradle readied for $him. $He leaves to take a shower as your servants clean up and remove $his child<<if $slaves[$i].pregType > 1>>ren<</if>>. Freshened up, $he returns to resting knowing full well that $HeadGirl.slaveName will be eager to play with $his body upon returning.
+			$He collects $his newborn<<if $slaves[$i].pregType > 1>>s<</if>> and places them in the cradle readied for $him. $He leaves to take a shower as your servants clean up and remove $his child<<if $slaves[$i].pregType > 1>>ren<</if>>. Freshened up, $he returns to resting knowing full well that _S.HeadGirl.slaveName will be eager to play with $his body upon returning.
 		<</if>>
 	<</if>>
 
diff --git a/src/utility/slaveCreationWidgets.tw b/src/utility/slaveCreationWidgets.tw
index 465a3259a160d412b940361e79ba30818ed7ee37..3d3556ed73b40dd046c282e0430e5a6a81c75e55 100644
--- a/src/utility/slaveCreationWidgets.tw
+++ b/src/utility/slaveCreationWidgets.tw
@@ -553,6 +553,8 @@
 	the Trainers' Market
 <<case "TSS">>
 	The Slavegirl School
+<<case "TUO">>
+	The Utopian Orphanage
 <<case "GRI">>
 	Growth Research Institute
 <<case "SCP">>
@@ -616,6 +618,8 @@
 	the Trainers' Market.
 <<case "TSS">>
 	The Slavegirl School.
+<<case "TUO">>
+	The Utopian Orphanage.
 <<case "GRI">>
 	Growth Research Institute.
 <<case "SCP">>
diff --git a/src/zz1-last/setupEventHandlers.js b/src/zz1-last/setupEventHandlers.js
index 593787eae840212bd4fdbfc1beb51a72738348e5..0a6ef4160227ad480de47f3b895c24cc870cd2ba 100644
--- a/src/zz1-last/setupEventHandlers.js
+++ b/src/zz1-last/setupEventHandlers.js
@@ -4,7 +4,6 @@ Config.saves.onSave = App.EventHandlers.onSave;
 
 $(document).on(':storyready', function() {
 	App.EventHandlers.storyReady();
-	App.UI.Hotkeys.init();
 });
 
 $(document).one(':passagestart', function() {
diff --git a/submodules/sugarcube-2 b/submodules/sugarcube-2
index 01b018f02691f013d2c568d87a00fad63d3805be..c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f 160000
--- a/submodules/sugarcube-2
+++ b/submodules/sugarcube-2
@@ -1 +1 @@
-Subproject commit 01b018f02691f013d2c568d87a00fad63d3805be
+Subproject commit c9351665e98ed6f74c6afa0bb5e181f5bd58ef0f