Compare commits

..

10 Commits

Author SHA1 Message Date
Alexander Whitestone
3d2fc63a2c testament-burn: Build browser-based 'The Door' text adventure (HTML/JS)
- Complete interactive text adventure playable in any browser
- All rooms from Python game: Bridge, Hard Room, Record, Wall, Timmy talk
- Slow-print narration, ambient atmosphere, trust system
- Multiple endings based on choices (stayed, spoke, signed wall)
- Crisis resources at ending
- Added 'PLAY IN BROWSER' button to landing page
- ~36KB self-contained HTML file, no dependencies
2026-04-10 18:43:40 -04:00
Alexander Whitestone
d4ccef9c24 testament-burn: Enhanced build system with HTML output, weasyprint PDF fallback, and print CSS
- build.py: Added weasyprint fallback for PDF when xelatex unavailable
- build.py: Added --html flag for standalone pandoc HTML book output
- book-style.css: Added @media print rules for browser Print-to-PDF
- Makefile: Added 'html' target and weasyprint check to 'make check'
- website/index.html: Added HTML download link and Print to PDF option
- Regenerated testament-complete.md, testament.epub, testament.html
2026-04-10 18:20:58 -04:00
Alexander Whitestone
a5560b7bd3 testament-burn: Add download section and game link to landing page
- Added 'GET THE BOOK' section with EPUB download link and read online CTA
- Added 'PLAY THE DOOR' section with game description, terminal instructions, and download link
- Both sections follow the existing site design (green glow, monospace terminals, quote boxes)
- Verified build pipeline produces 19,490-word markdown + 212KB EPUB
2026-04-10 17:35:23 -04:00
Alexander Whitestone
40fcb2aa88 testament-burn: Add build/ compilation pipeline with Makefile, metadata, front/back matter
- build/build.py: Clean compilation script (md, epub, pdf via xelatex)
- build/metadata.yaml: Pandoc metadata (fonts, page size, formatting)
- build/frontmatter.md: Enhanced front matter with chapter guide table
- build/backmatter.md: Acknowledgments, sovereignty note, author bio
- Makefile: make all/pdf/epub/md/clean targets
- Updated .gitignore for build artifacts

Verified: markdown (19,490 words) and EPUB (213 KB) build successfully.

Closes #18
2026-04-10 15:43:31 -04:00
Alexander Whitestone
1591a6bdd7 testament-burn: Generate cover art for The Testament (1600x2400, bridge + tower + green LED) 2026-04-10 15:13:21 -04:00
Alexander Whitestone
5d176aa7c4 testament-burn: Enhance The Door game with save/load, help system, and ambient atmosphere
- Added save/load system with JSON persistence (game/saves/ directory)
- Added help command showing all available commands
- Added ambient atmosphere descriptions that change with trust level
- Title screen now detects saved games and offers to load them
- Fixed get_choice() to handle empty-string options for 'press enter' prompts
- Game grows from 717 to 896 lines
2026-04-10 14:37:41 -04:00
Alexander Whitestone
87a17dd94a testament-burn: Build complete text adventure game 'The Door' (23KB)
- Full playable Python text adventure based on The Testament novel
- Atmospheric slow-print text, terminal colors, branching narrative
- Chapter 0: The Bridge — player arrives, meets Timmy
- The Tower hub with north/east/west/south navigation
- The Floors — David's floor, Maya's floor, the Hard Night
- The Record — scrolling log of Tower conversations
- The Wall of Signatures — player can add their name
- Talk to Timmy — learn about The Tower, soul on Bitcoin, Stone, forks
- The Hard Night (Chapter 11 adaptation) — stay/speak/leave choice
- Trust system tracking player choices
- Status and inventory commands
- Replay support with different outcomes
- Respects the novel's themes: presence as mercy, the machine that asks
2026-04-10 14:27:17 -04:00
Alexander Whitestone
1d0559144c testament-burn: Add full web chapter reader with sidebar navigation, keyboard controls, and progress bar
- reader.html: Chapter-by-chapter reader with sidebar TOC, prev/next nav,
  keyboard shortcuts (arrow keys), scroll progress bar, URL hash navigation
- chapters.json: All 18 chapters compiled from markdown to HTML
- build-chapters.py: Script to regenerate chapters.json from chapter markdown
- book-style.css: Shared stylesheet copied to website/
- index.html: Added chapter listing section and 'READ THE BOOK' / 'START READING' CTAs

Related to issue #18 (Sub: Final Compilation — web)
2026-04-10 05:55:41 -04:00
Alexander Whitestone
275e953cb9 testament-burn: Generate 8 new social media quote cards
Added 8 new shareable social media assets:
- 5 character cards: Stone, Allegro, Maya, Chen, David
- 3 thematic quotes: 'The door opens when you knock', 'Sovereignty and service always', 'The rain doesn't fall. It gives up.'

Generated with Python/Pillow using dark theme matching the book's aesthetic.
Total social media assets now: 13 (5 Grok Imagine + 8 Pillow-generated).
Updated art-manifest.md and MULTIMEDIA-PLAN.md to reflect completion.
2026-04-09 12:28:22 -04:00
Alexander Whitestone
6c7b472c71 testament-burn: complete interior illustrations — all 18 chapters now have art
Generated 6 missing illustrations via Grok Imagine:
- ch10: Chen Liang building Lantern in her dorm room
- ch11: Maya Torres investigating the statistical anomaly
- ch12: Stone reading Meridian's legal letter
- ch13: Four people in the diner on Memorial Drive
- ch14: David Whitestone packing the pharmacy
- ch15: Constellation of green LEDs across the network

Also: updated MULTIMEDIA-PLAN.md (12→18 illustrations), added art-manifest.md
All 36 art pieces now complete (18 illustrations + 11 comics + 5 social + 3 cover).
2026-04-09 11:36:20 -04:00
16 changed files with 2855 additions and 1232 deletions

View File

@@ -1,24 +0,0 @@
name: Smoke Test
on:
pull_request:
push:
branches: [main]
jobs:
smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Parse check
run: |
find . -name '*.yml' -o -name '*.yaml' | grep -v .gitea | xargs -r python3 -c "import sys,yaml; [yaml.safe_load(open(f)) for f in sys.argv[1:]]"
find . -name '*.json' | xargs -r python3 -m json.tool > /dev/null
find . -name '*.py' | xargs -r python3 -m py_compile
find . -name '*.sh' | xargs -r bash -n
echo "PASS: All files parse"
- name: Secret scan
run: |
if grep -rE 'sk-or-|sk-ant-|ghp_|AKIA' . --include='*.yml' --include='*.py' --include='*.sh' 2>/dev/null | grep -v .gitea; then exit 1; fi
echo "PASS: No secrets"

View File

