diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts
index 20abbe0b6..9ef0838e1 100644
--- a/src/Corporation/Actions.ts
+++ b/src/Corporation/Actions.ts
@@ -51,13 +51,15 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
 export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void {
   if (corporation.funds < CorporationConstants.OfficeInitialCost) {
     throw new Error("You don't have enough company funds to open a new office!");
-  } else {
-    corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost;
-    division.offices[city] = new OfficeSpace({
-      loc: city,
-      size: CorporationConstants.OfficeInitialSize,
-    });
   }
+  if (division.offices[city]) {
+    throw new Error(`You have already expanded into ${city} for ${division.name}`);
+  }
+  corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost;
+  division.offices[city] = new OfficeSpace({
+    loc: city,
+    size: CorporationConstants.OfficeInitialSize,
+  });
 }
 
 export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void {
@@ -345,10 +347,17 @@ export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city:
   corp.funds = corp.funds - CorporationConstants.WarehouseInitialCost;
 }
 
-export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse): void {
-  const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1);
+export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number {
+  return Array.from(Array(amt).keys()).reduce(
+    (acc, index) => acc + CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1 + index),
+    0,
+  );
+}
+
+export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse, amt = 1): void {
+  const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt);
   if (corp.funds < sizeUpgradeCost) return;
-  ++warehouse.level;
+  warehouse.level += amt;
   warehouse.updateSize(corp, division);
   corp.funds = corp.funds - sizeUpgradeCost;
 }
