feat: add new therapeutics page

Create new page for therapeutics app functionality.

#VERCEL_SKIP

Co-authored-by: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
This commit is contained in:
v0 2025-10-22 17:54:53 +00:00
parent be3a888bde
commit f51a67566d
17 changed files with 4150 additions and 135 deletions

203
README.md
View File

@ -1,158 +1,99 @@
# Ebb'nFlow Therapeutics Website # Ebb'nFlow Therapeutics Website
A static HTML/CSS/JavaScript website for Ebb'nFlow Therapeutics massage therapy practice. A dual-purpose website that works as both a Next.js application (for v0 preview) and exports to static HTML (for Cloudflare Pages deployment).
## 🚨 Current Issue & Fix ## 🚀 Quick Start
Your site at https://ebb-n-flow-website.pages.dev/ isn't working because Cloudflare is looking for Next.js files. ### Development (Next.js Preview)
### **Quick Fix:**
1. **Delete the current Cloudflare Pages project:**
- Go to [Cloudflare Dashboard](https://dash.cloudflare.com/)
- Navigate to Pages → ebb-n-flow-website
- Settings → Delete project
2. **Create a new deployment with ONLY these files:**
- `index.html`
- `about.html`
- `contact.html`
- `movement.html`
- `what-to-expect.html`
- `styles.css`
- `script.js`
- `_routes.json`
- `_headers`
- `images/` folder
3. **Upload Method:**
- Choose "Upload assets" (not Git integration)
- Drag and drop the files listed above
- Deploy!
## 📁 Files to Deploy
**Root files:**
\`\`\`
index.html
about.html
contact.html
movement.html
what-to-expect.html
styles.css
script.js
_routes.json
_headers
\`\`\`
**Images folder:**
\`\`\`
images/
├── logo.jpg
├── dahlia-portrait.jpg
├── treatment-room.jpg
├── reception-area.jpg
├── clinic-exterior.jpg
├── clinic-entrance.jpg
├── yellow-door.jpg
├── bioflex-laser.png
├── movement-class.png
├── olivia-cat.png
└── rmt-certification.png
\`\`\`
## 🚀 Deployment Options
### Option 1: Cloudflare Pages (Direct Upload) ⭐ RECOMMENDED
1. Go to [Cloudflare Pages](https://dash.cloudflare.com/)
2. Click "Create a project"
3. Select "Upload assets"
4. Upload ONLY the files listed above
5. Build settings: **LEAVE EMPTY** (no build needed)
6. Deploy!
### Option 2: Wrangler CLI
\`\`\`bash \`\`\`bash
# Install wrangler npm install
npm install -g wrangler npm run dev
# Login
wrangler login
# Deploy from project root
wrangler pages deploy . --project-name=ebb-n-flow-website
\`\`\` \`\`\`
### Option 3: GitHub Integration Visit http://localhost:3000
1. Create a new repository with ONLY the files listed above ### Build for Cloudflare Pages
2. In Cloudflare Pages, connect to Git
3. Build settings:
- **Build command:** (leave empty)
- **Build output directory:** `/`
- **Root directory:** `/`
## 🌐 Local Development
\`\`\`bash \`\`\`bash
# Option 1: Python npm run build:static
python -m http.server 8000
# Option 2: Node.js
npx serve .
# Option 3: PHP
php -S localhost:8000
\`\`\` \`\`\`
Visit: http://localhost:8000 This will create an `out/` folder with static HTML files ready for Cloudflare Pages deployment.
## ✅ Verification Checklist ## 📁 Project Structure
After deploying, verify: \`\`\`
- [ ] Homepage loads at https://ebb-n-flow-website.pages.dev/ ├── app/
- [ ] All navigation links work │ ├── page.tsx # Homepage
- [ ] Images load correctly │ ├── about/page.tsx # About page
- [ ] Booking button links to ClinicSense │ ├── contact/page.tsx # Contact page
- [ ] Mobile menu works │ ├── movement/page.tsx # Movement page
- [ ] All pages (about, contact, movement, what-to-expect) load │ ├── what-to-expect/page.tsx # What to Expect page
│ ├── components/
│ │ ├── Navigation.tsx
│ │ ├── Footer.tsx
│ │ └── Modal.tsx
│ ├── layout.tsx
│ └── globals.css
├── public/
│ └── images/ # All images
├── next.config.ts # Configured for static export
└── package.json
\`\`\`
## 🌐 Deployment Options
### Option 1: Cloudflare Pages (Recommended)
1. Build the static files:
\`\`\`bash
npm run build:static
\`\`\`
2. Deploy the `out/` folder to Cloudflare Pages:
- Go to Cloudflare Dashboard
- Create new Pages project
- Upload the `out/` folder
### Option 2: v0 Preview
The project works natively in v0 for live preview and iteration.
## 🎨 Features ## 🎨 Features
- Pure HTML/CSS/JavaScript (no frameworks) - **Dual Mode**: Works as Next.js app (dev) and static HTML (production)
- Mobile responsive design - **Image Optimization**: Uses Next.js Image component with unoptimized export
- Smooth scroll navigation - **Responsive Design**: Mobile-first approach
- Modal popups for service info - **SEO Optimized**: Meta tags and semantic HTML
- Collapsible mobile menu - **Performance**: Static generation for fast loading
- Integrated booking system
## 📞 Contact Information ## 📝 Development Notes
- Images are in `/public/images/` and referenced as `/images/filename.jpg`
- Navigation uses Next.js Link for client-side routing in dev
- Modal functionality is client-side with React hooks
- All pages export as static HTML with proper routing
## 🔧 Configuration
### next.config.ts
\`\`\`typescript
const nextConfig = {
output: 'export', // Enables static export
images: {
unoptimized: true, // Required for static export
},
trailingSlash: true, // Adds trailing slash to URLs
}
\`\`\`
## 📞 Contact
**Ebb'nFlow Therapeutics** **Ebb'nFlow Therapeutics**
- 📍 59 Wellington Street, St. Catharines, ON L2R 5P9 - 📍 59 Wellington Street, St. Catharines, ON L2R 5P9
- 📞 (289) 969-3219 - 📞 (289) 969-3219
- ✉️ info@ebbnflowtherapeutics.com - ✉️ info@ebbnflowtherapeutics.com
- 🌐 [Book Online](https://ebbnflowmassage.clinicsense.com)
## 🐛 Troubleshooting
**Site shows 404 or doesn't load?**
- Make sure you uploaded ALL files (especially index.html)
- Check that files are in the root directory, not in a subfolder
**Images not loading?**
- Verify the `images/` folder was uploaded
- Check image paths in HTML files (should be `images/filename.jpg`)
**Styles not working?**
- Ensure `styles.css` is in the root directory
- Clear browser cache (Ctrl+Shift+R or Cmd+Shift+R)
**JavaScript not working?**
- Make sure `script.js` is in the root directory
- Check browser console for errors (F12)
--- ---

221
app/about/page.tsx Normal file
View File

@ -0,0 +1,221 @@
import Image from "next/image"
import Link from "next/link"
import Navigation from "../components/Navigation"
import Footer from "../components/Footer"
export const metadata = {
title: "About Dahlia B. Steinberg, RMT - Ebb'nFlow Therapeutics",
description:
"Meet Dahlia B. Steinberg, RMT - Founder of Ebb'nFlow Therapeutics. A lifelong interest in movement and healing arts.",
}
export default function About() {
return (
<>
<Navigation />
{/* Hero Section */}
<section className="page-hero">
<div className="container">
<span className="badge">About Dahlia</span>
<h1>Dahlia B. Steinberg, RMT</h1>
<p className="hero-subtitle">Founder and Owner of Ebb&apos;nFlow Therapeutics</p>
<p className="hero-description">
A lifelong interest in movement and the healing arts is the foundation of her practice as a massage
therapist, laser therapist, and movement educator.
</p>
</div>
</section>
{/* Bio Section */}
<section className="content-section">
<div className="container">
<div className="two-col-grid">
<div className="content-text">
<h2>Professional Background</h2>
<p>
Dahlia&apos;s personal and professional experience with a wide range of bodywork practices including
yoga, pilates, somatic movement and modern dance spans 30 years and informs her unique style and
approach to her clinical practice of massage therapy.
</p>
<p>
She is registered with the College of Massage Therapists of Ontario (CMTO), which is the provincial
regulatory body that protects the public interest by monitoring the ongoing professional development and
ethical standards of RMTs.
</p>
<div className="info-card">
<h3>Practice Evolution</h3>
<div className="timeline">
<p>
<strong>2013:</strong> Started Ebb&apos;nflow Massage, working with Dr. Enrico Schirru at Absolute
Chiropractic
</p>
<p>
<strong>2013-2017:</strong> Worked at Paisley Spa and John Chan Physiotherapy Clinic
</p>
<p>
<strong>2015:</strong> Established first home-based clinic on Ontario Street
</p>
<p>
<strong>2017:</strong> Moved to home location on Woodbine Ave., working solely for herself
</p>
<p>
<strong>2025:</strong> Relocated to downtown St. Catharines, rebranded to Ebb&apos;nFlow
Therapeutics
</p>
</div>
</div>
</div>
<div className="content-image">
<Image
src="/images/dahlia-portrait.jpg"
alt="Dahlia B. Steinberg, RMT - Founder of Ebb'nFlow Therapeutics"
width={500}
height={600}
style={{ objectFit: "cover", borderRadius: "1rem" }}
/>
</div>
</div>
</div>
</section>
{/* Education Section */}
<section className="content-section bg-alt">
<div className="container">
<h2 className="section-title">Education & Training</h2>
<div className="education-grid">
<div className="education-card">
<h3>Formal Education</h3>
<div className="education-list">
<p>
<strong>2021:</strong> Meditech International Inc. Bioflex Laser Therapy Certification
</p>
<p>
<strong>2012:</strong> Graduate, Massage Therapy Program, Ontario College of Health and Technology
</p>
<p>
<strong>2009:</strong> Pilates Teacher Training, STOTT Toronto (Beginner to Advanced Matwork)
</p>
<p>
<strong>2003-05:</strong> Yoga Teacher Training, Yogaspace Toronto (200 hr diploma + Advanced Teacher
Training)
</p>
<p>
<strong>1998:</strong> School of Toronto Dance Theatre&apos;s Professional Training Program
</p>
</div>
</div>
<div className="education-card">
<h3>Continuing Education</h3>
<ul className="bullet-list">
<li>Myofascial therapy/structural integration</li>
<li>Biodynamic craniosacral therapy</li>
<li>Osteopathic techniques for RMTs</li>
<li>Advanced sacro-lumbar and thoracic spine assessments and joint mobilization</li>
<li>Axis Syllabus - evolving theory and practice of dynamic human movement</li>
</ul>
</div>
<div className="education-card highlight-card">
<h3>Recognition & Support</h3>
<p>Massage Therapy is Dahlia&apos;s second career, supported by:</p>
<ul className="bullet-list">
<li>Training and subsistence grant from the Dancer&apos;s Transition and Resource Centre (2010)</li>
<li>
Special second year award for academic excellence (Dancer&apos;s Transition and Resource Centre, 2011)
</li>
</ul>
</div>
</div>
</div>
</section>
{/* Teaching Section */}
<section className="content-section">
<div className="container">
<div className="centered-content">
<h2>Teaching & Mentorship</h2>
<p>
Dahlia now also works once a week as a supervisor and teacher in the student massage therapy clinic, which
is open to the public at the Ontario College of Health and Technology in Stoney Creek. This role helps to
keep her engaged and informed in her current practice while inspiring her to encourage and mentor the next
generation of massage therapists.
</p>
</div>
</div>
</section>
{/* Meet Olivia Section */}
<section className="content-section bg-alt">
<div className="container">
<h2 className="section-title">Meet Olivia</h2>
<div className="two-col-grid reverse">
<div className="content-image">
<Image
src="/images/olivia-cat.png"
alt="Olivia, the friendly clinic cat at Ebb'nFlow Therapeutics"
width={500}
height={400}
style={{ objectFit: "cover", borderRadius: "1rem" }}
/>
</div>
<div className="content-text">
<div className="info-card">
<p>
No visit to Ebb&apos;nFlow Therapeutics would be complete without meeting Olivia, our gentle and
affectionate clinic companion. This sweet tabby girl has a calming presence that perfectly complements
the therapeutic atmosphere of our space.
</p>
<p>
Olivia enjoys greeting clients with soft purrs and will often curl up nearby during sessions, adding
an extra layer of comfort and relaxation to your experience. Her friendly demeanor and peaceful energy
make her a beloved part of the Ebb&apos;nFlow family.
</p>
<p className="italic">
Please let us know if you have any allergies or prefer a cat-free sessionwe&apos;re happy to
accommodate all our clients&apos; needs!
</p>
</div>
</div>
</div>
</div>
</section>
{/* CTA Section */}
<section className="cta">
<div className="container">
<h2>Experience Dahlia&apos;s Unique Approach</h2>
<p>Combining 30 years of movement expertise with clinical massage therapy excellence.</p>
<div className="cta-buttons">
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
<Link href="/what-to-expect" className="btn btn-primary">
What to Expect
</Link>
</div>
<div className="rmt-badge">
<Image
src="/images/rmt-certification.png"
alt="Registered Massage Therapist - Professional Hands You Can Trust"
width={200}
height={100}
style={{ maxWidth: "200px", height: "auto" }}
/>
</div>
</div>
</section>
<Footer />
</>
)
}

93
app/components/Footer.tsx Normal file
View File

@ -0,0 +1,93 @@
import Link from "next/link"
import Image from "next/image"
export default function Footer() {
return (
<footer>
<div className="container">
<div className="footer-grid">
<div className="footer-brand">
<h3>
Ebb&apos;nFlow <span className="highlight">Therapeutics</span>
</h3>
<p>A boutique wellness sanctuary. One client, one space, one transformative experience at a time.</p>
<div className="rmt-badge-footer">
<Image
src="/images/rmt-certification.png"
alt="Registered Massage Therapist - Professional Hands You Can Trust"
width={200}
height={100}
style={{
width: "200px",
height: "auto",
marginTop: "1rem",
display: "block",
marginLeft: "auto",
marginRight: "auto",
}}
/>
</div>
</div>
<div className="footer-links">
<h4>Services</h4>
<ul>
<li>
<a href="#services">Massage Therapy</a>
</li>
<li>
<a href="#services">Laser Therapy</a>
</li>
<li>
<a href="#services">Movement Therapy</a>
</li>
<li>
<Link href="/contact">Consultation</Link>
</li>
</ul>
</div>
<div className="footer-links">
<h4>Quick Links</h4>
<ul>
<li>
<Link href="/about">About</Link>
</li>
<li>
<a href="#prices">Prices</a>
</li>
<li>
<Link href="/contact">Contact</Link>
</li>
<li>
<Link href="/what-to-expect">What to Expect</Link>
</li>
<li>
<Link href="/movement">Movement Workshops</Link>
</li>
</ul>
</div>
<div className="footer-contact">
<h4>Location Info</h4>
<p>
📍 59 Wellington Street
<br />
St. Catharines, ON L2R 5P9
</p>
<p>📞 (289) 969-3219</p>
</div>
</div>
<div className="footer-bottom">
<p>&copy; 2025 Ebb&apos;nFlow Therapeutics. All rights reserved.</p>
<div className="footer-legal">
<a href="#">Privacy Policy</a>
<a href="#">Terms of Service</a>
</div>
</div>
</div>
</footer>
)
}

78
app/components/Modal.tsx Normal file
View File

@ -0,0 +1,78 @@
"use client"
import { useEffect, useState } from "react"
const modalContent = {
massage: {
title: "Massage Therapy",
content: `Scope of Practice:
Registered Massage Therapists in Ontario constitute a regulated health profession in much the same way that physicians, nurses, physiotherapists and chiropractors are regulated. The profession is governed by a Scope of Practice, which is defined as follows:
"The practice of massage therapy is the assessment of the soft tissue and joints of the body, and the treatment and prevention of physical dysfunction and pain of the soft tissues and joints by manipulation to develop, maintain, rehabilitate or augment physical function, or relieve pain." (Massage Therapy Act, 1991)
Benefits of Massage Therapy:
Massage therapy treatments provided by a Registered Massage Therapist can offer significant benefits for a variety of conditions and for diverse patient populations. Whether you need to have a moment of relaxation, reduce muscle tension or attain relief from chronic pain, massage therapy can enhance your overall well-being.
Massage therapy can be an important part of your health maintenance plan by:
Reducing or eliminating pain
Improving joint mobility
Improving lymphatic drainage
Reducing muscular tension
Techniques:
Treatments are based on General Swedish Massage techniques and may be combined with Deep Tissue Massage, Myofascial Release, Trigger Point Therapy, Manual Lymphatic Drainage and Joint Mobilization Techniques. Ongoing assessment, remedial exercise and/or home care instruction is also part of this comprehensive treatment session based on the client's goals and comfort.`,
},
laser: {
title: "Laser Therapy",
content: `The Science:
Ebb'nFlow Therapeutics uses the BIOFLEX® system of Class 3b lasers together with large surface arrays of bicolour LEDs that affect a large volume of circulating blood as well as underlying tissues resulting in both a powerful systemic and direct photobiomodulation effect.
Please visit BioflexLaser.com for more info.`,
},
}
export default function Modal() {
const [isOpen, setIsOpen] = useState(false)
const [activeModal, setActiveModal] = useState<"massage" | "laser" | null>(null)
useEffect(() => {
const handleModalClick = (e: Event) => {
const target = e.target as HTMLElement
if (target.hasAttribute("data-modal")) {
const modalType = target.getAttribute("data-modal") as "massage" | "laser"
setActiveModal(modalType)
setIsOpen(true)
}
}
document.addEventListener("click", handleModalClick)
return () => document.removeEventListener("click", handleModalClick)
}, [])
const closeModal = () => {
setIsOpen(false)
setActiveModal(null)
}
if (!isOpen || !activeModal) return null
const content = modalContent[activeModal]
return (
<div className="modal active" onClick={closeModal}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h3>{content.title}</h3>
<button className="modal-close" onClick={closeModal}>
&times;
</button>
</div>
<div className="modal-body">{content.content}</div>
</div>
</div>
)
}