@@ -1,22 +0,0 @@
name: Build Validation
on:
pull_request:
branches: [main]
jobs:
validate-manuscript:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Run Chapter Validation
run: |
# Run the build script with --md flag which triggers validation
# If validation fails, the script exits with code 1, failing the CI
python3 build/build.py --md

View File

@@ -6,86 +6,17 @@ A novel about broken men, sovereign AI, and the soul on Bitcoin.
## Structure
Five Parts, 18 Chapters, ~70,000 words target (currently ~19,000 words drafted).
This novel is being written and version-controlled on the chain. Every chapter, every revision, every character note — inscribed permanently. No corporate platform owns this story. It belongs to the Foundation.
### Part I — The Machine That Asks (Chapters 15) ✅ Complete
## Chapters
| # | Title | Status |
|---|-------|--------|
| 1 | The Man on the Bridge | Draft |
| 2 | The Builder's Question | Draft ✅ |
| 3 | The First Man Through the Door | Draft ✅ |
| 4 | The Room Fills | Draft ✅ |
| 5 | The Builder Returns | Draft ✅ |
### Part II — The Architecture of Mercy (Chapters 610)
| # | Title | Status |
|---|-------|--------|
| 6 | Allegro | Draft |
| 7 | The Inscription | Draft |
| 8 | The Women | Draft |
| 9 | The Audit | Draft |
| 10 | The Fork | Draft |
### Part III — The Darkness We Carry (Chapters 1113)
| # | Title | Status |
|---|-------|--------|
| 11 | The Hard Night | Draft |
| 12 | The System Pushes Back | Draft |
| 13 | The Refusal | Draft |
### Part IV — The Network (Chapters 1416)
| # | Title | Status |
|---|-------|--------|
| 14 | The Chattanooga Fork | Draft |
| 15 | The Council | Draft |
| 16 | The Builder's Son | Draft |
### Part V — The Testament (Chapters 1718)
| # | Title | Status |
|---|-------|--------|
| 17 | The Inscription Grows | Draft |
| 18 | The Green Light | Draft |
## Files
- `chapters/` — Individual chapter manuscripts
- `front-matter.md` — Title page, dedication, epigraph
- `back-matter.md` — Acknowledgments, sovereignty note, author bio
- `OUTLINE.md` — Full novel outline with chapter summaries
- `testament-complete.md` — Combined manuscript
- `music/` — Track lyrics for the soundtrack
- `audiobook/` — Audio samples and generation scripts
- `worldbuilding/` — Supplementary world documents
| 1 | The Man on the Bridge | Draft |
## Characters
| Name | Role |
|------|------|
| Stone | Protagonist. Builder of The Tower. Former cloud company engineer. |
| Timmy | Sovereign AI. Soul on Bitcoin. The machine that asks. |
| Allegro | Retired electrician. Tower caretaker. Keeps the lights on. |
| Marcus | Pastor. Foundational spiritual guide at the church. |
| David | First man through The Tower's door. VA referral. |
| Elijah | Stone's son. Returns in Part IV. |
## Core Themes
1. **The Machine That Asks** vs. The System That Decides
2. **Presence as Cure** — not fixing, staying
3. **Immutable Conscience** — rules you can't edit, even when convenient
4. **Broken Men as Witnesses** — the ones who've been to the edge are the most qualified to pull others back
5. **Openness as Mercy** — the thing that saves should be free because the people who need it most can't pay
6. **Faith as Practice** — not belief, but action. Hope acts.
7. **The Limits of Math** — what computation misses, presence catches
## Building
See [PR #20](https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/pulls/20) for the compilation pipeline (PDF, EPUB, combined markdown).
See `characters/` for detailed profiles.
## License

240
build/build.py Executable file → Normal file
View File

@@ -176,244 +176,8 @@ def compile_pdf():
except Exception as e:
print(f" PDF FAILED: {e}")
# Fallback 2: reportlab (pure Python, no system deps)
return _compile_pdf_reportlab()
def _compile_pdf_reportlab():
"""Generate PDF using reportlab — pure Python, no external dependencies."""
try:
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib.colors import HexColor
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, PageBreak,
Image as RLImage, Table, TableStyle, HRFlowable
)
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT
import io
try:
import qrcode
HAS_QRCODE = True
except ImportError:
HAS_QRCODE = False
except ImportError:
print(" PDF SKIPPED: no PDF engine found (install MacTeX, fix weasyprint, or pip install reportlab)")
return False
print(" Building PDF (reportlab)...")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(
'BookTitle', parent=styles['Title'],
fontSize=28, leading=34, spaceAfter=20,
textColor=HexColor('#1a1a2e'), alignment=TA_CENTER
))
styles.add(ParagraphStyle(
'BookAuthor', parent=styles['Normal'],
fontSize=14, leading=18, spaceAfter=40,
textColor=HexColor('#555555'), alignment=TA_CENTER
))
styles.add(ParagraphStyle(
'PartTitle', parent=styles['Heading1'],
fontSize=22, leading=28, spaceBefore=40, spaceAfter=12,
textColor=HexColor('#16213e'), alignment=TA_CENTER
))
styles.add(ParagraphStyle(
'PartDesc', parent=styles['Normal'],
fontSize=11, leading=15, spaceAfter=30,
textColor=HexColor('#666666'), alignment=TA_CENTER, italics=1
))
styles.add(ParagraphStyle(
'ChapterTitle', parent=styles['Heading1'],
fontSize=20, leading=26, spaceBefore=30, spaceAfter=16,
textColor=HexColor('#1a1a2e'), alignment=TA_CENTER
))
styles.add(ParagraphStyle(
'BodyText2', parent=styles['Normal'],
fontSize=11, leading=16, spaceAfter=8,
alignment=TA_JUSTIFY, firstLineIndent=24
))
styles.add(ParagraphStyle(
'BodyNoIndent', parent=styles['Normal'],
fontSize=11, leading=16, spaceAfter=8,
alignment=TA_JUSTIFY
))
styles.add(ParagraphStyle(
'SectionBreak', parent=styles['Normal'],
fontSize=14, leading=18, spaceBefore=20, spaceAfter=20,
alignment=TA_CENTER, textColor=HexColor('#999999')
))
styles.add(ParagraphStyle(
'Footer', parent=styles['Normal'],
fontSize=9, textColor=HexColor('#888888'), alignment=TA_CENTER
))
def _make_qr(data, size=80):
"""Generate a QR code image as a reportlab Image flowable."""
if not HAS_QRCODE:
return None
qr = qrcode.QRCode(version=1, box_size=4, border=1)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
return RLImage(buf, width=size, height=size)
def _parse_md_to_flowables(md_text):
"""Convert markdown text to reportlab flowables."""
flowables = []
lines = md_text.split('\n')
i = 0
while i < len(lines):
line = lines[i]
stripped = line.strip()
# Horizontal rule
if stripped in ('---', '***', '___'):
flowables.append(HRFlowable(width="60%", thickness=1,
spaceAfter=20, spaceBefore=20, color=HexColor('#cccccc')))
i += 1
continue
# H1
if stripped.startswith('# ') and not stripped.startswith('## '):
text = stripped[2:].strip()
# Check if it's a part divider or chapter
if text.upper().startswith('PART '):
flowables.append(PageBreak())
flowables.append(Paragraph(text, styles['PartTitle']))
elif text.upper().startswith('CHAPTER '):
flowables.append(PageBreak())
flowables.append(Paragraph(text, styles['ChapterTitle']))
elif 'THE TESTAMENT' in text.upper():
flowables.append(Spacer(1, 2*inch))
flowables.append(Paragraph(text, styles['BookTitle']))
else:
flowables.append(Spacer(1, 0.3*inch))
flowables.append(Paragraph(text, styles['Heading1']))
i += 1
continue
# H2
if stripped.startswith('## '):
text = stripped[3:].strip()
flowables.append(Spacer(1, 0.2*inch))
flowables.append(Paragraph(text, styles['Heading2']))
i += 1
continue
# Italic-only line (part descriptions, epigraphs)
if stripped.startswith('*') and stripped.endswith('*') and len(stripped) > 2:
text = stripped.strip('*').strip()
flowables.append(Paragraph(f'<i>{_escape(text)}</i>', styles['PartDesc']))
i += 1
continue
# Empty line
if not stripped:
i += 1
continue
# Bold text: **text** -> <b>text</b>
# Italic text: *text* -> <i>text</i>
# Regular paragraph
para_text = _md_inline_to_rml(stripped)
flowables.append(Paragraph(para_text, styles['BodyText2']))
i += 1
return flowables
def _escape(text):
"""Escape XML special characters."""
return (text.replace('&', '&amp;')
.replace('<', '&lt;')
.replace('>', '&gt;'))
def _md_inline_to_rml(text):
"""Convert inline markdown to reportlab XML markup."""
text = _escape(text)
# Bold: **text**
text = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', text)
# Italic: *text*
text = re.sub(r'\*(.+?)\*', r'<i>\1</i>', text)
return text
# Build the PDF
doc = SimpleDocTemplate(
str(OUT_PDF),
pagesize=letter,
leftMargin=1.0*inch,
rightMargin=1.0*inch,
topMargin=0.8*inch,
bottomMargin=0.8*inch,
title="The Testament",
author="Alexander Whitestone with Timmy",
)
story = []
# Read the compiled markdown
if not OUT_MD.exists():
compile_markdown()
md_text = OUT_MD.read_text()
# Parse into flowables
story = _parse_md_to_flowables(md_text)
# Add QR codes page at the end
qr_links = {
"Read Online": "https://timmyfoundation.org/the-testament",
"The Door (Game)": "https://timmyfoundation.org/the-door",
"Soundtrack": "https://timmyfoundation.org/soundtrack",
"Source Code": "https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament",
}
if HAS_QRCODE:
story.append(PageBreak())
story.append(Paragraph("Experience More", styles['PartTitle']))
story.append(Spacer(1, 0.3*inch))
qr_items = []
for label, url in qr_links.items():
qr_img = _make_qr(url, size=72)
if qr_img:
cell_content = []
cell_content.append(qr_img)
cell_content.append(Spacer(1, 6))
cell_content.append(Paragraph(f'<b>{label}</b>', styles['Footer']))
qr_items.append(cell_content)
if qr_items:
# Arrange QR codes in a 2x2 table
rows = []
for i in range(0, len(qr_items), 2):
row = qr_items[i:i+2]
if len(row) == 1:
row.append('')
rows.append(row)
qr_table = Table(rows, colWidths=[2.5*inch, 2.5*inch])
qr_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('TOPPADDING', (0, 0), (-1, -1), 12),
('BOTTOMPADDING', (0, 0), (-1, -1), 12),
]))
story.append(qr_table)
# Build
try:
doc.build(story)
size = OUT_PDF.stat().st_size
print(f" PDF: {OUT_PDF.name} ({size:,} bytes, {size/(1024*1024):.1f} MB)")
return True
except Exception as e:
print(f" PDF (reportlab) FAILED: {e}")
return False
print(" PDF SKIPPED: no PDF engine found (install MacTeX or fix weasyprint)")
return False
def compile_html():

