Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a41a726
Convert Vail site to Next.js platform foundation
harbourviewcompany-create May 12, 2026
7bff9a3
Add TypeScript configuration
harbourviewcompany-create May 12, 2026
fde3feb
Add Next.js environment types
harbourviewcompany-create May 12, 2026
e356db1
Add Next.js configuration
harbourviewcompany-create May 12, 2026
84ec261
Add PostCSS configuration
harbourviewcompany-create May 12, 2026
6c95efe
Add Tailwind configuration
harbourviewcompany-create May 12, 2026
ea3689e
Add ESLint configuration
harbourviewcompany-create May 12, 2026
ee48da1
Update Netlify config for Next.js
harbourviewcompany-create May 12, 2026
b720684
Add global styles
harbourviewcompany-create May 12, 2026
2cee917
Add public route registry
harbourviewcompany-create May 12, 2026
4d217c7
Add public site header component
harbourviewcompany-create May 12, 2026
d2f7812
Add public site footer component
harbourviewcompany-create May 12, 2026
647a81a
Add mobile sticky CTA bar
harbourviewcompany-create May 12, 2026
a7aef2e
Add reusable placeholder page component
harbourviewcompany-create May 12, 2026
2ddc82f
Add Next.js root layout
harbourviewcompany-create May 12, 2026
dc9b87b
Add Next.js homepage foundation
harbourviewcompany-create May 12, 2026
15da17a
Add services overview route
harbourviewcompany-create May 12, 2026
fc25b64
Add service detail route placeholders
harbourviewcompany-create May 12, 2026
31b9f86
Add how-it-works route
harbourviewcompany-create May 12, 2026
2839670
Add Project Starter placeholder route
harbourviewcompany-create May 12, 2026
8b1ed54
Add Scope Builder placeholder route
harbourviewcompany-create May 12, 2026
8fcd3f5
Add book-call placeholder route
harbourviewcompany-create May 12, 2026
b180441
Add callback placeholder route
harbourviewcompany-create May 12, 2026
d3ec007
Add contact placeholder route
harbourviewcompany-create May 12, 2026
5eee82a
Add privacy placeholder route
harbourviewcompany-create May 12, 2026
170842a
Add terms placeholder route
harbourviewcompany-create May 12, 2026
3dce5e6
Add legacy static route redirects
harbourviewcompany-create May 12, 2026
2ebb03f
Replace static checker with platform foundation checks
harbourviewcompany-create May 13, 2026
0190559
Update README for Next.js foundation
harbourviewcompany-create May 13, 2026
74f4447
Update sitemap for platform routes
harbourviewcompany-create May 13, 2026
db6324c
Add Vail platform foundation report
harbourviewcompany-create May 13, 2026
538c3a6
Harden service slug typing
harbourviewcompany-create May 13, 2026
19a40cd
Fix platform foundation checker false positives
harbourviewcompany-create May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 68 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,80 @@
# Vail Renovations Static Site
# Vail Renovations Platform Foundation

Static Netlify-ready website for Vail Renovations built around the Vail Home Command intake paths.
This repo is being converted from a static Netlify HTML site into a Next.js App Router foundation for the Vail Renovations public website and future mobile-friendly CRM.

## Current scope

Implemented in the platform foundation:

- Next.js App Router
- TypeScript
- Tailwind configuration
- Public layout, header, footer and mobile sticky CTAs
- Homepage foundation
- Public route placeholders
- Legacy static-route redirects
- Netlify Next.js build configuration
- Platform foundation report

Not implemented in this ticket:

- CRM
- Database
- Auth
- Lead creation
- Project Starter functionality
- Scope Builder functionality
- Callback submission
- Photo uploads
- Private storage
- Notifications
- Analytics
- Estimate workflow

## Required routes

- `/`
- `/start-a-project`
- `/fix-list-builder`
- `/maintenance-plans`
- `/inspection-report-repairs`
- `/thank-you`
- `/services`
- `/services/bathroom-renovations`
- `/services/basement-renovations`
- `/services/kitchen-updates`
- `/services/repairs-refreshes`
- `/services/pre-sale-improvements`
- `/services/rental-turnovers`
- `/how-it-works`
- `/project-request`
- `/scope-builder`
- `/book-call`
- `/callback`
- `/contact`
- `/privacy`
- `/terms`

## Netlify Forms
## Legacy redirects

Included forms:
Legacy static URLs redirect to the new foundation routes:

- `vail-project-intake`
- `vail-fix-list`
- `vail-maintenance-plan`
- `vail-inspection-report`
- `/start-a-project` -> `/project-request`
- `/fix-list-builder` -> `/scope-builder`
- `/maintenance-plans` -> `/services/repairs-refreshes`
- `/inspection-report-repairs` -> `/services/pre-sale-improvements`
- `/renovations` -> `/services`
- `/thank-you` -> `/contact`

## Deploy
## Scripts

Netlify settings:
```bash
npm run dev
npm run typecheck
npm run lint
npm run build
npm run check
```

## Deploy

