Gospel courtroom page: tabs, plea dropdown, plea email, Acts, accuser/charges, site nav and links; fix service worker for /salvation; courtroom contact type; accept community type; v2.39.0
Publish Container / build (push) Successful in 16m32s
Publish Container / build (push) Successful in 16m32s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ea463b5851
commit
27656b84f2
11 files changed
+875
-22
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "siteforge",
|
||||
"private": true,
|
||||
"version": "2.38.0",
|
||||
"version": "2.39.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
+753
-16
@@ -3,7 +3,17 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Gospel Harmony — The Trial & Crucifixion</title>
|
||||
<meta name="description" content="The good news of Jesus, presented as a courtroom case: God the Judge, the charge, the Substitute who took your place, and the pardon, with the full Gospel record of his trial, crucifixion and resurrection.">
|
||||
<link rel="canonical" href="https://versebyversewithnate.us/salvation">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:site_name" content="Verse by Verse with Nate">
|
||||
<meta property="og:url" content="https://versebyversewithnate.us/salvation">
|
||||
<meta property="og:title" content="The Gospel, in a Courtroom">
|
||||
<meta property="og:description" content="God is the Judge, you are on trial, and a Substitute has stepped forward. Hear the case and enter your plea.">
|
||||
<meta property="og:image" content="https://versebyversewithnate.us/images/banner.png">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<title>The Gospel, in a Courtroom | Verse by Verse with Nate</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700;900&family=EB+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
@@ -18,6 +28,7 @@
|
||||
--mark-color: #7a4a8f;
|
||||
--luke-color: #2e7d52;
|
||||
--john-color: #b5651d;
|
||||
--acts-color: #2f6f73;
|
||||
--shadow: rgba(26,18,8,0.18);
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
@@ -275,6 +286,7 @@
|
||||
.badge-mark { background: var(--mark-color); border-color: #6a3a7f; }
|
||||
.badge-luke { background: var(--luke-color); border-color: #1e6040; }
|
||||
.badge-john { background: var(--john-color); border-color: #8b4513; }
|
||||
.badge-acts { background: var(--acts-color); border-color: #1f4f52; }
|
||||
.ref-text {
|
||||
font-size: 0.72rem;
|
||||
color: #7a5a2a;
|
||||
@@ -297,6 +309,114 @@
|
||||
font-style: italic;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ── Gospel message + response ── */
|
||||
.gospel-message, .respond {
|
||||
background: linear-gradient(135deg, rgba(245,237,216,0.97) 0%, rgba(232,217,184,0.95) 100%);
|
||||
border-radius: 4px;
|
||||
border-top: 4px solid var(--crimson);
|
||||
box-shadow: 2px 3px 18px var(--shadow), inset 0 1px 0 rgba(255,255,255,0.4);
|
||||
padding: 2rem 2.2rem;
|
||||
margin: 1.5rem 0 2.2rem;
|
||||
}
|
||||
.gospel-message h2, .respond h2 {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: clamp(1.25rem, 3.4vw, 1.7rem);
|
||||
font-weight: 700;
|
||||
color: var(--ink);
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.gospel-lead { font-size: 1.15rem; font-style: italic; color: var(--ink-light); line-height: 1.6; margin-bottom: 1.4rem; }
|
||||
.gospel-points { list-style: none; display: grid; gap: 1.1rem; counter-reset: gp; }
|
||||
.gospel-points li { counter-increment: gp; position: relative; padding-left: 2.8rem; line-height: 1.6; font-size: 1.05rem; color: var(--ink); }
|
||||
.gospel-points li::before {
|
||||
content: counter(gp);
|
||||
position: absolute; left: 0; top: 0.05rem;
|
||||
width: 1.9rem; height: 1.9rem; border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--gold), var(--crimson));
|
||||
color: #fff; font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.85rem;
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
.gospel-points strong { font-family: 'Cinzel', serif; font-size: 0.95rem; letter-spacing: 0.03em; }
|
||||
.gospel-points .verse { display: block; margin-top: 0.2rem; font-style: italic; color: var(--ink-light); font-size: 0.98rem; }
|
||||
.gospel-points .verse cite { font-style: normal; font-size: 0.8rem; color: #7a5a2a; letter-spacing: 0.04em; }
|
||||
.gospel-cta { margin-top: 1.6rem; font-style: italic; color: var(--ink-light); }
|
||||
.gospel-cta a { color: var(--crimson); font-weight: 600; }
|
||||
|
||||
.docket { display: flex; align-items: center; gap: 1rem; padding: 0.8rem 1rem; margin-bottom: 1.2rem; border: 1px solid rgba(139,26,26,0.35); border-left: 4px solid var(--crimson); background: rgba(139,26,26,0.06); }
|
||||
.docket-scales { font-size: 2.1rem; line-height: 1; color: var(--crimson); }
|
||||
.docket-court { font-family: 'Cinzel', serif; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; font-size: 0.8rem; color: var(--crimson); }
|
||||
.docket-case { font-size: 0.95rem; color: var(--ink-light); margin-top: 0.15rem; }
|
||||
.docket-case em { font-style: normal; font-weight: 600; color: var(--ink); }
|
||||
.stage { display: block; font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--crimson); margin-bottom: 0.25rem; }
|
||||
.stage--plain { margin-bottom: 0.4rem; }
|
||||
.gospel-points li { padding-bottom: 1.1rem; border-bottom: 1px dashed rgba(139,26,26,0.25); }
|
||||
.gospel-points li:last-child { border-bottom: 0; padding-bottom: 0; }
|
||||
.gospel-points .verse + .verse { margin-top: 0.35rem; }
|
||||
.gospel-points li { padding-left: 0; }
|
||||
.gospel-points li::before { display: none; }
|
||||
.timeline-intro { text-align: center; margin: 2.8rem 0 0.4rem; }
|
||||
.timeline-intro h2 { font-family: 'Cinzel', serif; color: var(--parchment); font-size: 1.3rem; letter-spacing: 0.05em; }
|
||||
.timeline-intro p { color: var(--parchment-dark); font-style: italic; opacity: 0.85; margin-top: 0.3rem; }
|
||||
.prayer {
|
||||
margin: 1.2rem 0 1.4rem; padding: 1.1rem 1.4rem;
|
||||
border-left: 4px solid var(--gold); background: rgba(184,134,11,0.09);
|
||||
font-style: italic; font-size: 1.1rem; line-height: 1.75; color: var(--ink);
|
||||
}
|
||||
.respond .note { font-size: 0.95rem; color: var(--ink-light); line-height: 1.6; }
|
||||
.btn-pray, .btn-send {
|
||||
font-family: 'Cinzel', serif; font-weight: 700; letter-spacing: 0.08em; font-size: 0.85rem;
|
||||
text-transform: uppercase; cursor: pointer; border-radius: 2px;
|
||||
padding: 0.8rem 1.6rem; border: 1px solid #6b1010;
|
||||
background: linear-gradient(135deg, var(--crimson), #6b1010); color: var(--parchment);
|
||||
transition: filter 0.15s, transform 0.15s;
|
||||
}
|
||||
.btn-pray:hover, .btn-send:hover { filter: brightness(1.15); transform: translateY(-1px); }
|
||||
.btn-send:disabled { opacity: 0.6; cursor: default; transform: none; }
|
||||
.btn-ghost { font-family: 'Cinzel', serif; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; background: transparent; border: 0; color: var(--ink-light); cursor: pointer; text-decoration: underline; margin-left: 1rem; }
|
||||
.respond-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.2rem; }
|
||||
.respond-form { display: none; margin-top: 1.4rem; padding-top: 1.4rem; border-top: 1px dashed rgba(139,26,26,0.3); }
|
||||
.respond-form.open { display: block; }
|
||||
.respond-form label { display: block; font-family: 'Cinzel', serif; font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-light); margin: 0.9rem 0 0.3rem; }
|
||||
.respond-form input[type=text], .respond-form input[type=email], .respond-form textarea {
|
||||
width: 100%; font-family: 'EB Garamond', serif; font-size: 1.05rem; color: var(--ink);
|
||||
background: rgba(255,255,255,0.55); border: 1px solid rgba(26,18,8,0.3); border-radius: 2px; padding: 0.6rem 0.8rem;
|
||||
}
|
||||
.respond-form input:focus, .respond-form textarea:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
|
||||
.respond-form .hp { position: absolute; left: -9999px; }
|
||||
.form-error { color: var(--crimson); font-style: italic; margin-top: 0.8rem; min-height: 1.2em; }
|
||||
.respond-done { display: none; }
|
||||
.respond-done.open { display: block; }
|
||||
.respond-done ul { margin: 0.8rem 0 0 1.2rem; line-height: 1.8; }
|
||||
.respond-done a { color: var(--crimson); font-weight: 600; }
|
||||
@media (max-width: 600px) { .gospel-message, .respond { padding: 1.4rem 1.2rem; } }
|
||||
|
||||
/* ── Courtroom framing for the harmony timeline ── */
|
||||
.legend-title, .filter-title {
|
||||
text-align: center; font-family: 'Cinzel', serif; font-size: 0.72rem; letter-spacing: 0.18em;
|
||||
text-transform: uppercase; color: var(--gold-light); margin-top: 2rem; opacity: 0.9;
|
||||
}
|
||||
.filter-title { margin-top: 1.2rem; }
|
||||
.legend-title + .legend { margin-top: 0.8rem; }
|
||||
.court-note {
|
||||
margin: 0.2rem 0 1rem; padding: 1rem 1.2rem;
|
||||
border: 1px solid rgba(184,134,11,0.35); border-left: 4px solid var(--gold);
|
||||
background: rgba(245,237,216,0.06); border-radius: 3px; color: var(--parchment);
|
||||
}
|
||||
.court-note-head { font-family: 'Cinzel', serif; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-light); margin-bottom: 0.4rem; }
|
||||
.court-note p { font-size: 1rem; line-height: 1.65; font-style: italic; color: var(--parchment); opacity: 0.92; }
|
||||
.court-note p + p { margin-top: 0.5rem; }
|
||||
.court-note cite { font-style: normal; font-size: 0.8rem; color: var(--gold-light); letter-spacing: 0.04em; }
|
||||
.exhibit { display: block; font-family: 'Cinzel', serif; font-size: 0.6rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--crimson); margin-bottom: 0.2rem; opacity: 0.85; }
|
||||
.adjourned {
|
||||
margin: 2.2rem 0 0; padding: 1.2rem 1.4rem; text-align: center;
|
||||
border-top: 1px solid rgba(184,134,11,0.4); border-bottom: 1px solid rgba(184,134,11,0.4);
|
||||
color: var(--parchment);
|
||||
}
|
||||
.adjourned h3 { font-family: 'Cinzel', serif; font-size: 1rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold-light); margin-bottom: 0.5rem; }
|
||||
.adjourned p { font-style: italic; line-height: 1.65; opacity: 0.92; }
|
||||
@media (max-width: 600px) { .court-note { padding: 0.85rem 0.95rem; } }
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem 1.5rem;
|
||||
@@ -314,18 +434,374 @@
|
||||
.phase-header::before { left: -2rem; width: 1.6rem; }
|
||||
.event-card { padding: 0.9rem 1rem 0.9rem 1.1rem; }
|
||||
}
|
||||
|
||||
/* ── Readability ── */
|
||||
body { font-size: 18px; }
|
||||
header .subtitle { font-size: 1.25rem; opacity: 1; }
|
||||
.legend-item { font-size: 0.9rem; }
|
||||
.filter-btn { font-size: 0.82rem; padding: 0.5rem 1rem; }
|
||||
.filter-btn:not(.active-filter) { opacity: 0.85; }
|
||||
.legend-title, .filter-title { font-size: 0.82rem; opacity: 1; }
|
||||
.legend-sub { font-size: 1.05rem !important; opacity: 0.95 !important; }
|
||||
.phase-label { font-size: 0.82rem; }
|
||||
.phase-title { font-size: 1.3rem; line-height: 1.3; }
|
||||
.event-card { padding: 1.2rem 1.4rem 1.2rem 1.6rem; }
|
||||
.event-title { font-size: 1.05rem; line-height: 1.4; margin-bottom: 0.5rem; }
|
||||
.event-body { font-size: 1.12rem; line-height: 1.75; font-style: normal; color: #2b1d08; margin-bottom: 0.7rem; }
|
||||
.badge { font-size: 0.78rem; padding: 0.25rem 0.7rem; }
|
||||
.ref-text { font-size: 0.92rem; color: #5e4318; margin-top: 0.55rem; }
|
||||
.unique-note { font-size: 0.98rem; line-height: 1.55; color: #7a1414; padding-top: 0.45rem; margin-top: 0.5rem; }
|
||||
.exhibit { font-size: 0.72rem; opacity: 1; }
|
||||
.court-note { padding: 1.2rem 1.4rem; background: rgba(245,237,216,0.09); }
|
||||
.court-note-head { font-size: 0.8rem; }
|
||||
.court-note p { font-size: 1.15rem; line-height: 1.75; font-style: normal; opacity: 1; }
|
||||
.adjourned h3 { font-size: 1.1rem; }
|
||||
.adjourned p { font-size: 1.15rem; line-height: 1.75; font-style: normal; opacity: 1; }
|
||||
.timeline-intro h2 { font-size: 1.5rem; }
|
||||
.timeline-intro p { font-size: 1.15rem; opacity: 1; }
|
||||
.gospel-lead { font-size: 1.25rem; font-style: normal; }
|
||||
.gospel-points li { font-size: 1.15rem; line-height: 1.7; }
|
||||
.gospel-points strong { font-size: 1.05rem; }
|
||||
.gospel-points .verse { font-size: 1.08rem; line-height: 1.6; color: #3d2c10; }
|
||||
.gospel-points .verse cite { font-size: 0.9rem; color: #5e4318; }
|
||||
.stage { font-size: 0.82rem; }
|
||||
.docket-court { font-size: 0.9rem; }
|
||||
.docket-case { font-size: 1.05rem; }
|
||||
.gospel-cta { font-size: 1.1rem; font-style: normal; }
|
||||
.respond .note { font-size: 1.1rem; line-height: 1.7; }
|
||||
.prayer { font-size: 1.2rem; line-height: 1.8; }
|
||||
.respond-form label { font-size: 0.82rem; }
|
||||
.respond-form input[type=text], .respond-form input[type=email], .respond-form textarea { font-size: 1.15rem; }
|
||||
footer { font-size: 0.95rem; color: rgba(245,237,216,0.7); }
|
||||
@media (max-width: 600px) { body { font-size: 17px; } .phase-title { font-size: 1.15rem; } }
|
||||
|
||||
/* ══ Redesign ══════════════════════════════════════════════ */
|
||||
:root { --bar-h: 52px; }
|
||||
html { scroll-behavior: smooth; scroll-padding-top: calc(var(--bar-h) + 16px); }
|
||||
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
|
||||
.topbar { position: sticky; top: 0; z-index: 50; height: var(--bar-h); background: rgba(14,8,3,0.94); backdrop-filter: blur(8px); border-bottom: 1px solid rgba(184,134,11,0.35); }
|
||||
.topbar-inner { max-width: 1100px; height: 100%; margin: 0 auto; padding: 0 1.2rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
.topbar a { color: var(--parchment); text-decoration: none; font-family: 'Cinzel', serif; font-size: 0.78rem; letter-spacing: 0.08em; }
|
||||
.topbar-back { font-weight: 700; color: var(--gold-light) !important; white-space: nowrap; }
|
||||
.topbar-back:hover { color: #fff !important; }
|
||||
.topbar-back-short { display: none; }
|
||||
.topbar-links { display: flex; align-items: center; gap: 1.1rem; }
|
||||
.topbar-links a { opacity: 0.8; padding: 0.3rem 0; border-bottom: 2px solid transparent; transition: opacity .15s, border-color .15s; text-transform: uppercase; }
|
||||
.topbar-links a:hover, .topbar-links a.active { opacity: 1; border-bottom-color: var(--gold); }
|
||||
.topbar-plea { opacity: 1 !important; color: var(--gold-light) !important; }
|
||||
.topbar-sep { width: 1px; height: 1.1rem; background: rgba(245,237,216,0.25); }
|
||||
.hero { padding: 3.5rem 1.5rem 2rem; }
|
||||
.hero-kicker { font-family: 'Cinzel', serif; font-size: 0.9rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-light); margin: 1.6rem 0 0.9rem; }
|
||||
.hero-kicker span { font-size: 1.4rem; vertical-align: -0.1em; margin-right: 0.3rem; }
|
||||
.hero h1 { font-size: clamp(2.2rem, 7vw, 3.8rem); line-height: 1.1; }
|
||||
.hero-lead { max-width: 40rem; margin: 1.2rem auto 0; font-size: 1.3rem; line-height: 1.65; color: var(--parchment); }
|
||||
.hero-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.7rem; margin-top: 1.8rem; }
|
||||
.hero-btn { font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.82rem; letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; color: var(--parchment); border: 1px solid rgba(184,134,11,0.6); padding: 0.8rem 1.4rem; border-radius: 2px; transition: background .15s, transform .15s; }
|
||||
.hero-btn:hover { background: rgba(184,134,11,0.18); transform: translateY(-1px); }
|
||||
.hero-btn--primary { background: linear-gradient(135deg, var(--gold), #9a6f0a); color: var(--ink); border-color: var(--gold); }
|
||||
.hero-btn--primary:hover { background: linear-gradient(135deg, var(--gold-light), var(--gold)); }
|
||||
.hero-verse { margin-top: 1.8rem; font-style: italic; font-size: 1.15rem; color: var(--parchment-dark); }
|
||||
.hero-verse cite { font-style: normal; font-size: 0.9rem; color: var(--gold-light); letter-spacing: 0.05em; margin-left: 0.3rem; }
|
||||
.gospel-message { margin-top: 2.2rem; }
|
||||
.record-head { text-align: center; margin: 3.5rem 0 0.5rem; }
|
||||
.record-kicker { font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-light); }
|
||||
.record-head h2 { font-family: 'Cinzel', serif; font-size: clamp(1.7rem, 5vw, 2.5rem); color: var(--parchment); letter-spacing: 0.05em; margin-top: 0.4rem; }
|
||||
.record-sub { font-family: 'Cinzel', serif; color: var(--gold-light); letter-spacing: 0.06em; margin-top: 0.5rem; font-size: 1rem; }
|
||||
.record-text { color: var(--parchment-dark); font-style: italic; font-size: 1.15rem; margin-top: 0.7rem; }
|
||||
.filter-bar { position: sticky; top: var(--bar-h); z-index: 20; margin: 1rem -1.5rem 2rem; padding: 0.7rem 1.5rem; background: linear-gradient(180deg, #1a0f05 75%, rgba(26,15,5,0)); }
|
||||
.site-foot { text-align: center; padding: 3rem 1rem 3.5rem; margin-top: 2.5rem; border-top: 1px solid rgba(184,134,11,0.4); font-style: normal; color: var(--parchment); }
|
||||
.foot-brand { font-family: 'Cinzel', serif; font-size: 1.15rem; letter-spacing: 0.08em; color: var(--gold-light); text-decoration: none; }
|
||||
.foot-line { margin-top: 0.4rem; opacity: 0.85; font-size: 1.05rem; font-style: italic; }
|
||||
.foot-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; margin-top: 1.5rem; }
|
||||
.foot-btn { font-family: 'Cinzel', serif; font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; color: var(--parchment); border: 1px solid rgba(184,134,11,0.45); padding: 0.6rem 1rem; border-radius: 2px; transition: background .15s; }
|
||||
.foot-btn:hover { background: rgba(184,134,11,0.18); }
|
||||
.foot-btn--primary { background: rgba(184,134,11,0.22); color: #fff; border-color: var(--gold); font-weight: 700; }
|
||||
.foot-share { margin-top: 1.4rem; background: transparent; border: 0; color: var(--gold-light); font-family: 'Cinzel', serif; font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase; text-decoration: underline; cursor: pointer; }
|
||||
.foot-note { min-height: 1.3em; margin-top: 0.4rem; font-size: 0.95rem; color: var(--parchment-dark); font-style: italic; }
|
||||
.foot-small { margin-top: 1.4rem; font-size: 0.95rem; line-height: 1.7; color: rgba(245,237,216,0.7); font-style: italic; }
|
||||
.to-top { position: fixed; right: 1rem; bottom: 1rem; z-index: 40; width: 2.8rem; height: 2.8rem; border-radius: 50%; border: 1px solid var(--gold); background: rgba(14,8,3,0.92); color: var(--gold-light); font-size: 1.2rem; cursor: pointer; opacity: 0; pointer-events: none; transition: opacity .2s; }
|
||||
.to-top.show { opacity: 1; pointer-events: auto; }
|
||||
@media (max-width: 720px) {
|
||||
.topbar-links a:not(.topbar-plea):not([data-spy="record"]) { display: none; }
|
||||
.topbar-sep { display: none; }
|
||||
.topbar-links { gap: 0.9rem; }
|
||||
.topbar-back-long { display: none; }
|
||||
.topbar-back-short { display: inline; }
|
||||
.hero { padding: 2.2rem 0.5rem 1.5rem; }
|
||||
.hero-lead { font-size: 1.15rem; }
|
||||
.filter-bar { flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start; margin: 1rem -1.5rem 1.6rem; -webkit-overflow-scrolling: touch; }
|
||||
.filter-btn { white-space: nowrap; }
|
||||
}
|
||||
.filter-bar { gap: 0.4rem; padding-top: 0.55rem; padding-bottom: 0.55rem; }
|
||||
.filter-btn { font-size: 0.74rem; padding: 0.42rem 0.7rem; letter-spacing: 0.06em; }
|
||||
.gospel-points .para { display: block; margin-top: 0.55rem; }
|
||||
|
||||
/* ══ Tabs + plea ══════════════════════════════════════════ */
|
||||
:root { --tab-h: 52px; }
|
||||
.tabbar { position: sticky; top: var(--bar-h); z-index: 30; display: flex; justify-content: center; gap: 0.5rem; padding: 0.6rem 0; margin: 1.2rem -1.5rem 0; background: linear-gradient(180deg, #1a0f05 80%, rgba(26,15,5,0)); }
|
||||
.tabbar button { font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.95rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--parchment-dark); background: transparent; border: 1px solid rgba(184,134,11,0.5); padding: 0.7rem 1.8rem; border-radius: 2px; cursor: pointer; transition: background .15s, color .15s; }
|
||||
.tabbar button:hover { background: rgba(184,134,11,0.15); }
|
||||
.tabbar button[aria-selected="true"] { background: var(--gold); color: var(--ink); border-color: var(--gold); }
|
||||
.panel[hidden] { display: none; }
|
||||
.filter-bar { top: calc(var(--bar-h) + var(--tab-h)); }
|
||||
.panel, .gospel-message, .respond, .record-head { scroll-margin-top: calc(var(--bar-h) + var(--tab-h) + 12px); }
|
||||
.adjourned-cta { margin-top: 1.4rem; }
|
||||
.adjourned-cta .hero-btn { display: inline-block; }
|
||||
.respond { margin-top: 2rem; }
|
||||
.respond h3 { font-family: 'Cinzel', serif; color: var(--ink); }
|
||||
.plea-picker { margin: 1.2rem 0 1rem; }
|
||||
.plea-picker label { display: block; font-family: 'Cinzel', serif; font-size: 0.85rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-light); margin-bottom: 0.4rem; }
|
||||
.plea-picker select { width: 100%; max-width: 30rem; font-family: 'EB Garamond', serif; font-size: 1.25rem; color: var(--ink); background: rgba(255,255,255,0.7); border: 2px solid var(--crimson); border-radius: 2px; padding: 0.7rem 0.9rem; cursor: pointer; }
|
||||
.plea-picker select:focus { outline: 3px solid var(--gold); outline-offset: 2px; }
|
||||
.plea-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1.2rem 0; }
|
||||
.plea-card { padding: 1.1rem 1.2rem; border: 1px solid rgba(26,18,8,0.25); border-top: 4px solid var(--gold); background: rgba(255,255,255,0.35); border-radius: 2px; transition: box-shadow .2s, border-color .2s, opacity .2s; }
|
||||
.plea-card h3 { font-size: 1.15rem; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 0.5rem; }
|
||||
.plea-card p { font-size: 1.05rem; line-height: 1.6; color: var(--ink); }
|
||||
.plea-card p + p { margin-top: 0.6rem; }
|
||||
.plea-card.is-selected { border-color: var(--crimson); border-top-color: var(--crimson); box-shadow: 0 0 0 2px var(--crimson); background: rgba(255,255,255,0.65); }
|
||||
.plea-card.is-dim { opacity: 0.55; }
|
||||
.plea-panel { margin-top: 1.6rem; padding-top: 1.4rem; border-top: 2px solid var(--crimson); }
|
||||
.plea-panel[hidden] { display: none; }
|
||||
.plea-title { font-size: clamp(1.15rem, 3vw, 1.45rem); line-height: 1.35; margin-bottom: 0.8rem; }
|
||||
.plea-panel .note { margin-top: 0.8rem; }
|
||||
.plea-panel .verse { display: block; margin-top: 0.5rem; font-style: italic; font-size: 1.08rem; color: #3d2c10; }
|
||||
.plea-panel .verse cite { font-style: normal; font-size: 0.9rem; color: #5e4318; }
|
||||
.next-steps { margin: 1rem 0 0 1.3rem; display: grid; gap: 0.9rem; font-size: 1.1rem; line-height: 1.65; color: var(--ink); }
|
||||
.next-steps li::marker { color: var(--crimson); font-weight: 700; }
|
||||
.next-steps .prayer { margin-top: 0.7rem; }
|
||||
.selftest { margin: 1.2rem 0; padding: 1rem 1.2rem; border-left: 4px solid var(--crimson); background: rgba(139,26,26,0.07); }
|
||||
.selftest-head { font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.85rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--crimson); }
|
||||
.selftest ul { margin: 0.6rem 0 0.4rem 1.2rem; display: grid; gap: 0.4rem; font-size: 1.08rem; line-height: 1.55; color: var(--ink); }
|
||||
.btn-link { font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; color: var(--crimson); border: 1px solid var(--crimson); padding: 0.75rem 1.1rem; border-radius: 2px; }
|
||||
.btn-link:hover { background: rgba(139,26,26,0.1); }
|
||||
@media (max-width: 720px) {
|
||||
.plea-cards { grid-template-columns: 1fr; }
|
||||
.tabbar button { padding: 0.65rem 1.1rem; font-size: 0.85rem; }
|
||||
}
|
||||
.plea-panel { scroll-margin-top: calc(var(--bar-h) + var(--tab-h) + 20px); }
|
||||
|
||||
.record-callout { margin: 2rem 0; padding: 2rem 2.2rem; text-align: center; background: linear-gradient(145deg, rgba(139,26,26,0.35), rgba(26,9,5,0.85)); border: 2px solid var(--gold); border-radius: 4px; box-shadow: 0 6px 28px rgba(0,0,0,0.45); color: var(--parchment); }
|
||||
.callout-kicker { font-family: 'Cinzel', serif; font-size: 0.85rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-light); }
|
||||
.record-callout h2 { font-family: 'Cinzel', serif; font-size: clamp(1.6rem, 5vw, 2.3rem); line-height: 1.2; color: #fff; margin: 0.5rem 0 1rem; letter-spacing: 0.04em; }
|
||||
.record-callout p { font-size: 1.2rem; line-height: 1.7; max-width: 38rem; margin: 0 auto 0.9rem; }
|
||||
.record-callout strong { color: var(--gold-light); }
|
||||
.callout-actions { display: flex; flex-direction: column; align-items: center; gap: 0.9rem; margin-top: 1.4rem; }
|
||||
.record-callout .hero-btn--primary { font-size: 0.95rem; padding: 1rem 2rem; }
|
||||
.callout-skip { color: var(--parchment-dark); font-size: 1rem; font-style: italic; text-decoration: underline; opacity: 0.85; }
|
||||
.callout-skip:hover { opacity: 1; }
|
||||
@media (max-width: 600px) { .record-callout { padding: 1.5rem 1.1rem; } .record-callout p { font-size: 1.1rem; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar" aria-label="Site and page navigation">
|
||||
<div class="topbar-inner">
|
||||
<a class="topbar-back" href="/"><span aria-hidden="true">←</span> <span class="topbar-back-long">Verse by Verse with Nate</span><span class="topbar-back-short">Back to site</span></a>
|
||||
<div class="topbar-links">
|
||||
<a href="#good-news" data-spy="good-news">The Case</a>
|
||||
<a href="#record" data-spy="record">The Record</a>
|
||||
<a href="#respond" data-spy="respond" class="topbar-plea">Your Plea</a>
|
||||
<span class="topbar-sep" aria-hidden="true"></span>
|
||||
<a href="/start-here" class="topbar-site">Start Here</a>
|
||||
<a href="/episodes" class="topbar-site">Episodes</a>
|
||||
<a href="/study" class="topbar-site">Studies</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="scroll-wrapper">
|
||||
|
||||
<header>
|
||||
<header class="hero">
|
||||
<div class="header-rule"></div>
|
||||
<h1>A <span>Gospel Harmony</span><br>The Trial & Crucifixion of Jesus</h1>
|
||||
<p class="subtitle">Matthew · Mark · Luke · John — Woven into One Timeline</p>
|
||||
<div class="header-rule" style="margin-top:1.6rem;margin-bottom:0"></div>
|
||||
<p class="hero-kicker"><span aria-hidden="true">⚖</span> The Court of Heaven Is in Session</p>
|
||||
<h1>The Gospel, <span>in a Courtroom</span></h1>
|
||||
<p class="hero-lead">God is the Judge. Satan is the accuser. The charges are your sins, and you are the one on trial. But a Substitute has stepped forward to take your place. Hear the case, read the record, and enter your plea.</p>
|
||||
<div class="hero-actions">
|
||||
<a class="hero-btn hero-btn--primary" href="#good-news">Hear the Case</a>
|
||||
<a class="hero-btn" href="#record">Read the Record</a>
|
||||
<a class="hero-btn" href="#respond">Enter Your Plea</a>
|
||||
</div>
|
||||
<p class="hero-verse">“Therefore, there is now no condemnation for those who are in Christ Jesus.” <cite>Romans 8:1</cite></p>
|
||||
<div class="header-rule" style="margin-top:1.8rem;margin-bottom:0"></div>
|
||||
</header>
|
||||
|
||||
<div class="tabbar" role="tablist" aria-label="Page sections">
|
||||
<button type="button" role="tab" id="tab-case" aria-controls="panel-case" aria-selected="true" data-tab="case">The Case</button>
|
||||
<button type="button" role="tab" id="tab-record" aria-controls="panel-record" aria-selected="false" data-tab="record">The Record</button>
|
||||
</div>
|
||||
|
||||
<div class="panel" id="panel-case" role="tabpanel" aria-labelledby="tab-case">
|
||||
<section class="gospel-message" aria-labelledby="good-news">
|
||||
<div class="docket" aria-hidden="true">
|
||||
<span class="docket-scales">⚖</span>
|
||||
<div>
|
||||
<p class="docket-court">In the Court of Heaven</p>
|
||||
<p class="docket-case">The Case of <em>You</em> · Presiding: <em>God, the Righteous Judge</em> · Accuser: <em>Satan</em></p>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="good-news">The Case and the Good News</h2>
|
||||
<p class="gospel-lead">Every one of us will stand before God. The gospel is the news of how a guilty person can walk out of that courtroom free. Follow the case from the accuser’s first charge to the final pardon.</p>
|
||||
<ol class="gospel-points">
|
||||
<li><span class="stage">I · The Judge</span>
|
||||
<strong>God is perfectly just. He will not overlook wrongdoing.</strong> Nothing is hidden from him, and he judges rightly.
|
||||
<span class="verse">“Will not the Judge of all the earth do right?” <cite>Genesis 18:25</cite></span>
|
||||
<span class="verse">“For He has set a day when He will judge the world with justice by the Man He has appointed.” <cite>Acts 17:31</cite></span></li>
|
||||
<li><span class="stage">II · The Accuser</span>
|
||||
<strong>Every courtroom has an accuser, and ours is Satan.</strong> His name means “adversary.” Scripture calls him the accuser of the brothers, who brings charges against us before God. He does not have to invent anything. He only has to point to what we have actually done.
|
||||
<span class="verse">“...the accuser of our brothers, who accuses them before our God day and night.” <cite>Revelation 12:10</cite></span>
|
||||
<span class="verse">“Your adversary the devil prowls around like a roaring lion, looking for someone to devour.” <cite>1 Peter 5:8</cite></span></li>
|
||||
<li><span class="stage">III · The Charges</span>
|
||||
<strong>The charges are the sins you have committed.</strong>
|
||||
<span class="para">Sin is breaking God’s law. That includes the wrong things we knowingly do, the good things we fail to do, and the things we say and think in secret.</span>
|
||||
<span class="para">It also includes breaking his law <em>without knowing it</em>. Not knowing the law has never made anyone innocent. God’s standard is written on every conscience, so no one is without excuse (Romans 1:20; 2:14–15), and even sin done unknowingly still leaves a person guilty (Leviticus 5:17).</span>
|
||||
<span class="para">And God does not grade on a curve. Stumbling at one point means you have broken the whole law. <em>We have all sinned. That includes you and me.</em></span>
|
||||
<span class="verse">“Everyone who practices sin practices lawlessness; indeed, sin is lawlessness.” <cite>1 John 3:4</cite></span>
|
||||
<span class="verse">“For whoever keeps the whole law but stumbles at just one point is guilty of breaking all of it.” <cite>James 2:10</cite></span>
|
||||
<span class="verse">“There is no one righteous, not even one.” <cite>Romans 3:10</cite></span>
|
||||
<span class="verse">“For all have sinned and fall short of the glory of God.” <cite>Romans 3:23</cite></span></li>
|
||||
<li><span class="stage">IV · The Verdict</span>
|
||||
<strong>The evidence is clear, and there is no defense.</strong> We cannot argue our way out, and our good deeds cannot cancel our guilt. The sentence for sin is death, separation from the God who made us.
|
||||
<span class="verse">“...so that every mouth may be silenced and the whole world held accountable to God.” <cite>Romans 3:19</cite></span>
|
||||
<span class="verse">“For the wages of sin is death...” <cite>Romans 6:23</cite></span></li>
|
||||
<li><span class="stage">V · The Substitute</span>
|
||||
<strong>Then God himself stepped forward to take your place.</strong>
|
||||
<span class="para">Jesus is God. The eternal Son took on flesh and was born as a baby in Bethlehem, and he came for one purpose: to take the place of sinners like you and me. He did not come to be served. He came to give his life, and the cross was always where the road was heading.</span>
|
||||
<span class="para">He lived the perfect life you could not live. On the cross he took the sentence that belonged to you, and he rose again on the third day to prove the payment was accepted.</span>
|
||||
<span class="para">The story below walks through his arrest, his trials, his crucifixion and his resurrection.</span>
|
||||
<span class="verse">“The Word became flesh and dwelt among us.” <cite>John 1:14</cite></span>
|
||||
<span class="verse">“She will bear a Son, and you are to name Him Jesus, because He will save His people from their sins.” <cite>Matthew 1:21</cite></span>
|
||||
<span class="verse">“For even the Son of Man did not come to be served, but to serve, and to give His life as a ransom for many.” <cite>Mark 10:45</cite></span>
|
||||
<span class="verse">“For Christ also suffered once for sins, the righteous for the unrighteous, to bring you to God.” <cite>1 Peter 3:18</cite></span>
|
||||
<span class="verse">“God made Him who knew no sin to be sin for us, so that in Him we might become the righteousness of God.” <cite>2 Corinthians 5:21</cite></span>
|
||||
<span class="verse">“But God proves His love for us in this: While we were still sinners, Christ died for us.” <cite>Romans 5:8</cite></span></li>
|
||||
<li><span class="stage">VI · The Pardon</span>
|
||||
<strong>The pardon is a gift. It cannot be earned or bought.</strong> You receive it by turning from your sin and trusting Jesus alone. When you do, God declares you forgiven and counts you righteous because of what Christ has done.
|
||||
<span class="verse">“For by grace you have been saved through faith, and this is not from yourselves, but it is the gift of God, not by works, so that no one can boast.” <cite>Ephesians 2:8–9</cite></span>
|
||||
<span class="verse">“If you confess with your mouth that Jesus is Lord, and believe in your heart that God raised Him from the dead, you will be saved.” <cite>Romans 10:9</cite></span></li>
|
||||
<li><span class="stage">VII · The Result</span>
|
||||
<strong>Case closed. The accuser has nothing left to say.</strong> Our sins were real charges, but they have been answered, because the penalty has been paid in full by the Substitute. The one who trusts in Christ is not waiting nervously for a verdict. The verdict has already been given in their favor.
|
||||
<span class="verse">“Therefore, there is now no condemnation for those who are in Christ Jesus.” <cite>Romans 8:1</cite></span>
|
||||
<span class="verse">“Who can bring an accusation against those whom God has chosen? It is God who justifies. Who then can condemn? Christ Jesus, who died—more than that, was raised to life—is at the right hand of God, and He is interceding for us.” <cite>Romans 8:33–34</cite></span></li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<aside class="record-callout" aria-labelledby="callout-h">
|
||||
<p class="callout-kicker">Before you plead</p>
|
||||
<h2 id="callout-h">Read what Jesus did for you.</h2>
|
||||
<p>Everything above tells you <em>that</em> the Substitute took your place. The record shows you <em>how</em>: the night he was arrested, the trials where he was declared innocent and condemned anyway, the long road to the cross, and the empty tomb on the third day.</p>
|
||||
<p><strong>Please do not skip it.</strong> It is the story of what Jesus did for you, told by four witnesses and woven into one account. Take your time and read it slowly. Then come back and enter your plea.</p>
|
||||
<div class="callout-actions">
|
||||
<a class="hero-btn hero-btn--primary" href="#record">Read what Jesus did for you →</a>
|
||||
<a class="callout-skip" href="#respond">I have already read it. Take me to my plea</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="respond" id="respond" aria-labelledby="respond-h">
|
||||
<p class="stage stage--plain">VIII · Your Plea</p>
|
||||
<h2 id="respond-h">How Do You Plead?</h2>
|
||||
<p class="note">The court is not asking whether you are a good person. It is asking you to be honest about the charges and to decide where you will stand: on your own record, or on what Jesus has done in your place. Choose your plea below. Have a question first? <a href="/contact?source=salvation">Ask a question about the courtroom</a>.</p>
|
||||
|
||||
<div class="plea-picker">
|
||||
<label for="plea-select">Your plea</label>
|
||||
<select id="plea-select">
|
||||
<option value="">Choose your plea…</option>
|
||||
<option value="guilty">Guilty: I accept the pardon</option>
|
||||
<option value="not-guilty">Not guilty: I stand on my own record</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="plea-cards" aria-label="What each plea means">
|
||||
<div class="plea-card" id="card-guilty">
|
||||
<h3>Guilty</h3>
|
||||
<p class="plea-means"><strong>What it means:</strong> I admit it. I have broken God’s law, whether I knew it or not, and I cannot save myself. I am not asking the court to find me innocent. I am asking for the pardon Jesus paid for with his life.</p>
|
||||
<p class="plea-result"><strong>What happens:</strong> The Judge declares you forgiven and counts you righteous because of Christ. The accuser has nothing left to say.</p>
|
||||
</div>
|
||||
<div class="plea-card" id="card-notguilty">
|
||||
<h3>Not Guilty</h3>
|
||||
<p class="plea-means"><strong>What it means:</strong> I stand on my own record. I believe I have kept God’s law well enough to be found innocent, and I do not need anyone to take my place.</p>
|
||||
<p class="plea-result"><strong>What happens:</strong> You are judged by God’s perfect standard, with no Substitute. Be sure you understand what that means before you stand on it.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GUILTY: next steps -->
|
||||
<div class="plea-panel" id="plea-guilty" hidden>
|
||||
<h3 class="plea-title">You have pleaded guilty. That is the beginning of the good news.</h3>
|
||||
<p class="note">You are not earning anything by saying this. You are receiving a gift: “For the wages of sin is death, but the gift of God is eternal life in Christ Jesus our Lord” (Romans 6:23). Here is what to do next.</p>
|
||||
<ol class="next-steps">
|
||||
<li><strong>Tell God.</strong> Words do not save you. Trusting Jesus does. But you can say it to him in your own words, or you can pray something like this:
|
||||
<div class="prayer">“Lord God, I plead guilty. I have sinned against you, and I cannot save myself. I believe Jesus Christ took my place on the cross, died for my sins and rose again. I turn from my sin and I trust him alone. Thank you for the pardon I could never earn. Make me new, and help me follow you. Amen.”</div></li>
|
||||
<li><strong>Believe it is finished.</strong> If you meant that prayer, God has forgiven you: “Therefore, there is now no condemnation for those who are in Christ Jesus” (Romans 8:1). Your feelings may go up and down, but the verdict does not.</li>
|
||||
<li><strong>Tell someone.</strong> Tell a Christian friend or family member what you decided, and tell us too (the optional form is just below).</li>
|
||||
<li><strong>Start reading.</strong> Begin with the Gospel of John, then keep going through the New Testament, and talk with God every day.</li>
|
||||
<li><strong>Find a church.</strong> Join a Bible-teaching church where you can grow, and be baptized as a public sign of your faith (Acts 2:38).</li>
|
||||
</ol>
|
||||
<div id="respond-intro">
|
||||
<p class="note">If you prayed this and meant it, we would love to rejoice with you and help you take your next steps. Letting us know is completely optional.</p>
|
||||
<div class="respond-actions">
|
||||
<button type="button" class="btn-pray" id="btn-pray" aria-expanded="false" aria-controls="respond-form">I prayed. Let the team know</button>
|
||||
</div>
|
||||
<form class="respond-form" id="respond-form" novalidate>
|
||||
<p class="note">Thank you for telling us. Everything below is optional. Your response is sent by email to the Verse by Verse with Nate team at versebyversewithnate.us. It is never posted publicly or shared.</p>
|
||||
<input class="hp" type="text" name="_honey" tabindex="-1" autocomplete="off" aria-hidden="true">
|
||||
<label for="r-name">First name</label>
|
||||
<input type="text" id="r-name" name="name" maxlength="100" autocomplete="given-name">
|
||||
<label for="r-email">Email (if you would like someone to reply)</label>
|
||||
<input type="email" id="r-email" name="email" maxlength="200" autocomplete="email">
|
||||
<label for="r-msg">Anything you would like to share</label>
|
||||
<textarea id="r-msg" name="message" rows="4" maxlength="2000"></textarea>
|
||||
<p class="form-error" id="r-error" role="alert"></p>
|
||||
<div class="respond-actions">
|
||||
<button type="submit" class="btn-send" id="btn-send">Let us know</button>
|
||||
<button type="button" class="btn-ghost" id="btn-cancel">Not now</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="respond-done" id="respond-done" role="status" tabindex="-1">
|
||||
<h3 class="plea-title">Pardoned. Welcome to the family of God.</h3>
|
||||
<p class="note">“Therefore, there is now no condemnation for those who are in Christ Jesus.” (Romans 8:1) You are no longer a defendant. You are a child of the Judge. Thank you for letting us know. We will be glad to hear from you again, and <a href="/start-here">Start Here</a> is a good path through the podcast and studies, or you can <a href="/contact?source=salvation">ask a question</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NOT GUILTY: make sure they understand -->
|
||||
<div class="plea-panel" id="plea-notguilty" hidden>
|
||||
<h3 class="plea-title">You have pleaded not guilty. Please be sure you understand what that means.</h3>
|
||||
<p class="note">Pleading not guilty is not just saying, “I’m not a bad person.” It is telling the Judge, “I have kept your law perfectly, and I need no one to take my place.”</p>
|
||||
<p class="note">God’s standard is not “better than most people.” It is perfection, and breaking it at even one point makes a person guilty of breaking all of it. That includes the things done without knowing they were wrong.</p>
|
||||
<span class="verse">“Be perfect, therefore, as your heavenly Father is perfect.” <cite>Matthew 5:48</cite></span>
|
||||
<span class="verse">“For whoever keeps the whole law but stumbles at just one point is guilty of breaking all of it.” <cite>James 2:10</cite></span>
|
||||
|
||||
<div class="selftest">
|
||||
<p class="selftest-head">Test your plea honestly</p>
|
||||
<ul>
|
||||
<li>Have you ever told a lie, even a small one?</li>
|
||||
<li>Have you ever taken something that was not yours?</li>
|
||||
<li>Have you ever used God’s name carelessly, or put something else ahead of him: money, a person, yourself?</li>
|
||||
<li>Have you ever hated someone in your heart, or looked at someone with lust? Jesus said those count too (Matthew 5:21–22, 27–28).</li>
|
||||
</ul>
|
||||
<p class="note">If the answer to any of these is yes, then by God’s own standard the plea does not hold. “If we say that we have no sin, we are deceiving ourselves, and the truth is not in us” (1 John 1:8).</p>
|
||||
</div>
|
||||
|
||||
<p class="note"><strong>What a not-guilty plea costs.</strong> It means standing before the Judge on your own record, with no Substitute. The sentence for sin stands (Romans 6:23). That is not what God wants for you. He says he takes no pleasure in anyone’s death (Ezekiel 33:11), and the offer of pardon is open right now: “Now is the day of salvation” (2 Corinthians 6:2).</p>
|
||||
<p class="note"><strong>If you are not sure.</strong> Questions and doubts are fine. Asking honestly is not the same as refusing. You can read the record of what Jesus did, or ask us a question.</p>
|
||||
<div class="respond-actions">
|
||||
<button type="button" class="btn-pray" id="btn-change-plea">Change my plea to guilty</button>
|
||||
<a class="btn-link" href="#record">Read the record</a>
|
||||
<a class="btn-link" href="/contact?source=salvation">Ask a question</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="panel" id="panel-record" role="tabpanel" aria-labelledby="tab-record" hidden>
|
||||
<section class="record-head" id="record" aria-labelledby="record-h">
|
||||
<p class="record-kicker">Part Two</p>
|
||||
<h2 id="record-h">The Record of the Court</h2>
|
||||
<p class="record-sub">A Gospel Harmony: the Trial, Crucifixion & Resurrection of Jesus</p>
|
||||
<p class="record-text">Jesus was tried in your place. The witnesses give their testimony, woven into one record of the proceedings.</p>
|
||||
</section>
|
||||
|
||||
<div class="record-wrap">
|
||||
<p class="legend-title">The Witnesses</p>
|
||||
<p class="legend-sub" style="text-align:center;color:var(--parchment-dark);font-style:italic;opacity:0.8;font-size:0.95rem;margin-top:0.3rem">The four Gospel writers give testimony, and Luke’s second account, Acts, adds what happened after the resurrection. Tap a name to hear one witness on his own.</p>
|
||||
<div class="legend">
|
||||
<div class="legend-item" data-gospel="matt" onclick="toggleGospel('matt',this)">
|
||||
<div class="legend-dot" style="background:var(--matt-color)"></div> Matthew
|
||||
@@ -339,8 +815,12 @@
|
||||
<div class="legend-item" data-gospel="john" onclick="toggleGospel('john',this)">
|
||||
<div class="legend-dot" style="background:var(--john-color)"></div> John
|
||||
</div>
|
||||
<div class="legend-item" data-gospel="acts" onclick="toggleGospel('acts',this)">
|
||||
<div class="legend-dot" style="background:var(--acts-color)"></div> Acts
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="filter-title">The Proceedings</p>
|
||||
<div class="filter-bar">
|
||||
<button class="filter-btn active-filter" style="border-color:var(--gold);background:var(--gold);color:var(--ink)" onclick="filterPhase('all',this)">All Events</button>
|
||||
<button class="filter-btn" style="border-color:#555" onclick="filterPhase('arrest',this)">Arrest</button>
|
||||
@@ -348,32 +828,59 @@
|
||||
<button class="filter-btn" style="border-color:#555" onclick="filterPhase('roman-trial',this)">Roman Trial</button>
|
||||
<button class="filter-btn" style="border-color:#555" onclick="filterPhase('crucifixion',this)">Crucifixion</button>
|
||||
<button class="filter-btn" style="border-color:#555" onclick="filterPhase('burial',this)">Burial</button>
|
||||
<button class="filter-btn" style="border-color:#555" onclick="filterPhase('resurrection',this)">Resurrection</button>
|
||||
</div>
|
||||
|
||||
<div class="timeline" id="timeline"></div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
A harmonic arrangement of Matthew 26–27, Mark 14–15, Luke 22–23, and John 18–19.<br>
|
||||
Click any Gospel badge to open that passage in the Berean Standard Bible (BSB) at BibleHub.
|
||||
<section class="adjourned" aria-label="Court adjourned">
|
||||
<h3>Court Is Adjourned, but the Case Is Not Over</h3>
|
||||
<p>The record is complete. The accused was innocent, the sentence was real, the payment was made in full by the Substitute, and God raised him from the dead. The only question left is the one the court now puts to you.</p>
|
||||
<p class="adjourned-cta"><a class="hero-btn hero-btn--primary" href="#respond">Enter Your Plea</a></p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<footer class="site-foot">
|
||||
<a class="foot-brand" href="/">✦ Verse by Verse with Nate</a>
|
||||
<p class="foot-line">Free Bible teaching, one verse at a time.</p>
|
||||
<nav class="foot-links" aria-label="More from the site">
|
||||
<a class="foot-btn foot-btn--primary" href="/">← Back to the main site</a>
|
||||
<a class="foot-btn" href="/start-here">Start Here</a>
|
||||
<a class="foot-btn" href="/episodes">Episodes</a>
|
||||
<a class="foot-btn" href="/study">Bible Studies</a>
|
||||
<a class="foot-btn" href="/questions">Q&A</a>
|
||||
<a class="foot-btn" href="/contact?source=salvation">Ask a question</a>
|
||||
</nav>
|
||||
<button type="button" class="foot-share" id="btn-share">Share this page</button>
|
||||
<p class="foot-note" id="share-note" role="status"></p>
|
||||
<p class="foot-small">
|
||||
A harmonic arrangement of Matthew 26–28, Mark 14–16, Luke 22–24, John 18–20, and Acts 1.<br>
|
||||
Click any Gospel badge to open that passage in the Berean Standard Bible (BSB) at BibleHub.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<button type="button" class="to-top" id="to-top" aria-label="Back to top">↑</button>
|
||||
|
||||
<script>
|
||||
// BSB URL helper — BibleHub format: https://biblehub.com/bsb/matthew/26.htm
|
||||
const BSB = {
|
||||
matt: (ch) => `https://biblehub.com/bsb/matthew/${ch}.htm`,
|
||||
mark: (ch) => `https://biblehub.com/bsb/mark/${ch}.htm`,
|
||||
acts: (ch) => `https://biblehub.com/bsb/acts/${ch}.htm`,
|
||||
luke: (ch) => `https://biblehub.com/bsb/luke/${ch}.htm`,
|
||||
john: (ch) => `https://biblehub.com/bsb/john/${ch}.htm`,
|
||||
};
|
||||
|
||||
const GOSPELS = ['matt','mark','luke','john'];
|
||||
const GOSPELS = ['matt','mark','luke','john','acts'];
|
||||
const activeGospels = new Set(GOSPELS);
|
||||
let activePhase = 'all';
|
||||
|
||||
const data = [
|
||||
// ── PHASE 1: ARREST ──────────────────────────────────────────
|
||||
{ phase:'arrest', phaseLabel:'Phase I', phaseTitle:'The Arrest in Gethsemane', events:[
|
||||
{ phase:'arrest', court:{ head:"The Accused Is Taken Into Custody", text:["The night before the trial begins, the court meets in a garden. The accused is not captured by force; he gives himself up. Everything that follows is the Father’s plan and the Son’s willing choice."] }, phaseLabel:'Phase I', phaseTitle:'The Arrest in Gethsemane', events:[
|
||||
{
|
||||
title:'The Last Supper & Passover Meal',
|
||||
body:"Jesus eats the Passover meal with his twelve disciples, institutes the Lord's Supper, and predicts his betrayal.",
|
||||
@@ -448,7 +955,7 @@ const data = [
|
||||
]},
|
||||
|
||||
// ── PHASE 2: JEWISH TRIAL ─────────────────────────────────────
|
||||
{ phase:'jewish-trial', phaseLabel:'Phase II', phaseTitle:'The Jewish Trial', events:[
|
||||
{ phase:'jewish-trial', court:{ head:"The Hearing Before the Religious Court", text:["Witnesses are called, but their testimony does not agree (Mark 14:56). In the end the verdict comes from the accused’s own lips, when he truthfully claims who he is. The Judge of all the earth stands in the dock, and he is found “guilty” of blasphemy by men who would not believe him."] }, phaseLabel:'Phase II', phaseTitle:'The Jewish Trial', events:[
|
||||
{
|
||||
title:'Jesus Led to Annas (John)',
|
||||
body:"Jesus is first brought to Annas, father-in-law of Caiaphas, who questions Jesus about his disciples and teaching.",
|
||||
@@ -513,7 +1020,7 @@ const data = [
|
||||
]},
|
||||
|
||||
// ── PHASE 3: ROMAN TRIAL ──────────────────────────────────────
|
||||
{ phase:'roman-trial', phaseLabel:'Phase III', phaseTitle:'The Roman Trial Before Pilate', events:[
|
||||
{ phase:'roman-trial', court:{ head:"The Trial Before the Governor", text:["Now the case moves to the civil court, and the verdict reverses. Pilate examines the accused and declares him innocent again and again: “I find no basis for a charge” (John 18:38; Luke 23:4, 14–15, 22). Yet the crowd demands a conviction. A guilty man, Barabbas, is released, and the innocent one is condemned in his place. This is the gospel in a single exchange."] }, phaseLabel:'Phase III', phaseTitle:'The Roman Trial Before Pilate', events:[
|
||||
{
|
||||
title:'Jesus Brought Before Pilate',
|
||||
body:"The Jewish leaders bring Jesus to Pilate's headquarters. They do not enter so as not to be defiled before Passover. They accuse Jesus of misleading the nation, opposing taxes, and claiming to be King.",
|
||||
@@ -608,7 +1115,7 @@ const data = [
|
||||
]},
|
||||
|
||||
// ── PHASE 4: CRUCIFIXION ──────────────────────────────────────
|
||||
{ phase:'crucifixion', phaseLabel:'Phase IV', phaseTitle:'The Way of the Cross & Crucifixion', events:[
|
||||
{ phase:'crucifixion', court:{ head:"The Sentence Is Carried Out", text:["The sentence is passed and carried out. Everything the law demanded for sin, the penalty our verdict deserved, falls on the Substitute. Hear the words he cries at the end: “It is finished.” The debt is paid in full."] }, phaseLabel:'Phase IV', phaseTitle:'The Way of the Cross & Crucifixion', events:[
|
||||
{
|
||||
title:'Jesus Carries His Cross',
|
||||
body:'Jesus goes out carrying his own cross to the place called Golgotha, "The Place of a Skull."',
|
||||
@@ -765,7 +1272,7 @@ const data = [
|
||||
]},
|
||||
|
||||
// ── PHASE 5: BURIAL ──────────────────────────────────────────
|
||||
{ phase:'burial', phaseLabel:'Phase V', phaseTitle:'The Burial of Jesus', events:[
|
||||
{ phase:'burial', court:{ head:"The Sentence Served, the Case Sealed", text:["The death is certified, the body is claimed, and the tomb is sealed and guarded. The court records show that the sentence has been served in full. But the case is not over."] }, phaseLabel:'Phase V', phaseTitle:'The Burial of Jesus', events:[
|
||||
{
|
||||
title:'Joseph of Arimathea Requests the Body',
|
||||
body:"Joseph of Arimathea, a respected council member and secret disciple, goes to Pilate and asks for the body of Jesus. Pilate confirms with the centurion that Jesus is dead and grants the request.",
|
||||
@@ -804,12 +1311,105 @@ const data = [
|
||||
bsbLinks:{ matt:BSB.matt(27) }
|
||||
},
|
||||
]},
|
||||
{ phase:'resurrection', court:{ head:'The Verdict Overturned', text:[
|
||||
"The court meets again at dawn, and what the world called the end becomes the evidence that settles everything. The tomb is empty, the stone is rolled away, and the risen Jesus appears alive to many witnesses. Even the cover story the authorities invent (Matthew 28:11–15) only confirms that the body was gone.",
|
||||
"This is the Father’s public ruling. Jesus “was delivered over to death for our trespasses and was raised to life for our justification” (Romans 4:25). The sentence he served in our place is accepted, death has no more claim on him, and everyone who trusts him receives the same verdict: forgiven, and declared righteous."
|
||||
] }, phaseLabel:'Phase VI', phaseTitle:'The Resurrection of Jesus', events:[
|
||||
{
|
||||
title:'The Women Come to the Tomb at Dawn',
|
||||
body:'Very early on the first day of the week, Mary Magdalene and the other women come to the tomb with spices and find the great stone already rolled away from the entrance.',
|
||||
gospels:['matt','mark','luke','john'],
|
||||
refs:'Matt 28:1–4 · Mark 16:1–4 · Luke 24:1–2 · John 20:1',
|
||||
unique:'Matthew alone records the great earthquake and the angel of the Lord rolling back the stone, so that the guards shook with fear and became like dead men (Matt 28:2–4).',
|
||||
bsbLinks:{ matt:BSB.matt(28), mark:BSB.mark(16), luke:BSB.luke(24), john:BSB.john(20) }
|
||||
},
|
||||
{
|
||||
title:'The Angels Announce: "He Has Risen"',
|
||||
body:'An angel tells the frightened women, "He is not here; He has risen, just as He said." They are invited to see the place where he lay and sent to tell his disciples.',
|
||||
gospels:['matt','mark','luke'],
|
||||
refs:'Matt 28:5–8 · Mark 16:5–8 · Luke 24:3–8',
|
||||
unique:'Luke records two men in dazzling clothes who remind the women that Jesus had told them he would rise on the third day (Luke 24:4–8).',
|
||||
bsbLinks:{ matt:BSB.matt(28), mark:BSB.mark(16), luke:BSB.luke(24) }
|
||||
},
|
||||
{
|
||||
title:'Peter and John Run to the Tomb',
|
||||
body:'Mary Magdalene tells Peter and John that the body is gone. They run to the tomb and find the linen cloths lying there, and the face cloth folded up by itself.',
|
||||
gospels:['luke','john'],
|
||||
refs:'Luke 24:12 · John 20:2–10',
|
||||
unique:'John alone records that the other disciple outran Peter, went in, and "saw and believed" (John 20:8).',
|
||||
bsbLinks:{ luke:BSB.luke(24), john:BSB.john(20) }
|
||||
},
|
||||
{
|
||||
title:'Jesus Appears to Mary Magdalene (John)',
|
||||
body:'Weeping outside the tomb, Mary meets a man she takes to be the gardener. When he speaks her name, she recognizes her risen Lord and is sent to tell the disciples, "I have seen the Lord."',
|
||||
gospels:['john'],
|
||||
refs:'John 20:11–18',
|
||||
unique:'Unique to John — the first person to see the risen Jesus recognizes him only when he says her name.',
|
||||
bsbLinks:{ john:BSB.john(20) }
|
||||
},
|
||||
{
|
||||
title:'Jesus Meets the Women on the Road (Matthew)',
|
||||
body:'As the women hurry to tell the disciples, Jesus meets them. They take hold of his feet and worship him, and he tells them not to be afraid and to send word to his brothers to go to Galilee.',
|
||||
gospels:['matt'],
|
||||
refs:'Matt 28:9–10',
|
||||
bsbLinks:{ matt:BSB.matt(28) }
|
||||
},
|
||||
{
|
||||
title:"The Guards' Report & the Bribe (Matthew)",
|
||||
body:'Some of the guards report everything to the chief priests. The elders give the soldiers a large sum of money and tell them to say the disciples stole the body while they slept.',
|
||||
gospels:['matt'],
|
||||
refs:'Matt 28:11–15',
|
||||
unique:'Unique to Matthew — the authorities tamper with the evidence, and Matthew notes the story was still being told "to this day."',
|
||||
bsbLinks:{ matt:BSB.matt(28) }
|
||||
},
|
||||
{
|
||||
title:'The Road to Emmaus (Luke)',
|
||||
body:'Two disciples walking to Emmaus are joined by Jesus, whom they do not recognize. He explains from Moses and all the Prophets that the Christ had to suffer and enter his glory. They know him when he breaks the bread, and they hurry back to Jerusalem.',
|
||||
gospels:['luke'],
|
||||
refs:'Luke 24:13–35',
|
||||
unique:'Unique to Luke — Jesus shows from all the Scriptures that the cross and the resurrection were the plan all along.',
|
||||
bsbLinks:{ luke:BSB.luke(24) }
|
||||
},
|
||||
{
|
||||
title:'Jesus Appears to the Disciples',
|
||||
body:'That evening Jesus stands among the gathered disciples: "Peace be with you." He shows them his hands and side, and they rejoice when they see the Lord.',
|
||||
gospels:['luke','john'],
|
||||
refs:'Luke 24:36–49 · John 20:19–23',
|
||||
unique:'Luke records Jesus eating a piece of broiled fish to show he is no ghost (Luke 24:39–43). John records him breathing on them: "Receive the Holy Spirit" (John 20:22).',
|
||||
bsbLinks:{ luke:BSB.luke(24), john:BSB.john(20) }
|
||||
},
|
||||
{
|
||||
title:'Thomas Sees and Believes (John)',
|
||||
body:'Thomas, who was absent, says he will not believe unless he touches the wounds. A week later Jesus appears again and invites him to touch, and Thomas answers, "My Lord and my God!"',
|
||||
gospels:['john'],
|
||||
refs:'John 20:24–29',
|
||||
unique:'Jesus adds, "Blessed are those who have not seen and yet believe" (John 20:29).',
|
||||
bsbLinks:{ john:BSB.john(20) }
|
||||
},
|
||||
{
|
||||
title:'The Great Commission',
|
||||
body:'Jesus declares that all authority in heaven and on earth has been given to him, and sends his disciples to make disciples of all nations, teaching them to obey all he has commanded. Acts adds his promise that they will receive power when the Holy Spirit comes on them and will be his witnesses in Jerusalem, in all Judea and Samaria, and to the ends of the earth.',
|
||||
gospels:['matt','luke','acts'],
|
||||
refs:'Matt 28:16–20 · Luke 24:44–49 · Acts 1:4–8',
|
||||
unique:'Luke records that repentance and forgiveness of sins are to be proclaimed in his name to all nations (Luke 24:46–47). Acts 1:8 gives the plan: Jerusalem, Judea and Samaria, and the ends of the earth.',
|
||||
bsbLinks:{ matt:BSB.matt(28), luke:BSB.luke(24), acts:BSB.acts(1) }
|
||||
},
|
||||
{
|
||||
title:'The Ascension (Luke & Acts)',
|
||||
body:'Jesus leads his disciples out to Bethany, lifts his hands in blessing, and is taken up into heaven. They worship him and return to Jerusalem with great joy. Acts adds that he had appeared to them over forty days, and that as they watched, a cloud took him out of their sight.',
|
||||
gospels:['luke','acts'],
|
||||
refs:'Luke 24:50–53 · Acts 1:1–11',
|
||||
unique:'Luke’s second volume, Acts, repeats and expands the account: forty days of appearances, the promise of the Holy Spirit and power to be his witnesses, and the promise of two men in white that Jesus will return in the same way he went (Acts 1:3–11).',
|
||||
bsbLinks:{ luke:BSB.luke(24), acts:BSB.acts(1) }
|
||||
},
|
||||
]},
|
||||
];
|
||||
|
||||
function buildTimeline() {
|
||||
const container = document.getElementById('timeline');
|
||||
container.innerHTML = '';
|
||||
const colorMap = { matt:'var(--matt-color)', mark:'var(--mark-color)', luke:'var(--luke-color)', john:'var(--john-color)' };
|
||||
const colorMap = { matt:'var(--matt-color)', mark:'var(--mark-color)', luke:'var(--luke-color)', john:'var(--john-color)', acts:'var(--acts-color)' };
|
||||
let exhibitNo = 0;
|
||||
|
||||
data.forEach(phase => {
|
||||
const phHeader = document.createElement('div');
|
||||
@@ -818,6 +1418,14 @@ function buildTimeline() {
|
||||
phHeader.innerHTML = `<div class="phase-node"></div><span class="phase-label">${phase.phaseLabel}</span><span class="phase-title">${phase.phaseTitle}</span>`;
|
||||
container.appendChild(phHeader);
|
||||
|
||||
if (phase.court) {
|
||||
const note = document.createElement('div');
|
||||
note.className = 'court-note';
|
||||
note.setAttribute('data-phase', phase.phase);
|
||||
note.innerHTML = `<div class="court-note-head">The Court Record · ${phase.court.head}</div>` + phase.court.text.map(t => `<p>${t}</p>`).join('');
|
||||
container.appendChild(note);
|
||||
}
|
||||
|
||||
phase.events.forEach(ev => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'event-card';
|
||||
@@ -838,7 +1446,9 @@ function buildTimeline() {
|
||||
|
||||
const uniqueNote = ev.unique ? `<div class="unique-note">✦ ${ev.unique}</div>` : '';
|
||||
|
||||
exhibitNo += 1;
|
||||
card.innerHTML = `
|
||||
<span class="exhibit">Exhibit ${exhibitNo}</span>
|
||||
<div class="event-title">${ev.title}</div>
|
||||
<div class="event-body">${ev.body}</div>
|
||||
<div class="gospel-badges">${badges}</div>
|
||||
@@ -852,6 +1462,16 @@ function buildTimeline() {
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
// After a filter shortens the page, the browser would leave the reader at the bottom (on the
|
||||
// plea and footer). Bring them back to the top of the results instead.
|
||||
function resetScrollToResults() {
|
||||
const timeline = document.getElementById('timeline');
|
||||
const bar = document.querySelector('.filter-bar');
|
||||
const barH = document.querySelector('.topbar').offsetHeight + document.querySelector('.tabbar').offsetHeight + (bar ? bar.offsetHeight : 0);
|
||||
const top = timeline.getBoundingClientRect().top + window.scrollY - barH - 12;
|
||||
if (window.scrollY > top) window.scrollTo({ top: Math.max(top, 0), behavior: 'auto' });
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
document.querySelectorAll('.event-card').forEach(card => {
|
||||
const cPhase = card.getAttribute('data-phase');
|
||||
@@ -860,7 +1480,7 @@ function applyFilters() {
|
||||
const gospelOk = cGospels.some(g => activeGospels.has(g));
|
||||
card.classList.toggle('hidden', !phaseOk || !gospelOk);
|
||||
});
|
||||
document.querySelectorAll('.phase-header').forEach(h => {
|
||||
document.querySelectorAll('.phase-header, .court-note').forEach(h => {
|
||||
const hPhase = h.getAttribute('data-phase');
|
||||
h.style.display = (activePhase === 'all' || hPhase === activePhase) ? '' : 'none';
|
||||
});
|
||||
@@ -876,6 +1496,7 @@ function toggleGospel(gospel, el) {
|
||||
el.classList.remove('dimmed');
|
||||
}
|
||||
applyFilters();
|
||||
resetScrollToResults();
|
||||
}
|
||||
|
||||
function filterPhase(phase, btn) {
|
||||
@@ -891,9 +1512,125 @@ function filterPhase(phase, btn) {
|
||||
btn.style.color = 'var(--ink)';
|
||||
btn.style.borderColor = 'var(--gold)';
|
||||
applyFilters();
|
||||
resetScrollToResults();
|
||||
}
|
||||
|
||||
buildTimeline();
|
||||
|
||||
// ── Tabs, navigation, plea ───────────────────────────────────
|
||||
(function () {
|
||||
const tabs = { case: document.getElementById('panel-case'), record: document.getElementById('panel-record') };
|
||||
const tabBtns = document.querySelectorAll('.tabbar [data-tab]');
|
||||
const recordIds = new Set(['record', 'record-h']);
|
||||
|
||||
function showTab(name) {
|
||||
Object.keys(tabs).forEach(function (k) { tabs[k].hidden = (k !== name); });
|
||||
tabBtns.forEach(function (b) { b.setAttribute('aria-selected', b.getAttribute('data-tab') === name ? 'true' : 'false'); });
|
||||
document.querySelectorAll('[data-spy="good-news"]').forEach(function (a) { a.classList.toggle('active', name === 'case'); });
|
||||
document.querySelectorAll('[data-spy="record"]').forEach(function (a) { a.classList.toggle('active', name === 'record'); });
|
||||
}
|
||||
|
||||
function route(scroll) {
|
||||
const id = decodeURIComponent(location.hash.slice(1));
|
||||
const name = recordIds.has(id) || (id && tabs.record.querySelector('#' + CSS.escape(id))) ? 'record' : 'case';
|
||||
showTab(name);
|
||||
const target = id ? document.getElementById(id) : null;
|
||||
if (scroll) {
|
||||
if (target) target.scrollIntoView({ block: 'start' });
|
||||
else window.scrollTo({ top: document.querySelector('.tabbar').offsetTop - document.querySelector('.topbar').offsetHeight, behavior: 'auto' });
|
||||
}
|
||||
}
|
||||
|
||||
tabBtns.forEach(function (b) {
|
||||
b.addEventListener('click', function () {
|
||||
const name = b.getAttribute('data-tab');
|
||||
history.pushState(null, '', name === 'record' ? '#record' : '#good-news');
|
||||
route(true);
|
||||
});
|
||||
});
|
||||
window.addEventListener('hashchange', function () { route(true); });
|
||||
route(location.hash.length > 1);
|
||||
|
||||
// back to top + share
|
||||
const toTop = document.getElementById('to-top');
|
||||
window.addEventListener('scroll', function () { toTop.classList.toggle('show', window.scrollY > 600); }, { passive: true });
|
||||
toTop.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); });
|
||||
|
||||
const share = document.getElementById('btn-share');
|
||||
const note = document.getElementById('share-note');
|
||||
share.addEventListener('click', async function () {
|
||||
const url = location.origin + '/salvation';
|
||||
const data = { title: 'The Gospel, in a Courtroom', text: 'The good news of Jesus, presented as a courtroom case.', url: url };
|
||||
try {
|
||||
if (navigator.share) { await navigator.share(data); return; }
|
||||
await navigator.clipboard.writeText(url);
|
||||
note.textContent = 'Link copied. Thank you for sharing the good news.';
|
||||
} catch (e) {
|
||||
note.textContent = 'You can copy the address from your browser to share this page.';
|
||||
}
|
||||
});
|
||||
|
||||
// ── The plea ──
|
||||
const select = document.getElementById('plea-select');
|
||||
const guilty = document.getElementById('plea-guilty');
|
||||
const notGuilty = document.getElementById('plea-notguilty');
|
||||
const cardG = document.getElementById('card-guilty');
|
||||
const cardN = document.getElementById('card-notguilty');
|
||||
function choose(value) {
|
||||
guilty.hidden = value !== 'guilty';
|
||||
notGuilty.hidden = value !== 'not-guilty';
|
||||
cardG.classList.toggle('is-selected', value === 'guilty');
|
||||
cardN.classList.toggle('is-selected', value === 'not-guilty');
|
||||
cardG.classList.toggle('is-dim', value === 'not-guilty');
|
||||
cardN.classList.toggle('is-dim', value === 'guilty');
|
||||
const panel = value === 'guilty' ? guilty : value === 'not-guilty' ? notGuilty : null;
|
||||
if (panel) panel.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
select.addEventListener('change', function () { choose(select.value); });
|
||||
document.getElementById('btn-change-plea').addEventListener('click', function () { select.value = 'guilty'; choose('guilty'); });
|
||||
|
||||
// ── Let us know (guilty only) ──
|
||||
const pray = document.getElementById('btn-pray');
|
||||
const form = document.getElementById('respond-form');
|
||||
const err = document.getElementById('r-error');
|
||||
const send = document.getElementById('btn-send');
|
||||
const intro = document.getElementById('respond-intro');
|
||||
const done = document.getElementById('respond-done');
|
||||
pray.addEventListener('click', function () {
|
||||
form.classList.add('open');
|
||||
pray.setAttribute('aria-expanded', 'true');
|
||||
pray.style.display = 'none';
|
||||
document.getElementById('r-name').focus();
|
||||
});
|
||||
document.getElementById('btn-cancel').addEventListener('click', function () {
|
||||
form.classList.remove('open');
|
||||
pray.style.display = '';
|
||||
pray.setAttribute('aria-expanded', 'false');
|
||||
pray.focus();
|
||||
});
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
err.textContent = '';
|
||||
send.disabled = true;
|
||||
send.textContent = 'Sending…';
|
||||
try {
|
||||
const res = await fetch('/api/salvation-response', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: form.name.value, email: form.email.value, message: form.message.value, _honey: form._honey.value })
|
||||
});
|
||||
const data = await res.json().catch(function () { return {}; });
|
||||
if (!res.ok) throw new Error(data.message || 'We could not send this right now.');
|
||||
intro.style.display = 'none';
|
||||
done.classList.add('open');
|
||||
done.focus();
|
||||
} catch (ex) {
|
||||
err.textContent = (ex && ex.message ? ex.message : 'We could not send this right now.') + ' Your decision is between you and God either way, and you are welcome to write to us from the contact page.';
|
||||
send.disabled = false;
|
||||
send.textContent = 'Let us know';
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -76,6 +76,7 @@ import { register as registerFeed } from './server/routes/feed.js'
|
||||
import { register as registerQrCodes } from './server/routes/qr-codes.js'
|
||||
import { register as registerBiblePassage } from './server/routes/bible-passage.js'
|
||||
import { register as registerNewsletter } from './server/routes/newsletter.js'
|
||||
import { register as registerSalvation } from './server/routes/salvation.js'
|
||||
import { register as registerPublic } from './server/routes/public.js'
|
||||
|
||||
preferIpv4ForSeafile()
|
||||
@@ -136,6 +137,7 @@ registerFeed(app)
|
||||
registerQrCodes(app)
|
||||
registerBiblePassage(app)
|
||||
registerNewsletter(app)
|
||||
registerSalvation(app)
|
||||
|
||||
// Hit-counting middleware (must come before public routes)
|
||||
app.use((req, res, next) => {
|
||||
|
||||
+1
-1
@@ -1166,7 +1166,7 @@ export async function restoreFromBackup(filename) {
|
||||
// (These are defined here rather than study-helpers to avoid circular imports)
|
||||
|
||||
export function normalizeMessageType(value) {
|
||||
if (value === 'question' || value === 'testimony' || value === 'topic') return value
|
||||
if (value === 'question' || value === 'testimony' || value === 'topic' || value === 'gospel' || value === 'community') return value
|
||||
return 'general'
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export function register(app) {
|
||||
const root = canonical.endsWith('/') ? canonical.slice(0, -1) : canonical
|
||||
|
||||
res.type('text/plain').send(
|
||||
['User-agent: *', 'Allow: /', 'Disallow: /salvation', `Sitemap: ${root}/sitemap.xml`].join('\n'),
|
||||
['User-agent: *', 'Allow: /', `Sitemap: ${root}/sitemap.xml`].join('\n'),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -111,6 +111,8 @@ export function register(app) {
|
||||
for (const ep of highlights) {
|
||||
if (ep.id) urls.push(`${root}/episodes/${encodeURIComponent(ep.id)}`)
|
||||
}
|
||||
// The standalone gospel page
|
||||
if (!urls.includes(`${root}/salvation`)) urls.push(`${root}/salvation`)
|
||||
// Newsletter archive + published issues
|
||||
urls.push(`${root}/newsletter`)
|
||||
for (const issue of state.newsletterIssues) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Resend } from 'resend'
|
||||
import { escapeHtml } from '../helpers.js'
|
||||
import { getResendFromAddress, getResendInboxAddress, sendResendEmailWithRetry } from '../email.js'
|
||||
|
||||
// A visitor who finished the /salvation page can tell us they prayed to receive Christ.
|
||||
// Nothing is stored on the server: the response is emailed once to the site inbox and that is all.
|
||||
const hits = new Map()
|
||||
|
||||
function rateLimit(req, res, next) {
|
||||
const ip = req.ip ?? 'unknown'
|
||||
const now = Date.now()
|
||||
const windowMs = 10 * 60 * 1000
|
||||
const entry = hits.get(ip) ?? { count: 0, start: now }
|
||||
if (now - entry.start > windowMs) { entry.count = 0; entry.start = now }
|
||||
entry.count += 1
|
||||
hits.set(ip, entry)
|
||||
if (hits.size > 2000) {
|
||||
for (const [k, v] of hits) { if (now - v.start > windowMs) hits.delete(k) }
|
||||
}
|
||||
if (entry.count > 3) {
|
||||
res.status(429).json({ message: 'Please wait a few minutes before trying again.' })
|
||||
return
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
function clean(value, max) {
|
||||
return typeof value === 'string' ? value.trim().replace(/\s+/g, ' ').slice(0, max) : ''
|
||||
}
|
||||
|
||||
export function register(app) {
|
||||
app.post('/api/salvation-response', rateLimit, async (req, res) => {
|
||||
const { name, email, message, _honey } = req.body ?? {}
|
||||
if (_honey) { res.json({ ok: true }); return }
|
||||
|
||||
const safeName = clean(name, 100)
|
||||
const safeEmail = clean(email, 200)
|
||||
const safeMessage = typeof message === 'string' ? message.trim().slice(0, 2000) : ''
|
||||
if (safeEmail && !/^[a-zA-Z0-9._%+\-]{1,64}@[a-zA-Z0-9.\-]{1,253}\.[a-zA-Z]{2,}$/.test(safeEmail)) {
|
||||
res.status(400).json({ message: 'That email address does not look right. You can also leave it blank.' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!process.env.RESEND_API_KEY) {
|
||||
console.error('[salvation] RESEND_API_KEY env var not set')
|
||||
res.status(503).json({ message: 'We could not send this right now.' })
|
||||
return
|
||||
}
|
||||
|
||||
const when = new Date().toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' })
|
||||
const nameLine = safeName || 'Name not given'
|
||||
const emailLine = safeEmail || 'Email not given'
|
||||
const text = [
|
||||
'Someone responded to the gospel on the /salvation page (the courtroom presentation) and says they pleaded guilty, accepted the pardon, and prayed to receive Jesus as Savior.',
|
||||
'',
|
||||
`Name: ${nameLine}`,
|
||||
`Email: ${emailLine}`,
|
||||
`When: ${when}`,
|
||||
'',
|
||||
safeMessage ? `Their message:\n${safeMessage}` : 'They did not leave a message.',
|
||||
'',
|
||||
safeEmail ? 'You can reply directly to this email to reach them.' : 'They did not leave an email address, so there is no way to reply.',
|
||||
].join('\n')
|
||||
const html = `<div style="font-family:Georgia,serif;max-width:560px;line-height:1.6;color:#1a1208">
|
||||
<p><strong>Someone responded to the gospel</strong> on the /salvation page and says they prayed to receive Jesus as Savior.</p>
|
||||
<p><strong>Name:</strong> ${escapeHtml(nameLine)}<br><strong>Email:</strong> ${escapeHtml(emailLine)}<br><strong>When:</strong> ${escapeHtml(when)}</p>
|
||||
${safeMessage ? `<p><strong>Their message:</strong></p><blockquote style="margin:0;padding-left:12px;border-left:3px solid #b8860b">${escapeHtml(safeMessage).replace(/\n/g, '<br>')}</blockquote>` : '<p><em>They did not leave a message.</em></p>'}
|
||||
<p style="color:#6b5a30">${safeEmail ? 'You can reply directly to this email to reach them.' : 'They did not leave an email address, so there is no way to reply.'}</p>
|
||||
</div>`
|
||||
|
||||
try {
|
||||
const resend = new Resend(process.env.RESEND_API_KEY)
|
||||
await sendResendEmailWithRetry({
|
||||
resend,
|
||||
context: 'salvation-response',
|
||||
payload: {
|
||||
from: getResendFromAddress(),
|
||||
to: [getResendInboxAddress()],
|
||||
...(safeEmail ? { replyTo: safeEmail } : {}),
|
||||
subject: 'Someone prayed to receive Christ on your salvation page',
|
||||
tags: [{ name: 'flow', value: 'salvation-response' }],
|
||||
text,
|
||||
html,
|
||||
},
|
||||
})
|
||||
res.json({ ok: true })
|
||||
} catch (err) {
|
||||
console.error('[salvation] email failed:', err?.message ?? err)
|
||||
res.status(502).json({ message: 'We could not send this right now.' })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -14150,3 +14150,10 @@ a.sl-pager-link:hover { border-color: rgba(201, 168, 76, 0.6); transform: transl
|
||||
#root .ed-side .ed-side-heading { font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; color: #c9a84c; margin: 0 0 0.8rem; font-family: inherit; font-weight: 600; line-height: 1.2; }
|
||||
.auth-card--center .study-signup-form { text-align: left; }
|
||||
.cp-main--bare { min-height: 100vh; align-items: center; }
|
||||
|
||||
/* ── Start Here: gospel callout ── */
|
||||
.start-gospel { max-width: 760px; margin: 2.5rem auto 0; padding: 1.75rem 1.9rem; text-align: center; background: linear-gradient(145deg, #17170f, #0c0c08); border: 1px solid rgba(201, 168, 76, 0.35); border-radius: 16px; }
|
||||
.start-gospel h2 { font-family: var(--brand-font-heading); font-size: 1.7rem; color: #f0e9cc; margin: 0.3rem 0 0.7rem; }
|
||||
.start-gospel p { color: #a89a6a; line-height: 1.7; margin: 0 auto 1.2rem; max-width: 56ch; }
|
||||
.start-gospel .eyebrow { margin-bottom: 0; color: #c9a84c; }
|
||||
.header-nav-link { white-space: nowrap; }
|
||||
+11
@@ -503,6 +503,8 @@ function SiteHeader({ content }: { content: SiteContent }) {
|
||||
</div>
|
||||
</div>
|
||||
<NavLink to="/resources" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Resources</NavLink>
|
||||
{/* /salvation is a standalone static page, so it needs a real page load rather than a router link */}
|
||||
<a href="/salvation" className="header-nav-link" onClick={() => setMenuOpen(false)}>The Gospel</a>
|
||||
<NavLink to="/about" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>About</NavLink>
|
||||
<NavLink to="/contact" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Contact</NavLink>
|
||||
<a
|
||||
@@ -577,6 +579,8 @@ function SiteFooter({ content }: { content: SiteContent }) {
|
||||
)}
|
||||
</nav>
|
||||
<nav className="footer-links footer-links--legal" aria-label="Legal links">
|
||||
<a href="/salvation">The Gospel</a>
|
||||
<span aria-hidden="true">·</span>
|
||||
<Link to="/finished">Finished Books</Link>
|
||||
<span aria-hidden="true">·</span>
|
||||
<Link to="/subscribe">Newsletter</Link>
|
||||
@@ -3065,6 +3069,13 @@ function StartHerePage({ content }: { content: SiteContent }) {
|
||||
})}
|
||||
</ol>
|
||||
|
||||
<aside className="start-gospel" aria-labelledby="start-gospel-h">
|
||||
<p className="eyebrow">Never heard the gospel, or not sure where you stand with God?</p>
|
||||
<h2 id="start-gospel-h">The Gospel, in a Courtroom</h2>
|
||||
<p>God is the Judge, Satan is the accuser, the charges are our sins, and a Substitute has stepped forward. Follow the case from the charge to the pardon, then read the full account of the trial, crucifixion and resurrection of Jesus.</p>
|
||||
<a href="/salvation" className="btn-primary">Hear the case <span aria-hidden="true">→</span></a>
|
||||
</aside>
|
||||
|
||||
<p className="start-back"><Link to="/">← Back to site</Link></p>
|
||||
</main>
|
||||
<SiteFooter content={content} />
|
||||
|
||||
@@ -28,6 +28,7 @@ const TYPE_LABEL: Record<string, string> = {
|
||||
community: 'Community Question',
|
||||
testimony: 'Testimony',
|
||||
topic: 'Topic Request',
|
||||
gospel: 'Gospel Courtroom Question',
|
||||
general: 'General Message',
|
||||
}
|
||||
const KIND_LABEL: Record<Kind, string> = { email: 'Email', newsletter: 'Newsletter signup', download: 'Download request', message: 'Contact form' }
|
||||
|
||||
@@ -15,6 +15,7 @@ interface ContactFields {
|
||||
|
||||
const TYPES: { value: string; label: string; hint: string; placeholder: string }[] = [
|
||||
{ value: 'question', label: 'Bible question', hint: 'Ask anything about Scripture. Answers are posted on the Q&A page.', placeholder: 'What would you like to know?' },
|
||||
{ value: 'gospel', label: 'Gospel courtroom', hint: 'A question about the gospel courtroom: the Judge, the accuser, the charges, the Substitute, the pardon or your plea. It goes to Nate privately and is not posted publicly.', placeholder: 'What would you like to ask about the courtroom, the verdict or the pardon?' },
|
||||
{ value: 'community', label: 'Community question', hint: 'A question about a study discussion or the community.', placeholder: 'What’s on your mind?' },
|
||||
{ value: 'testimony', label: 'Testimony', hint: 'Share what God is doing in your life. Every one is read.', placeholder: 'Tell your story…' },
|
||||
{ value: 'topic', label: 'Topic request', hint: 'Suggest a passage or topic for a future episode.', placeholder: 'Which passage or topic?' },
|
||||
@@ -32,7 +33,7 @@ export default function ContactForm() {
|
||||
lastName: '',
|
||||
email: '',
|
||||
message: '',
|
||||
messageType: source === 'community' ? 'community' : 'question',
|
||||
messageType: source === 'community' ? 'community' : source === 'salvation' ? 'gospel' : 'question',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+2
-2
@@ -25,10 +25,10 @@ export default defineConfig({
|
||||
navigateFallback: '/index.html',
|
||||
// Allowlist-only approach: SW only serves index.html for known SPA routes.
|
||||
// Everything else (admin-configured redirects, /spotify, /apple, /amazon,
|
||||
// feed.xml, robots.txt, etc.) goes to the server so server-side redirects work.
|
||||
// feed.xml, robots.txt, the static /salvation page, etc.) goes to the server so server-side redirects work.
|
||||
navigateFallbackAllowlist: [
|
||||
/^\/$/, // exact root
|
||||
/^\/(start-here|about|contact|resources|thanks|preview|privacy|terms|salvation)(\/.*)?$/,
|
||||
/^\/(start-here|about|contact|resources|thanks|preview|privacy|terms)(\/.*)?$/,
|
||||
/^\/(study|studys|episodes|finished|downloads|subscribe|certificate)(\/.*)?$/,
|
||||
/^\/(questions|admin|email|contacts|calendar)(\/.*)?$/,
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user