2026-01-27 16:21:58 +00:00
---
2026-01-29 10:38:43 +00:00
export const prerender = false;
2026-01-27 16:21:58 +00:00
import PublicLayout from '../layouts/PublicLayout.astro';
import { API_BASE } from '../lib/api';
2026-01-29 17:41:52 +00:00
const nextParamRaw = Astro.url.searchParams.get('next') || '';
const nextParam = nextParamRaw.startsWith('/') ? nextParamRaw : '';
const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
2026-01-27 16:21:58 +00:00
---
<PublicLayout title="Demo" description="Explore Likwid's governance features with a live demo instance featuring pre-populated communities and real governance history.">
<article class="demo-page">
<header class="page-header">
<span class="page-label">Live Demo</span>
<h1>Experience Governance in Action</h1>
<p class="lead">
A governance platform cannot be understood from screenshots or empty interfaces.
Explore a live instance with real communities, ongoing decisions, and visible history.
</p>
</header>
2026-01-29 17:41:52 +00:00
${nextParam ? `
<div class="demo-status-banner" id="demo-continue">
<p>
You were trying to open <code>${nextParam.replace(/</g, '<')}</code>.
Enter the demo to continue.
</p>
</div>
` : ''}
2026-01-27 16:21:58 +00:00
<section class="demo-intro">
<div class="intro-content">
<h2>Why a Demo Instance?</h2>
<p>
Governance only makes sense when it has context. Decisions need history.
Delegation needs visible relationships. Moderation needs traceable events.
An empty system communicates nothing.
</p>
<p>
Our demo instance includes pre-seeded communities with:
</p>
<ul>
<li>Past, ongoing, and scheduled decisions</li>
<li>Active delegation networks</li>
<li>Visible moderation history</li>
<li>Multiple voting methods in use</li>
<li>Real governance narratives, not random data</li>
</ul>
</div>
</section>
<section class="demo-communities">
<h2>Demo Communities</h2>
<p class="section-intro">
Explore different types of organizations and see how they use Likwid for governance.
</p>
<div class="community-cards" id="community-cards">
<div class="community-card">
<div class="card-header">
<h3>Aurora Framework</h3>
<span class="card-tag">Open Source Project</span>
</div>
<p>
A fictional open source project demonstrating technical decision-making.
See RFC processes, feature prioritization, and maintainer elections using
Schulze voting and liquid delegation.
</p>
<div class="card-stats" id="stats-aurora">
<span class="loading-text">Loading...</span>
</div>
2026-01-29 10:38:43 +00:00
<a href="/demo?enter=1&next=/communities/aurora" class="card-link">Explore Aurora →</a>
2026-01-27 16:21:58 +00:00
</div>
<div class="community-card">
<div class="card-header">
<h3>Civic Commons Network</h3>
<span class="card-tag">Political Movement</span>
</div>
<p>
A grassroots civic organization showing policy development and delegate assemblies.
Observe quadratic voting for priority setting and transparent moderation
for community standards.
</p>
<div class="card-stats" id="stats-civic-commons">
<span class="loading-text">Loading...</span>
</div>
2026-01-29 10:38:43 +00:00
<a href="/demo?enter=1&next=/communities/civic-commons" class="card-link">Explore Civic Commons →</a>
2026-01-27 16:21:58 +00:00
</div>
<div class="community-card">
<div class="card-header">
<h3>Regional Makers Collective</h3>
<span class="card-tag">Federation</span>
</div>
<p>
A network of local chapters demonstrating federated governance.
See cross-community coordination, shared resources decisions,
and chapter autonomy in practice.
</p>
<div class="card-stats" id="stats-makers">
<span class="loading-text">Loading...</span>
</div>
2026-01-29 10:38:43 +00:00
<a href="/demo?enter=1&next=/communities/makers" class="card-link">Explore Makers →</a>
2026-01-27 16:21:58 +00:00
</div>
</div>
</section>
<section class="demo-access">
<h2>How to Explore</h2>
<div class="access-modes">
<div class="access-mode">
<div class="mode-icon">👁️</div>
<h4>Browse Freely</h4>
<p>
Navigate communities, read proposals, view voting results, and explore
delegation networks without any account. Full read access is public.
</p>
2026-01-29 10:38:43 +00:00
<a href="/demo?enter=1&next=/communities" class="mode-link">Browse Communities →</a>
2026-01-27 16:21:58 +00:00
</div>
<div class="access-mode">
<div class="mode-icon">🧪</div>
<h4>Demo Accounts</h4>
<p>
Click a demo account below to log in instantly and experience participation:
vote on proposals, create delegations, and see the member experience firsthand.
</p>
<div class="demo-accounts">
<button class="demo-login-btn" data-username="contributor">
<div class="btn-icon">👤</div>
<div class="btn-info">
<strong>Contributor</strong>
<span>Standard member - can vote and participate</span>
</div>
</button>
<button class="demo-login-btn" data-username="moderator">
<div class="btn-icon">🛡️</div>
<div class="btn-info">
<strong>Moderator</strong>
<span>Can moderate content and view logs</span>
</div>
</button>
<button class="demo-login-btn" data-username="observer">
<div class="btn-icon">👁️</div>
<div class="btn-info">
<strong>Observer</strong>
<span>Read-only access to explore</span>
</div>
</button>
</div>
<p class="demo-hint">All demo accounts use password: <code>demo123</code></p>
</div>
</div>
<div class="demo-notice">
<h4>Demo Reset Notice</h4>
<p>
The demo instance resets periodically to maintain a known state.
Any changes you make will be reverted. This ensures every visitor
experiences meaningful governance data, not accumulated noise.
</p>
</div>
</section>
<section class="demo-journey">
<h2>Suggested Exploration Path</h2>
<div class="journey-steps">
<div class="journey-step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Browse a Community</h4>
<p>Start with Aurora Framework. See the community overview, active members, and governance structure.</p>
</div>
</div>
<div class="journey-step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Read a Completed Decision</h4>
<p>Find a closed proposal. See the deliberation history, voting results, and how the outcome was reached.</p>
</div>
</div>
<div class="journey-step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Explore Delegations</h4>
<p>View the delegation network. See who trusts whom on which topics and how votes flow.</p>
</div>
</div>
<div class="journey-step">
<div class="step-number">4</div>
<div class="step-content">
<h4>Check the Moderation Log</h4>
<p>See transparent moderation in action. Every action has a reason, a rule reference, and accountability.</p>
</div>
</div>
<div class="journey-step">
<div class="step-number">5</div>
<div class="step-content">
<h4>Cast a Vote</h4>
<p>Log in with a demo account and vote on an active proposal. Experience different voting methods.</p>
</div>
</div>
</div>
</section>
<section class="demo-cta">
<h2>Ready to Explore?</h2>
<p>
The demo is live and waiting. No registration required to browse.
Use demo accounts to participate.
</p>
<div class="cta-buttons">
2026-01-29 17:41:52 +00:00
<a href={`/demo?enter=1${nextQuery || '&next=%2Fcommunities'}`} class="btn-primary btn-large">Enter the Demo</a>
<a href={`/demo?enter=1${nextQuery || '&next=%2Flogin'}`} class="btn-secondary">Continue</a>
2026-01-27 16:21:58 +00:00
</div>
</section>
<section class="demo-next">
<h2>What's Next?</h2>
<div class="next-options">
<a href="/about" class="next-card">
<h4>Learn More</h4>
<p>Understand what Likwid is, who it's for, and how it works.</p>
</a>
<a href="/manifesto" class="next-card">
<h4>Read the Manifesto</h4>
<p>Explore the technical and political vision behind Likwid.</p>
</a>
<a href="/docs" class="next-card">
<h4>Documentation</h4>
<p>Technical guides for deployment, configuration, and development.</p>
</a>
</div>
</section>
</article>
</PublicLayout>
<style>
.demo-page {
max-width: 1000px;
margin: 0 auto;
padding: 0 2rem;
}
.page-header {
text-align: center;
padding: 3rem 0 3.5rem;
border-bottom: 1px solid var(--color-border);
}
.page-label {
display: inline-block;
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--color-success);
background: var(--color-success-muted);
padding: 0.375rem 1rem;
border-radius: 999px;
margin-bottom: 1.25rem;
}
.page-header h1 {
font-size: 2.25rem;
font-weight: 700;
margin-bottom: 1rem;
letter-spacing: -0.02em;
}
.lead {
font-size: 1.25rem;
color: var(--color-text-muted);
max-width: 650px;
margin: 0 auto;
line-height: 1.6;
}
/* Intro Section */
.demo-intro {
padding: 3rem 0;
border-bottom: 1px solid var(--color-border);
}
.intro-content h2 {
font-size: 1.75rem;
margin-bottom: 1rem;
}
.intro-content p {
color: var(--color-text);
line-height: 1.7;
margin-bottom: 1rem;
}
.intro-content ul {
list-style: none;
padding: 0;
margin: 1.5rem 0 0;
}
.intro-content li {
padding: 0.5rem 0 0.5rem 1.5rem;
position: relative;
color: var(--color-text);
}
.intro-content li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--color-success);
font-weight: 600;
}
/* Communities Section */
.demo-communities {
padding: 3rem 0;
border-bottom: 1px solid var(--color-border);
}
.demo-communities h2 {
font-size: 1.75rem;
text-align: center;
margin-bottom: 0.75rem;
}
.section-intro {
text-align: center;
color: var(--color-text-muted);
margin-bottom: 2rem;
}
.community-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
@media (max-width: 900px) {
.community-cards {
grid-template-columns: 1fr;
}
}
.community-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 1.5rem;
transition: all var(--motion-normal) var(--easing-standard);
}
.community-card:hover {
border-color: var(--color-border-hover);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.card-header {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
}
.card-header h3 {
font-size: 1.25rem;
margin: 0;
}
.card-tag {
display: inline-block;
font-size: 0.75rem;
padding: 0.25rem 0.75rem;
background: var(--color-primary-muted);
color: var(--color-primary);
border-radius: 999px;
font-weight: 500;
width: fit-content;
}
.community-card p {
color: var(--color-text-muted);
font-size: 0.9375rem;
line-height: 1.5;
margin-bottom: 1rem;
}
.card-stats {
display: flex;
gap: 1rem;
padding-top: 1rem;
border-top: 1px solid var(--color-border);
font-size: 0.8125rem;
color: var(--color-text-muted);
}
/* Access Section */
.demo-access {
padding: 3rem 0;
border-bottom: 1px solid var(--color-border);
}
.demo-access h2 {
font-size: 1.75rem;
text-align: center;
margin-bottom: 2rem;
}
.access-modes {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
margin-bottom: 2rem;
}
@media (max-width: 768px) {
.access-modes {
grid-template-columns: 1fr;
}
}
.access-mode {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 2rem;
}
.mode-icon {
font-size: 2rem;
margin-bottom: 1rem;
}
.access-mode h4 {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
.access-mode p {
color: var(--color-text-muted);
line-height: 1.6;
margin-bottom: 1rem;
}
.mode-link {
display: inline-block;
font-weight: 500;
}
.demo-accounts {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.demo-login-btn {
display: flex;
align-items: center;
gap: 1rem;
width: 100%;
padding: 1rem 1.25rem;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--motion-fast) var(--easing-standard);
text-align: left;
}
.demo-login-btn:hover {
border-color: var(--color-primary);
background: var(--color-primary-muted);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(129, 140, 248, 0.15);
}
.demo-login-btn:active {
transform: translateY(0);
}
.demo-login-btn .btn-icon {
font-size: 1.5rem;
width: 2.5rem;
text-align: center;
}
.demo-login-btn .btn-info {
flex: 1;
}
.demo-login-btn .btn-info strong {
display: block;
font-size: 1rem;
color: var(--color-text);
margin-bottom: 0.25rem;
}
.demo-login-btn .btn-info span {
font-size: 0.8125rem;
color: var(--color-text-muted);
}
.demo-login-btn.loading {
opacity: 0.7;
pointer-events: none;
}
.demo-hint {
font-size: 0.8125rem;
color: var(--color-text-muted);
text-align: center;
margin-top: 0.5rem;
}
.demo-hint code {
background: var(--color-bg-alt);
padding: 0.125rem 0.5rem;
border-radius: var(--radius-sm);
font-family: monospace;
}
.demo-notice {
background: var(--color-warning-muted);
border: 1px solid var(--color-warning);
border-radius: var(--radius-md);
padding: 1.5rem;
text-align: center;
}
.demo-notice h4 {
font-size: 1rem;
margin-bottom: 0.5rem;
color: var(--color-warning);
}
.demo-notice p {
color: var(--color-text-muted);
font-size: 0.9375rem;
margin: 0;
}
/* Journey Section */
.demo-journey {
padding: 3rem 0;
border-bottom: 1px solid var(--color-border);
}
.demo-journey h2 {
font-size: 1.75rem;
text-align: center;
margin-bottom: 2rem;
}
.journey-steps {
display: flex;
flex-direction: column;
gap: 1rem;
}
.journey-step {
display: flex;
gap: 1.5rem;
align-items: flex-start;
padding: 1.5rem;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
}
.step-number {
width: 36px;
height: 36px;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
color: var(--color-on-primary);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.875rem;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(129, 140, 248, 0.25);
}
.step-content h4 {
font-size: 1rem;
margin-bottom: 0.25rem;
}
.step-content p {
color: var(--color-text-muted);
font-size: 0.9375rem;
margin: 0;
}
/* CTA Section */
.demo-cta {
text-align: center;
padding: 4rem 0;
border-bottom: 1px solid var(--color-border);
}
.demo-cta h2 {
font-size: 2rem;
margin-bottom: 1rem;
}
.demo-cta p {
color: var(--color-text-muted);
margin-bottom: 2rem;
}
.cta-buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: var(--color-text-inverse);
padding: 0.875rem 1.75rem;
border-radius: var(--radius-md);
font-weight: 600;
font-size: 0.9375rem;
transition: all var(--motion-fast) var(--easing-standard);
}
.btn-primary:hover {
background: var(--color-primary-hover);
color: var(--color-text-inverse);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(129, 140, 248, 0.3);
}
.btn-primary.btn-large {
padding: 1rem 2.5rem;
font-size: 1.125rem;
}
.btn-secondary {
display: inline-block;
background: transparent;
color: var(--color-text);
padding: 0.875rem 2rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-weight: 500;
transition: all var(--motion-fast) var(--easing-standard);
}
.btn-secondary:hover {
border-color: var(--color-primary);
color: var(--color-primary);
}
/* Next Section */
.demo-next {
padding: 3rem 0;
}
.demo-next h2 {
font-size: 1.75rem;
text-align: center;
margin-bottom: 2rem;
}
.next-options {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
@media (max-width: 768px) {
.next-options {
grid-template-columns: 1fr;
}
}
.next-card {
display: block;
padding: 1.5rem;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
text-decoration: none;
transition: all var(--motion-fast) var(--easing-standard);
}
.next-card:hover {
border-color: var(--color-primary);
transform: translateY(-2px);
}
.next-card h4 {
font-size: 1.125rem;
color: var(--color-text);
margin-bottom: 0.5rem;
}
.next-card p {
color: var(--color-text-muted);
font-size: 0.875rem;
margin: 0;
}
.card-link {
display: inline-block;
margin-top: 1rem;
font-weight: 500;
font-size: 0.875rem;
color: var(--color-primary);
}
.card-link:hover {
text-decoration: underline;
}
.loading-text {
color: var(--color-text-muted);
font-style: italic;
}
.demo-status-banner {
background: var(--color-success-muted);
border: 1px solid var(--color-success);
border-radius: var(--radius-md);
padding: 1rem 1.5rem;
margin-bottom: 2rem;
text-align: center;
}
.demo-status-banner.inactive {
background: var(--color-warning-muted);
border-color: var(--color-warning);
}
.demo-status-banner p {
margin: 0;
font-size: 0.9375rem;
}
</style>
<script define:vars={{ API_BASE }}>
2026-01-29 17:41:52 +00:00
const currentUrl = new URL(window.location.href);
const nextParam = currentUrl.searchParams.get('next') || '';
const nextPath = nextParam && nextParam.startsWith('/') ? nextParam : '';
2026-01-27 16:21:58 +00:00
async function loadDemoData() {
try {
// Fetch demo status
const statusRes = await fetch(API_BASE + '/api/demo/status');
const status = await statusRes.json();
// Update demo status indicator if needed
const statusBanner = document.getElementById('demo-status');
if (statusBanner) {
if (status.demo_mode) {
statusBanner.innerHTML = '<p>✓ Demo mode is active. Explore freely!</p>';
statusBanner.classList.remove('inactive');
} else {
statusBanner.innerHTML = '<p>Demo mode is not currently enabled on this instance.</p>';
statusBanner.classList.add('inactive');
}
}
// Fetch communities data
const commRes = await fetch(API_BASE + '/api/demo/communities');
const commData = await commRes.json();
if (commData.communities && commData.communities.length > 0) {
// Map slugs to stat element IDs
const slugToId = {
'aurora': 'stats-aurora',
'civic-commons': 'stats-civic-commons',
'makers': 'stats-makers'
};
commData.communities.forEach(function(comm) {
const statsEl = document.getElementById(slugToId[comm.slug]);
if (statsEl) {
statsEl.innerHTML = '<span>' + comm.member_count + ' members</span><span>' + comm.proposal_count + ' proposals</span>';
}
});
}
} catch (error) {
console.log('Demo API not available:', error.message);
// Show fallback stats
['stats-aurora', 'stats-civic-commons', 'stats-makers'].forEach(function(id) {
const el = document.getElementById(id);
if (el) {
el.innerHTML = '<span>View community for details</span>';
}
});
}
}
// Demo login handler
2026-01-29 10:38:43 +00:00
async function loginDemoUser(username) {
2026-01-27 16:21:58 +00:00
const btn = document.querySelector('[data-username="' + username + '"]');
if (btn) {
btn.classList.add('loading');
btn.querySelector('.btn-info span').textContent = 'Logging in...';
}
try {
const response = await fetch(API_BASE + '/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: 'demo123'
}),
});
if (!response.ok) {
throw new Error('Login failed');
}
2026-01-29 10:38:43 +00:00
const result = await response.json();
localStorage.setItem('token', result.token);
localStorage.setItem('user', JSON.stringify(result.user));
2026-01-29 17:41:52 +00:00
const nextTarget = nextPath || '/communities';
window.location.href = '/demo?enter=1&next=' + encodeURIComponent(nextTarget);
2026-01-27 16:21:58 +00:00
} catch (error) {
console.error('Demo login failed:', error);
if (btn) {
btn.classList.remove('loading');
btn.querySelector('.btn-info span').textContent = 'Login failed - try again';
setTimeout(() => {
// Reset button text
const texts = {
'contributor': 'Standard member - can vote and participate',
'moderator': 'Can moderate content and view logs',
'observer': 'Read-only access to explore'
};
btn.querySelector('.btn-info span').textContent = texts[username] || '';
}, 2000);
}
2026-01-29 10:38:43 +00:00
alert('Demo login failed. Please try again.');
2026-01-27 16:21:58 +00:00
}
}
// Attach click handlers to demo login buttons
function setupDemoLoginButtons() {
var buttons = document.querySelectorAll('.demo-login-btn');
for (var i = 0; i < buttons.length; i++) {
(function(btn) {
btn.addEventListener('click', function() {
var username = btn.dataset.username;
if (username) {
2026-01-29 10:38:43 +00:00
loginDemoUser(username);
2026-01-27 16:21:58 +00:00
}
});
})(buttons[i]);
}
}
// Load on page ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
loadDemoData();
setupDemoLoginButtons();
});
} else {
loadDemoData();
setupDemoLoginButtons();
}
</script>