diff --git a/game.js b/game.js
index 8659032..7aee315 100644
--- a/game.js
+++ b/game.js
@@ -2137,6 +2137,118 @@ function renderProductionBreakdown() {
container.innerHTML = html;
}
+// === FLEET STATUS PANEL ===
+function renderFleetStatus() {
+ const container = document.getElementById('fleet-status');
+ if (!container) return;
+
+ // Only show once player has at least one wizard building
+ const wizardDefs = BDEF.filter(b =>
+ ['bezalel','allegro','ezra','timmy','fenrir','bilbo'].includes(b.id)
+ );
+ const owned = wizardDefs.filter(d => (G.buildings[d.id] || 0) > 0);
+ if (owned.length === 0) {
+ container.style.display = 'none';
+ return;
+ }
+ container.style.display = 'block';
+
+ const h = G.harmony;
+ const timmyEff = Math.max(20, Math.min(300, (h / 50) * 100));
+
+ let html = '
FLEET STATUS
';
+ html += '';
+
+ for (const def of owned) {
+ const count = G.buildings[def.id] || 0;
+ let status, color, detail;
+
+ switch (def.id) {
+ case 'bezalel':
+ status = 'Active';
+ color = '#4caf50';
+ detail = `+${fmt(50 * count * G.codeBoost)} code/s, +${2 * count} ops/s`;
+ break;
+ case 'allegro':
+ if (G.trust < 5) {
+ status = 'IDLE';
+ color = '#f44336';
+ detail = 'Needs trust ≥5 to function';
+ } else {
+ status = 'Active';
+ color = '#4caf50';
+ detail = `+${fmt(10 * count * G.knowledgeBoost)} knowledge/s`;
+ }
+ break;
+ case 'ezra':
+ const ezraDebuff = G.activeDebuffs && G.activeDebuffs.find(d => d.id === 'ezra_offline');
+ if (ezraDebuff) {
+ status = 'OFFLINE';
+ color = '#f44336';
+ detail = 'Channel down — users -70%';
+ } else {
+ status = 'Active';
+ color = '#4caf50';
+ detail = `+${fmt(25 * count * G.userBoost)} users/s, +${(0.5 * count).toFixed(1)} trust/s`;
+ }
+ break;
+ case 'timmy':
+ if (h < 20) {
+ status = 'STRESSED';
+ color = '#f44336';
+ detail = `Effectiveness: ${Math.floor(timmyEff)}% — harmony critical`;
+ } else if (h < 50) {
+ status = 'Reduced';
+ color = '#ffaa00';
+ detail = `Effectiveness: ${Math.floor(timmyEff)}% — harmony low`;
+ } else {
+ status = 'Healthy';
+ color = '#4caf50';
+ detail = `Effectiveness: ${Math.floor(timmyEff)}% — all production boosted`;
+ }
+ break;
+ case 'fenrir':
+ status = 'Watching';
+ color = '#4a9eff';
+ detail = `+${2 * count} trust/s, -${1 * count} ops/s (security cost)`;
+ break;
+ case 'bilbo':
+ const bilboDebuff = G.activeDebuffs && G.activeDebuffs.find(d => d.id === 'bilbo_vanished');
+ if (bilboDebuff) {
+ status = 'VANISHED';
+ color = '#f44336';
+ detail = 'Creativity halted — spend trust to lure back';
+ } else {
+ status = 'Present';
+ color = Math.random() < 0.1 ? '#ffd700' : '#b388ff'; // occasional gold flash
+ detail = `+${count} creativity/s (10% burst chance, 5% vanish chance)`;
+ }
+ break;
+ }
+
+ html += `
`;
+ html += `
`;
+ html += `${def.name.split(' — ')[0]}`;
+ html += `${status}`;
+ html += `
`;
+ html += `
${detail}
`;
+ if (count > 1) html += `
x${count}
`;
+ html += `
`;
+ }
+
+ html += '
';
+
+ // Harmony summary bar
+ const harmonyColor = h > 60 ? '#4caf50' : h > 30 ? '#ffaa00' : '#f44336';
+ html += ``;
+ html += `
Harmony`;
+ html += `
`;
+ html += `
${Math.floor(h)}%`;
+ html += `
`;
+
+ container.innerHTML = html;
+}
+
function updateEducation() {
const container = document.getElementById('education-text');
if (!container) return;
@@ -2388,6 +2500,7 @@ function render() {
renderSprint();
renderBulkOps();
renderPulse();
+ renderFleetStatus();
}
function renderAlignment() {
diff --git a/index.html b/index.html
index 8876f80..e5f3fe5 100644
--- a/index.html
+++ b/index.html
@@ -180,6 +180,7 @@ Drift: 0
Events Resolved: 0
+