Bunch more stability fixes

This commit is contained in:
2026-03-23 22:12:24 -06:00
parent a56f8446f7
commit 63e05adcee
268 changed files with 20626 additions and 2260 deletions

View File

@@ -386,6 +386,29 @@
box-shadow: 0 4px 16px rgba(111, 66, 193, 0.4);
}
.btn-demo {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1.5rem;
background: transparent;
color: var(--primary-light);
font-weight: 600;
font-size: 0.9rem;
border-radius: 8px;
border: 1px solid var(--primary-light);
cursor: pointer;
transition: all var(--transition);
text-decoration: none;
}
.btn-demo:hover {
background: rgba(139, 92, 246, 0.1);
color: var(--primary-light);
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(111, 66, 193, 0.2);
}
.btn-secondary {
display: inline-flex;
align-items: center;
@@ -1167,6 +1190,141 @@
color: var(--text-muted);
}
/* ============================================
FEATURE NODE — HOVER SCREENSHOT PREVIEW
============================================ */
.feature-node[data-screenshot] {
cursor: pointer;
}
.node-screenshot {
max-height: 0;
overflow: hidden;
margin: 0 -1.5rem;
border-radius: 0;
transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1), margin-top 0.35s ease, margin-bottom 0.35s ease;
margin-top: 0;
margin-bottom: 0;
}
.feature-node:hover .node-screenshot {
max-height: 300px;
margin-top: 0.75rem;
margin-bottom: 0.5rem;
}
.node-screenshot img {
width: 100%;
height: auto;
display: block;
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
}
.node-screenshot-label {
padding: 0.3rem 1.5rem;
font-size: 0.7rem;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 0.4rem;
background: rgba(0,0,0,0.15);
}
[data-theme="light"] .node-screenshot-label {
background: rgba(0,0,0,0.04);
}
.node-screenshot-label .screenshot-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--success);
flex-shrink: 0;
}
/* On mobile, hide hover screenshots (touch doesn't hover well) */
@media (max-width: 768px) {
.node-screenshot {
display: none;
}
}
/* ============================================
NARRATIVE BRIDGE SECTION
============================================ */
.narrative-bridge {
padding: 4rem 0 2rem;
background: transparent;
}
.narrative-content {
max-width: 720px;
margin: 0 auto;
text-align: center;
background: rgba(30, 41, 59, 0.85);
border: 1px solid rgba(148, 163, 184, 0.1);
border-radius: var(--radius-lg);
padding: 3rem 2.5rem;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
position: relative;
z-index: 2;
}
[data-theme="light"] .narrative-content {
background: rgba(255, 255, 255, 0.88);
border-color: rgba(100, 116, 139, 0.15);
}
.narrative-eyebrow {
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--primary-light);
margin-bottom: 1rem;
}
.narrative-content h2 {
font-size: clamp(1.5rem, 3vw, 2.25rem);
margin-bottom: 1.25rem;
line-height: 1.3;
}
.narrative-body {
color: var(--text-secondary);
font-size: 1.1rem;
line-height: 1.7;
}
/* In-feature narrative connectors */
.branch-narrative {
max-width: 640px;
margin: 2rem auto 3rem;
text-align: center;
padding: 1.5rem 2rem;
background: rgba(30, 41, 59, 0.85);
border: 1px solid rgba(148, 163, 184, 0.1);
border-radius: var(--radius-lg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
position: relative;
z-index: 2;
}
[data-theme="light"] .branch-narrative {
background: rgba(255, 255, 255, 0.45);
border-color: rgba(100, 116, 139, 0.1);
}
.branch-narrative p {
color: var(--text-secondary);
font-size: 1rem;
line-height: 1.7;
margin: 0;
}
/* ============================================
LIVE SITES
============================================ */
@@ -1915,6 +2073,7 @@
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<a href="https://app.cmlite.org" class="btn-demo" target="_blank" rel="noopener">Explore Demo</a>
<a href="#get-started" class="btn-primary">Get Started</a>
<button class="hamburger" id="hamburger" aria-label="Open menu" type="button">
<svg class="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
@@ -1935,7 +2094,8 @@
<a href="#pricing">Pricing</a>
<a href="/docs/">Documentation</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite" target="_blank" rel="noopener">Source Code</a>
<a href="#get-started" class="btn-primary" style="text-align:center; margin-top:1rem;">Get Started</a>
<a href="https://app.cmlite.org" class="btn-demo" style="text-align:center; margin-top:0.5rem;" target="_blank" rel="noopener">Explore Demo</a>
<a href="#get-started" class="btn-primary" style="text-align:center; margin-top:0.5rem;">Get Started</a>
</div>
<!-- ============================================
@@ -1964,8 +2124,8 @@
A <a href="#" class="free-asterisk" id="free-asterisk-link">free*</a> and open source toolkit built for growing political movements.
</p>
<div class="hero-cta">
<a href="mailto:cmlite@bnkops.ca?subject=Request%20to%20Chat%20-%20CMLITE&body=Hi%20CMlite%20Team%2C%20I%20would%20like%20to%20chat!%20Please%20send%20me%20a%20email%20back.%20Cheers%2C%20" class="btn-primary">Schedule a Chat <span aria-hidden="true">&rarr;</span></a>
<a href="/docs/" class="btn-secondary">Read the Docs</a>
<a href="https://app.cmlite.org" class="btn-primary" target="_blank" rel="noopener">Explore the Demo <span aria-hidden="true">&rarr;</span></a>
<a href="mailto:cmlite@bnkops.ca?subject=Request%20to%20Chat%20-%20CMLITE&body=Hi%20CMlite%20Team%2C%20I%20would%20like%20to%20chat!%20Please%20send%20me%20a%20email%20back.%20Cheers%2C%20" class="btn-secondary">Schedule a Chat</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/v2" class="btn-secondary" target="_blank" rel="noopener">Source Code</a>
</div>
@@ -2088,6 +2248,21 @@
</div>
</section>
<!-- ============================================
NARRATIVE BRIDGE — Journey Starts
============================================ -->
<section class="section narrative-bridge" id="journey">
<div class="container">
<div class="narrative-content reveal">
<p class="narrative-eyebrow">The Journey</p>
<h2>What if your entire campaign ran from one place you actually own?</h2>
<p class="narrative-body">
Imagine a new volunteer walks up to your campaign office. Within minutes, they&rsquo;re in your team chat, signed up for a canvassing shift, and synced across your map, newsletter, and event calendar &mdash; <strong>without creating accounts on five different platforms</strong>. That&rsquo;s what an integrated, self-hosted stack makes possible. Below is everything you get from day one.
</p>
</div>
</div>
</section>
<!-- ============================================
FEATURE NETWORK
============================================ -->
@@ -2112,7 +2287,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Public Campaigns — postal code lookup to find representatives and send advocacy emails</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-listmonk">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-listmonk.png" alt="Listmonk newsletter admin" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Newsletter sync dashboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4EC;</div>
<h4>Listmonk Newsletters</h4>
@@ -2120,7 +2296,8 @@
<p>Full newsletter platform with subscriber management, templates, and analytics. Drop-in replacement for Mailchimp.</p>
<div class="node-tags"><span class="node-tag">Unlimited subscribers</span><span class="node-tag">Templates</span><span class="node-tag">Analytics</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="influence-campaigns">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/influence-campaigns.png" alt="Advocacy campaign management" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Campaign management with tracking</div></div>
<div class="node-header">
<div class="node-icon">&#x1F3AF;</div>
<h4>Influence Campaigns</h4>
@@ -2128,7 +2305,8 @@
<p>Postal code to representative lookup. Automated advocacy emails to elected officials with tracking and response collection.</p>
<div class="node-tags"><span class="node-tag">Rep lookup</span><span class="node-tag">BullMQ queue</span><span class="node-tag">Tracking</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="email-templates">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/email-templates.png" alt="Email template editor" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Visual email template builder</div></div>
<div class="node-header">
<div class="node-icon">&#x2709;</div>
<h4>Email Templates</h4>
@@ -2136,7 +2314,8 @@
<p>GrapesJS visual email editor with variable substitution, versioning, and instant preview. Build once, send everywhere.</p>
<div class="node-tags"><span class="node-tag">Visual editor</span><span class="node-tag">Variables</span><span class="node-tag">Versioning</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-campaigns">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-campaigns.png" alt="Public response wall" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public campaign page with rep lookup</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4AC;</div>
<h4>Response Wall</h4>
@@ -2144,7 +2323,8 @@
<p>Public response collection with moderation, upvoting, and verification. Showcase supporter voices on your campaigns.</p>
<div class="node-tags"><span class="node-tag">Moderation</span><span class="node-tag">Upvoting</span><span class="node-tag">Verification</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-dashboard.png" alt="Admin dashboard with chat widget" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Team chat integrated into admin</div></div>
<div class="node-header">
<div class="node-icon">&#x1F680;</div>
<h4>Rocket.Chat</h4>
@@ -2152,7 +2332,8 @@
<p>Self-hosted team chat with SSO integration. Automatic channel notifications for shift signups, canvass sessions, and campaign responses.</p>
<div class="node-tags"><span class="node-tag">SSO</span><span class="node-tag">Channels</span><span class="node-tag">Slack alternative</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="email-queue">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/email-queue.png" alt="Email queue dashboard" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Async email queue with status tracking</div></div>
<div class="node-header">
<div class="node-icon">&#x1F514;</div>
<h4>Smart Notifications</h4>
@@ -2160,7 +2341,8 @@
<p>Async notification queue for admin alerts and volunteer feedback. Shift reminders, session summaries, and signup confirmations.</p>
<div class="node-tags"><span class="node-tag">BullMQ</span><span class="node-tag">Reminders</span><span class="node-tag">Summaries</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="sms-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/sms-dashboard.png" alt="SMS campaign dashboard" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> SMS outreach via Termux bridge</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F1;</div>
<h4>SMS Campaigns</h4>
@@ -2168,7 +2350,8 @@
<p>Text message outreach via Termux Android bridge. Contact lists, templated campaigns, delivery tracking, response sync, and device health monitoring.</p>
<div class="node-tags"><span class="node-tag">Termux bridge</span><span class="node-tag">BullMQ queue</span><span class="node-tag">Response sync</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-dashboard.png" alt="Admin with floating chat widget" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Floating chat panel in admin</div></div>
<div class="node-header">
<div class="node-icon">&#x1F5E8;</div>
<h4>Chat Widget</h4>
@@ -2193,7 +2376,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Public Map — interactive Leaflet map with territory cuts, marker clustering, and support levels</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="public-map">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-map.png" alt="Interactive Leaflet map" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Full-screen map with cuts and support levels</div></div>
<div class="node-header">
<div class="node-icon">&#x1F30D;</div>
<h4>Interactive Map</h4>
@@ -2201,7 +2385,8 @@
<p>Leaflet-powered map with multi-provider geocoding, color-coded markers, cuts overlay, and fullscreen mode.</p>
<div class="node-tags"><span class="node-tag">6 geocode providers</span><span class="node-tag">Leaflet</span><span class="node-tag">Clustering</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="volunteer-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/volunteer-dashboard.png" alt="GPS canvassing map" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Mobile canvass map with GPS tracking</div></div>
<div class="node-header">
<div class="node-icon">&#x1F6B6;</div>
<h4>GPS Canvassing</h4>
@@ -2209,7 +2394,8 @@
<p>Full-screen mobile canvass map with real-time GPS, walking route algorithm, visit recording, and outcome tracking.</p>
<div class="node-tags"><span class="node-tag">GPS tracking</span><span class="node-tag">Walking routes</span><span class="node-tag">Mobile-first</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-cuts">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-cuts.png" alt="Polygon cuts editor" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Territory cut management</div></div>
<div class="node-header">
<div class="node-icon">&#x2702;</div>
<h4>Polygon Cuts</h4>
@@ -2217,7 +2403,8 @@
<p>Draw geographic boundaries on the map. Assign locations to cuts for organized canvassing territories.</p>
<div class="node-tags"><span class="node-tag">Drawing mode</span><span class="node-tag">Point-in-polygon</span><span class="node-tag">Spatial queries</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-shifts">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-shifts.png" alt="Public volunteer shifts" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public shift signup with capacity tracking</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4C5;</div>
<h4>Volunteer Shifts</h4>
@@ -2225,7 +2412,8 @@
<p>Shift scheduling with public signup, confirmation emails, cut assignment, and capacity management.</p>
<div class="node-tags"><span class="node-tag">Public signup</span><span class="node-tag">Email confirm</span><span class="node-tag">Cut assignment</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="data-quality">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/data-quality.png" alt="Walk sheets and data quality" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Data quality dashboard for canvassing</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4CB;</div>
<h4>Walk Sheets</h4>
@@ -2233,7 +2421,8 @@
<p>Printable walk sheet forms with QR codes for each cut. Take the field data offline with printed reports.</p>
<div class="node-tags"><span class="node-tag">QR codes</span><span class="node-tag">Printable</span><span class="node-tag">Cut reports</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-locations">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-locations.png" alt="NAR import and locations" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Location management with NAR import</div></div>
<div class="node-header">
<div class="node-icon">&#x1F1E8;&#x1F1E6;</div>
<h4>NAR Import</h4>
@@ -2241,7 +2430,8 @@
<p>Import Canadian National Address Register data with province/city/postal filtering, coordinate projection, and streaming.</p>
<div class="node-tags"><span class="node-tag">2025 format</span><span class="node-tag">Proj4</span><span class="node-tag">Streaming</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-events">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-events.png" alt="Gancio event calendar" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Community calendar with shift sync</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4C6;</div>
<h4>Gancio Events</h4>
@@ -2252,6 +2442,11 @@
</div>
</div>
<!-- Narrative connector -->
<div class="branch-narrative reveal">
<p>Your message is only as strong as the channels that carry it. Once your team is connected and your territory mapped, you need to <strong>tell your story</strong> &mdash; through video, documentation, landing pages, and more.</p>
</div>
<!-- ====== BRANCH 3: Content & Media ====== -->
<div class="branch branch-content" data-branch="content">
<div class="branch-header reveal">
@@ -2266,7 +2461,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Media Gallery — browse videos, shorts, photos, and playlists with live chat and reactions</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="media-library">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/media-library.png" alt="Video library" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Video management with metadata</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F9;</div>
<h4>Video Library</h4>
@@ -2274,7 +2470,8 @@
<p>Upload and manage videos with FFprobe metadata, scheduled publishing, view analytics, emoji reactions, threaded comments, and live chat.</p>
<div class="node-tags"><span class="node-tag">Analytics</span><span class="node-tag">Live chat</span><span class="node-tag">Scheduling</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="landing-pages">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/landing-pages.png" alt="Landing page builder" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> GrapesJS drag-and-drop page builder</div></div>
<div class="node-header">
<div class="node-icon">&#x1F3A8;</div>
<h4>Landing Page Builder</h4>
@@ -2282,7 +2479,8 @@
<p>GrapesJS drag-and-drop page editor with block library, custom components, and instant public publishing at /p/slug.</p>
<div class="node-tags"><span class="node-tag">Drag &amp; drop</span><span class="node-tag">Block library</span><span class="node-tag">Instant publish</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="mkdocs">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/mkdocs.png" alt="MkDocs documentation site" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Material-themed documentation site</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4D6;</div>
<h4>MkDocs Documentation</h4>
@@ -2290,7 +2488,8 @@
<p>Material-themed docs with full-text search, blog, social cards, and Gitea-backed page comments with anonymous posting and moderation.</p>
<div class="node-tags"><span class="node-tag">Material theme</span><span class="node-tag">Comments</span><span class="node-tag">Blog</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="code-server">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/code-server.png" alt="Code Server IDE" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> VS Code in the browser</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4BB;</div>
<h4>Code Server</h4>
@@ -2298,7 +2497,8 @@
<p>Full VS Code in the browser. Edit configuration, templates, and code from anywhere without SSH.</p>
<div class="node-tags"><span class="node-tag">VS Code</span><span class="node-tag">Browser IDE</span><span class="node-tag">Extensions</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="excalidraw">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/excalidraw.png" alt="Excalidraw whiteboard" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Collaborative whiteboard</div></div>
<div class="node-header">
<div class="node-icon">&#x270F;</div>
<h4>Excalidraw Whiteboard</h4>
@@ -2306,7 +2506,8 @@
<p>Collaborative diagramming and whiteboard tool. Plan canvassing routes, sketch campaign strategies, and brainstorm as a team.</p>
<div class="node-tags"><span class="node-tag">Collaborative</span><span class="node-tag">Diagrams</span><span class="node-tag">Real-time</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="media-analytics">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/media-analytics.png" alt="Media analytics" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Engagement analytics dashboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F7;</div>
<h4>Photo Management</h4>
@@ -2314,7 +2515,8 @@
<p>Album organization with bulk uploads, metadata extraction, and engagement tracking. Reactions, comments, and a public photo gallery.</p>
<div class="node-tags"><span class="node-tag">Albums</span><span class="node-tag">Engagement</span><span class="node-tag">Gallery</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-gallery">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-gallery.png" alt="Public media gallery" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public gallery with categories</div></div>
<div class="node-header">
<div class="node-icon">&#x1F3B5;</div>
<h4>Playlists</h4>
@@ -2322,7 +2524,8 @@
<p>Curated video collections with admin, user, and public playlists. Drag-reorder, sidebar navigation, featured carousel, and dedicated viewer page.</p>
<div class="node-tags"><span class="node-tag">Curated</span><span class="node-tag">Public/Private</span><span class="node-tag">Reorderable</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-gallery">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-gallery.png" alt="Shorts video feed" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Video gallery with live chat</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F2;</div>
<h4>Shorts Feed</h4>
@@ -2347,7 +2550,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Public Homepage — hero section, upcoming shifts, latest videos, and activity feed</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="nocodb">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/nocodb.png" alt="NocoDB data browser" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Airtable-alternative data browser</div></div>
<div class="node-header">
<div class="node-icon">&#x1F5C4;</div>
<h4>NocoDB</h4>
@@ -2355,7 +2559,8 @@
<p>Airtable-alternative database browser. Browse, filter, and export your campaign data through a spreadsheet-like interface.</p>
<div class="node-tags"><span class="node-tag">Read-only</span><span class="node-tag">Filters</span><span class="node-tag">Export</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="n8n">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/n8n.png" alt="n8n workflow automation" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Visual workflow automation</div></div>
<div class="node-header">
<div class="node-icon">&#x26A1;</div>
<h4>n8n Workflows</h4>
@@ -2363,7 +2568,8 @@
<p>Visual workflow automation. Connect APIs, trigger actions, and build custom integrations without code.</p>
<div class="node-tags"><span class="node-tag">Visual builder</span><span class="node-tag">400+ integrations</span><span class="node-tag">Webhooks</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="gitea">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/gitea.png" alt="Gitea Git hosting" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Self-hosted Git with CI/CD</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4E6;</div>
<h4>Gitea</h4>
@@ -2371,7 +2577,8 @@
<p>Self-hosted Git repository. Version control for your campaign code, configs, and documentation.</p>
<div class="node-tags"><span class="node-tag">Git hosting</span><span class="node-tag">Issues</span><span class="node-tag">CI/CD</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="miniqr">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/miniqr.png" alt="Mini QR generator" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> QR code generator</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F1;</div>
<h4>Mini QR</h4>
@@ -2379,7 +2586,8 @@
<p>QR code generator for walk sheets, campaign materials, and event signage. Instant PNG generation.</p>
<div class="node-tags"><span class="node-tag">PNG output</span><span class="node-tag">Embeddable</span><span class="node-tag">Public API</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-dashboard.png" alt="Command palette search" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Ctrl+K global command palette</div></div>
<div class="node-header">
<div class="node-icon">&#x2318;</div>
<h4>Command Palette</h4>
@@ -2387,7 +2595,8 @@
<p>Global Ctrl+K search across pages, campaigns, locations, users, and settings. Fuzzy matching, recent items, and keyboard-driven navigation.</p>
<div class="node-tags"><span class="node-tag">Ctrl+K</span><span class="node-tag">Fuzzy search</span><span class="node-tag">Keyboard-first</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-settings">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-settings.png" alt="Navigation settings" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Organization and nav settings</div></div>
<div class="node-header">
<div class="node-icon">&#x2699;</div>
<h4>Navigation Settings</h4>
@@ -2412,7 +2621,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Observability Dashboard — Prometheus metrics, Grafana dashboards, and alert management</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="pangolin-tunnel">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/pangolin-tunnel.png" alt="Pangolin tunnel management" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Tunnel management dashboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F310;</div>
<h4>Pangolin Tunnel</h4>
@@ -2420,7 +2630,8 @@
<p>Expose your self-hosted services to the internet without port forwarding. Newt container integration with automatic SSL.</p>
<div class="node-tags"><span class="node-tag">No port-forward</span><span class="node-tag">Auto SSL</span><span class="node-tag">Newt</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-observability">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-observability.png" alt="Observability dashboard" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Prometheus + Grafana monitoring</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4C8;</div>
<h4>Prometheus + Grafana</h4>
@@ -2428,7 +2639,8 @@
<p>12 custom metrics, 3 dashboards, alert rules, and service health monitoring. Full observability stack.</p>
<div class="node-tags"><span class="node-tag">12 metrics</span><span class="node-tag">3 dashboards</span><span class="node-tag">Alerts</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-users">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-users.png" alt="User management with RBAC" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Role-based access control</div></div>
<div class="node-header">
<div class="node-icon">&#x1F50F;</div>
<h4>Security Hardened</h4>
@@ -2436,7 +2648,8 @@
<p>13-finding security audit addressed. JWT rotation, rate limiting, XSS prevention, encrypted secrets, HSTS headers.</p>
<div class="node-tags"><span class="node-tag">Audit complete</span><span class="node-tag">RBAC</span><span class="node-tag">Encryption</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="mailhog">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/mailhog.png" alt="MailHog email capture" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Email testing with MailHog</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4BE;</div>
<h4>Automated Backups</h4>
@@ -2444,7 +2657,8 @@
<p>PostgreSQL dumps, Listmonk data, uploads archive, and optional S3 upload. One-command backup script.</p>
<div class="node-tags"><span class="node-tag">PostgreSQL</span><span class="node-tag">S3 optional</span><span class="node-tag">Scripted</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-settings">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-settings.png" alt="Vaultwarden password manager" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Organization security settings</div></div>
<div class="node-header">
<div class="node-icon">&#x1F510;</div>
<h4>Vaultwarden</h4>
@@ -2452,7 +2666,8 @@
<p>Self-hosted Bitwarden-compatible password manager. Secure credential sharing for your team with real-time sync and browser extensions.</p>
<div class="node-tags"><span class="node-tag">Bitwarden</span><span class="node-tag">Team sharing</span><span class="node-tag">Encrypted</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="admin-users">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/admin-users.png" alt="User provisioning" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Multi-service user provisioning</div></div>
<div class="node-header">
<div class="node-icon">&#x1F465;</div>
<h4>User Provisioning</h4>
@@ -2463,6 +2678,11 @@
</div>
</div>
<!-- Narrative connector -->
<div class="branch-narrative reveal">
<p>A movement that can&rsquo;t sustain itself financially will always depend on someone else&rsquo;s goodwill. <strong>Own your revenue</strong> the same way you own your data &mdash; with tools that put every dollar directly into your cause.</p>
</div>
<!-- ====== BRANCH 6: Fundraising & Commerce ====== -->
<div class="branch branch-fundraising" data-branch="fundraising">
<div class="branch-header reveal">
@@ -2477,7 +2697,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Pricing Plans — subscription tiers with monthly/yearly billing and feature comparison</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="public-donations">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-donations.png" alt="Donation platform" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public donation page</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4B0;</div>
<h4>Donation Platform</h4>
@@ -2485,7 +2706,8 @@
<p>Accept one-time donations with configurable suggested amounts, anonymous giving, and automatic tax receipts via email.</p>
<div class="node-tags"><span class="node-tag">Stripe</span><span class="node-tag">Anonymous</span><span class="node-tag">Receipts</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="subscription-plans">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/subscription-plans.png" alt="Subscription plan management" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Plan management dashboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F504;</div>
<h4>Subscription Plans</h4>
@@ -2493,7 +2715,8 @@
<p>Recurring revenue with tiered plans, monthly and yearly billing, and automatic renewal management. Replace Patreon.</p>
<div class="node-tags"><span class="node-tag">Recurring</span><span class="node-tag">Tiers</span><span class="node-tag">MRR tracking</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-shop">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-shop.png" alt="Product shop" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public product storefront</div></div>
<div class="node-header">
<div class="node-icon">&#x1F6D2;</div>
<h4>Product Shop</h4>
@@ -2501,7 +2724,8 @@
<p>Sell digital products, event tickets, and merchandise. Inventory management, download delivery, and capacity limits.</p>
<div class="node-tags"><span class="node-tag">Digital goods</span><span class="node-tag">Events</span><span class="node-tag">Inventory</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="payments-dashboard">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/payments-dashboard.png" alt="Payment analytics" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Revenue analytics dashboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4CA;</div>
<h4>Payment Dashboard</h4>
@@ -2509,7 +2733,8 @@
<p>Revenue analytics with subscriber counts, MRR tracking, donation history, and CSV exports for accounting.</p>
<div class="node-tags"><span class="node-tag">Analytics</span><span class="node-tag">CSV export</span><span class="node-tag">Refunds</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="gallery-ads">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/gallery-ads.png" alt="Gallery ad management" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> In-gallery promotion manager</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4E2;</div>
<h4>Gallery Ads</h4>
@@ -2517,7 +2742,8 @@
<p>Promote donations, products, and subscriptions within the media gallery. Visibility targeting, scheduling, and click analytics.</p>
<div class="node-tags"><span class="node-tag">Targeting</span><span class="node-tag">Scheduling</span><span class="node-tag">CTR tracking</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="donation-pages">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/donation-pages.png" alt="Donation page builder" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Custom branded donation pages</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4DD;</div>
<h4>Donation Pages</h4>
@@ -2542,7 +2768,8 @@
<div class="branch-preview-caption"><span class="caption-dot"></span> Wall of Fame — volunteer spotlight, achievement leaderboards, and community recognition</div>
</div>
<div class="nodes-grid stagger">
<div class="feature-node">
<div class="feature-node" data-screenshot="volunteer-friends">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/volunteer-friends.png" alt="Friend system" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Volunteer friend connections</div></div>
<div class="node-header">
<div class="node-icon">&#x1F465;</div>
<h4>Friend System</h4>
@@ -2550,7 +2777,8 @@
<p>Friend requests, suggestions, pokes, cross-module badges on campaigns, shifts, and the map.</p>
<div class="node-tags"><span class="node-tag">Friend requests</span><span class="node-tag">Suggestions</span><span class="node-tag">Poke</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-home">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-home.png" alt="Activity feed" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Public homepage with activity</div></div>
<div class="node-header">
<div class="node-icon">&#x1F4F0;</div>
<h4>Activity Feed</h4>
@@ -2558,7 +2786,8 @@
<p>Real-time SSE feed of friend activity across campaigns, shifts, canvassing, and responses.</p>
<div class="node-tags"><span class="node-tag">Real-time</span><span class="node-tag">SSE</span><span class="node-tag">Cross-module</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-wall-of-fame">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-wall-of-fame.png" alt="Achievements and leaderboard" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Volunteer achievements leaderboard</div></div>
<div class="node-header">
<div class="node-icon">&#x1F3C6;</div>
<h4>Achievements &amp; Notifications</h4>
@@ -2566,7 +2795,8 @@
<p>Milestone badges, real-time notification bell with friend requests, pokes, comments, and alerts.</p>
<div class="node-tags"><span class="node-tag">Badges</span><span class="node-tag">Bell UI</span><span class="node-tag">Real-time</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="volunteer-calendar">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/volunteer-calendar.png" alt="Team calendar" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Personal calendar with layers</div></div>
<div class="node-header">
<div class="node-icon">&#x1F46B;</div>
<h4>Groups &amp; Teams</h4>
@@ -2574,7 +2804,8 @@
<p>Auto-groups for shift teams and campaign crews, custom groups, and shared updates.</p>
<div class="node-tags"><span class="node-tag">Shift teams</span><span class="node-tag">Campaign crews</span><span class="node-tag">Custom groups</span></div>
</div>
<div class="feature-node">
<div class="feature-node" data-screenshot="public-gallery">
<div class="node-screenshot"><img src="/assets/images/screenshots/features/public-gallery.png" alt="Reactions and comments" loading="lazy" width="420" height="263"><div class="node-screenshot-label"><span class="screenshot-dot"></span> Gallery with reactions and chat</div></div>
<div class="node-header">
<div class="node-icon">&#x1F60D;</div>
<h4>Reactions &amp; Comments</h4>