@@ -416,7 +425,7 @@ export function MakeProduct(
   }
 
   const product = new Product({
-    name: productName.replace(/[<>]/g, ""), //Sanitize for HTMl elements
+    name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements
     createCity: city,
     designCost: designInvest,
     advCost: marketingInvest,
@@ -488,12 +497,23 @@ export function CancelExportMaterial(divisionName: string, cityName: string, mat
 export function LimitProductProduction(product: Product, cityName: string, qty: number): void {
   if (qty < 0 || isNaN(qty)) {
     product.prdman[cityName][0] = false;
+    product.prdman[cityName][1] = 0;
   } else {
     product.prdman[cityName][0] = true;
     product.prdman[cityName][1] = qty;
   }
 }
 
+export function LimitMaterialProduction(material: Material, qty: number): void {
+  if (qty < 0 || isNaN(qty)) {
+    material.prdman[0] = false;
+    material.prdman[1] = 0;
+  } else {
+    material.prdman[0] = true;
+    material.prdman[1] = qty;
+  }
+}
+
 export function SetMaterialMarketTA1(material: Material, on: boolean): void {
   material.marketTa1 = on;
 }
diff --git a/src/Corporation/ui/IndustryOffice.tsx b/src/Corporation/ui/IndustryOffice.tsx
index af04c7591..10bf65af8 100644
--- a/src/Corporation/ui/IndustryOffice.tsx
+++ b/src/Corporation/ui/IndustryOffice.tsx
@@ -8,8 +8,8 @@ import { EmployeePositions } from "../EmployeePositions";
 
 import { numeralWrapper } from "../../ui/numeralFormat";
 
-import { UpgradeOfficeSizeModal } from "./UpgradeOfficeSizeModal";
-import { ThrowPartyModal } from "./ThrowPartyModal";
+import { UpgradeOfficeSizeModal } from "./modals/UpgradeOfficeSizeModal";
+import { ThrowPartyModal } from "./modals/ThrowPartyModal";
 import { Money } from "../../ui/React/Money";
 import { useCorporation, useDivision } from "./Context";
 
diff --git a/src/Corporation/ui/IndustryOverview.tsx b/src/Corporation/ui/IndustryOverview.tsx
index 97633225a..60390e11e 100644
--- a/src/Corporation/ui/IndustryOverview.tsx
+++ b/src/Corporation/ui/IndustryOverview.tsx
@@ -7,8 +7,8 @@ import { Industries } from "../IndustryData";
 import { IndustryUpgrades } from "../IndustryUpgrades";
 import { numeralWrapper } from "../../ui/numeralFormat";
 import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
-import { MakeProductModal } from "./MakeProductModal";
-import { ResearchModal } from "./ResearchModal";
+import { MakeProductModal } from "./modals/MakeProductModal";
+import { ResearchModal } from "./modals/ResearchModal";
 import { Money } from "../../ui/React/Money";
 import { MoneyRate } from "../../ui/React/MoneyRate";
 import { StatsTable } from "../../ui/React/StatsTable";
diff --git a/src/Corporation/ui/IndustryWarehouse.tsx b/src/Corporation/ui/IndustryWarehouse.tsx
index 84fd1d95b..7d40a24de 100644
--- a/src/Corporation/ui/IndustryWarehouse.tsx
+++ b/src/Corporation/ui/IndustryWarehouse.tsx
@@ -6,7 +6,7 @@ import { CorporationConstants } from "../data/Constants";
 import { Material } from "../Material";
 import { Product } from "../Product";
 import { Warehouse } from "../Warehouse";
-import { SmartSupplyModal } from "./SmartSupplyModal";
+import { SmartSupplyModal } from "./modals/SmartSupplyModal";
 import { ProductElem } from "./ProductElem";
 import { MaterialElem } from "./MaterialElem";
 import { MaterialSizes } from "../MaterialSizes";
diff --git a/src/Corporation/ui/MaterialElem.tsx b/src/Corporation/ui/MaterialElem.tsx
index 2de6769d5..fcc0ca6c0 100644
--- a/src/Corporation/ui/MaterialElem.tsx
+++ b/src/Corporation/ui/MaterialElem.tsx
@@ -5,10 +5,10 @@ import React, { useState } from "react";
 import { OfficeSpace } from "../OfficeSpace";
 import { Material } from "../Material";
 import { Warehouse } from "../Warehouse";
-import { ExportModal } from "./ExportModal";
-import { MaterialMarketTaModal } from "./MaterialMarketTaModal";
-import { SellMaterialModal } from "./SellMaterialModal";
-import { PurchaseMaterialModal } from "./PurchaseMaterialModal";
+import { ExportModal } from "./modals/ExportModal";
+import { MaterialMarketTaModal } from "./modals/MaterialMarketTaModal";
+import { SellMaterialModal } from "./modals/SellMaterialModal";
+import { PurchaseMaterialModal } from "./modals/PurchaseMaterialModal";
 
 import { numeralWrapper } from "../../ui/numeralFormat";
 
@@ -21,6 +21,7 @@ import Tooltip from "@mui/material/Tooltip";
 import Paper from "@mui/material/Paper";
 import Button from "@mui/material/Button";
 import Box from "@mui/material/Box";
+import { LimitMaterialProductionModal } from "./modals/LimitMaterialProductionModal";
 
 interface IMaterialProps {
   warehouse: Warehouse;
@@ -37,6 +38,8 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
   const [exportOpen, setExportOpen] = useState(false);
   const [sellMaterialOpen, setSellMaterialOpen] = useState(false);
   const [materialMarketTaOpen, setMaterialMarketTaOpen] = useState(false);
+  const [limitProductionOpen, setLimitProductionOpen] = useState(false);
+
   const warehouse = props.warehouse;
   const city = props.city;
   const mat = props.mat;
@@ -110,6 +113,12 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
     sellButtonText = <>Sell (0.000/0.000)</>;
   }
 
+  // Limit Production button
+  let limitMaterialButtonText = "Limit Material";
+  if (mat.prdman[0]) {
+    limitMaterialButtonText += " (" + numeralWrapper.format(mat.prdman[1], nf) + ")";
+  }
+
   return (
     <Paper>
       <Box sx={{ display: "grid", gridTemplateColumns: "2fr 1fr", m: "5px" }}>
@@ -194,6 +203,14 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
               />
             </>
           )}
+          <Button color={tutorial ? "error" : "primary"} onClick={() => setLimitProductionOpen(true)}>
+            {limitMaterialButtonText}
+          </Button>
+          <LimitMaterialProductionModal
+            material={mat}
+            open={limitProductionOpen}
+            onClose={() => setLimitProductionOpen(false)}
+          />
         </Box>
       </Box>
     </Paper>
diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx
index 469dcdc83..3d13c05b2 100644
--- a/src/Corporation/ui/Overview.tsx
+++ b/src/Corporation/ui/Overview.tsx
@@ -2,13 +2,13 @@
 import React, { useState } from "react";
 import { LevelableUpgrade } from "./LevelableUpgrade";
 import { UnlockUpgrade } from "./UnlockUpgrade";
-import { BribeFactionModal } from "./BribeFactionModal";
-import { SellSharesModal } from "./SellSharesModal";
-import { BuybackSharesModal } from "./BuybackSharesModal";
-import { IssueDividendsModal } from "./IssueDividendsModal";
-import { IssueNewSharesModal } from "./IssueNewSharesModal";
-import { FindInvestorsModal } from "./FindInvestorsModal";
-import { GoPublicModal } from "./GoPublicModal";
+import { BribeFactionModal } from "./modals/BribeFactionModal";
+import { SellSharesModal } from "./modals/SellSharesModal";
+import { BuybackSharesModal } from "./modals/BuybackSharesModal";
+import { IssueDividendsModal } from "./modals/IssueDividendsModal";
+import { IssueNewSharesModal } from "./modals/IssueNewSharesModal";
+import { FindInvestorsModal } from "./modals/FindInvestorsModal";
+import { GoPublicModal } from "./modals/GoPublicModal";
 import { Factions } from "../../Faction/Factions";
 
 import { CorporationConstants } from "../data/Constants";
diff --git a/src/Corporation/ui/ProductElem.tsx b/src/Corporation/ui/ProductElem.tsx
index f8240b92a..1481a9c55 100644
--- a/src/Corporation/ui/ProductElem.tsx
+++ b/src/Corporation/ui/ProductElem.tsx
@@ -2,10 +2,11 @@ import React, { useState } from "react";
 
 import { CorporationConstants } from "../data/Constants";
 import { Product } from "../Product";
-import { DiscontinueProductModal } from "./DiscontinueProductModal";
-import { LimitProductProductionModal } from "./LimitProductProductionModal";
-import { SellProductModal } from "./SellProductModal";
-import { ProductMarketTaModal } from "./ProductMarketTaModal";
+import { DiscontinueProductModal } from "./modals/DiscontinueProductModal";
+import { LimitProductProductionModal } from "./modals/LimitProductProductionModal";
+import { SellProductModal } from "./modals/SellProductModal";
+import { ProductMarketTaModal } from "./modals/ProductMarketTaModal";
+import { CancelProductModal } from "./modals/CancelProductModal";
 
 import { numeralWrapper } from "../../ui/numeralFormat";
 
@@ -32,6 +33,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
   const [sellOpen, setSellOpen] = useState(false);
   const [limitOpen, setLimitOpen] = useState(false);
   const [discontinueOpen, setDiscontinueOpen] = useState(false);
+  const [cancelOpen, setCancelOpen] = useState(false);
   const [marketTaOpen, setMarketTaOpen] = useState(false);
   const city = props.city;
   const product = props.product;
@@ -111,6 +113,13 @@ export function ProductElem(props: IProductProps): React.ReactElement {
           </Typography>
           <br />
           <Typography>{numeralWrapper.format(product.prog, "0.00")}% complete</Typography>
+          <Button onClick={() => setCancelOpen(true)}>Cancel</Button>
+          <CancelProductModal
+            product={product}
+            rerender={props.rerender}
+            open={cancelOpen}
+            onClose={() => setCancelOpen(false)}
+          />
         </>
       ) : (
         <>
@@ -171,6 +180,13 @@ export function ProductElem(props: IProductProps): React.ReactElement {
               <Typography>Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}</Typography>
             </Tooltip>
           </Box>
+          <Button onClick={() => setDiscontinueOpen(true)}>Discontinue</Button>
+          <DiscontinueProductModal
+            product={product}
+            rerender={props.rerender}
+            open={discontinueOpen}
+            onClose={() => setDiscontinueOpen(false)}
+          />
         </>
       )}
 
@@ -186,14 +202,6 @@ export function ProductElem(props: IProductProps): React.ReactElement {
             open={limitOpen}
             onClose={() => setLimitOpen(false)}
           />
-          <Button onClick={() => setDiscontinueOpen(true)}>Discontinue</Button>
-
-          <DiscontinueProductModal
-            product={product}
-            rerender={props.rerender}
-            open={discontinueOpen}
-            onClose={() => setDiscontinueOpen(false)}
-          />
           {division.hasResearch("Market-TA.I") && (
             <>
               <Button onClick={() => setMarketTaOpen(true)}>Market-TA</Button>
diff --git a/src/Corporation/ui/BribeFactionModal.tsx b/src/Corporation/ui/modals/BribeFactionModal.tsx
similarity index 91%
rename from src/Corporation/ui/BribeFactionModal.tsx
rename to src/Corporation/ui/modals/BribeFactionModal.tsx
index dc678a4d8..a690d74bc 100644
--- a/src/Corporation/ui/BribeFactionModal.tsx
+++ b/src/Corporation/ui/modals/BribeFactionModal.tsx
@@ -1,11 +1,11 @@
 import React, { useState } from "react";
-import { Factions } from "../../Faction/Factions";
-import { CorporationConstants } from "../data/Constants";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { use } from "../../ui/Context";
-import { useCorporation } from "./Context";
+import { Factions } from "../../../Faction/Factions";
+import { CorporationConstants } from "../../data/Constants";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { use } from "../../../ui/Context";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import MenuItem from "@mui/material/MenuItem";
diff --git a/src/Corporation/ui/BuybackSharesModal.tsx b/src/Corporation/ui/modals/BuybackSharesModal.tsx
similarity index 89%
rename from src/Corporation/ui/BuybackSharesModal.tsx
rename to src/Corporation/ui/modals/BuybackSharesModal.tsx
index b55e860e5..ae67656c4 100644
--- a/src/Corporation/ui/BuybackSharesModal.tsx
+++ b/src/Corporation/ui/modals/BuybackSharesModal.tsx
@@ -1,14 +1,14 @@
 import React, { useState } from "react";
-import { Modal } from "../../ui/React/Modal";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { use } from "../../ui/Context";
-import { useCorporation } from "./Context";
+import { Modal } from "../../../ui/React/Modal";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { use } from "../../../ui/Context";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
-import { BuyBackShares } from "../Actions";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { BuyBackShares } from "../../Actions";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IProps {
   open: boolean;
diff --git a/src/Corporation/ui/modals/CancelProductModal.tsx b/src/Corporation/ui/modals/CancelProductModal.tsx
new file mode 100644
index 000000000..58e6d18b3
--- /dev/null
+++ b/src/Corporation/ui/modals/CancelProductModal.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+
+import { Product } from "../../Product";
+import { Modal } from "../../../ui/React/Modal";
+import { useDivision } from "../Context";
+import Typography from "@mui/material/Typography";
+import Button from "@mui/material/Button";
+
+interface IProps {
+  open: boolean;
+  onClose: () => void;
+  product: Product;
+  rerender: () => void;
+}
+
+// Create a popup that lets the player cancel a product
+export function CancelProductModal(props: IProps): React.ReactElement {
+  const division = useDivision();
+  function cancel(): void {
+    division.discontinueProduct(props.product);
+    props.onClose();
+    props.rerender();
+  }
+
+  return (
+    <Modal open={props.open} onClose={props.onClose}>
+      <Typography>
+        Are you sure you want to do this? Canceling a product removes it completely and permanently. You will receive no
+        money back by doing so
+      </Typography>
+      <Button onClick={cancel}>Cancel</Button>
+    </Modal>
+  );
+}
diff --git a/src/Corporation/ui/CreateCorporationModal.tsx b/src/Corporation/ui/modals/CreateCorporationModal.tsx
similarity index 93%
rename from src/Corporation/ui/CreateCorporationModal.tsx
rename to src/Corporation/ui/modals/CreateCorporationModal.tsx
index 5d8e5e302..2c63baf4e 100644
--- a/src/Corporation/ui/CreateCorporationModal.tsx
+++ b/src/Corporation/ui/modals/CreateCorporationModal.tsx
@@ -1,8 +1,8 @@
 import React, { useState } from "react";
 
-import { Money } from "../../ui/React/Money";
-import { Modal } from "../../ui/React/Modal";
-import { use } from "../../ui/Context";
+import { Money } from "../../../ui/React/Money";
+import { Modal } from "../../../ui/React/Modal";
+import { use } from "../../../ui/Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
diff --git a/src/Corporation/ui/DiscontinueProductModal.tsx b/src/Corporation/ui/modals/DiscontinueProductModal.tsx
similarity index 87%
rename from src/Corporation/ui/DiscontinueProductModal.tsx
rename to src/Corporation/ui/modals/DiscontinueProductModal.tsx
index 00b1b614f..4992879ad 100644
--- a/src/Corporation/ui/DiscontinueProductModal.tsx
+++ b/src/Corporation/ui/modals/DiscontinueProductModal.tsx
@@ -1,8 +1,8 @@
 import React from "react";
 
-import { Product } from "../Product";
-import { Modal } from "../../ui/React/Modal";
-import { useDivision } from "./Context";
+import { Product } from "../../Product";
+import { Modal } from "../../../ui/React/Modal";
+import { useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 
diff --git a/src/Corporation/ui/ExportModal.tsx b/src/Corporation/ui/modals/ExportModal.tsx
similarity index 92%
rename from src/Corporation/ui/ExportModal.tsx
rename to src/Corporation/ui/modals/ExportModal.tsx
index 08d5ea0e3..947e932de 100644
--- a/src/Corporation/ui/ExportModal.tsx
+++ b/src/Corporation/ui/modals/ExportModal.tsx
@@ -1,12 +1,12 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Material } from "../Material";
-import { Export } from "../Export";
-import { IIndustry } from "../IIndustry";
-import { ExportMaterial } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
-import { useCorporation } from "./Context";
-import { isRelevantMaterial } from "./Helpers";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Material } from "../../Material";
+import { Export } from "../../Export";
+import { IIndustry } from "../../IIndustry";
+import { ExportMaterial } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
+import { useCorporation } from "../Context";
+import { isRelevantMaterial } from "../Helpers";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
diff --git a/src/Corporation/ui/FindInvestorsModal.tsx b/src/Corporation/ui/modals/FindInvestorsModal.tsx
similarity index 89%
rename from src/Corporation/ui/FindInvestorsModal.tsx
rename to src/Corporation/ui/modals/FindInvestorsModal.tsx
index 9f2136b2f..a6ee81ca6 100644
--- a/src/Corporation/ui/FindInvestorsModal.tsx
+++ b/src/Corporation/ui/modals/FindInvestorsModal.tsx
@@ -1,8 +1,8 @@
 import React from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { CorporationConstants } from "../data/Constants";
-import { Modal } from "../../ui/React/Modal";
-import { useCorporation } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { CorporationConstants } from "../../data/Constants";
+import { Modal } from "../../../ui/React/Modal";
+import { useCorporation } from "../Context";
 
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
diff --git a/src/Corporation/ui/GoPublicModal.tsx b/src/Corporation/ui/modals/GoPublicModal.tsx
similarity index 90%
rename from src/Corporation/ui/GoPublicModal.tsx
rename to src/Corporation/ui/modals/GoPublicModal.tsx
index 6c91e1513..6f24ce3ac 100644
--- a/src/Corporation/ui/GoPublicModal.tsx
+++ b/src/Corporation/ui/modals/GoPublicModal.tsx
@@ -1,13 +1,13 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { useCorporation } from "./Context";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
 import Box from "@mui/material/Box";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IProps {
   open: boolean;
diff --git a/src/Corporation/ui/IssueDividendsModal.tsx b/src/Corporation/ui/modals/IssueDividendsModal.tsx
similarity index 89%
rename from src/Corporation/ui/IssueDividendsModal.tsx
rename to src/Corporation/ui/modals/IssueDividendsModal.tsx
index 351776dca..4531faedf 100644
--- a/src/Corporation/ui/IssueDividendsModal.tsx
+++ b/src/Corporation/ui/modals/IssueDividendsModal.tsx
@@ -1,13 +1,13 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { CorporationConstants } from "../data/Constants";
-import { IssueDividends } from "../Actions";
-import { useCorporation } from "./Context";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { CorporationConstants } from "../../data/Constants";
+import { IssueDividends } from "../../Actions";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 interface IProps {
   open: boolean;
   onClose: () => void;
diff --git a/src/Corporation/ui/IssueNewSharesModal.tsx b/src/Corporation/ui/modals/IssueNewSharesModal.tsx
similarity index 92%
rename from src/Corporation/ui/IssueNewSharesModal.tsx
rename to src/Corporation/ui/modals/IssueNewSharesModal.tsx
index 0022828e9..570bf86e5 100644
--- a/src/Corporation/ui/IssueNewSharesModal.tsx
+++ b/src/Corporation/ui/modals/IssueNewSharesModal.tsx
@@ -1,14 +1,14 @@
 import React, { useState } from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { getRandomInt } from "../../utils/helpers/getRandomInt";
-import { CorporationConstants } from "../data/Constants";
-import { useCorporation } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { getRandomInt } from "../../../utils/helpers/getRandomInt";
+import { CorporationConstants } from "../../data/Constants";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IEffectTextProps {
   shares: number | null;
diff --git a/src/Corporation/ui/modals/LimitMaterialProductionModal.tsx b/src/Corporation/ui/modals/LimitMaterialProductionModal.tsx
new file mode 100644
index 000000000..9d9ab6563
--- /dev/null
+++ b/src/Corporation/ui/modals/LimitMaterialProductionModal.tsx
@@ -0,0 +1,53 @@
+import React, { useEffect, useState } from "react";
+import { LimitMaterialProduction } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
+import Typography from "@mui/material/Typography";
+import Button from "@mui/material/Button";
+import TextField from "@mui/material/TextField";
+import { KEY } from "../../../utils/helpers/keyCodes";
+import { Material } from "../../Material";
+
+interface IProps {
+  open: boolean;
+  onClose: () => void;
+  material: Material;
+}
+
+// Create a popup that lets the player limit the production of a product
+export function LimitMaterialProductionModal(props: IProps): React.ReactElement {
+  const [limit, setLimit] = useState<number | null>(null);
+
+  // reset modal internal state on modal close
+  useEffect(() => {
+    if (!props.open) {
+      setLimit(null);
+    }
+  }, [props.open]);
+
+  function limitMaterialProduction(): void {
+    let qty = limit;
+    if (qty === null) qty = -1;
+    LimitMaterialProduction(props.material, qty);
+    props.onClose();
+  }
+
+  function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
+    if (event.key === KEY.ENTER) limitMaterialProduction();
+  }
+
+  function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
+    if (event.target.value === "") setLimit(null);
+    else setLimit(parseFloat(event.target.value));
+  }
+
+  return (
+    <Modal open={props.open} onClose={props.onClose}>
+      <Typography>
+        Enter a limit to the amount of this material you would like to produce per second. Leave the box empty to set no
+        limit.
+      </Typography>
+      <TextField autoFocus={true} placeholder="Limit" type="number" onChange={onChange} onKeyDown={onKeyDown} />
+      <Button onClick={limitMaterialProduction}>Limit production</Button>
+    </Modal>
+  );
+}
diff --git a/src/Corporation/ui/LimitProductProductionModal.tsx b/src/Corporation/ui/modals/LimitProductProductionModal.tsx
similarity index 78%
rename from src/Corporation/ui/LimitProductProductionModal.tsx
rename to src/Corporation/ui/modals/LimitProductProductionModal.tsx
index 2d28ea75f..db9c55c74 100644
--- a/src/Corporation/ui/LimitProductProductionModal.tsx
+++ b/src/Corporation/ui/modals/LimitProductProductionModal.tsx
@@ -1,11 +1,11 @@
-import React, { useState } from "react";
-import { Product } from "../Product";
-import { LimitProductProduction } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
+import React, { useEffect, useState } from "react";
+import { Product } from "../../Product";
+import { LimitProductProduction } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IProps {
   open: boolean;
@@ -18,6 +18,13 @@ interface IProps {
 export function LimitProductProductionModal(props: IProps): React.ReactElement {
   const [limit, setLimit] = useState<number | null>(null);
 
+  // reset modal internal state on modal close
+  useEffect(() => {
+    if (!props.open) {
+      setLimit(null);
+    }
+  }, [props.open]);
+
   function limitProductProduction(): void {
     let qty = limit;
     if (qty === null) qty = -1;
diff --git a/src/Corporation/ui/MakeProductModal.tsx b/src/Corporation/ui/modals/MakeProductModal.tsx
similarity index 94%
rename from src/Corporation/ui/MakeProductModal.tsx
rename to src/Corporation/ui/modals/MakeProductModal.tsx
index 784e3acda..11e36375b 100644
--- a/src/Corporation/ui/MakeProductModal.tsx
+++ b/src/Corporation/ui/modals/MakeProductModal.tsx
@@ -1,15 +1,15 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { Industries } from "../IndustryData";
-import { MakeProduct } from "../Actions";
-import { useCorporation, useDivision } from "./Context";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { Industries } from "../../IndustryData";
+import { MakeProduct } from "../../Actions";
+import { useCorporation, useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
 import MenuItem from "@mui/material/MenuItem";
 import Select, { SelectChangeEvent } from "@mui/material/Select";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IProps {
   open: boolean;
diff --git a/src/Corporation/ui/MaterialMarketTaModal.tsx b/src/Corporation/ui/modals/MaterialMarketTaModal.tsx
similarity index 95%
rename from src/Corporation/ui/MaterialMarketTaModal.tsx
rename to src/Corporation/ui/modals/MaterialMarketTaModal.tsx
index 772a702f2..c78710f37 100644
--- a/src/Corporation/ui/MaterialMarketTaModal.tsx
+++ b/src/Corporation/ui/modals/MaterialMarketTaModal.tsx
@@ -1,8 +1,8 @@
 import React, { useState } from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { Material } from "../Material";
-import { Modal } from "../../ui/React/Modal";
-import { useDivision } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { Material } from "../../Material";
+import { Modal } from "../../../ui/React/Modal";
+import { useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import FormControlLabel from "@mui/material/FormControlLabel";
diff --git a/src/Corporation/ui/ProductMarketTaModal.tsx b/src/Corporation/ui/modals/ProductMarketTaModal.tsx
similarity index 95%
rename from src/Corporation/ui/ProductMarketTaModal.tsx
rename to src/Corporation/ui/modals/ProductMarketTaModal.tsx
index a7ea46558..436e4b88b 100644
--- a/src/Corporation/ui/ProductMarketTaModal.tsx
+++ b/src/Corporation/ui/modals/ProductMarketTaModal.tsx
@@ -1,8 +1,8 @@
 import React, { useState } from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { Product } from "../Product";
-import { Modal } from "../../ui/React/Modal";
-import { useDivision } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { Product } from "../../Product";
+import { Modal } from "../../../ui/React/Modal";
+import { useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import FormControlLabel from "@mui/material/FormControlLabel";
diff --git a/src/Corporation/ui/PurchaseMaterialModal.tsx b/src/Corporation/ui/modals/PurchaseMaterialModal.tsx
similarity index 65%
rename from src/Corporation/ui/PurchaseMaterialModal.tsx
rename to src/Corporation/ui/modals/PurchaseMaterialModal.tsx
index 419176e6d..2cd9dd1ea 100644
--- a/src/Corporation/ui/PurchaseMaterialModal.tsx
+++ b/src/Corporation/ui/modals/PurchaseMaterialModal.tsx
@@ -1,16 +1,16 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { MaterialSizes } from "../MaterialSizes";
-import { Warehouse } from "../Warehouse";
-import { Material } from "../Material";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { BulkPurchase, BuyMaterial } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
-import { useCorporation, useDivision } from "./Context";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { MaterialSizes } from "../../MaterialSizes";
+import { Warehouse } from "../../Warehouse";
+import { Material } from "../../Material";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { BulkPurchase, BuyMaterial } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
+import { useCorporation, useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IBulkPurchaseTextProps {
   warehouse: Warehouse;
@@ -18,37 +18,6 @@ interface IBulkPurchaseTextProps {
   amount: string;
 }
 
-function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
-  const parsedAmt = parseFloat(props.amount);
-  const cost = parsedAmt * props.mat.bCost;
-
-  const matSize = MaterialSizes[props.mat.name];
-  const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize;
-
-  if (parsedAmt * matSize > maxAmount) {
-    return (
-      <>
-        <Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography>
-      </>
-    );
-  } else if (isNaN(cost) || parsedAmt < 0) {
-    return (
-      <>
-        <Typography color={"error"}>Invalid put for Bulk Purchase amount</Typography>
-      </>
-    );
-  } else {
-    return (
-      <>
-        <Typography>
-          Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
-          {numeralWrapper.formatMoney(cost)}
-        </Typography>
-      </>
-    );
-  }
-}
-
 interface IBPProps {
   onClose: () => void;
   mat: Material;
@@ -58,6 +27,41 @@ interface IBPProps {
 function BulkPurchaseSection(props: IBPProps): React.ReactElement {
   const corp = useCorporation();
   const [buyAmt, setBuyAmt] = useState("");
+  const [disabled, setDisabled] = useState(false);
+
+  function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
+    const parsedAmt = parseFloat(props.amount);
+    const cost = parsedAmt * props.mat.bCost;
+
+    const matSize = MaterialSizes[props.mat.name];
+    const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize;
+
+    if (parsedAmt > maxAmount) {
+      setDisabled(true);
+      return (
+        <>
+          <Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography>
+        </>
+      );
+    } else if (isNaN(cost) || parsedAmt < 0) {
+      setDisabled(true);
+      return (
+        <>
+          <Typography color={"error"}>Invalid input for Bulk Purchase amount</Typography>
+        </>
+      );
+    } else {
+      setDisabled(false);
+      return (
+        <>
+          <Typography>
+            Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
+            {numeralWrapper.formatMoney(cost)}
+          </Typography>
+        </>
+      );
+    }
+  }
 
   function bulkPurchase(): void {
     try {
@@ -90,7 +94,9 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
         placeholder="Bulk Purchase amount"
         onKeyDown={onKeyDown}
       />
-      <Button onClick={bulkPurchase}>Confirm Bulk Purchase</Button>
+      <Button disabled={disabled} onClick={bulkPurchase}>
+        Confirm Bulk Purchase
+      </Button>
     </>
   );
 }
diff --git a/src/Corporation/ui/ResearchModal.tsx b/src/Corporation/ui/modals/ResearchModal.tsx
similarity index 90%
rename from src/Corporation/ui/ResearchModal.tsx
rename to src/Corporation/ui/modals/ResearchModal.tsx
index b48366872..40570aba5 100644
--- a/src/Corporation/ui/ResearchModal.tsx
+++ b/src/Corporation/ui/modals/ResearchModal.tsx
@@ -1,13 +1,13 @@
 import React, { useState } from "react";
-import { Modal } from "../../ui/React/Modal";
-import { IndustryResearchTrees } from "../IndustryData";
-import { CorporationConstants } from "../data/Constants";
-import { IIndustry } from "../IIndustry";
-import { Research } from "../Actions";
-import { Node } from "../ResearchTree";
-import { ResearchMap } from "../ResearchMap";
-import { Settings } from "../../Settings/Settings";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { IndustryResearchTrees } from "../../IndustryData";
+import { CorporationConstants } from "../../data/Constants";
+import { IIndustry } from "../../IIndustry";
+import { Research } from "../../Actions";
+import { Node } from "../../ResearchTree";
+import { ResearchMap } from "../../ResearchMap";
+import { Settings } from "../../../Settings/Settings";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
 import Typography from "@mui/material/Typography";
 import Tooltip from "@mui/material/Tooltip";
 import Button from "@mui/material/Button";
diff --git a/src/Corporation/ui/SellMaterialModal.tsx b/src/Corporation/ui/modals/SellMaterialModal.tsx
similarity index 91%
rename from src/Corporation/ui/SellMaterialModal.tsx
rename to src/Corporation/ui/modals/SellMaterialModal.tsx
index 83a36d314..023c1f8c6 100644
--- a/src/Corporation/ui/SellMaterialModal.tsx
+++ b/src/Corporation/ui/modals/SellMaterialModal.tsx
@@ -1,12 +1,12 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Material } from "../Material";
-import { SellMaterial } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Material } from "../../Material";
+import { SellMaterial } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 function initialPrice(mat: Material): string {
   let val = mat.sCost ? mat.sCost + "" : "";
diff --git a/src/Corporation/ui/SellProductModal.tsx b/src/Corporation/ui/modals/SellProductModal.tsx
similarity index 93%
rename from src/Corporation/ui/SellProductModal.tsx
rename to src/Corporation/ui/modals/SellProductModal.tsx
index 00026a5b0..d01009045 100644
--- a/src/Corporation/ui/SellProductModal.tsx
+++ b/src/Corporation/ui/modals/SellProductModal.tsx
@@ -1,15 +1,15 @@
 import React, { useState } from "react";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Product } from "../Product";
-import { SellProduct } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Product } from "../../Product";
+import { SellProduct } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
 
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
 import FormControlLabel from "@mui/material/FormControlLabel";
 import Switch from "@mui/material/Switch";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 function initialPrice(product: Product): string {
   let val = product.sCost ? product.sCost + "" : "";
diff --git a/src/Corporation/ui/SellSharesModal.tsx b/src/Corporation/ui/modals/SellSharesModal.tsx
similarity index 78%
rename from src/Corporation/ui/SellSharesModal.tsx
rename to src/Corporation/ui/modals/SellSharesModal.tsx
index fc6fc8aff..688fb119b 100644
--- a/src/Corporation/ui/SellSharesModal.tsx
+++ b/src/Corporation/ui/modals/SellSharesModal.tsx
@@ -1,16 +1,16 @@
 import React, { useState } from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { use } from "../../ui/Context";
-import { useCorporation } from "./Context";
-import { ICorporation } from "../ICorporation";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { use } from "../../../ui/Context";
+import { useCorporation } from "../Context";
+import { ICorporation } from "../../ICorporation";
 import Typography from "@mui/material/Typography";
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
-import { Money } from "../../ui/React/Money";
-import { SellShares } from "../Actions";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { Money } from "../../../ui/React/Money";
+import { SellShares } from "../../Actions";
+import { KEY } from "../../../utils/helpers/keyCodes";
 interface IProps {
   open: boolean;
   onClose: () => void;
@@ -33,19 +33,22 @@ export function SellSharesModal(props: IProps): React.ReactElement {
 
   function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement {
     if (props.shares === null) return <></>;
+    let text = "";
     if (isNaN(props.shares) || props.shares <= 0) {
-      return <>ERROR: Invalid value entered for number of shares to sell</>;
+      text = `ERROR: Invalid value entered for number of shares to sell`;
     } else if (props.shares > corp.numShares) {
-      return <>You don't have this many shares to sell!</>;
+      text = `You don't have this many shares to sell!`;
     } else {
       const stockSaleResults = corp.calculateShareSale(props.shares);
       const profit = stockSaleResults[0];
-      return (
-        <>
-          Sell {props.shares} shares for a total of {numeralWrapper.formatMoney(profit)}
-        </>
-      );
+      text = `Sell ${props.shares} shares for a total of ${numeralWrapper.formatMoney(profit)}`;
     }
+
+    return (
+      <Typography>
+        <small>{text}</small>
+      </Typography>
+    );
   }
 
   function sell(): void {
@@ -84,7 +87,6 @@ export function SellSharesModal(props: IProps): React.ReactElement {
         <br />
         The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)}
       </Typography>
-      <ProfitIndicator shares={shares} corp={corp} />
       <br />
       <TextField
         variant="standard"
@@ -97,6 +99,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
       <Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}>
         Sell shares
       </Button>
+      <ProfitIndicator shares={shares} corp={corp} />
     </Modal>
   );
 }
diff --git a/src/Corporation/ui/SmartSupplyModal.tsx b/src/Corporation/ui/modals/SmartSupplyModal.tsx
similarity index 88%
rename from src/Corporation/ui/SmartSupplyModal.tsx
rename to src/Corporation/ui/modals/SmartSupplyModal.tsx
index c76208693..a4fd540d6 100644
--- a/src/Corporation/ui/SmartSupplyModal.tsx
+++ b/src/Corporation/ui/modals/SmartSupplyModal.tsx
@@ -1,11 +1,11 @@
 import React, { useState } from "react";
 
-import { Warehouse } from "../Warehouse";
-import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../Actions";
-import { Material } from "../Material";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { Modal } from "../../ui/React/Modal";
-import { useDivision } from "./Context";
+import { Warehouse } from "../../Warehouse";
+import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions";
+import { Material } from "../../Material";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { Modal } from "../../../ui/React/Modal";
+import { useDivision } from "../Context";
 import Typography from "@mui/material/Typography";
 import FormControlLabel from "@mui/material/FormControlLabel";
 import Switch from "@mui/material/Switch";
diff --git a/src/Corporation/ui/ThrowPartyModal.tsx b/src/Corporation/ui/modals/ThrowPartyModal.tsx
similarity index 84%
rename from src/Corporation/ui/ThrowPartyModal.tsx
rename to src/Corporation/ui/modals/ThrowPartyModal.tsx
index 94ce03eb9..1df61aac0 100644
--- a/src/Corporation/ui/ThrowPartyModal.tsx
+++ b/src/Corporation/ui/modals/ThrowPartyModal.tsx
@@ -1,16 +1,16 @@
 import React, { useState } from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { dialogBoxCreate } from "../../ui/React/DialogBox";
-import { OfficeSpace } from "../OfficeSpace";
-import { ThrowParty } from "../Actions";
-import { Money } from "../../ui/React/Money";
-import { Modal } from "../../ui/React/Modal";
-import { useCorporation } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { OfficeSpace } from "../../OfficeSpace";
+import { ThrowParty } from "../../Actions";
+import { Money } from "../../../ui/React/Money";
+import { Modal } from "../../../ui/React/Modal";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
 import Box from "@mui/material/Box";
-import { KEY } from "../../utils/helpers/keyCodes";
+import { KEY } from "../../../utils/helpers/keyCodes";
 
 interface IProps {
   open: boolean;
diff --git a/src/Corporation/ui/UpgradeOfficeSizeModal.tsx b/src/Corporation/ui/modals/UpgradeOfficeSizeModal.tsx
similarity index 89%
rename from src/Corporation/ui/UpgradeOfficeSizeModal.tsx
rename to src/Corporation/ui/modals/UpgradeOfficeSizeModal.tsx
index 41c13c54d..e23831324 100644
--- a/src/Corporation/ui/UpgradeOfficeSizeModal.tsx
+++ b/src/Corporation/ui/modals/UpgradeOfficeSizeModal.tsx
@@ -1,11 +1,11 @@
 import React from "react";
-import { numeralWrapper } from "../../ui/numeralFormat";
-import { CorporationConstants } from "../data/Constants";
-import { OfficeSpace } from "../OfficeSpace";
-import { ICorporation } from "../ICorporation";
-import { UpgradeOfficeSize } from "../Actions";
-import { Modal } from "../../ui/React/Modal";
-import { useCorporation } from "./Context";
+import { numeralWrapper } from "../../../ui/numeralFormat";
+import { CorporationConstants } from "../../data/Constants";
+import { OfficeSpace } from "../../OfficeSpace";
+import { ICorporation } from "../../ICorporation";
+import { UpgradeOfficeSize } from "../../Actions";
+import { Modal } from "../../../ui/React/Modal";
+import { useCorporation } from "../Context";
 import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 import Tooltip from "@mui/material/Tooltip";
diff --git a/src/CotMG/ui/DummyGrid.tsx b/src/CotMG/ui/DummyGrid.tsx
index a31646cc6..80842e9aa 100644
--- a/src/CotMG/ui/DummyGrid.tsx
+++ b/src/CotMG/ui/DummyGrid.tsx
@@ -17,7 +17,7 @@ export function DummyGrid(props: IProps): React.ReactElement {
   const ghostGrid = zeros([props.width, props.height]);
   return (
     <Box>
-      <Table>
+      <Table sx={{ width: props.width, height: props.height }}>
         <Grid
           width={props.width}
           height={props.height}
diff --git a/src/CotMG/ui/StaneksGiftRoot.tsx b/src/CotMG/ui/StaneksGiftRoot.tsx
index 2eaa6c849..d4b805636 100644
--- a/src/CotMG/ui/StaneksGiftRoot.tsx
+++ b/src/CotMG/ui/StaneksGiftRoot.tsx
@@ -92,7 +92,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
                 <br />
 
                 <DummyGrid
-                  width={4}
+                  width={3}
                   height={4}
                   fragments={[
                     new ActiveFragment({
@@ -116,8 +116,8 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
                 <br />
 
                 <DummyGrid
-                  width={4}
-                  height={4}
+                  width={5}
+                  height={3}
                   fragments={[
                     new ActiveFragment({
                       x: 0,
diff --git a/src/DevMenu/ui/General.tsx b/src/DevMenu/ui/General.tsx
index ba01ee10a..ea4368304 100644
--- a/src/DevMenu/ui/General.tsx
+++ b/src/DevMenu/ui/General.tsx
@@ -10,6 +10,10 @@ import Button from "@mui/material/Button";
 import { Money } from "../../ui/React/Money";
 import { IPlayer } from "../../PersonObjects/IPlayer";
 import { IRouter } from "../../ui/Router";
+import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material";
+import { Bladeburner } from "../../Bladeburner/Bladeburner";
+import { GangConstants } from "../../Gang/data/Constants";
+import { FactionNames } from "../../Faction/data/FactionNames";
 import { checkForMessagesToSend } from "../../Message/MessageHelpers";
 
 interface IProps {
@@ -19,6 +23,8 @@ interface IProps {
 
 export function General(props: IProps): React.ReactElement {
   const [error, setError] = useState(false);
+  const [corporationName, setCorporationName] = useState("");
+  const [gangFaction, setGangFaction] = useState("");
 
   function addMoney(n: number) {
     return function () {
@@ -46,6 +52,23 @@ export function General(props: IProps): React.ReactElement {
     props.router.toBitVerse(false, false);
   }
 
+  function createCorporation(): void {
+    props.player.startCorporation(corporationName);
+  }
+
+  function joinBladeburner(): void {
+    props.player.bladeburner = new Bladeburner(props.player);
+  }
+
+  function startGang(): void {
+    const isHacking = gangFaction === FactionNames.NiteSec || gangFaction === FactionNames.TheBlackHand;
+    props.player.startGang(gangFaction, isHacking);
+  }
+
+  function setGangFactionDropdown(event: SelectChangeEvent<string>): void {
+    setGangFaction(event.target.value);
+  }
+    
   function checkMessages(): void {
     checkForMessagesToSend();
   }
@@ -87,6 +110,22 @@ export function General(props: IProps): React.ReactElement {
         </Button>
         <Button onClick={upgradeRam}>+ RAM</Button>
         <br />
+        <Typography>Corporation Name:</Typography>
+        <TextField value={corporationName} onChange={(x) => setCorporationName(x.target.value)} />
+        <Button onClick={createCorporation}>Create Corporation</Button>
+        <br />
+        <Typography>Gang Faction:</Typography>
+        <Select value={gangFaction} onChange={setGangFactionDropdown}>
+          {GangConstants.Names.map((factionName) => (
+            <MenuItem key={factionName} value={factionName}>
+              {factionName}
+            </MenuItem>
+          ))}
+        </Select>
+        <Button onClick={startGang}>Start Gang</Button>
+        <br />
+        <Button onClick={joinBladeburner}>Join BladeBurner</Button>
+        <br />
 
         <Button onClick={quickB1tFlum3}>Quick b1t_flum3.exe</Button>
         <Button onClick={b1tflum3}>Run b1t_flum3.exe</Button>
diff --git a/src/Locations/ui/SpecialLocation.tsx b/src/Locations/ui/SpecialLocation.tsx
index a7159540c..f4cadd5ec 100644
--- a/src/Locations/ui/SpecialLocation.tsx
+++ b/src/Locations/ui/SpecialLocation.tsx
@@ -15,7 +15,7 @@ import Typography from "@mui/material/Typography";
 import Button from "@mui/material/Button";
 
 import { Location } from "../Location";
-import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
+import { CreateCorporationModal } from "../../Corporation/ui/modals/CreateCorporationModal";
 import { LocationName } from "../data/LocationNames";
 import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
 import { Factions } from "../../Faction/Factions";
@@ -34,6 +34,7 @@ import { HacknetServer } from "../../Hacknet/HacknetServer";
 import { GetServer } from "../../Server/AllServers";
 import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
 import { FactionNames } from "../../Faction/data/FactionNames";
+import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
 
 type IProps = {
   loc: Location;
@@ -316,7 +317,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
       return renderGrafting();
     }
     case LocationName.Sector12CityHall: {
-      return <CreateCorporation />;
+      return (BitNodeMultipliers.CorporationSoftCap < 0.15 && <></>) || <CreateCorporation />;
     }
     case LocationName.Sector12NSA: {
       return renderBladeburner();
diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts
index d72fef4ac..b7f8b704e 100644
--- a/src/Netscript/RamCostGenerator.ts
+++ b/src/Netscript/RamCostGenerator.ts
@@ -177,6 +177,8 @@ const singularity: IMap<any> = {
   installAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
   isFocused: SF4Cost(0.1),
   setFocus: SF4Cost(0.1),
+  b1tflum3: SF4Cost(16),
+  destroyW0r1dD43m0n: SF4Cost(32),
 };
 
 // Gang API
diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts
index 7e0b022b1..59830c157 100644
--- a/src/NetscriptFunctions/Corporation.ts
+++ b/src/NetscriptFunctions/Corporation.ts
@@ -53,6 +53,9 @@ import {
   SellShares,
   BuyBackShares,
   SetSmartSupplyUseLeftovers,
+  LimitMaterialProduction,
+  LimitProductProduction,
+  UpgradeWarehouseCost,
 } from "../Corporation/Actions";
 import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades";
 import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades";
@@ -64,6 +67,7 @@ import { CorporationConstants } from "../Corporation/data/Constants";
 import { IndustryUpgrades } from "../Corporation/IndustryUpgrades";
 import { ResearchMap } from "../Corporation/ResearchMap";
 import { Factions } from "../Faction/Factions";
+import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
 
 export function NetscriptCorporation(
   player: IPlayer,
@@ -74,6 +78,8 @@ export function NetscriptCorporation(
     if (!player.canAccessCorporation() || player.hasCorporation()) return false;
     if (!corporationName) return false;
     if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3");
+    if (BitNodeMultipliers.CorporationSoftCap < 0.15)
+      throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`);
 
     if (selfFund) {
       if (!player.canAfford(150e9)) return false;
@@ -311,12 +317,16 @@ export function NetscriptCorporation(
       checkAccess("getPurchaseWarehouseCost", 7);
       return CorporationConstants.WarehouseInitialCost;
     },
-    getUpgradeWarehouseCost: function (_divisionName: unknown, _cityName: unknown): number {
+    getUpgradeWarehouseCost: function (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): number {
       checkAccess("upgradeWarehouse", 7);
       const divisionName = helper.string("getUpgradeWarehouseCost", "divisionName", _divisionName);
       const cityName = helper.city("getUpgradeWarehouseCost", "cityName", _cityName);
+      const amt = helper.number("getUpgradeWarehouseCost", "amount", _amt);
+      if (amt < 1) {
+        throw helper.makeRuntimeErrorMsg(`corporation.getUpgradeWarehouseCost`, "You must provide a positive number");
+      }
       const warehouse = getWarehouse(divisionName, cityName);
-      return CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1);
+      return UpgradeWarehouseCost(warehouse, amt);
     },
     hasWarehouse: function (_divisionName: unknown, _cityName: unknown): boolean {
       checkAccess("hasWarehouse", 7);
@@ -348,6 +358,7 @@ export function NetscriptCorporation(
       const material = getMaterial(divisionName, cityName, materialName);
       const corporation = getCorporation();
       return {
+        cost: material.bCost,
         name: material.name,
         qty: material.qty,
         qlt: material.qlt,
@@ -389,12 +400,16 @@ export function NetscriptCorporation(
       const corporation = getCorporation();
       PurchaseWarehouse(corporation, getDivision(divisionName), cityName);
     },
-    upgradeWarehouse: function (_divisionName: unknown, _cityName: unknown): void {
+    upgradeWarehouse: function (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): void {
       checkAccess("upgradeWarehouse", 7);
       const divisionName = helper.string("upgradeWarehouse", "divisionName", _divisionName);
       const cityName = helper.city("upgradeWarehouse", "cityName", _cityName);
+      const amt = helper.number("upgradeWarehouse", "amount", _amt);
       const corporation = getCorporation();
-      UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName));
+      if (amt < 1) {
+        throw helper.makeRuntimeErrorMsg(`corporation.upgradeWarehouse`, "You must provide a positive number");
+      }
+      UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName), amt);
     },
     sellMaterial: function (
       _divisionName: unknown,
@@ -508,6 +523,19 @@ export function NetscriptCorporation(
       const corporation = getCorporation();
       MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest);
     },
+    limitProductProduction: function (
+      _divisionName: unknown,
+      _productName: unknown,
+      _cityName: unknown,
+      _qty: unknown,
+    ) {
+      checkAccess("limitProductProduction", 7);
+      const divisionName = helper.string("limitProductProduction", "divisionName", _divisionName);
+      const cityName = helper.city("limitMaterialProduction", "cityName", _cityName);
+      const productName = helper.string("limitProductProduction", "productName", _productName);
+      const qty = helper.number("limitMaterialProduction", "qty", _qty);
+      LimitProductProduction(getProduct(divisionName, productName), cityName, qty);
+    },
     exportMaterial: function (
       _sourceDivision: unknown,
       _sourceCity: unknown,
@@ -548,6 +576,19 @@ export function NetscriptCorporation(
       const amt = helper.string("cancelExportMaterial", "amt", _amt);
       CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "");
     },
+    limitMaterialProduction: function (
+      _divisionName: unknown,
+      _cityName: unknown,
+      _materialName: unknown,
+      _qty: unknown,
+    ) {
+      checkAccess("limitMaterialProduction", 7);
+      const divisionName = helper.string("limitMaterialProduction", "divisionName", _divisionName);
+      const cityName = helper.city("limitMaterialProduction", "cityName", _cityName);
+      const materialName = helper.string("limitMaterialProduction", "materialName", _materialName);
+      const qty = helper.number("limitMaterialProduction", "qty", _qty);
+      LimitMaterialProduction(getMaterial(divisionName, cityName, materialName), qty);
+    },
     setMaterialMarketTA1: function (
       _divisionName: unknown,
       _cityName: unknown,
diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts
index 4e34f427b..acd94e78d 100644
--- a/src/NetscriptFunctions/Singularity.ts
+++ b/src/NetscriptFunctions/Singularity.ts
@@ -47,6 +47,8 @@ import { Server } from "../Server/Server";
 import { netscriptCanHack } from "../Hacking/netscriptCanHack";
 import { FactionInfos } from "../Faction/FactionInfo";
 import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
+import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
+import { enterBitNode } from "../RedPill";
 
 export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> {
   const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation {
@@ -1335,5 +1337,47 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
         }
         return item.price;
       },
+    b1tflum3:
+      (_ctx: NetscriptContext) =>
+      (_nextBN: unknown, _callbackScript: unknown = ""): void => {
+        const nextBN = _ctx.helper.number("nextBN", _nextBN);
+        const callbackScript = _ctx.helper.string("callbackScript", _callbackScript);
+        enterBitNode(Router, true, player.bitNodeN, nextBN);
+        if (callbackScript)
+          setTimeout(() => {
+            runAfterReset(callbackScript);
+          }, 0);
+      },
+    destroyW0r1dD43m0n:
+      (_ctx: NetscriptContext) =>
+      (_nextBN: unknown, _callbackScript: unknown = ""): void => {
+        const nextBN = _ctx.helper.number("nextBN", _nextBN);
+        const callbackScript = _ctx.helper.string("callbackScript", _callbackScript);
+
+        const hackingRequirements = (): boolean => {
+          const wd = GetServer(SpecialServers.WorldDaemon);
+          if (!(wd instanceof Server))
+            throw new Error("WorldDaemon was not a normal server. This is a bug contact dev.");
+          if (player.hacking < wd.requiredHackingSkill) return false;
+          if (!wd.hasAdminRights) return false;
+          return true;
+        };
+        const bladeburnerRequirements = (): boolean => {
+          if (!player.inBladeburner()) return false;
+          if (!player.bladeburner) return false;
+          return player.bladeburner.blackops[BlackOperationNames.OperationDaedalus];
+        };
+
+        if (!hackingRequirements() && !bladeburnerRequirements()) {
+          _ctx.log(() => "Requirements not met to destroy the world daemon");
+          return;
+        }
+
+        enterBitNode(Router, false, player.bitNodeN, nextBN);
+        if (callbackScript)
+          setTimeout(() => {
+            runAfterReset(callbackScript);
+          }, 0);
+      },
   };
 }
diff --git a/src/Programs/Program.ts b/src/Programs/Program.ts
index e26d5bbb8..1b63e27ec 100644
--- a/src/Programs/Program.ts
+++ b/src/Programs/Program.ts
@@ -24,9 +24,4 @@ export class Program {
     this.create = create;
     this.run = run;
   }
-
-  htmlID(): string {
-    const name = this.name.endsWith(".exe") ? this.name.slice(0, -".exe".length) : this.name;
-    return "create-program-" + name;
-  }
 }
diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts
index 271ca9e7a..b1d429df2 100644
--- a/src/ScriptEditor/NetscriptDefinitions.d.ts
+++ b/src/ScriptEditor/NetscriptDefinitions.d.ts
@@ -2386,6 +2386,30 @@ export interface Singularity {
    * purchased. Throws an error if the specified program/exploit does not exist
    */
   getDarkwebProgramCost(programName: string): number;
+
+  /**
+   * b1t_flum3 into a different BN.
+   * @remarks
+   * RAM cost: 16 GB * 16/4/1
+   *
+   * @param nextBN - BN number to jump to
+   * @param callbackScript - Name of the script to launch in the next BN.
+   */
+  b1tflum3(nextBN: number, callbackScript?: string): void;
+
+  /**
+   * Destroy the w0r1d_d43m0n and move on to the next BN.
+   * @remarks
+   * RAM cost: 32 GB * 16/4/1
+   *
+   * You must have the special augment installed and the required hacking level
+   *   OR
+   * Completed the final black op.
+   *
+   * @param nextBN - BN number to jump to
+   * @param callbackScript - Name of the script to launch in the next BN.
+   */
+  destroyW0r1dD43m0n(nextBN: number, callbackScript?: string): void;
 }
 
 /**
@@ -6737,8 +6761,9 @@ export interface WarehouseAPI {
    * Upgrade warehouse
    * @param divisionName - Name of the division
    * @param cityName - Name of the city
+   * @param amt - amount of upgrades defaults to 1
    */
-  upgradeWarehouse(divisionName: string, cityName: string): void;
+  upgradeWarehouse(divisionName: string, cityName: string, amt?: number): void;
   /**
    * Create a new product
    * @param divisionName - Name of the division
@@ -6754,6 +6779,22 @@ export interface WarehouseAPI {
     designInvest: number,
     marketingInvest: number,
   ): void;
+  /**
+   * Limit Material Production.
+   * @param divisionName - Name of the division
+   * @param cityName - Name of the city
+   * @param materialName - Name of the material
+   * @param qty - Amount to limit to
+   */
+  limitMaterialProduction(divisionName: string, cityName: string, materialName: string, qty: number): void;
+  /**
+   * Limit Product Production.
+   * @param divisionName - Name of the division
+   * @param cityName - Name of the city
+   * @param productName - Name of the product
+   * @param qty - Amount to limit to
+   */
+  limitProductProduction(divisionName: string, cityName: string, productName: string, qty: number): void;
   /**
    * Gets the cost to purchase a warehouse
    * @returns cost
@@ -6761,9 +6802,12 @@ export interface WarehouseAPI {
   getPurchaseWarehouseCost(): number;
   /**
    * Gets the cost to upgrade a warehouse to the next level
+   * @param divisionName - Name of the division
+   * @param cityName - Name of the city
+   * @param amt - amount of upgrades defaults to 1
    * @returns cost to upgrade
    */
-  getUpgradeWarehouseCost(adivisionName: any, acityName: any): number;
+  getUpgradeWarehouseCost(adivisionName: any, acityName: any, amt?: number): number;
   /**
    * Check if you have a warehouse in city
    * @returns true if warehouse is present, false if not
@@ -7007,8 +7051,10 @@ interface Material {
   cmp: number | undefined;
   /** Amount of material produced  */
   prod: number;
-  /** Amount of material sold  */
+  /** Amount of material sold */
   sell: number;
+  /** cost to buy material */
+  cost: number;
 }
 
 /**
diff --git a/src/data/codingcontracttypes.ts b/src/data/codingcontracttypes.ts
index a61a03c51..21e7e40f1 100644
--- a/src/data/codingcontracttypes.ts
+++ b/src/data/codingcontracttypes.ts
@@ -1121,14 +1121,17 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
 
       dfs(0, 0, left, right, data, "", res);
 
-      const sanitizedPlayerAns = removeBracketsFromArrayString(ans).replace(/\s/g, "");
-
-      const playerAnsArray: string[] = sanitizedPlayerAns.split(",");
-      if (playerAnsArray.length !== res.length) {
+      const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
+      const sanitizedPlayerAnsArr: string[] = sanitizedPlayerAns.split(",");
+      for (let i = 0; i < sanitizedPlayerAnsArr.length; ++i) {
+        sanitizedPlayerAnsArr[i] = removeQuotesFromString(sanitizedPlayerAnsArr[i]).replace(/\s/g, "");
+      }
+      
+      if (sanitizedPlayerAnsArr.length !== res.length) {
         return false;
       }
       for (const resultInAnswer of res) {
-        if (!playerAnsArray.includes(resultInAnswer)) {
+        if (!sanitizedPlayerAnsArr.includes(resultInAnswer)) {
           return false;
         }
       }