diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js
index 25d5589b0c11ade88829fc498109f522ea85349b..64880cd344d497b5d9be629d74705bb2dc583021 100644
--- a/src/gui/quicklinks.js
+++ b/src/gui/quicklinks.js
@@ -8,6 +8,26 @@ App.UI.quickMenu = (function() {
 	const jumpFrom = Story.lookup("tags", "jump-from-safe").map(passage => passage.title);
 	const jumpTo = Story.lookup("tags", "jump-to-safe").map(passage => passage.title);
 
+	// if property name is a passage name, then it's a link, otherwise only text.
+	// Only two values are allowed: true or an object following the same rules
+	const layout = {
+		Main: true,
+		Manage: {
+			"Manage Personal Affairs": true,
+			"Manage Arcology": true,
+			"Manage Penthouse": true,
+		},
+		Facilities: {
+			Cellblock: true,
+			Clinic: true,
+			"Head Girl Suite": true,
+			"Master Suite": true,
+			Schoolroom: true,
+			"Servants' Quarters": true,
+			Spa: true
+		}
+	};
+
 	// setup hotkeys list, upper/lower case is important!
 	const hotkeys = cleanHotkeys({
 		Main: "m",
@@ -68,6 +88,7 @@ App.UI.quickMenu = (function() {
 
 		const fragment = document.createDocumentFragment();
 
+		// go back link
 		if (history.length > 0) {
 			const div = document.createElement("div");
 			const a = document.createElement("a");
@@ -77,9 +98,24 @@ App.UI.quickMenu = (function() {
 			fragment.append(div);
 		}
 
-		for (let i = 0; i < jumpTo.length; i++) {
-			if (jumpTo[i] !== State.passage) {
-				fragment.append(generatePassageLink(jumpTo[i]));
+		// quick menu
+		// we need to modify jumpTo, but not the original
+		const jumpToCopy = jumpTo.slice();
+		fragment.append(generateQuickMenu(jumpToCopy, layout, 0));
+
+		// put uncategorized passages in others menu
+		if (jumpToCopy.length > 0) {
+			const div = document.createElement("div");
+			div.append("Other");
+			fragment.append("0 ", div);
+			for (let i = 0; i < jumpToCopy.length; i++) {
+				if (State.passage !== jumpToCopy[i]) {
+					fragment.append(generatePassageLink(jumpToCopy[i], 1));
+				} else {
+					const div = document.createElement("div");
+					div.append("1 ", jumpToCopy[i]);
+					fragment.append(div);
+				}
 			}
 		}
 
@@ -87,14 +123,43 @@ App.UI.quickMenu = (function() {
 		return fragment;
 	}
 
-	function generatePassageLink(passage) {
+	function generateQuickMenu(passages, group, level) {
+		const fragment = document.createDocumentFragment();
+		for (const passage in group) {
+			const included = jumpTo.includes(passage);
+			// remove used passage from list
+			if (included) {
+				const index = passages.indexOf(passage);
+				if (index > -1) {
+					passages.splice(index, 1);
+				}
+			}
+
+			// make link
+			if (included && State.passage !== passage) {
+				fragment.append(generatePassageLink(passage, level));
+			} else {
+				const div = document.createElement("div");
+				div.append(level, " ", passage);
+				fragment.append(div);
+			}
+
+			// lower levels
+			if (group[passage] !== true) {
+				fragment.append(generateQuickMenu(passages, group[passage], level + 1));
+			}
+		}
+		return fragment;
+	}
+
+	function generatePassageLink(passage, level) {
 		const div = document.createElement("div");
 		const a = document.createElement("a");
 		a.append(passage);
 		a.onclick = () => {
 			Engine.play(passage);
 		};
-		div.append(a);
+		div.append(level, " ", a);
 		if (hotkeys[passage]) {
 			div.append(" ", App.UI.DOM.makeElement("span", `[${hotkeys[passage]}]`, "hotkey"));
 		}