diff --git a/client/index.html b/client/index.html
index 7677652..eb107e8 100644
--- a/client/index.html
+++ b/client/index.html
@@ -1,9 +1,15 @@
-
+
Shadowdark Character Manager
+
+
+
diff --git a/client/public/textures/parchment-noise-light.png b/client/public/textures/parchment-noise-light.png
new file mode 100644
index 0000000..b5af4ec
Binary files /dev/null and b/client/public/textures/parchment-noise-light.png differ
diff --git a/client/public/textures/parchment-noise.png b/client/public/textures/parchment-noise.png
new file mode 100644
index 0000000..80caf73
Binary files /dev/null and b/client/public/textures/parchment-noise.png differ
diff --git a/client/public/textures/speckle-light.png b/client/public/textures/speckle-light.png
new file mode 100644
index 0000000..6c069bc
Binary files /dev/null and b/client/public/textures/speckle-light.png differ
diff --git a/client/public/textures/speckle.png b/client/public/textures/speckle.png
new file mode 100644
index 0000000..91fb664
Binary files /dev/null and b/client/public/textures/speckle.png differ
diff --git a/client/public/textures/wood-grain-light.png b/client/public/textures/wood-grain-light.png
new file mode 100644
index 0000000..12d371e
Binary files /dev/null and b/client/public/textures/wood-grain-light.png differ
diff --git a/client/public/textures/wood-grain.png b/client/public/textures/wood-grain.png
new file mode 100644
index 0000000..a983e63
Binary files /dev/null and b/client/public/textures/wood-grain.png differ
diff --git a/client/src/App.module.css b/client/src/App.module.css
index f7cf62c..3cfe0da 100644
--- a/client/src/App.module.css
+++ b/client/src/App.module.css
@@ -5,9 +5,21 @@
}
body {
- font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
- background: #1a1a2e;
- color: #e0e0e0;
+ font-family: "Alegreya", Georgia, serif;
+ background-color: var(--bg-body);
+ background-image:
+ radial-gradient(
+ ellipse at center,
+ var(--bg-body-vignette) 0%,
+ transparent 70%
+ ),
+ var(--texture-body), var(--texture-speckle);
+ background-size:
+ 100% 100%,
+ 512px 512px,
+ 128px 128px;
+ background-repeat: no-repeat, repeat, repeat;
+ color: var(--text-primary);
min-height: 100vh;
}
@@ -19,14 +31,42 @@ body {
.header {
text-align: center;
- padding: 1rem 0;
- border-bottom: 1px solid #333;
+ padding: 1.25rem 0;
margin-bottom: 1.5rem;
+ position: relative;
+ z-index: 9999;
+}
+
+.header::before,
+.header::after {
+ content: "";
+ display: block;
+ height: 1px;
+ background: linear-gradient(
+ 90deg,
+ transparent 10%,
+ rgba(var(--gold-rgb), 0.3) 35%,
+ rgba(var(--gold-rgb), 0.5) 50%,
+ rgba(var(--gold-rgb), 0.3) 65%,
+ transparent 90%
+ );
+}
+
+.header::before {
+ margin-bottom: 1rem;
+}
+
+.header::after {
+ margin-top: 0.5rem;
}
.header h1 {
- font-size: 1.8rem;
- color: #c9a84c;
+ font-family: "Cinzel", Georgia, serif;
+ font-size: 2rem;
+ color: var(--gold);
font-variant: small-caps;
- letter-spacing: 0.05em;
+ letter-spacing: 0.15em;
+ text-shadow:
+ 0 0 30px rgba(var(--gold-rgb), 0.25),
+ 0 2px 4px rgba(var(--shadow-rgb), 0.5);
}
diff --git a/client/src/App.tsx b/client/src/App.tsx
index c040e3f..662a9a8 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,6 +1,7 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import CampaignList from "./pages/CampaignList";
import CampaignView from "./pages/CampaignView";
+import ThemeToggle from "./components/ThemeToggle";
import styles from "./App.module.css";
export default function App() {
@@ -9,6 +10,7 @@ export default function App() {
} />
diff --git a/client/src/components/AcDisplay.module.css b/client/src/components/AcDisplay.module.css
index c6b5304..7354455 100644
--- a/client/src/components/AcDisplay.module.css
+++ b/client/src/components/AcDisplay.module.css
@@ -5,28 +5,31 @@
}
.label {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.value {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.4rem;
font-weight: 700;
- color: #5dade2;
+ color: var(--ac);
cursor: pointer;
min-width: 2rem;
text-align: center;
}
.value.overridden {
- color: #c9a84c;
+ color: var(--gold);
}
.source {
font-size: 0.7rem;
- color: #666;
+ color: var(--text-tertiary);
}
.override {
@@ -37,30 +40,31 @@
.overrideIndicator {
font-size: 0.65rem;
- color: #c9a84c;
+ color: var(--gold);
cursor: pointer;
- background: rgba(201, 168, 76, 0.15);
+ background: rgba(var(--gold-rgb), 0.15);
border: none;
border-radius: 3px;
padding: 0.1rem 0.3rem;
}
.overrideIndicator:hover {
- background: rgba(201, 168, 76, 0.3);
+ background: rgba(var(--gold-rgb), 0.3);
}
.calculatedHint {
font-size: 0.65rem;
- color: #555;
+ color: var(--text-faint);
}
.editInput {
width: 3rem;
padding: 0.2rem 0.3rem;
- background: #0f1a30;
- border: 1px solid #c9a84c;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
border-radius: 4px;
- color: #e0e0e0;
+ color: var(--text-primary);
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.2rem;
font-weight: 700;
text-align: center;
diff --git a/client/src/components/AttackBlock.module.css b/client/src/components/AttackBlock.module.css
index ecea745..3d02d71 100644
--- a/client/src/components/AttackBlock.module.css
+++ b/client/src/components/AttackBlock.module.css
@@ -3,12 +3,14 @@
}
.title {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.9rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.5rem;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.list {
@@ -21,40 +23,43 @@
display: flex;
justify-content: space-between;
align-items: center;
- background: #0f1a30;
- border-radius: 6px;
+ background: var(--bg-inset);
+ border-radius: 4px;
padding: 0.35rem 0.6rem;
font-size: 0.85rem;
+ border-left: 2px solid rgba(var(--gold-rgb), 0.2);
}
.weaponName {
+ font-family: "Cinzel", Georgia, serif;
font-weight: 700;
text-transform: uppercase;
- color: #e0e0e0;
+ color: var(--text-primary);
+ letter-spacing: 0.03em;
}
.stats {
- color: #888;
+ color: var(--text-secondary);
}
.modifier {
- color: #c9a84c;
+ color: var(--gold);
font-weight: 600;
}
.damage {
- color: #e0e0e0;
+ color: var(--text-primary);
}
.tags {
font-size: 0.7rem;
- color: #666;
+ color: var(--text-tertiary);
margin-left: 0.3rem;
}
.talentLine {
font-style: italic;
- color: #888;
+ color: var(--text-secondary);
font-size: 0.8rem;
padding: 0.25rem 0.6rem;
}
@@ -62,7 +67,7 @@
.rollSpace {
width: 2.5rem;
text-align: center;
- color: #444;
+ color: var(--text-faint);
font-size: 0.75rem;
}
@@ -73,6 +78,6 @@
.empty {
font-size: 0.8rem;
- color: #555;
+ color: var(--text-faint);
font-style: italic;
}
diff --git a/client/src/components/CharacterCard.module.css b/client/src/components/CharacterCard.module.css
index ded79fd..6c687c2 100644
--- a/client/src/components/CharacterCard.module.css
+++ b/client/src/components/CharacterCard.module.css
@@ -1,17 +1,32 @@
.card {
- background: #16213e;
- border: 1px solid #333;
- border-radius: 10px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
padding: 1rem;
cursor: pointer;
+ box-shadow:
+ 0 4px 12px rgba(var(--shadow-rgb), 0.5),
+ 0 1px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06);
transition:
border-color 0.15s,
- transform 0.1s;
+ transform 0.1s,
+ box-shadow 0.15s;
}
.card:hover {
- border-color: #c9a84c;
+ border-color: rgba(var(--gold-rgb), 0.5);
transform: translateY(-2px);
+ box-shadow:
+ 0 8px 20px rgba(var(--shadow-rgb), 0.6),
+ 0 2px 6px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.1),
+ 0 0 15px rgba(var(--gold-rgb), 0.05);
}
.cardHeader {
@@ -22,19 +37,20 @@
}
.name {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.1rem;
font-weight: 700;
- color: #e0e0e0;
+ color: var(--text-primary);
}
.level {
font-size: 0.8rem;
- color: #888;
+ color: var(--text-secondary);
}
.meta {
font-size: 0.8rem;
- color: #666;
+ color: var(--text-tertiary);
margin-bottom: 0.75rem;
}
@@ -53,26 +69,27 @@
}
.acLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
}
.acValue {
font-size: 1.1rem;
- color: #5dade2;
+ color: var(--ac);
}
.gearSummary {
font-size: 0.75rem;
- color: #666;
+ color: var(--text-tertiary);
text-align: right;
margin-top: 0.5rem;
}
.xp {
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-align: right;
}
diff --git a/client/src/components/CharacterDetail.module.css b/client/src/components/CharacterDetail.module.css
index 92ddd33..82d0f94 100644
--- a/client/src/components/CharacterDetail.module.css
+++ b/client/src/components/CharacterDetail.module.css
@@ -1,7 +1,7 @@
.overlay {
position: absolute;
inset: 0;
- background: rgba(0, 0, 0, 0.7);
+ background: var(--bg-overlay);
display: flex;
align-items: center;
justify-content: center;
@@ -10,9 +10,14 @@
}
.modal {
- background: #1a1a2e;
- border: 1px solid #333;
- border-radius: 12px;
+ background-color: var(--bg-modal);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 2px solid rgba(var(--gold-rgb), 0.3);
+ border-radius: 4px;
width: 100%;
max-width: 900px;
max-height: 90vh;
@@ -20,7 +25,12 @@
overflow-x: hidden;
padding: 1.5rem;
scrollbar-width: thin;
- scrollbar-color: #333 transparent;
+ scrollbar-color: rgba(var(--gold-rgb), 0.2) transparent;
+ box-shadow:
+ 0 8px 40px rgba(var(--shadow-rgb), 0.7),
+ 0 2px 8px rgba(var(--shadow-rgb), 0.5),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.1),
+ inset 0 0 60px rgba(var(--shadow-rgb), 0.2);
}
.modal::-webkit-scrollbar {
@@ -32,12 +42,12 @@
}
.modal::-webkit-scrollbar-thumb {
- background: #333;
+ background: rgba(var(--gold-rgb), 0.2);
border-radius: 3px;
}
.modal::-webkit-scrollbar-thumb:hover {
- background: #555;
+ background: rgba(var(--gold-rgb), 0.35);
}
.topBar {
@@ -49,34 +59,39 @@
}
.editBtn {
+ font-family: "Cinzel", Georgia, serif;
padding: 0.35rem 0.75rem;
background: transparent;
- border: 1px solid #c9a84c;
- border-radius: 5px;
- color: #c9a84c;
+ border: 1px solid rgba(var(--gold-rgb), 0.4);
+ border-radius: 3px;
+ color: var(--gold);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
+ letter-spacing: 0.05em;
+ transition: all 0.15s;
}
.editBtn:hover {
- background: rgba(201, 168, 76, 0.15);
+ background: rgba(var(--gold-rgb), 0.1);
+ box-shadow: 0 0 8px rgba(var(--gold-rgb), 0.15);
}
.editBtn.active {
- background: #c9a84c;
- color: #1a1a2e;
+ background: rgba(var(--gold-rgb), 0.9);
+ color: var(--btn-active-text);
+ text-shadow: none;
}
.closeBtn {
background: none;
border: none;
- color: #888;
+ color: var(--text-secondary);
font-size: 1.5rem;
cursor: pointer;
padding: 0.25rem 0.5rem;
}
.closeBtn:hover {
- color: #e0e0e0;
+ color: var(--text-primary);
}
diff --git a/client/src/components/CharacterSheet.module.css b/client/src/components/CharacterSheet.module.css
index 8086390..89d8432 100644
--- a/client/src/components/CharacterSheet.module.css
+++ b/client/src/components/CharacterSheet.module.css
@@ -2,11 +2,20 @@
display: flex;
justify-content: space-between;
align-items: center;
- background: linear-gradient(135deg, #16213e, #0f1a30);
- border: 1px solid #333;
- border-radius: 8px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.3);
+ border-radius: 4px;
padding: 0.75rem 1rem;
margin-bottom: 1rem;
+ box-shadow:
+ 0 3px 12px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.1),
+ inset 0 0 20px rgba(var(--shadow-rgb), 0.15);
}
.identity {
@@ -14,18 +23,20 @@
}
.name {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.4rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
+ letter-spacing: 0.03em;
}
.title {
- color: #888;
+ color: var(--text-secondary);
font-size: 0.9rem;
}
.subtitle {
- color: #666;
+ color: var(--text-tertiary);
font-size: 0.8rem;
margin-top: 0.15rem;
}
@@ -46,16 +57,17 @@
}
.hp {
- color: #4caf50;
+ color: var(--hp);
}
.ac {
- color: #5dade2;
+ color: var(--ac);
}
.vitalLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
}
@@ -67,37 +79,38 @@
}
.hpSlash {
- color: #666;
+ color: var(--text-tertiary);
font-size: 0.9rem;
}
.hpMax {
- color: #888;
+ color: var(--text-secondary);
font-weight: 600;
}
.hpBonus {
- color: #4caf50;
+ color: var(--hp);
font-size: 0.65rem;
margin-left: 0.15rem;
}
.xpThreshold {
font-size: 0.75rem;
- color: #666;
+ color: var(--text-tertiary);
}
.xpCurrent {
- color: #c9a84c;
+ color: var(--gold);
font-weight: 600;
}
.nameInput {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.3rem;
font-weight: 700;
- color: #c9a84c;
- background: #0f1a30;
- border: 1px solid #333;
+ color: var(--gold);
+ background: var(--bg-input);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
border-radius: 5px;
padding: 0.2rem 0.4rem;
width: 10rem;
@@ -105,23 +118,24 @@
.nameInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.titleInput {
font-size: 0.85rem;
- color: #888;
- background: #0f1a30;
- border: 1px solid #333;
+ color: var(--text-secondary);
+ background: var(--bg-input);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
border-radius: 5px;
padding: 0.15rem 0.4rem;
width: 8rem;
margin-left: 0.3rem;
+ font-family: "Alegreya", Georgia, serif;
}
.titleInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.panels {
@@ -145,19 +159,19 @@
.deleteSection {
margin-top: 1rem;
padding-top: 0.75rem;
- border-top: 1px solid #333;
+ border-top: 1px solid rgba(var(--gold-rgb), 0.15);
}
.deleteBtn {
padding: 0.4rem 0.75rem;
background: transparent;
- border: 1px solid #e74c3c;
+ border: 1px solid var(--danger);
border-radius: 5px;
- color: #e74c3c;
+ color: var(--danger);
cursor: pointer;
font-size: 0.8rem;
}
.deleteBtn:hover {
- background: rgba(231, 76, 60, 0.1);
+ background: rgba(var(--danger-rgb), 0.1);
}
diff --git a/client/src/components/CurrencyRow.module.css b/client/src/components/CurrencyRow.module.css
index 0810c9b..d4e8ea3 100644
--- a/client/src/components/CurrencyRow.module.css
+++ b/client/src/components/CurrencyRow.module.css
@@ -12,44 +12,47 @@
}
.coinLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.gp {
- color: #c9a84c;
+ color: var(--gold);
}
.sp {
- color: #a0a0a0;
+ color: var(--silver);
}
.cp {
- color: #b87333;
+ color: var(--copper);
}
.coinInput {
width: 3.5rem;
padding: 0.25rem 0.4rem;
- background: #0f1a30;
- border: 1px solid #333;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
border-radius: 4px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: 0.85rem;
text-align: center;
+ font-family: "Alegreya", Georgia, serif;
}
.coinInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.coinBtn {
width: 20px;
height: 20px;
border-radius: 50%;
- border: 1px solid #444;
- background: #16213e;
- color: #e0e0e0;
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ background: var(--bg-inset);
+ color: var(--text-primary);
cursor: pointer;
font-size: 0.75rem;
display: flex;
@@ -58,8 +61,8 @@
}
.coinBtn:hover {
- border-color: #c9a84c;
- color: #c9a84c;
+ border-color: var(--gold);
+ color: var(--gold);
}
.coinValue {
diff --git a/client/src/components/DiceButton.module.css b/client/src/components/DiceButton.module.css
index f939e30..68074d9 100644
--- a/client/src/components/DiceButton.module.css
+++ b/client/src/components/DiceButton.module.css
@@ -2,9 +2,9 @@
width: 22px;
height: 22px;
border-radius: 4px;
- border: 1px solid #444;
- background: #16213e;
- color: #888;
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ background: var(--bg-inset);
+ color: var(--text-secondary);
cursor: pointer;
font-size: 0.75rem;
display: flex;
@@ -13,31 +13,37 @@
transition:
border-color 0.15s,
color 0.15s,
- background 0.15s;
+ background 0.15s,
+ box-shadow 0.15s;
+ box-shadow: inset 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.btn:hover {
- border-color: #c9a84c;
- color: #c9a84c;
- background: rgba(201, 168, 76, 0.1);
+ border-color: rgba(var(--gold-rgb), 0.6);
+ color: var(--gold);
+ background: rgba(var(--gold-rgb), 0.08);
+ box-shadow:
+ 0 0 6px rgba(var(--gold-rgb), 0.15),
+ inset 0 1px 2px rgba(var(--shadow-rgb), 0.2);
}
.btn:active {
- background: rgba(201, 168, 76, 0.25);
+ background: rgba(var(--gold-rgb), 0.15);
}
.btn.crit {
- border-color: #ffd700;
- color: #ffd700;
+ border-color: var(--crit);
+ color: var(--crit);
animation: critPulse 1s ease-in-out infinite;
+ box-shadow: 0 0 8px rgba(var(--crit-rgb), 0.3);
}
@keyframes critPulse {
0%,
100% {
- box-shadow: 0 0 4px rgba(255, 215, 0, 0.3);
+ box-shadow: 0 0 4px rgba(var(--crit-rgb), 0.3);
}
50% {
- box-shadow: 0 0 8px rgba(255, 215, 0, 0.6);
+ box-shadow: 0 0 12px rgba(var(--crit-rgb), 0.6);
}
}
diff --git a/client/src/components/GearList.module.css b/client/src/components/GearList.module.css
index 0ffdfe8..c04c365 100644
--- a/client/src/components/GearList.module.css
+++ b/client/src/components/GearList.module.css
@@ -10,11 +10,13 @@
}
.title {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.9rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
- letter-spacing: 0.05em;
+ letter-spacing: 0.1em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.slotCounter {
@@ -23,13 +25,13 @@
}
.slotCounter.normal {
- color: #4caf50;
+ color: var(--hp);
}
.slotCounter.warning {
- color: #ff9800;
+ color: var(--warning);
}
.slotCounter.over {
- color: #e74c3c;
+ color: var(--danger);
}
.table {
@@ -38,13 +40,15 @@
}
.tableHeader {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.7rem;
- color: #666;
+ color: var(--text-tertiary);
text-transform: uppercase;
font-weight: 600;
text-align: left;
padding: 0.25rem 0.5rem;
- border-bottom: 1px solid #333;
+ border-bottom: 1px solid rgba(var(--gold-rgb), 0.2);
+ letter-spacing: 0.05em;
}
.right {
@@ -55,11 +59,11 @@
}
.row {
- border-bottom: 1px solid #222;
+ border-bottom: 1px solid rgba(var(--gold-rgb), 0.06);
}
.row:hover {
- background: rgba(201, 168, 76, 0.05);
+ background: rgba(var(--gold-rgb), 0.06);
}
.cell {
@@ -75,14 +79,14 @@
.removeBtn {
background: none;
border: none;
- color: #555;
+ color: var(--text-faint);
cursor: pointer;
font-size: 0.9rem;
padding: 0.1rem 0.3rem;
}
.removeBtn:hover {
- color: #e74c3c;
+ color: var(--danger);
}
.addArea {
@@ -91,25 +95,34 @@
.addBtn {
padding: 0.4rem 0.75rem;
- background: #c9a84c;
- color: #1a1a2e;
+ background: var(--btn-gold-bg);
+ color: var(--btn-active-text);
border: none;
- border-radius: 6px;
+ border-radius: 4px;
font-weight: 600;
cursor: pointer;
font-size: 0.8rem;
+ box-shadow:
+ 0 2px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ text-shadow: 0 1px 1px rgba(var(--shadow-rgb), 0.2);
}
.addBtn:hover {
- background: #d4b65a;
+ background: linear-gradient(
+ 180deg,
+ var(--gold-bright),
+ var(--gold-hover) 40%,
+ var(--gold)
+ );
}
.cancelBtn {
padding: 0.4rem 0.75rem;
- background: #333;
- color: #888;
- border: none;
- border-radius: 6px;
+ background: var(--bg-inset);
+ color: var(--text-secondary);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
font-weight: 600;
cursor: pointer;
font-size: 0.8rem;
@@ -124,30 +137,32 @@
.customInput {
flex: 1;
padding: 0.4rem 0.6rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.customInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.customSelect {
padding: 0.4rem 0.6rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.empty {
font-size: 0.8rem;
- color: #555;
+ color: var(--text-faint);
font-style: italic;
padding: 0.5rem;
}
diff --git a/client/src/components/GearPanel.module.css b/client/src/components/GearPanel.module.css
index 2f78a99..a4c509c 100644
--- a/client/src/components/GearPanel.module.css
+++ b/client/src/components/GearPanel.module.css
@@ -1,6 +1,16 @@
.panel {
- background: #16213e;
- border: 1px solid #333;
- border-radius: 8px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
padding: 0.75rem;
+ box-shadow:
+ 0 4px 16px rgba(var(--shadow-rgb), 0.5),
+ 0 1px 4px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06),
+ inset 0 0 30px rgba(var(--shadow-rgb), 0.15);
}
diff --git a/client/src/components/HpBar.module.css b/client/src/components/HpBar.module.css
index d4618ad..eb9e842 100644
--- a/client/src/components/HpBar.module.css
+++ b/client/src/components/HpBar.module.css
@@ -5,10 +5,12 @@
}
.label {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.values {
@@ -20,32 +22,32 @@
}
.current {
- color: #4caf50;
+ color: var(--hp);
}
.current.hurt {
- color: #ff9800;
+ color: var(--warning);
}
.current.critical {
- color: #e74c3c;
+ color: var(--danger);
}
.slash {
- color: #666;
+ color: var(--text-tertiary);
}
.max {
- color: #888;
+ color: var(--text-secondary);
}
.btn {
width: 24px;
height: 24px;
border-radius: 50%;
- border: 1px solid #444;
- background: #16213e;
- color: #e0e0e0;
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ background: var(--bg-inset);
+ color: var(--text-primary);
cursor: pointer;
font-size: 0.9rem;
display: flex;
@@ -55,6 +57,6 @@
}
.btn:hover {
- border-color: #c9a84c;
- color: #c9a84c;
+ border-color: var(--gold);
+ color: var(--gold);
}
diff --git a/client/src/components/HpDisplay.module.css b/client/src/components/HpDisplay.module.css
index 5c11c5a..d7e7d93 100644
--- a/client/src/components/HpDisplay.module.css
+++ b/client/src/components/HpDisplay.module.css
@@ -14,33 +14,35 @@
.current {
font-size: 1.4rem;
font-weight: 700;
- color: #4caf50;
+ color: var(--hp);
}
.current.hurt {
- color: #ff9800;
+ color: var(--warning);
}
.current.critical {
- color: #e74c3c;
+ color: var(--danger);
}
.slash {
- color: #666;
+ color: var(--text-tertiary);
font-size: 0.9rem;
}
.max {
- color: #888;
+ color: var(--text-secondary);
font-weight: 600;
font-size: 1rem;
}
.label {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.65rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.buttons {
@@ -51,9 +53,9 @@
.healBtn {
padding: 0.15rem 0.5rem;
background: rgba(76, 175, 80, 0.15);
- border: 1px solid #4caf50;
+ border: 1px solid var(--hp);
border-radius: 4px;
- color: #4caf50;
+ color: var(--hp);
cursor: pointer;
font-size: 0.65rem;
font-weight: 600;
@@ -66,10 +68,10 @@
.dmgBtn {
padding: 0.15rem 0.5rem;
- background: rgba(231, 76, 60, 0.15);
- border: 1px solid #e74c3c;
+ background: rgba(var(--danger-rgb), 0.15);
+ border: 1px solid var(--danger);
border-radius: 4px;
- color: #e74c3c;
+ color: var(--danger);
cursor: pointer;
font-size: 0.65rem;
font-weight: 600;
@@ -77,7 +79,7 @@
}
.dmgBtn:hover {
- background: rgba(231, 76, 60, 0.3);
+ background: rgba(var(--danger-rgb), 0.3);
}
.inputRow {
@@ -89,10 +91,10 @@
.amountInput {
width: 2.5rem;
padding: 0.2rem 0.3rem;
- background: #0f1a30;
- border: 1px solid #444;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
border-radius: 4px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: 0.85rem;
text-align: center;
font-weight: 600;
@@ -103,11 +105,11 @@
}
.amountInput.healing {
- border-color: #4caf50;
+ border-color: var(--hp);
}
.amountInput.damage {
- border-color: #e74c3c;
+ border-color: var(--danger);
}
.applyBtn {
@@ -120,24 +122,24 @@
}
.applyBtn.healing {
- background: #4caf50;
- color: #1a1a2e;
+ background: var(--hp);
+ color: var(--btn-active-text);
}
.applyBtn.damage {
- background: #e74c3c;
+ background: var(--danger);
color: #fff;
}
.cancelBtn {
background: none;
border: none;
- color: #666;
+ color: var(--text-tertiary);
cursor: pointer;
font-size: 0.85rem;
padding: 0 0.2rem;
}
.cancelBtn:hover {
- color: #e0e0e0;
+ color: var(--text-primary);
}
diff --git a/client/src/components/InfoPanel.module.css b/client/src/components/InfoPanel.module.css
index 733510c..251a023 100644
--- a/client/src/components/InfoPanel.module.css
+++ b/client/src/components/InfoPanel.module.css
@@ -1,17 +1,29 @@
.panel {
- background: #16213e;
- border: 1px solid #333;
- border-radius: 8px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
padding: 0.75rem;
+ box-shadow:
+ 0 4px 16px rgba(var(--shadow-rgb), 0.5),
+ 0 1px 4px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06),
+ inset 0 0 30px rgba(var(--shadow-rgb), 0.15);
}
.sectionTitle {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.8rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
- letter-spacing: 0.05em;
+ letter-spacing: 0.1em;
margin-bottom: 0.5rem;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.infoGrid {
@@ -28,15 +40,17 @@
}
.infoLabel {
- color: #666;
+ font-family: "Cinzel", Georgia, serif;
+ color: var(--text-tertiary);
font-size: 0.7rem;
text-transform: uppercase;
font-weight: 600;
min-width: 5rem;
+ letter-spacing: 0.03em;
}
.infoValue {
- color: #e0e0e0;
+ color: var(--text-primary);
}
.notes {
@@ -48,26 +62,28 @@
.editField {
padding: 0.3rem 0.5rem;
- background: #0f1a30;
- border: 1px solid #333;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
border-radius: 5px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: 0.85rem;
width: 100%;
+ font-family: "Alegreya", Georgia, serif;
}
.editField:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.editSelect {
padding: 0.3rem 0.5rem;
- background: #0f1a30;
- border: 1px solid #333;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
border-radius: 5px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.editRow {
@@ -87,26 +103,28 @@
}
.fieldLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.65rem;
- color: #666;
+ color: var(--text-tertiary);
text-transform: uppercase;
font-weight: 600;
+ letter-spacing: 0.03em;
}
.notesEdit {
width: 100%;
min-height: 50px;
padding: 0.4rem;
- background: #0f1a30;
- border: 1px solid #333;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
border-radius: 5px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: 0.85rem;
- font-family: inherit;
+ font-family: "Alegreya", Georgia, serif;
resize: vertical;
}
.notesEdit:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
diff --git a/client/src/components/InlineNumber.module.css b/client/src/components/InlineNumber.module.css
index ba5ac66..732e3db 100644
--- a/client/src/components/InlineNumber.module.css
+++ b/client/src/components/InlineNumber.module.css
@@ -5,16 +5,16 @@
}
.value:hover {
- border-bottom-color: #c9a84c;
+ border-bottom-color: var(--gold);
}
.input {
width: 3rem;
padding: 0.1rem 0.2rem;
- background: #0f1a30;
- border: 1px solid #c9a84c;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.3);
border-radius: 4px;
- color: #e0e0e0;
+ color: var(--text-primary);
font-size: inherit;
font-weight: inherit;
text-align: center;
diff --git a/client/src/components/ItemPicker.module.css b/client/src/components/ItemPicker.module.css
index 37cd520..e6c49cb 100644
--- a/client/src/components/ItemPicker.module.css
+++ b/client/src/components/ItemPicker.module.css
@@ -5,16 +5,17 @@
.searchInput {
width: 100%;
padding: 0.5rem 0.75rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.searchInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.dropdown {
@@ -24,12 +25,22 @@
right: 0;
max-height: 220px;
overflow-y: auto;
- background: #16213e;
- border: 1px solid #444;
- border-radius: 6px;
+ background:
+ repeating-linear-gradient(
+ 0deg,
+ transparent,
+ transparent 3px,
+ rgba(180, 155, 100, 0.02) 3px,
+ rgba(180, 155, 100, 0.02) 4px
+ ),
+ linear-gradient(175deg, #221e18 0%, #1e1b16 50%, #201c17 100%);
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ border-radius: 4px;
margin-bottom: 0.25rem;
z-index: 200;
- box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5);
+ box-shadow:
+ 0 -4px 20px rgba(var(--shadow-rgb), 0.5),
+ 0 -2px 8px rgba(var(--shadow-rgb), 0.4);
}
.group {
@@ -37,12 +48,14 @@
}
.groupLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.7rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
padding: 0.25rem 0.75rem;
- letter-spacing: 0.05em;
+ letter-spacing: 0.08em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.item {
@@ -54,15 +67,15 @@
}
.item:hover {
- background: rgba(201, 168, 76, 0.15);
+ background: rgba(var(--gold-rgb), 0.12);
}
.itemName {
- color: #e0e0e0;
+ color: var(--text-primary);
}
.itemMeta {
- color: #666;
+ color: var(--text-tertiary);
font-size: 0.75rem;
}
@@ -70,12 +83,12 @@
padding: 0.5rem 0.75rem;
cursor: pointer;
font-size: 0.85rem;
- color: #888;
+ color: var(--text-secondary);
font-style: italic;
- border-top: 1px solid #333;
+ border-top: 1px solid rgba(var(--gold-rgb), 0.1);
}
.customOption:hover {
- background: rgba(201, 168, 76, 0.15);
- color: #c9a84c;
+ background: rgba(var(--gold-rgb), 0.12);
+ color: var(--gold);
}
diff --git a/client/src/components/RollEntry.module.css b/client/src/components/RollEntry.module.css
index dde812c..d014ed5 100644
--- a/client/src/components/RollEntry.module.css
+++ b/client/src/components/RollEntry.module.css
@@ -1,11 +1,15 @@
.card {
- background: #0f1a30;
- border: 1px solid #2a2a4a;
- border-radius: 6px;
+ background-color: var(--bg-roll-entry);
+ background-image: var(--texture-surface);
+ background-size: 256px 256px;
+ background-repeat: repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.1);
+ border-radius: 4px;
padding: 0.5rem 0.6rem;
animation: slideIn 0.3s ease-out;
border-left-width: 3px;
border-left-style: solid;
+ box-shadow: 0 2px 6px rgba(var(--shadow-rgb), 0.3);
}
@keyframes slideIn {
@@ -20,16 +24,23 @@
}
.card.nat20 {
- border-color: #ffd700;
- background: linear-gradient(135deg, #1a1a0e, #0f1a30);
+ border-color: var(--crit);
+ background: linear-gradient(
+ 135deg,
+ rgba(40, 35, 15, 0.9),
+ rgba(24, 21, 16, 0.9)
+ );
+ box-shadow:
+ 0 0 12px rgba(var(--crit-rgb), 0.15),
+ 0 2px 6px rgba(var(--shadow-rgb), 0.3);
}
.card.nat20 .total {
- color: #ffd700;
+ color: var(--crit);
}
.card.fresh {
- border-color: #c9a84c;
+ border-color: var(--gold);
animation:
slideIn 0.3s ease-out,
glow 1s ease-out;
@@ -37,7 +48,7 @@
@keyframes glow {
0% {
- box-shadow: 0 0 8px rgba(201, 168, 76, 0.4);
+ box-shadow: 0 0 8px rgba(var(--gold-rgb), 0.4);
}
100% {
box-shadow: none;
@@ -52,75 +63,78 @@
}
.charName {
+ font-family: "Cinzel", Georgia, serif;
font-weight: 700;
font-size: 0.8rem;
- color: #c9a84c;
+ color: var(--gold);
+ letter-spacing: 0.03em;
}
.timestamp {
font-size: 0.65rem;
- color: #555;
+ color: var(--text-faint);
}
.label {
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
margin-bottom: 0.2rem;
}
.breakdown {
font-size: 0.75rem;
- color: #666;
+ color: var(--text-tertiary);
margin-bottom: 0.15rem;
}
.dieResult {
- color: #e0e0e0;
+ color: var(--text-primary);
font-weight: 600;
}
.dieChosen {
- color: #c9a84c;
+ color: var(--gold);
font-weight: 700;
}
.dieDiscarded {
- color: #555;
+ color: var(--text-faint);
text-decoration: line-through;
}
.modLine {
font-size: 0.7rem;
- color: #666;
+ color: var(--text-tertiary);
}
.total {
font-size: 1.3rem;
font-weight: 700;
- color: #e0e0e0;
+ color: var(--text-primary);
text-align: center;
margin-top: 0.2rem;
}
.advantage {
- color: #4caf50;
+ color: var(--hp);
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
}
.disadvantage {
- color: #e74c3c;
+ color: var(--danger);
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
}
.critBanner {
+ font-family: "Cinzel", Georgia, serif;
text-align: center;
font-size: 0.75rem;
font-weight: 700;
- color: #ffd700;
+ color: var(--crit);
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.15rem 0;
diff --git a/client/src/components/RollLog.module.css b/client/src/components/RollLog.module.css
index 83389ca..a3b20a5 100644
--- a/client/src/components/RollLog.module.css
+++ b/client/src/components/RollLog.module.css
@@ -1,14 +1,18 @@
.panel {
width: 300px;
height: 100%;
- background: #1a1a2e;
- border-left: 1px solid #333;
+ background-color: var(--bg-roll-log);
+ background-image: var(--texture-surface);
+ background-size: 256px 256px;
+ background-repeat: repeat;
+ border-left: 2px solid rgba(var(--gold-rgb), 0.2);
display: flex;
flex-direction: column;
transition: width 0.2s;
flex-shrink: 0;
z-index: 150;
position: relative;
+ box-shadow: -4px 0 16px rgba(var(--shadow-rgb), 0.3);
}
.panel.collapsed {
@@ -21,28 +25,30 @@
justify-content: space-between;
align-items: center;
padding: 0.5rem 0.75rem;
- border-bottom: 1px solid #333;
+ border-bottom: 1px solid rgba(var(--gold-rgb), 0.15);
}
.title {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.85rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
- letter-spacing: 0.05em;
+ letter-spacing: 0.1em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.collapseBtn {
background: none;
border: none;
- color: #888;
+ color: var(--text-secondary);
cursor: pointer;
font-size: 1rem;
padding: 0.2rem;
}
.collapseBtn:hover {
- color: #c9a84c;
+ color: var(--gold);
}
.collapsedContent {
@@ -61,34 +67,35 @@
.collapsedLast {
writing-mode: vertical-rl;
font-size: 0.7rem;
- color: #888;
+ color: var(--text-secondary);
max-height: 100px;
overflow: hidden;
}
.inputArea {
padding: 0.5rem 0.75rem;
- border-bottom: 1px solid #333;
+ border-bottom: 1px solid rgba(var(--gold-rgb), 0.15);
}
.input {
width: 100%;
padding: 0.4rem 0.6rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.input:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.hint {
font-size: 0.6rem;
- color: #555;
+ color: var(--text-faint);
margin-top: 0.25rem;
text-align: center;
}
@@ -101,7 +108,7 @@
flex-direction: column;
gap: 0.4rem;
scrollbar-width: thin;
- scrollbar-color: #333 transparent;
+ scrollbar-color: rgba(var(--gold-rgb), 0.15) transparent;
}
.entries::-webkit-scrollbar {
@@ -109,13 +116,13 @@
}
.entries::-webkit-scrollbar-thumb {
- background: #333;
+ background: rgba(var(--gold-rgb), 0.15);
border-radius: 2px;
}
.empty {
text-align: center;
- color: #555;
+ color: var(--text-faint);
font-size: 0.8rem;
font-style: italic;
padding: 2rem 0;
@@ -126,7 +133,7 @@
width: 100%;
height: 200px;
border-left: none;
- border-top: 1px solid #333;
+ border-top: 2px solid rgba(var(--gold-rgb), 0.2);
}
.panel.collapsed {
diff --git a/client/src/components/StatBlock.module.css b/client/src/components/StatBlock.module.css
index 2543972..5ed388f 100644
--- a/client/src/components/StatBlock.module.css
+++ b/client/src/components/StatBlock.module.css
@@ -8,20 +8,23 @@
display: flex;
flex-direction: column;
align-items: center;
- background: #0f1a30;
- border: 1px solid #2a2a4a;
- border-radius: 8px;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.12);
+ border-radius: 4px;
padding: 0.5rem 0.3rem;
position: relative;
+ box-shadow: inset 0 2px 4px rgba(var(--shadow-rgb), 0.3);
}
.statName {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.7rem;
- color: #c9a84c;
+ color: var(--gold);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 0.15rem;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.statRow {
@@ -33,22 +36,22 @@
.modifier {
font-size: 1.3rem;
font-weight: 700;
- color: #e0e0e0;
+ color: var(--text-primary);
min-width: 2.2rem;
text-align: center;
}
.score {
font-size: 0.75rem;
- color: #666;
- background: #1a1a2e;
+ color: var(--text-tertiary);
+ background: var(--bg-inset);
border-radius: 3px;
padding: 0 0.3rem;
margin-top: 0.15rem;
}
.bonus {
- color: #4caf50;
+ color: var(--hp);
font-size: 0.65rem;
}
@@ -56,9 +59,9 @@
width: 22px;
height: 22px;
border-radius: 50%;
- border: 1px solid #444;
- background: #16213e;
- color: #e0e0e0;
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ background: var(--bg-inset);
+ color: var(--text-primary);
cursor: pointer;
font-size: 0.8rem;
display: flex;
@@ -71,8 +74,8 @@
}
.btn:hover {
- border-color: #c9a84c;
- color: #c9a84c;
+ border-color: var(--gold);
+ color: var(--gold);
}
.rollSpace {
diff --git a/client/src/components/StatsPanel.module.css b/client/src/components/StatsPanel.module.css
index 9216725..caf6dc2 100644
--- a/client/src/components/StatsPanel.module.css
+++ b/client/src/components/StatsPanel.module.css
@@ -1,21 +1,41 @@
.panel {
- background: #16213e;
- border: 1px solid #333;
- border-radius: 8px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
padding: 0.75rem;
+ box-shadow:
+ 0 4px 16px rgba(var(--shadow-rgb), 0.5),
+ 0 1px 4px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06),
+ inset 0 0 30px rgba(var(--shadow-rgb), 0.15);
}
.sectionTitle {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.8rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
- letter-spacing: 0.05em;
+ letter-spacing: 0.1em;
margin-bottom: 0.5rem;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.separator {
border: none;
- border-top: 1px solid #2a2a4a;
+ height: 1px;
+ background: linear-gradient(
+ 90deg,
+ transparent 5%,
+ rgba(var(--gold-rgb), 0.3) 30%,
+ rgba(var(--gold-rgb), 0.5) 50%,
+ rgba(var(--gold-rgb), 0.3) 70%,
+ transparent 95%
+ );
margin: 0.75rem 0;
}
diff --git a/client/src/components/TalentList.module.css b/client/src/components/TalentList.module.css
index 7b399d7..642dfa8 100644
--- a/client/src/components/TalentList.module.css
+++ b/client/src/components/TalentList.module.css
@@ -10,11 +10,13 @@
}
.sectionTitle {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.9rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
- letter-spacing: 0.05em;
+ letter-spacing: 0.1em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.list {
@@ -27,8 +29,9 @@
display: flex;
justify-content: space-between;
align-items: flex-start;
- background: #0f1a30;
- border-radius: 6px;
+ background: rgba(var(--shadow-rgb), 0.2);
+ border-left: 2px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
padding: 0.5rem 0.75rem;
}
@@ -43,20 +46,20 @@
.itemDesc {
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
}
.removeBtn {
background: none;
border: none;
- color: #666;
+ color: var(--text-tertiary);
cursor: pointer;
font-size: 1rem;
padding: 0.25rem;
}
.removeBtn:hover {
- color: #e74c3c;
+ color: var(--danger);
}
.addForm {
@@ -68,36 +71,46 @@
.addInput {
flex: 1;
padding: 0.4rem 0.6rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.addInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.addBtn {
padding: 0.4rem 0.75rem;
- background: #c9a84c;
- color: #1a1a2e;
+ background: var(--btn-gold-bg);
+ color: var(--btn-active-text);
border: none;
- border-radius: 6px;
+ border-radius: 4px;
font-weight: 600;
cursor: pointer;
font-size: 0.85rem;
+ box-shadow:
+ 0 2px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ text-shadow: 0 1px 1px rgba(var(--shadow-rgb), 0.2);
}
.addBtn:hover {
- background: #d4b65a;
+ background: linear-gradient(
+ 180deg,
+ var(--gold-bright),
+ var(--gold-hover) 40%,
+ var(--gold)
+ );
}
.empty {
font-size: 0.8rem;
- color: #555;
+ color: var(--text-faint);
font-style: italic;
padding: 0.5rem 0;
}
diff --git a/client/src/components/TalentPicker.module.css b/client/src/components/TalentPicker.module.css
index 38f1dfc..a9f8bd9 100644
--- a/client/src/components/TalentPicker.module.css
+++ b/client/src/components/TalentPicker.module.css
@@ -5,16 +5,17 @@
.searchInput {
width: 100%;
padding: 0.5rem 0.75rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.85rem;
+ font-family: "Alegreya", Georgia, serif;
}
.searchInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.dropdown {
@@ -24,12 +25,22 @@
right: 0;
max-height: 220px;
overflow-y: auto;
- background: #16213e;
- border: 1px solid #444;
- border-radius: 6px;
+ background:
+ repeating-linear-gradient(
+ 0deg,
+ transparent,
+ transparent 3px,
+ rgba(180, 155, 100, 0.02) 3px,
+ rgba(180, 155, 100, 0.02) 4px
+ ),
+ linear-gradient(175deg, #221e18 0%, #1e1b16 50%, #201c17 100%);
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ border-radius: 4px;
margin-bottom: 0.25rem;
z-index: 200;
- box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5);
+ box-shadow:
+ 0 -4px 20px rgba(var(--shadow-rgb), 0.5),
+ 0 -2px 8px rgba(var(--shadow-rgb), 0.4);
}
.group {
@@ -37,12 +48,14 @@
}
.groupLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.7rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
text-transform: uppercase;
padding: 0.25rem 0.75rem;
- letter-spacing: 0.05em;
+ letter-spacing: 0.08em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.item {
@@ -54,16 +67,16 @@
}
.item:hover {
- background: rgba(201, 168, 76, 0.15);
+ background: rgba(var(--gold-rgb), 0.12);
}
.itemName {
- color: #e0e0e0;
+ color: var(--text-primary);
font-weight: 600;
}
.itemDesc {
- color: #888;
+ color: var(--text-secondary);
font-size: 0.75rem;
}
@@ -71,12 +84,12 @@
padding: 0.5rem 0.75rem;
cursor: pointer;
font-size: 0.85rem;
- color: #888;
+ color: var(--text-secondary);
font-style: italic;
- border-top: 1px solid #333;
+ border-top: 1px solid rgba(var(--gold-rgb), 0.1);
}
.customOption:hover {
- background: rgba(201, 168, 76, 0.15);
- color: #c9a84c;
+ background: rgba(var(--gold-rgb), 0.12);
+ color: var(--gold);
}
diff --git a/client/src/components/ThemeToggle.module.css b/client/src/components/ThemeToggle.module.css
new file mode 100644
index 0000000..d4919ff
--- /dev/null
+++ b/client/src/components/ThemeToggle.module.css
@@ -0,0 +1,76 @@
+.container {
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 9999;
+}
+
+.trigger {
+ background: none;
+ border: 1px solid rgba(var(--gold-rgb), 0.25);
+ border-radius: 4px;
+ padding: 0.3rem 0.5rem;
+ cursor: pointer;
+ font-size: 1rem;
+ line-height: 1;
+ transition:
+ border-color 0.15s,
+ box-shadow 0.15s;
+}
+
+.trigger:hover {
+ border-color: rgba(var(--gold-rgb), 0.5);
+ box-shadow: 0 0 6px rgba(var(--gold-rgb), 0.15);
+}
+
+.dropdown {
+ position: absolute;
+ top: 100%;
+ right: 0;
+ margin-top: 0.35rem;
+ background-color: var(--bg-modal);
+ background-image: var(--texture-surface);
+ background-size: 256px 256px;
+ background-repeat: repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.3);
+ border-radius: 4px;
+ padding: 0.3rem;
+ min-width: 10rem;
+ z-index: 9999;
+ box-shadow:
+ 0 4px 16px rgba(var(--shadow-rgb), 0.5),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06);
+}
+
+.option {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ width: 100%;
+ padding: 0.4rem 0.6rem;
+ background: none;
+ border: none;
+ border-radius: 3px;
+ color: var(--text-primary);
+ font-family: "Alegreya", Georgia, serif;
+ font-size: 0.85rem;
+ cursor: pointer;
+ text-align: left;
+ transition: background 0.1s;
+}
+
+.option:hover {
+ background: rgba(var(--gold-rgb), 0.1);
+}
+
+.option.active {
+ color: var(--gold);
+ font-weight: 600;
+}
+
+.optionIcon {
+ font-size: 1rem;
+ width: 1.4rem;
+ text-align: center;
+}
diff --git a/client/src/components/ThemeToggle.tsx b/client/src/components/ThemeToggle.tsx
new file mode 100644
index 0000000..f424e31
--- /dev/null
+++ b/client/src/components/ThemeToggle.tsx
@@ -0,0 +1,68 @@
+import { useState } from "react";
+import styles from "./ThemeToggle.module.css";
+
+const THEMES = [
+ { id: "dark-parchment", label: "Dark Parchment", icon: "\u{1F4DC}" },
+ { id: "light-parchment", label: "Light Parchment", icon: "\u{1F3F0}" },
+ { id: "white", label: "White", icon: "\u2600" },
+ { id: "abyss", label: "Abyss", icon: "\u{1F311}" },
+] as const;
+
+type ThemeId = (typeof THEMES)[number]["id"];
+
+function getInitialTheme(): ThemeId {
+ const saved = localStorage.getItem("shadowdark-theme");
+ if (saved && THEMES.some((t) => t.id === saved)) return saved as ThemeId;
+ return "dark-parchment";
+}
+
+function applyTheme(theme: ThemeId) {
+ if (theme === "dark-parchment") {
+ document.documentElement.removeAttribute("data-theme");
+ } else {
+ document.documentElement.setAttribute("data-theme", theme);
+ }
+ localStorage.setItem("shadowdark-theme", theme);
+}
+
+// Apply saved theme immediately on module load
+applyTheme(getInitialTheme());
+
+export default function ThemeToggle() {
+ const [current, setCurrent] = useState(getInitialTheme);
+ const [open, setOpen] = useState(false);
+
+ function selectTheme(theme: ThemeId) {
+ applyTheme(theme);
+ setCurrent(theme);
+ setOpen(false);
+ }
+
+ const currentTheme = THEMES.find((t) => t.id === current)!;
+
+ return (
+
+
+ {open && (
+
+ {THEMES.map((theme) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 77d159f..568f5d4 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,9 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
+import "./theme.css";
import App from "./App";
createRoot(document.getElementById("root")!).render(
-
+ ,
);
diff --git a/client/src/pages/CampaignList.module.css b/client/src/pages/CampaignList.module.css
index 0144820..bed9dcd 100644
--- a/client/src/pages/CampaignList.module.css
+++ b/client/src/pages/CampaignList.module.css
@@ -14,32 +14,49 @@
display: flex;
align-items: center;
justify-content: space-between;
- background: #16213e;
- border: 1px solid #333;
- border-radius: 8px;
+ background-color: var(--bg-surface);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
padding: 1rem 1.25rem;
cursor: pointer;
- transition: border-color 0.15s;
+ transition:
+ border-color 0.15s,
+ box-shadow 0.15s;
+ box-shadow:
+ 0 4px 12px rgba(var(--shadow-rgb), 0.5),
+ 0 1px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.06);
}
.campaignCard:hover {
- border-color: #c9a84c;
+ border-color: rgba(var(--gold-rgb), 0.5);
+ box-shadow:
+ 0 6px 16px rgba(var(--shadow-rgb), 0.6),
+ 0 2px 6px rgba(var(--shadow-rgb), 0.4),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.1);
}
.campaignName {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.1rem;
font-weight: 600;
+ color: var(--text-primary);
}
.campaignDate {
font-size: 0.8rem;
- color: #888;
+ color: var(--text-secondary);
}
.deleteBtn {
background: none;
border: none;
- color: #666;
+ color: var(--text-tertiary);
cursor: pointer;
font-size: 1.2rem;
padding: 0.25rem 0.5rem;
@@ -47,8 +64,8 @@
}
.deleteBtn:hover {
- color: #e74c3c;
- background: rgba(231, 76, 60, 0.1);
+ color: var(--danger);
+ background: rgba(var(--danger-rgb), 0.1);
}
.createForm {
@@ -59,36 +76,47 @@
.createInput {
flex: 1;
padding: 0.6rem 1rem;
- background: #16213e;
- border: 1px solid #333;
- border-radius: 8px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 1rem;
+ font-family: "Alegreya", Georgia, serif;
}
.createInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.createBtn {
padding: 0.6rem 1.25rem;
- background: #c9a84c;
- color: #1a1a2e;
+ background: var(--btn-gold-bg);
+ color: var(--btn-active-text);
border: none;
- border-radius: 8px;
+ border-radius: 4px;
+ font-family: "Cinzel", Georgia, serif;
font-weight: 600;
cursor: pointer;
font-size: 1rem;
+ box-shadow:
+ 0 2px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ text-shadow: 0 1px 1px rgba(var(--shadow-rgb), 0.2);
}
.createBtn:hover {
- background: #d4b65a;
+ background: linear-gradient(
+ 180deg,
+ var(--gold-bright),
+ var(--gold-hover) 40%,
+ var(--gold)
+ );
}
.empty {
text-align: center;
- color: #666;
+ color: var(--text-tertiary);
padding: 3rem 0;
font-style: italic;
}
diff --git a/client/src/pages/CampaignView.module.css b/client/src/pages/CampaignView.module.css
index 5c98d63..12b1603 100644
--- a/client/src/pages/CampaignView.module.css
+++ b/client/src/pages/CampaignView.module.css
@@ -30,34 +30,47 @@
}
.backLink {
- color: #888;
+ color: var(--text-secondary);
text-decoration: none;
font-size: 0.9rem;
}
.backLink:hover {
- color: #c9a84c;
+ color: var(--gold);
}
.campaignName {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.5rem;
font-weight: 700;
- color: #c9a84c;
+ color: var(--gold);
+ letter-spacing: 0.05em;
+ text-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.4);
}
.addBtn {
padding: 0.5rem 1rem;
- background: #c9a84c;
- color: #1a1a2e;
+ background: var(--btn-gold-bg);
+ color: var(--btn-active-text);
border: none;
- border-radius: 8px;
+ border-radius: 4px;
+ font-family: "Cinzel", Georgia, serif;
font-weight: 600;
cursor: pointer;
font-size: 0.9rem;
+ box-shadow:
+ 0 2px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ text-shadow: 0 1px 1px rgba(var(--shadow-rgb), 0.2);
}
.addBtn:hover {
- background: #d4b65a;
+ background: linear-gradient(
+ 180deg,
+ var(--gold-bright),
+ var(--gold-hover) 40%,
+ var(--gold)
+ );
}
.grid {
@@ -80,7 +93,7 @@
.empty {
text-align: center;
- color: #666;
+ color: var(--text-tertiary);
padding: 3rem 0;
font-style: italic;
grid-column: 1 / -1;
@@ -89,7 +102,7 @@
.createModal {
position: fixed;
inset: 0;
- background: rgba(0, 0, 0, 0.7);
+ background: var(--bg-overlay);
display: flex;
align-items: center;
justify-content: center;
@@ -98,19 +111,32 @@
}
.createForm {
- background: #1a1a2e;
- border: 1px solid #333;
- border-radius: 12px;
+ background-color: var(--bg-modal);
+ background-image: var(--texture-surface), var(--texture-speckle);
+ background-size:
+ 256px 256px,
+ 128px 128px;
+ background-repeat: repeat, repeat;
+ border: 2px solid rgba(var(--gold-rgb), 0.3);
+ border-radius: 4px;
padding: 1.5rem;
width: 100%;
max-width: 400px;
+ box-shadow:
+ 0 8px 40px rgba(var(--shadow-rgb), 0.7),
+ 0 2px 8px rgba(var(--shadow-rgb), 0.5),
+ inset 0 1px 0 rgba(var(--gold-rgb), 0.1),
+ inset 0 0 60px rgba(var(--shadow-rgb), 0.2);
}
.createTitle {
+ font-family: "Cinzel", Georgia, serif;
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 1rem;
- color: #c9a84c;
+ color: var(--gold);
+ letter-spacing: 0.05em;
+ text-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.3);
}
.formField {
@@ -121,33 +147,37 @@
}
.formLabel {
+ font-family: "Cinzel", Georgia, serif;
font-size: 0.75rem;
- color: #888;
+ color: var(--text-secondary);
text-transform: uppercase;
font-weight: 600;
+ letter-spacing: 0.05em;
}
.formInput {
padding: 0.5rem 0.75rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.9rem;
+ font-family: "Alegreya", Georgia, serif;
}
.formInput:focus {
outline: none;
- border-color: #c9a84c;
+ border-color: var(--gold);
}
.formSelect {
padding: 0.5rem 0.75rem;
- background: #0f1a30;
- border: 1px solid #333;
- border-radius: 6px;
- color: #e0e0e0;
+ background: var(--bg-inset);
+ border: 1px solid rgba(var(--gold-rgb), 0.15);
+ border-radius: 4px;
+ color: var(--text-primary);
font-size: 0.9rem;
+ font-family: "Alegreya", Georgia, serif;
}
.formActions {
@@ -159,29 +189,39 @@
.formBtn {
padding: 0.5rem 1rem;
- border-radius: 6px;
+ border-radius: 4px;
font-weight: 600;
cursor: pointer;
font-size: 0.9rem;
}
.formBtnPrimary {
- background: #c9a84c;
- color: #1a1a2e;
+ background: var(--btn-gold-bg);
+ color: var(--btn-active-text);
border: none;
+ font-family: "Cinzel", Georgia, serif;
+ box-shadow:
+ 0 2px 4px rgba(var(--shadow-rgb), 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ text-shadow: 0 1px 1px rgba(var(--shadow-rgb), 0.2);
}
.formBtnPrimary:hover {
- background: #d4b65a;
+ background: linear-gradient(
+ 180deg,
+ var(--gold-bright),
+ var(--gold-hover) 40%,
+ var(--gold)
+ );
}
.formBtnSecondary {
background: transparent;
- color: #888;
- border: 1px solid #333;
+ color: var(--text-secondary);
+ border: 1px solid rgba(var(--gold-rgb), 0.2);
}
.formBtnSecondary:hover {
- border-color: #888;
- color: #e0e0e0;
+ border-color: rgba(var(--gold-rgb), 0.4);
+ color: var(--text-primary);
}
diff --git a/client/src/theme.css b/client/src/theme.css
new file mode 100644
index 0000000..58a2023
--- /dev/null
+++ b/client/src/theme.css
@@ -0,0 +1,182 @@
+/* ============================================================
+ Shadowdark Theme System
+ ============================================================
+ All theme-dependent colors use CSS custom properties.
+ Themes are activated via data-theme attribute on .
+ Default (no attribute) = dark-parchment.
+ ============================================================ */
+
+:root {
+ /* --- Color components (for rgba() usage) --- */
+ --gold-rgb: 201, 168, 76;
+ --shadow-rgb: 0, 0, 0;
+ --danger-rgb: 231, 76, 60;
+ --crit-rgb: 255, 215, 0;
+
+ /* --- Backgrounds --- */
+ --bg-body: #0a0908;
+ --bg-body-vignette: rgba(25, 22, 18, 0.6);
+ --bg-surface: #2a2520;
+ --bg-modal: #201c18;
+ --bg-input: #15130f;
+ --bg-inset: rgba(0, 0, 0, 0.3);
+ --bg-overlay: rgba(0, 0, 0, 0.75);
+ --bg-roll-log: #1a1714;
+ --bg-roll-entry: #252118;
+
+ /* --- Text --- */
+ --text-primary: #dad5c9;
+ --text-secondary: #908878;
+ --text-tertiary: #665e52;
+ --text-faint: #4a4238;
+
+ /* --- Gold accent --- */
+ --gold: #c9a84c;
+ --gold-hover: #d4b65a;
+ --gold-bright: #dcc06a;
+ --btn-gold-bg: linear-gradient(180deg, #d4b65a, #c9a84c 40%, #b8973e);
+ --btn-active-text: #1a1a2e;
+
+ /* --- Status colors --- */
+ --hp: #4caf50;
+ --ac: #5dade2;
+ --danger: #e74c3c;
+ --warning: #ff9800;
+ --crit: #ffd700;
+ --copper: #b87333;
+ --silver: #a0a0a0;
+
+ /* --- Textures --- */
+ --texture-surface: url("/textures/parchment-noise.png");
+ --texture-speckle: url("/textures/speckle.png");
+ --texture-body: url("/textures/wood-grain.png");
+ --texture-surface-size: 256px 256px;
+ --texture-speckle-size: 128px 128px;
+ --texture-body-size: 512px 512px;
+}
+
+/* ============================================================
+ Light Parchment — warm beige, like reading by daylight
+ ============================================================ */
+[data-theme="light-parchment"] {
+ --gold-rgb: 140, 110, 40;
+ --shadow-rgb: 80, 60, 30;
+ --danger-rgb: 200, 60, 45;
+
+ --bg-body: #c4b594;
+ --bg-body-vignette: rgba(160, 140, 100, 0.3);
+ --bg-surface: #ddd0b0;
+ --bg-modal: #d5c8a8;
+ --bg-input: #c8bb9a;
+ --bg-inset: rgba(0, 0, 0, 0.06);
+ --bg-overlay: rgba(0, 0, 0, 0.5);
+ --bg-roll-log: #d0c3a0;
+ --bg-roll-entry: #d8cbb0;
+
+ --text-primary: #2a2218;
+ --text-secondary: #5a5040;
+ --text-tertiary: #7a7060;
+ --text-faint: #9a9080;
+
+ --gold: #8a6d2b;
+ --gold-hover: #a0832e;
+ --gold-bright: #b89530;
+ --btn-gold-bg: linear-gradient(180deg, #a08335, #8a6d2b 40%, #7a5d20);
+ --btn-active-text: #f0e8d0;
+
+ --hp: #2e8b34;
+ --ac: #2e7da8;
+ --danger: #c83c2d;
+ --warning: #c87800;
+ --crit: #b8960a;
+ --copper: #8a5520;
+ --silver: #707070;
+
+ --texture-surface: url("/textures/parchment-noise-light.png");
+ --texture-speckle: url("/textures/speckle-light.png");
+ --texture-body: url("/textures/wood-grain-light.png");
+}
+
+/* ============================================================
+ White — clean, minimal, near-white
+ ============================================================ */
+[data-theme="white"] {
+ --gold-rgb: 154, 125, 48;
+ --shadow-rgb: 0, 0, 0;
+ --danger-rgb: 200, 60, 45;
+
+ --bg-body: #f0ede8;
+ --bg-body-vignette: transparent;
+ --bg-surface: #faf8f4;
+ --bg-modal: #f5f3ef;
+ --bg-input: #eae7e0;
+ --bg-inset: rgba(0, 0, 0, 0.04);
+ --bg-overlay: rgba(0, 0, 0, 0.4);
+ --bg-roll-log: #f2f0ec;
+ --bg-roll-entry: #f8f6f2;
+
+ --text-primary: #1a1816;
+ --text-secondary: #555048;
+ --text-tertiary: #807870;
+ --text-faint: #aaa498;
+
+ --gold: #9a7d30;
+ --gold-hover: #b0932e;
+ --gold-bright: #c8a830;
+ --btn-gold-bg: linear-gradient(180deg, #b09335, #9a7d30 40%, #8a6d25);
+ --btn-active-text: #f5f3ef;
+
+ --hp: #2e8b34;
+ --ac: #2e7da8;
+ --danger: #c83c2d;
+ --warning: #c87800;
+ --crit: #8a7010;
+ --copper: #8a5520;
+ --silver: #707070;
+
+ --texture-surface: none;
+ --texture-speckle: none;
+ --texture-body: none;
+}
+
+/* ============================================================
+ Abyss — maximum dark, pitch black
+ ============================================================ */
+[data-theme="abyss"] {
+ --gold-rgb: 212, 182, 90;
+ --shadow-rgb: 0, 0, 0;
+ --danger-rgb: 231, 76, 60;
+
+ --bg-body: #050505;
+ --bg-body-vignette: transparent;
+ --bg-surface: #0e0e14;
+ --bg-modal: #0a0a10;
+ --bg-input: #08080e;
+ --bg-inset: rgba(0, 0, 0, 0.5);
+ --bg-overlay: rgba(0, 0, 0, 0.85);
+ --bg-roll-log: #0a0a10;
+ --bg-roll-entry: #0e0e14;
+
+ --text-primary: #e8e8e8;
+ --text-secondary: #888888;
+ --text-tertiary: #606060;
+ --text-faint: #404040;
+
+ --gold: #d4b65a;
+ --gold-hover: #e0c870;
+ --gold-bright: #edd880;
+ --btn-gold-bg: linear-gradient(180deg, #e0c870, #d4b65a 40%, #c4a64a);
+ --btn-active-text: #0a0a10;
+
+ --hp: #4caf50;
+ --ac: #5dade2;
+ --danger: #e74c3c;
+ --warning: #ff9800;
+ --crit: #ffd700;
+ --copper: #b87333;
+ --silver: #a0a0a0;
+
+ --texture-surface: none;
+ --texture-speckle: none;
+ --texture-body: none;
+}