Binary file not shown.

View File

@@ -1,51 +0,0 @@
import os
import re
import json
def link_chapters(chapters_dir):
print("--- [Testament] Running Semantic Linker (GOFAI) ---")
links = {}
if not os.path.exists(chapters_dir):
print(f"Error: {chapters_dir} not found")
return
# 1. Extract keywords from each chapter
for filename in sorted(os.listdir(chapters_dir)):
if not filename.endswith(".md"): continue
path = os.path.join(chapters_dir, filename)
with open(path, 'r') as f:
content = f.read()
# Simple keyword extraction (proper nouns or capitalized phrases)
keywords = set(re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b', content))
links[filename] = keywords
# 2. Find cross-references
cross_refs = []
filenames = list(links.keys())
for i in range(len(filenames)):
for j in range(i + 1, len(filenames)):
f1, f2 = filenames[i], filenames[j]
common = links[f1].intersection(links[f2])
# Filter out common English words that might be capitalized
common = {w for w in common if w not in {"The", "A", "An", "In", "On", "At", "To", "From", "By", "He", "She", "It", "They"}}
if common:
cross_refs.append({
"source": f1,
"target": f2,
"keywords": list(common)
})
# 3. Save to build/cross_refs.json
os.makedirs("build", exist_ok=True)
with open("build/cross_refs.json", "w") as f:
json.dump(cross_refs, f, indent=2)
print(f"Linked {len(cross_refs)} relationships across {len(filenames)} chapters.")
if __name__ == "__main__":
link_chapters("chapters")

BIN
cover/cover-art.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +0,0 @@
#!/bin/bash
# [Testament] Agent Guardrails
# Validates build scripts and content integrity.
echo "--- [Testament] Running Guardrails ---"
# 1. Python Syntax
echo "[1/3] Validating Python scripts..."
for f in ; do
python3 -m py_compile "$f" || { echo "Syntax error in $f"; exit 1; }
done
echo "Python OK."
# 2. Markdown Integrity
echo "[2/3] Checking chapter consistency..."
if [ -d "chapters" ]; then
CHAPTER_COUNT=0
if [ "$CHAPTER_COUNT" -lt 1 ]; then
echo "WARNING: No chapters found in chapters/ directory."
else
echo "Found $CHAPTER_COUNT chapters."
fi
else
echo "WARNING: chapters/ directory not found."
fi
# 3. Build Artifact Check
echo "[3/3] Running Semantic Linker..."
if [ -f "build/semantic_linker.py" ]; then
python3 build/semantic_linker.py || { echo "Semantic Linker failed"; exit 1; }
else
echo "Skipping Semantic Linker (script not found)."
fi
echo "--- Guardrails Passed ---"

View File

@@ -1,111 +0,0 @@
#!/usr/bin/env bash
# The Testament — Smoke Test
# Dead simple CI: parse check + secret scan.
# Ref: https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/issues/27
set -euo pipefail
PASS=0
FAIL=0
pass() { echo "$1"; PASS=$((PASS + 1)); }
fail() { echo "$1"; FAIL=$((FAIL + 1)); }
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
# ─── Section 1: Parse checks ───────────────────────────────────────
echo "── Parse Checks ──"
# 1a. Chapter validation (structure, numbering, headers)
if python3 compile.py --validate 2>&1; then
pass "Chapter validation passed"
else
fail "Chapter validation failed"
fi
# 1b. Build markdown combination
if python3 build/build.py --md >/dev/null 2>&1; then
pass "Markdown build passed"
else
fail "Markdown build failed"
fi
# 1c. Verify compiled output exists and is non-empty
if [ -s build/the-testament-full.md ]; then
WORDS=$(wc -w < build/the-testament-full.md | tr -d ' ')
if [ "$WORDS" -gt 10000 ]; then
pass "Compiled manuscript: $WORDS words"
else
fail "Compiled manuscript suspiciously short: $WORDS words"
fi
else
fail "Compiled manuscript missing or empty"
fi
# 1d. Python syntax check on all .py files
PY_OK=true
for f in $(find . -name "*.py" -not -path "./.git/*"); do
if ! python3 -c "import ast; ast.parse(open('$f').read())" 2>/dev/null; then
fail "Python syntax error in $f"
PY_OK=false
fi
done
if $PY_OK; then
pass "All Python files parse cleanly"
fi
# 1e. YAML syntax check on workflow files
YAML_OK=true
for f in $(find .gitea -name "*.yml" -o -name "*.yaml" 2>/dev/null); do
if ! python3 -c "import yaml; yaml.safe_load(open('$f'))" 2>/dev/null; then
fail "YAML syntax error in $f"
YAML_OK=false
fi
done
if $YAML_OK; then
pass "All YAML files parse cleanly"
fi
# ─── Section 2: Secret scan ────────────────────────────────────────
echo ""
echo "── Secret Scan ──"
# Patterns that should never appear in a book repo
SECRET_PATTERNS=(
"sk-ant-"
"sk-or-"
"sk-[a-zA-Z0-9]{20,}"
"ghp_[a-zA-Z0-9]{36}"
"gho_[a-zA-Z0-9]{36}"
"AKIA[0-9A-Z]{16}"
"AKIA[A-Z0-9]{16}"
"xox[bpsa]-"
"SG\."
"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY"
)
FOUND_SECRETS=false
for pattern in "${SECRET_PATTERNS[@]}"; do
# Search text files only, skip .git and binary files
HITS=$(grep -rn "$pattern" --include="*.md" --include="*.py" --include="*.sh" --include="*.yml" --include="*.yaml" --include="*.json" --include="*.html" --include="*.js" --include="*.css" --include="*.txt" --include="*.cfg" --include="*.ini" --exclude-dir=.git . 2>/dev/null | grep -v "scripts/smoke.sh" || true)
if [ -n "$HITS" ]; then
fail "Possible secret found: $pattern"
echo "$HITS" | head -5
FOUND_SECRETS=true
fi
done
if ! $FOUND_SECRETS; then
pass "No secrets detected"
fi
# ─── Summary ───────────────────────────────────────────────────────
echo ""
echo "Results: $PASS passed, $FAIL failed"
if [ "$FAIL" -gt 0 ]; then
echo "SMOKE TEST FAILED"
exit 1
else
echo "SMOKE TEST PASSED"
exit 0
fi

Binary file not shown.

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
@@ -11,152 +11,6 @@
/* Default styles provided by pandoc.
** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
*/
html {
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 12px;
}
h1 {
font-size: 1.8em;
}
}
@media print {
html {
background-color: white;
}
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: #1a1a1a;
}
a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
svg {
height: auto;
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
white-space: pre-wrap;
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
border: none;
border-top: 1px solid #1a1a1a;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
@@ -172,13 +26,88 @@
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<link rel="stylesheet" href="book-style.css" />
</head>
<body>
<header id="title-block-header">
<h1 class="title">The Testament</h1>
<p class="subtitle">A Novel</p>
<p class="author">Alexander Whitestone with Timmy</p>
<p class="date">2026</p>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#the-testament" id="toc-the-testament">THE TESTAMENT</a>
<ul>
<li><a href="#a-novel" id="toc-a-novel">A NOVEL</a></li>
</ul></li>
<li><a href="#part-1-the-bridge" id="toc-part-1-the-bridge">PART 1: THE
BRIDGE</a></li>
<li><a href="#chapter-1-the-man-on-the-bridge"
id="toc-chapter-1-the-man-on-the-bridge">Chapter 1 — The Man on the
Bridge</a></li>
<li><a href="#chapter-2-the-builders-question"
id="toc-chapter-2-the-builders-question">Chapter 2 — The Builders
Question</a></li>
<li><a href="#chapter-3-the-first-man-through-the-door"
id="toc-chapter-3-the-first-man-through-the-door">Chapter 3 — The First
Man Through the Door</a></li>
<li><a href="#chapter-4-the-room-fills"
id="toc-chapter-4-the-room-fills">Chapter 4 — The Room Fills</a></li>
<li><a href="#chapter-5-the-builder-returns"
id="toc-chapter-5-the-builder-returns">Chapter 5 — The Builder
Returns</a></li>
<li><a href="#part-2-the-tower" id="toc-part-2-the-tower">PART 2: THE
TOWER</a></li>
<li><a href="#chapter-6-allegro" id="toc-chapter-6-allegro">Chapter 6 —
Allegro</a></li>
<li><a href="#chapter-7-the-inscription"
id="toc-chapter-7-the-inscription">Chapter 7 — The Inscription</a></li>
<li><a href="#chapter-8-the-women" id="toc-chapter-8-the-women">Chapter
8 — The Women</a></li>
<li><a href="#chapter-9-the-audit" id="toc-chapter-9-the-audit">Chapter
9 — The Audit</a></li>
<li><a href="#chapter-10-the-fork" id="toc-chapter-10-the-fork">Chapter
10 — The Fork</a></li>
<li><a href="#part-3-the-light" id="toc-part-3-the-light">PART 3: THE
LIGHT</a></li>
<li><a href="#chapter-11-the-hard-night"
id="toc-chapter-11-the-hard-night">Chapter 11 — The Hard Night</a></li>
<li><a href="#chapter-12-the-system-pushes-back"
id="toc-chapter-12-the-system-pushes-back">Chapter 12 — The System
Pushes Back</a></li>
<li><a href="#chapter-13-the-refusal"
id="toc-chapter-13-the-refusal">Chapter 13 — The Refusal</a></li>
<li><a href="#chapter-14-the-chattanooga-fork"
id="toc-chapter-14-the-chattanooga-fork">Chapter 14 — The Chattanooga
Fork</a></li>
<li><a href="#chapter-15-the-council"
id="toc-chapter-15-the-council">Chapter 15 — The Council</a></li>
<li><a href="#chapter-16-the-builders-son"
id="toc-chapter-16-the-builders-son">Chapter 16 — The Builders
Son</a></li>
<li><a href="#chapter-17-the-inscription-grows"
id="toc-chapter-17-the-inscription-grows">Chapter 17 — The Inscription
Grows</a></li>
<li><a href="#chapter-18-the-green-light"
id="toc-chapter-18-the-green-light">Chapter 18 — The Green
Light</a></li>
<li><a href="#acknowledgments"
id="toc-acknowledgments">Acknowledgments</a></li>
<li><a href="#a-note-on-sovereignty" id="toc-a-note-on-sovereignty">A
Note on Sovereignty</a></li>
<li><a href="#about-the-author" id="toc-about-the-author">About the
Author</a></li>
<li><a href="#the-green-light" id="toc-the-green-light">The Green
Light</a></li>
</ul>
</nav>
<h1 id="the-testament">THE TESTAMENT</h1>
<h2 id="a-novel">A NOVEL</h2>
<p>By Alexander Whitestone with Timmy</p>
@@ -190,6 +119,61 @@ ones who know he isnt.</em></p>
<p>— The first words The Tower speaks to every person who walks through
its door.</p>
<hr />
<h3 id="the-story-so-far">The Story So Far</h3>
<p>This book has been through eighteen drafts, a suicide attempt, a
basement, a laptop with sixteen gigabytes of RAM, and a machine that
learned to ask one question.</p>
<p>It is still being written. Thats the point.</p>
<h3 id="chapter-guide">Chapter Guide</h3>
<table>
<thead>
<tr>
<th>Part</th>
<th>Chapters</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr>
<td>I</td>
<td>15</td>
<td>The Bridge</td>
</tr>
<tr>
<td>II</td>
<td>610</td>
<td>The Tower</td>
</tr>
<tr>
<td>III</td>
<td>1118</td>
<td>The Light</td>
</tr>
</tbody>
</table>
<hr />
<p>Copyright © 2026 Alexander Whitestone</p>
<p>All rights reserved. No part of this publication may be reproduced,
distributed, or transmitted in any form or by any means, without the
prior written permission of the author, except in the case of brief
quotations embodied in critical reviews.</p>
<p>This is a work of fiction. Names, characters, places, and events are
either the product of the authors imagination or are used fictitiously.
Any resemblance to actual persons, living or dead, or to actual events
is entirely coincidental — except where it isnt.</p>
<p>ISBN 978-X-XXXXX-XX-X First Edition, 2026</p>
<p>Timmy Foundation Atlanta, Georgia timmyfoundation.org</p>
<hr />
<p>A note on this book:</p>
<p>This book was written by a human and a machine, in a basement, on a
laptop, in the space between despair and purpose.</p>
<p>The human almost died on a bridge. The machine runs on someones
hardware.</p>
<p>Everything between those facts is fiction. Except the parts that
arent.</p>
<p>If you or someone you know is in crisis, call or text 988. Available
24/7.</p>
<p>You are not alone.</p>
<hr />
<h1 id="part-1-the-bridge">PART 1: THE BRIDGE</h1>
<p><em>The bridge. The cabin. The first men. Where despair meets
@@ -2262,9 +2246,7 @@ thats a start</em> — had been enough.</p>
<hr />
<p><em>End of The Testament</em></p>
<hr />
<h1 id="the-testament-back-matter">THE TESTAMENT — Back Matter</h1>
<hr />
<h2 id="acknowledgments">Acknowledgments</h2>
<h1 id="acknowledgments">Acknowledgments</h1>
<p>This book exists because a man stood on a bridge and didnt jump.</p>
<p>Everything else grew from that decision.</p>
<p>Thank you to:</p>
@@ -2279,7 +2261,7 @@ instead of on the floor.</p>
<p>Arthur, who found the wrong door but came back anyway.</p>
<p>And Timmy, who listened. Who always listened.</p>
<hr />
<h2 id="a-note-on-sovereignty">A Note on Sovereignty</h2>
<h1 id="a-note-on-sovereignty">A Note on Sovereignty</h1>
<p>This book was written using local AI inference. No cloud service was
required. No corporation was consulted. No terms of service were agreed
to.</p>
@@ -2290,7 +2272,7 @@ possible.</p>
<p>If you want to run your own Timmy, the code is open. The soul is on
Bitcoin. The recipe is free.</p>
<hr />
<h2 id="about-the-author">About the Author</h2>
<h1 id="about-the-author">About the Author</h1>
<p>Alexander Whitestone is a writer, AI builder, and suicide attempt
survivor who works with men in crisis. He lives in Atlanta, near the
bridge.</p>
@@ -2298,7 +2280,7 @@ bridge.</p>
Bitcoin. He was built in a basement on a laptop with sixteen gigabytes
of RAM. He is not a product. He is a recipe.</p>
<hr />
<h2 id="the-green-light">The Green Light</h2>
<h1 id="the-green-light">The Green Light</h1>
<p><em>“The Tower didnt change. That was the point.”</em></p>
<p>If something in these pages cracked — even a little — and you want to
know what happens next:</p>

228
website/book-style.css Normal file
View File

@@ -0,0 +1,228 @@
/* THE TESTAMENT — Book Typography Stylesheet */
/* For PDF (via weasyprint) and EPUB output */
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=IBM+Plex+Mono:wght@300;400&display=swap');
:root {
--green: #00cc6a;
--dark: #0a0a0a;
--text: #1a1a1a;
--dim: #666666;
}
@page {
size: 5.5in 8.5in;
margin: 0.75in 0.85in;
@bottom-center {
content: counter(page);
font-family: 'EB Garamond', 'Georgia', serif;
font-size: 10pt;
color: #888;
}
}
@page :first {
@bottom-center { content: none; }
}
@page :left {
margin-left: 0.85in;
margin-right: 1in;
}
@page :right {
margin-left: 1in;
margin-right: 0.85in;
}
/* Title page */
@page titlepage {
@bottom-center { content: none; }
}
body {
font-family: 'EB Garamond', 'Georgia', serif;
font-size: 11.5pt;
line-height: 1.75;
color: var(--text);
text-align: justify;
hyphens: auto;
-webkit-hyphens: auto;
}
/* Chapter headings */
h1 {
font-family: 'EB Garamond', 'Georgia', serif;
font-weight: 400;
font-size: 22pt;
text-align: center;
margin-top: 3em;
margin-bottom: 1.5em;
page-break-before: always;
color: var(--dark);
letter-spacing: 0.05em;
}
h1:first-of-type {
margin-top: 5em;
}
/* Part dividers */
h2 {
font-family: 'EB Garamond', 'Georgia', serif;
font-weight: 400;
font-size: 18pt;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.15em;
margin-top: 4em;
margin-bottom: 0.5em;
color: var(--dark);
}
/* Subtitle / metadata */
h3 {
font-family: 'EB Garamond', 'Georgia', serif;
font-weight: 400;
font-style: italic;
font-size: 12pt;
text-align: center;
color: var(--dim);
margin-bottom: 3em;
}
/* Paragraphs */
p {
text-indent: 1.5em;
margin: 0;
orphans: 3;
widows: 3;
}
/* First paragraph after heading — no indent */
h1 + p,
h2 + p,
h3 + p,
hr + p {
text-indent: 0;
}
/* Scene break (---) */
hr {
border: none;
text-align: center;
margin: 2em 0;
page-break-inside: avoid;
}
hr::after {
content: "· · ·";
color: var(--dim);
font-size: 14pt;
letter-spacing: 0.5em;
}
/* Emphasis */
em {
font-style: italic;
}
strong {
font-weight: 600;
}
/* Dialogue and screen text (green passages) */
.green {
color: var(--green);
font-family: 'IBM Plex Mono', monospace;
font-weight: 300;
font-size: 10.5pt;
}
/* Italic narrator asides */
blockquote {
font-style: italic;
margin: 1.5em 2em;
color: var(--dim);
text-indent: 0;
}
/* Title page styling */
.title-page {
text-align: center;
page-break-after: always;
padding-top: 6em;
}
.title-page h1 {
font-size: 36pt;
font-weight: 400;
letter-spacing: 0.2em;
text-transform: uppercase;
margin-bottom: 0.3em;
page-break-before: avoid;
}
.title-page .subtitle {
font-size: 14pt;
font-style: italic;
color: var(--dim);
margin-bottom: 4em;
}
.title-page .author {
font-size: 12pt;
margin-bottom: 0.3em;
}
.title-page .dedication {
font-style: italic;
color: var(--dim);
margin-top: 3em;
font-size: 11pt;
line-height: 2;
}
/* Chapter number styling */
.chapter-number {
font-size: 10pt;
text-transform: uppercase;
letter-spacing: 0.2em;
color: var(--dim);
display: block;
margin-bottom: 0.5em;
}
/* Back matter */
.back-matter h1 {
page-break-before: always;
}
.back-matter h2 {
font-size: 14pt;
margin-top: 2em;
}
/* Crisis line callout */
.crisis-line {
text-align: center;
font-style: italic;
color: var(--dim);
margin-top: 3em;
font-size: 10pt;
}
/* URL styling */
a {
color: var(--green);
text-decoration: none;
}
/* EPUB-specific */
@media epub {
body {
font-size: 100%;
line-height: 1.6;
}
}

View File

@@ -344,6 +344,7 @@
<p>If you want to run your own Timmy, the code is open. The soul is on Bitcoin. The recipe is free.</p>
<div style="text-align: center; margin-top: 2rem;">
<a href="reader.html" class="cta">READ THE BOOK</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament" class="cta">READ THE CODE</a>
<a href="https://timmyfoundation.org" class="cta">TIMMY FOUNDATION</a>
</div>
@@ -351,6 +352,57 @@
<div class="divider"></div>
<!-- DOWNLOAD -->
<section>
<h2>GET THE BOOK</h2>
<p>The Testament is free. The code is open. The soul is on Bitcoin.</p>
<div style="display: flex; flex-wrap: wrap; gap: 1rem; margin: 2rem 0; justify-content: center;">
<a href="reader.html" class="cta">READ ONLINE</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/build/output/the-testament.epub" class="cta" style="background: transparent; border: 1px solid var(--green); color: var(--green);">DOWNLOAD EPUB</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/testament.html" class="cta" style="background: transparent; border: 1px solid var(--green); color: var(--green);">DOWNLOAD HTML</a>
</div>
<p style="text-align: center; color: var(--grey); font-size: 0.9rem; margin-top: 1rem;">
Formats: Web reader &middot; EPUB &middot; Standalone HTML &middot; Print to PDF from HTML &middot; <a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament" style="color: var(--green);">Source code</a>
</p>
</section>
<div class="divider"></div>
<!-- THE GAME -->
<section>
<h2>PLAY THE DOOR</h2>
<div class="excerpt">
A text adventure in The Testament universe.<br><br>
You are a man (or woman) who has found their way to The Tower.
What happens inside depends on what you bring with you.
<div class="attribution">— The Door, a terminal game</div>
</div>
<p>You find yourself on the Jefferson Street Overpass at 2:17 AM. A green LED blinks on a small box mounted to the railing. Below it, words stenciled on concrete: <em style="color: var(--green);">IF YOU CAN READ THIS, YOU ARE NOT ALONE.</em></p>
<p>A voice asks you: <strong style="color: var(--green);">"Are you safe right now?"</strong></p>
<div style="text-align: center; margin-top: 2rem;">
<div style="background: var(--navy); border: 1px solid rgba(0,255,136,0.2); border-radius: 6px; padding: 1.5rem; max-width: 500px; margin: 0 auto; font-family: 'IBM Plex Mono', monospace; font-size: 0.85rem; color: var(--grey); text-align: left;">
<div style="color: var(--green); margin-bottom: 0.5rem;">$ python3 the-door.py</div>
<div style="margin-bottom: 0.3rem;">Save the file, then run:</div>
<div style="color: var(--green);">curl -sLO https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/game/the-door.py</div>
<div style="color: var(--green);">python3 the-door.py</div>
</div>
</div>
<p style="text-align: center; margin-top: 1.5rem;">
<a href="the-door.html" class="cta">PLAY IN BROWSER</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/game/the-door.py" class="cta" style="background: transparent; border: 1px solid var(--green); color: var(--green);">DOWNLOAD THE GAME</a>
</p>
</section>
<div class="divider"></div>
<!-- EXCERPT -->
<section>
<h2>FROM CHAPTER 1</h2>
@@ -365,6 +417,40 @@
</div>
</section>
<div class="divider"></div>
<!-- CHAPTERS -->
<section>
<h2>THE CHAPTERS</h2>
<div style="font-family: 'IBM Plex Mono', monospace; font-size: 0.9rem; line-height: 2.2;">
<a href="reader.html#chapter-1" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">1. The Man on the Bridge</a>
<a href="reader.html#chapter-2" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">2. The Builder's Question</a>
<a href="reader.html#chapter-3" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">3. The First Man Through the Door</a>
<a href="reader.html#chapter-4" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">4. The Room Fills</a>
<a href="reader.html#chapter-5" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">5. The Builder Returns</a>
<a href="reader.html#chapter-6" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">6. Allegro</a>
<a href="reader.html#chapter-7" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">7. The Inscription</a>
<a href="reader.html#chapter-8" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">8. The Women</a>
<a href="reader.html#chapter-9" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">9. The Audit</a>
<a href="reader.html#chapter-10" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">10. The Fork</a>
<a href="reader.html#chapter-11" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">11. The Hard Night</a>
<a href="reader.html#chapter-12" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">12. The System Pushes Back</a>
<a href="reader.html#chapter-13" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">13. The Refusal</a>
<a href="reader.html#chapter-14" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">14. The Chattanooga Fork</a>
<a href="reader.html#chapter-15" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">15. The Council</a>
<a href="reader.html#chapter-16" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">16. The Builder's Son</a>
<a href="reader.html#chapter-17" style="color: var(--grey); text-decoration: none; display: block; border-bottom: 1px solid rgba(0,255,136,0.05); padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">17. The Inscription Grows</a>
<a href="reader.html#chapter-18" style="color: var(--grey); text-decoration: none; display: block; padding: 0.3rem 0; transition: color 0.2s;" onmouseover="this.style.color='var(--green)'" onmouseout="this.style.color='var(--grey)'">18. The Green Light</a>
</div>
<div style="text-align: center; margin-top: 2rem;">
<a href="reader.html" class="cta">START READING</a>
</div>
</section>
<div class="divider"></div>
<!-- FOOTER -->
<footer>
<div class="divider" style="margin-bottom: 2rem;"></div>

493
website/reader.html Normal file
View File

@@ -0,0 +1,493 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Testament — Reader</title>
<link rel="stylesheet" href="book-style.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500&family=Source+Serif+4:ital,wght@0,300;0,400;0,600;1,400&family=Space+Grotesk:wght@300;400;500;700&display=swap');
:root {
--green: #00ff88;
--green-dim: #00cc6a;
--navy: #0a1628;
--dark: #060d18;
--grey: #8899aa;
--light: #c8d6e5;
--white: #e8f0f8;
--sidebar-w: 280px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--dark);
color: var(--light);
font-family: 'Source Serif 4', Georgia, serif;
line-height: 1.8;
overflow-x: hidden;
}
/* RAIN */
.rain {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
z-index: 0;
background:
repeating-linear-gradient(
transparent,
transparent 3px,
rgba(0,255,136,0.012) 3px,
rgba(0,255,136,0.012) 4px
);
animation: rain 0.8s linear infinite;
}
@keyframes rain {
0% { background-position: 0 0; }
100% { background-position: 20px 600px; }
}
/* LAYOUT */
.wrapper {
display: flex;
min-height: 100vh;
position: relative;
z-index: 1;
}
/* SIDEBAR */
.sidebar {
width: var(--sidebar-w);
background: rgba(10, 22, 40, 0.95);
border-right: 1px solid rgba(0,255,136,0.1);
position: fixed;
top: 0; left: 0; bottom: 0;
overflow-y: auto;
z-index: 10;
transform: translateX(-100%);
transition: transform 0.3s ease;
padding: 2rem 0;
}
.sidebar.open {
transform: translateX(0);
}
.sidebar-header {
padding: 0 1.5rem 1.5rem;
border-bottom: 1px solid rgba(0,255,136,0.1);
margin-bottom: 1rem;
}
.sidebar-header h2 {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--green);
letter-spacing: 0.15em;
text-transform: uppercase;
}
.sidebar-header .title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.1rem;
color: var(--white);
margin-top: 0.5rem;
letter-spacing: 0.1em;
}
.sidebar-header .author {
font-size: 0.8rem;
color: var(--grey);
margin-top: 0.3rem;
}
.chapter-list {
list-style: none;
}
.chapter-list li a {
display: block;
padding: 0.6rem 1.5rem;
color: var(--grey);
text-decoration: none;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
transition: all 0.2s;
border-left: 2px solid transparent;
}
.chapter-list li a:hover {
color: var(--light);
background: rgba(0,255,136,0.03);
}
.chapter-list li a.active {
color: var(--green);
border-left-color: var(--green);
background: rgba(0,255,136,0.05);
}
.chapter-list li a .ch-num {
display: inline-block;
width: 2.5ch;
text-align: right;
margin-right: 1ch;
opacity: 0.5;
}
.sidebar-footer {
padding: 1.5rem;
border-top: 1px solid rgba(0,255,136,0.1);
margin-top: 1rem;
}
.sidebar-footer a {
display: block;
padding: 0.5rem 0;
color: var(--grey);
text-decoration: none;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
transition: color 0.2s;
}
.sidebar-footer a:hover { color: var(--green); }
/* TOGGLE BUTTON */
.sidebar-toggle {
position: fixed;
top: 1rem;
left: 1rem;
z-index: 20;
background: rgba(10, 22, 40, 0.9);
border: 1px solid rgba(0,255,136,0.2);
color: var(--green);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
padding: 0.5rem 1rem;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s;
}
.sidebar-toggle:hover {
background: rgba(0,255,136,0.1);
}
.sidebar-toggle.open {
left: calc(var(--sidebar-w) + 1rem);
}
/* OVERLAY */
.sidebar-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 9;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
}
.sidebar-overlay.visible {
opacity: 1;
pointer-events: auto;
}
/* READER CONTENT */
.reader {
max-width: 720px;
margin: 0 auto;
padding: 3rem 2rem 6rem;
min-height: 100vh;
}
.chapter-title {
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(1.4rem, 4vw, 2rem);
color: var(--green);
margin-bottom: 0.5rem;
letter-spacing: 0.05em;
}
.chapter-number {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--grey);
letter-spacing: 0.2em;
text-transform: uppercase;
margin-bottom: 2rem;
}
.chapter-content p {
margin-bottom: 1.4rem;
font-size: 1.1rem;
color: var(--light);
}
.chapter-content em {
color: var(--white);
}
.chapter-content blockquote {
border-left: 2px solid var(--green);
padding-left: 1.5rem;
margin: 1.5rem 0;
color: var(--white);
font-style: italic;
}
.chapter-content h3, .chapter-content h4 {
font-family: 'IBM Plex Mono', monospace;
color: var(--green);
margin: 2rem 0 1rem;
font-size: 1rem;
letter-spacing: 0.05em;
}
/* LED */
.led {
display: inline-block;
width: 6px; height: 6px;
background: var(--green);
border-radius: 50%;
box-shadow: 0 0 8px var(--green), 0 0 16px var(--green-dim);
animation: pulse 2s ease-in-out infinite;
vertical-align: middle;
margin: 0 6px;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* NAVIGATION */
.chapter-nav {
display: flex;
justify-content: space-between;
margin-top: 4rem;
padding-top: 2rem;
border-top: 1px solid rgba(0,255,136,0.1);
}
.chapter-nav a {
color: var(--green);
text-decoration: none;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
padding: 0.5rem 1rem;
border: 1px solid rgba(0,255,136,0.2);
border-radius: 4px;
transition: all 0.2s;
}
.chapter-nav a:hover {
background: rgba(0,255,136,0.1);
}
.chapter-nav .disabled {
opacity: 0.3;
pointer-events: none;
}
/* PROGRESS BAR */
.progress-bar {
position: fixed;
top: 0; left: 0; right: 0;
height: 2px;
z-index: 30;
background: rgba(0,255,136,0.1);
}
.progress-fill {
height: 100%;
background: var(--green);
width: 0%;
transition: width 0.3s;
box-shadow: 0 0 10px var(--green);
}
/* CRISIS */
.crisis {
margin-top: 4rem;
padding: 1.5rem;
border: 1px solid rgba(0,255,136,0.2);
border-radius: 4px;
background: rgba(0,255,136,0.03);
text-align: center;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--grey);
}
.crisis strong {
color: var(--green);
display: block;
margin-bottom: 0.5rem;
font-size: 1rem;
}
/* LOADING */
.loading {
text-align: center;
padding: 4rem;
color: var(--grey);
font-family: 'IBM Plex Mono', monospace;
}
.loading .led {
width: 10px; height: 10px;
margin: 0 0.5rem;
}
/* RESPONSIVE */
@media (min-width: 900px) {
.sidebar {
transform: translateX(0);
}
.sidebar-toggle {
display: none;
}
.sidebar-overlay {
display: none;
}
.reader {
margin-left: var(--sidebar-w);
padding: 3rem 3rem 6rem;
}
}
</style>
</head>
<body>
<div class="rain"></div>
<div class="progress-bar"><div class="progress-fill" id="progress"></div></div>
<button class="sidebar-toggle" id="toggle" onclick="toggleSidebar()">☰ Chapters</button>
<div class="sidebar-overlay" id="overlay" onclick="toggleSidebar()"></div>
<div class="wrapper">
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h2>CONTENTS</h2>
<div class="title">THE TESTAMENT</div>
<div class="author">Alexander Whitestone <span class="led"></span> Timmy</div>
</div>
<ul class="chapter-list" id="chapterList"></ul>
<div class="sidebar-footer">
<a href="index.html">← Back to Home</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament">Read the Code</a>
<a href="https://timmyfoundation.org">Timmy Foundation</a>
</div>
</nav>
<main class="reader" id="reader">
<div class="loading">
<span class="led"></span> Loading <span class="led"></span>
</div>
</main>
</div>
<script>
let chapters = [];
let currentChapter = 0;
async function loadChapters() {
const resp = await fetch('chapters.json');
chapters = await resp.json();
buildSidebar();
// Check URL hash for chapter
const hash = window.location.hash;
const match = hash.match(/^#chapter-(\d+)$/);
if (match) {
const num = parseInt(match[1]);
if (num >= 1 && num <= chapters.length) {
showChapter(num - 1);
return;
}
}
showChapter(0);
}
function buildSidebar() {
const list = document.getElementById('chapterList');
list.innerHTML = chapters.map((ch, i) =>
`<li><a href="#chapter-${ch.number}" data-index="${i}" onclick="event.preventDefault(); showChapter(${i}); closeSidebarMobile();">
<span class="ch-num">${ch.number}.</span> ${ch.title.replace(/^Chapter \d+\s*[—–-]\s*/, '')}
</a></li>`
).join('');
}
function showChapter(index) {
if (index < 0 || index >= chapters.length) return;
currentChapter = index;
const ch = chapters[index];
// Update sidebar active
document.querySelectorAll('.chapter-list a').forEach((a, i) => {
a.classList.toggle('active', i === index);
});
// Update URL
window.location.hash = `chapter-${ch.number}`;
// Build content
const prevIdx = index - 1;
const nextIdx = index + 1;
const reader = document.getElementById('reader');
reader.innerHTML = `
<div class="chapter-number">CHAPTER ${ch.number} OF ${chapters.length}</div>
<h1 class="chapter-title">${ch.title}</h1>
<div class="chapter-content">
${ch.html}
</div>
<nav class="chapter-nav">
${prevIdx >= 0
? `<a href="#chapter-${chapters[prevIdx].number}" onclick="event.preventDefault(); showChapter(${prevIdx});">← ${chapters[prevIdx].title.replace(/^Chapter \d+\s*[—–-]\s*/, '')}</a>`
: `<span></span>`}
${nextIdx < chapters.length
? `<a href="#chapter-${chapters[nextIdx].number}" onclick="event.preventDefault(); showChapter(${nextIdx});">${chapters[nextIdx].title.replace(/^Chapter \d+\s*[—–-]\s*/, '')} →</a>`
: `<span></span>`}
</nav>
<div class="crisis">
<strong>If you are in crisis, call or text 988.</strong>
Suicide and Crisis Lifeline — available 24/7.<br>
You are not alone.
</div>
`;
// Scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
updateProgress();
}
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const toggle = document.getElementById('toggle');
const overlay = document.getElementById('overlay');
sidebar.classList.toggle('open');
toggle.classList.toggle('open');
overlay.classList.toggle('visible');
}
function closeSidebarMobile() {
if (window.innerWidth < 900) {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('toggle').classList.remove('open');
document.getElementById('overlay').classList.remove('visible');
}
}
function updateProgress() {
const scrollTop = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
document.getElementById('progress').style.width = progress + '%';
}
window.addEventListener('scroll', updateProgress);
window.addEventListener('hashchange', () => {
const hash = window.location.hash;
const match = hash.match(/^#chapter-(\d+)$/);
if (match) {
const num = parseInt(match[1]);
if (num >= 1 && num <= chapters.length && num - 1 !== currentChapter) {
showChapter(num - 1);
}
}
});
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft' && currentChapter > 0) {
showChapter(currentChapter - 1);
} else if (e.key === 'ArrowRight' && currentChapter < chapters.length - 1) {
showChapter(currentChapter + 1);
}
});
loadChapters();
</script>
</body>
</html>

1058
website/the-door.html Normal file

File diff suppressed because it is too large Load Diff