Compare commits
4 Commits
bc57e4d883
...
74efbe9c1b
| Author | SHA1 | Date | |
|---|---|---|---|
| 74efbe9c1b | |||
| 75bdf92743 | |||
| d22393771a | |||
| 49f1419bc1 |
10
Justfile
Normal file
10
Justfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
user := "admin"
|
||||||
|
host := "chimaera.malzahn.lan"
|
||||||
|
www-root := "/home/admin/caddy/public_html/fabula"
|
||||||
|
|
||||||
|
serve:
|
||||||
|
python3 -m http.server
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
scp fabula-ultima-sheet.html {{ user }}@{{ host }}:{{ www-root }}/index.html
|
||||||
|
scp fabula-ultima-sheet.css {{ user }}@{{ host }}:{{ www-root }}/fabula-ultima-sheet.css
|
||||||
1070
fabula-ultima-sheet.css
Normal file
1070
fabula-ultima-sheet.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,664 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" data-theme="dark">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Fabula Ultima — Character Sheet</title>
|
<title>Fabula Ultima — Character Sheet</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Crimson+Text:ital,wght@0,400;0,600;1,400&family=Inconsolata:wght@400;600&display=swap" rel="stylesheet">
|
<link
|
||||||
<style>
|
href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Crimson+Text:ital,wght@0,400;0,600;1,400&family=Inconsolata:wght@400;600&display=swap"
|
||||||
:root {
|
rel="stylesheet">
|
||||||
--bg: #0d0f0e;
|
<link rel="stylesheet" href="fabula-ultima-sheet.css">
|
||||||
--surface: #141816;
|
<script>(function () { var t = localStorage.getItem('fabulaUltimaTheme') || (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'); document.documentElement.dataset.theme = t; })()</script>
|
||||||
--surface2: #1b1f1d;
|
|
||||||
--surface3: #222826;
|
|
||||||
--border: #2e3830;
|
|
||||||
--border-bright: #3d4f47;
|
|
||||||
--teal: #4ecdc4;
|
|
||||||
--teal-dim: #2a6b66;
|
|
||||||
--gold: #c9a84c;
|
|
||||||
--gold-dim: #7a6130;
|
|
||||||
--red: #c0392b;
|
|
||||||
--text: #d4cfc4;
|
|
||||||
--text-dim: #7a8a82;
|
|
||||||
--text-bright: #ede8dc;
|
|
||||||
--crisis: #e74c3c;
|
|
||||||
--font-display: 'Cinzel', serif;
|
|
||||||
--font-body: 'Crimson Text', serif;
|
|
||||||
--font-mono: 'Inconsolata', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: 16px;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-image:
|
|
||||||
radial-gradient(ellipse at 20% 10%, rgba(78,205,196,0.04) 0%, transparent 50%),
|
|
||||||
radial-gradient(ellipse at 80% 90%, rgba(201,168,76,0.04) 0%, transparent 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── HEADER ─────────────────────────────────────── */
|
|
||||||
header {
|
|
||||||
background: linear-gradient(135deg, var(--surface) 0%, #0f1614 100%);
|
|
||||||
border-bottom: 1px solid var(--border-bright);
|
|
||||||
padding: 20px 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 1.1rem;
|
|
||||||
color: var(--teal);
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.logo::before {
|
|
||||||
content: '✦';
|
|
||||||
color: var(--gold);
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
.tab {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.65rem;
|
|
||||||
letter-spacing: 0.12em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 7px 16px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-dim);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
.tab:hover { border-color: var(--teal-dim); color: var(--teal); }
|
|
||||||
.tab.active { background: var(--teal-dim); border-color: var(--teal); color: var(--text-bright); }
|
|
||||||
|
|
||||||
/* ── LAYOUT ──────────────────────────────────────── */
|
|
||||||
.page { display: none; padding: 28px 32px; max-width: 1400px; margin: 0 auto; }
|
|
||||||
.page.active { display: block; animation: fadeIn 0.3s ease; }
|
|
||||||
|
|
||||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
|
|
||||||
|
|
||||||
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
|
||||||
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; }
|
|
||||||
.col-span-2 { grid-column: span 2; }
|
|
||||||
|
|
||||||
/* ── SECTION ─────────────────────────────────────── */
|
|
||||||
.section {
|
|
||||||
background: var(--surface);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 18px 20px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.section::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0;
|
|
||||||
width: 3px; height: 100%;
|
|
||||||
background: var(--teal-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.6rem;
|
|
||||||
letter-spacing: 0.2em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--teal);
|
|
||||||
margin-bottom: 14px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.section-title .icon { color: var(--gold); }
|
|
||||||
|
|
||||||
/* ── FIELDS ──────────────────────────────────────── */
|
|
||||||
.field { margin-bottom: 14px; }
|
|
||||||
.field:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.18em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-dim);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"], input[type="number"], textarea, select {
|
|
||||||
width: 100%;
|
|
||||||
background: var(--surface2);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
color: var(--text-bright);
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 8px 11px;
|
|
||||||
outline: none;
|
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
input[type="text"]:focus, input[type="number"]:focus, textarea:focus, select:focus {
|
|
||||||
border-color: var(--teal);
|
|
||||||
box-shadow: 0 0 0 2px rgba(78,205,196,0.12);
|
|
||||||
}
|
|
||||||
textarea { resize: vertical; min-height: 70px; }
|
|
||||||
input[type="number"] { font-family: var(--font-mono); }
|
|
||||||
|
|
||||||
.field-row { display: flex; gap: 12px; }
|
|
||||||
.field-row .field { flex: 1; }
|
|
||||||
|
|
||||||
/* ── ATTRIBUTES ──────────────────────────────────── */
|
|
||||||
.attr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
||||||
|
|
||||||
.attr-block {
|
|
||||||
background: var(--surface2);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 12px 14px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attr-name {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.65rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--gold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.attr-inputs { display: flex; gap: 8px; align-items: center; }
|
|
||||||
.attr-inputs input { font-family: var(--font-mono); font-size: 1.2rem; text-align: center; width: 52px; flex-shrink: 0; }
|
|
||||||
.attr-sep { color: var(--text-dim); font-size: 0.8rem; }
|
|
||||||
|
|
||||||
/* ── STATUS EFFECTS ──────────────────────────────── */
|
|
||||||
.status-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
||||||
|
|
||||||
.status-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 7px 10px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: var(--surface2);
|
|
||||||
transition: all 0.15s;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.status-item:hover { border-color: var(--border-bright); }
|
|
||||||
.status-item.active-status { border-color: var(--red); background: rgba(192,57,43,0.12); }
|
|
||||||
|
|
||||||
.status-check {
|
|
||||||
width: 14px; height: 14px;
|
|
||||||
border: 1px solid var(--border-bright);
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--red);
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
.status-item.active-status .status-check { border-color: var(--red); background: rgba(192,57,43,0.2); }
|
|
||||||
|
|
||||||
.status-label {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.58rem;
|
|
||||||
letter-spacing: 0.12em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-dim);
|
|
||||||
}
|
|
||||||
.status-item.active-status .status-label { color: var(--crisis); }
|
|
||||||
|
|
||||||
/* ── HP / MP / IP ────────────────────────────────── */
|
|
||||||
.vital-block {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
background: var(--surface2);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 14px 16px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.vital-block:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
.vital-label {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 700;
|
|
||||||
width: 32px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.vital-label.hp { color: var(--red); }
|
|
||||||
.vital-label.mp { color: var(--teal); }
|
|
||||||
.vital-label.ip { color: var(--gold); }
|
|
||||||
|
|
||||||
.vital-inputs { display: flex; gap: 8px; flex: 1; align-items: center; }
|
|
||||||
.vital-inputs input { text-align: center; font-family: var(--font-mono); font-size: 1.1rem; }
|
|
||||||
.vital-sep { color: var(--text-dim); font-size: 0.8rem; flex-shrink: 0; }
|
|
||||||
|
|
||||||
.vital-formula {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: var(--text-dim);
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.crisis-badge {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.5rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--crisis);
|
|
||||||
border: 1px solid var(--crisis);
|
|
||||||
padding: 2px 6px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── LEVEL / XP ──────────────────────────────────── */
|
|
||||||
.level-display {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--surface2);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.level-num {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 3.5rem;
|
|
||||||
color: var(--gold);
|
|
||||||
line-height: 1;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.level-text {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.25em;
|
|
||||||
color: var(--text-dim);
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: block;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xp-bar-wrap {
|
|
||||||
margin-top: 14px;
|
|
||||||
background: var(--surface3);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
height: 8px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.xp-bar {
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, var(--gold-dim), var(--gold));
|
|
||||||
transition: width 0.5s ease;
|
|
||||||
}
|
|
||||||
.xp-label {
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: var(--text-dim);
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── FABULA POINTS ───────────────────────────────── */
|
|
||||||
.fp-pips {
|
|
||||||
display: flex;
|
|
||||||
gap: 6px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.fp-pip {
|
|
||||||
width: 28px; height: 28px;
|
|
||||||
border: 2px solid var(--border-bright);
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s;
|
|
||||||
background: var(--surface2);
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
}
|
|
||||||
.fp-pip.filled { background: var(--teal); border-color: var(--teal); box-shadow: 0 0 8px rgba(78,205,196,0.4); }
|
|
||||||
.fp-pip:hover { border-color: var(--teal); }
|
|
||||||
|
|
||||||
.fp-rules {
|
|
||||||
margin-top: 14px;
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
.fp-rule {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--text-dim);
|
|
||||||
padding: 3px 0;
|
|
||||||
padding-left: 12px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.fp-rule::before { content: '✦'; position: absolute; left: 0; font-size: 0.5rem; color: var(--gold); top: 5px; }
|
|
||||||
.fp-rule strong { color: var(--text); }
|
|
||||||
|
|
||||||
/* ── BONDS ───────────────────────────────────────── */
|
|
||||||
.bond-block {
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 14px;
|
|
||||||
background: var(--surface2);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.bond-block:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
.bond-header { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
|
|
||||||
.bond-num {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.6rem;
|
|
||||||
color: var(--gold);
|
|
||||||
border: 1px solid var(--gold-dim);
|
|
||||||
width: 22px; height: 22px;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.bond-header input { flex: 1; }
|
|
||||||
|
|
||||||
.bond-feelings {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
.bond-feeling {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: var(--text-dim);
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.bond-feeling.active { color: var(--teal); }
|
|
||||||
.bond-feeling-box {
|
|
||||||
width: 12px; height: 12px;
|
|
||||||
border: 1px solid var(--border-bright);
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 0.6rem;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
.bond-feeling.active .bond-feeling-box { background: var(--teal); border-color: var(--teal); color: #000; }
|
|
||||||
|
|
||||||
/* ── EQUIPMENT ───────────────────────────────────── */
|
|
||||||
.equip-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 120px 1fr;
|
|
||||||
gap: 0;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.equip-row:last-child { border-bottom: none; }
|
|
||||||
|
|
||||||
.equip-slot {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--gold);
|
|
||||||
background: var(--surface3);
|
|
||||||
padding: 10px 12px;
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equip-fields { display: flex; flex-direction: column; gap: 0; }
|
|
||||||
.equip-fields input {
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
padding: 7px 10px;
|
|
||||||
}
|
|
||||||
.equip-fields input:last-child { border-bottom: none; }
|
|
||||||
.equip-fields input::placeholder { color: var(--border-bright); font-style: italic; }
|
|
||||||
|
|
||||||
/* ── MARTIAL CHECKBOXES ──────────────────────────── */
|
|
||||||
.martial-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.martial-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.martial-box {
|
|
||||||
width: 14px; height: 14px;
|
|
||||||
border: 1px solid var(--border-bright);
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 0.65rem;
|
|
||||||
color: var(--teal);
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
.martial-item.checked .martial-box { background: var(--teal-dim); border-color: var(--teal); }
|
|
||||||
.martial-item span {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── CLASSES ─────────────────────────────────────── */
|
|
||||||
.class-block {
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: var(--surface2);
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.class-block:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
.class-header {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 0;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.class-header input {
|
|
||||||
border: none;
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
.class-header input:last-child { border-right: none; }
|
|
||||||
|
|
||||||
.class-skills { padding: 0; }
|
|
||||||
.class-skills textarea {
|
|
||||||
border: none;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
min-height: 80px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── SPELLS TABLE ────────────────────────────────── */
|
|
||||||
.spells-table { width: 100%; border-collapse: collapse; }
|
|
||||||
.spells-table thead tr {
|
|
||||||
background: var(--surface3);
|
|
||||||
}
|
|
||||||
.spells-table th {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.52rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--teal);
|
|
||||||
padding: 8px 10px;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid var(--border-bright);
|
|
||||||
}
|
|
||||||
.spells-table td { border-bottom: 1px solid var(--border); vertical-align: top; }
|
|
||||||
.spells-table td input, .spells-table td textarea {
|
|
||||||
border: none;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
min-height: 32px;
|
|
||||||
padding: 6px 8px;
|
|
||||||
}
|
|
||||||
.spells-table td textarea { min-height: 55px; }
|
|
||||||
.spell-name-col { width: 35%; }
|
|
||||||
.spell-mp-col { width: 12%; }
|
|
||||||
.spell-targets-col { width: 18%; }
|
|
||||||
.spell-dur-col { width: 18%; }
|
|
||||||
.spell-del-col { width: 40px; }
|
|
||||||
.spell-del-btn {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-dim);
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 6px 8px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: color 0.15s;
|
|
||||||
}
|
|
||||||
.spell-del-btn:hover { color: var(--red); }
|
|
||||||
|
|
||||||
.add-btn {
|
|
||||||
margin-top: 8px;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--teal);
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--teal-dim);
|
|
||||||
padding: 6px 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
.add-btn:hover { background: var(--teal-dim); color: var(--text-bright); }
|
|
||||||
|
|
||||||
/* ── DISCIPLINES ─────────────────────────────────── */
|
|
||||||
.disciplines-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
.disc-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.disc-box {
|
|
||||||
width: 13px; height: 13px;
|
|
||||||
border: 1px solid var(--border-bright);
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 0.6rem;
|
|
||||||
color: var(--gold);
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
.disc-item.checked .disc-box { background: var(--gold-dim); border-color: var(--gold); }
|
|
||||||
.disc-item span {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-dim);
|
|
||||||
}
|
|
||||||
.disc-item.checked span { color: var(--gold); }
|
|
||||||
|
|
||||||
/* ── SAVE / LOAD ─────────────────────────────────── */
|
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.btn-save, .btn-load {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
letter-spacing: 0.15em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 7px 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid var(--gold-dim);
|
|
||||||
background: transparent;
|
|
||||||
color: var(--gold);
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
.btn-save:hover { background: var(--gold-dim); color: var(--text-bright); }
|
|
||||||
.btn-load { border-color: var(--border-bright); color: var(--text-dim); }
|
|
||||||
.btn-load:hover { border-color: var(--teal-dim); color: var(--teal); }
|
|
||||||
.btn-export { border-color: var(--teal-dim); color: var(--teal); }
|
|
||||||
.btn-export:hover { background: var(--teal-dim); color: var(--text-bright); }
|
|
||||||
.btn-import { border-color: var(--border-bright); color: var(--text-dim); }
|
|
||||||
.btn-import:hover { border-color: var(--gold-dim); color: var(--gold); }
|
|
||||||
.toolbar-sep { width: 1px; height: 20px; background: var(--border); margin: 0 4px; }
|
|
||||||
|
|
||||||
.save-status {
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--teal);
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
}
|
|
||||||
.save-status.show { opacity: 1; }
|
|
||||||
|
|
||||||
/* ── ORNAMENTS ───────────────────────────────────── */
|
|
||||||
.ornament {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--gold-dim);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
letter-spacing: 0.5em;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.def-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
.def-block {
|
|
||||||
background: var(--surface2);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
padding: 10px 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.def-block label { display: block; margin-bottom: 6px; }
|
|
||||||
.def-block input { text-align: center; font-family: var(--font-mono); font-size: 1.2rem; font-weight: 600; }
|
|
||||||
|
|
||||||
/* ── RESPONSIVE ──────────────────────────────────── */
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.grid-2, .grid-3 { grid-template-columns: 1fr; }
|
|
||||||
.col-span-2 { grid-column: span 1; }
|
|
||||||
header { flex-direction: column; gap: 12px; }
|
|
||||||
.tabs { flex-wrap: wrap; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── TOOLTIP ─────────────────────────────────────── */
|
|
||||||
[title] { cursor: help; }
|
|
||||||
|
|
||||||
/* scrollbar */
|
|
||||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
||||||
::-webkit-scrollbar-track { background: var(--bg); }
|
|
||||||
::-webkit-scrollbar-thumb { background: var(--border-bright); }
|
|
||||||
::-webkit-scrollbar-thumb:hover { background: var(--teal-dim); }
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="logo">Fabula Ultima — Character Sheet</div>
|
<div class="logo">Fabula Ultima</div>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<button class="tab active" onclick="switchTab('main')">Character</button>
|
<button class="tab active" onclick="switchTab('main')">Character</button>
|
||||||
<button class="tab" onclick="switchTab('classes')">Classes</button>
|
<button class="tab" onclick="switchTab('classes')">Classes</button>
|
||||||
@@ -670,8 +27,11 @@
|
|||||||
<div class="toolbar-sep"></div>
|
<div class="toolbar-sep"></div>
|
||||||
<button class="btn-save btn-export" onclick="exportSheet()">↓ Export JSON</button>
|
<button class="btn-save btn-export" onclick="exportSheet()">↓ Export JSON</button>
|
||||||
<button class="btn-load btn-import" onclick="importSheet()">↑ Import JSON</button>
|
<button class="btn-load btn-import" onclick="importSheet()">↑ Import JSON</button>
|
||||||
<input type="file" id="importFileInput" accept=".json,application/json" style="display:none" onchange="handleImportFile(this)">
|
<input type="file" id="importFileInput" accept=".json,application/json" style="display:none"
|
||||||
|
onchange="handleImportFile(this)">
|
||||||
<span class="save-status" id="saveStatus">Saved!</span>
|
<span class="save-status" id="saveStatus">Saved!</span>
|
||||||
|
<div class="toolbar-sep"></div>
|
||||||
|
<button class="btn-theme" id="themeToggle" onclick="toggleTheme()">☀ Light</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -712,7 +72,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Traits (comma-separated)</label>
|
<label>Traits (comma-separated)</label>
|
||||||
<textarea id="charTraits" placeholder="Brave, Reckless, Loyal to a fault…" style="min-height:55px;"></textarea>
|
<textarea id="charTraits" placeholder="Brave, Reckless, Loyal to a fault…"
|
||||||
|
style="min-height:55px;"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -725,8 +86,11 @@
|
|||||||
<div class="level-display">
|
<div class="level-display">
|
||||||
<span class="level-num" id="levelDisplay">1</span>
|
<span class="level-num" id="levelDisplay">1</span>
|
||||||
<span class="level-text">Character Level</span>
|
<span class="level-text">Character Level</span>
|
||||||
<button class="add-btn" style="margin-top:10px; width:100%; justify-content:center;" onclick="adjustLevel(1)">+ Level Up</button>
|
<button class="add-btn" style="margin-top:10px; width:100%; justify-content:center;"
|
||||||
<button class="add-btn" style="margin-top:4px; width:100%; justify-content:center; border-color:var(--border-bright); color:var(--text-dim);" onclick="adjustLevel(-1)">− Level Down</button>
|
onclick="adjustLevel(1)">+ Level Up</button>
|
||||||
|
<button class="add-btn"
|
||||||
|
style="margin-top:4px; width:100%; justify-content:center; border-color:var(--border-bright); color:var(--text-dim);"
|
||||||
|
onclick="adjustLevel(-1)">− Level Down</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -858,7 +222,8 @@
|
|||||||
<div class="crisis-badge" id="crisisBadge" style="display:none">CRISIS</div>
|
<div class="crisis-badge" id="crisisBadge" style="display:none">CRISIS</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="add-btn" style="padding:4px 8px;" onclick="calcHP()" title="Auto-calculate from Might">Calc</button>
|
<button class="add-btn" style="padding:4px 8px;" onclick="calcHP()"
|
||||||
|
title="Auto-calculate from Might">Calc</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="vital-block">
|
<div class="vital-block">
|
||||||
@@ -871,7 +236,8 @@
|
|||||||
<input type="number" id="mpCur" placeholder="Cur">
|
<input type="number" id="mpCur" placeholder="Cur">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="add-btn" style="padding:4px 8px;" onclick="calcMP()" title="Auto-calculate from Willpower">Calc</button>
|
<button class="add-btn" style="padding:4px 8px;" onclick="calcMP()"
|
||||||
|
title="Auto-calculate from Willpower">Calc</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="vital-block">
|
<div class="vital-block">
|
||||||
@@ -897,7 +263,9 @@
|
|||||||
<div style="display:flex; align-items:center; gap:14px; flex-wrap:wrap;">
|
<div style="display:flex; align-items:center; gap:14px; flex-wrap:wrap;">
|
||||||
<div>
|
<div>
|
||||||
<label>Current FP</label>
|
<label>Current FP</label>
|
||||||
<input type="number" id="fpCount" value="0" min="0" max="20" style="width:70px; text-align:center; font-size:1.4rem; font-family:var(--font-mono);" oninput="renderFP()">
|
<input type="number" id="fpCount" value="0" min="0" max="20"
|
||||||
|
style="width:70px; text-align:center; font-size:1.4rem; font-family:var(--font-mono);"
|
||||||
|
oninput="renderFP()">
|
||||||
</div>
|
</div>
|
||||||
<div class="fp-pips" id="fpPips"></div>
|
<div class="fp-pips" id="fpPips"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -906,7 +274,8 @@
|
|||||||
<div class="fp-rule"><strong>+1 FP</strong> when a Villain makes an entrance.</div>
|
<div class="fp-rule"><strong>+1 FP</strong> when a Villain makes an entrance.</div>
|
||||||
<div class="fp-rule"><strong>+1 FP</strong> when you fumble a Check.</div>
|
<div class="fp-rule"><strong>+1 FP</strong> when you fumble a Check.</div>
|
||||||
<div class="fp-rule"><strong>+2 FP</strong> if you surrender at zero HP.</div>
|
<div class="fp-rule"><strong>+2 FP</strong> if you surrender at zero HP.</div>
|
||||||
<div class="fp-rule" style="margin-top:6px;"><strong>Spend 1 FP</strong> to invoke a trait: reroll one or both dice.</div>
|
<div class="fp-rule" style="margin-top:6px;"><strong>Spend 1 FP</strong> to invoke a trait: reroll one or both
|
||||||
|
dice.</div>
|
||||||
<div class="fp-rule"><strong>Spend 1 FP</strong> to invoke a bond: add its strength to the result.</div>
|
<div class="fp-rule"><strong>Spend 1 FP</strong> to invoke a bond: add its strength to the result.</div>
|
||||||
<div class="fp-rule"><strong>Spend 1 FP</strong> to alter the story.</div>
|
<div class="fp-rule"><strong>Spend 1 FP</strong> to alter the story.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1005,7 +374,8 @@
|
|||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title"><span class="icon">★</span> Heroic Skills</div>
|
<div class="section-title"><span class="icon">★</span> Heroic Skills</div>
|
||||||
<textarea id="heroicSkills" placeholder="Record your heroic skill abilities here…" style="min-height:100px;"></textarea>
|
<textarea id="heroicSkills" placeholder="Record your heroic skill abilities here…"
|
||||||
|
style="min-height:100px;"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -1035,14 +405,27 @@
|
|||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title"><span class="icon">⊕</span> Rituals</div>
|
<div class="section-title"><span class="icon">⊕</span> Rituals</div>
|
||||||
<div class="disciplines-row" id="disciplinesRow">
|
<div class="disciplines-row" id="disciplinesRow">
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Arcanism</span></div>
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Chimerism</span></div>
|
<div class="disc-box"></div><span>Arcanism</span>
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Elementalism</span></div>
|
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Entropism</span></div>
|
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Ritualism</span></div>
|
|
||||||
<div class="disc-item" onclick="toggleDisc(this)"><div class="disc-box"></div><span>Spiritism</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<textarea id="ritualsNotes" placeholder="Record ritual details, components, and notes here…" style="min-height:120px;"></textarea>
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
|
<div class="disc-box"></div><span>Chimerism</span>
|
||||||
|
</div>
|
||||||
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
|
<div class="disc-box"></div><span>Elementalism</span>
|
||||||
|
</div>
|
||||||
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
|
<div class="disc-box"></div><span>Entropism</span>
|
||||||
|
</div>
|
||||||
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
|
<div class="disc-box"></div><span>Ritualism</span>
|
||||||
|
</div>
|
||||||
|
<div class="disc-item" onclick="toggleDisc(this)">
|
||||||
|
<div class="disc-box"></div><span>Spiritism</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<textarea id="ritualsNotes" placeholder="Record ritual details, components, and notes here…"
|
||||||
|
style="min-height:120px;"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -1076,6 +459,9 @@ function switchTab(tab) {
|
|||||||
|
|
||||||
// ── INIT ───────────────────────────────────────────
|
// ── INIT ───────────────────────────────────────────
|
||||||
function init() {
|
function init() {
|
||||||
|
const savedTheme = localStorage.getItem('fabulaUltimaTheme')
|
||||||
|
|| (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
||||||
|
document.getElementById('themeToggle').textContent = savedTheme === 'light' ? '☾ Dark' : '☀ Light';
|
||||||
renderStatuses();
|
renderStatuses();
|
||||||
renderFP();
|
renderFP();
|
||||||
renderBonds();
|
renderBonds();
|
||||||
@@ -1397,10 +783,20 @@ function handleImportFile(input) {
|
|||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── THEME ──────────────────────────────────────────
|
||||||
|
function toggleTheme() {
|
||||||
|
const html = document.documentElement;
|
||||||
|
const goLight = html.dataset.theme !== 'light';
|
||||||
|
html.dataset.theme = goLight ? 'light' : 'dark';
|
||||||
|
document.getElementById('themeToggle').textContent = goLight ? '☾ Dark' : '☀ Light';
|
||||||
|
localStorage.setItem('fabulaUltimaTheme', html.dataset.theme);
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-save every 30s
|
// Auto-save every 30s
|
||||||
setInterval(saveSheet, 30000);
|
setInterval(saveSheet, 30000);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user