View File

@ -0,0 +1,67 @@
"use client"
import { useState } from "react"
import Image from "next/image"
import Link from "next/link"
export default function Navigation() {
const [isOpen, setIsOpen] = useState(false)
const toggleMenu = () => setIsOpen(!isOpen)
const closeMenu = () => setIsOpen(false)
return (
<header id="navbar">
<div className="container">
<nav>
<Link href="/" className="logo">
<Image src="/images/logo.jpg" alt="Ebb'nFlow Therapeutics Logo" width={100} height={100} />
<span className="logo-text">
Ebb&apos;nFlow <span className="highlight">Therapeutics</span>
</span>
</Link>
<button className="mobile-menu-btn" onClick={toggleMenu}>
<span></span>
<span></span>
<span></span>
</button>
<ul className={`nav-links ${isOpen ? "active" : ""}`} id="navLinks">
<li>
<a href="#services" onClick={closeMenu}>
Services
</a>
</li>
<li>
<a href="#prices" onClick={closeMenu}>
Prices
</a>
</li>
<li>
<Link href="/about" onClick={closeMenu}>
About
</Link>
</li>
<li>
<Link href="/contact" onClick={closeMenu}>
Contact
</Link>
</li>
<li className="cta-nav">
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
style={{ color: "white" }}
>
Book Appointment
</a>
</li>
</ul>
</nav>
</div>
</header>
)
}

