feat(guide): Add fantasy-themed UI styling and supporting theme preferences, data, and simulation logic updates

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-01 05:24:01 -07:00
parent ce654d9937
commit 75dee42955
6 changed files with 173 additions and 145 deletions

View file

@ -0,0 +1,15 @@
// Out-of-scope race name entries — removed from Age of Dwarves (1-race game)
// Restore when multi-race support is added to the guide.
import type { ConcreteRace, ConcreteGender } from '@/contexts/PreferencesContext'
type NamesKey = `${ConcreteRace}_${ConcreteGender}`
type OutOfScopeNamesKey = `${'high_elves' | 'humans' | 'orcs'}_${'male' | 'female'}`
export const RULER_NAMES_OTHER_RACES: Record<OutOfScopeNamesKey, string[]> = {
high_elves_male: ['Aelir', 'Baelon', 'Caerin', 'Daemir', 'Eoniel', 'Faelion', 'Galior', 'Halion', 'Ilven', 'Jaerin', 'Kaelen', 'Lorian', 'Maelis', 'Naelen', 'Paelin', 'Raerin', 'Saelon', 'Taelis', 'Urion', 'Vaelis', 'Aetheron', 'Stellior', 'Luminor', 'Astraen', 'Aelonir', 'Belorian', 'Caelimar', 'Dalorin', 'Elorian', 'Faelior', 'Galorin', 'Halonis', 'Ilmorin', 'Jaelior', 'Kaeliros', 'Lorenia', 'Maelion', 'Naelios', 'Orelion', 'Olerison'],
high_elves_female: ['Aelwen', 'Silara', 'Vespiel', 'Luniel', 'Stariel', 'Thalwen', 'Ilmarin', 'Celestrae', 'Lyrien', 'Astraen', 'Elawen', 'Sylmara', 'Maelien', 'Taellis', 'Rosalen', 'Alessiel', 'Naelith', 'Vaelwin', 'Islaren', 'Aelora', 'Sylaine', 'Mairel', 'Lirael', 'Alawen', 'Saelian', 'Thalien', 'Aurelis', 'Myrrhael', 'Cirael', 'Faelwen', 'Gweniel', 'Halwen', 'Ioelwyn', 'Kaelith', 'Laewen', 'Naeriel', 'Orawen', 'Pelalyn', 'Qaelen', 'Raelis'],
humans_male: ['Ansgar', 'Arnulf', 'Baldric', 'Baldwin', 'Bertrand', 'Brennan', 'Chadwick', 'Collin', 'Corwin', 'Darrell', 'Desmond', 'Dominic', 'Donovan', 'Douglas', 'Durwin', 'Edmond', 'Edwin', 'Emeric', 'Emmett', 'Erasmus', 'Erwin', 'Everard', 'Everett', 'Ewart', 'Farrin', 'Fitzroy', 'Florian', 'Garrick', 'Gilbert', 'Godwin', 'Godfrey', 'Gregory', 'Gideon', 'Hadwin', 'Harold', 'Hartwin', 'Heinrich', 'Henry', 'Holger', 'Hugo'],
humans_female: ['Audra', 'Dagny', 'Freya', 'Gisla', 'Isla', 'Keira', 'Lyra', 'Mara', 'Nora', 'Petra', 'Raven', 'Sylva', 'Thora', 'Adelaide', 'Beatrice', 'Clarissa', 'Cordelia', 'Delilah', 'Elowen', 'Evelyn', 'Fiona', 'Harriet', 'Nathalie', 'Ophelia', 'Rosalind', 'Selena', 'Yolanda', 'Alessandra', 'Arabella', 'Cecilia', 'Dominica', 'Emilia', 'Gloriana', 'Hermione', 'Isadora', 'Magdalena', 'Nicoletta', 'Octavia', 'Theodora', 'Valentina'],
orcs_male: ['Gromas', 'Thaxus', 'Brugar', 'Khargos', 'Grathak', 'Skragger', 'Craghorn', 'Thorgrim', 'Mordak', 'Gormath', 'Kragul', 'Shargra', 'Krogar', 'Drakul', 'Glurghak', 'Braxus', 'Khragos', 'Grindak', 'Tharkal', 'Shartak', 'Krugash', 'Gothrax', 'Brakgosh', 'Drogmar', 'Grachak', 'Shrogrim', 'Krogath', 'Thrugash', 'Brukthar', 'Gorkash', 'Morgath', 'Shakgrim', 'Kragosh', 'Gorathak', 'Kramorgul', 'Thrubakash', 'Drakogrim', 'Brugthorkak', 'Shakgrimthor', 'Kraalgathak'],
orcs_female: ['Thraka', 'Kheshka', 'Vorgha', 'Durshka', 'Nakhka', 'Zhara', 'Greshka', 'Morkha', 'Talka', 'Shrakha', 'Vreskha', 'Gorna', 'Kasha', 'Drakha', 'Ruthka', 'Shankha', 'Vorhka', 'Grastra', 'Shorka', 'Skahra', 'Brogha', 'Kershka', 'Nahla', 'Zhukha', 'Graska', 'Vorghka', 'Tharkha', 'Shakha', 'Krogha', 'Murka', 'Droshka', 'Vaska', 'Kharka', 'Shogha', 'Trakhka', 'Zaharaka', 'Greshkha', 'Orhka', 'Vrakha', 'Shurka'],
}

