feat: restructure application form (10 steps), add sponsorship tiers, consolidate CTAs

- Restructure apply form: landing screen, pricing at step 1, theme picker,
  belief update, review & pay step, localStorage autosave, warm success state
- Add 7 new DB columns (migration-004): selected_weeks, top_themes,
  belief_update, volunteer_interest, coupon_code, food_preference, accessibility_needs
- Update confirmation emails: 1-week review timeline, warmer tone
- Add sponsorship tiers page (Friend/Symbiont/Spore/Mycelium)
- Consolidate all "Apply Now" → "Register Now", remove duplicate CTAs
- Add new fields to admin panel display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-31 09:58:46 -07:00
parent 707bdc3d53
commit 79141b7142
9 changed files with 1516 additions and 398 deletions

View File

@ -791,6 +791,52 @@
</div>
</div>
<div class="detail-section">
<h3>Themes & Beliefs</h3>
<div class="detail-grid">
<div class="detail-item full-width">
<label>Top Themes</label>
<div class="chip-list">
${(app.top_themes || []).map(t => `<span class="chip">${t}</span>`).join('') || '-'}
</div>
</div>
<div class="detail-item full-width">
<label>Belief Update</label>
<div class="long-text">${app.belief_update || '-'}</div>
</div>
<div class="detail-item full-width">
<label>Selected Weeks</label>
<div class="chip-list">
${(app.selected_weeks || []).map(w => `<span class="chip">${w}</span>`).join('') || '-'}
</div>
</div>
</div>
</div>
<div class="detail-section">
<h3>Practical Needs</h3>
<div class="detail-grid">
<div class="detail-item">
<label>Food Preference</label>
<p>${app.food_preference || '-'}</p>
</div>
<div class="detail-item">
<label>Volunteer Interest</label>
<p>${app.volunteer_interest ? '✓ Yes' : 'No'}</p>
</div>
<div class="detail-item full-width">
<label>Accessibility Needs</label>
<div class="long-text">${app.accessibility_needs || '-'}</div>
</div>
${app.coupon_code ? `
<div class="detail-item">
<label>Coupon Code</label>
<p>${app.coupon_code}</p>
</div>
` : ''}
</div>
</div>
<div class="detail-section">
<h3>Financial & Practical</h3>
<div class="detail-grid">

View File