- Base directory: blank
- Build command: blank
- Publish directory: `.`
Netlify settings for this foundation:

Run `npm run check` before deploying.
- Build command: `npm run build`
- Publish directory: `.next`
- Plugin: `@netlify/plugin-nextjs`
20 changes: 20 additions & 0 deletions app/book-call/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PlaceholderPage } from "@/components/placeholder-page";

export const metadata = {
title: "Book a Quick Call",
description: "Vail quick call foundation route."
};

export default function BookCallPage() {
return (
<PlaceholderPage
eyebrow="Quick call"
title="Book a quick call when the scheduling path is connected."
body="The foundation route is in place. This ticket does not add a native scheduling engine or lead creation. The next implementation can connect an external booking URL or callback fallback."
primaryHref="/callback"
primaryLabel="Request a Callback"
secondaryHref="/project-request"
secondaryLabel="Start a Project Request"
/>
);
}
20 changes: 20 additions & 0 deletions app/callback/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PlaceholderPage } from "@/components/placeholder-page";

export const metadata = {
title: "Request a Callback",
description: "Vail callback request foundation route."
};

export default function CallbackPage() {
return (
<PlaceholderPage
eyebrow="Callback"
title="Request a callback without filling out a long form."
body="The callback route is now part of the platform foundation. Actual callback submission, CRM lead creation and notification logic are intentionally out of scope for this ticket."
primaryHref="/project-request"
primaryLabel="Start a Project Request"
secondaryHref="/contact"
secondaryLabel="Contact Vail"
/>
);
}
20 changes: 20 additions & 0 deletions app/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PlaceholderPage } from "@/components/placeholder-page";

export const metadata = {
title: "Contact",
description: "Contact Vail Renovations."
};

export default function ContactPage() {
return (
<PlaceholderPage
eyebrow="Contact"
title="Start with the next clear step."
body="This foundation route avoids inventing phone numbers, emails or public addresses. The next ticket can connect verified contact details and the low-friction intake flows."
primaryHref="/project-request"
primaryLabel="Start a Project Request"
secondaryHref="/book-call"
secondaryLabel="Book a Quick Call"
/>
);
}
41 changes: 41 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--background: #f6f1e9;
--foreground: #16201a;
}

* {
box-sizing: border-box;
}

html {
scroll-behavior: smooth;
}

body {
min-width: 320px;
margin: 0;
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

a {
color: inherit;
text-decoration: none;
}

button,
input,
select,
textarea {
font: inherit;
}

::selection {
background: #b98b4b;
color: #16201a;
}
20 changes: 20 additions & 0 deletions app/how-it-works/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PlaceholderPage } from "@/components/placeholder-page";

export const metadata = {
title: "How It Works",
description: "The Vail Project Path foundation route."
};

export default function HowItWorksPage() {
return (
<PlaceholderPage
eyebrow="The Vail Project Path"
title="A simpler way to move from idea to next step."
body="This foundation route preserves the low-friction Vail project path before the full Project Starter, Scope Builder and CRM workflows are implemented."
primaryHref="/project-request"
primaryLabel="Start the Vail Project Path"
secondaryHref="/scope-builder"
secondaryLabel="Build My Scope"
/>
);
}
33 changes: 33 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import "./globals.css";
import { MobileCtaBar } from "@/components/mobile-cta-bar";
import { SiteFooter } from "@/components/site-footer";
import { SiteHeader } from "@/components/site-header";

export const metadata: Metadata = {
title: {
default: "Vail Renovations | Renovation Help Without the Runaround",
template: "%s | Vail Renovations"
},
description:
"Vail Renovations helps Ottawa homeowners start repairs, updates and renovation projects with one clear point of contact and practical scope guidance.",
metadataBase: new URL("https://vail-renovations.netlify.app"),
openGraph: {
title: "Vail Renovations",
description: "Renovation help without the runaround.",
type: "website"
}
};

export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
<SiteHeader />
{children}
<SiteFooter />
<MobileCtaBar />
</body>
</html>
);
}
120 changes: 120 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Link from "next/link";
import { serviceRoutes } from "@/lib/routes";

const projectSteps = [
"Tell Vail what you want fixed, changed or improved.",
"Share photos or a budget only if you already have them.",
"Vail reviews the request and gives you the next clear step."
];

