diff --git a/src/App.css b/src/App.css index 214fb77..75d250b 100644 --- a/src/App.css +++ b/src/App.css @@ -421,3 +421,161 @@ .btn-new-game:hover { color: #6b5a3e; } + +/* ── Prologue screen ─────────────────────────────────────────────── */ + +.prologue-screen { + max-width: 680px; + margin: 0 auto; + padding: 2rem 1rem 4rem; +} + +.prologue-header { + margin-bottom: 1.75rem; +} + +.prologue-label { + display: block; + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.12em; + color: #6b5a3e; + margin-bottom: 0.4rem; +} + +.prologue-title { + font-size: 1.6rem; + font-weight: normal; + color: #2a1f0e; + letter-spacing: 0.05em; + margin: 0 0 0.4rem; +} + +.prologue-subtitle { + font-size: 0.8rem; + color: #6b5a3e; + font-style: italic; + margin: 0; +} + +/* Selection grid — two columns of cards */ +.prologue-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + margin-bottom: 1.5rem; +} + +.prologue-card { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + background: #faf7f2; + border: 1px solid #c8b89a; + border-radius: 8px; + padding: 0.875rem 1rem; + cursor: pointer; + text-align: left; + transition: border-color 0.15s, background 0.15s; +} + +.prologue-card:hover { + border-color: #8fbc8f; + background: #f0f7f0; +} + +.prologue-card--selected { + border: 2px solid #2d5a2d; + background: #f0f7f0; +} + +.prologue-card-latin { + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #9a8a6e; +} + +.prologue-card-name { + font-size: 0.9rem; + font-weight: bold; + color: #2a1f0e; +} + +.prologue-card-summary { + font-size: 0.73rem; + color: #6b5a3e; + line-height: 1.5; +} + +.prologue-card-den { + font-size: 0.72rem; + color: #2d5a2d; + font-weight: bold; + margin-top: 2px; +} + +/* Confirm row */ +.prologue-confirm-row { + display: flex; + justify-content: flex-end; +} + +.prologue-confirm { + padding: 0.75rem 1.5rem; + border-radius: 6px; + border: 1px solid #2d5a2d; + background: #faf7f2; + color: #2d5a2d; + font-size: 0.85rem; + font-weight: bold; + cursor: pointer; + transition: background 0.15s; +} + +.prologue-confirm:hover:not(:disabled) { + background: #d4e8d4; +} + +.prologue-confirm:disabled { + opacity: 0.4; + cursor: not-allowed; + border-color: #c8b89a; + color: #6b5a3e; +} + +/* Read-only chosen view */ +.prologue-chosen { + /* inherits .prologue-screen padding */ +} + +.prologue-chosen-name { + font-size: 1.4rem; + font-weight: normal; + color: #2a1f0e; + letter-spacing: 0.05em; + margin: 0 0 0.2rem; +} + +.prologue-chosen-latin { + display: block; + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #9a8a6e; + margin-bottom: 1rem; +} + +.prologue-chosen-summary { + font-size: 0.85rem; + color: #6b5a3e; + line-height: 1.7; + font-style: italic; + margin-bottom: 0.75rem; +} + +.prologue-chosen-den { + font-size: 0.78rem; + color: #2a1f0e; +} diff --git a/src/App.jsx b/src/App.jsx index 2c166bd..45da7e0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,10 @@ import { useState, useEffect } from 'react' import { generateToken, loadState, saveState } from './api.js' import { createState } from './gameState.js' +import { BACKGROUNDS } from './constants.js' import Ledger from './screens/Ledger.jsx' import Map from './screens/Map.jsx' +import Prologue from './screens/Prologue.jsx' import './App.css' const TOKEN_KEY = 'otivm_token' @@ -11,7 +13,7 @@ export default function App() { const [state, setState] = useState(null) const [token, setToken] = useState(null) const [loading, setLoading] = useState(true) - const [screen, setScreen] = useState('ledger') + const [screen, setScreen] = useState('prologue') useEffect(() => { async function bootstrap() { @@ -24,10 +26,13 @@ export default function App() { const saved = await loadState(tok) if (saved) { setState(saved) + // If background already chosen, open on ledger + setScreen(saved.background_id ? 'ledger' : 'prologue') } else { const fresh = createState(tok) setState(fresh) await saveState(tok, fresh) + setScreen('prologue') } setLoading(false) } @@ -39,6 +44,21 @@ export default function App() { await saveState(token, newState) } + // Called from Prologue when player confirms a background. + // Seeds background_id into state, saves, switches to Ledger. + async function onSelectBackground(backgroundId) { + const bg = BACKGROUNDS.find(b => b.id === backgroundId) + if (!bg) return + const updated = { + ...state, + background_id: backgroundId, + den: bg.starting_den, + } + setState(updated) + await saveState(token, updated) + setScreen('ledger') + } + // Session abandonment — forward-looking lifecycle handler. // Does not delete the old save. Appends a terminal event so the record // is complete. The old save becomes a historical artefact on disk. @@ -62,7 +82,7 @@ export default function App() { const fresh = createState(newTok) setState(fresh) await saveState(newTok, fresh) - setScreen('ledger') + setScreen('prologue') } if (loading) { @@ -78,6 +98,12 @@ export default function App() {