// screens_status.jsx — Status & Journal screens window.S = window.S || {}; (() => { const { useState } = React; const { PORTS, SHIPS, FACTIONS, EQUIPMENT, STARTS, RESOURCES } = window.D; const L = window.L; const A = window.E.A; const { T, panelStyle, Bar, Pill, Btn, StatBlock, SectionTitle, LogList, Divider, EmptyState, NarrativePanel, NarrativeLine, TutorialPopup, BackButton, Tooltip, Panel, IconMap, IconBarChart, IconMarket, IconJournal, IconAnchor, IconCrew, IconFloppy, IconFileTransfer, IconTalking, IconGold, IconSkull, IconHandshake, IconSearch, PortSilhouette } = window.UI; const { FactionPill, RepPill, ShipSprite } = window.UI; const { shouldShowTutorial, markTutorialSeen } = window.L; // ── STATUS SCREEN ──────────────────────────────────────────────────── function StatusScreen({ state, dispatch }) { const [showTutorial, setShowTutorial] = React.useState(() => shouldShowTutorial(state, "status")); const [showFullLedger, setShowFullLedger] = React.useState(false); const [isNarrowStatus, setIsNarrowStatus] = React.useState(window.innerWidth < 700); React.useEffect(() => { const handle = () => setIsNarrowStatus(window.innerWidth < 700); window.addEventListener("resize", handle); return () => window.removeEventListener("resize", handle); }, []); const career = state.career || {}; const daysSurvived = state.day; const portsTotal = Object.keys(PORTS).length; const portsVisitedCount = (career.portsVisited || []).length; const totalBattles = (career.battles?.won || 0) + (career.battles?.lost || 0) + (career.battles?.fled || 0); const totalCrewLost = (career.crewLost?.inBattle || 0) + (career.crewLost?.inStorm || 0) + (career.crewLost?.deserted || 0) + (career.crewLost?.other || 0); const captainTag = L.getCaptainTag(state); const highlights = L.getCareerHighlights(state); const getFactionSummary = (factionKey) => { const ports = Object.entries(PORTS).filter(([_, p]) => p.faction === factionKey); if (ports.length === 0) return null; const avgRep = Math.round( ports.reduce((sum, [k]) => sum + (state.reputation[k] ?? 50), 0) / ports.length ); const repLabel = L.reputationLabel(avgRep); const heat = state.factionAlerts?.[factionKey] || 0; const heatLabel = L.getHeatLabel(heat); const crewOfFaction = (state.crew?.roster || []).filter(m => m.faction === factionKey).length; const totalCrew = (state.crew?.roster || []).length; const crewPct = totalCrew > 0 ? Math.round((crewOfFaction / totalCrew) * 100) : 0; return { avgRep, repLabel, heat, heatLabel, crewOfFaction, totalCrew, crewPct }; }; return (
{showTutorial && ( { markTutorialSeen("status", disableAll); setShowTutorial(false); }}>

This is where your career is tracked — your identity, your deeds, and your standing with the powers of the Caribbean.

The Caribbean keeps a ledger. Your name is written in it.

)} {/* Section 1: Captain Identity */}
Captain
{state.captainName || "Unknown"}
{FACTIONS[state.faction]?.label || "No faction"}
{captainTag.text}
★ {state.fame}
{L.getFameInfo(state.fame).label}
Fame
0 ? T.red : T.textFaint, fontSize: 22, fontWeight: "bold" }}> 0 ? T.red : T.textFaint} /> {state.infamy ?? 0}
{L.getInfamyLabel(state.infamy ?? 0)}
Infamy
{daysSurvived}
days at sea
Tenure
{/* Section 2: Career – title OUTSIDE the panel */}
CAREER
{highlights.map((line, i) => (
{line}
))}
setShowFullLedger(v => !v)} style={{ color: T.textFaint, fontSize: T.captionFontSize, cursor: "pointer", marginTop: 4, padding: 4, borderTop: `1px solid ${T.borderFaint}` }}> {showFullLedger ? "▾ Hide full ledger" : "▸ Show full ledger"}
{showFullLedger && (
Economic
Combat
Crew
World
)}
{/* Section 3: The World's View – title OUTSIDE the panel, left-aligned */}
THE WORLD'S VIEW

How each faction sees you, and how your crew aligns with them.