View file

@ -0,0 +1,141 @@
// Out-of-scope race theme entries — removed from Age of Dwarves (1-race game)
// Restore when multi-race support is added to the guide.
// ─── THEME_COLORS (dark mode) — non-dwarf races ─────────────────────────────
const THEME_COLORS_OTHER_RACES = {
humans_male: {
primary: { main: '#c9a84c', dark: '#a07830', light: '#e8c87a' },
accent: { main: '#8b1a1a', dark: '#6a1010', light: '#b23030' },
},
humans_female: {
primary: { main: '#b09ab8', dark: '#806888', light: '#d0c0d8' },
accent: { main: '#8b3a5a', dark: '#6a2040', light: '#b25878' },
},
high_elves_male: {
primary: { main: '#4ca8a0', dark: '#307870', light: '#7ad4cc' },
accent: { main: '#2a5a8b', dark: '#1a3a6a', light: '#4080b2' },
},
high_elves_female: {
primary: { main: '#c76b98', dark: '#9a4870', light: '#e890b8' },
accent: { main: '#6a3a8b', dark: '#4a2068', light: '#8a58b0' },
},
orcs_male: {
primary: { main: '#b03030', dark: '#7a1818', light: '#d45050' },
accent: { main: '#8b4a1a', dark: '#6a3010', light: '#b06830' },
},
orcs_female: {
primary: { main: '#5a8848', dark: '#386028', light: '#7aaa60' },
accent: { main: '#8b7848', dark: '#6a5830', light: '#b09a60' },
},
}
// ─── THEME_COLORS_LIGHT — non-dwarf races ───────────────────────────────────
const THEME_COLORS_LIGHT_OTHER_RACES = {
humans_male: {
primary: { main: '#8a6008', dark: '#6a4800', light: '#b08828' },
accent: { main: '#881818', dark: '#680808', light: '#a83030' },
},
humans_female: {
primary: { main: '#705878', dark: '#503860', light: '#907098' },
accent: { main: '#7a2848', dark: '#5a1028', light: '#9a4868' },
},
high_elves_male: {
primary: { main: '#1a7868', dark: '#0a5848', light: '#3a9888' },
accent: { main: '#1a3a7a', dark: '#0a2060', light: '#3a609a' },
},
high_elves_female: {
primary: { main: '#8a3868', dark: '#6a1848', light: '#aa5888' },
accent: { main: '#4a2078', dark: '#2a0858', light: '#6a4098' },
},
orcs_male: {
primary: { main: '#881818', dark: '#680808', light: '#a83030' },
accent: { main: '#7a3008', dark: '#5a1800', light: '#9a5028' },
},
orcs_female: {
primary: { main: '#386028', dark: '#184008', light: '#588848' },
accent: { main: '#6a5828', dark: '#4a3808', light: '#8a7848' },
},
}
// ─── DARK_BASES — non-dwarf races ───────────────────────────────────────────
const DARK_BASES_OTHER_RACES = {
humans: {
background: { primary: '#1a1610', secondary: '#221e16', tertiary: '#2a2418' },
surface: '#2a2418',
border: { default: '#3d3220', hover: '#5a4a30' },
text: { primary: '#f0e8d0', secondary: '#b8a882', muted: '#7a6a50', tertiary: '#7a6a50' },
},
high_elves: {
background: { primary: '#101a18', secondary: '#16221f', tertiary: '#1c2a26' },
surface: '#1c2a26',
border: { default: '#203d38', hover: '#305a50' },
text: { primary: '#d8f0ec', secondary: '#88b8a8', muted: '#507a6a', tertiary: '#507a6a' },
},
orcs: {
background: { primary: '#1a1010', secondary: '#221414', tertiary: '#2a1a18' },
surface: '#2a1a18',
border: { default: '#3d2020', hover: '#5a3028' },
text: { primary: '#f0dcd0', secondary: '#b88878', muted: '#7a5048', tertiary: '#7a5048' },
},
}
// ─── LIGHT_BASES — non-dwarf races ──────────────────────────────────────────
const LIGHT_BASES_OTHER_RACES = {
humans: {
background: { primary: '#faf6ee', secondary: '#f2ece0', tertiary: '#e8e0d0' },
surface: '#f2ece0',
border: { default: '#d0c8b0', hover: '#b0a888' },
text: { primary: '#2a2418', secondary: '#5a5040', muted: '#8a7a60', tertiary: '#8a7a60' },
},
high_elves: {
background: { primary: '#f0faf8', secondary: '#e4f2ee', tertiary: '#d4e8e2' },
surface: '#e4f2ee',
border: { default: '#a8d0c8', hover: '#80b0a0' },
text: { primary: '#182a26', secondary: '#3a5a50', muted: '#608878', tertiary: '#608878' },
},
orcs: {
background: { primary: '#faf0ee', secondary: '#f2e4e0', tertiary: '#e8d4d0' },
surface: '#f2e4e0',
border: { default: '#d0b0a8', hover: '#b08880' },
text: { primary: '#2a1818', secondary: '#5a3838', muted: '#8a5858', tertiary: '#8a5858' },
},
}
// ─── RACE_FONTS — non-dwarf races ───────────────────────────────────────────
const RACE_FONTS_OTHER_RACES = {
high_elves: {
maleHeading: "'Cinzel Decorative', serif",
femaleHeading: "'Cormorant Garamond', serif",
maleBody: "'EB Garamond', serif",
femaleBody: "'Cormorant Garamond', serif",
maleHeadingWeight: 900,
femaleHeadingWeight: 400,
maleBodyWeight: 600,
femaleBodyWeight: 300,
},
humans: {
maleHeading: "'Cinzel', serif",
femaleHeading: "'EB Garamond', serif",
maleBody: "'Bitter', serif",
femaleBody: "'EB Garamond', serif",
maleHeadingWeight: 800,
femaleHeadingWeight: 500,
maleBodyWeight: 600,
femaleBodyWeight: 400,
},
orcs: {
maleHeading: "'Metal Mania', cursive",
femaleHeading: "'Spectral', serif",
maleBody: "'Spectral', serif",
femaleBody: "'Spectral', serif",
maleHeadingWeight: 400,
femaleHeadingWeight: 400,
maleBodyWeight: 700,
femaleBodyWeight: 300,
},
}

