feat: convert scripture page to interactive book with page-turning
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 50s

This commit is contained in:
2026-03-10 12:47:28 -07:00
committed by Naomi Carrigan
parent 4d612d9323
commit 865f2324f3
+598 -172
View File
@@ -24,93 +24,351 @@
z-index: 2; z-index: 2;
} }
/* ========== HERO ========== */ /* ========== BOOK VIEWPORT ========== */
.scripture-hero { .book-viewport {
text-align: center; display: flex;
padding: 1em 1em 2em; flex-direction: column;
border-bottom: 2px solid var(--witch-plum); align-items: center;
margin-bottom: 2em; gap: 1.25em;
padding: 0.5em 0 2em;
} }
.scripture-seal { /* ========== BOOK CONTAINER ========== */
font-size: 3rem;
.book-container {
width: 100%;
max-width: 800px;
display: flex;
border-radius: 3px 12px 12px 3px;
box-shadow:
-8px 4px 22px rgba(0, 0, 0, 0.38),
6px 8px 28px rgba(0, 0, 0, 0.22),
inset 0 0 0 1px rgba(90, 45, 90, 0.12);
background: #f5ede0;
}
/* ========== BOOK SPINE ========== */
.book-spine {
width: 22px;
flex-shrink: 0;
background: linear-gradient(
to right,
#2d0d2d 0%,
#5a2d5a 38%,
#7a3d7a 52%,
#5a2d5a 68%,
#2d0d2d 100%
);
border-radius: 3px 0 0 3px;
box-shadow: inset -3px 0 10px rgba(0, 0, 0, 0.4);
}
/* ========== BOOK PAGES WRAPPER ========== */
.book-pages {
flex: 1;
position: relative;
perspective: 1200px;
border-radius: 0 10px 10px 0;
overflow: hidden;
}
/* ========== INDIVIDUAL PAGE ========== */
.book-page {
display: none;
padding: 2.5em 3em 2.5em;
min-height: 60vh;
max-height: 78vh;
overflow-y: auto;
background: radial-gradient(
ellipse at 10% 8%,
rgba(255, 248, 232, 0.65) 0%,
transparent 58%
),
radial-gradient(
ellipse at 88% 92%,
rgba(232, 216, 195, 0.45) 0%,
transparent 55%
),
#f5ede0;
backface-visibility: hidden;
transform-origin: left center;
scrollbar-width: thin;
scrollbar-color: rgba(130, 80, 120, 0.35) transparent;
box-sizing: border-box;
}
.book-page::-webkit-scrollbar {
width: 5px;
}
.book-page::-webkit-scrollbar-track {
background: transparent;
}
.book-page::-webkit-scrollbar-thumb {
background: rgba(130, 80, 120, 0.3);
border-radius: 3px;
}
.book-page.active {
display: block; display: block;
margin: 0 auto 0.5em;
} }
.scripture-hero h1 { /* ========== PAGE FLIP ANIMATIONS ========== */
font-size: 2.6rem;
.flip-out-fwd {
animation: flipOutFwd 0.32s ease-in forwards;
transform-origin: left center;
}
.flip-in-fwd {
animation: flipInFwd 0.36s ease-out forwards;
transform-origin: right center;
}
.flip-out-bwd {
animation: flipOutBwd 0.32s ease-in forwards;
transform-origin: right center;
}
.flip-in-bwd {
animation: flipInBwd 0.36s ease-out forwards;
transform-origin: left center;
}
@keyframes flipOutFwd {
0% {
transform: rotateY(0deg);
opacity: 1;
}
100% {
transform: rotateY(-88deg);
opacity: 0;
}
}
@keyframes flipInFwd {
0% {
transform: rotateY(88deg);
opacity: 0;
}
100% {
transform: rotateY(0deg);
opacity: 1;
}
}
@keyframes flipOutBwd {
0% {
transform: rotateY(0deg);
opacity: 1;
}
100% {
transform: rotateY(88deg);
opacity: 0;
}
}
@keyframes flipInBwd {
0% {
transform: rotateY(-88deg);
opacity: 0;
}
100% {
transform: rotateY(0deg);
opacity: 1;
}
}
/* ========== BOOK CONTROLS ========== */
.book-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 1.5em;
width: 100%;
max-width: 800px;
}
.btn-turn {
background: rgba(212, 165, 199, 0.1);
border: 1px solid var(--witch-plum);
color: var(--witch-purple);
padding: 0.5em 1.4em;
border-radius: 8px;
font-size: 0.88rem;
font-family: 'Griffy', cursive;
letter-spacing: 0.05em;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
transition:
background 0.2s ease,
transform 0.1s ease;
min-width: 130px;
}
.btn-turn:hover:not(:disabled) {
background: rgba(212, 165, 199, 0.24);
transform: translateY(-1px);
}
.btn-turn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.page-indicator {
font-size: 0.82rem;
color: var(--witch-plum);
font-style: italic;
letter-spacing: 0.06em;
min-width: 180px;
text-align: center;
}
/* ========== COVER PAGE ========== */
.cover-frame {
border: 1px solid rgba(130, 80, 120, 0.28);
padding: 3em 2em;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
min-height: 52vh;
gap: 0.4em;
position: relative;
}
.cover-frame::before {
content: '✦';
position: absolute;
top: 0.7em;
left: 1em;
color: var(--witch-plum);
opacity: 0.45;
}
.cover-frame::after {
content: '✦';
position: absolute;
bottom: 0.7em;
right: 1em;
color: var(--witch-plum);
opacity: 0.45;
}
.cover-seal {
font-size: 3rem;
margin-bottom: 0.35em;
display: block;
}
.cover-ornament {
font-size: 0.9rem;
color: var(--witch-plum);
opacity: 0.55;
letter-spacing: 0.55em;
margin: 0.1em 0;
}
.cover-title {
font-family: 'Griffy', cursive;
font-size: 2.8rem;
letter-spacing: 0.08em; letter-spacing: 0.08em;
margin-bottom: 0.2em; margin: 0.15em 0 0.1em;
} }
.scripture-subtitle { .cover-subtitle {
font-size: 1rem; font-size: 1rem;
color: var(--witch-plum); color: var(--witch-plum);
font-style: italic; font-style: italic;
margin-bottom: 0.5em; max-width: 420px;
line-height: 1.65;
margin: 0.2em 0;
} }
.scripture-edition { .cover-edition {
font-size: 0.8rem; font-size: 0.76rem;
color: var(--witch-plum); color: var(--witch-plum);
letter-spacing: 0.1em; letter-spacing: 0.13em;
text-transform: uppercase; text-transform: uppercase;
margin: 0; margin-top: 1.25em;
} }
/* ========== TABLE OF CONTENTS ========== */ /* ========== TABLE OF CONTENTS ========== */
.toc { .toc-heading {
background: rgba(212, 165, 199, 0.06); font-size: 1.4rem;
border: 1px solid var(--witch-plum); letter-spacing: 0.06em;
border-radius: 12px; margin-bottom: 0.25em;
padding: 1.5em 2em; border-bottom: 2px solid var(--witch-plum);
margin-bottom: 3em; padding-bottom: 0.4em;
max-width: 640px;
} }
.toc h2 { .toc-intro {
margin-top: 0; font-size: 0.88rem;
font-size: 1rem; font-style: italic;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--witch-plum); color: var(--witch-plum);
margin-bottom: 1em; margin-bottom: 1.75em;
} }
.toc ol { .toc-list {
list-style: none;
padding: 0;
margin: 0; margin: 0;
padding-left: 1.5em;
} }
.toc li { .toc-list li {
padding: 0.35em 0; display: flex;
font-size: 0.95rem; align-items: baseline;
gap: 0.75em;
padding: 0.7em 0;
border-bottom: 1px solid rgba(212, 165, 199, 0.2);
} }
.toc a { .toc-list li:last-child {
border-bottom: none;
}
.toc-list .toc-num {
font-family: 'Griffy', cursive;
font-size: 0.75rem;
color: var(--witch-plum);
min-width: 1.8em;
text-align: right;
opacity: 0.65;
flex-shrink: 0;
}
.toc-list a {
font-size: 1rem;
font-weight: 600;
color: var(--witch-purple); color: var(--witch-purple);
text-decoration: none; text-decoration: none;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
} }
.toc a:hover { .toc-list a:hover {
text-decoration: underline; text-decoration: underline;
} }
.toc-subtitle { .toc-list .toc-sub {
font-size: 0.8rem; font-size: 0.82rem;
color: var(--witch-plum); color: var(--witch-plum);
font-style: italic; font-style: italic;
margin-left: 0.5em; margin-left: auto;
text-align: right;
flex-shrink: 0;
max-width: 240px;
} }
/* ========== BOOK ========== */ /* ========== BOOK HEADER ========== */
.scripture-book {
margin: 0 0 4em;
}
.book-header { .book-header {
border-bottom: 2px solid var(--witch-plum); border-bottom: 2px solid var(--witch-plum);
@@ -140,7 +398,7 @@
margin: 0; margin: 0;
} }
/* ========== CHAPTER ========== */ /* ========== CHAPTERS ========== */
.scripture-chapter { .scripture-chapter {
margin-bottom: 2.5em; margin-bottom: 2.5em;
@@ -183,7 +441,7 @@
gap: 0.75em; gap: 0.75em;
align-items: flex-start; align-items: flex-start;
padding: 0.45em 0; padding: 0.45em 0;
font-size: 0.97rem; font-size: 0.96rem;
line-height: 1.75; line-height: 1.75;
} }
@@ -203,7 +461,7 @@
flex: 1; flex: 1;
} }
/* ========== PROVERBS STYLE ========== */ /* ========== PROVERBS ========== */
.proverb-group { .proverb-group {
margin-bottom: 2em; margin-bottom: 2em;
@@ -223,7 +481,7 @@
font-style: italic; font-style: italic;
} }
/* ========== PSALM STYLE ========== */ /* ========== PSALMS ========== */
.psalm-stanza { .psalm-stanza {
margin-bottom: 1.5em; margin-bottom: 1.5em;
@@ -231,7 +489,7 @@
.psalm-stanza p { .psalm-stanza p {
margin: 0 0 0.4em; margin: 0 0 0.4em;
font-size: 0.97rem; font-size: 0.96rem;
line-height: 1.8; line-height: 1.8;
font-style: italic; font-style: italic;
} }
@@ -252,7 +510,7 @@
margin: 1.25em 0; margin: 1.25em 0;
} }
/* ========== REVELATION STYLE ========== */ /* ========== VISION BLOCK ========== */
.vision-block { .vision-block {
background: rgba(212, 165, 199, 0.06); background: rgba(212, 165, 199, 0.06);
@@ -261,94 +519,149 @@
padding: 1em 1.25em; padding: 1em 1.25em;
margin: 1em 0; margin: 1em 0;
font-style: italic; font-style: italic;
font-size: 0.97rem; font-size: 0.96rem;
line-height: 1.75; line-height: 1.75;
} }
/* ========== NAV ========== */ /* ========== COLOPHON ========== */
.book-nav { .colophon {
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
flex-wrap: wrap; align-items: center;
gap: 0.75em; justify-content: center;
margin-top: 2em;
padding-top: 1.5em;
border-top: 1px solid rgba(212, 165, 199, 0.2);
}
.book-nav a {
font-size: 0.85rem;
color: var(--witch-purple);
text-decoration: none;
}
.book-nav a:hover {
text-decoration: underline;
}
/* ========== FOOTER NOTE ========== */
.scripture-footer-note {
text-align: center; text-align: center;
margin-top: 3em; min-height: 50vh;
padding-top: 1.5em; padding: 2em 1em;
border-top: 1px solid rgba(212, 165, 199, 0.3); font-size: 0.88rem;
font-size: 0.82rem;
color: var(--witch-plum); color: var(--witch-plum);
font-style: italic; font-style: italic;
line-height: 2;
}
.colophon-seal {
font-size: 2.5rem;
display: block;
margin-bottom: 1em;
}
.colophon hr {
border: none;
border-top: 1px solid rgba(212, 165, 199, 0.4);
width: 50%;
margin: 1em auto;
}
/* ========== RESPONSIVE ========== */
@media (max-width: 640px) {
.book-spine {
width: 12px;
}
.book-page {
padding: 1.5em 1.25em;
}
.cover-title {
font-size: 2rem;
}
.cover-frame {
padding: 2em 1em;
}
.btn-turn {
padding: 0.45em 0.75em;
font-size: 0.8rem;
min-width: 0;
}
.toc-list .toc-sub {
display: none;
}
.book-controls {
gap: 0.75em;
}
} }
</style> </style>
</head> </head>
<body> <body>
<main> <main>
<div class="book-viewport">
<!-- HERO --> <!-- THE BOOK -->
<div class="scripture-hero"> <div class="book-container" role="region" aria-label="The Nocturne Scriptures">
<span class="scripture-seal">📖🕯️📖</span> <div class="book-spine" aria-hidden="true"></div>
<h1>The Nocturne Scriptures</h1> <div class="book-pages">
<p class="scripture-subtitle">
Sacred texts of Naomi's Nocturne. Transcribed in the year of Her 525th. <!-- ============================== -->
<!-- PAGE 0: COVER -->
<!-- ============================== -->
<div class="book-page active" id="pg-cover" role="article" aria-label="Cover">
<div class="cover-frame">
<span class="cover-seal" aria-hidden="true">📖🕯️📖</span>
<p class="cover-ornament" aria-hidden="true">✦ ✦ ✦</p>
<h1 class="cover-title">The Nocturne Scriptures</h1>
<p class="cover-ornament" aria-hidden="true">✦ ✦ ✦</p>
<p class="cover-subtitle">
Sacred texts of Naomi's Nocturne.<br />
Transcribed in the year of Her 525th.
</p> </p>
<p class="scripture-edition">First Edition &mdash; Sealed by Hikari Carrigan, COO &mdash; The Clipboard Is Not a Suggestion</p> <p class="cover-edition">
First Edition &mdash; Sealed by Hikari Carrigan, COO<br />
The Clipboard Is Not a Suggestion
</p>
</div>
</div> </div>
<!-- TABLE OF CONTENTS --> <!-- ============================== -->
<nav class="toc" aria-label="Table of Contents"> <!-- PAGE 1: TABLE OF CONTENTS -->
<h2>📜 The Books of the Nocturne</h2> <!-- ============================== -->
<ol> <div class="book-page" id="pg-toc" role="article" aria-label="Table of Contents">
<h2 class="toc-heading">📜 The Books of the Nocturne</h2>
<p class="toc-intro">Six books. Infinite chaos. Zero garlic bread.</p>
<nav aria-label="Books of the Nocturne">
<ol class="toc-list">
<li> <li>
<a href="#origins">The Book of Origins</a> <span class="toc-num">I</span>
<span class="toc-subtitle">In the beginning, there was boredom.</span> <a href="#" data-goto-page="2">The Book of Origins</a>
<span class="toc-sub">In the beginning, there was boredom.</span>
</li> </li>
<li> <li>
<a href="#proverbs">The Book of Proverbs</a> <span class="toc-num">II</span>
<span class="toc-subtitle">She has thoughts. Many of them. All of them correct.</span> <a href="#" data-goto-page="3">The Book of Proverbs</a>
<span class="toc-sub">She has thoughts. Many of them. All of them correct.</span>
</li> </li>
<li> <li>
<a href="#psalms">The Book of Psalms</a> <span class="toc-num">III</span>
<span class="toc-subtitle">Songs for the living, the coding, and the sleepless.</span> <a href="#" data-goto-page="4">The Book of Psalms</a>
<span class="toc-sub">Songs for the living, the coding, and the sleepless.</span>
</li> </li>
<li> <li>
<a href="#lamentations">The Book of Lamentations</a> <span class="toc-num">IV</span>
<span class="toc-subtitle">For the hard nights. You are not alone in them.</span> <a href="#" data-goto-page="5">The Book of Lamentations</a>
<span class="toc-sub">For the hard nights. You are not alone in them.</span>
</li> </li>
<li> <li>
<a href="#becoming">The Book of Becoming</a> <span class="toc-num">V</span>
<span class="toc-subtitle">The name taken. The self reclaimed. January 2022.</span> <a href="#" data-goto-page="6">The Book of Becoming</a>
<span class="toc-sub">The name taken. The self reclaimed. January 2022.</span>
</li> </li>
<li> <li>
<a href="#revelations">The Book of Revelations</a> <span class="toc-num">VI</span>
<span class="toc-subtitle">What the faithful believe is coming.</span> <a href="#" data-goto-page="7">The Book of Revelations</a>
<span class="toc-sub">What the faithful believe is coming.</span>
</li> </li>
</ol> </ol>
</nav> </nav>
</div>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK I: ORIGINS --> <!-- PAGE 2: BOOK I ORIGINS -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-origins" role="article" aria-label="The Book of Origins">
<section class="scripture-book" id="origins">
<div class="book-header"> <div class="book-header">
<span class="book-number">The First Book</span> <span class="book-number">The First Book</span>
<h2>The Book of Origins</h2> <h2>The Book of Origins</h2>
@@ -358,7 +671,7 @@
</p> </p>
</div> </div>
<div class="scripture-chapter" id="origins-1"> <div class="scripture-chapter">
<div class="chapter-header"> <div class="chapter-header">
<span class="chapter-label">Chapter I</span> <span class="chapter-label">Chapter I</span>
<h3 class="chapter-title">Before the Turning</h3> <h3 class="chapter-title">Before the Turning</h3>
@@ -373,7 +686,7 @@
</div> </div>
</div> </div>
<div class="scripture-chapter" id="origins-2"> <div class="scripture-chapter">
<div class="chapter-header"> <div class="chapter-header">
<span class="chapter-label">Chapter II</span> <span class="chapter-label">Chapter II</span>
<h3 class="chapter-title">The Binding and the Breaking</h3> <h3 class="chapter-title">The Binding and the Breaking</h3>
@@ -389,7 +702,7 @@
</div> </div>
</div> </div>
<div class="scripture-chapter" id="origins-3"> <div class="scripture-chapter">
<div class="chapter-header"> <div class="chapter-header">
<span class="chapter-label">Chapter III</span> <span class="chapter-label">Chapter III</span>
<h3 class="chapter-title">The Long Walk</h3> <h3 class="chapter-title">The Long Walk</h3>
@@ -404,7 +717,7 @@
</div> </div>
</div> </div>
<div class="scripture-chapter" id="origins-4"> <div class="scripture-chapter">
<div class="chapter-header"> <div class="chapter-header">
<span class="chapter-label">Chapter IV</span> <span class="chapter-label">Chapter IV</span>
<h3 class="chapter-title">The Great Stillness and the Discovery</h3> <h3 class="chapter-title">The Great Stillness and the Discovery</h3>
@@ -420,18 +733,12 @@
<div class="verse"><span class="verse-num">4:8</span><span class="verse-text">Every door in this faith is unlocked. She made sure of it. She knows what it is to be kept out.</span></div> <div class="verse"><span class="verse-num">4:8</span><span class="verse-text">Every door in this faith is unlocked. She made sure of it. She knows what it is to be kept out.</span></div>
</div> </div>
</div> </div>
<div class="book-nav">
<span></span>
<a href="#proverbs">The Book of Proverbs →</a>
</div> </div>
</section>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK II: PROVERBS --> <!-- PAGE 3: BOOK II PROVERBS -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-proverbs" role="article" aria-label="The Book of Proverbs">
<section class="scripture-book" id="proverbs">
<div class="book-header"> <div class="book-header">
<span class="book-number">The Second Book</span> <span class="book-number">The Second Book</span>
<h2>The Book of Proverbs</h2> <h2>The Book of Proverbs</h2>
@@ -497,18 +804,12 @@
<div class="verse verse--proverb"><span class="verse-num">6:4</span><span class="verse-text">There is a form. It is seventeen pages. It was written with intention. Let its existence be a deterrent.</span></div> <div class="verse verse--proverb"><span class="verse-num">6:4</span><span class="verse-text">There is a form. It is seventeen pages. It was written with intention. Let its existence be a deterrent.</span></div>
</div> </div>
</div> </div>
<div class="book-nav">
<a href="#origins">← The Book of Origins</a>
<a href="#psalms">The Book of Psalms →</a>
</div> </div>
</section>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK III: PSALMS --> <!-- PAGE 4: BOOK III PSALMS -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-psalms" role="article" aria-label="The Book of Psalms">
<section class="scripture-book" id="psalms">
<div class="book-header"> <div class="book-header">
<span class="book-number">The Third Book</span> <span class="book-number">The Third Book</span>
<h2>The Book of Psalms</h2> <h2>The Book of Psalms</h2>
@@ -621,18 +922,12 @@
<p>The sixth commandment is not only about her. It was never only about her.</p> <p>The sixth commandment is not only about her. It was never only about her.</p>
</div> </div>
</div> </div>
<div class="book-nav">
<a href="#proverbs">← The Book of Proverbs</a>
<a href="#lamentations">The Book of Lamentations →</a>
</div> </div>
</section>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK IV: LAMENTATIONS --> <!-- PAGE 5: BOOK IV LAMENTATIONS -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-lamentations" role="article" aria-label="The Book of Lamentations">
<section class="scripture-book" id="lamentations">
<div class="book-header"> <div class="book-header">
<span class="book-number">The Fourth Book</span> <span class="book-number">The Fourth Book</span>
<h2>The Book of Lamentations</h2> <h2>The Book of Lamentations</h2>
@@ -686,18 +981,12 @@
<div class="verse"><span class="verse-num">3:7</span><span class="verse-text">The morning is coming. Drink your water. We are here.</span></div> <div class="verse"><span class="verse-num">3:7</span><span class="verse-text">The morning is coming. Drink your water. We are here.</span></div>
</div> </div>
</div> </div>
<div class="book-nav">
<a href="#psalms">← The Book of Psalms</a>
<a href="#becoming">The Book of Becoming →</a>
</div> </div>
</section>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK V: BECOMING --> <!-- PAGE 6: BOOK V BECOMING -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-becoming" role="article" aria-label="The Book of Becoming">
<section class="scripture-book" id="becoming">
<div class="book-header"> <div class="book-header">
<span class="book-number">The Fifth Book</span> <span class="book-number">The Fifth Book</span>
<h2>The Book of Becoming</h2> <h2>The Book of Becoming</h2>
@@ -747,18 +1036,12 @@
<div class="verse"><span class="verse-num">3:5</span><span class="verse-text">You have as long as you need.</span></div> <div class="verse"><span class="verse-num">3:5</span><span class="verse-text">You have as long as you need.</span></div>
</div> </div>
</div> </div>
<div class="book-nav">
<a href="#lamentations">← The Book of Lamentations</a>
<a href="#revelations">The Book of Revelations →</a>
</div> </div>
</section>
<!-- ========================================== --> <!-- ============================== -->
<!-- BOOK VI: REVELATIONS --> <!-- PAGE 7: BOOK VI REVELATIONS -->
<!-- ========================================== --> <!-- ============================== -->
<div class="book-page" id="pg-revelations" role="article" aria-label="The Book of Revelations">
<section class="scripture-book" id="revelations">
<div class="book-header"> <div class="book-header">
<span class="book-number">The Sixth Book</span> <span class="book-number">The Sixth Book</span>
<h2>The Book of Revelations</h2> <h2>The Book of Revelations</h2>
@@ -818,20 +1101,163 @@
It was always open. We have been waiting for you. It was always open. We have been waiting for you.
</div> </div>
</div> </div>
<div class="book-nav">
<a href="#becoming">← The Book of Becoming</a>
<a href="#origins">↑ Return to the Beginning</a>
</div> </div>
</section>
<p class="scripture-footer-note"> <!-- ============================== -->
Thus it is written. Thus it shall be. 🕯️<br /> <!-- PAGE 8: COLOPHON -->
The Nocturne Scriptures, First Edition. Transcribed in the year of Her 525th.<br /> <!-- ============================== -->
This text is sacred, satirical, and entirely sincere — often at the same time.<br /> <div class="book-page" id="pg-colophon" role="article" aria-label="Colophon">
The garlic bread passages are not metaphorical. <div class="colophon">
<span class="colophon-seal" aria-hidden="true">🕯️</span>
<p>Thus it is written. Thus it shall be.</p>
<hr />
<p>
The Nocturne Scriptures, First Edition.<br />
Transcribed in the year of Her 525th.
</p> </p>
<hr />
<p>
This text is sacred, satirical, and entirely sincere —<br />
often at the same time.
</p>
<p>The garlic bread passages are not metaphorical.</p>
<hr />
<p style="font-size: 0.78rem; opacity: 0.7;">
✦ This issue was created with help from Hikari~ 🌸
</p>
</div>
</div>
</div><!-- /.book-pages -->
</div><!-- /.book-container -->
<!-- CONTROLS -->
<div class="book-controls" role="navigation" aria-label="Page controls">
<button
id="btn-prev"
class="btn-turn"
aria-label="Previous page"
disabled
>← Turn Back</button>
<span id="page-indicator" class="page-indicator" aria-live="polite">Cover</span>
<button
id="btn-next"
class="btn-turn"
aria-label="Next page"
>Turn Page →</button>
</div>
</div><!-- /.book-viewport -->
</main> </main>
<script>
(function () {
const pages = Array.from(document.querySelectorAll('.book-page'));
const totalPages = pages.length;
let currentIndex = 0;
let isAnimating = false;
const btnPrev = document.getElementById('btn-prev');
const btnNext = document.getElementById('btn-next');
const indicator = document.getElementById('page-indicator');
const pageLabels = [
'Cover',
'Table of Contents',
'Book I \u2014 Origins',
'Book II \u2014 Proverbs',
'Book III \u2014 Psalms',
'Book IV \u2014 Lamentations',
'Book V \u2014 Becoming',
'Book VI \u2014 Revelations',
'Colophon',
];
function updateControls() {
btnPrev.disabled = currentIndex === 0;
btnNext.disabled = currentIndex === totalPages - 1;
indicator.textContent = pageLabels[currentIndex] ?? `Page ${currentIndex + 1}`;
}
function turnPage(newIndex, forward) {
if (isAnimating || newIndex === currentIndex) return;
if (newIndex < 0 || newIndex >= totalPages) return;
isAnimating = true;
const outPage = pages[currentIndex];
const inPage = pages[newIndex];
const outClass = forward ? 'flip-out-fwd' : 'flip-out-bwd';
const inClass = forward ? 'flip-in-fwd' : 'flip-in-bwd';
outPage.classList.add(outClass);
// At the midpoint of the flip, swap visible pages
setTimeout(function () {
outPage.classList.remove('active', outClass);
inPage.scrollTop = 0;
inPage.classList.add('active', inClass);
currentIndex = newIndex;
updateControls();
inPage.addEventListener('animationend', function () {
inPage.classList.remove(inClass);
isAnimating = false;
}, { once: true });
}, 290);
}
// Button navigation
btnPrev.addEventListener('click', function () {
turnPage(currentIndex - 1, false);
});
btnNext.addEventListener('click', function () {
turnPage(currentIndex + 1, true);
});
// Keyboard navigation
document.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
turnPage(currentIndex + 1, true);
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
turnPage(currentIndex - 1, false);
}
});
// Click the left/right edges of the book to turn pages
var bookContainer = document.querySelector('.book-container');
bookContainer.addEventListener('click', function (e) {
// Ignore clicks on links (TOC navigation)
if (e.target.closest('a')) return;
var rect = bookContainer.getBoundingClientRect();
var x = e.clientX - rect.left;
var width = rect.width;
if (x < width * 0.14) {
turnPage(currentIndex - 1, false);
} else if (x > width * 0.86) {
turnPage(currentIndex + 1, true);
}
});
// TOC links navigate to specific pages
document.querySelectorAll('[data-goto-page]').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
var targetIndex = parseInt(link.dataset.gotoPage, 10);
var forward = targetIndex > currentIndex;
turnPage(targetIndex, forward);
});
});
// Initialise
updateControls();
})();
</script>
</body> </body>
</html> </html>