From a41a726b1b55f96d02c2f6ca05e02cb5e82fdfa1 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 09:40:16 -0400 Subject: [PATCH 01/33] Convert Vail site to Next.js platform foundation --- package.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index db7d3ac..dda48f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,31 @@ { - "name": "vail-renovations-static-site", + "name": "vail-renovations-platform", + "version": "0.1.0", "private": true, "scripts": { - "check": "node scripts/check-site.js" + "dev": "next dev", + "build": "next build", + "start": "next start", + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings=0", + "check": "node scripts/check-site.js && npm run typecheck && npm run build" + }, + "dependencies": { + "next": "15.3.4", + "react": "19.0.0", + "react-dom": "19.0.0" + }, + "devDependencies": { + "@eslint/eslintrc": "3.3.1", + "@netlify/plugin-nextjs": "5.11.3", + "@types/node": "22.15.30", + "@types/react": "19.0.12", + "@types/react-dom": "19.0.4", + "autoprefixer": "10.4.21", + "eslint": "9.28.0", + "eslint-config-next": "15.3.4", + "postcss": "8.5.6", + "tailwindcss": "3.4.17", + "typescript": "5.8.3" } } From 7bff9a392527a0cc17bab5d4d961bfd033939d72 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 09:50:04 -0400 Subject: [PATCH 02/33] Add TypeScript configuration --- tsconfig.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tsconfig.json diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6b9a958 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": false, + "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"] +} From fde3febb05d728ed0a9456dd16e403102db59263 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:05:06 -0400 Subject: [PATCH 03/33] Add Next.js environment types --- next-env.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 next-env.d.ts diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..efc031d --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,4 @@ +/// +/// + +// This file is automatically maintained by Next.js. Do not edit manually. From e356db16b57274711e7e35f077eb0c8c79448be8 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:16:47 -0400 Subject: [PATCH 04/33] Add Next.js configuration --- next.config.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 next.config.ts diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..bf53630 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + reactStrictMode: true +}; + +export default nextConfig; From 84ec261427f5ad98391b0aa32c83ddb9a07ede3d Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:21:44 -0400 Subject: [PATCH 05/33] Add PostCSS configuration --- postcss.config.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 postcss.config.js diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..5cbc2c7 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; From 6c95efe6dd45f658a09e5bb17422ee66e2b3d7c0 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:24:47 -0400 Subject: [PATCH 06/33] Add Tailwind configuration --- tailwind.config.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tailwind.config.ts diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..56ec307 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,23 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./lib/**/*.{ts,tsx}"], + theme: { + extend: { + colors: { + ink: "#16201a", + forest: "#254734", + moss: "#6f8364", + stone: "#e9e1d5", + oatmeal: "#f6f1e9", + brass: "#b98b4b" + }, + boxShadow: { + soft: "0 24px 80px rgba(22, 32, 26, 0.12)" + } + } + }, + plugins: [] +}; + +export default config; From ea3689e8d87430ccc7991cb77d07d9df1f31b1a2 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:27:36 -0400 Subject: [PATCH 07/33] Add ESLint configuration --- eslint.config.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 eslint.config.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..0749562 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,14 @@ +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname +}); + +const eslintConfig = [...compat.extends("next/core-web-vitals", "next/typescript")]; + +export default eslintConfig; From ee48da1645ee9efa7873b2d7050b9f86b49c2601 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:30:51 -0400 Subject: [PATCH 08/33] Update Netlify config for Next.js --- netlify.toml | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/netlify.toml b/netlify.toml index d12e8d2..771d822 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,28 +1,6 @@ [build] - publish = "." - command = "" + command = "npm run build" + publish = ".next" -[[redirects]] - from = "/start-a-project" - to = "/start-a-project.html" - status = 200 - -[[redirects]] - from = "/fix-list-builder" - to = "/fix-list-builder.html" - status = 200 - -[[redirects]] - from = "/maintenance-plans" - to = "/maintenance-plans.html" - status = 200 - -[[redirects]] - from = "/inspection-report-repairs" - to = "/inspection-report-repairs.html" - status = 200 - -[[redirects]] - from = "/thank-you" - to = "/thank-you.html" - status = 200 +[[plugins]] + package = "@netlify/plugin-nextjs" From b720684fd11f281a9cd4e92cab3afa87f6fad2ff Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:32:46 -0400 Subject: [PATCH 09/33] Add global styles --- app/globals.css | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 app/globals.css diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..664f41f --- /dev/null +++ b/app/globals.css @@ -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; +} From 2cee91748d76875077b9840ce8753067836f970f Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:33:51 -0400 Subject: [PATCH 10/33] Add public route registry --- lib/routes.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 lib/routes.ts diff --git a/lib/routes.ts b/lib/routes.ts new file mode 100644 index 0000000..5edb36a --- /dev/null +++ b/lib/routes.ts @@ -0,0 +1,50 @@ +export const publicRoutes = [ + { href: "/services", label: "Services" }, + { href: "/how-it-works", label: "How It Works" }, + { href: "/project-request", label: "Project Request" }, + { href: "/scope-builder", label: "Scope Builder" }, + { href: "/contact", label: "Contact" } +] as const; + +export const serviceRoutes = [ + { + href: "/services/bathroom-renovations", + title: "Bathroom Renovations", + description: "Bathroom updates, repairs and practical refreshes organized around the work that actually needs to happen." + }, + { + href: "/services/basement-renovations", + title: "Basement Renovations", + description: "Basement finishing, repair planning and multi-step interior projects with clear next steps." + }, + { + href: "/services/kitchen-updates", + title: "Kitchen Updates", + description: "Kitchen improvements, finish updates and functional fixes without forcing a full luxury remodel path." + }, + { + href: "/services/repairs-refreshes", + title: "Repairs and Refreshes", + description: "Drywall, trim, paint, flooring and home repair lists grouped into a manageable request." + }, + { + href: "/services/pre-sale-improvements", + title: "Pre-Sale Improvements", + description: "Focused improvements that help homeowners prepare a property for listing or turnover." + }, + { + href: "/services/rental-turnovers", + title: "Rental Turnovers", + description: "Repair and refresh planning for rental units, turnover lists and practical make-ready work." + } +] as const; + +export const placeholderRoutes = [ + "/project-request", + "/scope-builder", + "/book-call", + "/callback", + "/contact", + "/privacy", + "/terms" +] as const; From 4d217c7dfe8a5316838af9bada7fb7f2829429fe Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:35:31 -0400 Subject: [PATCH 11/33] Add public site header component --- components/site-header.tsx | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 components/site-header.tsx diff --git a/components/site-header.tsx b/components/site-header.tsx new file mode 100644 index 0000000..81a5786 --- /dev/null +++ b/components/site-header.tsx @@ -0,0 +1,55 @@ +import Link from "next/link"; +import { publicRoutes } from "@/lib/routes"; + +export function SiteHeader() { + return ( +
+ +
+ ); +} From d2f7812b19b1ecd3ef41f19d8358a1b22afe5857 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:37:07 -0400 Subject: [PATCH 12/33] Add public site footer component --- components/site-footer.tsx | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 components/site-footer.tsx diff --git a/components/site-footer.tsx b/components/site-footer.tsx new file mode 100644 index 0000000..8cdf31a --- /dev/null +++ b/components/site-footer.tsx @@ -0,0 +1,40 @@ +import Link from "next/link"; +import { publicRoutes, serviceRoutes } from "@/lib/routes"; + +export function SiteFooter() { + return ( +
+
+
+

Vail Renovations

+

+ Renovation help without the runaround. Photos, budget and a complete scope are not required to start. +

+

Ottawa and surrounding areas.

+
+
+

Public routes

+
+ {publicRoutes.map((route) => ( + + {route.label} + + ))} + Privacy + Terms +
+
+
+

Service pages

+
+ {serviceRoutes.slice(0, 6).map((route) => ( + + {route.title} + + ))} +
+
+
+
+ ); +} From 647a81a3a3f2537b1d62c071b3a39589a51f286f Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 11:38:29 -0400 Subject: [PATCH 13/33] Add mobile sticky CTA bar --- components/mobile-cta-bar.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 components/mobile-cta-bar.tsx diff --git a/components/mobile-cta-bar.tsx b/components/mobile-cta-bar.tsx new file mode 100644 index 0000000..f9bd7b8 --- /dev/null +++ b/components/mobile-cta-bar.tsx @@ -0,0 +1,16 @@ +import Link from "next/link"; + +export function MobileCtaBar() { + return ( +
+
+ + Start Project + + + Book Call + +
+
+ ); +} From a7aef2e73fba5a6efd66ac79d03092b0beb70ebf Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:35:56 -0400 Subject: [PATCH 14/33] Add reusable placeholder page component --- components/placeholder-page.tsx | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 components/placeholder-page.tsx diff --git a/components/placeholder-page.tsx b/components/placeholder-page.tsx new file mode 100644 index 0000000..38d2a2b --- /dev/null +++ b/components/placeholder-page.tsx @@ -0,0 +1,41 @@ +import Link from "next/link"; + +interface PlaceholderPageProps { + eyebrow: string; + title: string; + body: string; + primaryHref?: string; + primaryLabel?: string; + secondaryHref?: string; + secondaryLabel?: string; +} + +export function PlaceholderPage({ + eyebrow, + title, + body, + primaryHref = "/project-request", + primaryLabel = "Start a Project Request", + secondaryHref = "/book-call", + secondaryLabel = "Book a Quick Call" +}: PlaceholderPageProps) { + return ( +
+
+
+

{eyebrow}

+

{title}

+

{body}

+
+ + {primaryLabel} + + + {secondaryLabel} + +
+
+
+
+ ); +} From 2ddc82fa72fdd353dbae8aa8aab113d064958fee Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:41:45 -0400 Subject: [PATCH 15/33] Add Next.js root layout --- app/layout.tsx | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 app/layout.tsx diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..f2c1940 --- /dev/null +++ b/app/layout.tsx @@ -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 ( + + + + {children} + + + + + ); +} From dc9b87b9e143f39f27a2227c0af62be33110c62f Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:49:53 -0400 Subject: [PATCH 16/33] Add Next.js homepage foundation --- app/page.tsx | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 app/page.tsx diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..721101d --- /dev/null +++ b/app/page.tsx @@ -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 ( +
+
+
+ +
+
+
+

Low friction by design

+

You do not need the whole project figured out.

+

+ 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. +

+
+
+
+

Simple starting point

+

Homeowners should be able to start with plain-language notes, not contractor terminology.

+
+
+

Optional detail

+

Photos, budget and exact measurements belong as optional helpers, not barriers.

+
+
+

Clear next step

+

Every route should point back to a project request, callback or quick call.

+
+
+
+
+ +
+
+
+
+

Services

+

Repairs, updates and renovation work organized properly.

+
+ + View Services + +
+
+ {serviceRoutes.map((service) => ( + +

{service.title}

+

{service.description}

+ + ))} +
+
+
+ +
+
+

Shareable by design

+

Know someone trying to figure out a renovation or repair?

+

+ 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. +

+
+ + Start a Project Request + + + Build My Scope + +
+
+
+
+ ); +} From 15da17ac6c9891d12be53b3188941f52d7396674 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:51:23 -0400 Subject: [PATCH 17/33] Add services overview route --- app/services/page.tsx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 app/services/page.tsx diff --git a/app/services/page.tsx b/app/services/page.tsx new file mode 100644 index 0000000..cf683e5 --- /dev/null +++ b/app/services/page.tsx @@ -0,0 +1,29 @@ +import Link from "next/link"; +import { serviceRoutes } from "@/lib/routes"; + +export const metadata = { + title: "Services", + description: "Vail Renovations service route placeholders for bathroom, basement, kitchen, repair, pre-sale and rental turnover work." +}; + +export default function ServicesPage() { + return ( +
+
+

Services

+

Renovation work organized around the next clear step.

+

+ These routes establish the public-site foundation for Vail Renovations. They are placeholders for the next content and intake tickets, not live estimating or lead creation flows. +

+
+ {serviceRoutes.map((service) => ( + +

{service.title}

+

{service.description}

+ + ))} +
+
+
+ ); +} From fc25b6434ea4467b28878aabe10f12ac90d5ba6a Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:52:59 -0400 Subject: [PATCH 18/33] Add service detail route placeholders --- app/services/[slug]/page.tsx | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 app/services/[slug]/page.tsx diff --git a/app/services/[slug]/page.tsx b/app/services/[slug]/page.tsx new file mode 100644 index 0000000..906b956 --- /dev/null +++ b/app/services/[slug]/page.tsx @@ -0,0 +1,58 @@ +import { notFound } from "next/navigation"; +import Link from "next/link"; +import { serviceRoutes } from "@/lib/routes"; + +interface ServicePageProps { + params: Promise<{ slug: string }>; +} + +export function generateStaticParams() { + return serviceRoutes.map((route) => ({ slug: route.href.split("/").pop() })); +} + +export async function generateMetadata({ params }: ServicePageProps) { + const { slug } = await params; + const service = serviceRoutes.find((route) => route.href.endsWith(`/${slug}`)); + + if (!service) { + return {}; + } + + return { + title: service.title, + description: service.description + }; +} + +export default async function ServicePage({ params }: ServicePageProps) { + const { slug } = await params; + const service = serviceRoutes.find((route) => route.href.endsWith(`/${slug}`)); + + if (!service) { + notFound(); + } + + return ( +
+
+

Vail service route

+

{service.title}

+

{service.description}

+
+

Foundation placeholder

+

+ This route exists so the public site architecture is ready before the full content, Project Starter, Scope Builder and CRM tickets are implemented. No lead is created from this page in the foundation ticket. +

+
+
+ + Start a Project Request + + + Back to Services + +
+
+
+ ); +} From 31b9f860ea8b9d059a2ccccf144141fe309a8c13 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:56:16 -0400 Subject: [PATCH 19/33] Add how-it-works route --- app/how-it-works/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/how-it-works/page.tsx diff --git a/app/how-it-works/page.tsx b/app/how-it-works/page.tsx new file mode 100644 index 0000000..a1b746c --- /dev/null +++ b/app/how-it-works/page.tsx @@ -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 ( + + ); +} From 2839670d31a69ca933d5220f1349081c27ff946e Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 13:58:36 -0400 Subject: [PATCH 20/33] Add Project Starter placeholder route --- app/project-request/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/project-request/page.tsx diff --git a/app/project-request/page.tsx b/app/project-request/page.tsx new file mode 100644 index 0000000..b73d83c --- /dev/null +++ b/app/project-request/page.tsx @@ -0,0 +1,20 @@ +import { PlaceholderPage } from "@/components/placeholder-page"; + +export const metadata = { + title: "Project Request", + description: "Vail Project Starter foundation route." +}; + +export default function ProjectRequestPage() { + return ( + + ); +} From 8b1ed54506f6d0ca55ad7e9c9fa658bff45f000f Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 14:46:05 -0400 Subject: [PATCH 21/33] Add Scope Builder placeholder route --- app/scope-builder/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/scope-builder/page.tsx diff --git a/app/scope-builder/page.tsx b/app/scope-builder/page.tsx new file mode 100644 index 0000000..cee0973 --- /dev/null +++ b/app/scope-builder/page.tsx @@ -0,0 +1,20 @@ +import { PlaceholderPage } from "@/components/placeholder-page"; + +export const metadata = { + title: "Scope Builder", + description: "Vail Scope Builder foundation route." +}; + +export default function ScopeBuilderPage() { + return ( + + ); +} From 8fcd3f54304a53373fd8fa9a8675a75b3a1c0345 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 14:51:18 -0400 Subject: [PATCH 22/33] Add book-call placeholder route --- app/book-call/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/book-call/page.tsx diff --git a/app/book-call/page.tsx b/app/book-call/page.tsx new file mode 100644 index 0000000..4447d2f --- /dev/null +++ b/app/book-call/page.tsx @@ -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 ( + + ); +} From b18044198a8e03f20a2d080ffdbe377ba78537bd Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 14:56:18 -0400 Subject: [PATCH 23/33] Add callback placeholder route --- app/callback/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/callback/page.tsx diff --git a/app/callback/page.tsx b/app/callback/page.tsx new file mode 100644 index 0000000..01eaadd --- /dev/null +++ b/app/callback/page.tsx @@ -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 ( + + ); +} From d3ec007f305ead1997f0a69b4fc7af889ba94fc5 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 14:58:01 -0400 Subject: [PATCH 24/33] Add contact placeholder route --- app/contact/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/contact/page.tsx diff --git a/app/contact/page.tsx b/app/contact/page.tsx new file mode 100644 index 0000000..fb8df6b --- /dev/null +++ b/app/contact/page.tsx @@ -0,0 +1,20 @@ +import { PlaceholderPage } from "@/components/placeholder-page"; + +export const metadata = { + title: "Contact", + description: "Contact Vail Renovations." +}; + +export default function ContactPage() { + return ( + + ); +} From 5eee82aabeb3a2786001eaa07a058fca814b9511 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 19:19:49 -0400 Subject: [PATCH 25/33] Add privacy placeholder route --- app/privacy/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/privacy/page.tsx diff --git a/app/privacy/page.tsx b/app/privacy/page.tsx new file mode 100644 index 0000000..a39ba1b --- /dev/null +++ b/app/privacy/page.tsx @@ -0,0 +1,20 @@ +import { PlaceholderPage } from "@/components/placeholder-page"; + +export const metadata = { + title: "Privacy", + description: "Vail Renovations privacy placeholder." +}; + +export default function PrivacyPage() { + return ( + + ); +} From 170842a6f293ef6787168fda2044d0e79dfa0daa Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 19:22:14 -0400 Subject: [PATCH 26/33] Add terms placeholder route --- app/terms/page.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/terms/page.tsx diff --git a/app/terms/page.tsx b/app/terms/page.tsx new file mode 100644 index 0000000..e778c8c --- /dev/null +++ b/app/terms/page.tsx @@ -0,0 +1,20 @@ +import { PlaceholderPage } from "@/components/placeholder-page"; + +export const metadata = { + title: "Terms", + description: "Vail Renovations terms and disclaimer placeholder." +}; + +export default function TermsPage() { + return ( + + ); +} From 3dce5e67987329cbbd2687bd78eb78cd6c5a9f4e Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 19:26:58 -0400 Subject: [PATCH 27/33] Add legacy static route redirects --- next.config.ts | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/next.config.ts b/next.config.ts index bf53630..afaa36c 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,41 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - reactStrictMode: true + reactStrictMode: true, + async redirects() { + return [ + { + source: "/start-a-project", + destination: "/project-request", + permanent: true + }, + { + source: "/fix-list-builder", + destination: "/scope-builder", + permanent: true + }, + { + source: "/maintenance-plans", + destination: "/services/repairs-refreshes", + permanent: false + }, + { + source: "/inspection-report-repairs", + destination: "/services/pre-sale-improvements", + permanent: false + }, + { + source: "/renovations", + destination: "/services", + permanent: true + }, + { + source: "/thank-you", + destination: "/contact", + permanent: false + } + ]; + } }; export default nextConfig; From 2ebb03f9fc19fbdcb6e248c2984814f74194b423 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 20:07:16 -0400 Subject: [PATCH 28/33] Replace static checker with platform foundation checks --- scripts/check-site.js | 137 +++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 61 deletions(-) diff --git a/scripts/check-site.js b/scripts/check-site.js index 4726247..a0a487e 100644 --- a/scripts/check-site.js +++ b/scripts/check-site.js @@ -3,103 +3,118 @@ const path = require("node:path"); const root = process.cwd(); const requiredFiles = [ - "index.html", - "start-a-project.html", - "fix-list-builder.html", - "maintenance-plans.html", - "inspection-report-repairs.html", - "thank-you.html", - "assets/styles.css", - "assets/app.js", + "package.json", + "next.config.ts", + "tsconfig.json", + "tailwind.config.ts", + "postcss.config.js", + "eslint.config.mjs", + "app/layout.tsx", + "app/page.tsx", + "app/globals.css", + "app/services/page.tsx", + "app/services/[slug]/page.tsx", + "app/how-it-works/page.tsx", + "app/project-request/page.tsx", + "app/scope-builder/page.tsx", + "app/book-call/page.tsx", + "app/callback/page.tsx", + "app/contact/page.tsx", + "app/privacy/page.tsx", + "app/terms/page.tsx", + "components/site-header.tsx", + "components/site-footer.tsx", + "components/mobile-cta-bar.tsx", + "components/placeholder-page.tsx", + "lib/routes.ts", "netlify.toml", - "_redirects", - "robots.txt", - "sitemap.xml", + "docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md" ]; -const formRequirements = { - "start-a-project.html": "vail-project-intake", - "fix-list-builder.html": "vail-fix-list", - "maintenance-plans.html": "vail-maintenance-plan", - "inspection-report-repairs.html": "vail-inspection-report", -}; - const bannedPatterns = [/SummitLine/i, /Roofing/i, /roof repair/i, /roofing-lead/i]; +const forbiddenV1Patterns = [ + /createLead/i, + /lead_photos/i, + /supabase/i, + /CRM lead creation/i, + /data-netlify/i, + /localStorage/i +]; const failures = []; -function read(file) { - return fs.readFileSync(path.join(root, file), "utf8"); -} - function exists(file) { return fs.existsSync(path.join(root, file)); } +function read(file) { + return fs.readFileSync(path.join(root, file), "utf8"); +} + for (const file of requiredFiles) { if (!exists(file)) { failures.push(`Missing required file: ${file}`); } } -for (const [file, formName] of Object.entries(formRequirements)) { - if (!exists(file)) { - continue; +if (exists("package.json")) { + const packageJson = JSON.parse(read("package.json")); + const scripts = packageJson.scripts || {}; + for (const script of ["dev", "build", "typecheck", "lint", "check"]) { + if (!scripts[script]) { + failures.push(`package.json is missing script: ${script}`); + } } - - const html = read(file); - const formPattern = new RegExp(`]*name=["']${formName}["'][\\s\\S]*?`, "i"); - const formMatch = html.match(formPattern); - - if (!formMatch) { - failures.push(`${file} is missing form ${formName}`); - continue; + for (const dependency of ["next", "react", "react-dom"]) { + if (!packageJson.dependencies || !packageJson.dependencies[dependency]) { + failures.push(`package.json is missing dependency: ${dependency}`); + } } +} - const form = formMatch[0]; - const checks = [ - { label: "method=\"POST\"", pattern: /method=["']POST["']/i }, - { label: "data-netlify=\"true\"", pattern: /data-netlify=["']true["']/i }, - { label: "action=\"/thank-you\"", pattern: /action=["']\/thank-you["']/i }, - { label: "netlify-honeypot=\"bot-field\"", pattern: /netlify-honeypot=["']bot-field["']/i }, - { label: "hidden form-name input", pattern: new RegExp(`]*type=["']hidden["'][^>]*name=["']form-name["'][^>]*value=["']${formName}["']`, "i") }, - { label: "honeypot input", pattern: /]*name=["']bot-field["']/i }, - ]; - - for (const check of checks) { - if (!check.pattern.test(form)) { - failures.push(`${file} form ${formName} is missing ${check.label}`); - } +if (exists("netlify.toml")) { + const netlifyConfig = read("netlify.toml"); + if (!/command\s*=\s*["']npm run build["']/.test(netlifyConfig)) { + failures.push('netlify.toml must run "npm run build"'); + } + if (!/publish\s*=\s*["']\.next["']/.test(netlifyConfig)) { + failures.push('netlify.toml must publish ".next"'); + } + if (!/@netlify\/plugin-nextjs/.test(netlifyConfig)) { + failures.push("netlify.toml must include @netlify/plugin-nextjs"); } } -for (const file of fs.readdirSync(root).filter((name) => name.endsWith(".html"))) { - const html = read(file); +for (const file of fs.readdirSync(root, { recursive: true }).filter((name) => typeof name === "string" && /\.(ts|tsx|js|html|md|toml)$/.test(name))) { + if (file.includes("node_modules") || file.includes(".next")) { + continue; + } + const body = read(file); for (const pattern of bannedPatterns) { - if (pattern.test(html)) { + if (pattern.test(body)) { failures.push(`${file} contains banned legacy term: ${pattern}`); } } } -if (exists("netlify.toml")) { - const netlifyConfig = read("netlify.toml"); - if (!/publish\s*=\s*["']\.["']/.test(netlifyConfig)) { - failures.push('netlify.toml must contain publish = "."'); - } - if (/publish\s*=\s*["']dist["']/.test(netlifyConfig)) { - failures.push('netlify.toml must not contain publish = "dist"'); +const scopedFiles = ["app/project-request/page.tsx", "app/scope-builder/page.tsx", "app/callback/page.tsx"]; +for (const file of scopedFiles) { + if (!exists(file)) { + continue; } - if (!/command\s*=\s*["']["']/.test(netlifyConfig)) { - failures.push('netlify.toml must contain command = ""'); + const body = read(file); + for (const pattern of forbiddenV1Patterns) { + if (pattern.test(body)) { + failures.push(`${file} appears to introduce out-of-scope V1 backend behavior: ${pattern}`); + } } } if (failures.length > 0) { - console.error("Static site check failed:"); + console.error("Platform foundation check failed:"); for (const failure of failures) { console.error(`- ${failure}`); } process.exit(1); } -console.log("Static site check passed."); +console.log("Platform foundation check passed."); From 019055962ebb15e8c83bd309a2553a29653d3080 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 20:09:41 -0400 Subject: [PATCH 29/33] Update README for Next.js foundation --- README.md | 87 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f0f7063..1144d70 100644 --- a/README.md +++ b/README.md @@ -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` From 74f4447e5075f5e86567a2b09da65005976f7a8d Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 20:32:15 -0400 Subject: [PATCH 30/33] Update sitemap for platform routes --- sitemap.xml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sitemap.xml b/sitemap.xml index f292fe6..5901c25 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,9 +1,19 @@ https://vail-renovations.netlify.app/ - https://vail-renovations.netlify.app/start-a-project - https://vail-renovations.netlify.app/fix-list-builder - https://vail-renovations.netlify.app/maintenance-plans - https://vail-renovations.netlify.app/inspection-report-repairs - https://vail-renovations.netlify.app/thank-you + https://vail-renovations.netlify.app/services + https://vail-renovations.netlify.app/services/bathroom-renovations + https://vail-renovations.netlify.app/services/basement-renovations + https://vail-renovations.netlify.app/services/kitchen-updates + https://vail-renovations.netlify.app/services/repairs-refreshes + https://vail-renovations.netlify.app/services/pre-sale-improvements + https://vail-renovations.netlify.app/services/rental-turnovers + https://vail-renovations.netlify.app/how-it-works + https://vail-renovations.netlify.app/project-request + https://vail-renovations.netlify.app/scope-builder + https://vail-renovations.netlify.app/book-call + https://vail-renovations.netlify.app/callback + https://vail-renovations.netlify.app/contact + https://vail-renovations.netlify.app/privacy + https://vail-renovations.netlify.app/terms From db6324ccf1b2d1770ec13f0dade0fc070f1f32f2 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 20:34:02 -0400 Subject: [PATCH 31/33] Add Vail platform foundation report --- .../VAIL_PLATFORM_FOUNDATION_REPORT.md | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md diff --git a/docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md b/docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md new file mode 100644 index 0000000..7ef953a --- /dev/null +++ b/docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md @@ -0,0 +1,152 @@ +# Vail Platform Foundation Report + +## GO/HOLD + +HOLD pending live dependency installation and command execution in CI or a local environment with npm registry access. + +The repository has been converted at the file level from a static Netlify HTML site into a Next.js App Router foundation. Verification commands were defined but not executed by this implementation environment. + +## Repo conversion summary + +Before this ticket, the repo was a static Netlify site with root-level HTML files, static CSS/JS assets, Netlify Forms and a static checker. + +This ticket adds a Next.js App Router foundation with TypeScript, Tailwind, public route placeholders, shared public layout components and Netlify Next.js deployment settings. + +## Changed file categories + +### Platform configuration + +- `package.json` — replaces static-only package with Next.js, React, TypeScript, Tailwind, ESLint and Netlify Next plugin scripts/dependencies. +- `next.config.ts` — adds Next.js configuration and redirects from legacy static routes to the new route architecture. +- `tsconfig.json` — adds strict TypeScript configuration. +- `next-env.d.ts` — adds Next.js type references. +- `tailwind.config.ts` — adds Tailwind configuration and Vail design tokens. +- `postcss.config.js` — adds Tailwind/PostCSS configuration. +- `eslint.config.mjs` — adds Next.js ESLint flat config. +- `netlify.toml` — switches Netlify from static root publish to Next.js build with `.next` and `@netlify/plugin-nextjs`. + +### App routes + +- `app/layout.tsx` — creates the root public layout. +- `app/page.tsx` — creates the homepage foundation. +- `app/globals.css` — adds global CSS and Tailwind directives. +- `app/services/page.tsx` — adds services overview route. +- `app/services/[slug]/page.tsx` — adds service-detail placeholders. +- `app/how-it-works/page.tsx` — adds How It Works placeholder. +- `app/project-request/page.tsx` — adds Project Starter placeholder. +- `app/scope-builder/page.tsx` — adds Scope Builder placeholder. +- `app/book-call/page.tsx` — adds booking handoff placeholder. +- `app/callback/page.tsx` — adds callback placeholder. +- `app/contact/page.tsx` — adds contact placeholder. +- `app/privacy/page.tsx` — adds privacy placeholder. +- `app/terms/page.tsx` — adds terms placeholder. + +### Shared code + +- `components/site-header.tsx` — adds desktop/mobile public header. +- `components/site-footer.tsx` — adds public footer. +- `components/mobile-cta-bar.tsx` — adds mobile sticky CTAs. +- `components/placeholder-page.tsx` — adds reusable placeholder-page component. +- `lib/routes.ts` — centralizes public and service route metadata. + +### Verification and docs + +- `scripts/check-site.js` — replaces static HTML/Netlify Forms checker with platform-foundation checks. +- `README.md` — updates project documentation for the Next.js foundation. +- `sitemap.xml` — updates sitemap to new public routes. +- `docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md` — records this report. + +## Routes added + +- `/` +- `/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` + +## Legacy redirects + +- `/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` + +## Scripts added + +- `npm run dev` +- `npm run build` +- `npm run start` +- `npm run typecheck` +- `npm run lint` +- `npm run check` + +## Deployment assumptions + +- Netlify remains the deployment target for this foundation. +- Netlify must install dependencies and run `npm run build`. +- The publish directory is `.next`. +- `@netlify/plugin-nextjs` is required for runtime support. + +## Explicit V1 deferrals + +This ticket does not implement: + +- CRM +- Database +- Auth +- Lead creation +- Project Starter functionality +- Scope Builder functionality +- Callback submission +- Photo uploads +- Private storage +- Notifications +- Analytics +- Estimate workflow +- Native booking engine + +## Remaining blockers before full V1 CRM + +The full V1 CRM remains blocked until a real backend path is implemented or selected: + +1. Database persistence. +2. Authenticated CRM users. +3. Role-protected CRM routes. +4. Private photo/file storage. +5. Durable migrations or equivalent schema mechanism. +6. Server-side validation and permission enforcement. +7. First-admin bootstrap path. +8. Environment variable inventory for backend/auth/storage. + +## Verification commands required + +Run in an environment with npm registry access: + +```bash +npm install +npm run check +npm run typecheck +npm run lint +npm run build +``` + +## Known limitations + +- No package lock file was generated in this environment. +- Build/typecheck/lint were not executed in this environment. +- Existing root-level static HTML files remain in the repo for now and are superseded by Next.js routes and redirects. +- The current public routes are placeholders where the implementation ticket required placeholders. +- No backend, auth, storage or CRM behavior exists yet. From 538c3a682064c8c3713e78fc48c2a38eeb7c5d72 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Tue, 12 May 2026 21:43:49 -0400 Subject: [PATCH 32/33] Harden service slug typing --- app/services/[slug]/page.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/services/[slug]/page.tsx b/app/services/[slug]/page.tsx index 906b956..872b78f 100644 --- a/app/services/[slug]/page.tsx +++ b/app/services/[slug]/page.tsx @@ -1,18 +1,30 @@ -import { notFound } from "next/navigation"; import Link from "next/link"; +import { notFound } from "next/navigation"; import { serviceRoutes } from "@/lib/routes"; interface ServicePageProps { params: Promise<{ slug: string }>; } +function getServiceBySlug(slug: string) { + return serviceRoutes.find((route) => route.href.endsWith(`/${slug}`)); +} + export function generateStaticParams() { - return serviceRoutes.map((route) => ({ slug: route.href.split("/").pop() })); + return serviceRoutes.map((route) => { + const slug = route.href.split("/").pop(); + + if (!slug) { + throw new Error(`Missing service slug for route: ${route.href}`); + } + + return { slug }; + }); } export async function generateMetadata({ params }: ServicePageProps) { const { slug } = await params; - const service = serviceRoutes.find((route) => route.href.endsWith(`/${slug}`)); + const service = getServiceBySlug(slug); if (!service) { return {}; @@ -26,7 +38,7 @@ export async function generateMetadata({ params }: ServicePageProps) { export default async function ServicePage({ params }: ServicePageProps) { const { slug } = await params; - const service = serviceRoutes.find((route) => route.href.endsWith(`/${slug}`)); + const service = getServiceBySlug(slug); if (!service) { notFound(); From 19a40cde74d31270401c555a1845aba1c4bff006 Mon Sep 17 00:00:00 2001 From: Harbourview Date: Wed, 13 May 2026 09:10:33 -0400 Subject: [PATCH 33/33] Fix platform foundation checker false positives --- scripts/check-site.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/scripts/check-site.js b/scripts/check-site.js index a0a487e..bb1fd82 100644 --- a/scripts/check-site.js +++ b/scripts/check-site.js @@ -31,14 +31,13 @@ const requiredFiles = [ "docs/control/VAIL_PLATFORM_FOUNDATION_REPORT.md" ]; -const bannedPatterns = [/SummitLine/i, /Roofing/i, /roof repair/i, /roofing-lead/i]; -const forbiddenV1Patterns = [ - /createLead/i, - /lead_photos/i, - /supabase/i, - /CRM lead creation/i, - /data-netlify/i, - /localStorage/i +const bannedLegacyTerms = ["SummitLine", "Roofing", "roof repair", "roofing-lead"]; +const forbiddenImplementationTerms = [ + "createLead", + "lead_photos", + "supabase", + "data-netlify", + "localStorage" ]; const failures = []; @@ -85,13 +84,13 @@ if (exists("netlify.toml")) { } for (const file of fs.readdirSync(root, { recursive: true }).filter((name) => typeof name === "string" && /\.(ts|tsx|js|html|md|toml)$/.test(name))) { - if (file.includes("node_modules") || file.includes(".next")) { + if (file.includes("node_modules") || file.includes(".next") || file === "scripts/check-site.js") { continue; } const body = read(file); - for (const pattern of bannedPatterns) { - if (pattern.test(body)) { - failures.push(`${file} contains banned legacy term: ${pattern}`); + for (const term of bannedLegacyTerms) { + if (body.includes(term)) { + failures.push(`${file} contains banned legacy term: ${term}`); } } } @@ -102,9 +101,9 @@ for (const file of scopedFiles) { continue; } const body = read(file); - for (const pattern of forbiddenV1Patterns) { - if (pattern.test(body)) { - failures.push(`${file} appears to introduce out-of-scope V1 backend behavior: ${pattern}`); + for (const term of forbiddenImplementationTerms) { + if (body.includes(term)) { + failures.push(`${file} appears to introduce out-of-scope foundation behavior: ${term}`); } } }