Merge issue-44-templates-D (Refs #44)
This commit is contained in:
commit
8860349c9b
2 files changed
+241
-113
No files matched your search
@@ -0,0 +1,124 @@
|
||||
import { describe, test, expect, afterEach } from 'vitest';
|
||||
import { renderEditorHarness, EditorHarness } from '../test-utils/editorHarness';
|
||||
import { allTemplates, } from './index';
|
||||
import type { TemplateComponent } from './definitions';
|
||||
import { templateComponentToTreeNode } from './apply-template';
|
||||
import { buildNodeTree } from '../utils/craft-tree';
|
||||
import { exportBodyHtml, exportToHtml } from '../utils/html-export';
|
||||
import { DEFAULT_SITE_DESIGN } from '../state/SiteDesignContext';
|
||||
|
||||
/**
|
||||
* Group D (coming-soon, church, non-profit, fitness-gym) must carry its
|
||||
* palette/fonts as `var(--wsb-*)` token refs, not hard-coded copies of its own
|
||||
* `design` values -- otherwise changing Site Design after applying the
|
||||
* template would not restyle it.
|
||||
*/
|
||||
|
||||
const IDS = ['coming-soon', 'church', 'non-profit', 'fitness-gym'] as const;
|
||||
const COLOR_KEYS = [
|
||||
'primaryColor', 'secondaryColor', 'accentColor', 'backgroundColor', 'textColor',
|
||||
'mutedTextColor', 'borderColor', 'linkColor',
|
||||
] as const;
|
||||
const FONT_KEYS = ['headingFont', 'bodyFont'] as const;
|
||||
|
||||
function tplOf(id: string) {
|
||||
return allTemplates.find((t) => t.id === id)!;
|
||||
}
|
||||
|
||||
/** Every string leaf in a props tree, with its dotted path. */
|
||||
function stringLeaves(v: unknown, path: string, out: { path: string; value: string }[]) {
|
||||
if (typeof v === 'string') out.push({ path, value: v });
|
||||
else if (Array.isArray(v)) v.forEach((x, i) => stringLeaves(x, `${path}[${i}]`, out));
|
||||
else if (v && typeof v === 'object') {
|
||||
for (const [k, x] of Object.entries(v)) stringLeaves(x, `${path}.${k}`, out);
|
||||
}
|
||||
}
|
||||
|
||||
function collectLeaves(comps: TemplateComponent[], base: string, out: { path: string; value: string }[]) {
|
||||
comps.forEach((c, i) => {
|
||||
const p = `${base}/${c.type}[${i}]`;
|
||||
stringLeaves(c.props, `${p}.props`, out);
|
||||
if (c.children) collectLeaves(c.children, p, out);
|
||||
});
|
||||
}
|
||||
|
||||
function allLeaves(id: string) {
|
||||
const t = tplOf(id);
|
||||
const out: { path: string; value: string }[] = [];
|
||||
collectLeaves(t.header.components, 'header', out);
|
||||
collectLeaves(t.footer.components, 'footer', out);
|
||||
for (const p of t.pages) collectLeaves(p.content.components, `page:${p.slug}`, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
let harness: EditorHarness | null = null;
|
||||
afterEach(() => {
|
||||
harness?.unmount();
|
||||
harness = null;
|
||||
});
|
||||
|
||||
describe.each(IDS)('tokens-D: %s', (id) => {
|
||||
const t = tplOf(id);
|
||||
const design = t.design as Record<string, string>;
|
||||
|
||||
test('template exists', () => {
|
||||
expect(t).toBeDefined();
|
||||
});
|
||||
|
||||
test('no prop string equals one of the template\'s own design colours or fonts', () => {
|
||||
const own = new Set<string>();
|
||||
for (const k of [...COLOR_KEYS, ...FONT_KEYS]) {
|
||||
if (design[k]) own.add(design[k].toLowerCase());
|
||||
}
|
||||
const offenders = allLeaves(id).filter((l) => own.has(l.value.trim().toLowerCase()));
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
test('the primary hex never appears inside any prop string (gradients included)', () => {
|
||||
const primary = design.primaryColor.toLowerCase();
|
||||
const offenders = allLeaves(id).filter((l) => l.value.toLowerCase().includes(primary));
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
test('uses token refs and no colour-mix / relative colour syntax', () => {
|
||||
const leaves = allLeaves(id);
|
||||
expect(leaves.some((l) => l.value.includes('var(--wsb-primary)'))).toBe(true);
|
||||
expect(leaves.some((l) => l.value.includes('var(--wsb-secondary)'))).toBe(true);
|
||||
expect(leaves.filter((l) => /color-mix\(|\bfrom\s+var\(/.test(l.value))).toEqual([]);
|
||||
});
|
||||
|
||||
test('applies through templateComponentToTreeNode + buildNodeTree in a real editor without throwing', () => {
|
||||
harness = renderEditorHarness();
|
||||
for (const page of t.pages) {
|
||||
for (const comp of page.content.components) {
|
||||
expect(() => {
|
||||
harness!.act(() => {
|
||||
const tree = buildNodeTree(harness!.query, templateComponentToTreeNode(comp));
|
||||
harness!.actions.addNodeTree(tree, 'ROOT');
|
||||
});
|
||||
}).not.toThrow();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('exported body of the first page carries var(--wsb-primary) and not the primary hex', () => {
|
||||
harness = renderEditorHarness();
|
||||
for (const comp of t.pages[0].content.components) {
|
||||
harness.act(() => {
|
||||
const tree = buildNodeTree(harness!.query, templateComponentToTreeNode(comp));
|
||||
harness!.actions.addNodeTree(tree, 'ROOT');
|
||||
});
|
||||
}
|
||||
const serialized = harness.getSerialized();
|
||||
const { html } = exportBodyHtml(serialized);
|
||||
expect(html).toContain('var(--wsb-primary)');
|
||||
expect(html.toLowerCase()).not.toContain(design.primaryColor.toLowerCase());
|
||||
|
||||
// Full document: :root defines the template primary, body does not repeat it.
|
||||
const full = exportToHtml(serialized, { design: { ...DEFAULT_SITE_DESIGN, ...t.design } }).html;
|
||||
const rootDecl = `--wsb-primary:${design.primaryColor}`;
|
||||
expect(full.toLowerCase()).toContain(rootDecl.toLowerCase());
|
||||
const body = full.slice(full.toLowerCase().indexOf('<body'));
|
||||
expect(body.toLowerCase()).not.toContain(design.primaryColor.toLowerCase());
|
||||
});
|
||||
});
|
||||
+117
-113
@@ -2219,6 +2219,7 @@ const comingSoon: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#0f0f1a',
|
||||
textColor: '#e0e7ff',
|
||||
mutedTextColor: '#a5b4fc',
|
||||
navStyle: 'dark',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -2226,11 +2227,11 @@ const comingSoon: TemplateDefinition = {
|
||||
[
|
||||
{ text: 'Notify Me', href: '#notify', isCta: true },
|
||||
],
|
||||
'#0f0f1a',
|
||||
'#818cf8',
|
||||
'#6366f1',
|
||||
tokenRef('secondary'),
|
||||
tokenRef('accent'),
|
||||
tokenRef('primary'),
|
||||
),
|
||||
footer: makeFooterContent('Launchpad', '#0f0f1a', '#4338ca', {
|
||||
footer: makeFooterContent('Launchpad', tokenRef('secondary'), '#4338ca', {
|
||||
tagline: 'Something amazing is coming.',
|
||||
socialText: 'Twitter \u00b7 Discord',
|
||||
}),
|
||||
@@ -2242,23 +2243,23 @@ const comingSoon: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '160px 20px 80px', backgroundColor: '#0f0f1a', textAlign: 'center' } },
|
||||
props: { style: { padding: '160px 20px 80px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Something Big Is Coming', level: 'h1', style: { fontSize: '52px', fontWeight: '700', color: '#ffffff', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We are building something that will change the way you work. Be the first to know when we launch.', style: { color: '#a5b4fc', fontSize: '18px', maxWidth: '550px', margin: '0 auto 40px', lineHeight: '1.7' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We are building something that will change the way you work. Be the first to know when we launch.', style: { color: tokenRef('muted'), fontSize: '18px', maxWidth: '550px', margin: '0 auto 40px', lineHeight: '1.7' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '0 20px 60px', backgroundColor: '#0f0f1a', textAlign: 'center' } },
|
||||
props: { style: { padding: '0 20px 60px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Launching Q3 2026', level: 'h3', style: { color: '#6366f1', fontSize: '20px', fontWeight: '600', letterSpacing: '2px', marginBottom: '40px' } } },
|
||||
{ type: 'Heading', props: { text: 'Launching Q3 2026', level: 'h3', style: { color: tokenRef('primary'), fontSize: '20px', fontWeight: '600', letterSpacing: '2px', marginBottom: '40px' } } },
|
||||
{
|
||||
type: 'FormContainer',
|
||||
props: { style: { maxWidth: '460px', margin: '0 auto' } },
|
||||
children: [
|
||||
{ type: 'InputField', props: { label: '', placeholder: 'Enter your email address', inputType: 'email' } },
|
||||
{ type: 'FormButton', props: { text: 'Notify Me', style: { backgroundColor: '#6366f1', color: '#ffffff', width: '100%' } } },
|
||||
{ type: 'FormButton', props: { text: 'Notify Me', style: { backgroundColor: tokenRef('primary'), color: '#ffffff', width: '100%' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -2276,10 +2277,10 @@ const comingSoon: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#0f0f1a', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Follow the Journey', level: 'h3', style: { color: '#e0e7ff', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Star us on GitHub for updates, or follow along on Twitter and Discord for behind-the-scenes progress.', style: { color: '#818cf8', lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Follow the Journey', level: 'h3', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Star us on GitHub for updates, or follow along on Twitter and Discord for behind-the-scenes progress.', style: { color: tokenRef('accent'), lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -2309,6 +2310,7 @@ const church: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#fff7ed',
|
||||
textColor: '#7c2d12',
|
||||
mutedTextColor: '#9a3412',
|
||||
navStyle: 'light',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -2319,11 +2321,11 @@ const church: TemplateDefinition = {
|
||||
{ text: 'Events', href: 'events' },
|
||||
{ text: 'Visit Us', href: 'contact', isCta: true },
|
||||
],
|
||||
'#fff7ed',
|
||||
'#7c2d12',
|
||||
'#c2410c',
|
||||
tokenRef('bg'),
|
||||
tokenRef('text'),
|
||||
tokenRef('primary'),
|
||||
),
|
||||
footer: makeFooterContent('Grace Community Church', '#7c2d12', '#fdba74', {
|
||||
footer: makeFooterContent('Grace Community Church', tokenRef('secondary'), '#fdba74', {
|
||||
tagline: 'All are welcome.',
|
||||
contactText: '789 Faith Avenue, Springfield \u00b7 (555) 456-7890',
|
||||
socialText: 'Facebook \u00b7 YouTube \u00b7 Instagram',
|
||||
@@ -2341,7 +2343,7 @@ const church: TemplateDefinition = {
|
||||
subtitle: 'A place of faith, fellowship, and love. Join us every Sunday at 10 AM for worship.',
|
||||
buttonText: 'Plan Your Visit',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #7c2d12 0%, #9a3412 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #9a3412 100%)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -2352,23 +2354,23 @@ const church: TemplateDefinition = {
|
||||
{ title: 'Youth Programs', description: 'Activities and mentorship for teens and young adults every Wednesday evening.', icon: '\ud83c\udf1f' },
|
||||
{ title: 'Community Service', description: 'Monthly outreach events, food drives, and volunteer opportunities.', icon: '\u2764\ufe0f' },
|
||||
],
|
||||
style: { backgroundColor: '#fff7ed' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '80px 20px', backgroundColor: '#ffffff', textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Mission', level: 'h2', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'To love God, love people, and serve our community. We believe every person has a place at our table, regardless of where they are on their journey.', style: { color: '#9a3412', lineHeight: '1.8', maxWidth: '650px', margin: '0 auto', fontSize: '16px' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Mission', level: 'h2', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'To love God, love people, and serve our community. We believe every person has a place at our table, regardless of where they are on their journey.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '650px', margin: '0 auto', fontSize: '16px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'This Week at Grace', level: 'h2', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px', textAlign: 'center' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Sunday 10 AM \u2014 Worship Service\nWednesday 6:30 PM \u2014 Youth Group\nFriday 7 PM \u2014 Community Dinner\nSaturday 9 AM \u2014 Volunteer Day at the Food Bank', style: { color: '#9a3412', lineHeight: '2', textAlign: 'center', maxWidth: '500px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'This Week at Grace', level: 'h2', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px', textAlign: 'center' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Sunday 10 AM \u2014 Worship Service\nWednesday 6:30 PM \u2014 Youth Group\nFriday 7 PM \u2014 Community Dinner\nSaturday 9 AM \u2014 Volunteer Day at the Food Bank', style: { color: tokenRef('muted'), lineHeight: '2', textAlign: 'center', maxWidth: '500px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2378,7 +2380,7 @@ const church: TemplateDefinition = {
|
||||
description: '10:00 AM at Grace Community Church, 789 Faith Ave.',
|
||||
buttonText: 'Get Directions',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #c2410c 0%, #ea580c 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, ${tokenRef('accent')} 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2391,10 +2393,10 @@ const church: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'About Grace Community', level: 'h1', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Grace Community Church was founded in 1982 with a simple vision: to be a place where everyone feels welcome. Over four decades, we have grown into a vibrant congregation of 500+ families united by faith and friendship.\n\nOur pastor, Rev. David Harper, has led our church since 2015 with warmth, wisdom, and a heart for service.', style: { color: '#9a3412', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'About Grace Community', level: 'h1', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Grace Community Church was founded in 1982 with a simple vision: to be a place where everyone feels welcome. Over four decades, we have grown into a vibrant congregation of 500+ families united by faith and friendship.\n\nOur pastor, Rev. David Harper, has led our church since 2015 with warmth, wisdom, and a heart for service.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2410,10 +2412,10 @@ const church: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'What We Believe', level: 'h2', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We believe in the love of God, the grace of Jesus Christ, and the fellowship of the Holy Spirit. We are a non-denominational church that welcomes all people. Our worship is contemporary and engaging, our teaching is Bible-based and practical, and our community is warm and authentic.', style: { color: '#9a3412', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'What We Believe', level: 'h2', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We believe in the love of God, the grace of Jesus Christ, and the fellowship of the Holy Spirit. We are a non-denominational church that welcomes all people. Our worship is contemporary and engaging, our teaching is Bible-based and practical, and our community is warm and authentic.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -2426,23 +2428,23 @@ const church: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Upcoming Events', level: 'h1', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Easter Sunday Celebration \u2014 April 20', level: 'h3', style: { color: '#7c2d12', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Special service at 10 AM with live music, children\'s activities, and a community brunch afterward.', style: { color: '#9a3412', marginBottom: '24px' } } },
|
||||
{ type: 'Heading', props: { text: 'Summer Bible Camp \u2014 June 15-19', level: 'h3', style: { color: '#7c2d12', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'A week of fun, learning, and fellowship for kids ages 6-14. Registration now open.', style: { color: '#9a3412', marginBottom: '24px' } } },
|
||||
{ type: 'Heading', props: { text: 'Community Food Drive \u2014 First Saturday Monthly', level: 'h3', style: { color: '#7c2d12', marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Help us serve families in need. Drop off donations at the church or volunteer to help distribute.', style: { color: '#9a3412' } } },
|
||||
{ type: 'Heading', props: { text: 'Upcoming Events', level: 'h1', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Easter Sunday Celebration \u2014 April 20', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Special service at 10 AM with live music, children\'s activities, and a community brunch afterward.', style: { color: tokenRef('muted'), marginBottom: '24px' } } },
|
||||
{ type: 'Heading', props: { text: 'Summer Bible Camp \u2014 June 15-19', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'A week of fun, learning, and fellowship for kids ages 6-14. Registration now open.', style: { color: tokenRef('muted'), marginBottom: '24px' } } },
|
||||
{ type: 'Heading', props: { text: 'Community Food Drive \u2014 First Saturday Monthly', level: 'h3', style: { color: tokenRef('text'), marginBottom: '8px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Help us serve families in need. Drop off donations at the church or volunteer to help distribute.', style: { color: tokenRef('muted') } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#ffffff' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Weekly Schedule', level: 'h2', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Sunday 10:00 AM \u2014 Worship Service\nSunday 11:30 AM \u2014 Children\'s Church\nWednesday 6:30 PM \u2014 Youth Group\nWednesday 7:00 PM \u2014 Adult Bible Study\nFriday 7:00 PM \u2014 Community Dinner (1st & 3rd Friday)', style: { color: '#9a3412', lineHeight: '2' } } },
|
||||
{ type: 'Heading', props: { text: 'Weekly Schedule', level: 'h2', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Sunday 10:00 AM \u2014 Worship Service\nSunday 11:30 AM \u2014 Children\'s Church\nWednesday 6:30 PM \u2014 Youth Group\nWednesday 7:00 PM \u2014 Adult Bible Study\nFriday 7:00 PM \u2014 Community Dinner (1st & 3rd Friday)', style: { color: tokenRef('muted'), lineHeight: '2' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2452,7 +2454,7 @@ const church: TemplateDefinition = {
|
||||
description: 'There are many ways to serve. Contact us to learn about volunteer opportunities.',
|
||||
buttonText: 'Contact Us',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #c2410c 0%, #ea580c 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, ${tokenRef('accent')} 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2465,10 +2467,10 @@ const church: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Visit & Contact Us', level: 'h1', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '789 Faith Avenue, Springfield\nPhone: (555) 456-7890\nEmail: info@gracecommunity.org\n\nSunday Service: 10:00 AM\nOffice Hours: Mon-Fri 9 AM - 4 PM', style: { color: '#9a3412', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Visit & Contact Us', level: 'h1', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '789 Faith Avenue, Springfield\nPhone: (555) 456-7890\nEmail: info@gracecommunity.org\n\nSunday Service: 10:00 AM\nOffice Hours: Mon-Fri 9 AM - 4 PM', style: { color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2482,17 +2484,17 @@ const church: TemplateDefinition = {
|
||||
{ type: 'InputField', props: { label: 'Name', placeholder: 'Your name' } },
|
||||
{ type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } },
|
||||
{ type: 'TextareaField', props: { label: 'Message', placeholder: 'How can we help?' } },
|
||||
{ type: 'FormButton', props: { text: 'Send Message', style: { backgroundColor: '#c2410c', color: '#ffffff' } } },
|
||||
{ type: 'FormButton', props: { text: 'Send Message', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#fff7ed', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'New Here?', level: 'h3', style: { fontFamily: 'Merriweather, serif', color: '#7c2d12', marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We would love to meet you! Come as you are \u2014 no dress code, no expectations. Just a warm welcome and a great cup of coffee.', style: { color: '#9a3412', lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'New Here?', level: 'h3', style: { fontFamily: tokenRef('heading-font'), color: tokenRef('text'), marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'We would love to meet you! Come as you are \u2014 no dress code, no expectations. Just a warm welcome and a great cup of coffee.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -2518,6 +2520,7 @@ const nonProfit: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#f0fdf4',
|
||||
textColor: '#14532d',
|
||||
mutedTextColor: '#166534',
|
||||
navStyle: 'dark',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -2528,11 +2531,11 @@ const nonProfit: TemplateDefinition = {
|
||||
{ text: 'Get Involved', href: 'get-involved' },
|
||||
{ text: 'Donate', href: 'contact', isCta: true },
|
||||
],
|
||||
'#14532d',
|
||||
tokenRef('secondary'),
|
||||
'#bbf7d0',
|
||||
'#16a34a',
|
||||
tokenRef('primary'),
|
||||
),
|
||||
footer: makeFooterContent('GreenPath Foundation', '#14532d', '#4ade80', {
|
||||
footer: makeFooterContent('GreenPath Foundation', tokenRef('secondary'), tokenRef('accent'), {
|
||||
tagline: 'A 501(c)(3) nonprofit organization.',
|
||||
contactText: '1234 Conservation Way, Portland, OR \u00b7 info@greenpathfoundation.org',
|
||||
socialText: 'Facebook \u00b7 Instagram \u00b7 Twitter \u00b7 YouTube',
|
||||
@@ -2550,7 +2553,7 @@ const nonProfit: TemplateDefinition = {
|
||||
subtitle: 'GreenPath Foundation plants trees, restores wetlands, and educates communities to fight climate change \u2014 one acre at a time.',
|
||||
buttonText: 'Learn More',
|
||||
buttonHref: 'mission',
|
||||
gradient: 'linear-gradient(135deg, #14532d 0%, #166534 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #166534 100%)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -2561,15 +2564,15 @@ const nonProfit: TemplateDefinition = {
|
||||
{ title: '50K Volunteers', description: 'A global network of passionate individuals making a local impact.', icon: '\ud83e\udd1d' },
|
||||
{ title: '95% to Programs', description: '95 cents of every dollar goes directly to conservation programs.', icon: '\ud83d\udc9a' },
|
||||
],
|
||||
style: { backgroundColor: '#f0fdf4' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '80px 20px', backgroundColor: '#ffffff', textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Impact in Numbers', level: 'h2', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '12 countries \u00b7 2 million trees \u00b7 500 acres of wetland restored \u00b7 10,000 students educated\nEvery action counts. Every dollar makes a difference.', style: { color: '#166534', lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Impact in Numbers', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '12 countries \u00b7 2 million trees \u00b7 500 acres of wetland restored \u00b7 10,000 students educated\nEvery action counts. Every dollar makes a difference.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '600px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2579,7 +2582,7 @@ const nonProfit: TemplateDefinition = {
|
||||
description: '$10 plants 5 trees. $50 restores a wetland plot. $100 funds a school garden.',
|
||||
buttonText: 'Donate Now',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #15803d 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2592,10 +2595,10 @@ const nonProfit: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Mission', level: 'h1', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation exists to combat climate change through reforestation, wetland restoration, and environmental education. We believe that community-driven conservation is the most effective path to a sustainable future.\n\nFounded in 2015 by environmental scientist Dr. Amara Osei, we have grown from a local tree-planting initiative into a global organization operating in 12 countries.', style: { color: '#166534', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Mission', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation exists to combat climate change through reforestation, wetland restoration, and environmental education. We believe that community-driven conservation is the most effective path to a sustainable future.\n\nFounded in 2015 by environmental scientist Dr. Amara Osei, we have grown from a local tree-planting initiative into a global organization operating in 12 countries.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2611,10 +2614,10 @@ const nonProfit: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Founder', level: 'h2', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dr. Amara Osei holds a PhD in Environmental Science from UC Berkeley and spent a decade researching deforestation patterns in West Africa before founding GreenPath. Her vision is simple: empower communities to restore their own ecosystems, and the planet will heal itself.', style: { color: '#166534', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Founder', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Dr. Amara Osei holds a PhD in Environmental Science from UC Berkeley and spent a decade researching deforestation patterns in West Africa before founding GreenPath. Her vision is simple: empower communities to restore their own ecosystems, and the planet will heal itself.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2624,7 +2627,7 @@ const nonProfit: TemplateDefinition = {
|
||||
description: 'Your donation directly funds tree planting, wetland restoration, and education programs.',
|
||||
buttonText: 'Donate Today',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #15803d 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2637,10 +2640,10 @@ const nonProfit: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Get Involved', level: 'h1', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'There are many ways to support our work and make a tangible impact on the environment.', style: { color: '#166534', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Get Involved', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'There are many ways to support our work and make a tangible impact on the environment.', style: { color: tokenRef('muted'), marginBottom: '32px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2656,10 +2659,10 @@ const nonProfit: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Upcoming Volunteer Events', level: 'h2', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'April 22 \u2014 Earth Day Tree Planting, Forest Park, Portland\nMay 10 \u2014 Wetland Cleanup Day, Columbia River Gorge\nJune 5 \u2014 World Environment Day School Workshops\nJuly 12 \u2014 Summer Planting Weekend, Mount Hood National Forest', style: { color: '#166534', lineHeight: '2', maxWidth: '600px' } } },
|
||||
{ type: 'Heading', props: { text: 'Upcoming Volunteer Events', level: 'h2', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'April 22 \u2014 Earth Day Tree Planting, Forest Park, Portland\nMay 10 \u2014 Wetland Cleanup Day, Columbia River Gorge\nJune 5 \u2014 World Environment Day School Workshops\nJuly 12 \u2014 Summer Planting Weekend, Mount Hood National Forest', style: { color: tokenRef('muted'), lineHeight: '2', maxWidth: '600px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2669,7 +2672,7 @@ const nonProfit: TemplateDefinition = {
|
||||
description: 'Sign up to volunteer or start a fundraising campaign today.',
|
||||
buttonText: 'Sign Up',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #15803d 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2682,18 +2685,18 @@ const nonProfit: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Contact & Donate', level: 'h1', style: { color: '#14532d', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation\n1234 Conservation Way, Portland, OR\ninfo@greenpathfoundation.org\n(555) 234-5678', style: { color: '#166534', lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Donate via PayPal', href: '#', style: { padding: '14px 32px', backgroundColor: '#16a34a', color: '#ffffff', borderRadius: '8px', fontWeight: '600', fontSize: '16px', display: 'inline-block' } } },
|
||||
{ type: 'Heading', props: { text: 'Contact & Donate', level: 'h1', style: { color: tokenRef('text'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation\n1234 Conservation Way, Portland, OR\ninfo@greenpathfoundation.org\n(555) 234-5678', style: { color: tokenRef('muted'), lineHeight: '1.8', marginBottom: '24px' } } },
|
||||
{ type: 'ButtonLink', props: { text: 'Donate via PayPal', href: '#', style: { padding: '14px 32px', backgroundColor: tokenRef('primary'), color: '#ffffff', borderRadius: '8px', fontWeight: '600', fontSize: '16px', display: 'inline-block' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px', backgroundColor: '#ffffff' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Send Us a Message', level: 'h2', style: { color: '#14532d', marginBottom: '20px' } } },
|
||||
{ type: 'Heading', props: { text: 'Send Us a Message', level: 'h2', style: { color: tokenRef('text'), marginBottom: '20px' } } },
|
||||
{
|
||||
type: 'FormContainer',
|
||||
props: { style: { maxWidth: '600px' } },
|
||||
@@ -2701,17 +2704,17 @@ const nonProfit: TemplateDefinition = {
|
||||
{ type: 'InputField', props: { label: 'Name', placeholder: 'Your name' } },
|
||||
{ type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } },
|
||||
{ type: 'TextareaField', props: { label: 'Message', placeholder: 'How would you like to get involved?' } },
|
||||
{ type: 'FormButton', props: { text: 'Send', style: { backgroundColor: '#16a34a', color: '#ffffff' } } },
|
||||
{ type: 'FormButton', props: { text: 'Send', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#f0fdf4', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Tax Deductible', level: 'h3', style: { color: '#14532d', marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation is a registered 501(c)(3) nonprofit organization. All donations are tax-deductible to the extent allowed by law. EIN: 82-1234567', style: { color: '#166534', lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Tax Deductible', level: 'h3', style: { color: tokenRef('text'), marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'GreenPath Foundation is a registered 501(c)(3) nonprofit organization. All donations are tax-deductible to the extent allowed by law. EIN: 82-1234567', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '500px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -2737,6 +2740,7 @@ const fitnessGym: TemplateDefinition = {
|
||||
bodyFont: 'Inter, sans-serif',
|
||||
backgroundColor: '#1c1c1c',
|
||||
textColor: '#fafafa',
|
||||
mutedTextColor: '#a3a3a3',
|
||||
navStyle: 'dark',
|
||||
},
|
||||
header: makeHeader(
|
||||
@@ -2747,11 +2751,11 @@ const fitnessGym: TemplateDefinition = {
|
||||
{ text: 'Trainers', href: 'trainers' },
|
||||
{ text: 'Join Now', href: 'contact', isCta: true },
|
||||
],
|
||||
'#18181b',
|
||||
'#fafafa',
|
||||
'#dc2626',
|
||||
tokenRef('secondary'),
|
||||
tokenRef('text'),
|
||||
tokenRef('primary'),
|
||||
),
|
||||
footer: makeFooterContent('IRONWORKS Fitness', '#18181b', '#737373', {
|
||||
footer: makeFooterContent('IRONWORKS Fitness', tokenRef('secondary'), '#737373', {
|
||||
tagline: 'No excuses. Just results.',
|
||||
contactText: '100 Iron Street, Downtown \u00b7 Open 24/7',
|
||||
socialText: 'Instagram \u00b7 YouTube \u00b7 TikTok',
|
||||
@@ -2769,7 +2773,7 @@ const fitnessGym: TemplateDefinition = {
|
||||
subtitle: 'State-of-the-art equipment, world-class trainers, and a community that pushes you to be your best. Your transformation starts here.',
|
||||
buttonText: 'Start Free Trial',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #18181b 0%, #292929 50%, #18181b 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('secondary')} 0%, #292929 50%, ${tokenRef('secondary')} 100%)`,
|
||||
style: { minHeight: '520px' },
|
||||
},
|
||||
},
|
||||
@@ -2781,23 +2785,23 @@ const fitnessGym: TemplateDefinition = {
|
||||
{ title: '24/7 Access', description: 'Train on your schedule. Our facility is open around the clock, every day of the year.', icon: '\ud83d\udd11' },
|
||||
{ title: 'Expert Coaching', description: 'Certified trainers who build personalized programs around your goals.', icon: '\ud83c\udfc6' },
|
||||
],
|
||||
style: { backgroundColor: '#1c1c1c' },
|
||||
style: { backgroundColor: tokenRef('bg') },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '80px 20px', backgroundColor: '#111111', textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: '5,000+ Members Strong', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Join the largest fitness community in the city. Whether you are a beginner or an elite athlete, IRONWORKS has the space, equipment, and coaching to help you reach your potential.', style: { color: '#a3a3a3', lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: '5,000+ Members Strong', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Join the largest fitness community in the city. Whether you are a beginner or an elite athlete, IRONWORKS has the space, equipment, and coaching to help you reach your potential.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '650px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Membership Plans', level: 'h2', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '24px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Basic \u2014 $29/month \u2014 Full gym access, locker room\nPro \u2014 $49/month \u2014 Gym + unlimited classes\nElite \u2014 $79/month \u2014 Everything + 4 personal training sessions', style: { textAlign: 'center', color: '#a3a3a3', lineHeight: '2', maxWidth: '500px', margin: '0 auto' } } },
|
||||
{ type: 'Heading', props: { text: 'Membership Plans', level: 'h2', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '24px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Basic \u2014 $29/month \u2014 Full gym access, locker room\nPro \u2014 $49/month \u2014 Gym + unlimited classes\nElite \u2014 $79/month \u2014 Everything + 4 personal training sessions', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '2', maxWidth: '500px', margin: '0 auto' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2807,7 +2811,7 @@ const fitnessGym: TemplateDefinition = {
|
||||
description: 'No commitment, no contracts. Just show up and see for yourself.',
|
||||
buttonText: 'Claim Free Trial',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #b91c1c 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2820,10 +2824,10 @@ const fitnessGym: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Class Schedule', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '50+ classes per week across all fitness levels.', style: { textAlign: 'center', color: '#a3a3a3', marginBottom: '40px' } } },
|
||||
{ type: 'Heading', props: { text: 'Class Schedule', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '50+ classes per week across all fitness levels.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2850,10 +2854,10 @@ const fitnessGym: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Weekly Schedule', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Mon/Wed/Fri 6 AM \u2014 HIIT Blast\nMon/Wed 7 PM \u2014 Boxing Fundamentals\nTue/Thu 6:30 AM \u2014 Spin Studio\nTue/Thu 12 PM \u2014 Power Yoga\nSat 9 AM \u2014 Strength Lab\nSun 10 AM \u2014 Recovery & Stretch', style: { color: '#a3a3a3', lineHeight: '2' } } },
|
||||
{ type: 'Heading', props: { text: 'Weekly Schedule', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Mon/Wed/Fri 6 AM \u2014 HIIT Blast\nMon/Wed 7 PM \u2014 Boxing Fundamentals\nTue/Thu 6:30 AM \u2014 Spin Studio\nTue/Thu 12 PM \u2014 Power Yoga\nSat 9 AM \u2014 Strength Lab\nSun 10 AM \u2014 Recovery & Stretch', style: { color: tokenRef('muted'), lineHeight: '2' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2863,7 +2867,7 @@ const fitnessGym: TemplateDefinition = {
|
||||
description: 'Your first class is always free. No booking required \u2014 just show up.',
|
||||
buttonText: 'Get Started',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #b91c1c 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2876,10 +2880,10 @@ const fitnessGym: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Our Trainers', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Certified professionals who are passionate about your success.', style: { textAlign: 'center', color: '#a3a3a3', marginBottom: '40px' } } },
|
||||
{ type: 'Heading', props: { text: 'Our Trainers', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Certified professionals who are passionate about your success.', style: { textAlign: 'center', color: tokenRef('muted'), marginBottom: '40px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2895,10 +2899,10 @@ const fitnessGym: TemplateDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Personal Training', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'All our trainers offer 1-on-1 personal training sessions. Whether your goal is weight loss, muscle gain, athletic performance, or injury rehabilitation, we will build a custom program tailored to your needs.\n\nSingle session: $65 \u00b7 5-pack: $275 \u00b7 10-pack: $500\nElite members get 4 sessions included monthly.', style: { color: '#a3a3a3', lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
{ type: 'Heading', props: { text: 'Personal Training', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'All our trainers offer 1-on-1 personal training sessions. Whether your goal is weight loss, muscle gain, athletic performance, or injury rehabilitation, we will build a custom program tailored to your needs.\n\nSingle session: $65 \u00b7 5-pack: $275 \u00b7 10-pack: $500\nElite members get 4 sessions included monthly.', style: { color: tokenRef('muted'), lineHeight: '1.8', maxWidth: '700px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2908,7 +2912,7 @@ const fitnessGym: TemplateDefinition = {
|
||||
description: 'Meet with a trainer for a free goal-setting consultation.',
|
||||
buttonText: 'Contact Us',
|
||||
buttonHref: 'contact',
|
||||
gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)',
|
||||
gradient: `linear-gradient(135deg, ${tokenRef('primary')} 0%, #b91c1c 100%)`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -2921,17 +2925,17 @@ const fitnessGym: TemplateDefinition = {
|
||||
components: [
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg') } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Join IRONWORKS', level: 'h1', style: { textAlign: 'center', color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '100 Iron Street, Downtown\nPhone: (555) 789-0123\nOpen 24/7, 365 days a year', style: { textAlign: 'center', color: '#a3a3a3', lineHeight: '1.8', marginBottom: '32px' } } },
|
||||
{ type: 'Heading', props: { text: 'Join IRONWORKS', level: 'h1', style: { textAlign: 'center', color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '16px' } } },
|
||||
{ type: 'TextBlock', props: { text: '100 Iron Street, Downtown\nPhone: (555) 789-0123\nOpen 24/7, 365 days a year', style: { textAlign: 'center', color: tokenRef('muted'), lineHeight: '1.8', marginBottom: '32px' } } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '40px 20px', backgroundColor: '#111111' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Start Your Free Trial', level: 'h2', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '20px' } } },
|
||||
{ type: 'Heading', props: { text: 'Start Your Free Trial', level: 'h2', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '20px' } } },
|
||||
{
|
||||
type: 'FormContainer',
|
||||
props: { style: { maxWidth: '600px', margin: '0 auto' } },
|
||||
@@ -2940,17 +2944,17 @@ const fitnessGym: TemplateDefinition = {
|
||||
{ type: 'InputField', props: { label: 'Email', placeholder: 'your@email.com', inputType: 'email' } },
|
||||
{ type: 'InputField', props: { label: 'Phone', placeholder: '(555) 000-0000' } },
|
||||
{ type: 'TextareaField', props: { label: 'Fitness Goals', placeholder: 'What are you looking to achieve?' } },
|
||||
{ type: 'FormButton', props: { text: 'Claim Free Trial', style: { backgroundColor: '#dc2626', color: '#ffffff' } } },
|
||||
{ type: 'FormButton', props: { text: 'Claim Free Trial', style: { backgroundColor: tokenRef('primary'), color: '#ffffff' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Section',
|
||||
props: { style: { padding: '60px 20px', backgroundColor: '#1c1c1c', textAlign: 'center' } },
|
||||
props: { style: { padding: '60px 20px', backgroundColor: tokenRef('bg'), textAlign: 'center' } },
|
||||
children: [
|
||||
{ type: 'Heading', props: { text: 'Follow Us', level: 'h3', style: { color: '#fafafa', fontFamily: 'Montserrat, sans-serif', marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Workout tips, member spotlights, and class previews on our social channels.\nInstagram \u00b7 YouTube \u00b7 TikTok', style: { color: '#a3a3a3', lineHeight: '1.8' } } },
|
||||
{ type: 'Heading', props: { text: 'Follow Us', level: 'h3', style: { color: tokenRef('text'), fontFamily: tokenRef('heading-font'), marginBottom: '12px' } } },
|
||||
{ type: 'TextBlock', props: { text: 'Workout tips, member spotlights, and class previews on our social channels.\nInstagram \u00b7 YouTube \u00b7 TikTok', style: { color: tokenRef('muted'), lineHeight: '1.8' } } },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user