@ -61,14 +61,12 @@ const confirmationEmail = (application) => {
: '';
return {
subject: 'Application Received - Valley of the Commons',
subject: 'Welcome to the Process - Valley of the Commons',
html: `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<h1 style="color: #2d5016; margin-bottom: 24px;">Thank You for Applying!</h1>
<h1 style="color: #2d5016; margin-bottom: 24px;">We're glad you're here, ${application.first_name}!</h1>
<p>Dear ${application.first_name},</p>
<p>We've received your application to join <strong>Valley of the Commons</strong> (August 24 - September 20, 2026).</p>
<p>Your application to <strong>Valley of the Commons</strong> (August 24 September 20, 2026) has been received. We're excited to read about what you'll bring to the village.</p>
<div style="background: #f5f5f0; padding: 20px; border-radius: 8px; margin: 24px 0;">
<h3 style="margin-top: 0; color: #2d5016;">Your Booking Summary</h3>
@ -99,10 +97,9 @@ const confirmationEmail = (application) => {
<h3 style="margin-top: 0; color: #2d5016;">What happens next?</h3>
<ol style="margin-bottom: 0;">
<li><a href="${process.env.BASE_URL || 'https://valleyofthecommons.com'}/api/mollie/resume?id=${application.id}" style="color: #2d5016; font-weight: 600;">Complete your registration payment</a> (if you haven't already)</li>
<li>Our team will review your application</li>
<li>Our team will review your application within <strong>1 week</strong></li>
<li>We may reach out with follow-up questions</li>
<li>You'll receive a decision within 2-3 weeks</li>
${accomType ? '<li>Your bed will be assigned automatically once payment is confirmed</li>' : ''}
${accomType ? '<li>Your accommodation will be allocated and details sent to you shortly after payment is confirmed</li>' : ''}
</ol>
</div>
@ -112,7 +109,7 @@ const confirmationEmail = (application) => {
<li><a href="https://valleyofthecommons.com/">Valley of the Commons</a></li>
</ul>
<p>If you have any questions, reply to this email and we'll get back to you.</p>
<p>If you have any questions, just reply to this email we'd love to hear from you.</p>
<p style="margin-top: 32px;">
With warmth,<br>
@ -212,7 +209,7 @@ module.exports = async function handler(req, res) {
const data = req.body;
// Validate required fields
const required = ['first_name', 'last_name', 'email', 'motivation', 'code_of_conduct_accepted', 'privacy_policy_accepted'];
const required = ['first_name', 'last_name', 'email', 'motivation', 'belief_update', 'privacy_policy_accepted'];
for (const field of required) {
if (!data[field]) {
return res.status(400).json({ error: `Missing required field: ${field}` });
@ -244,6 +241,10 @@ module.exports = async function handler(req, res) {
const governance = Array.isArray(data.governance_interest) ? data.governance_interest : (data.governance_interest ? [data.governance_interest] : null);
const previousEvents = Array.isArray(data.previous_events) ? data.previous_events : (data.previous_events ? [data.previous_events] : null);
// Prepare new array fields
const selectedWeeks = Array.isArray(data.weeks) ? data.weeks : (data.weeks ? [data.weeks] : []);
const topThemes = Array.isArray(data.top_themes) ? data.top_themes : (data.top_themes ? [data.top_themes] : null);
// Insert application
const result = await pool.query(
`INSERT INTO applications (
@ -255,11 +256,14 @@ module.exports = async function handler(req, res) {
how_heard, referral_name, previous_events, emergency_name, emergency_phone,
emergency_relationship, code_of_conduct_accepted, privacy_policy_accepted,
photo_consent, scholarship_needed, scholarship_reason, contribution_amount,
ip_address, user_agent, need_accommodation, want_food, accommodation_type
ip_address, user_agent, need_accommodation, want_food, accommodation_type,
selected_weeks, top_themes, belief_update, volunteer_interest, coupon_code,
food_preference, accessibility_needs
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
$18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32,
$33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44
$33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44,
$45, $46, $47, $48, $49, $50, $51
) RETURNING id, submitted_at`,
[
data.first_name?.trim(),
@ -305,11 +309,17 @@ module.exports = async function handler(req, res) {
req.headers['user-agent'] || null,
data.need_accommodation || false,
data.want_food || false,
data.accommodation_type || null
data.accommodation_type || null,
selectedWeeks.length > 0 ? selectedWeeks : null,
topThemes,
data.belief_update?.trim() || null,
data.volunteer_interest || false,
data.coupon_code?.trim() || null,
data.food_preference?.trim() || null,
data.accessibility_needs?.trim() || null
]
);
const weeksSelected = Array.isArray(data.weeks) ? data.weeks : [];
const application = {
id: result.rows[0].id,
submitted_at: result.rows[0].submitted_at,
@ -328,7 +338,7 @@ module.exports = async function handler(req, res) {
referral_name: data.referral_name,
arrival_date: data.arrival_date,
departure_date: data.departure_date,
weeks: weeksSelected,
weeks: selectedWeeks,
need_accommodation: data.need_accommodation || false,
accommodation_preference: data.accommodation_preference || null,
accommodation_type: data.accommodation_type || null,
@ -342,7 +352,7 @@ module.exports = async function handler(req, res) {
// Add to Listmonk newsletter
addToListmonk(application.email, `${application.first_name} ${application.last_name}`, {
source: 'application',
weeks: weeksSelected,
weeks: selectedWeeks,
contributionAmount: data.contribution_amount,
}).catch(err => console.error('[Listmonk] Application sync failed:', err.message));
@ -382,17 +392,17 @@ module.exports = async function handler(req, res) {
// Create Mollie payment for registration + accommodation fee
let checkoutUrl = null;
if (weeksSelected.length > 0 && process.env.MOLLIE_API_KEY) {
if (selectedWeeks.length > 0 && process.env.MOLLIE_API_KEY) {
try {
const paymentResult = await createPayment(
application.id,
'registration',
weeksSelected.length,
selectedWeeks.length,
application.email,
application.first_name,
application.last_name,
application.accommodation_type,
weeksSelected
selectedWeeks
);
checkoutUrl = paymentResult.checkoutUrl;
console.log(`Mollie payment created: ${paymentResult.paymentId} (€${paymentResult.amount})`);

View File

@ -226,7 +226,7 @@ const paymentConfirmationEmail = (application, bookingResult) => {
${bookingHtml}
<p>Your application is now complete. Our team will review it and get back to you within 2-3 weeks.</p>
<p>Your application is now complete. Our team will review it and get back to you within 1 week.</p>
<p>If you have any questions, reply to this email and we'll get back to you.</p>

1273
apply.html

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
-- Migration 004: Form restructure — add columns for new application flow
-- selected_weeks, top_themes, belief_update, volunteer_interest, coupon_code, food_preference, accessibility_needs
ALTER TABLE applications
ADD COLUMN IF NOT EXISTS selected_weeks TEXT[],
ADD COLUMN IF NOT EXISTS top_themes TEXT[],
ADD COLUMN IF NOT EXISTS belief_update TEXT,
ADD COLUMN IF NOT EXISTS volunteer_interest BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS coupon_code TEXT,
ADD COLUMN IF NOT EXISTS food_preference TEXT,
ADD COLUMN IF NOT EXISTS accessibility_needs TEXT;

View File

@ -51,9 +51,8 @@
<span class="nav-speakers-short">Collaborators</span>
</a>
<a href="#explore">Explore the Valley</a>
<a href="#register" class="nav-get-involved">Register</a>
<a href="game.html" target="_blank" rel="noopener noreferrer" class="nav-rabbit">🐰</a>
<a href="/apply.html" class="nav-cta-button">APPLY NOW</a>
<a href="/apply.html" class="nav-cta-button">REGISTER NOW</a>
</nav>
</div>
</header>
@ -78,7 +77,7 @@
<h2 class="event-title">Pop-Up Event to Seed the Valley</h2>
<p class="event-dates">24 August 2026 20 September 2026</p>
</div>
<a href="/apply.html" class="cta-button">APPLY NOW</a>
<a href="/apply.html" class="cta-button">REGISTER NOW</a>
</section>
<!-- Main Content -->
@ -202,9 +201,6 @@
<div id="partners-container" class="partners-container">
<!-- Partners will be dynamically loaded here -->
</div>
<p style="text-align: center; margin-top: var(--spacing-md);">
<a href="#register" class="explore-link">Register →</a>
</p>
</section>
<!-- Explore the Valley Section -->
@ -226,7 +222,6 @@
<span>from €120 / week</span>
</div>
<a href="/apply.html" class="register-button">REGISTER NOW</a>
<p class="cta-note">You'll be added to our mailing list to stay updated.</p>
</div>
</section>
@ -238,6 +233,7 @@
<input type="email" id="newsletter-email" name="email" placeholder="your@email.com" required aria-label="Email address">
<button type="submit">Subscribe</button>
</form>
<p class="cta-note">You'll be added to our mailing list to stay updated.</p>
<div id="newsletter-message" class="newsletter-message" role="status" aria-live="polite"></div>
</div>
</section>

View File

@ -216,7 +216,7 @@
switch (data.paymentStatus) {
case 'paid':
showStatus('success', 'Payment Confirmed!',
'Your payment has been received. We\'ll review your application and get back to you within 2-3 weeks.', true);
'Your payment has been received. We\'ll review your application and get back to you within 1 week.', true);
break;
case 'pending':
case 'open':

View File

@ -87,6 +87,18 @@ async function runMigrations() {
ADD COLUMN IF NOT EXISTS accommodation_type VARCHAR(50)
`);
// Form restructure columns (migration 004)
await pool.query(`
ALTER TABLE applications
ADD COLUMN IF NOT EXISTS selected_weeks TEXT[],
ADD COLUMN IF NOT EXISTS top_themes TEXT[],
ADD COLUMN IF NOT EXISTS belief_update TEXT,
ADD COLUMN IF NOT EXISTS volunteer_interest BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS coupon_code TEXT,
ADD COLUMN IF NOT EXISTS food_preference TEXT,
ADD COLUMN IF NOT EXISTS accessibility_needs TEXT
`);
// Rename resend_id → message_id in email_log (legacy column name)
const colCheck = await pool.query(`
SELECT column_name FROM information_schema.columns

View File

@ -16,6 +16,7 @@
--color-bg: #ffffff;
--color-accent: #000;
--color-link: #000;
--color-forest: #2d5016;
--font-sans: 'Urbanist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
@ -39,7 +40,7 @@
}
.header-container {
max-width: 900px;
max-width: 1100px;
margin: 0 auto;
padding: 0 2rem;
display: flex;
@ -59,15 +60,15 @@
.header a:hover { text-decoration: underline; }
.content {
max-width: 900px;
max-width: 1100px;
margin: 0 auto;
padding: 4rem 2rem;
padding: 3rem 2rem 4rem;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.page-header {
text-align: center;
margin-bottom: 3rem;
}
h1 {
@ -81,21 +82,177 @@
.subtitle {
font-size: 1.2rem;
color: var(--color-text-light);
margin-bottom: 2rem;
margin-bottom: 1rem;
}
.coming-soon {
font-size: 1.1rem;
.intro-text {
font-size: 1.05rem;
color: var(--color-text-light);
max-width: 500px;
margin-bottom: 2rem;
max-width: 650px;
margin: 0 auto;
}
.contact {
font-size: 1rem;
/* Tier cards */
.tiers {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.25rem;
margin-bottom: 3rem;
}
.contact a {
.tier-card {
border: 1px solid #e5e5e5;
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.tier-card.featured {
border-color: var(--color-forest);
box-shadow: 0 4px 16px rgba(45, 80, 22, 0.12);
}
.tier-header {
padding: 1.5rem 1.25rem 1rem;
text-align: center;
border-bottom: 1px solid #e5e5e5;
}
.tier-card.featured .tier-header {
background: var(--color-forest);
border-bottom-color: var(--color-forest);
}
.tier-card.featured .tier-name,
.tier-card.featured .tier-price,
.tier-card.featured .tier-limit {
color: white;
}
.tier-name {
font-size: 0.85rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 0.5rem;
color: var(--color-text);
}
.tier-price {
font-size: 2rem;
font-weight: 700;
color: var(--color-text);
line-height: 1.2;
}
.tier-limit {
font-size: 0.8rem;
color: var(--color-text-light);
margin-top: 0.25rem;
}
.tier-body {
padding: 1.25rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
}
.tier-perk {
display: flex;
gap: 0.5rem;
font-size: 0.9rem;
line-height: 1.4;
}
.tier-perk .perk-icon {
flex-shrink: 0;
width: 20px;
text-align: center;
color: var(--color-forest);
font-weight: 600;
}
.tier-perk .perk-label {
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--color-text-light);
display: block;
margin-bottom: 0.1rem;
}
.tier-perk.empty {
color: #ccc;
}
.tier-footer {
padding: 1.25rem;
border-top: 1px solid #f0f0f0;
text-align: center;
}
.tier-cta {
display: inline-block;
padding: 0.6rem 1.5rem;
border-radius: 6px;
font-size: 0.9rem;
font-weight: 600;
text-decoration: none;
text-transform: uppercase;
letter-spacing: 0.04em;
border: 1px solid var(--color-text);
color: var(--color-text);
transition: all 0.2s;
}
.tier-cta:hover {
background: var(--color-text);
color: white;
}
.tier-card.featured .tier-cta {
background: var(--color-forest);
color: white;
border-color: var(--color-forest);
}
.tier-card.featured .tier-cta:hover {
background: #3a6b1e;
}
/* Footnote */
.footnote {
text-align: center;
font-size: 0.85rem;
color: var(--color-text-light);
font-style: italic;
margin-bottom: 3rem;
}
/* CTA */
.cta-section {
text-align: center;
padding: 2.5rem;
background: #f9f9f6;
border-radius: 12px;
}
.cta-section h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.cta-section p {
color: var(--color-text-light);
margin-bottom: 1rem;
}
.cta-section a {
color: var(--color-link);
font-weight: 600;
}
@ -114,25 +271,202 @@
}
.footer a:hover { text-decoration: underline; }
/* Responsive */
@media (max-width: 900px) {
.tiers {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 550px) {
.tiers {
grid-template-columns: 1fr;
}
.content { padding: 2rem 1rem; }
}
</style>
</head>
<body>
<header class="header">
<div class="header-container">
<a href="/">Valley of the Commons</a>
<a href="/apply.html">Apply Now</a>
<a href="/apply.html">Register Now</a>
</div>
</header>
<div class="content">
<h1>Sponsorships</h1>
<p class="subtitle">Aug 24 Sep 20, 2026 · Höllental, Austrian Alps</p>
<p class="coming-soon">Details coming soon. We're finalizing sponsorship packages for Valley of the Commons and will share more here shortly.</p>
<p class="contact">Interested? Reach out at <a href="mailto:team@valleyofthecommons.com">team@valleyofthecommons.com</a></p>
<div class="page-header">
<h1>Sponsorship Tiers</h1>
<p class="subtitle">Aug 24 &ndash; Sep 20, 2026 &middot; H&ouml;llental, Austrian Alps</p>
<p class="intro-text">Support the commons and gain visibility with a community of practitioners, researchers, and builders shaping life beyond extractive systems.</p>
</div>
<div class="tiers">
<!-- Friend of the Commons -->
<div class="tier-card">
<div class="tier-header">
<div class="tier-name">Friend of<br>the Commons</div>
<div class="tier-price">&euro;1,000</div>
<div class="tier-limit">Unlimited spots</div>
</div>
<div class="tier-body">
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Ticket Voucher</span>&euro;250</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Visibility</span>Name on sponsor list</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Public Gratitude</span>Social media recognition</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Stage Access</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Video Assets</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Branded Handouts</div>
</div>
</div>
<div class="tier-footer">
<a href="mailto:team@valleyofthecommons.com?subject=Sponsorship%20—%20Friend%20of%20the%20Commons" class="tier-cta">Get in Touch</a>
</div>
</div>
<!-- Symbiont Supporter -->
<div class="tier-card">
<div class="tier-header">
<div class="tier-name">Symbiont<br>Supporter</div>
<div class="tier-price">&euro;2,500</div>
<div class="tier-limit">10 spots</div>
</div>
<div class="tier-body">
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Ticket Voucher</span>&euro;625</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Visibility</span>Small logo on website</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Public Gratitude</span>Social media recognition</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Stage Access</span>25 min exclusive session (no competing sessions)</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Video Assets</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Branded Handouts</div>
</div>
</div>
<div class="tier-footer">
<a href="mailto:team@valleyofthecommons.com?subject=Sponsorship%20—%20Symbiont%20Supporter" class="tier-cta">Get in Touch</a>
</div>
</div>
<!-- Spore Partner -->
<div class="tier-card featured">
<div class="tier-header">
<div class="tier-name">Spore<br>Partner</div>
<div class="tier-price">&euro;5,000</div>
<div class="tier-limit">5 spots</div>
</div>
<div class="tier-body">
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Ticket Voucher</span>&euro;1,250</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Visibility</span>Mid-sized logo on website &amp; signage</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Public Gratitude</span>Social media recognition + naming in opening &amp; closing ceremony</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Stage Access</span>45 min exclusive session</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Video Assets</span>Short clip for your socials</div>
</div>
<div class="tier-perk empty">
<span class="perk-icon">&mdash;</span>
<div>Branded Handouts</div>
</div>
</div>
<div class="tier-footer">
<a href="mailto:team@valleyofthecommons.com?subject=Sponsorship%20—%20Spore%20Partner" class="tier-cta">Get in Touch</a>
</div>
</div>
<!-- Mycelium Partner -->
<div class="tier-card">
<div class="tier-header">
<div class="tier-name">Mycelium<br>Partner</div>
<div class="tier-price">&euro;10,000</div>
<div class="tier-limit">3 spots</div>
</div>
<div class="tier-body">
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Ticket Voucher</span>&euro;2,500</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Visibility</span>Central logo on website &amp; signage</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Public Gratitude</span>Social media recognition + naming in opening &amp; closing ceremony</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Stage Access</span>2&times; 45 min exclusive session</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Video Assets</span>Extended feature</div>
</div>
<div class="tier-perk">
<span class="perk-icon">&check;</span>
<div><span class="perk-label">Branded Handouts</span>Cosmo-local gimmicks* produced in our fablab</div>
</div>
</div>
<div class="tier-footer">
<a href="mailto:team@valleyofthecommons.com?subject=Sponsorship%20—%20Mycelium%20Partner" class="tier-cta">Get in Touch</a>
</div>
</div>
</div>
<p class="footnote">* Customized to your specific needs</p>
<div class="cta-section">
<h2>Interested in sponsoring?</h2>
<p>We'd love to discuss how we can work together. Every tier is customizable.</p>
<p><a href="mailto:team@valleyofthecommons.com">team@valleyofthecommons.com</a></p>
</div>
</div>
<footer class="footer">
<p>&copy; 2026 Commons Hub · <a href="mailto:team@valleyofthecommons.com">team@valleyofthecommons.com</a> · <a href="/privacy.html">Privacy Policy</a> · <a href="/">Home</a></p>
<p>&copy; 2026 Commons Hub &middot; <a href="mailto:team@valleyofthecommons.com">team@valleyofthecommons.com</a> &middot; <a href="/privacy.html">Privacy Policy</a> &middot; <a href="/">Home</a></p>
</footer>
</body>
</html>