{Object.entries(FACTIONS).map(([factionKey, fac]) => { const summary = getFactionSummary(factionKey); if (!summary) return null; const { avgRep, repLabel, heat, heatLabel, crewOfFaction, totalCrew } = summary; const repColor = avgRep >= 60 ? T.greenBr : avgRep >= 30 ? T.gold : T.redBr; const isOwnFaction = factionKey === state.faction; // Build sentence – colorize faction name inline const repTier = repLabel.toLowerCase(); const heatTier = heat >= 7 ? "hunted" : heat >= 3 ? "watched" : "clean"; const templateKey = `${repTier}_${heatTier}`; const template = window.D.FACTION_RELATIONSHIP_TEMPLATES?.[templateKey]; let sentence = template ? template(fac.label, avgRep) : `The ${fac.label} regard you with ${repTier} standing.`; // If own faction, replace initial "The [Faction] " with "Your people, the [Faction], " if (isOwnFaction) { sentence = sentence.replace(new RegExp(`^The ${fac.label} `, 'i'), `Your people, the ${fac.label}, `); } // Insert colored span around the faction label in the sentence const coloredFaction = `${fac.label}`; const htmlSentence = sentence.replace(new RegExp(fac.label, 'g'), coloredFaction); return (
{/* Prose – left side, same style as career sentences */}

{/* Stats – right side, stacked vertically */}
Rep: {avgRep} ({repLabel}) {heat > 0 && ( Heat: {heat} ({heatLabel}) )} {totalCrew > 0 && ( Crew: {crewOfFaction} / {totalCrew} )}
); })}

Reputation decays slowly toward neutral (50) over time. Complete missions, aid distressed ships, or parley with faction vessels to improve standing. Attacking their ships will anger all ports of that faction. Heat decays naturally as you stay clear of trouble.

); } // ── JOURNAL SCREEN ────────────────────────────────────────────────── function JournalScreen({ state, dispatch }) { const [filterTab, setFilterTab] = useState("all"); const [search, setSearch] = useState(""); const [showTutorial, setShowTutorial] = React.useState(() => shouldShowTutorial(state,"journal")); const parsed = state.log.map(entry => { const match = entry.match(/^\[(\d+)\]\s*(.*)/); const day = match ? parseInt(match[1], 10) : null; const text = match ? match[2] : entry; return { day, text, raw: entry, tab: L.getLogTabCategory(text) }; }); let filtered = parsed; if (filterTab !== "all") filtered = filtered.filter(e => e.tab === filterTab); if (search.trim()) { const query = search.toLowerCase(); filtered = filtered.filter(e => e.text.toLowerCase().includes(query)); } filtered = [...filtered].reverse(); let lastDay = null; const tabs = [ { key: "all", label: "All" }, { key: "crew", label: "Crew" }, { key: "combat", label: "Combat" }, { key: "ports", label: "Ports" }, { key: "missions", label: "Missions" }, { key: "trade", label: "Trade" }, ]; return (
{showTutorial && ( { markTutorialSeen("journal", disableAll); setShowTutorial(false); }}>

Everything that has happened on this voyage is recorded here — battles, arrivals, crew events, trades, and discoveries.

The journal is the story of your career. The longer you sail, the richer it becomes.

)} CAPTAIN'S JOURNAL

Every storm, every battle, every whispered secret—recorded here for posterity.

{tabs.map(tab => ( setFilterTab(tab.key)}>{tab.label} ))}
setSearch(e.target.value)} style={{ width: "100%", padding: "6px 10px", background: T.panel, border: `1px solid ${T.border}`, color: T.text, borderRadius: 3, fontSize: T.metadataFontSize, fontFamily: T.font, outline: "none" }} /> {search &&
{filtered.length} entr{filtered.length === 1 ? "y" : "ies"} found
}
{filtered.length === 0 ? : ( filtered.map((entry, i) => { const showDay = entry.day !== null && entry.day !== lastDay; lastDay = entry.day; return ( {showDay &&
Day {entry.day}
}
{(() => { const categoryKey = L.classifyLogLine(entry.text); const LOG_ICONS = window.UI.LOG_ICONS || {}; const IconComponent = categoryKey ? LOG_ICONS[categoryKey] : null; return IconComponent ? : null; })()} {entry.text}
); }) )}
); } // ── EXPORTS ────────────────────────────────────────────────────────── Object.assign(window.S, { StatusScreen, JournalScreen, }); })();