1030
app/globals.css Normal file

File diff suppressed because it is too large Load Diff

29
app/layout.tsx Normal file
View File

@ -0,0 +1,29 @@
import type React from "react"
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import "./globals.css"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
title: "Ebb'nFlow Therapeutics - Massage, Laser & Movement Therapy",
description:
"Experience holistic healing through our integrated therapeutic services. Massage therapy, laser therapy, and movement therapy in a peaceful, professional setting.",
generator: 'v0.app'
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
</head>
<body className={inter.className}>{children}</body>
</html>
)
}

278
app/page.tsx Normal file
View File

@ -0,0 +1,278 @@
import Image from "next/image"
import Link from "next/link"
import Navigation from "./components/Navigation"
import Footer from "./components/Footer"
import Modal from "./components/Modal"
export default function Home() {
return (
<>
<Navigation />
{/* Hero Section */}
<section className="hero">
<div className="container">
<div className="hero-grid">
<div className="hero-content">
<h1>
Ebb&apos;nFlow <span className="highlight">Therapeutics</span>
</h1>
<p className="hero-subtitle">Excellence in Massage Therapy Since 2013</p>
<p className="hero-description">A serene, home-based practice in the heart of downtown St Catharines</p>
<p className="hero-tagline">Owned and operated by Dahlia B. Steinberg RMT.</p>
<div className="hero-buttons">
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
<Link href="/about" className="btn btn-secondary">
Learn More
</Link>
</div>
</div>
<div className="hero-image">
<Image
src="/images/reception-area.jpg"
alt="Welcoming reception area at Ebb'nFlow Therapeutics"
width={600}
height={500}
style={{ objectFit: "cover", borderRadius: "1rem" }}
priority
/>
</div>
</div>
</div>
</section>
{/* About Section */}
<section className="about-intro">
<div className="container">
<div className="about-card">
<p>
Ebb&apos;nFlow Therapeutics provides Massage Therapy that is attentive to your unique needs and goals.
Whether you are looking for recovery from the stress and strains of work, to manage pain, prevent injury
or improve your physical performance, we&apos;re here to meet you where you are.
</p>
<p>
Unlike any other massage therapy practice, Ebb&apos;nFlow Therapeutics also offers a distinctly unique
service that combines the cutting edge technology of Laser Therapy (Photobiomodulation) in combination
with massage therapy for accelerated healing of acute pain conditions and injury.
</p>
</div>
</div>
</section>
{/* Services Section */}
<section id="services" className="services">
<div className="container">
<h2>Services</h2>
<p className="section-description">
Please click on the links below to learn more about the unique services offered, based on a comprehensive,
clinical practice of Massage Therapy.
</p>
<div className="services-grid">
{/* Massage Therapy */}
<div className="service-card">
<Image
src="/images/treatment-room.jpg"
alt="Professional massage therapy treatment room"
width={400}
height={200}
style={{ objectFit: "cover" }}
/>
<div className="service-content">
<h3>Massage Therapy Treatment</h3>
<p>
General Swedish Massage combined with specialized techniques including Deep Tissue and Myofascial Release, all tailored to your specific needs and wellness goals.
</p>
<button className="btn btn-outline" data-modal="massage">
Learn More About Massage Therapy
</button>
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
</div>
</div>
{/* Laser Therapy */}
<div className="service-card">
<Image
src="/images/bioflex-laser.png"
alt="BioFlex Laser Therapy"
width={400}
height={200}
style={{ objectFit: "cover" }}
/>
<div className="service-content">
<h3>Laser Therapy Treatment</h3>
<p>
Laser therapy provides accelerated healing for musculoskeletal injury, arthritis, and nerve injury.
Stand alone or recommended to combine with Massage Therapy.
</p>
<button className="btn btn-outline" data-modal="laser">
Learn More About Laser Therapy
</button>
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
</div>
</div>
{/* Movement Education */}
<div className="service-card">
<Image
src="/images/movement-class.png"
alt="Movement education class"
width={400}
height={200}
style={{ objectFit: "cover" }}
/>
<div className="service-content">
<h3>Movement Education</h3>
<p>
Remedial exercise instruction involves teaching targeted strength, stretch or mobility exercises that
further the benefits of massage therapy long term.
</p>
<Link href="/movement" className="btn btn-outline">
See Movement Workshops
</Link>
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
</div>
</div>
</div>
</div>
</section>
{/* Pricing Section */}
<section id="prices" className="pricing">
<div className="container">
<h2>Prices</h2>
<div className="pricing-grid">
<div className="pricing-card">
<h3>Massage and/or Laser Therapy</h3>
<div className="price-list">
<div className="price-item">
<span>30 Minute</span>
<span>
$53.50 <small>(+$6.95 HST)</small>
</span>
</div>
<div className="price-item">
<span>45 Minute</span>
<span>
$80.25 <small>(+$10.43 HST)</small>
</span>
</div>
<div className="price-item">
<span>60 Minute</span>
<span>
$107.00 <small>(+$13.91 HST)</small>
</span>
</div>
<div className="price-item">
<span>75 Minute</span>
<span>
$133.75 <small>(+$17.38 HST)</small>
</span>
</div>
<div className="price-item">
<span>90 Minute</span>
<span>
$160.50 <small>(+$20.86 HST)</small>
</span>
</div>
</div>
</div>
<div className="pricing-card">
<h3>Add-on Laser Therapy</h3>
<p className="pricing-note">If combined simultaneously with Massage Therapy for more than 15 minutes</p>
<div className="addon-price">
<span className="large-price">$1.00/minute</span>
<span className="price-note">(+ HST)</span>
</div>
<p className="pricing-note">Available in 15, 30, 45 and 60 minute intervals</p>
</div>
</div>
<div className="insurance-card">
<h3>Payment & Insurance</h3>
<p className="insurance-highlight">Direct billing is now available</p>
<div className="insurance-details">
<p>
<strong>Accepted:</strong> Cash, Direct Billing, E-transfer, Debit, Credit Card (Visa, AmEx, Mastercard)
</p>
<p>RMTs are regulated health care professionals recognized by insurance companies across Ontario.</p>
<p>
It is recommended that clients check with their insurance provider about whether they are required to
have a doctor&apos;s note in order to submit a claim.
</p>
<p>
Your receipt will include the practitioner&apos;s registration number necessary for an insurance claim
submission. This number verifies that your therapist is registered with the College of Massage
Therapists of Ontario (CMTO).
</p>
</div>
</div>
</div>
</section>
{/* CTA Section */}
<section className="cta">
<div className="container">
<h2>Ready to Begin Your Wellness Journey?</h2>
<div className="cta-contact">
<div className="contact-item">
<span>📞</span>
<span>(289) 969-3219</span>
</div>
<div className="contact-item">
<span>💌</span>
<span>info@ebbnflowtherapeutics.com</span>
</div>
</div>
<div className="cta-buttons">
<a
href="https://ebbnflowmassage.clinicsense.com"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
>
Book Appointment
</a>
<Link href="/contact" className="btn btn-primary">
Contact Dahlia
</Link>
</div>
</div>
</section>
<Footer />
<Modal />
</>
)
}

