Files
Dune/components/introduction.module.css
T
Quintine 9be7dfb9fb Add an interactive Basic-game introduction
New players can open Learn to play from the lobby or rules reference. Five lessons explain public/private information and turn phases, then offer three independent exercises for ordinary shipment budgets, a sealed battle plan and spice collection, followed by joining and recovering a real table. Shipping, combat and collection use existing production quotes rather than a separate simplified calculator. The fixed opposing plan is a teaching example, not an AI match.

Reuse the battle wheel and card inspectors, provide keyboard inputs, move focus to the current lesson heading and stack the layout on phones. Versioned browser-local choices preserve lesson position, shipment, defense/dial and reveal state. Whitelisted validation rejects corrupt saves and impossible committed shipments. SSR uses a stable hydration placeholder before reading browser storage; direct user handlers save choices and retain usable in-memory state on storage failure. No room, seat credential or multiplayer API is used.

Six new tests cover prices and affordability, every offered battle dial/defense, protected ties, weapon death, actual losses/bounty, collection limits and city capacity, saved-choice validation and internal rule links. Types, lint, 4872 offline tests and production build pass on the unchanged source fingerprint below. The first required check stopped on React setState-in-effect and semantic-status lint findings; these were fixed before the successful complete run. Independent review found no material defect in the examples or final hydration/save implementation.

Browser controls verified an unaffordable shipment, legal six-force shipment, card inspection, protected 8-8 aggressor victory, Snooper/projectile death and bounty, sealed refresh, city collection, lobby return/re-entry and newly changed choices after the hydration correction. At 390px the battle controls and lobby Learn link stayed within page bounds; the viewport override was reset. Screenshot capture timed out, so visual screenshot acceptance is still open. Storage-denial browser testing and the broader faction, movement, bidding, alliance, Advanced and expansion curriculum remain unfinished.

All 1106 current rooms, 2314 seats and all original recovery/entry/handover rows exactly match the fresh outage backup. SQLite integrity and foreign-key checks pass. The healthy server was reused; no restart, database migration/reset or maintenance automation was needed. HTTP integration and full-game samples were not rerun for this isolated teaching surface; server game behavior did not change.

A bounded independent primary-source review supports Smuggler's reveal-bound deferred survival receipt but found no exact ruling for modified leader strength. Record the one new user question and preserve the unanswered gate; lower collection remains missing. This does not enable any rules mode, expansion or publication gate.

Verified source tree: 99718ab49c4fec14820cbaf35f2e0f815e6424e910ee67c4daa1941afdeca2f0
2026-09-20 14:25:58 +10:00

34 lines
2.8 KiB
CSS

.page { max-width: 1120px; margin: 0 auto; padding: 2rem clamp(1rem, 4vw, 3rem) 4rem; color: #e9e2cf; }
.header { max-width: 760px; margin-bottom: 1.5rem; }
.header h1 { font-family: var(--font-display), Georgia, serif; font-size: clamp(2rem, 5vw, 3.6rem); line-height: 1.1; margin: .7rem 0 1rem; }
.page p, .page li { line-height: 1.65; }
.page p { margin: .8rem 0; }
.page a { color: #e8c982; text-underline-offset: .2em; }
.page a:hover { text-decoration: underline; }
.steps { display: flex; gap: .5rem; flex-wrap: wrap; margin: 1.5rem 0; }
.page button { min-height: 44px; padding: .6rem .9rem; background: #252d23; color: #eee6cf; border: 1px solid #778064; border-radius: .4rem; cursor: pointer; }
.page button:hover:enabled { background: #374232; }
.page button:disabled { opacity: .5; cursor: default; }
.page :is(button, a, input, select):focus-visible { outline: 3px solid #efd294; outline-offset: 3px; }
.steps button { flex: 1 1 155px; text-align: left; display: flex; gap: .6rem; align-items: center; }
.steps button[aria-current] { background: #cdb177; color: #171d16; border-color: #f3d79b; }
.steps span { font-variant-numeric: tabular-nums; }
.lesson { background: #151d16; border: 1px solid #58604a; border-radius: .8rem; padding: clamp(1rem, 3vw, 2rem); }
.lesson h2 { font-family: var(--font-display), Georgia, serif; font-size: 1.8rem; line-height: 1.2; margin: .6rem 0 1.2rem; }
.lesson h3 { font-size: 1.05rem; color: #ecd29b; margin: .5rem 0; font-weight: 650; }
.lesson ul, .phases { padding-left: 1.3rem; }
.phases { columns: 2; margin: 1rem 0; }
.columns { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.5rem; margin: 1.5rem 0; }
.controls { display: flex; flex-direction: column; gap: .8rem; min-width: 0; }
.controls select { background: #202a21; color: #eee6cf; border: 1px solid #899072; border-radius: .3rem; min-height: 44px; padding: .5rem; width: 100%; }
.controls input { accent-color: #d8b777; }
.controls input[type=range] { width: 100%; min-height: 44px; }
.checkbox { display: flex; align-items: center; gap: .7rem; min-height: 44px; }
.checkbox input { width: 20px; height: 20px; flex-shrink: 0; }
.wheel { max-width: 250px; margin: 0 auto; width: 100%; }
.result { background: #20291f; border: 1px solid #637052; border-radius: .6rem; padding: 1rem; min-width: 0; align-self: start; }
.footer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; border-top: 1px solid #454f3b; margin-top: 2rem; padding-top: 1.2rem; flex-wrap: wrap; }
.links { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-top: 1.5rem; }
.saved { display: block; margin-top: .8rem; color: #b8c0a7; font-size: .85rem; }
@media (max-width: 650px) { .columns { grid-template-columns: minmax(0, 1fr); } .steps button { flex-basis: 45%; font-size: .9rem; } .footer { align-items: stretch; } .footer a { flex-basis: 100%; } }