<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BSG-117 Danverse – Fleet Command</title>
<style>
:root {
--bg:#05070b;--panel:#10141d;--accent:#3fd5ff;--accent-soft:#1b8fb3;
--text:#e5f7ff;--danger:#ff4b4b;--ok:#4bff7a;--warn:#ffd54b;
--border:#2a3445;--muted:#8fa3b8;
--font-main:"Orbitron",system-ui,sans-serif;
--font-body:"Segoe UI",system-ui,sans-serif;
*{box-sizing:border-box;}
body{
margin:0;
background:#05070b;
color:var(--text);
font-family:var(--font-body);
.app{
display:grid;
grid-template-columns:2fr 2fr 2fr;
gap:10px;
padding:10px;
header{
grid-column:1/4;
padding:10px 14px;
background:#10141d;
border-bottom:1px solid var(--border);
display:flex;
justify-content:space-between;
header h1{
font-family:var(--font-main);
letter-spacing:.18em;
font-size:18px;
margin:0;
color:var(--accent);
header .subtitle{font-size:12px;color:var(--muted);}
.panel{
background:#0b0f16;
border:1px solid var(--border);
border-radius:6px;
padding:8px 10px;
.panel h2{
font-family:var(--font-main);
font-size:13px;
letter-spacing:.16em;
margin:0 0 6px;
color:var(--accent-soft);
}
.panel small{color:var(--muted);font-size:11px;}
.panel-section{
margin-top:6px;
border-top:1px solid #1b2433;
padding-top:6px;
label{font-size:11px;color:var(--muted);display:block;margin-bottom:2px;}
input,select{
width:100%;
font-size:11px;
padding:4px 5px;
border-radius:3px;
border:1px solid #283344;
background:#05070b;
color:var(--text);
.row{display:flex;gap:6px;}
.row>div{flex:1;}
.btn-bar{display:flex;gap:6px;margin-top:6px;}
button{
font-size:11px;
padding:4px 8px;
border-radius:3px;
border:1px solid var(--accent-soft);
background:#05070b;
color:var(--accent);
cursor:pointer;
button:hover{background:#0c111a;}
.btn-danger{border-color:var(--danger);color:var(--danger);}
.btn-muted{border-color:var(--muted);color:var(--muted);}
table{
width:100%;
border-collapse:collapse;
font-size:11px;
margin-top:4px;
th,td{
border-bottom:1px solid #1b2433;
padding:3px 4px;
th{
font-family:var(--font-main);
font-size:10px;
letter-spacing:.12em;
color:var(--muted);
tr:hover{background:#182032;}
.status-pill{
display:inline-block;
padding:1px 6px;
border-radius:999px;
font-size:9px;
}
.status-ok{background:#0f3; color:#0f3;}
.status-warn{background:#fc0;color:#fc0;}
.status-bad{background:#f33;color:#f33;}
#dradisCanvas{
width:100%;
height:220px;
background:#020308;
border:1px solid #283344;
border-radius:4px;
.dc-grid{
display:grid;
grid-template-columns:repeat(3,1fr);
gap:4px;
.dc-cell{
border:1px solid #283344;
background:#05070b;
padding:4px;
font-size:10px;
text-align:center;
.story-log{
font-size:11px;
max-height:180px;
overflow-y:auto;
}
</style>
</head>
<body>
<div class="app">
<header>
<div>
<h1>BSG-117 DANVERSE – FLEET COMMAND</h1>
<div class="subtitle">Colonial Fleet Roster • Officer Development •
DRADIS Tactical Display • Damage Control</div>
</div>
<div class="subtitle">Admiral Jonathan Cain</div>
</header>
<!-- ===== FLEET PANEL ===== -->
<section class="panel" id="fleetPanel">
<h2>Fleet Roster</h2>
<small>Manage Colonial Fleet vessels.</small>
<div class="panel-section">
<div class="row">
<div>
<label>Ship</label>
<select id="shipName">
<option>BSG-117 Danverse</option>
<option>BSG-75 Galactica</option>
<option>BSG-62 Pegasus</option>
<option>Gunstar Sentinel</option>
</select>
</div>
<div>
<label>Status</label>
<select id="shipStatus">
<option>Operational</option>
<option>Damaged</option>
<option>Docked</option>
<option>Lost</option>
</select>
</div>
</div>
<div class="row" style="margin-top:4px;">
<div><label>CO</label><input id="shipCO"></div>
<div><label>XO</label><input id="shipXO"></div>
</div>
<div class="btn-bar">
<button id="addShipBtn">Add / Update</button>
<button id="resetShipBtn" class="btn-muted">Reset</button>
<button id="deleteShipBtn" class="btn-danger">Delete</button>
</div>
<table id="fleetTable">
<thead><tr><th>Ship</th><th>Class</th><th>CO</th><th>XO</th><t
h>Status</th></tr></thead>
<tbody></tbody>
</table>
</div>
</section>
<!-- ===== OFFICERS ===== -->
<section class="panel" id="officerPanel">
<h2>Officer Development</h2>
<small>Manage Colonial Fleet personnel.</small>
<div class="panel-section">
<div class="row">
<div><label>Name</label><input id="officerName"></div>
<div><label>Callsign</label><input id="officerCallsign"></div>
</div>
<div class="row" style="margin-top:4px;">
<div>
<label>Rank</label>
<select id="officerRank">
<option>Ensign</option>
<option>Lieutenant</option>
<option>Captain</option>
<option>Major</option>
<option>Commander</option>
</select>
</div>
<div>
<label>MOS</label>
<select id="officerMOS"></select>
</div>
</div>
<div class="row" style="margin-top:4px;">
<div>
<label>Ship</label>
<select id="officerShip"></select>
</div>
<div>
<label>Notes</label>
<input id="officerNotes">
</div>
</div>
<div class="btn-bar">
<button id="addOfficerBtn">Add / Update</button>
<button id="resetOfficerBtn" class="btn-muted">Reset</button>
<button id="deleteOfficerBtn" class="btn-danger">Delete</button>
</div>
<table id="officerTable">
<thead><tr><th>Name</th><th>Rank</th><th>MOS</th><th>Ship</t
h><th>Callsign</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="panel-section">
<h2 style="font-size:11px;">MOS Registry</h2>
<small>Manage Military Occupational Specialties.</small>
<div class="row" style="margin-top:4px;">
<div><label>MOS Name</label><input id="mosName"></div>
<div><label>Code</label><input id="mosCode"></div>
</div>
<div class="btn-bar">
<button id="addMOSBtn">Add / Update</button>
<button id="resetMOSBtn" class="btn-muted">Reset</button>
<button id="deleteMOSBtn" class="btn-danger">Delete</button>
</div>
<table id="mosTable">
<thead><tr><th>Code</th><th>MOS</th></tr></thead>
<tbody></tbody>
</table>
</div>
</section>
<!-- ===== DRADIS ===== -->
<section class="panel" id="dradisPanel">
<h2>DRADIS Tactical Display</h2>
<small>Fleet positions and contacts.</small>
<div class="panel-section">
<canvas id="dradisCanvas"></canvas>
</div>
</section>
<!-- ===== DAMAGE CONTROL ===== -->
<section class="panel" id="dcPanel">
<h2>Damage Control</h2>
<small>Hull & system integrity.</small>
<div class="panel-section">
<div class="dc-grid" id="dcGrid"></div>
<div class="btn-bar">
<button id="dcRandomBtn">Simulate Hit</button>
<button id="dcResetBtn" class="btn-muted">Reset</button>
</div>
</div>
</section>
<!-- ===== STORY ===== -->
<section class="panel" id="storyPanel">
<h2>Fleet Chronicle</h2>
<small>Danverse historical log.</small>
<div class="panel-section story-log">
<p><b>BSG-117 Danverse</b> — Ghost ship at Scorpia.</p>
<p><b>Fall of Scorpia</b> — Basestars strike the yards.</p>
<p><b>Rally to Galactica</b> — Sector 7-12 regroup.</p>
</div>
</section>
<script>
/* ===== DATA ===== */
const fleet = [];
const officers = [];
const mosList = [];
/* ===== HELPERS ===== */
function findIndex(arr,key,val){return [Link](x=>x[key]===val);}
function inferClass(name){
if([Link]("Galactica")) return "Columbia-Class";
if([Link]("Pegasus")) return "Mercury-Class";
if([Link]("Danverse")) return "Nova-Class";
if([Link]("Gunstar")) return "Gunstar";
return "Colonial Vessel";
function pill(status){
let cls="status-ok";
if(status==="Damaged") cls="status-warn";
if(status==="Lost") cls="status-bad";
return `<span class="status-pill ${cls}">${status}</span>`;
/* ===== FLEET ===== */
const fleetBody=[Link]("#fleetTable tbody");
const shipName=[Link]("shipName");
const shipStatus=[Link]("shipStatus");
const shipCO=[Link]("shipCO");
const shipXO=[Link]("shipXO");
function renderFleet(){
[Link]="";
[Link](s=>{
const tr=[Link]("tr");
[Link]=`
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]||"-"}</td>
<td>${[Link]||"-"}</td>
<td>${pill([Link])}</td>
`;
[Link]=()=>loadShip(s);
[Link](tr);
});
refreshOfficerShips();
function loadShip(s){
[Link]=[Link];
[Link]=[Link];
[Link]=[Link]||"";
[Link]=[Link]||"";
[Link]("addShipBtn").onclick=()=>{
const name=[Link]();
if(!name) return;
const idx=findIndex(fleet,"name",name);
const obj={
name,
class:inferClass(name),
status:[Link],
co:[Link](),
xo:[Link]()
};
if(idx>=0) fleet[idx]=obj;
else [Link](obj);
renderFleet();
};
[Link]("resetShipBtn").onclick=()=>{
[Link]="";
[Link]="";
[Link]="Operational";
};
[Link]("deleteShipBtn").onclick=()=>{
const name=[Link]();
const idx=findIndex(fleet,"name",name);
if(idx>=0){[Link](idx,1);renderFleet();}
};
/* ===== MOS + OFFICERS ===== */
const mosBody=[Link]("#mosTable tbody");
const mosName=[Link]("mosName");
const mosCode=[Link]("mosCode");
const officerMOS=[Link]("officerMOS");
const officerShip=[Link]("officerShip");
const officerBody=[Link]("#officerTable tbody");
const officerName=[Link]("officerName");
const officerCallsign=[Link]("officerCallsign");
const officerRank=[Link]("officerRank");
const officerNotes=[Link]("officerNotes");
function renderMOS(){
[Link]="";
[Link]="";
[Link](m=>{
const tr=[Link]("tr");
[Link]=`<td>${[Link]}</td><td>${[Link]}</td>`;
[Link]=()=>{[Link]=[Link];[Link]=[Link];};
[Link](tr);
const opt=[Link]("option");
[Link]=[Link];
[Link]=`${[Link]} (${[Link]})`;
[Link](opt);
});
function renderOfficers(){
[Link]="";
[Link](o=>{
const tr=[Link]("tr");
[Link]=`
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]||"-"}</td>
`;
[Link]=()=>loadOfficer(o);
[Link](tr);
});
function loadOfficer(o){
[Link]=[Link];
[Link]=[Link]||"";
[Link]=[Link];
[Link]=[Link];
[Link]=[Link];
[Link]=[Link]||"";
function refreshOfficerShips(){
[Link]="";
[Link](s=>{
const opt=[Link]("option");
[Link]=[Link];
[Link]=[Link];
[Link](opt);
});
[Link]("addMOSBtn").onclick=()=>{
const name=[Link]();
const code=[Link]();
if(!name||!code) return;
const idx=findIndex(mosList,"code",code);
const obj={name,code};
if(idx>=0) mosList[idx]=obj;
else [Link](obj);
renderMOS();
};
[Link]("resetMOSBtn").onclick=()=>{
[Link]="";
[Link]="";
};
[Link]("deleteMOSBtn").onclick=()=>{
const code=[Link]();
const idx=findIndex(mosList,"code",code);
if(idx>=0){[Link](idx,1);renderMOS();}
};
[Link]("addOfficerBtn").onclick=()=>{
const name=[Link]();
if(!name) return;
const idx=findIndex(officers,"name",name);
const obj={
name,
callsign:[Link](),
rank:[Link],
mos:[Link],
ship:[Link],
notes:[Link]()
};
if(idx>=0) officers[idx]=obj;
else [Link](obj);
renderOfficers();
};
[Link]("resetOfficerBtn").onclick=()=>{
[Link]="";
[Link]="";
[Link]="";
};
[Link]("deleteOfficerBtn").onclick=()=>{
const name=[Link]();
const idx=findIndex(officers,"name",name);
if(idx>=0){[Link](idx,1);renderOfficers();}
};
/* ===== DRADIS ===== */
const dradisCanvas=[Link]("dradisCanvas");
const dradisCtx=[Link]("2d");
function resizeDRADIS(){
[Link]=[Link];
[Link]=[Link];
}
function renderDRADIS(){
resizeDRADIS();
const ctx=dradisCtx;
const w=[Link];
const h = [Link];
[Link](0, 0, w, h);
// Rings
[Link] = "#283344";
for (let r = 40; r < [Link](w, h) / 2; r += 40) {
[Link]();
[Link](w / 2, h / 2, r, 0, [Link] * 2);
[Link]();
// Radials
[Link] = "#182032";
for (let a = 0; a < 360; a += 30) {
const rad = a * [Link] / 180;
[Link]();
[Link](w / 2, h / 2);
[Link](
w / 2 + [Link](rad) * w / 2,
h / 2 + [Link](rad) * h / 2
);
[Link]();
}
// Fleet contacts
[Link](s => {
let color = "#3fd5ff"; // Battlestar
if ([Link]("Gunstar")) color = "#4bff7a";
if ([Link] === "Damaged") color = "#ffd54b";
if ([Link] === "Lost") color = "#ff4b4b";
const angle = [Link]() * [Link] * 2;
const radius = 40 + [Link]() * (([Link](w, h) / 2) - 60);
const x = w / 2 + [Link](angle) * radius;
const y = h / 2 + [Link](angle) * radius;
[Link] = color;
[Link]();
[Link](x, y, 4, 0, [Link] * 2);
[Link]();
});
[Link]("resize", renderDRADIS);
/* ===========================
DAMAGE CONTROL
=========================== */
const dcGrid = [Link]("dcGrid");
const dcCells = [];
function initDC() {
const sections = [
"Bow Hull","Port Hull","Starboard Hull",
"Aft Hull","Flight Pod Alpha","Flight Pod Beta",
"Reactor Core","FTL Drives","Primary DRADIS"
];
[Link] = "";
[Link] = 0;
[Link](name => {
const cell = { name, status: "OK" };
[Link](cell);
const div = [Link]("div");
[Link] = "dc-cell";
[Link] = name;
[Link] = `
<div>${name}</div>
<div class="status-pill status-ok">OK</div>
`;
[Link](div);
});
}
function randomHit() {
const idx = [Link]([Link]() * [Link]);
const cell = dcCells[idx];
const states = ["OK", "WARN", "CRITICAL"];
const next = states[[Link]([Link]() * [Link])];
[Link] = next;
const div = [...[Link]].find(d => [Link] === [Link]);
const pillEl = [Link](".status-pill");
[Link] = next;
[Link] =
"status-pill " +
(next === "OK"
? "status-ok"
: next === "WARN"
? "status-warn"
: "status-bad");
[Link]("dcRandomBtn").onclick = randomHit;
[Link]("dcResetBtn").onclick = initDC;
/* ===========================
INIT
=========================== */
function init() {
// Seed MOS
[Link](
{ name: "Viper Pilot", code: "VP-01" },
{ name: "Raptor ECO", code: "RE-02" },
{ name: "CIC Officer", code: "CO-03" },
{ name: "Deck Chief", code: "DC-04" }
);
// Seed Fleet
[Link](
name: "BSG-117 Danverse",
class: "Nova-Class",
status: "Operational",
co: "Admiral Jonathan Cain",
xo: "Commander Elias Thorne"
},
name: "BSG-75 Galactica",
class: "Columbia-Class",
status: "Operational",
co: "Commander William Adama",
xo: "Colonel Saul Tigh"
},
name: "BSG-62 Pegasus",
class: "Mercury-Class",
status: "Damaged",
co: "Admiral Helena Cain",
xo: "Colonel Fisk"
},
name: "Gunstar Sentinel",
class: "Gunstar",
status: "Operational",
co: "Captain Rian Hale",
xo: "Major Kara Thorne"
);
// Seed Officers
[Link](
name: "Kara 'Starbuck' Thrace",
callsign: "Starbuck",
rank: "Captain",
mos: "Viper Pilot",
ship: "BSG-75 Galactica",
notes: "Ace pilot, reckless but brilliant."
},
name: "Lee 'Apollo' Adama",
callsign: "Apollo",
rank: "Major",
mos: "Viper Pilot",
ship: "BSG-75 Galactica",
notes: "CAG, strong moral compass."
},
name: "Galen Tyrol",
callsign: "",
rank: "Chief",
mos: "Deck Chief",
ship: "BSG-75 Galactica",
notes: "Responsible for flight deck operations."
},
name: "Elias Thorne",
callsign: "Specter",
rank: "Commander",
mos: "CIC Officer",
ship: "BSG-117 Danverse",
notes: "Danverse XO, tactical specialist."
);
// Now render everything
renderMOS();
renderFleet();
refreshOfficerShips();
renderOfficers();
initDC();
renderDRADIS();
init();
</script>
</body>
</html>