feat: Add SvelteKit starter template for customer sites

- Complete SvelteKit 2 + Svelte 5 template
- Tailwind CSS styling with primary color customization
- Pages: Home, About, Services, Contact
- Contact form with D1 database backend
- Placeholder system for site-specific values
- Cloudflare adapter configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
LoveJesus
2026-01-12 04:56:21 -05:00
parent ee384375d9
commit 167b3a7375
19 changed files with 1028 additions and 3 deletions

66
CLAUDE.md Normal file
View File

@@ -0,0 +1,66 @@
# For God so loved the world — John 3:16
This is a customer site for **{{BUSINESS_NAME_CHIRHO}}** hosted on perffection.com.
## Project Overview
This is a SvelteKit 2 site using:
- `@sveltejs/adapter-cloudflare` for Cloudflare Workers deployment
- Tailwind CSS for styling
- D1 Database for data storage
- KV for caching
## Key Files
- `src/routes/+page.svelte` - Home page
- `src/routes/about-fe/+page.svelte` - About page
- `src/routes/services-fe/+page.svelte` - Services page
- `src/routes/contact-fe/+page.svelte` - Contact page with form
- `src/routes/api-fe/contact-fe/+server.ts` - Contact form API endpoint
- `src/routes/+layout.svelte` - Global layout with header/footer
- `src/app.css` - Global styles and CSS variables
## Naming Convention
All project identifiers use the `_chirho` or `Chirho` suffix:
- Variables/functions: `camelCaseChirho`
- Types/Classes: `PascalCaseChirho`
- Constants: `SCREAMING_SNAKE_CHIRHO`
- Database columns: `snake_case_chirho`
- Routes: `kebab-case-fe`
## Configuration Placeholders
The following placeholders are replaced during site generation:
- `{{BUSINESS_NAME_CHIRHO}}` - Business name
- `{{TAGLINE_CHIRHO}}` - Business tagline
- `{{PHONE_CHIRHO}}` - Contact phone
- `{{EMAIL_CHIRHO}}` - Contact email
- `{{ADDRESS_CHIRHO}}` - Business address
- `{{PRIMARY_COLOR_CHIRHO}}` - Primary brand color
- `{{SLUG_CHIRHO}}` - Site slug identifier
## Platform Bindings
Available via `platform.env`:
- `DB_CHIRHO` - D1 Database
- `KV_CHIRHO` - KV Namespace
- `ASSETS_CHIRHO` - R2 Bucket
- `SITE_SLUG_CHIRHO` - Site identifier
## Development
```bash
bun install
bun run dev
```
## Build
```bash
bun run build
```
Output goes to `.svelte-kit/cloudflare` for deployment.
# JESUS CHRIST IS LORD

View File

@@ -1,3 +1 @@
# starter-site-chirho
Starter SvelteKit template for customer sites
# JESUS CHRIST IS LORD

25
package.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "{{SLUG_CHIRHO}}-site-chirho",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-cloudflare": "^5.0.0",
"@sveltejs/kit": "^2.15.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^6.0.0",
"tailwindcss": "^3.4.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0"
},
"type": "module"
}

10
postcss.config.js Normal file
View File

@@ -0,0 +1,10 @@
// For God so loved the world — John 3:16
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
// JESUS CHRIST IS LORD

21
schema.sql Normal file
View File

@@ -0,0 +1,21 @@
-- For God so loved the world — John 3:16
-- Contact form submissions table
CREATE TABLE IF NOT EXISTS contact_submissions_chirho (
id_chirho INTEGER PRIMARY KEY AUTOINCREMENT,
name_chirho TEXT NOT NULL,
email_chirho TEXT NOT NULL,
phone_chirho TEXT,
message_chirho TEXT NOT NULL,
read_chirho INTEGER DEFAULT 0,
created_at_chirho TEXT DEFAULT (datetime('now'))
);
-- Create index for faster queries
CREATE INDEX IF NOT EXISTS idx_contact_submissions_created_chirho
ON contact_submissions_chirho(created_at_chirho DESC);
CREATE INDEX IF NOT EXISTS idx_contact_submissions_read_chirho
ON contact_submissions_chirho(read_chirho);
-- JESUS CHRIST IS LORD