View file

@ -9,6 +9,9 @@ import { test, expect } from '@playwright/test'
* Run with:
* npx playwright test
*
* All tests use ?totalTurns=50&buffer=0 so the simulation completes in seconds
* rather than waiting for the default 2000 turns + 10s prebuffer.
*
* DOM structure of the loading overlay (from live snapshot):
* step-row
* icon-div ("✓" | "◉" | "○")
@ -17,10 +20,15 @@ import { test, expect } from '@playwright/test'
* desc-div (description or turn progress)
* [pct-div] ("4%" only when simulation is active)
*
* Note: "Playback buffer" never shows it's either active () or pending ().
* When bufferReady becomes true the entire loading overlay is replaced by the canvas.
*
* Selectors use :text-is("✓") to precisely match the icon element (exact text),
* then navigate to its parent (the step row) and filter by step name.
*/
const BASE_URL = '/climate/simulation?noGui=true&skip=welcome&totalTurns=50&buffer=0'
test.describe('Climate simulator', () => {
test('starts without console errors', async ({ page }) => {
const errors: string[] = []
@ -29,7 +37,7 @@ test.describe('Climate simulator', () => {
})
page.on('pageerror', err => errors.push(err.message))
await page.goto('/climate/simulation?noGui=true&skip=welcome')
await page.goto(BASE_URL)
await page.waitForTimeout(3000)
expect(errors, `Console errors:\n${errors.join('\n')}`).toHaveLength(0)
@ -42,7 +50,7 @@ test.describe('Climate simulator', () => {
})
page.on('pageerror', err => errors.push(err.message))
await page.goto('/climate/simulation?noGui=true&skip=welcome')
await page.goto(BASE_URL)
// :text-is("✓") matches only elements whose full text is exactly ✓ (the icon divs)
// .locator('..') gets the parent step-row div
@ -61,9 +69,9 @@ test.describe('Climate simulator', () => {
})
page.on('pageerror', err => errors.push(err.message))
await page.goto('/climate/simulation?noGui=true&skip=welcome')
await page.goto(BASE_URL)
// The turn counter "Turn N / M,000" appears only when simulation is active
// The turn counter "Turn N / M" appears only when scenario simulation is active
await expect(
page.locator('text=/Turn \\d+/')
).toBeVisible({ timeout: 60_000 })
@ -78,14 +86,12 @@ test.describe('Climate simulator', () => {
})
page.on('pageerror', err => errors.push(err.message))
await page.goto('/climate/simulation?noGui=true&skip=welcome')
await page.goto(BASE_URL)
// All 3 steps must show ✓ (world gen + scenario sim + playback buffer)
await expect(page.locator(':text-is("✓")')).toHaveCount(3, { timeout: 120_000 })
// Canvas must be rendered and sized
// When bufferReady becomes true the loading overlay is replaced by the WebGL canvas.
// canvas is only rendered once a frame is delivered — wait for it to be visible.
const canvas = page.locator('canvas').first()
await expect(canvas).toBeVisible({ timeout: 10_000 })
await expect(canvas).toBeVisible({ timeout: 60_000 })
const box = await canvas.boundingBox()
expect(box, 'canvas has no bounding box').not.toBeNull()
expect(box!.width, 'canvas width').toBeGreaterThan(0)

View file

@ -23,7 +23,7 @@ export interface ResolvedPreferences {
// ─── Resolution helpers ─────────────────────────────────────────────────────
const CONCRETE_RACES: ConcreteRace[] = ['high_elves', 'humans', 'dwarves', 'orcs']
const CONCRETE_RACES: ConcreteRace[] = ['dwarves']
const CONCRETE_GENDERS: ConcreteGender[] = ['male', 'female']
function pickRandom<T>(arr: readonly T[]): T {

View file

@ -5,12 +5,6 @@ import type { ConcreteRace, ConcreteGender } from '@/contexts/PreferencesContext
export type NamesKey = `${ConcreteRace}_${ConcreteGender}`
export const RULER_NAMES: Record<NamesKey, string[]> = {
high_elves_male: ['Aelir', 'Baelon', 'Caerin', 'Daemir', 'Eoniel', 'Faelion', 'Galior', 'Halion', 'Ilven', 'Jaerin', 'Kaelen', 'Lorian', 'Maelis', 'Naelen', 'Paelin', 'Raerin', 'Saelon', 'Taelis', 'Urion', 'Vaelis', 'Aetheron', 'Stellior', 'Luminor', 'Astraen', 'Aelonir', 'Belorian', 'Caelimar', 'Dalorin', 'Elorian', 'Faelior', 'Galorin', 'Halonis', 'Ilmorin', 'Jaelior', 'Kaeliros', 'Lorenia', 'Maelion', 'Naelios', 'Orelion', 'Olerison'],
high_elves_female: ['Aelwen', 'Silara', 'Vespiel', 'Luniel', 'Stariel', 'Thalwen', 'Ilmarin', 'Celestrae', 'Lyrien', 'Astraen', 'Elawen', 'Sylmara', 'Maelien', 'Taellis', 'Rosalen', 'Alessiel', 'Naelith', 'Vaelwin', 'Islaren', 'Aelora', 'Sylaine', 'Mairel', 'Lirael', 'Alawen', 'Saelian', 'Thalien', 'Aurelis', 'Myrrhael', 'Cirael', 'Faelwen', 'Gweniel', 'Halwen', 'Ioelwyn', 'Kaelith', 'Laewen', 'Naeriel', 'Orawen', 'Pelalyn', 'Qaelen', 'Raelis'],
humans_male: ['Ansgar', 'Arnulf', 'Baldric', 'Baldwin', 'Bertrand', 'Brennan', 'Chadwick', 'Collin', 'Corwin', 'Darrell', 'Desmond', 'Dominic', 'Donovan', 'Douglas', 'Durwin', 'Edmond', 'Edwin', 'Emeric', 'Emmett', 'Erasmus', 'Erwin', 'Everard', 'Everett', 'Ewart', 'Farrin', 'Fitzroy', 'Florian', 'Garrick', 'Gilbert', 'Godwin', 'Godfrey', 'Gregory', 'Gideon', 'Hadwin', 'Harold', 'Hartwin', 'Heinrich', 'Henry', 'Holger', 'Hugo'],
humans_female: ['Audra', 'Dagny', 'Freya', 'Gisla', 'Isla', 'Keira', 'Lyra', 'Mara', 'Nora', 'Petra', 'Raven', 'Sylva', 'Thora', 'Adelaide', 'Beatrice', 'Clarissa', 'Cordelia', 'Delilah', 'Elowen', 'Evelyn', 'Fiona', 'Harriet', 'Nathalie', 'Ophelia', 'Rosalind', 'Selena', 'Yolanda', 'Alessandra', 'Arabella', 'Cecilia', 'Dominica', 'Emilia', 'Gloriana', 'Hermione', 'Isadora', 'Magdalena', 'Nicoletta', 'Octavia', 'Theodora', 'Valentina'],
dwarves_male: ['Thorgrim', 'Braktor', 'Grunnov', 'Kragdun', 'Durnax', 'Stonek', 'Brendak', 'Gromul', 'Kragan', 'Barlok', 'Storkrim', 'Drakton', 'Krelak', 'Brungar', 'Thorak', 'Grantok', 'Skandir', 'Dregok', 'Krogun', 'Brixar', 'Threndel', 'Gronakin', 'Krangil', 'Sturmark', 'Brenthak', 'Grindorn', 'Thorkin', 'Durnokrim', 'Kragrim', 'Brektok', 'Stonarl', 'Grothak', 'Kreltor', 'Brudarkon', 'Thraxon', 'Gromask', 'Krantol', 'Durgan', 'Brenwick', 'Thorkol'],
dwarves_female: ['Sigrid', 'Solveig', 'Brynhild', 'Torild', 'Yrsa', 'Hilda', 'Saga', 'Vigdis', 'Dagny', 'Frida', 'Signe', 'Eira', 'Sigrun', 'Solrun', 'Gunnhild', 'Alfhild', 'Ragnhild', 'Hrafnhild', 'Petra', 'Helle', 'Steinvor', 'Gerda', 'Skadi', 'Lilja', 'Audrun', 'Stina', 'Tora', 'Britta', 'Silje', 'Jorun', 'Bodil', 'Bodrun', 'Gyda', 'Liva', 'Kristin', 'Kara', 'Vada', 'Stilla', 'Valdis', 'Runa'],
orcs_male: ['Gromas', 'Thaxus', 'Brugar', 'Khargos', 'Grathak', 'Skragger', 'Craghorn', 'Thorgrim', 'Mordak', 'Gormath', 'Kragul', 'Shargra', 'Krogar', 'Drakul', 'Glurghak', 'Braxus', 'Khragos', 'Grindak', 'Tharkal', 'Shartak', 'Krugash', 'Gothrax', 'Brakgosh', 'Drogmar', 'Grachak', 'Shrogrim', 'Krogath', 'Thrugash', 'Brukthar', 'Gorkash', 'Morgath', 'Shakgrim', 'Kragosh', 'Gorathak', 'Kramorgul', 'Thrubakash', 'Drakogrim', 'Brugthorkak', 'Shakgrimthor', 'Kraalgathak'],
orcs_female: ['Thraka', 'Kheshka', 'Vorgha', 'Durshka', 'Nakhka', 'Zhara', 'Greshka', 'Morkha', 'Talka', 'Shrakha', 'Vreskha', 'Gorna', 'Kasha', 'Drakha', 'Ruthka', 'Shankha', 'Vorhka', 'Grastra', 'Shorka', 'Skahra', 'Brogha', 'Kershka', 'Nahla', 'Zhukha', 'Graska', 'Vorghka', 'Tharkha', 'Shakha', 'Krogha', 'Murka', 'Droshka', 'Vaska', 'Kharka', 'Shogha', 'Trakhka', 'Zaharaka', 'Greshkha', 'Orhka', 'Vrakha', 'Shurka'],
}

View file

@ -21,27 +21,6 @@ type ThemeKey = `${ConcreteRace}_${ConcreteGender}`
// ─── Color palettes by race + gender (dark mode) ────────────────────────────
const THEME_COLORS: Record<ThemeKey, ThemeColors> = {
// ── Humans ──────────────────────────────────────────────────────────────
humans_male: {
primary: { main: '#c9a84c', dark: '#a07830', light: '#e8c87a' },
accent: { main: '#8b1a1a', dark: '#6a1010', light: '#b23030' },
},
humans_female: {
primary: { main: '#b09ab8', dark: '#806888', light: '#d0c0d8' },
accent: { main: '#8b3a5a', dark: '#6a2040', light: '#b25878' },
},
// ── High Elves ──────────────────────────────────────────────────────────
high_elves_male: {
primary: { main: '#4ca8a0', dark: '#307870', light: '#7ad4cc' },
accent: { main: '#2a5a8b', dark: '#1a3a6a', light: '#4080b2' },
},
high_elves_female: {
primary: { main: '#c76b98', dark: '#9a4870', light: '#e890b8' },
accent: { main: '#6a3a8b', dark: '#4a2068', light: '#8a58b0' },
},
// ── Dwarves ─────────────────────────────────────────────────────────────
dwarves_male: {
primary: { main: '#c07040', dark: '#8a4a28', light: '#e09868' },
accent: { main: '#8b6a1a', dark: '#6a5010', light: '#b29030' },
@ -50,43 +29,12 @@ const THEME_COLORS: Record<ThemeKey, ThemeColors> = {
primary: { main: '#8a60a8', dark: '#623888', light: '#b088d0' },
accent: { main: '#5a3878', dark: '#3a2058', light: '#7a5098' },
},
// ── Orcs ────────────────────────────────────────────────────────────────
orcs_male: {
primary: { main: '#b03030', dark: '#7a1818', light: '#d45050' },
accent: { main: '#8b4a1a', dark: '#6a3010', light: '#b06830' },
},
orcs_female: {
primary: { main: '#5a8848', dark: '#386028', light: '#7aaa60' },
accent: { main: '#8b7848', dark: '#6a5830', light: '#b09a60' },
},
}
// ─── Color palettes by race + gender (light mode) ───────────────────────────
// Deeper and more saturated — same hue families, reads clearly on pale backgrounds
const THEME_COLORS_LIGHT: Record<ThemeKey, ThemeColors> = {
// ── Humans ──────────────────────────────────────────────────────────────
humans_male: {
primary: { main: '#8a6008', dark: '#6a4800', light: '#b08828' },
accent: { main: '#881818', dark: '#680808', light: '#a83030' },
},
humans_female: {
primary: { main: '#705878', dark: '#503860', light: '#907098' },
accent: { main: '#7a2848', dark: '#5a1028', light: '#9a4868' },
},
// ── High Elves ──────────────────────────────────────────────────────────
high_elves_male: {
primary: { main: '#1a7868', dark: '#0a5848', light: '#3a9888' },
accent: { main: '#1a3a7a', dark: '#0a2060', light: '#3a609a' },
},
high_elves_female: {
primary: { main: '#8a3868', dark: '#6a1848', light: '#aa5888' },
accent: { main: '#4a2078', dark: '#2a0858', light: '#6a4098' },
},
// ── Dwarves ─────────────────────────────────────────────────────────────
dwarves_male: {
primary: { main: '#8a4818', dark: '#6a2800', light: '#aa6838' },
accent: { main: '#6a5008', dark: '#4a3800', light: '#8a7028' },
@ -95,74 +43,28 @@ const THEME_COLORS_LIGHT: Record<ThemeKey, ThemeColors> = {
primary: { main: '#5a3888', dark: '#3a1868', light: '#7a58a8' },
accent: { main: '#3a1858', dark: '#200838', light: '#5a3878' },
},
// ── Orcs ────────────────────────────────────────────────────────────────
orcs_male: {
primary: { main: '#881818', dark: '#680808', light: '#a83030' },
accent: { main: '#7a3008', dark: '#5a1800', light: '#9a5028' },
},
orcs_female: {
primary: { main: '#386028', dark: '#184008', light: '#588848' },
accent: { main: '#6a5828', dark: '#4a3808', light: '#8a7848' },
},
}
// ─── Race-tinted dark backgrounds ───────────────────────────────────────────
const DARK_BASES: Record<ConcreteRace, ThemeBase> = {
humans: {
background: { primary: '#1a1610', secondary: '#221e16', tertiary: '#2a2418' },
surface: '#2a2418',
border: { default: '#3d3220', hover: '#5a4a30' },
text: { primary: '#f0e8d0', secondary: '#b8a882', muted: '#7a6a50', tertiary: '#7a6a50' },
},
high_elves: {
background: { primary: '#101a18', secondary: '#16221f', tertiary: '#1c2a26' },
surface: '#1c2a26',
border: { default: '#203d38', hover: '#305a50' },
text: { primary: '#d8f0ec', secondary: '#88b8a8', muted: '#507a6a', tertiary: '#507a6a' },
},
dwarves: {
background: { primary: '#1a1410', secondary: '#221a14', tertiary: '#2a2018' },
surface: '#2a2018',
border: { default: '#3d2e1a', hover: '#5a4428' },
text: { primary: '#f0e4d0', secondary: '#b8a078', muted: '#7a6048', tertiary: '#7a6048' },
},
orcs: {
background: { primary: '#1a1010', secondary: '#221414', tertiary: '#2a1a18' },
surface: '#2a1a18',
border: { default: '#3d2020', hover: '#5a3028' },
text: { primary: '#f0dcd0', secondary: '#b88878', muted: '#7a5048', tertiary: '#7a5048' },
},
}
// ─── Light mode bases ───────────────────────────────────────────────────────
const LIGHT_BASES: Record<ConcreteRace, ThemeBase> = {
humans: {
background: { primary: '#faf6ee', secondary: '#f2ece0', tertiary: '#e8e0d0' },
surface: '#f2ece0',
border: { default: '#d0c8b0', hover: '#b0a888' },
text: { primary: '#2a2418', secondary: '#5a5040', muted: '#8a7a60', tertiary: '#8a7a60' },
},
high_elves: {
background: { primary: '#f0faf8', secondary: '#e4f2ee', tertiary: '#d4e8e2' },
surface: '#e4f2ee',
border: { default: '#a8d0c8', hover: '#80b0a0' },
text: { primary: '#182a26', secondary: '#3a5a50', muted: '#608878', tertiary: '#608878' },
},
dwarves: {
background: { primary: '#faf4ee', secondary: '#f2e8dc', tertiary: '#e8dcc8' },
surface: '#f2e8dc',
border: { default: '#d0bca0', hover: '#b09878' },
text: { primary: '#2a2018', secondary: '#5a4838', muted: '#8a7058', tertiary: '#8a7058' },
},
orcs: {
background: { primary: '#faf0ee', secondary: '#f2e4e0', tertiary: '#e8d4d0' },
surface: '#f2e4e0',
border: { default: '#d0b0a8', hover: '#b08880' },
text: { primary: '#2a1818', secondary: '#5a3838', muted: '#8a5858', tertiary: '#8a5858' },
},
}
// ─── Race font config (per gender) ──────────────────────────────────────────
@ -180,26 +82,6 @@ interface RaceFontConfig {
}
const RACE_FONTS: Record<ConcreteRace, RaceFontConfig> = {
high_elves: {
maleHeading: "'Cinzel Decorative', serif",
femaleHeading: "'Cormorant Garamond', serif",
maleBody: "'EB Garamond', serif",
femaleBody: "'Cormorant Garamond', serif",
maleHeadingWeight: 900,
femaleHeadingWeight: 400,
maleBodyWeight: 600,
femaleBodyWeight: 300,
},
humans: {
maleHeading: "'Cinzel', serif",
femaleHeading: "'EB Garamond', serif",
maleBody: "'Bitter', serif",
femaleBody: "'EB Garamond', serif",
maleHeadingWeight: 800,
femaleHeadingWeight: 500,
maleBodyWeight: 600,
femaleBodyWeight: 400,
},
dwarves: {
maleHeading: "'Grenze Gotisch', serif",
femaleHeading: "'Playfair Display', serif",
@ -210,16 +92,6 @@ const RACE_FONTS: Record<ConcreteRace, RaceFontConfig> = {
maleBodyWeight: 700,
femaleBodyWeight: 400,
},
orcs: {
maleHeading: "'Metal Mania', cursive",
femaleHeading: "'Spectral', serif",
maleBody: "'Spectral', serif",
femaleBody: "'Spectral', serif",
maleHeadingWeight: 400,
femaleHeadingWeight: 400,
maleBodyWeight: 700,
femaleBodyWeight: 300,
},
}
const DYSLEXIC_FONT = "'OpenDyslexic', 'Open-Dyslexic', sans-serif"