2181 lines
66 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Changemaker Lite - Campaign Power Tools</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #6f42c1;
--primary-dark: #5a32a3;
--accent: #F5A9B8;
--text-dark: #1a1a1a;
--text-light: #666;
--bg-light: #f8f9fa;
--bg-white: #ffffff;
--border: #e0e0e0;
--success: #28a745;
--warning: #ffc107;
--shadow: 0 2px 4px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 30px rgba(0,0,0,0.1);
}
body.dark {
--primary: #8B5CF6; /* A slightly brighter purple for dark mode */
--primary-dark: #7C3AED;
--accent: #EC4899; /* A brighter pink */
--text-dark: #E2E8F0; /* slate-200 */
--text-light: #94A3B8; /* slate-400 */
--bg-light: #1E293B; /* slate-800 */
--bg-white: #0F172A; /* slate-900 */
--border: #334155; /* slate-700 */
--success: #4ADE80; /* green-400 */
--warning: #FBBF24; /* amber-400 */
--shadow: 0 2px 4px rgba(0,0,0,0.2);
--shadow-lg: 0 10px 30px rgba(0,0,0,0.2);
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
color: var(--text-dark);
line-height: 1.6;
overflow-x: hidden;
background-color: var(--bg-white);
transition: background-color 0.3s ease;
}
/* Header */
.header {
position: fixed;
top: 0;
width: 100%;
background: var(--bg-white);
box-shadow: var(--shadow);
z-index: 1000;
transition: all 0.3s ease;
}
.header.scrolled {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
}
body.dark .header.scrolled {
background: rgba(15, 23, 42, 0.85); /* slate-900 with transparency */
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
}
.nav-items {
display: flex;
align-items: center;
gap: 2rem;
}
.nav-links {
display: flex;
gap: 2rem;
list-style: none;
}
.nav-links a {
text-decoration: none;
color: var(--text-dark);
font-weight: 500;
transition: color 0.3s ease;
}
.nav-links a:hover {
color: var(--primary);
}
.cta-button {
background: var(--primary);
color: white;
padding: 0.75rem 1.5rem;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.cta-button:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
/* Theme Toggle */
.theme-switch-wrapper {
display: flex;
align-items: center;
}
.theme-switch {
display: inline-block;
height: 24px;
position: relative;
width: 48px;
}
.theme-switch input {
display:none;
}
.slider {
background-color: #ccc;
bottom: 0;
cursor: pointer;
left: 0;
position: absolute;
right: 0;
top: 0;
transition: .4s;
border-radius: 24px;
}
.slider:before {
background-color: #fff;
bottom: 4px;
content: "";
height: 16px;
left: 4px;
position: absolute;
transition: .4s;
width: 16px;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--primary);
}
input:checked + .slider:before {
transform: translateX(24px);
}
/* Hero Section */
.hero {
padding: 8rem 2rem 4rem;
background: linear-gradient(135deg, var(--bg-light) 0%, var(--bg-white) 100%);
text-align: center;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
}
.hero-badge {
display: inline-block;
background: var(--accent);
color: var(--text-dark);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 1rem;
}
.canadian-badge {
display: inline-block;
background: #ff0000;
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 1rem;
margin-left: 1rem;
}
.hero h1 {
font-size: 3rem;
font-weight: 800;
margin-bottom: 1.5rem;
line-height: 1.2;
background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.25rem;
color: var(--text-light);
margin-bottom: 2rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.hero-cta {
display: flex;
gap: 1rem;
justify-content: center;
margin-bottom: 3rem;
}
.btn-primary {
background: var(--primary);
color: white;
padding: 1rem 2rem;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.btn-primary:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.btn-secondary {
background: transparent;
color: var(--primary);
padding: 1rem 2rem;
border: 2px solid var(--primary);
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-secondary:hover {
background: var(--primary);
color: white;
}
/* Search Styles */
.hero-search {
margin: 2rem 0;
width: 100%;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.search-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
.search-box {
width: 100%;
max-width: 500px;
padding: 1rem 1.5rem;
font-size: 1rem;
border: 2px solid var(--border);
border-radius: 50px;
outline: none;
transition: all 0.3s ease;
background: var(--bg-white);
color: var(--text-dark);
box-shadow: var(--shadow);
}
.search-box:focus {
border-color: var(--primary);
box-shadow: 0 4px 20px rgba(111, 66, 193, 0.2);
transform: translateY(-2px);
}
.search-box::placeholder {
color: var(--text-light);
}
.search-results {
max-width: 500px;
margin: 0 auto;
background: var(--bg-white);
border-radius: 12px;
box-shadow: var(--shadow-lg);
max-height: 400px;
overflow: hidden;
display: none;
position: relative;
z-index: 100;
border: 1px solid var(--border);
}
.search-results-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid var(--border);
background-color: var(--bg-light);
}
.results-count {
color: var(--text-light);
font-size: 0.9rem;
font-weight: 600;
}
.close-results {
background: none;
border: none;
font-size: 1.5rem;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s ease;
}
.close-results:hover {
background-color: var(--border);
color: var(--text-dark);
}
.docs-search-results-list {
max-height: 300px;
overflow-y: auto;
}
.search-result-item {
padding: 1rem;
border-bottom: 1px solid var(--border);
cursor: pointer;
transition: background-color 0.2s ease;
color: var(--text-dark);
text-decoration: none;
display: block;
}
.search-result-item:hover {
background-color: var(--bg-light);
}
.search-result-item:last-child {
border-bottom: none;
}
.search-result-title {
font-weight: 600;
color: var(--primary);
margin-bottom: 0.5rem;
font-size: 1rem;
}
.search-result-content {
color: var(--text-light);
font-size: 0.9rem;
line-height: 1.4;
}
.search-result-content mark {
background-color: var(--accent);
color: var(--text-dark);
padding: 0.1em 0.2em;
border-radius: 2px;
}
.no-results {
padding: 2rem;
text-align: center;
color: var(--text-light);
}
/* Mobile responsive for search */
@media (max-width: 768px) {
.hero-search {
margin: 1.5rem 0;
}
.search-box {
font-size: 0.9rem;
padding: 0.875rem 1.25rem;
}
.search-results {
margin: 0 1rem;
max-width: calc(100% - 2rem);
}
}
@media (max-width: 480px) {
.search-box {
font-size: 0.85rem;
padding: 0.75rem 1rem;
}
}
/* Quick Stats */
.quick-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
max-width: 800px;
margin: 0 auto;
}
/* Desktop: 4 columns for stats */
@media (min-width: 768px) {
.quick-stats {
grid-template-columns: repeat(4, 1fr);
gap: 1.5rem;
}
}
.stat {
text-align: center;
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 0.5rem;
}
.stat-label {
color: var(--text-dark);
font-size: 0.875rem;
}
/* Problem Section */
.problem-section {
padding: 4rem 2rem;
background: var(--bg-white);
}
.section-content {
max-width: 1200px;
margin: 0 auto;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--text-dark);
}
.section-header p {
font-size: 1.125rem;
color: var(--text-light);
max-width: 600px;
margin: 0 auto;
}
.problem-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.problem-card {
background: var(--bg-light);
padding: 2rem;
border-radius: 12px;
transition: all 0.3s ease;
}
.problem-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.problem-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.problem-card h3 {
font-size: 1.25rem;
margin-bottom: 0.75rem;
color: var(--text-dark);
}
.problem-card p {
color: var(--text-light);
font-size: 0.875rem;
}
/* Solution Section */
.solution-section {
padding: 4rem 2rem;
background: var(--bg-light);
}
.solution-showcase {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
margin-bottom: 4rem;
}
.solution-showcase:nth-child(even) {
direction: rtl;
}
.solution-showcase:nth-child(even) .showcase-content {
direction: ltr;
}
.showcase-content h3 {
font-size: 2rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.showcase-content p {
color: var(--text-light);
margin-bottom: 1.5rem;
}
.feature-list {
list-style: none;
}
.feature-list li {
padding: 0.75rem 0;
color: var(--text-dark);
display: flex;
align-items: center;
gap: 0.75rem;
}
.feature-list li::before {
content: "✓";
display: inline-block;
width: 24px;
height: 24px;
background: var(--success);
color: white;
border-radius: 50%;
text-align: center;
line-height: 24px;
flex-shrink: 0;
}
.screenshot-placeholder {
background: var(--bg-white);
border: 2px dashed var(--border);
border-radius: 12px;
padding: 4rem 2rem;
text-align: center;
color: var(--text-light);
font-style: italic;
min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
}
.mobile-screenshot {
max-width: 500px;
max-height: 500px;
width: auto;
height: auto;
border-radius: 12px;
box-shadow: var(--shadow-lg);
}
/* Power Tools Grid */
.tools-section {
padding: 4rem 2rem;
background: var(--bg-white);
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.tool-card {
background: var(--bg-white);
border: 1px solid var(--border);
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.tool-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%);
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.tool-card:hover::before {
transform: translateX(0);
}
.tool-card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-5px);
}
.tool-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.tool-icon {
font-size: 2rem;
width: 48px;
height: 48px;
background: var(--bg-light);
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
}
.tool-card h3 {
font-size: 1.25rem;
color: var(--text-dark);
margin-bottom: 0.5rem;
}
.tool-description {
color: var(--text-light);
margin-bottom: 1rem;
font-size: 0.875rem;
}
.tool-features {
list-style: none;
font-size: 0.875rem;
}
.tool-features li {
padding: 0.5rem 0;
color: var(--text-dark);
display: flex;
align-items: center;
gap: 0.5rem;
}
.tool-features li::before {
content: "→";
color: var(--primary);
font-weight: bold;
}
.tool-cta {
margin-top: 1.5rem;
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.tool-cta .btn-secondary {
padding: 0.5rem 1rem;
font-size: 0.875rem;
border-width: 1px;
}
/* Integration Section */
.integration-section {
padding: 4rem 2rem;
background: var(--bg-light);
}
.integration-visual {
max-width: 800px;
margin: 3rem auto;
text-align: center;
}
.integration-diagram {
background: var(--bg-white);
border-radius: 12px;
padding: 3rem;
box-shadow: var(--shadow-lg);
}
/* Testimonial Section */
.testimonial-section {
padding: 4rem 2rem;
background: var(--primary);
color: white;
text-align: center;
}
.testimonial-content {
max-width: 800px;
margin: 0 auto;
}
.testimonial-section h2 {
color: white;
margin-bottom: 2rem;
}
.testimonial-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.testimonial-card {
background: rgba(255, 255, 255, 0.1);
padding: 2rem;
border-radius: 12px;
backdrop-filter: blur(10px);
}
.testimonial-text {
font-style: italic;
margin-bottom: 1rem;
opacity: 0.9;
}
.testimonial-author {
font-weight: 600;
}
.testimonial-role {
font-size: 0.875rem;
opacity: 0.8;
}
/* Live Sites Grid */
.live-sites-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.site-card {
background: rgba(255, 255, 255, 0.1);
padding: 1.5rem;
border-radius: 12px;
backdrop-filter: blur(10px);
text-decoration: none;
color: white;
transition: all 0.3s ease;
display: flex;
align-items: flex-start;
gap: 1rem;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.site-card:hover {
transform: translateY(-4px);
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.2);
}
.site-card.featured-site {
border: 2px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.15);
}
.site-badge {
position: absolute;
top: -8px;
right: 1rem;
background: var(--accent);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
}
.site-icon {
font-size: 2rem;
flex-shrink: 0;
}
.site-content {
flex: 1;
}
.site-name {
font-weight: 600;
font-size: 1.125rem;
margin-bottom: 0.5rem;
}
.site-desc {
font-size: 0.875rem;
opacity: 0.9;
margin-bottom: 0.75rem;
line-height: 1.4;
color: #dfbfe6;
}
.site-status {
font-size: 0.75rem;
color: #4ade80;
font-weight: 600;
}
.proof-stats h3 {
color: white;
margin-bottom: 1.5rem;
text-align: center;
}
.stats-mini-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 1rem;
max-width: 600px;
margin: 0 auto;
}
.mini-stat {
text-align: center;
background: rgba(255, 255, 255, 0.1);
padding: 1rem;
border-radius: 8px;
backdrop-filter: blur(10px);
}
.mini-stat .stat-number {
font-size: 1.5rem;
font-weight: 700;
color: white;
margin-bottom: 0.25rem;
}
.mini-stat .stat-label {
font-size: 0.875rem;
opacity: 0.8;
}
/* CTA Section */
.cta-section {
padding: 4rem 2rem;
background: var(--bg-white);
text-align: center;
}
.cta-content {
max-width: 600px;
margin: 0 auto;
}
.cta-section h2 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.cta-section p {
font-size: 1.125rem;
color: var(--text-light);
margin-bottom: 2rem;
}
.cta-buttons {
display: flex;
gap: 1rem;
justify-content: center;
}
/* Data Ownership Section */
.sovereignty-section {
padding: 4rem 2rem;
background: var(--bg-white);
}
.sovereignty-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 3rem 0;
}
.sovereignty-card {
text-align: center;
padding: 2rem;
background: var(--bg-light);
border-radius: 12px;
transition: all 0.3s ease;
}
.sovereignty-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.sovereignty-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.sovereignty-card h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.comparison-visual {
max-width: 800px;
margin: 3rem auto;
text-align: center;
}
.comparison-visual h3 {
font-size: 1.75rem;
margin-bottom: 2rem;
}
.comparison-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.comparison-item {
background: var(--bg-light);
padding: 2rem;
border-radius: 12px;
}
.comparison-item h4 {
font-size: 1.25rem;
margin-bottom: 1rem;
}
.comparison-list {
list-style: none;
text-align: left;
}
.comparison-list li {
padding: 0.5rem 0;
font-size: 0.95rem;
}
.comparison-list.bad li {
color: #dc3545;
}
.comparison-list.good li {
color: var(--success);
}
/* Pricing Section */
.pricing-section {
padding: 4rem 2rem;
background: var(--bg-light);
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 3rem 0;
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
.pricing-card {
background: var(--bg-white);
border: 2px solid var(--border);
border-radius: 12px;
padding: 2rem;
text-align: center;
position: relative;
transition: all 0.3s ease;
}
.pricing-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.pricing-card.featured {
border-color: var(--primary);
transform: scale(1.05);
}
.pricing-badge {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: var(--primary);
color: white;
padding: 0.25rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
}
.pricing-header h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.price {
font-size: 3rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 0.5rem;
}
.price-period {
font-size: 1rem;
color: var(--text-light);
margin-bottom: 2rem;
}
.pricing-features {
list-style: none;
text-align: left;
margin-bottom: 2rem;
}
.pricing-features li {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border);
color: var(--text-dark);
}
.pricing-note {
font-size: 0.875rem;
color: var(--text-light);
margin-top: 1rem;
}
.cost-comparison {
text-align: center;
max-width: 600px;
margin: 3rem auto 0;
padding: 2rem;
background: var(--bg-white);
border-radius: 12px;
box-shadow: var(--shadow);
}
.cost-comparison h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.cost-comparison p {
font-size: 1.125rem;
margin-bottom: 1rem;
}
.cost-comparison strong {
color: var(--primary);
}
.comparison-link {
color: var(--primary);
text-decoration: none;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.5rem;
transition: gap 0.3s ease;
}
.comparison-link:hover {
gap: 1rem;
}
/* Mobile Features Badge */
.mobile-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: var(--success);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
margin: 0.5rem;
}
.footer {
background: var(--text-dark);
color: white;
padding: 3rem 2rem 1rem;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 3rem;
margin-bottom: 2rem;
}
.footer-section h4 {
margin-bottom: 1rem;
color: white;
}
.footer-links {
list-style: none;
}
.footer-links li {
padding: 0.5rem 0;
}
.footer-links a {
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: color 0.3s ease;
}
.footer-links a:hover {
color: white;
}
.footer-bottom {
text-align: center;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.6);
font-size: 0.875rem;
}
/* Mobile Responsive */
@media (max-width: 1024px) {
.solution-showcase {
gap: 3rem;
}
.showcase-content h3 {
font-size: 1.75rem;
}
}
@media (max-width: 768px) {
.nav-links {
display: none;
}
.hero h1 {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1rem;
}
.hero-cta {
flex-direction: column;
align-items: center;
}
.solution-showcase {
grid-template-columns: 1fr;
gap: 2rem;
text-align: center;
}
.solution-showcase:nth-child(even) {
direction: ltr;
}
.showcase-content {
order: 1;
}
.screenshot-placeholder {
order: 2;
padding: 2rem 1rem;
}
.mobile-screenshot {
max-width: 100%;
height: auto;
}
.tools-grid {
grid-template-columns: 1fr;
}
.cta-buttons {
flex-direction: column;
align-items: center;
}
}
</style>
</head>
<body>
<!-- Custom header for landing page -->
<header class="header">
<nav class="nav-container">
<div class="logo">Changemaker Lite</div>
<div class="nav-items">
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="#tools">Power Tools</a></li>
<li><a href="#sovereignty">Data Ownership</a></li>
<li><a href="#pricing">Pricing</a></li>
</ul>
<div class="theme-switch-wrapper">
<label class="theme-switch" for="theme-toggle">
<input type="checkbox" id="theme-toggle" />
<span class="slider"></span>
</label>
</div>
<a href="#get-started" class="cta-button">Get Started</a>
</div>
</nav>
</header>
<!-- Hero Section -->
<section class="hero">
<div class="hero-content">
<div class="hero-badge">🚀 Hardware Up This Site Served by Changemaker - Lite</div>
<h1>Power Tools for Modern Campaigns</h1>
<p class="hero-subtitle">
Complete political independence on your own infrastructure. Own every byte of data, control every system, scale at your own pace. No corporate surveillance, no foreign interference, no monthly ransoms. Deploy unlimited sites, send unlimited messages, organize unlimited supporters. Free and open source software for community organizers wanting to make change.
</p>
<div class="hero-cta">
<a href="/phil" class="btn-primary">
Philosophy
<span></span>
</a>
<a href="#tools" class="btn-secondary">Explore Tools</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite" class="btn-secondary" target="_blank">Git Code</a>
</div>
<div class="hero-cta" style="margin-top: 2rem;"></div>
<!-- Search Section -->
<div class="hero-search">
<div class="search-container">
<input type="text"
class="search-box"
id="docs-search-input"
placeholder="Search campaign tools & docs... (Ctrl+K)"
autocomplete="off">
</div>
<div class="search-results" id="docs-search-results">
<div class="search-results-header">
<span class="results-count"></span>
<button class="close-results" type="button">&times;</button>
</div>
<div class="docs-search-results-list"></div>
</div>
</div>
<div class="quick-stats">
<div class="stat">
<div class="stat-number">100%</div>
<div class="stat-label">Local Data Ownership</div>
</div>
<div class="stat">
<div class="stat-number">$0</div>
<div class="stat-label">Free to Self-Host</div>
</div>
<div class="stat">
<div class="stat-number">Canadian</div>
<div class="stat-label">Built by Locals</div>
</div>
<div class="stat">
<div class="stat-number">Open-Source</div>
<div class="stat-label">Built for Campaigns</div>
</div>
</div>
</div>
</div>
</section>
<!-- Problem Section -->
<section class="problem-section">
<div class="section-content">
<div class="section-header">
<h2>Your Supporters Are Struggling</h2>
<p>Traditional campaign tools weren't built for the reality of political action</p>
</div>
<div class="problem-grid">
<div class="problem-card">
<div class="problem-icon">📱</div>
<h3>Can't Find Answers Fast</h3>
<p>Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.</p>
</div>
<div class="problem-card">
<div class="problem-icon">🗺️</div>
<h3>Disconnected Data</h3>
<p>Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.</p>
</div>
<div class="problem-card">
<div class="problem-icon">💸</div>
<h3>Death by Subscription</h3>
<p>$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.</p>
</div>
<div class="problem-card">
<div class="problem-icon">🔒</div>
<h3>No Data Control</h3>
<p>Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.</p>
</div>
<div class="problem-card">
<div class="problem-icon">📵</div>
<h3>Not Mobile-Ready</h3>
<p>Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.</p>
</div>
<div class="problem-card">
<div class="problem-icon">🏢</div>
<h3>Foreign Dependencies</h3>
<p>US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.</p>
</div>
</div>
</div>
</section>
<!-- Solution Section -->
<section class="solution-section" id="features">
<div class="section-content">
<div class="section-header">
<h2>Political Documentation That Works</h2>
<p>Everything your team needs, instantly searchable, always accessible, and easy to communicate</p>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Communicate on Scale</h3>
<p>Full email and messenger campaign systems with unlimited users</p>
<ul class="feature-list">
<li>Drop in replacement for mailchimp, sendgrid, etc.</li>
<li>Track emails, clicks, and user actions</li>
<li>Unlimited contact, asset, and file storage</li>
<li>Compatible with all major email providers</li>
<li>Fully extensible with API's & webhooks</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/listmonk.png" alt="Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Mobile-First Everything</h3>
<p>Built for phones first, because that's what your supporters carry. Every feature, every interface, optimized for one-handed use in the field.</p>
<ul class="feature-list">
<li>Touch-optimized interfaces</li>
<li>Offline-capable after first load</li>
<li>Simple, easy to read, and clear buttons</li>
<li>One-thumb navigation</li>
<li>Instant load times</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/mobile_generic_view.png" alt="Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Your Data, Your Servers, Your Rules</h3>
<p>Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.</p>
<ul class="feature-list">
<li>100% self-hosted infrastructure</li>
<li>Canadian data residency</li>
<li>Complete export capabilities</li>
<li>No vendor lock-in ever</li>
<li>Privacy-first architecture</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/homepage_square.png" alt="Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Instant Search Everything</h3>
<p>Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.</p>
<ul class="feature-list">
<li>Full-text search across all documentation</li>
<li>Smart categorization and tagging</li>
<li>Works offline after first load</li>
<li>Mobile-optimized interface</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/search_square.png" alt="Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Interactive Canvassing Maps</h3>
<p>See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.</p>
<ul class="feature-list">
<li>Real-time location tracking</li>
<li>Color-coded support levels</li>
<li>Add notes directly from the field</li>
<li>Track door knocks & interactions</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/map_square.gif" alt="Map interface showing color-coded houses, with popup showing voter details and previous interaction history" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Living Documentation</h3>
<p>Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.</p>
<ul class="feature-list">
<li>Real-time collaborative editing</li>
<li>Version control and history</li>
<li>Automatic mobile optimization</li>
<li>Beautiful, branded output</li>
<li>Thousands of plugins available</li>
<li>Local AI</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/coder_square.png" alt="Split view showing markdown editor on left, live preview on right with campaign branding" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Beautiul Websites</h3>
<p>Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.</p>
<ul class="feature-list">
<li>Social media cards</li>
<li>Fully customizable</li>
<li>Custom pages and landers</li>
<li>Integrated blogging</li>
<li>Supports 60+ languages</li>
</ul>
</div>
<div class="screenshot-placeholder">
<img src="assets/built.png" alt="Map interface showing color-coded houses, with popup showing voter details and previous interaction history" class="mobile-screenshot">
</div>
</div>
<div class="solution-showcase">
<div class="showcase-content">
<h3>Connect Albertans to Their Representatives</h3>
<p>Help Alberta residents take action by connecting them directly with their elected officials at all government levels. Perfect for advocacy campaigns and issue-based organizing.</p>
<ul class="feature-list">
<li>Postal code lookup for federal, provincial & municipal representatives</li>
<li>Create unlimited advocacy campaigns with custom email templates</li>
<li>Track engagement metrics and email delivery</li>
<li>Smart caching for fast performance</li>
<li>Mobile-first design for on-the-go advocacy</li>
<li>Safe email testing mode for development</li>
</ul>
</div>
<div class="screenshot-placeholder">
[Insert photo: Screenshot of Influence app showing representative lookup results with contact cards for MP, MLA, and City Councillor, with "Send Email" buttons]
</div>
</div>
</div>
</section>
<!-- Power Tools Section -->
<section class="tools-section" id="tools">
<div class="section-content">
<div class="section-header">
<h2>Your Complete Campaign Power Tool Suite</h2>
<p>Everything works together. No integrations needed. No monthly fees.</p>
</div>
<div style="position: relative; padding-top: 56.25%; margin: 2rem 0;">
<iframe
src="https://customer-1ebw5tv06sxrrq32.cloudflarestream.com/33e0b6033cbc3c22d91df18ba867fe95/iframe?muted=true&preload=true&loop=true&autoplay=true&poster=https%3A%2F%2Fcustomer-1ebw5tv06sxrrq32.cloudflarestream.com%2F33e0b6033cbc3c22d91df18ba867fe95%2Fthumbnails%2Fthumbnail.jpg%3Ftime%3D%26height%3D600"
loading="lazy"
style="border: none; position: absolute; top: 0; left: 0; height: 100%; width: 100%;"
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
allowfullscreen="true"
></iframe>
</div>
<div class="tools-grid">
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">📝</div>
<div>
<h3>Smart Documentation Hub</h3>
<div class="tool-description">MkDocs + Code Server</div>
mkdocs/docs/blog </div>
</div>
<p>Create beautiful, searchable documentation that your team will actually use.</p>
<ul class="tool-features">
<li>Instant search across everything</li>
<li>Mobile-first responsive design</li>
<li>Automatic table of contents</li>
<li>Deep linking to any section</li>
</ul>
<div class="tool-cta">
<a href="/services/mkdocs/" class="btn-secondary">Learn More</a>
<a href="/services/code-server/" class="btn-secondary">Code Server</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">🗺️</div>
<div>
<h3>BNKops Map</h3>
<div class="tool-description">Interactive Canvassing System</div>
</div>
</div>
<p>Turn voter data into visual intelligence your canvassers can use.</p>
<ul class="tool-features">
<li>Real-time GPS tracking</li>
<li>Support level heat maps</li>
<li>Instant data entry</li>
<li>Offline-capable</li>
</ul>
<div class="tool-cta">
<a href="/services/map/" class="btn-secondary">Learn More</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">📊</div>
<div>
<h3>Voter Database</h3>
<div class="tool-description">NocoDB Spreadsheet Interface</div>
</div>
</div>
<p>Manage voter data like a spreadsheet, access it like a database.</p>
<ul class="tool-features">
<li>Familiar Excel-like interface</li>
<li>Custom forms for data entry</li>
<li>Advanced filtering & search</li>
<li>API access for automation</li>
</ul>
<div class="tool-cta">
<a href="/services/nocodb/" class="btn-secondary">Learn More</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">📧</div>
<div>
<h3>Email Command Center</h3>
<div class="tool-description">Listmonk Email Platform</div>
</div>
</div>
<p>Professional email & messenger campaigns without the professional price tag.</p>
<ul class="tool-features">
<li>Unlimited subscribers</li>
<li>Beautiful templates</li>
<li>Open & click tracking</li>
<li>Automated sequences</li>
</ul>
<div class="tool-cta">
<a href="/services/listmonk/" class="btn-secondary">Learn More</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">🤖</div>
<div>
<h3>Campaign Automation</h3>
<div class="tool-description">n8n Workflow Engine</div>
</div>
</div>
<p>Automate repetitive tasks so your team can focus on voters.</p>
<ul class="tool-features">
<li>Visual workflow builder</li>
<li>Connect any service</li>
<li>Trigger-based automation</li>
<li>No coding required</li>
</ul>
<div class="tool-cta">
<a href="/services/n8n/" class="btn-secondary">Learn More</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">🗃️</div>
<div>
<h3>Version Control</h3>
<div class="tool-description">Gitea Repository</div>
</div>
</div>
<p>Track changes, collaborate safely, and never lose work again.</p>
<ul class="tool-features">
<li>Full version history</li>
<li>Collaborative editing</li>
<li>Backup everything</li>
<li>Roll back changes</li>
</ul>
<div class="tool-cta">
<a href="/services/gitea/" class="btn-secondary">Learn More</a>
</div>
</div>
<div class="tool-card">
<div class="tool-header">
<div class="tool-icon">🏛️</div>
<div>
<h3>Influence Campaign Tool</h3>
<div class="tool-description">Representative Advocacy Platform</div>
</div>
</div>
<p>Connect Alberta residents with their elected officials for effective advocacy campaigns.</p>
<ul class="tool-features">
<li>Multi-level representative lookup</li>
<li>Campaign management dashboard</li>
<li>Email tracking & analytics</li>
<li>Custom campaign templates</li>
</ul>
<div class="tool-cta">
<a href="/build/influence/" class="btn-secondary">Learn More</a>
</div>
</div>
</div>
</div>
</section>
<!-- Data Ownership Section -->
<section class="sovereignty-section" id="sovereignty">
<div class="section-content">
<div class="section-header">
<h2>Canadian Tech for Canadian Campaigns</h2>
<p>Why trust your movement's future to foreign corporations?</p>
</div>
<div class="sovereignty-grid">
<div class="sovereignty-card">
<div class="sovereignty-icon">🇨🇦</div>
<h3>100% Canadian</h3>
<p>Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.</p>
</div>
<div class="sovereignty-card">
<div class="sovereignty-icon">🔐</div>
<h3>True Ownership</h3>
<p>Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.</p>
</div>
<div class="sovereignty-card">
<div class="sovereignty-icon">🛡️</div>
<h3>Privacy First</h3>
<p>Built to respect privacy from day one. Your supporters' data protected by design, not by policy.</p>
</div>
</div>
<div class="comparison-visual">
<h3>The Sovereignty Difference</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>US Corporate Platforms</h4>
<ul class="comparison-list bad">
<li>❌ Subject to Patriot Act</li>
<li>❌ NSA surveillance</li>
<li>❌ Corporate data mining</li>
<li>❌ Foreign jurisdiction</li>
</ul>
</div>
<div class="comparison-item">
<h4>Changemaker Lite</h4>
<ul class="comparison-list good">
<li>✅ Canadian sovereignty</li>
<li>✅ Zero surveillance</li>
<li>✅ Complete privacy</li>
<li>✅ Your servers, your rules</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Pricing Section -->
<section class="pricing-section" id="pricing">
<div class="section-content">
<div class="section-header">
<h2>Simple, Transparent Pricing</h2>
<p>No hidden fees. No usage limits. No surprises.</p>
</div>
<div class="pricing-grid">
<div class="pricing-card">
<div class="pricing-header">
<h3>Self-Hosted</h3>
<div class="price">$0</div>
<div class="price-period">forever</div>
</div>
<ul class="pricing-features">
<li>✓ All 11 campaign tools</li>
<li>✓ Unlimited users</li>
<li>✓ Unlimited data</li>
<li>✓ Complete documentation</li>
<li>✓ Community support</li>
<li>✓ Your infrastructure</li>
<li>✓ 100% open source</li>
</ul>
<a href="/build" class="btn-secondary">Installation Guide</a>
<p class="pricing-note">Perfect for tech-savvy campaigns</p>
</div>
<div class="pricing-card featured">
<div class="pricing-badge">Most Popular</div>
<div class="pricing-header">
<h3>Pre-Configured Server</h3>
<div class="price">Contact</div>
<div class="price-period">for pricing</div>
</div>
<ul class="pricing-features">
<li>✓ Everything in Self-Hosted</li>
<li>✓ Pre-installed & configured</li>
<li>✓ Hardware included</li>
<li>✓ 30-minute setup</li>
<li>✓ Training included</li>
<li>✓ Canadian support</li>
<li>✓ Ongoing updates</li>
</ul>
<a href="https://bnkops.com/hardware" class="btn-primary">Get a Quote</a>
<p class="pricing-note">Ready to use out of the box</p>
</div>
<div class="pricing-card">
<div class="pricing-header">
<h3>Managed Hosting</h3>
<div class="price">Custom</div>
<div class="price-period">monthly</div>
</div>
<ul class="pricing-features">
<li>✓ We handle everything</li>
<li>✓ Canadian data centers</li>
<li>✓ Daily backups</li>
<li>✓ 24/7 monitoring</li>
<li>✓ Security updates</li>
<li>✓ Priority support</li>
<li>✓ Still your data</li>
</ul>
<a href="https://bnkops.com/contact" class="btn-secondary">Contact Sales</a>
<p class="pricing-note">For larger campaigns</p>
</div>
</div>
<div class="cost-comparison">
<h3>Compare Your Savings</h3>
<p>Average campaign using corporate tools: <strong>$1,200-$4,000/month</strong></p>
<p>Same capabilities with Changemaker Lite: <strong>$0 (self-hosted)</strong></p>
<a href="/cost-comparison" class="comparison-link">See detailed cost breakdown →</a>
</div>
</div>
</section>
<!-- Integration Section -->
<section class="integration-section" id="integration">
<div class="section-content">
<div class="section-header">
<h2>Everything Works Together</h2>
<p>One login. One system. Infinite possibilities.</p>
</div>
<div class="integration-visual">
<div class="integration-diagram">
<div class="screenshot-placeholder">
<img src="assets/loop.png" alt="All systems communicate and build on one another" class="mobile-screenshot">
</div>
</div>
<p style="margin-top: 2rem; color: var(--text-light); font-size: 0.875rem;">
🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees
</p>
</div>
</div>
</section>
<!-- Live Examples Section -->
<section class="testimonial-section">
<div class="testimonial-content">
<h2>Real Sites, Real Results</h2>
<p style="text-align: center; margin-bottom: 3rem; color: var(--text-dark);">
Live sites powered by Changemaker Lite in production today
</p>
<div class="live-sites-grid">
<a href="https://pridecorner.ca/" target="_blank" class="site-card">
<div class="site-icon">🏳️‍🌈</div>
<div class="site-content">
<div class="site-name">Pride Corner</div>
<div class="site-desc">Community hub for LGBTQ+ advocacy and resources</div>
<div class="site-status">✓ Live</div>
</div>
</a>
<a href="https://publicinterestalberta.org/" target="_blank" class="site-card">
<div class="site-icon">🏛️</div>
<div class="site-content">
<div class="site-name">Public Interest Alberta</div>
<div class="site-desc">Policy advocacy and democratic engagement</div>
<div class="site-status">✓ Live</div>
</div>
</a>
<a href="https://lindalindsay.org/" target="_blank" class="site-card">
<div class="site-icon">🗳️</div>
<div class="site-content">
<div class="site-name">Linda Lindsay</div>
<div class="site-desc">Progressive political campaign platform</div>
<div class="site-status">✓ Live</div>
</div>
</a>
<a href="https://albertademocracytaskforce.org/" target="_blank" class="site-card">
<div class="site-icon">🤝</div>
<div class="site-content">
<div class="site-name">Alberta Democracy Taskforce</div>
<div class="site-desc">Defending democratic rights and freedoms</div>
<div class="site-status">✓ Live</div>
</div>
</a>
<a href="https://bnkops.com/" target="_blank" class="site-card featured-site">
<div class="site-badge">Platform Provider</div>
<div class="site-icon"></div>
<div class="site-content">
<div class="site-name">BNKops</div>
<div class="site-desc">Liberation technology and hosting services</div>
<div class="site-status">✓ Live</div>
</div>
</a>
</div>
<div class="proof-stats" style="margin-top: 3rem;">
<h3>Live Performance Metrics</h3>
<div class="stats-mini-grid">
<div class="mini-stat">
<div class="stat-number">5+</div>
<div class="stat-label">Live sites</div>
</div>
<div class="mini-stat">
<div class="stat-number">24/7</div>
<div class="stat-label">Uptime</div>
</div>
<div class="mini-stat">
<div class="stat-number">$0</div>
<div class="stat-label">Monthly fees</div>
</div>
<div class="mini-stat">
<div class="stat-number">100%</div>
<div class="stat-label">Data ownership</div>
</div>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="cta-section" id="get-started">
<div class="cta-content">
<h2>Ready to Power Up Your Campaign?</h2>
<p>Join hundreds of campaigns using open-source tools to win elections and save money.</p>
<div class="cta-buttons">
<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</a>
<a href="/build" class="btn-secondary">Read Documentation</a>
</div>
<p style="margin-top: 2rem; color: var(--text-light); font-size: 0.875rem;">
🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees
</p>
</div>
</section>
<script src="https://unpkg.com/lunr@2.3.9/lunr.min.js"></script>
<script>
// Inline MkDocs search implementation
class MkDocsSearch {
constructor() {
this.searchIndex = null;
this.searchDocs = null;
this.initialized = false;
this.debounceTimeout = null;
}
async initialize() {
try {
// Try to load search index
const response = await fetch('search/search_index.json');
if (!response.ok) throw new Error('Failed to load search index');
const searchData = await response.json();
// Build Lunr index
this.searchIndex = lunr(function() {
this.ref('location');
this.field('title', { boost: 10 });
this.field('text');
searchData.docs.forEach(doc => {
this.add(doc);
});
});
this.searchDocs = searchData.docs;
this.initialized = true;
return true;
} catch (error) {
console.error('Failed to initialize search:', error);
return false;
}
}
search(query) {
if (!this.initialized || !query || query.length < 2) return [];
try {
const results = this.searchIndex.search(query);
return results.slice(0, 10).map(result => {
const doc = this.searchDocs.find(d => d.location === result.ref);
if (!doc) return null;
return {
...doc,
score: result.score,
url: doc.location,
snippet: this.extractSnippet(doc.text, query)
};
}).filter(Boolean);
} catch (error) {
console.error('Search error:', error);
return [];
}
}
extractSnippet(text, query, maxLength = 150) {
const lowerText = text.toLowerCase();
const lowerQuery = query.toLowerCase();
const index = lowerText.indexOf(lowerQuery);
if (index === -1) {
return text.substring(0, maxLength) + '...';
}
const start = Math.max(0, index - 50);
const end = Math.min(text.length, index + query.length + 100);
let snippet = text.substring(start, end);
if (start > 0) snippet = '...' + snippet;
if (end < text.length) snippet = snippet + '...';
// Highlight the search term
const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
snippet = snippet.replace(regex, '<mark>$1</mark>');
return snippet;
}
}
// Initialize search when page loads
document.addEventListener('DOMContentLoaded', async () => {
const search = new MkDocsSearch();
const searchInput = document.getElementById('docs-search-input');
const searchResults = document.getElementById('docs-search-results');
const resultsContainer = searchResults.querySelector('.docs-search-results-list');
const resultsCount = searchResults.querySelector('.results-count');
const closeBtn = searchResults.querySelector('.close-results');
// Initialize search index
await search.initialize();
// Handle search input
searchInput.addEventListener('input', (e) => {
clearTimeout(search.debounceTimeout);
search.debounceTimeout = setTimeout(() => {
const query = e.target.value.trim();
if (!query || query.length < 2) {
searchResults.style.display = 'none';
return;
}
const results = search.search(query);
if (results.length === 0) {
resultsContainer.innerHTML = '<div class="no-results">No results found</div>';
resultsCount.textContent = 'No results';
} else {
resultsCount.textContent = `${results.length} result${results.length > 1 ? 's' : ''}`;
resultsContainer.innerHTML = results.map(result => `
<a href="${result.url}" class="search-result-item" style="text-decoration: none; display: block;">
<div class="search-result-title">${result.title}</div>
<div class="search-result-content">${result.snippet}</div>
</a>
`).join('');
}
searchResults.style.display = 'block';
}, 300);
});
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
searchInput.focus();
searchInput.select();
}
if (e.key === 'Escape' && searchResults.style.display !== 'none') {
searchResults.style.display = 'none';
searchInput.value = '';
searchInput.blur();
}
});
// Close button
closeBtn.addEventListener('click', () => {
searchResults.style.display = 'none';
searchInput.value = '';
});
// Click outside to close
document.addEventListener('click', (e) => {
if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
searchResults.style.display = 'none';
}
});
});
</script>
<script>
// Theme switcher logic
const themeToggle = document.getElementById('theme-toggle');
const body = document.body;
const applyTheme = (theme) => {
if (theme === 'dark') {
body.classList.add('dark');
themeToggle.checked = true;
} else {
body.classList.remove('dark');
themeToggle.checked = false;
}
};
// Check for saved theme in localStorage
const savedTheme = localStorage.getItem('theme');
// Check for system preference
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
applyTheme(savedTheme);
} else if (prefersDark) {
applyTheme('dark');
} else {
applyTheme('dark');
}
themeToggle.addEventListener('change', () => {
if (themeToggle.checked) {
applyTheme('dark');
localStorage.setItem('theme', 'dark');
} else {
applyTheme('light');
localStorage.setItem('theme', 'light');
}
});
// Add scroll effect to header
window.addEventListener('scroll', function() {
const header = document.querySelector('.header');
if (window.scrollY > 50) {
header.classList.add('scrolled');
} else {
header.classList.remove('scrolled');
}
});
// Smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Add intersection observer for fade-in animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe all cards and sections
document.querySelectorAll('.problem-card, .tool-card, .solution-showcase, .testimonial-card').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
</script>
</body>
</html>