diff --git a/code/modules/modular_computers/file_system/programs/civilian_bounty.dm b/code/modules/modular_computers/file_system/programs/civilian_bounty.dm
new file mode 100644
index 00000000000..c9e65be9cec
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/civilian_bounty.dm
@@ -0,0 +1,110 @@
+///Everything within this file is an edited form from this file, stripping it of some various components because they are not needed for the PDA app: code/game/machinery/civilian_bounties.dm
+
+///Percentage of a civilian bounty the civilian will make.
+#define CIV_BOUNTY_SPLIT 30
+
+/datum/computer_file/program/civilianbounties
+ filename = "bountyapp"
+ filedesc = "Civilian Bounties"
+ downloader_category = PROGRAM_CATEGORY_SUPPLY
+ program_open_overlay = "request"
+ extended_desc = "Nanotrasen Civilian Bounty Requisition Network interface for displaying wanted items."
+ program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
+ can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
+ size = 5
+ tgui_id = "NtosCivCargoHoldTerminal"
+ program_icon = FA_ICON_BOXES_STACKED
+ var/status_report = "Ready for delivery."
+ var/points = 0
+
+/datum/computer_file/program/civilianbounties/ui_data(mob/user)
+ var/list/data = list()
+ data["points"] = points
+ data["status_report"] = status_report
+ data["id_inserted"] = computer.computer_id_slot
+ if(computer.computer_id_slot?.registered_account)
+ if(computer.computer_id_slot.registered_account.civilian_bounty)
+ data["id_bounty_info"] = computer.computer_id_slot.registered_account.civilian_bounty.description
+ data["id_bounty_num"] = computer.computer_id_slot.registered_account.bounty_num()
+ data["id_bounty_value"] = (computer.computer_id_slot.registered_account.civilian_bounty.reward) * (CIV_BOUNTY_SPLIT/100)
+ if(computer.computer_id_slot.registered_account.bounties)
+ data["picking"] = TRUE
+ data["id_bounty_names"] = list(computer.computer_id_slot.registered_account.bounties[1].name,
+ computer.computer_id_slot.registered_account.bounties[2].name,
+ computer.computer_id_slot.registered_account.bounties[3].name)
+ data["id_bounty_infos"] = list(computer.computer_id_slot.registered_account.bounties[1].description,
+ computer.computer_id_slot.registered_account.bounties[2].description,
+ computer.computer_id_slot.registered_account.bounties[3].description)
+ data["id_bounty_values"] = list(computer.computer_id_slot.registered_account.bounties[1].reward * (CIV_BOUNTY_SPLIT/100),
+ computer.computer_id_slot.registered_account.bounties[2].reward * (CIV_BOUNTY_SPLIT/100),
+ computer.computer_id_slot.registered_account.bounties[3].reward * (CIV_BOUNTY_SPLIT/100))
+ else
+ data["picking"] = FALSE
+
+ return data
+
+/datum/computer_file/program/civilianbounties/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ var/mob/user = ui.user
+ switch(action)
+ if("pick")
+ pick_bounty(params["value"])
+ if("bounty")
+ add_bounties(user, 0)
+
+///Here is where cargo bounties are added to the player's bank accounts, then adjusted and scaled into a civilian bounty.
+/datum/computer_file/program/civilianbounties/proc/add_bounties(mob/user, cooldown_reduction = 0)
+ var/datum/bank_account/id_account = computer.computer_id_slot?.registered_account
+ if(!id_account)
+ return
+ if((id_account.civilian_bounty || id_account.bounties) && !COOLDOWN_FINISHED(id_account, bounty_timer))
+ var/time_left = DisplayTimeText(COOLDOWN_TIMELEFT(id_account, bounty_timer), round_seconds_to = 1)
+ computer.balloon_alert(user, "try again in [time_left]!")
+ return FALSE
+ if(!id_account.account_job)
+ computer.say("Requesting ID card has no job assignment registered!")
+ return FALSE
+
+ var/list/datum/bounty/crumbs = generate_bounty_list(id_account.account_job.bounty_types)
+ COOLDOWN_START(id_account, bounty_timer, (5 MINUTES) - cooldown_reduction)
+ id_account.bounties = crumbs
+
+/**
+ * Proc that assigned a civilian bounty to an ID card, from the list of potential bounties that that bank account currently has available.
+ * Available choices are assigned during add_bounties, and one is locked in here.
+ *
+ * @param choice The index of the bounty in the list of bounties that the player can choose from.
+ */
+/datum/computer_file/program/civilianbounties/proc/pick_bounty(datum/bounty/choice)
+ var/datum/bank_account/id_account = computer.computer_id_slot?.registered_account
+ if(!id_account?.bounties?[choice])
+ playsound(computer.loc, 'sound/machines/synth/synth_no.ogg', 40 , TRUE)
+ return
+ id_account.civilian_bounty = id_account.bounties[choice]
+ id_account.bounties = null
+ SSblackbox.record_feedback("tally", "bounties_assigned", 1, id_account.civilian_bounty.type)
+ return id_account.civilian_bounty
+
+/**
+ * Generates a list of bounties for use with the civilian bounty pad, this is virtually identical to the stuff contained within: code/game/machinery/civilian_bounties.dm
+ * @param bounty_types the define taken from a job for selection of a random_bounty() proc.
+ * @param bounty_rolls the number of bounties to be selected from.
+ * @param assistant_failsafe Do we guarentee one assistant bounty per generated list? Used for non-assistant jobs to give an easier alternative to that job's default bounties.
+ */
+/datum/computer_file/program/civilianbounties/proc/generate_bounty_list(bounty_types, bounty_rolls = 3, assistant_failsafe = TRUE)
+ var/list/rolling_list = list()
+ if(assistant_failsafe)
+ rolling_list += random_bounty(CIV_JOB_BASIC)
+ while(bounty_rolls > 1)
+ var/datum/bounty/potential_bounty = random_bounty(bounty_types)
+ var/repeats_bool = FALSE
+ for(var/datum/iterator in rolling_list)
+ if(iterator.type == potential_bounty.type)
+ repeats_bool = TRUE
+ if(repeats_bool)
+ continue
+ rolling_list += potential_bounty
+ bounty_rolls -= 1
+ return rolling_list
+
+#undef CIV_BOUNTY_SPLIT
diff --git a/tgstation.dme b/tgstation.dme
index e220e38cb96..64ecdb7b493 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -5731,6 +5731,7 @@
#include "code\modules\modular_computers\file_system\programs\budgetordering.dm"
#include "code\modules\modular_computers\file_system\programs\card.dm"
#include "code\modules\modular_computers\file_system\programs\cargoship.dm"
+#include "code\modules\modular_computers\file_system\programs\civilian_bounty.dm"
#include "code\modules\modular_computers\file_system\programs\coupon.dm"
#include "code\modules\modular_computers\file_system\programs\crewmanifest.dm"
#include "code\modules\modular_computers\file_system\programs\dept_order.dm"
diff --git a/tgui/packages/tgui/interfaces/NtosCivCargoHoldTerminal.jsx b/tgui/packages/tgui/interfaces/NtosCivCargoHoldTerminal.jsx
new file mode 100644
index 00000000000..6b1f7dc6fe1
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosCivCargoHoldTerminal.jsx
@@ -0,0 +1,140 @@
+import {
+ Box,
+ Button,
+ Flex,
+ LabeledList,
+ NoticeBox,
+ Section,
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
+import { NtosWindow } from '../layouts';
+
+export const NtosCivCargoHoldTerminal = (props) => {
+ const { act, data } = useBackend();
+ const { status_report, id_inserted, id_bounty_info, picking } = data;
+ const in_text = 'Welcome valued employee.';
+ const out_text = 'To begin, insert your ID into the console.';
+ return (
+
+
+
+
+
+ {id_inserted ? in_text : out_text}
+
+
+
+ {picking ? : }
+
+
+
+
+ );
+};
+
+const BountyTextBox = (props) => {
+ const { data } = useBackend();
+ const { id_bounty_info, id_bounty_value, id_bounty_num } = data;
+ const na_text = 'N/A, please add a new bounty.';
+ return (
+
+
+
+ {id_bounty_info ? id_bounty_info : na_text}
+
+
+ {id_bounty_info ? id_bounty_num : 'N/A'}
+
+
+ {id_bounty_info ? id_bounty_value : 'N/A'}
+
+
+
+ );
+};
+
+const BountyPickBox = (props) => {
+ const { act, data } = useBackend();
+ const { id_bounty_names, id_bounty_infos, id_bounty_values } = data;
+ return (
+
+ );
+};
+
+const BountyPickButton = (props) => {
+ return (
+
+ );
+};