export default function Home() {
return (
<main>
<section className="relative overflow-hidden bg-oatmeal py-16 md:py-24">
<div className="absolute right-[-12rem] top-[-12rem] h-96 w-96 rounded-full bg-brass/20 blur-3xl" aria-hidden="true" />
<div className="mx-auto grid max-w-6xl items-center gap-10 px-5 lg:grid-cols-[1.1fr_0.9fr]">
<div>
<p className="text-sm font-bold uppercase tracking-wide text-brass">Vail Renovations</p>
<h1 className="mt-4 text-5xl font-bold tracking-tight text-ink md:text-7xl">
Renovation help without the runaround.
</h1>
<p className="mt-6 max-w-2xl text-lg leading-8 text-ink/70">
Vail Renovations helps Ottawa homeowners handle repairs, updates and renovation projects with one clear point of contact, practical scope guidance and the right specialists brought in when needed.
</p>
<p className="mt-4 max-w-2xl text-base leading-7 text-ink/65">
Not sure what trade you need? Just tell us what you are trying to fix, change or improve.
</p>
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
<Link href="/project-request" className="rounded-full bg-forest px-6 py-3 text-center font-bold text-white shadow-soft transition hover:bg-ink">
Start a Project Request
</Link>
<Link href="/book-call" className="rounded-full border border-forest/20 px-6 py-3 text-center font-bold text-forest transition hover:border-forest/50">
Book a Quick Call
</Link>
</div>
<Link href="/scope-builder" className="mt-5 inline-flex text-sm font-bold text-forest underline underline-offset-4">
Want to organize your ideas first? Try the Vail Scope Builder.
</Link>
</div>
<aside className="rounded-[2rem] border border-ink/10 bg-white p-6 shadow-soft">
<p className="text-sm font-bold uppercase tracking-wide text-brass">Project starter preview</p>
<div className="mt-5 grid gap-3">
{projectSteps.map((step, index) => (
<div key={step} className="rounded-2xl bg-oatmeal p-4">
<p className="text-xs font-bold uppercase text-moss">Step {index + 1}</p>
<p className="mt-1 text-sm leading-6 text-ink/75">{step}</p>
</div>
))}
</div>
</aside>
</div>
</section>

<section className="py-16 md:py-24">
<div className="mx-auto max-w-6xl px-5">
<div className="max-w-3xl">
<p className="text-sm font-bold uppercase tracking-wide text-brass">Low friction by design</p>
<h2 className="mt-3 text-3xl font-bold tracking-tight text-ink md:text-5xl">You do not need the whole project figured out.</h2>
<p className="mt-5 text-lg leading-8 text-ink/70">
The site is now a Next.js platform foundation ready for the future Project Starter, Scope Builder and CRM work. This ticket intentionally keeps forms, CRM, auth, database and uploads out of scope.
</p>
</div>
<div className="mt-10 grid gap-5 md:grid-cols-3">
<div className="rounded-3xl border border-ink/10 bg-white p-6 shadow-soft">
<h3 className="text-xl font-bold">Simple starting point</h3>
<p className="mt-3 text-sm leading-6 text-ink/65">Homeowners should be able to start with plain-language notes, not contractor terminology.</p>
</div>
<div className="rounded-3xl border border-ink/10 bg-white p-6 shadow-soft">
<h3 className="text-xl font-bold">Optional detail</h3>
<p className="mt-3 text-sm leading-6 text-ink/65">Photos, budget and exact measurements belong as optional helpers, not barriers.</p>
</div>
<div className="rounded-3xl border border-ink/10 bg-white p-6 shadow-soft">
<h3 className="text-xl font-bold">Clear next step</h3>
<p className="mt-3 text-sm leading-6 text-ink/65">Every route should point back to a project request, callback or quick call.</p>
</div>
</div>
</div>
</section>

<section className="bg-stone/60 py-16 md:py-24">
<div className="mx-auto max-w-6xl px-5">
<div className="flex flex-col justify-between gap-5 md:flex-row md:items-end">
<div>
<p className="text-sm font-bold uppercase tracking-wide text-brass">Services</p>
<h2 className="mt-3 text-3xl font-bold tracking-tight text-ink md:text-5xl">Repairs, updates and renovation work organized properly.</h2>
</div>
<Link href="/services" className="rounded-full border border-forest/20 px-6 py-3 text-center font-bold text-forest">
View Services
</Link>
</div>
<div className="mt-10 grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{serviceRoutes.map((service) => (
<Link key={service.href} href={service.href} className="rounded-3xl border border-ink/10 bg-white p-6 shadow-soft transition hover:-translate-y-1 hover:border-forest/25">
<h3 className="text-xl font-bold text-ink">{service.title}</h3>
<p className="mt-3 text-sm leading-6 text-ink/65">{service.description}</p>
</Link>
))}
</div>
</div>
</section>

<section className="bg-forest py-16 text-white md:py-24">
<div className="mx-auto max-w-5xl px-5 text-center">
<p className="text-sm font-bold uppercase tracking-wide text-stone">Shareable by design</p>
<h2 className="mt-3 text-3xl font-bold tracking-tight md:text-5xl">Know someone trying to figure out a renovation or repair?</h2>
<p className="mx-auto mt-5 max-w-2xl text-lg leading-8 text-white/75">
Send them the Project Starter when the route is connected in the next implementation ticket. For now, this foundation preserves the Vail positioning and app structure without faking lead capture.
</p>
<div className="mt-8 flex flex-col justify-center gap-3 sm:flex-row">
<Link href="/project-request" className="rounded-full bg-white px-6 py-3 text-center font-bold text-forest">
Start a Project Request
</Link>
<Link href="/scope-builder" className="rounded-full border border-white/25 px-6 py-3 text-center font-bold text-white">
Build My Scope
</Link>
</div>
</div>
</section>
</main>
);
}
Loading
Loading