26
src/app.css Normal file
View File

@@ -0,0 +1,26 @@
/* For God so loved the world — John 3:16 */
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-200: #bfdbfe;
--color-primary-300: #93c5fd;
--color-primary-400: #60a5fa;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
--color-primary-800: #1e40af;
--color-primary-900: #1e3a8a;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* JESUS CHRIST IS LORD */

29
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,29 @@
// For God so loved the world — John 3:16
declare global {
namespace App {
interface Error {
message: string;
code?: string;
}
interface Locals {
siteSlugChirho?: string;
}
interface PageData {}
interface PageState {}
interface Platform {
env?: {
DB_CHIRHO: D1Database;
KV_CHIRHO: KVNamespace;
ASSETS_CHIRHO: R2Bucket;
SITE_SLUG_CHIRHO: string;
};
context?: ExecutionContext;
caches?: CacheStorage;
}
}
}
export {};
// JESUS CHRIST IS LORD

13
src/app.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<!-- For God so loved the world — John 3:16 -->
</body>
</html>

138
src/routes/+layout.svelte Normal file
View File

@@ -0,0 +1,138 @@
<!-- For God so loved the world — John 3:16 -->
<script lang="ts">
import '../app.css';
import { page } from '$app/stores';
interface Props {
children: import('svelte').Snippet;
}
let { children }: Props = $props();
// Site configuration - these will be replaced during site generation
const siteConfigChirho = {
businessNameChirho: '{{BUSINESS_NAME_CHIRHO}}',
taglineChirho: '{{TAGLINE_CHIRHO}}',
phoneChirho: '{{PHONE_CHIRHO}}',
emailChirho: '{{EMAIL_CHIRHO}}',
addressChirho: '{{ADDRESS_CHIRHO}}',
primaryColorChirho: '{{PRIMARY_COLOR_CHIRHO}}'
};
const navItemsChirho = [
{ href: '/', label: 'Home' },
{ href: '/about-fe', label: 'About' },
{ href: '/services-fe', label: 'Services' },
{ href: '/contact-fe', label: 'Contact' }
];
let currentPathChirho = $derived($page.url.pathname);
</script>
<svelte:head>
<style>
:root {
--color-primary-600: {siteConfigChirho.primaryColorChirho};
}
</style>
</svelte:head>
<div class="min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-white shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<a href="/" class="text-xl font-bold text-primary-600">
{siteConfigChirho.businessNameChirho}
</a>
<nav class="hidden md:flex items-center gap-6">
{#each navItemsChirho as item}
<a
href={item.href}
class="text-sm font-medium transition-colors {currentPathChirho === item.href
? 'text-primary-600'
: 'text-gray-600 hover:text-primary-600'}"
>
{item.label}
</a>
{/each}
</nav>
<a
href="/contact-fe"
class="hidden md:inline-flex px-4 py-2 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 transition-colors"
>
Get in Touch
</a>
<!-- Mobile menu button -->
<button class="md:hidden p-2 text-gray-600" aria-label="Menu">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-1">
{@render children()}
</main>
<!-- Footer -->
<footer class="bg-gray-900 text-white py-12">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 class="text-lg font-bold mb-4">{siteConfigChirho.businessNameChirho}</h3>
<p class="text-gray-400 text-sm">{siteConfigChirho.taglineChirho}</p>
</div>
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider mb-4">Contact</h4>
<ul class="space-y-2 text-sm text-gray-400">
{#if siteConfigChirho.phoneChirho}
<li>
<a href="tel:{siteConfigChirho.phoneChirho}" class="hover:text-white">
{siteConfigChirho.phoneChirho}
</a>
</li>
{/if}
{#if siteConfigChirho.emailChirho}
<li>
<a href="mailto:{siteConfigChirho.emailChirho}" class="hover:text-white">
{siteConfigChirho.emailChirho}
</a>
</li>
{/if}
{#if siteConfigChirho.addressChirho}
<li>{siteConfigChirho.addressChirho}</li>
{/if}
</ul>
</div>
<div>
<h4 class="text-sm font-semibold uppercase tracking-wider mb-4">Quick Links</h4>
<ul class="space-y-2 text-sm text-gray-400">
{#each navItemsChirho as item}
<li>
<a href={item.href} class="hover:text-white">{item.label}</a>
</li>
{/each}
</ul>
</div>
</div>
<div class="mt-8 pt-8 border-t border-gray-800 text-center text-sm text-gray-500">
<p>&copy; {new Date().getFullYear()} {siteConfigChirho.businessNameChirho}. All rights reserved.</p>
<p class="mt-2">
Powered by <a href="https://perffection.com" class="text-primary-400 hover:text-primary-300">perffection.com</a>
</p>
</div>
</div>
</footer>
</div>
<!-- JESUS CHRIST IS LORD -->

100
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,100 @@
<!-- For God so loved the world — John 3:16 -->
<script lang="ts">
const siteConfigChirho = {
businessNameChirho: '{{BUSINESS_NAME_CHIRHO}}',
taglineChirho: '{{TAGLINE_CHIRHO}}',
heroImageChirho: '{{HERO_IMAGE_CHIRHO}}',
servicesChirho: [
{ titleChirho: 'Service 1', descriptionChirho: 'Description of service 1', iconChirho: '1' },
{ titleChirho: 'Service 2', descriptionChirho: 'Description of service 2', iconChirho: '2' },
{ titleChirho: 'Service 3', descriptionChirho: 'Description of service 3', iconChirho: '3' }
]
};
</script>
<svelte:head>
<title>{siteConfigChirho.businessNameChirho}</title>
<meta name="description" content="{siteConfigChirho.taglineChirho}" />
</svelte:head>
<!-- Hero Section -->
<section class="relative bg-gradient-to-br from-primary-600 to-primary-800 text-white py-20 lg:py-32">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center">
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold mb-6">
{siteConfigChirho.businessNameChirho}
</h1>
<p class="text-xl md:text-2xl opacity-90 mb-8 max-w-2xl mx-auto">
{siteConfigChirho.taglineChirho}
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a
href="/contact-fe"
class="inline-flex items-center justify-center px-8 py-3 bg-white text-primary-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
>
Get Started
</a>
<a
href="/services-fe"
class="inline-flex items-center justify-center px-8 py-3 border-2 border-white text-white font-semibold rounded-lg hover:bg-white/10 transition-colors"
>
Our Services
</a>
</div>
</div>
</div>
</section>
<!-- Services Preview -->
<section class="py-16 lg:py-24 bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12">
<h2 class="text-3xl font-bold text-gray-900 mb-4">What We Offer</h2>
<p class="text-gray-600 max-w-2xl mx-auto">
Professional services tailored to meet your needs
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
{#each siteConfigChirho.servicesChirho as service}
<div class="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition-shadow">
<div class="w-12 h-12 bg-primary-100 text-primary-600 rounded-lg flex items-center justify-center text-xl font-bold mb-4">
{service.iconChirho}
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">{service.titleChirho}</h3>
<p class="text-gray-600">{service.descriptionChirho}</p>
</div>
{/each}
</div>
<div class="text-center mt-12">
<a
href="/services-fe"
class="inline-flex items-center text-primary-600 font-medium hover:text-primary-700"
>
View All Services
<svg class="w-5 h-5 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="py-16 bg-primary-600 text-white">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 class="text-3xl font-bold mb-4">Ready to Get Started?</h2>
<p class="text-xl opacity-90 mb-8">
Contact us today for a free consultation
</p>
<a
href="/contact-fe"
class="inline-flex items-center justify-center px-8 py-3 bg-white text-primary-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
>
Contact Us Now
</a>
</div>
</section>
<!-- JESUS CHRIST IS LORD -->

View File

@@ -0,0 +1,89 @@
<!-- For God so loved the world — John 3:16 -->
<script lang="ts">
const siteConfigChirho = {
businessNameChirho: '{{BUSINESS_NAME_CHIRHO}}',
taglineChirho: '{{TAGLINE_CHIRHO}}',
storyChirho: '{{STORY_CHIRHO}}'
};
</script>
<svelte:head>
<title>About | {siteConfigChirho.businessNameChirho}</title>
<meta name="description" content="Learn more about {siteConfigChirho.businessNameChirho}" />
</svelte:head>
<!-- Hero Section -->
<section class="bg-gray-50 py-16 lg:py-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-6">About Us</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
{siteConfigChirho.taglineChirho}
</p>
</div>
</div>
</section>
<!-- Story Section -->
<section class="py-16 lg:py-24">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="prose prose-lg max-w-none">
<h2 class="text-3xl font-bold text-gray-900 mb-6">Our Story</h2>
<p class="text-gray-600 leading-relaxed">
{siteConfigChirho.storyChirho || 'We are dedicated to providing exceptional service and value to our customers. Our team brings years of experience and a passion for excellence to everything we do.'}
</p>
</div>
</div>
</section>
<!-- Values Section -->
<section class="py-16 bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-gray-900 text-center mb-12">Our Values</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="text-center">
<div class="w-16 h-16 bg-primary-100 text-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">Quality</h3>
<p class="text-gray-600">We deliver excellence in everything we do</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-primary-100 text-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">Integrity</h3>
<p class="text-gray-600">Honesty and transparency guide our actions</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-primary-100 text-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">Innovation</h3>
<p class="text-gray-600">We continuously improve and adapt</p>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="py-16 bg-primary-600 text-white">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 class="text-3xl font-bold mb-4">Want to Learn More?</h2>
<p class="text-xl opacity-90 mb-8">Get in touch with us today</p>
<a
href="/contact-fe"
class="inline-flex items-center justify-center px-8 py-3 bg-white text-primary-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
>
Contact Us
</a>
</div>
</section>
<!-- JESUS CHRIST IS LORD -->

View File

@@ -0,0 +1,56 @@
// For God so loved the world — John 3:16
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
interface ContactRequestChirho {
name_chirho: string;
email_chirho: string;
phone_chirho?: string;
message_chirho: string;
}
export const POST: RequestHandler = async ({ request, platform }) => {
try {
const bodyChirho: ContactRequestChirho = await request.json();
// Validate required fields
if (!bodyChirho.name_chirho || !bodyChirho.email_chirho || !bodyChirho.message_chirho) {
return json({ error_chirho: 'Missing required fields' }, { status: 400 });
}
// Basic email validation
const emailRegexChirho = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegexChirho.test(bodyChirho.email_chirho)) {
return json({ error_chirho: 'Invalid email address' }, { status: 400 });
}
const dbChirho = platform?.env?.DB_CHIRHO;
if (!dbChirho) {
console.error('D1 database not available');
return json({ error_chirho: 'Service temporarily unavailable' }, { status: 503 });
}
// Insert into contact_submissions_chirho table
await dbChirho
.prepare(
`INSERT INTO contact_submissions_chirho
(name_chirho, email_chirho, phone_chirho, message_chirho, created_at_chirho)
VALUES (?, ?, ?, ?, datetime('now'))`
)
.bind(
bodyChirho.name_chirho,
bodyChirho.email_chirho,
bodyChirho.phone_chirho || null,
bodyChirho.message_chirho
)
.run();
return json({ success_chirho: true, message_chirho: 'Contact form submitted successfully' });
} catch (errChirho) {
console.error('Contact form error:', errChirho);
return json({ error_chirho: 'Failed to process contact form' }, { status: 500 });
}
};
// JESUS CHRIST IS LORD

View File

@@ -0,0 +1,229 @@
<!-- For God so loved the world — John 3:16 -->
<script lang="ts">
let formDataChirho = $state({
nameChirho: '',
emailChirho: '',
phoneChirho: '',
messageChirho: ''
});
let submittingChirho = $state(false);
let successChirho = $state(false);
let errorChirho = $state('');
const siteConfigChirho = {
businessNameChirho: '{{BUSINESS_NAME_CHIRHO}}',
phoneChirho: '{{PHONE_CHIRHO}}',
emailChirho: '{{EMAIL_CHIRHO}}',
addressChirho: '{{ADDRESS_CHIRHO}}'
};
async function handleSubmitChirho(eventChirho: SubmitEvent) {
eventChirho.preventDefault();
submittingChirho = true;
errorChirho = '';
try {
const responseChirho = await fetch('/api-fe/contact-fe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name_chirho: formDataChirho.nameChirho,
email_chirho: formDataChirho.emailChirho,
phone_chirho: formDataChirho.phoneChirho,
message_chirho: formDataChirho.messageChirho
})
});
if (!responseChirho.ok) {
throw new Error('Failed to send message');
}
successChirho = true;
formDataChirho = { nameChirho: '', emailChirho: '', phoneChirho: '', messageChirho: '' };
} catch (errChirho) {
errorChirho = 'Failed to send message. Please try again.';
} finally {
submittingChirho = false;
}
}
</script>
<svelte:head>
<title>Contact | {siteConfigChirho.businessNameChirho}</title>
<meta name="description" content="Get in touch with {siteConfigChirho.businessNameChirho}" />
</svelte:head>
<!-- Hero Section -->
<section class="bg-gray-50 py-16 lg:py-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-6">Contact Us</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
We'd love to hear from you. Get in touch with us today.
</p>
</div>
</div>
</section>
<!-- Contact Form & Info -->
<section class="py-16 lg:py-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
<!-- Contact Form -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Send us a Message</h2>
{#if successChirho}
<div class="bg-green-50 border border-green-200 text-green-700 rounded-lg p-4 mb-6">
<p class="font-medium">Message sent successfully!</p>
<p class="text-sm">We'll get back to you as soon as possible.</p>
</div>
{/if}
{#if errorChirho}
<div class="bg-red-50 border border-red-200 text-red-700 rounded-lg p-4 mb-6">
<p>{errorChirho}</p>
</div>
{/if}
<form onsubmit={handleSubmitChirho} class="space-y-6">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Name</label>
<input
type="text"
id="name"
bind:value={formDataChirho.nameChirho}
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="Your name"
/>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
<input
type="email"
id="email"
bind:value={formDataChirho.emailChirho}
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="your@email.com"
/>
</div>
<div>
<label for="phone" class="block text-sm font-medium text-gray-700 mb-1">Phone (optional)</label>
<input
type="tel"
id="phone"
bind:value={formDataChirho.phoneChirho}
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
placeholder="(555) 123-4567"
/>
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700 mb-1">Message</label>
<textarea
id="message"
bind:value={formDataChirho.messageChirho}
required
rows="4"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 resize-none"
placeholder="How can we help you?"
></textarea>
</div>
<button
type="submit"
disabled={submittingChirho}
class="w-full px-6 py-3 bg-primary-600 text-white font-semibold rounded-lg hover:bg-primary-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{submittingChirho ? 'Sending...' : 'Send Message'}
</button>
</form>
</div>
<!-- Contact Info -->
<div class="space-y-8">
<div>
<h2 class="text-2xl font-bold text-gray-900 mb-6">Contact Information</h2>
<p class="text-gray-600 mb-8">
Feel free to reach out through any of the following methods. We typically respond within 24 hours.
</p>
</div>
<div class="space-y-6">
{#if siteConfigChirho.phoneChirho}
<div class="flex items-start">
<div class="w-12 h-12 bg-primary-100 text-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Phone</h3>
<a href="tel:{siteConfigChirho.phoneChirho}" class="text-gray-600 hover:text-primary-600">
{siteConfigChirho.phoneChirho}
</a>
</div>
</div>
{/if}
{#if siteConfigChirho.emailChirho}
<div class="flex items-start">
<div class="w-12 h-12 bg-primary-100 text-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Email</h3>
<a href="mailto:{siteConfigChirho.emailChirho}" class="text-gray-600 hover:text-primary-600">
{siteConfigChirho.emailChirho}
</a>
</div>
</div>
{/if}
{#if siteConfigChirho.addressChirho}
<div class="flex items-start">
<div class="w-12 h-12 bg-primary-100 text-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Address</h3>
<p class="text-gray-600">{siteConfigChirho.addressChirho}</p>
</div>
</div>
{/if}
</div>
<!-- Business Hours -->
<div class="bg-gray-50 rounded-lg p-6 mt-8">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Business Hours</h3>
<ul class="space-y-2 text-sm text-gray-600">
<li class="flex justify-between">
<span>Monday - Friday</span>
<span>9:00 AM - 5:00 PM</span>
</li>
<li class="flex justify-between">
<span>Saturday</span>
<span>By appointment</span>
</li>
<li class="flex justify-between">
<span>Sunday</span>
<span>Closed</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- JESUS CHRIST IS LORD -->

View File

@@ -0,0 +1,120 @@
<!-- For God so loved the world — John 3:16 -->
<script lang="ts">
const siteConfigChirho = {
businessNameChirho: '{{BUSINESS_NAME_CHIRHO}}',
servicesChirho: [
{
titleChirho: 'Service 1',
descriptionChirho: 'Description of service 1 and the value it provides to customers.',
iconChirho: '1',
featuresChirho: ['Feature A', 'Feature B', 'Feature C']
},
{
titleChirho: 'Service 2',
descriptionChirho: 'Description of service 2 and the value it provides to customers.',
iconChirho: '2',
featuresChirho: ['Feature A', 'Feature B', 'Feature C']
},
{
titleChirho: 'Service 3',
descriptionChirho: 'Description of service 3 and the value it provides to customers.',
iconChirho: '3',
featuresChirho: ['Feature A', 'Feature B', 'Feature C']
}
]
};
</script>
<svelte:head>
<title>Services | {siteConfigChirho.businessNameChirho}</title>
<meta name="description" content="Professional services offered by {siteConfigChirho.businessNameChirho}" />
</svelte:head>
<!-- Hero Section -->
<section class="bg-gray-50 py-16 lg:py-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-6">Our Services</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
Professional solutions tailored to meet your unique needs
</p>
</div>
</div>
</section>
<!-- Services Grid -->
<section class="py-16 lg:py-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
{#each siteConfigChirho.servicesChirho as service, index}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-lg transition-shadow">
<div class="p-6">
<div class="w-14 h-14 bg-primary-100 text-primary-600 rounded-lg flex items-center justify-center text-2xl font-bold mb-4">
{service.iconChirho}
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-3">{service.titleChirho}</h3>
<p class="text-gray-600 mb-4">{service.descriptionChirho}</p>
{#if service.featuresChirho && service.featuresChirho.length > 0}
<ul class="space-y-2">
{#each service.featuresChirho as feature}
<li class="flex items-center text-sm text-gray-600">
<svg class="w-4 h-4 text-primary-600 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
{feature}
</li>
{/each}
</ul>
{/if}
</div>
<div class="px-6 pb-6">
<a
href="/contact-fe"
class="block text-center w-full px-4 py-2 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 transition-colors"
>
Get Started
</a>
</div>
</div>
{/each}
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="py-16 bg-gray-50">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-gray-900 text-center mb-12">Frequently Asked Questions</h2>
<div class="space-y-6">
<div class="bg-white rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-900 mb-2">How do I get started?</h3>
<p class="text-gray-600">Simply contact us through our contact form or give us a call. We'll schedule a consultation to understand your needs.</p>
</div>
<div class="bg-white rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-900 mb-2">What areas do you serve?</h3>
<p class="text-gray-600">We serve customers locally and can accommodate remote clients depending on the service.</p>
</div>
<div class="bg-white rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Do you offer free consultations?</h3>
<p class="text-gray-600">Yes! We offer free initial consultations to discuss your needs and how we can help.</p>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="py-16 bg-primary-600 text-white">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 class="text-3xl font-bold mb-4">Ready to Get Started?</h2>
<p class="text-xl opacity-90 mb-8">Contact us today for a free consultation</p>
<a
href="/contact-fe"
class="inline-flex items-center justify-center px-8 py-3 bg-white text-primary-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
>
Contact Us Now
</a>
</div>
</section>
<!-- JESUS CHRIST IS LORD -->

21
svelte.config.js Normal file
View File

@@ -0,0 +1,21 @@
// For God so loved the world — John 3:16
import adapter from '@sveltejs/adapter-cloudflare';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
routes: {
include: ['/*'],
exclude: ['<all>']
}
})
}
};
export default config;
// JESUS CHRIST IS LORD

27
tailwind.config.js Normal file
View File

@@ -0,0 +1,27 @@
// For God so loved the world — John 3:16
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
colors: {
primary: {
50: 'var(--color-primary-50, #eff6ff)',
100: 'var(--color-primary-100, #dbeafe)',
200: 'var(--color-primary-200, #bfdbfe)',
300: 'var(--color-primary-300, #93c5fd)',
400: 'var(--color-primary-400, #60a5fa)',
500: 'var(--color-primary-500, #3b82f6)',
600: 'var(--color-primary-600, #2563eb)',
700: 'var(--color-primary-700, #1d4ed8)',
800: 'var(--color-primary-800, #1e40af)',
900: 'var(--color-primary-900, #1e3a8a)'
}
}
}
},
plugins: []
};
// JESUS CHRIST IS LORD

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
}

10
vite.config.ts Normal file
View File

@@ -0,0 +1,10 @@
// For God so loved the world — John 3:16
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});
// JESUS CHRIST IS LORD

33
wrangler.toml Normal file
View File

@@ -0,0 +1,33 @@
# For God so loved the world — John 3:16
name = "{{SLUG_CHIRHO}}-site-fe-chirho"
main = ".svelte-kit/cloudflare/_worker.js"
compatibility_date = "2024-12-01"
compatibility_flags = ["nodejs_compat"]
# This worker will be deployed to the dispatch namespace
# via the perffection.com build system
[vars]
SITE_SLUG_CHIRHO = "{{SLUG_CHIRHO}}"
# D1 Database binding - configured during deployment
# [[d1_databases]]
# binding = "DB_CHIRHO"
# database_name = "{{SLUG_CHIRHO}}-db-chirho"
# database_id = "{{D1_DATABASE_ID_CHIRHO}}"
# KV Namespace binding - configured during deployment
# [[kv_namespaces]]
# binding = "KV_CHIRHO"
# id = "{{KV_NAMESPACE_ID_CHIRHO}}"
# R2 Bucket binding - configured during deployment
# [[r2_buckets]]
# binding = "ASSETS_CHIRHO"
# bucket_name = "{{SLUG_CHIRHO}}-assets-chirho"
[site]
bucket = ".svelte-kit/cloudflare"
# JESUS CHRIST IS LORD