1798 lines
78 KiB
HTML
1798 lines
78 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Apply - Valley of the Commons</title>
|
||
<link rel="icon" type="image/svg+xml" href="icon.svg">
|
||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,400&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root {
|
||
--forest: #2d5016;
|
||
--forest-light: #4a7c23;
|
||
--cream: #faf8f5;
|
||
--sand: #f5f5f0;
|
||
--charcoal: #2c2c2c;
|
||
--error: #c53030;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: 'Inter', -apple-system, sans-serif;
|
||
background: var(--cream);
|
||
color: var(--charcoal);
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.header {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||
padding: 1rem 2rem;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
}
|
||
|
||
.header-content {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-family: 'Cormorant Garamond', serif;
|
||
font-size: 1.5rem;
|
||
font-weight: 400;
|
||
color: var(--forest);
|
||
}
|
||
|
||
.header a { color: var(--forest); text-decoration: none; }
|
||
|
||
.container {
|
||
max-width: 700px;
|
||
margin: 0 auto;
|
||
padding: 2rem;
|
||
}
|
||
|
||
/* Progress */
|
||
.progress-container {
|
||
margin-bottom: 2rem;
|
||
display: none;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 4px;
|
||
background: #ddd;
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: var(--forest);
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.progress-text {
|
||
text-align: center;
|
||
font-size: 0.8rem;
|
||
color: #666;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
/* Form */
|
||
.form-section {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 2rem;
|
||
margin-bottom: 1rem;
|
||
display: none;
|
||
}
|
||
|
||
.form-section.active { display: block; animation: fadeIn 0.3s; }
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(10px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.question-number {
|
||
font-size: 0.75rem;
|
||
color: #999;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.form-section h2 {
|
||
font-family: 'Cormorant Garamond', serif;
|
||
font-size: 1.5rem;
|
||
color: var(--forest);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.form-section > p.hint {
|
||
color: #666;
|
||
font-size: 0.9rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.form-group { margin-bottom: 1.25rem; }
|
||
|
||
label {
|
||
display: block;
|
||
font-weight: 500;
|
||
margin-bottom: 0.5rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
label .required { color: var(--error); }
|
||
label .optional { color: #999; font-weight: 400; font-size: 0.8rem; }
|
||
|
||
input[type="text"],
|
||
input[type="email"],
|
||
textarea,
|
||
select {
|
||
width: 100%;
|
||
padding: 0.75rem 1rem;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
font-family: inherit;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
input:focus, textarea:focus, select:focus {
|
||
outline: none;
|
||
border-color: var(--forest);
|
||
box-shadow: 0 0 0 3px rgba(45, 80, 22, 0.1);
|
||
}
|
||
|
||
textarea { min-height: 120px; resize: vertical; }
|
||
|
||
.field-hint {
|
||
font-size: 0.8rem;
|
||
color: #666;
|
||
margin-top: 0.25rem;
|
||
}
|
||
|
||
/* Week cards */
|
||
.week-cards { display: flex; flex-direction: column; gap: 0.75rem; }
|
||
|
||
.week-card {
|
||
border: 2px solid #ddd;
|
||
border-radius: 10px;
|
||
padding: 1rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.week-card:hover { border-color: var(--forest-light); }
|
||
.week-card.selected { border-color: var(--forest); background: rgba(45, 80, 22, 0.05); }
|
||
.week-card input[type="checkbox"],
|
||
.week-card input[type="radio"] { display: none; }
|
||
|
||
.week-card h4 { font-size: 0.95rem; color: var(--forest); margin-bottom: 0.25rem; }
|
||
.week-card .dates { font-size: 0.8rem; color: #666; margin-bottom: 0.5rem; }
|
||
.week-card .desc { font-size: 0.85rem; color: #555; }
|
||
|
||
/* Select-all card */
|
||
.select-all-card {
|
||
border-style: dashed;
|
||
background: var(--sand);
|
||
}
|
||
.select-all-card.selected {
|
||
border-style: solid;
|
||
}
|
||
|
||
.ticket-note {
|
||
font-size: 0.8rem;
|
||
color: #666;
|
||
background: var(--sand);
|
||
padding: 0.75rem 1rem;
|
||
border-radius: 8px;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
/* Nav */
|
||
.form-nav {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
.btn {
|
||
padding: 0.875rem 2rem;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-family: inherit;
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-primary { background: var(--forest); color: white; }
|
||
.btn-primary:hover { background: var(--forest-light); }
|
||
.btn-secondary { background: var(--sand); color: var(--charcoal); }
|
||
.btn-secondary:hover { background: #eee; }
|
||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
|
||
/* Error */
|
||
.error-message {
|
||
background: #fef2f2;
|
||
border: 1px solid #fecaca;
|
||
color: var(--error);
|
||
padding: 1rem;
|
||
border-radius: 8px;
|
||
margin-bottom: 1rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.field-error { border-color: var(--error) !important; }
|
||
|
||
.footer {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
color: #666;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.footer a { color: var(--forest); }
|
||
|
||
/* ===== Landing screen ===== */
|
||
.landing {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
background: white;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.landing h1 {
|
||
font-family: 'Cormorant Garamond', serif;
|
||
font-size: 2.25rem;
|
||
color: var(--forest);
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.landing .event-badge {
|
||
display: inline-block;
|
||
background: var(--forest);
|
||
color: white;
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 20px;
|
||
font-size: 0.875rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.landing .overview-text {
|
||
color: #666;
|
||
font-size: 0.95rem;
|
||
max-width: 550px;
|
||
margin: 0 auto 2rem;
|
||
}
|
||
|
||
.pricing-cards {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 1rem;
|
||
margin: 2rem 0;
|
||
text-align: left;
|
||
}
|
||
|
||
.pricing-card {
|
||
background: var(--sand);
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
}
|
||
|
||
.pricing-card h3 {
|
||
font-size: 1rem;
|
||
color: var(--forest);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.pricing-card .price-range {
|
||
font-size: 1.25rem;
|
||
font-weight: 600;
|
||
color: var(--charcoal);
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.pricing-card .price-note {
|
||
font-size: 0.8rem;
|
||
color: #666;
|
||
}
|
||
|
||
.time-estimate {
|
||
background: var(--sand);
|
||
padding: 0.75rem 1rem;
|
||
border-radius: 8px;
|
||
font-size: 0.85rem;
|
||
color: #666;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.resume-notice {
|
||
background: #e8f5e9;
|
||
border: 1px solid #c8e6c9;
|
||
color: #2d5016;
|
||
padding: 0.75rem 1rem;
|
||
border-radius: 8px;
|
||
margin-bottom: 1rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* ===== Theme picker ===== */
|
||
.theme-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.theme-option {
|
||
border: 2px solid #ddd;
|
||
border-radius: 10px;
|
||
padding: 1rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
text-align: center;
|
||
}
|
||
|
||
.theme-option:hover { border-color: var(--forest-light); }
|
||
.theme-option.selected { border-color: var(--forest); background: rgba(45, 80, 22, 0.05); }
|
||
.theme-option.disabled-theme { opacity: 0.4; cursor: not-allowed; }
|
||
|
||
.theme-option h4 {
|
||
font-size: 0.9rem;
|
||
color: var(--forest);
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.theme-counter {
|
||
text-align: center;
|
||
font-size: 0.85rem;
|
||
color: #666;
|
||
margin-top: 0.75rem;
|
||
}
|
||
|
||
/* ===== Food radio group ===== */
|
||
.radio-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.radio-option {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.radio-option input[type="radio"] {
|
||
width: 18px;
|
||
height: 18px;
|
||
accent-color: var(--forest);
|
||
}
|
||
|
||
/* ===== Review step ===== */
|
||
.review-section {
|
||
background: var(--sand);
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.review-section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
|
||
.review-section-header h3 {
|
||
font-size: 1rem;
|
||
color: var(--forest);
|
||
}
|
||
|
||
.review-edit-link {
|
||
font-size: 0.85rem;
|
||
color: var(--forest);
|
||
cursor: pointer;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.review-field {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.review-field .review-label {
|
||
font-size: 0.75rem;
|
||
color: #666;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.review-field .review-value {
|
||
font-size: 0.9rem;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.review-price-summary {
|
||
background: white;
|
||
border: 2px solid var(--forest);
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
margin-top: 1.5rem;
|
||
}
|
||
|
||
.review-price-summary h3 {
|
||
font-size: 1.1rem;
|
||
color: var(--forest);
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
|
||
.price-line {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 0.25rem 0;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.price-line.total {
|
||
border-top: 1px solid #ddd;
|
||
margin-top: 0.5rem;
|
||
padding-top: 0.75rem;
|
||
font-weight: 600;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
/* ===== Success state ===== */
|
||
.success-card {
|
||
text-align: center;
|
||
padding: 2.5rem 2rem;
|
||
background: linear-gradient(135deg, #f0f7eb 0%, #e8f5e9 50%, #f5f5f0 100%);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.success-icon {
|
||
width: 70px;
|
||
height: 70px;
|
||
background: var(--forest);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 1.5rem;
|
||
}
|
||
|
||
.success-icon svg { width: 35px; height: 35px; stroke: white; }
|
||
|
||
.success-card h2 {
|
||
font-family: 'Cormorant Garamond', serif;
|
||
font-size: 1.75rem;
|
||
color: var(--forest);
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.next-steps-box {
|
||
background: white;
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
margin: 1.5rem auto;
|
||
max-width: 420px;
|
||
text-align: left;
|
||
}
|
||
|
||
.next-steps-box h3 {
|
||
font-size: 0.95rem;
|
||
color: var(--forest);
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
|
||
.next-steps-box ol {
|
||
padding-left: 1.25rem;
|
||
font-size: 0.9rem;
|
||
color: #555;
|
||
}
|
||
|
||
.next-steps-box li { margin-bottom: 0.5rem; }
|
||
|
||
/* ===== Welcome-back modal ===== */
|
||
.modal-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
z-index: 200;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.modal-overlay.visible { display: flex; }
|
||
.modal-box {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 2rem;
|
||
max-width: 480px;
|
||
width: 90%;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
||
text-align: center;
|
||
}
|
||
.modal-box h3 {
|
||
font-family: 'Cormorant Garamond', serif;
|
||
color: var(--forest);
|
||
font-size: 1.4rem;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
.modal-box p { font-size: 0.95rem; color: #555; margin-bottom: 1.5rem; }
|
||
.modal-actions { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; }
|
||
.modal-actions .btn { min-width: 160px; }
|
||
.btn-secondary {
|
||
display: inline-block;
|
||
padding: 0.75rem 2rem;
|
||
border: 2px solid var(--forest);
|
||
color: var(--forest);
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
}
|
||
.btn-secondary:hover { background: rgba(45, 80, 22, 0.05); }
|
||
|
||
@media (max-width: 600px) {
|
||
.container { padding: 1rem; }
|
||
.form-section, .landing { padding: 1.5rem; }
|
||
.pricing-cards { grid-template-columns: 1fr; }
|
||
.theme-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="header">
|
||
<div class="header-content">
|
||
<h1><a href="/">Valley of the Commons</a></h1>
|
||
<a href="/">← Back</a>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="container">
|
||
<!-- Landing Screen -->
|
||
<div class="landing" id="landing-screen">
|
||
<span class="event-badge">August 24 – September 20, 2026</span>
|
||
<h1>Application Form</h1>
|
||
<p class="overview-text">Valley of the Commons is a four-week pop-up village exploring housing, production, decision-making and ownership in community. Each week has a different theme — attend as many weeks as you like.</p>
|
||
|
||
<div class="pricing-cards">
|
||
<div class="pricing-card">
|
||
<h3>Registration</h3>
|
||
<div class="price-range" id="landing-reg-price"></div>
|
||
<div class="price-note">per week, depending on timing</div>
|
||
</div>
|
||
<div class="pricing-card">
|
||
<h3>Accommodation</h3>
|
||
<div class="price-range">€275 – €700/wk</div>
|
||
<div class="price-note">optional, multiple room types</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="time-estimate">This application takes approximately 15–20 minutes to complete.</div>
|
||
|
||
<div id="resume-notice" class="resume-notice" style="display: none;">
|
||
You have saved progress from a previous session.
|
||
<a href="#" onclick="startForm(true); return false;" style="color: var(--forest); font-weight: 600;">Resume where you left off</a>
|
||
</div>
|
||
|
||
<button class="btn btn-primary" onclick="startForm(false)" style="font-size: 1.1rem; padding: 1rem 3rem;">Begin Application</button>
|
||
</div>
|
||
|
||
<div class="progress-container" id="progress-container">
|
||
<div class="progress-bar"><div class="progress-fill" id="progress-fill"></div></div>
|
||
<div class="progress-text">Step <span id="progress-step">1</span> of 10 — <span id="progress-percent">10</span>%</div>
|
||
</div>
|
||
|
||
<!-- Welcome-back modal -->
|
||
<div class="modal-overlay" id="welcome-back-modal">
|
||
<div class="modal-box">
|
||
<h3 id="welcome-back-heading">Welcome back!</h3>
|
||
<p>We found your previous application. Would you like to load it and continue where you left off?</p>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn btn-primary" onclick="loadExistingApplication()">Load my application</button>
|
||
<button type="button" class="btn btn-secondary" onclick="closeWelcomeModal()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<form id="application-form" style="display: none;">
|
||
<!-- Step 1: Which weeks + price calculator -->
|
||
<div class="form-section" data-step="1">
|
||
<div class="question-number">Step 1 of 10</div>
|
||
<h2>Which week(s) would you like to attend? <span class="required">*</span></h2>
|
||
<p class="hint">Select the weeks you'd like to join. Prices update as you choose.</p>
|
||
|
||
<div class="week-cards">
|
||
<label class="week-card select-all-card" onclick="toggleAllWeeks(this)">
|
||
<input type="checkbox" id="select-all-weeks">
|
||
<h4>Select all 4 weeks</h4>
|
||
<div class="desc">Full residency: August 24 – September 20, 2026</div>
|
||
</label>
|
||
|
||
<label class="week-card" onclick="toggleWeek(this)">
|
||
<input type="checkbox" name="weeks" value="week1">
|
||
<h4>Week 1: Return to the Commons</h4>
|
||
<div class="dates">August 24 – 30, 2026</div>
|
||
<div class="desc">A five-day course with Michel Bauwens and Adam Arvidsson exploring the history and future of the commons.</div>
|
||
</label>
|
||
|
||
<label class="week-card" onclick="toggleWeek(this)">
|
||
<input type="checkbox" name="weeks" value="week2">
|
||
<h4>Week 2: Post-Capitalist Production</h4>
|
||
<div class="dates">August 31 – September 6, 2026</div>
|
||
<div class="desc">How global knowledge commons and local production can sustain livelihoods and community resilience.</div>
|
||
</label>
|
||
|
||
<label class="week-card" onclick="toggleWeek(this)">
|
||
<input type="checkbox" name="weeks" value="week3">
|
||
<h4>Week 3: Future Living</h4>
|
||
<div class="dates">September 7 – 13, 2026</div>
|
||
<div class="desc">From vision to scouting: cooperative housing, mapping local resources, ecological design.</div>
|
||
</label>
|
||
|
||
<label class="week-card" onclick="toggleWeek(this)">
|
||
<input type="checkbox" name="weeks" value="week4">
|
||
<h4>Week 4: Governance & Funding Models</h4>
|
||
<div class="dates">September 14 – 20, 2026</div>
|
||
<div class="desc">Participatory governance, cooperative legal structures, and mechanisms for shared assets.</div>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Price summary -->
|
||
<div id="price-summary" class="ticket-note" style="margin-top: 1.5rem;">
|
||
<div id="price-calc">Select at least one week to see pricing</div>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<div></div>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2: Contact info + how heard + referral -->
|
||
<div class="form-section" data-step="2">
|
||
<div class="question-number">Step 2 of 10</div>
|
||
<h2>About You</h2>
|
||
|
||
<div class="form-group" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
|
||
<div>
|
||
<label for="first_name">First Name <span class="required">*</span></label>
|
||
<input type="text" id="first_name" name="first_name" required placeholder="First name">
|
||
</div>
|
||
<div>
|
||
<label for="last_name">Last Name <span class="required">*</span></label>
|
||
<input type="text" id="last_name" name="last_name" required placeholder="Last name">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="email">Email <span class="required">*</span></label>
|
||
<input type="email" id="email" name="email" required placeholder="your@email.com">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="social_media">Social Media Handles <span class="optional">(optional)</span></label>
|
||
<input type="text" id="social_media" name="social_media" placeholder="@handle (please specify which platforms)">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="how_heard">How did you hear about Valley of the Commons? <span class="required">*</span></label>
|
||
<textarea id="how_heard" name="how_heard" required placeholder="Social media, friend referral, newsletter, event..." rows="3"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="referral_names">Referral name(s) <span class="optional">(optional)</span></label>
|
||
<textarea id="referral_names" name="referral_names" placeholder="Names of people who know you or referred you" rows="2"></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 3: Affiliations -->
|
||
<div class="form-section" data-step="3">
|
||
<div class="question-number">Step 3 of 10</div>
|
||
<h2>What are your affiliations? <span class="required">*</span></h2>
|
||
<p class="hint">What projects or groups are you affiliated with?</p>
|
||
|
||
<div class="form-group">
|
||
<textarea id="affiliations" name="affiliations" required placeholder="Organizations, DAOs, cooperatives, communities, projects..."></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 4: Why join + good fit -->
|
||
<div class="form-section" data-step="4">
|
||
<div class="question-number">Step 4 of 10</div>
|
||
<h2>Why would you like to join Valley of the Commons, and why are you a good fit? <span class="required">*</span></h2>
|
||
|
||
<div class="form-group">
|
||
<textarea id="why_join" name="why_join" required placeholder="What draws you to this gathering? Why are you a good fit for this community?"></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 5: Current work -->
|
||
<div class="form-section" data-step="5">
|
||
<div class="question-number">Step 5 of 10</div>
|
||
<h2>What are you currently building, researching, or working on? <span class="required">*</span></h2>
|
||
|
||
<div class="form-group">
|
||
<textarea id="current_work" name="current_work" required placeholder="Tell us about your current projects, research, or focus areas..."></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 6: How you'll contribute -->
|
||
<div class="form-section" data-step="6">
|
||
<div class="question-number">Step 6 of 10</div>
|
||
<h2>How will you contribute to Valley of the Commons? <span class="required">*</span></h2>
|
||
<p class="hint">Villagers co-create their experience. You can start an interest club, lead a discussion or workshop, teach a cooking class, or more.</p>
|
||
|
||
<div class="form-group">
|
||
<textarea id="contribution" name="contribution" required placeholder="What skills, energy, workshops, or perspectives will you bring?"></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 7: Top 3 themes + familiarity -->
|
||
<div class="form-section" data-step="7">
|
||
<div class="question-number">Step 7 of 10</div>
|
||
<h2>Which themes interest you most?</h2>
|
||
<p class="hint">Pick up to 3 themes you're most drawn to.</p>
|
||
|
||
<div class="theme-grid">
|
||
<div class="theme-option" onclick="toggleTheme(this)" data-theme="commons">
|
||
<h4>The Commons</h4>
|
||
<div class="desc" style="font-size: 0.8rem; color: #666;">History, theory, and practice of shared resources</div>
|
||
</div>
|
||
<div class="theme-option" onclick="toggleTheme(this)" data-theme="production">
|
||
<h4>Post-Capitalist Production</h4>
|
||
<div class="desc" style="font-size: 0.8rem; color: #666;">Alternative economies and cooperative production</div>
|
||
</div>
|
||
<div class="theme-option" onclick="toggleTheme(this)" data-theme="living">
|
||
<h4>Future Living</h4>
|
||
<div class="desc" style="font-size: 0.8rem; color: #666;">Housing, ecological design, and community building</div>
|
||
</div>
|
||
<div class="theme-option" onclick="toggleTheme(this)" data-theme="governance">
|
||
<h4>Governance & Funding</h4>
|
||
<div class="desc" style="font-size: 0.8rem; color: #666;">Decision-making, legal structures, and shared assets</div>
|
||
</div>
|
||
</div>
|
||
<div class="theme-counter" id="theme-counter">0 of 3 selected</div>
|
||
|
||
<div class="form-group" style="margin-top: 1.5rem;">
|
||
<label for="themes_familiarity">How familiar are you with these topics? <span class="optional">(optional)</span></label>
|
||
<textarea id="themes_familiarity" name="themes_familiarity" placeholder="Tell us a bit about your background with these themes — it's okay if you're new to them!" rows="3"></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 8: Belief update -->
|
||
<div class="form-section" data-step="8">
|
||
<div class="question-number">Step 8 of 10</div>
|
||
<h2>What's a belief you've recently updated or changed your mind about? <span class="required">*</span></h2>
|
||
<p class="hint">We value intellectual curiosity and the ability to update one's thinking. This can be about anything — work, life, politics, a personal habit.</p>
|
||
|
||
<div class="form-group">
|
||
<textarea id="belief_update" name="belief_update" required placeholder="Share something you've changed your mind about and what prompted the change..."></textarea>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 9: Accommodation + food + accessibility + volunteer + coupon + anything else + privacy -->
|
||
<div class="form-section" data-step="9">
|
||
<div class="question-number">Step 9 of 10</div>
|
||
<h2>Practical Details</h2>
|
||
|
||
<!-- Accommodation -->
|
||
<h3 style="font-family: 'Cormorant Garamond', serif; font-size: 1.25rem; color: var(--forest); margin-bottom: 1rem;">Accommodation</h3>
|
||
|
||
<label class="week-card" onclick="toggleAddon(this, 'accommodation')" style="margin-bottom: 0.75rem;">
|
||
<input type="checkbox" id="need_accommodation">
|
||
<h4>Include accommodation with registration</h4>
|
||
<div class="desc">On-site housing paid upfront with your registration fee.</div>
|
||
</label>
|
||
|
||
<div id="accommodation-options" style="display: none;">
|
||
<!-- Venue selection -->
|
||
<div style="padding: 0.75rem 0; margin-bottom: 0.5rem;">
|
||
<label style="font-weight: 600; margin-bottom: 0.75rem; display: block;">Choose your venue</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectVenue('ch')">
|
||
<input type="radio" name="venue" value="ch">
|
||
<h4>Commons Hub</h4>
|
||
<div class="desc">Shared community living in the main hub building.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="cursor: pointer;" onclick="selectVenue('hh')">
|
||
<input type="radio" name="venue" value="hh">
|
||
<h4>Herrnhof Villa</h4>
|
||
<div class="desc">Private villa rooms with more comfort and privacy.</div>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Commons Hub room types -->
|
||
<div id="ch-rooms" style="display: none; padding: 0.75rem 0;">
|
||
<label style="font-weight: 600; margin-bottom: 0.75rem; display: block;">Room type — Commons Hub</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectRoom('ch-multi')">
|
||
<input type="radio" name="room_type" value="ch-multi">
|
||
<h4>Bed in Multi-Room <span style="float:right; color: var(--forest); font-weight: 600;">€275/wk</span></h4>
|
||
<div class="desc">Bed in a shared multi-bed room. The most affordable option.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="cursor: pointer;" onclick="selectRoom('ch-double')">
|
||
<input type="radio" name="room_type" value="ch-double">
|
||
<h4>Bed in Double Room <span style="float:right; color: var(--forest); font-weight: 600;">€350/wk</span></h4>
|
||
<div class="desc">Shared with one other person.</div>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Herrnhof room types -->
|
||
<div id="hh-rooms" style="display: none; padding: 0.75rem 0;">
|
||
<label style="font-weight: 600; margin-bottom: 0.75rem; display: block;">Room type — Herrnhof Villa</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectRoom('hh-living')">
|
||
<input type="radio" name="room_type" value="hh-living">
|
||
<h4>Bed in Living Room <span style="float:right; color: var(--forest); font-weight: 600;">€315/wk</span></h4>
|
||
<div class="desc">Shared living space with flexible sleeping arrangement.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectRoom('hh-triple')">
|
||
<input type="radio" name="room_type" value="hh-triple">
|
||
<h4>Bed in Triple Room <span style="float:right; color: var(--forest); font-weight: 600;">€350/wk</span></h4>
|
||
<div class="desc">Room shared between three people.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectRoom('hh-twin')">
|
||
<input type="radio" name="room_type" value="hh-twin">
|
||
<h4>Single Bed in Double Room <span style="float:right; color: var(--forest); font-weight: 600;">€420/wk</span></h4>
|
||
<div class="desc">Your own bed in a room shared with one other person.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="margin-bottom: 0.5rem; cursor: pointer;" onclick="selectRoom('hh-single')">
|
||
<input type="radio" name="room_type" value="hh-single">
|
||
<h4>Single Room <span style="float:right; color: var(--forest); font-weight: 600;">€665/wk</span></h4>
|
||
<div class="desc">Private room for one person.</div>
|
||
</label>
|
||
|
||
<label class="week-card" style="cursor: pointer;" onclick="selectRoom('hh-couple')">
|
||
<input type="radio" name="room_type" value="hh-couple">
|
||
<h4>Couple Room <span style="float:right; color: var(--forest); font-weight: 600;">€700/wk</span></h4>
|
||
<div class="desc">Private room with double bed for couples.<br><em style="color: #666; font-size: 0.85em;">(Your partner should book without accommodation)</em></div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<input type="hidden" id="accommodation_type" name="accommodation_type" value="">
|
||
|
||
<!-- Food preference -->
|
||
<h3 style="font-family: 'Cormorant Garamond', serif; font-size: 1.25rem; color: var(--forest); margin-bottom: 1rem; margin-top: 2rem;">Food Preference</h3>
|
||
<div class="radio-group">
|
||
<label class="radio-option">
|
||
<input type="radio" name="food_preference" value="no-preference">
|
||
<span>No preference</span>
|
||
</label>
|
||
<label class="radio-option">
|
||
<input type="radio" name="food_preference" value="vegetarian">
|
||
<span>Vegetarian</span>
|
||
</label>
|
||
<label class="radio-option">
|
||
<input type="radio" name="food_preference" value="vegan">
|
||
<span>Vegan</span>
|
||
</label>
|
||
<label class="radio-option">
|
||
<input type="radio" name="food_preference" value="non-vegetarian">
|
||
<span>Non-vegetarian</span>
|
||
</label>
|
||
<label class="radio-option">
|
||
<input type="radio" name="food_preference" value="allergies">
|
||
<span>Allergies / other</span>
|
||
</label>
|
||
</div>
|
||
<div id="food-allergies-detail" style="display: none; margin-top: 0.5rem;">
|
||
<input type="text" id="food_allergies_text" name="food_allergies_text" placeholder="Please describe your allergies or dietary needs">
|
||
</div>
|
||
|
||
<!-- Accessibility -->
|
||
<div class="form-group" style="margin-top: 2rem;">
|
||
<label for="accessibility_needs">Accessibility needs <span class="optional">(optional)</span></label>
|
||
<textarea id="accessibility_needs" name="accessibility_needs" rows="2" placeholder="Let us know if you have any accessibility requirements so we can prepare."></textarea>
|
||
</div>
|
||
|
||
<!-- Volunteer -->
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: flex-start; gap: 0.5rem; cursor: pointer;">
|
||
<input type="checkbox" id="volunteer_interest" name="volunteer_interest" style="width: 18px; height: 18px; margin-top: 0.2rem; accent-color: var(--forest);">
|
||
<span>I'm interested in volunteering (e.g. helping with setup, cooking, facilitation)</span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Coupon code -->
|
||
<div class="form-group">
|
||
<label for="coupon_code">Coupon code <span class="optional">(optional)</span></label>
|
||
<input type="text" id="coupon_code" name="coupon_code" placeholder="Enter a coupon code if you have one">
|
||
</div>
|
||
|
||
<!-- Anything else -->
|
||
<div class="form-group">
|
||
<label for="anything_else">Anything else you'd like to share? <span class="optional">(optional)</span></label>
|
||
<textarea id="anything_else" name="anything_else" rows="3" placeholder="Questions, special requests, or anything else..."></textarea>
|
||
</div>
|
||
|
||
<!-- Privacy -->
|
||
<div class="form-group" style="margin-top: 1.5rem;">
|
||
<label style="display: flex; align-items: flex-start; gap: 0.5rem; cursor: pointer;">
|
||
<input type="checkbox" id="privacy_accepted" name="privacy_accepted" required style="width: 18px; height: 18px; margin-top: 0.2rem; accent-color: var(--forest);">
|
||
<span>I agree to the <a href="privacy.html" target="_blank">privacy policy</a> and consent to my data being processed for this application <span class="required">*</span></span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="button" class="btn btn-primary" onclick="nextStep()">Continue to Review</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 10: Review & Pay -->
|
||
<div class="form-section" data-step="10">
|
||
<div class="question-number">Step 10 of 10</div>
|
||
<h2>Review Your Application</h2>
|
||
<p class="hint">Please review your answers below. Click "Edit" to make changes.</p>
|
||
|
||
<div id="review-content">
|
||
<!-- Populated by renderReview() -->
|
||
</div>
|
||
|
||
<div id="form-error" class="error-message" style="display: none;"></div>
|
||
|
||
<div class="form-nav">
|
||
<button type="button" class="btn btn-secondary" onclick="prevStep()">Back</button>
|
||
<button type="submit" class="btn btn-primary" id="submit-btn" style="font-size: 1.05rem;">Submit & Pay</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Success -->
|
||
<div class="form-section" data-step="success" style="display: none;">
|
||
<div class="success-card">
|
||
<div class="success-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M20 6L9 17l-5-5"/>
|
||
</svg>
|
||
</div>
|
||
<h2 id="success-heading">You're in the process!</h2>
|
||
<p>Thank you for applying to Valley of the Commons, <strong id="confirm-name"></strong>.</p>
|
||
<p>We've sent a confirmation to <strong id="confirm-email"></strong>.</p>
|
||
|
||
<div class="next-steps-box">
|
||
<h3>What happens next</h3>
|
||
<ol>
|
||
<li>Complete payment (you'll be redirected shortly)</li>
|
||
<li>Our team reviews your application within <strong>1 week</strong></li>
|
||
<li>You'll receive an email with the outcome</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<p style="margin-top: 1.5rem;">
|
||
<a href="/" class="btn btn-primary">Return to Homepage</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
<p>© 2026 Commons Hub · <a href="mailto:team@valleyofthecommons.com">team@valleyofthecommons.com</a> · <a href="/privacy.html">Privacy Policy</a> · <a href="/sponsorships.html">Sponsorships</a></p>
|
||
</footer>
|
||
|
||
<script>
|
||
let currentStep = 0; // 0 = landing screen
|
||
const totalSteps = 10;
|
||
const STORAGE_KEY = 'votc_application_v2';
|
||
const PROCESSING_FEE_PERCENT = 0.02;
|
||
|
||
// Tiered registration pricing (must match api/mollie.js)
|
||
const REGISTRATION_PRICING = {
|
||
early: { perWeek: 120, perMonth: 300 },
|
||
standard: { perWeek: 200, perMonth: 500 },
|
||
lastMin: { perWeek: 240, perMonth: 600 },
|
||
};
|
||
|
||
function getPricingTier() {
|
||
const now = new Date();
|
||
if (now < new Date('2026-05-15')) return 'early';
|
||
if (now < new Date('2026-07-15')) return 'standard';
|
||
return 'lastMin';
|
||
}
|
||
|
||
const currentTier = getPricingTier();
|
||
const tierPricing = REGISTRATION_PRICING[currentTier];
|
||
const TIER_LABELS = { early: 'Early Bird', standard: 'Standard', lastMin: 'Last Minute' };
|
||
|
||
// Accommodation prices — flat rates (must match api/mollie.js)
|
||
const ACCOMMODATION_PRICES = {
|
||
'ch-multi': { perWeek: 275, perMonth: 1100 },
|
||
'ch-double': { perWeek: 350, perMonth: 1400 },
|
||
'hh-living': { perWeek: 315, perMonth: 1260 },
|
||
'hh-triple': { perWeek: 350, perMonth: 1400 },
|
||
'hh-twin': { perWeek: 420, perMonth: 1680 },
|
||
'hh-single': { perWeek: 665, perMonth: 2660 },
|
||
'hh-couple': { perWeek: 700, perMonth: 2800 },
|
||
};
|
||
|
||
const ACCOMMODATION_LABELS = {
|
||
'ch-multi': 'Commons Hub — Bed in Multi-Room',
|
||
'ch-double': 'Commons Hub — Bed in Double Room',
|
||
'hh-living': 'Herrnhof Villa — Bed in Living Room',
|
||
'hh-triple': 'Herrnhof Villa — Bed in Triple Room',
|
||
'hh-twin': 'Herrnhof Villa — Single Bed in Double Room',
|
||
'hh-single': 'Herrnhof Villa — Single Room',
|
||
'hh-couple': 'Herrnhof Villa — Couple Room',
|
||
};
|
||
|
||
const WEEK_LABELS = {
|
||
week1: 'Week 1: Return to the Commons (Aug 24-30)',
|
||
week2: 'Week 2: Post-Capitalist Production (Aug 31-Sep 6)',
|
||
week3: 'Week 3: Future Living (Sep 7-13)',
|
||
week4: 'Week 4: Governance & Funding Models (Sep 14-20)',
|
||
};
|
||
|
||
const THEME_LABELS = {
|
||
commons: 'The Commons',
|
||
production: 'Post-Capitalist Production',
|
||
living: 'Future Living',
|
||
governance: 'Governance & Funding',
|
||
};
|
||
|
||
// Set landing registration price
|
||
document.getElementById('landing-reg-price').innerHTML =
|
||
`€${tierPricing.perWeek} – €${REGISTRATION_PRICING.lastMin.perWeek}/wk`;
|
||
|
||
// Check for saved data on load
|
||
(function checkSavedProgress() {
|
||
const saved = localStorage.getItem(STORAGE_KEY);
|
||
if (saved) {
|
||
document.getElementById('resume-notice').style.display = 'block';
|
||
}
|
||
})();
|
||
|
||
function startForm(resume) {
|
||
document.getElementById('landing-screen').style.display = 'none';
|
||
document.getElementById('application-form').style.display = 'block';
|
||
document.getElementById('progress-container').style.display = 'block';
|
||
|
||
if (resume) {
|
||
const saved = localStorage.getItem(STORAGE_KEY);
|
||
if (saved) {
|
||
try {
|
||
const data = JSON.parse(saved);
|
||
restoreFormData(data);
|
||
} catch (e) {
|
||
console.error('Failed to restore saved data:', e);
|
||
}
|
||
}
|
||
}
|
||
|
||
currentStep = 1;
|
||
showStep(1);
|
||
}
|
||
|
||
// ===== Autosave =====
|
||
function saveFormData() {
|
||
try {
|
||
const data = collectFormData();
|
||
data._savedStep = currentStep;
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||
} catch (e) {
|
||
console.error('Autosave failed:', e);
|
||
}
|
||
}
|
||
|
||
function restoreFormData(data) {
|
||
// Text inputs
|
||
if (data.first_name) document.getElementById('first_name').value = data.first_name;
|
||
if (data.last_name) document.getElementById('last_name').value = data.last_name;
|
||
if (data.email) document.getElementById('email').value = data.email;
|
||
if (data.social_links) document.getElementById('social_media').value = data.social_links;
|
||
if (data.how_heard) document.getElementById('how_heard').value = data.how_heard;
|
||
if (data.referral_name) document.getElementById('referral_names').value = data.referral_name;
|
||
if (data.affiliations) document.getElementById('affiliations').value = data.affiliations;
|
||
if (data.motivation) document.getElementById('why_join').value = data.motivation;
|
||
if (data.current_work) document.getElementById('current_work').value = data.current_work;
|
||
if (data.contribution) document.getElementById('contribution').value = data.contribution;
|
||
if (data.themes_familiarity) document.getElementById('themes_familiarity').value = data.themes_familiarity;
|
||
if (data.belief_update) document.getElementById('belief_update').value = data.belief_update;
|
||
if (data.accessibility_needs) document.getElementById('accessibility_needs').value = data.accessibility_needs;
|
||
if (data.coupon_code) document.getElementById('coupon_code').value = data.coupon_code;
|
||
if (data.anything_else) document.getElementById('anything_else').value = data.anything_else;
|
||
|
||
// Weeks
|
||
if (data.weeks && data.weeks.length > 0) {
|
||
data.weeks.forEach(w => {
|
||
const cb = document.querySelector(`input[name="weeks"][value="${w}"]`);
|
||
if (cb) {
|
||
cb.checked = true;
|
||
cb.closest('.week-card').classList.add('selected');
|
||
}
|
||
});
|
||
syncSelectAll();
|
||
updatePriceSummary();
|
||
}
|
||
|
||
// Themes
|
||
if (data.top_themes && data.top_themes.length > 0) {
|
||
data.top_themes.forEach(t => {
|
||
const el = document.querySelector(`.theme-option[data-theme="${t}"]`);
|
||
if (el) el.classList.add('selected');
|
||
});
|
||
updateThemeCounter();
|
||
}
|
||
|
||
// Food preference — handle "allergies: <text>" from DB
|
||
if (data.food_preference) {
|
||
let foodVal = data.food_preference;
|
||
let allergyText = '';
|
||
if (foodVal.startsWith('allergies: ')) {
|
||
allergyText = foodVal.substring('allergies: '.length);
|
||
foodVal = 'allergies';
|
||
}
|
||
const radio = document.querySelector(`input[name="food_preference"][value="${foodVal}"]`);
|
||
if (radio) {
|
||
radio.checked = true;
|
||
if (foodVal === 'allergies') {
|
||
document.getElementById('food-allergies-detail').style.display = 'block';
|
||
if (allergyText) document.getElementById('food_allergies_text').value = allergyText;
|
||
}
|
||
}
|
||
}
|
||
if (data.food_allergies_text && !document.getElementById('food_allergies_text').value) {
|
||
document.getElementById('food_allergies_text').value = data.food_allergies_text;
|
||
}
|
||
|
||
// Checkboxes
|
||
if (data.volunteer_interest) document.getElementById('volunteer_interest').checked = true;
|
||
if (data.privacy_policy_accepted) document.getElementById('privacy_accepted').checked = true;
|
||
|
||
// Accommodation
|
||
if (data.need_accommodation) {
|
||
const accomCb = document.getElementById('need_accommodation');
|
||
accomCb.checked = true;
|
||
accomCb.closest('.week-card').classList.add('selected');
|
||
document.getElementById('accommodation-options').style.display = 'block';
|
||
|
||
if (data.accommodation_type) {
|
||
const venue = data.accommodation_type.startsWith('hh') ? 'hh' : 'ch';
|
||
// Select venue
|
||
document.querySelectorAll('input[name="venue"]').forEach(r => {
|
||
r.checked = (r.value === venue);
|
||
r.closest('.week-card').classList.toggle('selected', r.checked);
|
||
});
|
||
document.getElementById('ch-rooms').style.display = venue === 'ch' ? 'block' : 'none';
|
||
document.getElementById('hh-rooms').style.display = venue === 'hh' ? 'block' : 'none';
|
||
|
||
// Select room
|
||
document.querySelectorAll('input[name="room_type"]').forEach(r => {
|
||
r.checked = (r.value === data.accommodation_type);
|
||
r.closest('.week-card').classList.toggle('selected', r.checked);
|
||
});
|
||
document.getElementById('accommodation_type').value = data.accommodation_type;
|
||
updatePriceSummary();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Autosave on input changes
|
||
document.addEventListener('input', function(e) {
|
||
if (e.target.closest('#application-form')) {
|
||
saveFormData();
|
||
}
|
||
});
|
||
document.addEventListener('change', function(e) {
|
||
if (e.target.closest('#application-form')) {
|
||
saveFormData();
|
||
}
|
||
});
|
||
|
||
// ===== Navigation =====
|
||
function updateProgress() {
|
||
if (currentStep < 1) return;
|
||
const percent = Math.round((currentStep / totalSteps) * 100);
|
||
document.getElementById('progress-fill').style.width = percent + '%';
|
||
document.getElementById('progress-percent').textContent = percent;
|
||
document.getElementById('progress-step').textContent = currentStep;
|
||
}
|
||
|
||
function showStep(step) {
|
||
document.querySelectorAll('.form-section').forEach(s => s.classList.remove('active'));
|
||
const target = document.querySelector(`.form-section[data-step="${step}"]`);
|
||
if (target) {
|
||
target.classList.add('active');
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
if (step === 10) renderReview();
|
||
updateProgress();
|
||
}
|
||
|
||
function validateStep(step) {
|
||
const section = document.querySelector(`.form-section[data-step="${step}"]`);
|
||
const required = section.querySelectorAll('[required]');
|
||
let valid = true;
|
||
|
||
required.forEach(field => {
|
||
field.style.borderColor = '';
|
||
if (field.type === 'checkbox') {
|
||
if (!field.checked) valid = false;
|
||
} else if (!field.value.trim()) {
|
||
valid = false;
|
||
field.style.borderColor = 'var(--error)';
|
||
}
|
||
});
|
||
|
||
// Email validation
|
||
const emailField = section.querySelector('input[type="email"]');
|
||
if (emailField && emailField.value && !emailField.value.includes('@')) {
|
||
valid = false;
|
||
emailField.style.borderColor = 'var(--error)';
|
||
}
|
||
|
||
// Week selection (step 1)
|
||
if (step === 1) {
|
||
const checked = document.querySelectorAll('input[name="weeks"]:checked');
|
||
if (checked.length === 0) {
|
||
valid = false;
|
||
alert('Please select at least one week.');
|
||
}
|
||
}
|
||
|
||
return valid;
|
||
}
|
||
|
||
// State for existing application resume flow
|
||
window._existingApplicationId = null;
|
||
window._pendingLookupData = null;
|
||
|
||
async function nextStep() {
|
||
if (!validateStep(currentStep)) return;
|
||
|
||
// Email check when leaving step 2
|
||
if (currentStep === 2 && !window._existingApplicationId) {
|
||
const email = document.getElementById('email').value.trim().toLowerCase();
|
||
if (email) {
|
||
try {
|
||
const resp = await fetch('/api/application/lookup?email=' + encodeURIComponent(email));
|
||
if (resp.ok) {
|
||
const result = await resp.json();
|
||
if (result.found) {
|
||
if (result.application.payment_status === 'paid') {
|
||
alert('This email already has a completed (paid) application. Contact us at contact@valleyofthecommons.com if you need to make changes.');
|
||
return;
|
||
}
|
||
// Show welcome-back modal
|
||
window._pendingLookupData = result.application;
|
||
const heading = document.getElementById('welcome-back-heading');
|
||
heading.textContent = `Welcome back, ${result.application.first_name}!`;
|
||
document.getElementById('welcome-back-modal').classList.add('visible');
|
||
return; // Don't advance until user decides
|
||
}
|
||
}
|
||
// 404 = no existing application, continue normally
|
||
} catch (e) {
|
||
console.error('Email lookup failed:', e);
|
||
// Non-blocking — continue with new application
|
||
}
|
||
}
|
||
}
|
||
|
||
if (currentStep < totalSteps) {
|
||
currentStep++;
|
||
showStep(currentStep);
|
||
saveFormData();
|
||
}
|
||
}
|
||
|
||
function loadExistingApplication() {
|
||
const data = window._pendingLookupData;
|
||
if (!data) return;
|
||
window._existingApplicationId = data.id;
|
||
restoreFormData(data);
|
||
saveFormData();
|
||
document.getElementById('welcome-back-modal').classList.remove('visible');
|
||
// Advance to step 3
|
||
currentStep = 3;
|
||
showStep(3);
|
||
}
|
||
|
||
function closeWelcomeModal() {
|
||
window._pendingLookupData = null;
|
||
document.getElementById('welcome-back-modal').classList.remove('visible');
|
||
// Stay on step 2 — user can change their email
|
||
}
|
||
|
||
function prevStep() {
|
||
if (currentStep > 1) {
|
||
currentStep--;
|
||
showStep(currentStep);
|
||
}
|
||
}
|
||
|
||
function jumpToStep(n) {
|
||
currentStep = n;
|
||
showStep(n);
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
|
||
// ===== Week selection =====
|
||
function toggleWeek(card) {
|
||
const cb = card.querySelector('input');
|
||
cb.checked = !cb.checked;
|
||
card.classList.toggle('selected', cb.checked);
|
||
syncSelectAll();
|
||
updatePriceSummary();
|
||
saveFormData();
|
||
}
|
||
|
||
function toggleAllWeeks(card) {
|
||
const selectAllCb = card.querySelector('input');
|
||
selectAllCb.checked = !selectAllCb.checked;
|
||
card.classList.toggle('selected', selectAllCb.checked);
|
||
|
||
const weekCbs = document.querySelectorAll('input[name="weeks"]');
|
||
weekCbs.forEach(cb => {
|
||
cb.checked = selectAllCb.checked;
|
||
cb.closest('.week-card').classList.toggle('selected', selectAllCb.checked);
|
||
});
|
||
updatePriceSummary();
|
||
saveFormData();
|
||
}
|
||
|
||
function syncSelectAll() {
|
||
const weekCbs = document.querySelectorAll('input[name="weeks"]');
|
||
const allChecked = Array.from(weekCbs).every(cb => cb.checked);
|
||
const selectAllCb = document.getElementById('select-all-weeks');
|
||
const selectAllCard = selectAllCb.closest('.week-card');
|
||
selectAllCb.checked = allChecked;
|
||
selectAllCard.classList.toggle('selected', allChecked);
|
||
}
|
||
|
||
// ===== Accommodation =====
|
||
function toggleAddon(card, type) {
|
||
const cb = card.querySelector('input');
|
||
cb.checked = !cb.checked;
|
||
card.classList.toggle('selected', cb.checked);
|
||
|
||
if (type === 'accommodation') {
|
||
const opts = document.getElementById('accommodation-options');
|
||
opts.style.display = cb.checked ? 'block' : 'none';
|
||
if (!cb.checked) {
|
||
document.getElementById('accommodation_type').value = '';
|
||
document.querySelectorAll('input[name="venue"]').forEach(r => { r.checked = false; r.closest('.week-card').classList.remove('selected'); });
|
||
document.querySelectorAll('input[name="room_type"]').forEach(r => { r.checked = false; r.closest('.week-card').classList.remove('selected'); });
|
||
document.getElementById('ch-rooms').style.display = 'none';
|
||
document.getElementById('hh-rooms').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
updatePriceSummary();
|
||
saveFormData();
|
||
}
|
||
|
||
function selectVenue(venue) {
|
||
document.querySelectorAll('input[name="venue"]').forEach(r => {
|
||
r.checked = (r.value === venue);
|
||
r.closest('.week-card').classList.toggle('selected', r.checked);
|
||
});
|
||
|
||
document.getElementById('ch-rooms').style.display = venue === 'ch' ? 'block' : 'none';
|
||
document.getElementById('hh-rooms').style.display = venue === 'hh' ? 'block' : 'none';
|
||
|
||
document.querySelectorAll('input[name="room_type"]').forEach(r => {
|
||
r.checked = false;
|
||
r.closest('.week-card').classList.remove('selected');
|
||
});
|
||
document.getElementById('accommodation_type').value = '';
|
||
updatePriceSummary();
|
||
saveFormData();
|
||
}
|
||
|
||
function selectRoom(roomType) {
|
||
document.querySelectorAll('input[name="room_type"]').forEach(r => {
|
||
r.checked = (r.value === roomType);
|
||
r.closest('.week-card').classList.toggle('selected', r.checked);
|
||
});
|
||
document.getElementById('accommodation_type').value = roomType;
|
||
updatePriceSummary();
|
||
saveFormData();
|
||
}
|
||
|
||
// ===== Theme picker =====
|
||
function getSelectedThemes() {
|
||
return Array.from(document.querySelectorAll('.theme-option.selected')).map(el => el.dataset.theme);
|
||
}
|
||
|
||
function updateThemeCounter() {
|
||
const count = getSelectedThemes().length;
|
||
document.getElementById('theme-counter').textContent = `${count} of 3 selected`;
|
||
|
||
// Disable unchecked when at max
|
||
document.querySelectorAll('.theme-option').forEach(el => {
|
||
if (!el.classList.contains('selected')) {
|
||
el.classList.toggle('disabled-theme', count >= 3);
|
||
}
|
||
});
|
||
}
|
||
|
||
function toggleTheme(el) {
|
||
if (el.classList.contains('disabled-theme')) return;
|
||
|
||
el.classList.toggle('selected');
|
||
updateThemeCounter();
|
||
saveFormData();
|
||
}
|
||
|
||
// ===== Food radio =====
|
||
document.querySelectorAll('input[name="food_preference"]').forEach(radio => {
|
||
radio.addEventListener('change', function() {
|
||
document.getElementById('food-allergies-detail').style.display =
|
||
this.value === 'allergies' ? 'block' : 'none';
|
||
});
|
||
});
|
||
|
||
// ===== Price calculation =====
|
||
function getAccommodationPrice(accomType, weeksCount) {
|
||
if (!accomType || !ACCOMMODATION_PRICES[accomType]) return 0;
|
||
const prices = ACCOMMODATION_PRICES[accomType];
|
||
if (weeksCount === 4) return prices.perMonth;
|
||
return prices.perWeek * weeksCount;
|
||
}
|
||
|
||
function getSelectedWeeks() {
|
||
return Array.from(document.querySelectorAll('input[name="weeks"]:checked')).map(cb => cb.value);
|
||
}
|
||
|
||
function calculatePrice() {
|
||
const weeksCount = getSelectedWeeks().length;
|
||
if (weeksCount === 0) return null;
|
||
|
||
const registration = weeksCount === 4 ? tierPricing.perMonth : tierPricing.perWeek * weeksCount;
|
||
const accomType = document.getElementById('accommodation_type').value;
|
||
const accommodation = getAccommodationPrice(accomType, weeksCount);
|
||
const subtotal = registration + accommodation;
|
||
const fee = subtotal * PROCESSING_FEE_PERCENT;
|
||
const total = subtotal + fee;
|
||
|
||
return { registration, accommodation, accomType, fee, total, weeksCount };
|
||
}
|
||
|
||
function updatePriceSummary() {
|
||
const el = document.getElementById('price-calc');
|
||
const pricing = calculatePrice();
|
||
|
||
if (!pricing) {
|
||
el.textContent = 'Select at least one week to see pricing';
|
||
return;
|
||
}
|
||
|
||
const { registration, accommodation, accomType, fee, total, weeksCount } = pricing;
|
||
const tierLabel = TIER_LABELS[currentTier];
|
||
|
||
let html = weeksCount === 4
|
||
? `<strong>Registration:</strong> €${tierPricing.perMonth} (full month, ${tierLabel})`
|
||
: `<strong>Registration:</strong> €${tierPricing.perWeek} × ${weeksCount} wk = €${registration.toFixed(2)} (${tierLabel})`;
|
||
|
||
if (accommodation > 0) {
|
||
const label = ACCOMMODATION_LABELS[accomType] || accomType;
|
||
const prices = ACCOMMODATION_PRICES[accomType];
|
||
if (weeksCount === 4) {
|
||
html += `<br><strong>Accommodation:</strong> ${label}<br> €${prices.perMonth} (full month)`;
|
||
} else {
|
||
html += `<br><strong>Accommodation:</strong> ${label}<br> €${prices.perWeek} × ${weeksCount} wk = €${accommodation.toFixed(2)}`;
|
||
}
|
||
}
|
||
|
||
html += `<br><strong>Processing fee (2%):</strong> €${fee.toFixed(2)}`;
|
||
html += `<br><strong style="font-size: 1.1em;">Total: €${total.toFixed(2)}</strong>`;
|
||
|
||
el.innerHTML = html;
|
||
}
|
||
|
||
// ===== Collect form data =====
|
||
function collectFormData() {
|
||
const form = document.getElementById('application-form');
|
||
const weeks = getSelectedWeeks();
|
||
const foodPref = document.querySelector('input[name="food_preference"]:checked');
|
||
let foodValue = foodPref ? foodPref.value : null;
|
||
if (foodValue === 'allergies') {
|
||
const allergyText = document.getElementById('food_allergies_text').value.trim();
|
||
if (allergyText) foodValue = 'allergies: ' + allergyText;
|
||
}
|
||
|
||
return {
|
||
first_name: form.first_name.value.trim(),
|
||
last_name: form.last_name.value.trim(),
|
||
email: form.email.value,
|
||
social_links: form.social_media.value,
|
||
how_heard: form.how_heard.value,
|
||
referral_name: form.referral_names.value,
|
||
affiliations: form.affiliations.value,
|
||
motivation: form.why_join.value,
|
||
current_work: form.current_work.value,
|
||
contribution: form.contribution.value,
|
||
weeks: weeks,
|
||
attendance_type: weeks.length === 4 ? 'full' : 'partial',
|
||
top_themes: getSelectedThemes(),
|
||
themes_familiarity: form.themes_familiarity.value,
|
||
belief_update: form.belief_update.value,
|
||
need_accommodation: document.getElementById('need_accommodation').checked,
|
||
accommodation_type: document.getElementById('accommodation_type').value || null,
|
||
accommodation_preference: document.getElementById('accommodation_type').value || null,
|
||
food_preference: foodValue,
|
||
food_allergies_text: document.getElementById('food_allergies_text').value,
|
||
accessibility_needs: form.accessibility_needs.value,
|
||
volunteer_interest: document.getElementById('volunteer_interest').checked,
|
||
coupon_code: form.coupon_code.value,
|
||
anything_else: form.anything_else.value,
|
||
privacy_policy_accepted: form.privacy_accepted.checked,
|
||
code_of_conduct_accepted: true,
|
||
// Map to existing DB columns
|
||
commons_experience: form.affiliations.value,
|
||
workshops_offer: form.themes_familiarity.value,
|
||
projects: form.current_work.value,
|
||
want_food: foodValue && foodValue !== 'no-preference' ? true : false,
|
||
};
|
||
}
|
||
|
||
// ===== Review step =====
|
||
function renderReview() {
|
||
const data = collectFormData();
|
||
const pricing = calculatePrice();
|
||
const container = document.getElementById('review-content');
|
||
|
||
function esc(str) {
|
||
if (!str) return '<em style="color:#999;">—</em>';
|
||
const div = document.createElement('div');
|
||
div.textContent = str;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
let html = '';
|
||
|
||
// Weeks
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Weeks Selected</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(1)">Edit</span>
|
||
</div>
|
||
<div class="review-field">
|
||
<div class="review-value">${data.weeks.map(w => WEEK_LABELS[w] || w).join('<br>')}</div>
|
||
</div>
|
||
</div>`;
|
||
|
||
// Contact
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Contact Information</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(2)">Edit</span>
|
||
</div>
|
||
<div class="review-field">
|
||
<div class="review-label">Name</div>
|
||
<div class="review-value">${esc(data.first_name)} ${esc(data.last_name)}</div>
|
||
</div>
|
||
<div class="review-field">
|
||
<div class="review-label">Email</div>
|
||
<div class="review-value">${esc(data.email)}</div>
|
||
</div>
|
||
${data.social_links ? `<div class="review-field"><div class="review-label">Social</div><div class="review-value">${esc(data.social_links)}</div></div>` : ''}
|
||
<div class="review-field">
|
||
<div class="review-label">How heard</div>
|
||
<div class="review-value">${esc(data.how_heard)}</div>
|
||
</div>
|
||
${data.referral_name ? `<div class="review-field"><div class="review-label">Referral</div><div class="review-value">${esc(data.referral_name)}</div></div>` : ''}
|
||
</div>`;
|
||
|
||
// Affiliations
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Affiliations</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(3)">Edit</span>
|
||
</div>
|
||
<div class="review-field"><div class="review-value">${esc(data.affiliations)}</div></div>
|
||
</div>`;
|
||
|
||
// Why join
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Why Join & Good Fit</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(4)">Edit</span>
|
||
</div>
|
||
<div class="review-field"><div class="review-value">${esc(data.motivation)}</div></div>
|
||
</div>`;
|
||
|
||
// Current work
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Current Work</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(5)">Edit</span>
|
||
</div>
|
||
<div class="review-field"><div class="review-value">${esc(data.current_work)}</div></div>
|
||
</div>`;
|
||
|
||
// Contribution
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Contribution</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(6)">Edit</span>
|
||
</div>
|
||
<div class="review-field"><div class="review-value">${esc(data.contribution)}</div></div>
|
||
</div>`;
|
||
|
||
// Themes
|
||
const themeNames = data.top_themes.map(t => THEME_LABELS[t] || t);
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Themes</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(7)">Edit</span>
|
||
</div>
|
||
<div class="review-field">
|
||
<div class="review-label">Top themes</div>
|
||
<div class="review-value">${themeNames.length > 0 ? themeNames.join(', ') : '<em style="color:#999;">None selected</em>'}</div>
|
||
</div>
|
||
${data.themes_familiarity ? `<div class="review-field"><div class="review-label">Familiarity</div><div class="review-value">${esc(data.themes_familiarity)}</div></div>` : ''}
|
||
</div>`;
|
||
|
||
// Belief update
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Belief Update</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(8)">Edit</span>
|
||
</div>
|
||
<div class="review-field"><div class="review-value">${esc(data.belief_update)}</div></div>
|
||
</div>`;
|
||
|
||
// Practical details
|
||
const foodLabel = data.food_preference ? data.food_preference.replace('allergies: ', 'Allergies: ') : 'Not specified';
|
||
html += `<div class="review-section">
|
||
<div class="review-section-header">
|
||
<h3>Practical Details</h3>
|
||
<span class="review-edit-link" onclick="jumpToStep(9)">Edit</span>
|
||
</div>
|
||
${data.accommodation_type ? `<div class="review-field"><div class="review-label">Accommodation</div><div class="review-value">${ACCOMMODATION_LABELS[data.accommodation_type] || data.accommodation_type}</div></div>` : '<div class="review-field"><div class="review-label">Accommodation</div><div class="review-value">None selected</div></div>'}
|
||
<div class="review-field"><div class="review-label">Food preference</div><div class="review-value">${esc(foodLabel)}</div></div>
|
||
${data.accessibility_needs ? `<div class="review-field"><div class="review-label">Accessibility</div><div class="review-value">${esc(data.accessibility_needs)}</div></div>` : ''}
|
||
${data.volunteer_interest ? '<div class="review-field"><div class="review-label">Volunteer</div><div class="review-value">Interested in volunteering</div></div>' : ''}
|
||
${data.coupon_code ? `<div class="review-field"><div class="review-label">Coupon code</div><div class="review-value">${esc(data.coupon_code)}</div></div>` : ''}
|
||
${data.anything_else ? `<div class="review-field"><div class="review-label">Other notes</div><div class="review-value">${esc(data.anything_else)}</div></div>` : ''}
|
||
</div>`;
|
||
|
||
// Price summary
|
||
if (pricing) {
|
||
const { registration, accommodation, accomType, fee, total, weeksCount } = pricing;
|
||
const tierLabel = TIER_LABELS[currentTier];
|
||
|
||
html += `<div class="review-price-summary">
|
||
<h3>Payment Summary</h3>
|
||
<div class="price-line">
|
||
<span>Registration (${weeksCount} wk, ${tierLabel})</span>
|
||
<span>€${registration.toFixed(2)}</span>
|
||
</div>`;
|
||
|
||
if (accommodation > 0) {
|
||
html += `<div class="price-line">
|
||
<span>Accommodation</span>
|
||
<span>€${accommodation.toFixed(2)}</span>
|
||
</div>`;
|
||
}
|
||
|
||
html += `<div class="price-line">
|
||
<span>Processing fee (2%)</span>
|
||
<span>€${fee.toFixed(2)}</span>
|
||
</div>
|
||
<div class="price-line total">
|
||
<span>Total</span>
|
||
<span>€${total.toFixed(2)}</span>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
container.innerHTML = html;
|
||
}
|
||
|
||
// ===== Submit handler =====
|
||
document.getElementById('application-form').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const errorDiv = document.getElementById('form-error');
|
||
const submitBtn = document.getElementById('submit-btn');
|
||
|
||
errorDiv.style.display = 'none';
|
||
submitBtn.disabled = true;
|
||
const isUpdate = !!window._existingApplicationId;
|
||
submitBtn.textContent = isUpdate ? 'Updating...' : 'Submitting...';
|
||
|
||
try {
|
||
const data = collectFormData();
|
||
|
||
const response = await fetch('/api/application', {
|
||
method: isUpdate ? 'PUT' : 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (response.ok && result.success) {
|
||
// Clear saved data
|
||
localStorage.removeItem(STORAGE_KEY);
|
||
|
||
// Show success state
|
||
document.getElementById('confirm-name').textContent = data.first_name;
|
||
document.getElementById('confirm-email').textContent = data.email;
|
||
document.getElementById('success-heading').textContent = isUpdate
|
||
? `Application updated, ${data.first_name}!`
|
||
: `You're in the process, ${data.first_name}!`;
|
||
|
||
document.querySelectorAll('.form-section').forEach(s => s.classList.remove('active'));
|
||
document.querySelector('.form-section[data-step="success"]').style.display = 'block';
|
||
document.querySelector('.form-section[data-step="success"]').classList.add('active');
|
||
document.getElementById('progress-container').style.display = 'none';
|
||
|
||
// Redirect to Mollie checkout after brief display
|
||
if (result.checkoutUrl) {
|
||
setTimeout(() => {
|
||
window.location.href = result.checkoutUrl;
|
||
}, 2000);
|
||
}
|
||
} else {
|
||
errorDiv.textContent = result.error || 'Something went wrong. Please try again.';
|
||
errorDiv.style.display = 'block';
|
||
submitBtn.disabled = false;
|
||
submitBtn.textContent = 'Submit & Pay';
|
||
}
|
||
} catch (error) {
|
||
console.error('Submission error:', error);
|
||
errorDiv.textContent = 'Network error. Please check your connection.';
|
||
errorDiv.style.display = 'block';
|
||
submitBtn.disabled = false;
|
||
submitBtn.textContent = 'Submit & Pay';
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|