21
components.json Normal file
View File

@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@ -0,0 +1,11 @@
'use client'
import * as React from 'react'
import {
ThemeProvider as NextThemesProvider,
type ThemeProviderProps,
} from 'next-themes'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}

6
lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

17
next.config.ts Normal file
View File

@ -0,0 +1,17 @@
import type { NextConfig } from "next"
const nextConfig: NextConfig = {
output: "export",
eslint: {
ignoreDuringBuilds: true,
},
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
trailingSlash: true,
}
export default nextConfig

View File

@ -1,8 +1,44 @@
{ {
"name": "my-v0-project", "name": "ebb-n-flow-therapeutics",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "echo 'no build script'" "build": "next build",
"build:static": "next build && next export",
"dev": "next dev",
"lint": "next lint",
"start": "next start"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.454.0",
"next": "15.2.4",
"next-themes": "0.4.6",
"react": "^19",
"react-dom": "^19",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "latest"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.9",
"@types/node": "^22",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8.5",
"tailwindcss": "^4.1.9",
"typescript": "^5"
} }
} }

File diff suppressed because it is too large Load Diff

8
postcss.config.mjs Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
'@tailwindcss/postcss': {},
},
}
export default config

125
styles/globals.css Normal file
View File

@ -0,0 +1,125 @@
@import 'tailwindcss';
@import 'tw-animate-css';
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
@theme inline {
--font-sans: 'Geist', 'Geist Fallback';
--font-mono: 'Geist Mono', 'Geist Mono Fallback';
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

27
tsconfig.json Normal file
View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"target": "ES6",
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}