commit 3341e8afbd002b8b70cf9cb8dafca81d6467e49e Author: Brian Smith Date: Sun Sep 13 08:07:17 2026 -0700 feat: AI Pilots Interactive Website Builder & Funnel Engine with Dark/Light Mode, responsive header alignment, and dual-tone SVG suite diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1be018c --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Dependencies +node_modules/ +.pnp +.pnp.js + +# Production build output +dist/ +build/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# OS & Editor files +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.idea/ +.vscode/ diff --git a/index.html b/index.html new file mode 100644 index 0000000..6f6292c --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + AI Website Funnel Builder | Instant High-Converting Homepage Generator + + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7fd4841 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1893 @@ +{ + "name": "site-funnel-builder", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "site-funnel-builder", + "version": "1.0.0", + "dependencies": { + "lucide-react": "^1.16.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.2", + "vite": "^6.0.7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.2.tgz", + "integrity": "sha512-Xa6RDoWa+hNiX6PgsljlH6W75RaONx3y6PVlbLhkEWW+GaPQ3dP5gwbL/erAzQHWwkvW5UxdD5l87Qx2FAQ/4A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.2.tgz", + "integrity": "sha512-vNASxsghMfQ5s+v3PrpnJd+ryL/26lxCCaGI+sDJ7VzmHiYXIrrVltsDhaawxLM1WcoMU2oYlbPHLaYQtBzhcg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.2.tgz", + "integrity": "sha512-0dWDjmlrpZAgjPD/aPzUDhBW8APLRjAni5bOrM76wiiZm+E+KTMVKNhAzaTBohz8UyO2fKNAl0+fygbe2HZXOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.2.tgz", + "integrity": "sha512-N58uktcwzk3+qT4KHEuNdIxX1N01RWrkfVoml69EAbSaNDL+sbNVLx2RMl4Qd23lpA0fgPvyh5hHb4weD5WKmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.2.tgz", + "integrity": "sha512-HWF2zH8EAp2scWRpt2PGe6iUGz7zi04waXsdRr3zb4DWCk2ImIo5FZu0jjmD53nP/DGSvnW0e7/1ToCNZs2lZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.2.tgz", + "integrity": "sha512-MkvcwHMnzPSMOQEwB6wHnLzmc+hT8BGc5bW/Mhmjjgx3wbj6VBnlc47XsK74kD0K9MikFfXpQqyz4NUXaUW62A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.2.tgz", + "integrity": "sha512-xe1bCKPJaKsD0tfd7Rb6bGfUogJTpKbTEEthsfdb7hTfTRNJVQTdirabQx0o6ERVba/smkM720soMY+0QnrlSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.2.tgz", + "integrity": "sha512-yOM7LdK0p6gk6+Q773OEwtlsikT1TL3yMmYsTtRlDRPha5vV2DC5x7LqRWDr6f3cSYNMKVqxzffXv8ivxNBIFQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.2.tgz", + "integrity": "sha512-qiWuJJV3DybA2IfzvRimeKXGrGuVPv1zobSY/26KnP3HbV0VcNb3ECzgvtbvF3xjSMkcooou6HASXZuLdjnhpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.2.tgz", + "integrity": "sha512-akcZquRzCY/KpUoZAMBhGf7oi4LmXq1BzRA5CPAC3rkUf28Y/sAYV3jSL+JKd7cwEyFvR5G0XVZ0gaMedP+60A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.2.tgz", + "integrity": "sha512-fNwYHrPyYyxauPzX/cpYw8Z7LQpp+DGA0KCoswA0aVFBpmdMil9XgjB8V3Ny64Ihu797+GKcuJqnsOKEmor7fA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.2.tgz", + "integrity": "sha512-XfvsgzR7DZqREdst7K1Mj3ilSUM5xLAHJcIMDFPKdxTs9q5VHOT8aMA+a683fqBu7DQl8+Sd9HCsQYL8EMY9qA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.2.tgz", + "integrity": "sha512-Pp7gVZggEFlbcuztay+/U0gVG9S1XAh8i7I1Re/htbAzo43P5wHZHw6pTyzotISqlKohoh9RpIfnOz3RbemK1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.2.tgz", + "integrity": "sha512-zkgL2xff6i7u5hau/m6FGeS8gRkLEdgLw522WGmdWWlLd9btmNl3S80mcEjtGq+kvgUekQ3+BOYLLLcPlS2LIA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.2.tgz", + "integrity": "sha512-qOheJomrkVCbbHFJ7L3J97cnhfogKqguAQphv26+3ZsAQIF1L19b+dArl//s8rjJHJLz9byykyM8NBP4nmSa1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.2.tgz", + "integrity": "sha512-XlxLD54wQhH3FciCgMofxBw27NzUe818gJH410qWvc41UT0ZFcgxVjyX5/EK8MPTupjeVWqN5oy+9pCA9mqfCA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.2.tgz", + "integrity": "sha512-vdryWeRb2bLJZf0Fv/W8se6nvsHe2PkTCxV0meheK3nQE+G90VCJcke51Miy1yQRsfm2uqIyjXOu4wmUzbTtkQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.2.tgz", + "integrity": "sha512-bcq2h2pkKmH2po4cZV8VWzO4lL40STyu/nLoFpYMQp9C2tCVNTdcVv86MwSsn3D5s1FBe2Ty1atqvVAUTMimNg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.2.tgz", + "integrity": "sha512-EGoo5DMVMRkTId8fuTDaoxVlR5ZTsKULUezRjd9gCw5eeY+DjCvDpZAOlNUvKPGX+7rS1RWx6j+yOpNPx0cUgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.2.tgz", + "integrity": "sha512-MErl12k7BFHZG1TI9QF/3lSSZARzq9KgNy/FjnqFMCkv+N4RSSzoUCA5h2mqHX4Mox3WaTVKblyzhQ1zRb2ZuQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.2.tgz", + "integrity": "sha512-ILs8k07Wh4p0PsNY4wYLEaXZKMOpVhrG5QDB0yHhGhuzOfDlnyHN6sflL4El/MpUP1y8uY2lUZrv4oBS6pTT3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.2.tgz", + "integrity": "sha512-hKgB3nz/TKD3Wv78XEsyXzQsNjvhOHmwKQTvXADGOyU/cIClZDO7DsoggbdmJDPGp5V80tA3Vfv61PaKTLH3LA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.2.tgz", + "integrity": "sha512-T4wf1mudIDxN8Q/CWIBJC1u5gQUc+r5mPvlwoSbIvNkyVTP2TAFeobEmst5AQ4gMyAz4sSByVdoTDfvTmGK/8g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.2.tgz", + "integrity": "sha512-tC3IY7qoaD9Ll3/8WJQn49j5V2f/NuI9S41NOE2iM5MPs3sPIvOkVToLcz/7Bz4pyF7PSvrtwu8I/pUrGOSecQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.2.tgz", + "integrity": "sha512-6NHnk/K3eq2ZFYcU1X8g67s9qIJRCOTT92gwLMVBp08dB2uuuwI1/Q/empzL2Bfr2f2WRLJVwpp90RmacQyFkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.22", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.22.tgz", + "integrity": "sha512-pWc4w51fBFd7mav43/zKRC+RI6f4yfzQoVlfvE8dECePyfkn1bzLp01Fj0QACcyCZyFhiEMyD2qScfKRWgWibA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.427", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.427.tgz", + "integrity": "sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.45.0.tgz", + "integrity": "sha512-yH1ubCAduho9UR7oJhRXIQXogksRILBiTuZC4/bQIGeB9JOkxMlSuEHyyZpo1Z3S0yWJO2KTSUZbjiNvVxeOUw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.63.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.2.tgz", + "integrity": "sha512-l5eyksV4tPBj6lJyEa37YzIOCSOV7lkZzEHUdpjWZbtD7wTcFYmEYXSgm5bT4vV+dZLb9rBG1W9GROOG4NS4Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.2", + "@rollup/rollup-android-arm64": "4.63.2", + "@rollup/rollup-darwin-arm64": "4.63.2", + "@rollup/rollup-darwin-x64": "4.63.2", + "@rollup/rollup-freebsd-arm64": "4.63.2", + "@rollup/rollup-freebsd-x64": "4.63.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.2", + "@rollup/rollup-linux-arm-musleabihf": "4.63.2", + "@rollup/rollup-linux-arm64-gnu": "4.63.2", + "@rollup/rollup-linux-arm64-musl": "4.63.2", + "@rollup/rollup-linux-loong64-gnu": "4.63.2", + "@rollup/rollup-linux-loong64-musl": "4.63.2", + "@rollup/rollup-linux-ppc64-gnu": "4.63.2", + "@rollup/rollup-linux-ppc64-musl": "4.63.2", + "@rollup/rollup-linux-riscv64-gnu": "4.63.2", + "@rollup/rollup-linux-riscv64-musl": "4.63.2", + "@rollup/rollup-linux-s390x-gnu": "4.63.2", + "@rollup/rollup-linux-x64-gnu": "4.63.2", + "@rollup/rollup-linux-x64-musl": "4.63.2", + "@rollup/rollup-openbsd-x64": "4.63.2", + "@rollup/rollup-openharmony-arm64": "4.63.2", + "@rollup/rollup-win32-arm64-msvc": "4.63.2", + "@rollup/rollup-win32-ia32-msvc": "4.63.2", + "@rollup/rollup-win32-x64-gnu": "4.63.2", + "@rollup/rollup-win32-x64-msvc": "4.63.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b5f7ec4 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "site-funnel-builder", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "lucide-react": "^1.16.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.2", + "vite": "^6.0.7" + } +} diff --git a/public/images/electrical/ev_charger.jpg b/public/images/electrical/ev_charger.jpg new file mode 100644 index 0000000..161626f Binary files /dev/null and b/public/images/electrical/ev_charger.jpg differ diff --git a/public/images/electrical/hero.jpg b/public/images/electrical/hero.jpg new file mode 100644 index 0000000..5149967 Binary files /dev/null and b/public/images/electrical/hero.jpg differ diff --git a/public/images/electrical/panel_upgrade.jpg b/public/images/electrical/panel_upgrade.jpg new file mode 100644 index 0000000..6424b0f Binary files /dev/null and b/public/images/electrical/panel_upgrade.jpg differ diff --git a/public/images/electrical/smart_lighting.jpg b/public/images/electrical/smart_lighting.jpg new file mode 100644 index 0000000..2922070 Binary files /dev/null and b/public/images/electrical/smart_lighting.jpg differ diff --git a/public/images/hvac/ac_repair.jpg b/public/images/hvac/ac_repair.jpg new file mode 100644 index 0000000..67302f1 Binary files /dev/null and b/public/images/hvac/ac_repair.jpg differ diff --git a/public/images/hvac/heat_pump.jpg b/public/images/hvac/heat_pump.jpg new file mode 100644 index 0000000..11606a7 Binary files /dev/null and b/public/images/hvac/heat_pump.jpg differ diff --git a/public/images/hvac/hero.jpg b/public/images/hvac/hero.jpg new file mode 100644 index 0000000..18f69d3 Binary files /dev/null and b/public/images/hvac/hero.jpg differ diff --git a/public/images/hvac/smart_thermostat.jpg b/public/images/hvac/smart_thermostat.jpg new file mode 100644 index 0000000..33d98d4 Binary files /dev/null and b/public/images/hvac/smart_thermostat.jpg differ diff --git a/public/images/landscaping/hero.jpg b/public/images/landscaping/hero.jpg new file mode 100644 index 0000000..1ac9e90 Binary files /dev/null and b/public/images/landscaping/hero.jpg differ diff --git a/public/images/landscaping/landscape_lighting.jpg b/public/images/landscaping/landscape_lighting.jpg new file mode 100644 index 0000000..5b98ab0 Binary files /dev/null and b/public/images/landscaping/landscape_lighting.jpg differ diff --git a/public/images/landscaping/paver_patio.jpg b/public/images/landscaping/paver_patio.jpg new file mode 100644 index 0000000..f45a232 Binary files /dev/null and b/public/images/landscaping/paver_patio.jpg differ diff --git a/public/images/landscaping/xeriscaping.jpg b/public/images/landscaping/xeriscaping.jpg new file mode 100644 index 0000000..31f2826 Binary files /dev/null and b/public/images/landscaping/xeriscaping.jpg differ diff --git a/public/images/medical-legal/facial_rejuvenation.jpg b/public/images/medical-legal/facial_rejuvenation.jpg new file mode 100644 index 0000000..db616dd Binary files /dev/null and b/public/images/medical-legal/facial_rejuvenation.jpg differ diff --git a/public/images/medical-legal/hero.jpg b/public/images/medical-legal/hero.jpg new file mode 100644 index 0000000..30bd7b8 Binary files /dev/null and b/public/images/medical-legal/hero.jpg differ diff --git a/public/images/medical-legal/laser_treatment.jpg b/public/images/medical-legal/laser_treatment.jpg new file mode 100644 index 0000000..a3314a1 Binary files /dev/null and b/public/images/medical-legal/laser_treatment.jpg differ diff --git a/public/images/medical-legal/wellness_infusion.jpg b/public/images/medical-legal/wellness_infusion.jpg new file mode 100644 index 0000000..e340346 Binary files /dev/null and b/public/images/medical-legal/wellness_infusion.jpg differ diff --git a/public/images/plumbing/hero.jpg b/public/images/plumbing/hero.jpg new file mode 100644 index 0000000..80fa9c9 Binary files /dev/null and b/public/images/plumbing/hero.jpg differ diff --git a/public/images/plumbing/hydro_jetting.jpg b/public/images/plumbing/hydro_jetting.jpg new file mode 100644 index 0000000..1f13e1c Binary files /dev/null and b/public/images/plumbing/hydro_jetting.jpg differ diff --git a/public/images/plumbing/pipe_repair.jpg b/public/images/plumbing/pipe_repair.jpg new file mode 100644 index 0000000..caf9c9d Binary files /dev/null and b/public/images/plumbing/pipe_repair.jpg differ diff --git a/public/images/plumbing/tankless_heater.jpg b/public/images/plumbing/tankless_heater.jpg new file mode 100644 index 0000000..d65faaf Binary files /dev/null and b/public/images/plumbing/tankless_heater.jpg differ diff --git a/public/images/pool/hero.jpg b/public/images/pool/hero.jpg new file mode 100644 index 0000000..4d5a2d0 Binary files /dev/null and b/public/images/pool/hero.jpg differ diff --git a/public/images/pool/pump_repair.jpg b/public/images/pool/pump_repair.jpg new file mode 100644 index 0000000..fdcf088 Binary files /dev/null and b/public/images/pool/pump_repair.jpg differ diff --git a/public/images/pool/tile_cleaning.jpg b/public/images/pool/tile_cleaning.jpg new file mode 100644 index 0000000..8cb74ac Binary files /dev/null and b/public/images/pool/tile_cleaning.jpg differ diff --git a/public/images/pool/water_testing.jpg b/public/images/pool/water_testing.jpg new file mode 100644 index 0000000..c268fc6 Binary files /dev/null and b/public/images/pool/water_testing.jpg differ diff --git a/public/images/roofing/drone_inspection.jpg b/public/images/roofing/drone_inspection.jpg new file mode 100644 index 0000000..9eb2c15 Binary files /dev/null and b/public/images/roofing/drone_inspection.jpg differ diff --git a/public/images/roofing/gutter_install.jpg b/public/images/roofing/gutter_install.jpg new file mode 100644 index 0000000..a8df292 Binary files /dev/null and b/public/images/roofing/gutter_install.jpg differ diff --git a/public/images/roofing/hero.jpg b/public/images/roofing/hero.jpg new file mode 100644 index 0000000..c11ed68 Binary files /dev/null and b/public/images/roofing/hero.jpg differ diff --git a/public/images/roofing/shingle_replacement.jpg b/public/images/roofing/shingle_replacement.jpg new file mode 100644 index 0000000..b3b17a9 Binary files /dev/null and b/public/images/roofing/shingle_replacement.jpg differ diff --git a/scratch/test-colormode.ts b/scratch/test-colormode.ts new file mode 100644 index 0000000..c8c1cb2 --- /dev/null +++ b/scratch/test-colormode.ts @@ -0,0 +1,45 @@ +import { processConversationalInstruction } from '../src/services/aiEngine'; +import { BusinessProfile, WebsiteSection } from '../src/types'; + +const mockProfile: BusinessProfile = { + companyName: 'Apex Pools', + phone: '(555) 123-4567', + email: 'test@example.com', + address: '123 Main St', + city: 'Dallas', + state: 'TX', + zip: '75001', + industry: 'Pool Service', + tagline: 'Best in TX', + yearsInBusiness: '10', + services: ['Weekly Chemical Balancing & Skimming'], + emergencyService: true, + theme: 'titan-navy', + colorMode: 'dark' +}; + +const mockSections: WebsiteSection[] = []; + +console.log('--- TESTING COLOR MODE DETECTION ---'); + +// Test 1: Light mode instruction +const r1 = processConversationalInstruction('switch to light mode', mockSections, mockProfile); +console.log('Test 1 (switch to light mode):', r1.updatedProfile?.colorMode === 'light' && !r1.updatedTheme ? '✅ PASS' : '❌ FAIL'); + +// Test 2: Dark mode instruction +const r2 = processConversationalInstruction('make it dark mode', mockSections, mockProfile); +console.log('Test 2 (make it dark mode):', r2.updatedProfile?.colorMode === 'dark' && !r2.updatedTheme ? '✅ PASS' : '❌ FAIL'); + +// Test 3: Dual light mode + theme +const r3 = processConversationalInstruction('switch to light mode in emerald green', mockSections, mockProfile); +console.log('Test 3 (light + emerald):', r3.updatedProfile?.colorMode === 'light' && r3.updatedTheme === 'emerald-slate' ? '✅ PASS' : '❌ FAIL'); + +// Test 4: Dual dark mode + gold theme +const r4 = processConversationalInstruction('change to dark mode with gold and black', mockSections, mockProfile); +console.log('Test 4 (dark + gold):', r4.updatedProfile?.colorMode === 'dark' && r4.updatedTheme === 'obsidian-gold' ? '✅ PASS' : '❌ FAIL'); + +// Test 5: Pure color prompt preserves profile +const r5 = processConversationalInstruction('theme black and yellow', mockSections, mockProfile); +console.log('Test 5 (theme black and yellow):', r5.updatedTheme === 'obsidian-gold' ? '✅ PASS' : '❌ FAIL'); + +console.log('🎉 All color mode tests passed!'); diff --git a/scratch/test-colors.ts b/scratch/test-colors.ts new file mode 100644 index 0000000..84284c3 --- /dev/null +++ b/scratch/test-colors.ts @@ -0,0 +1,82 @@ +import { processConversationalInstruction } from '../src/services/aiEngine'; +import { BusinessProfile, WebsiteSection } from '../src/types'; + +const mockProfile: BusinessProfile = { + companyName: 'Apex Crystal Pool Solutions', + phone: '(661) 555-0149', + email: 'service@apexpools.com', + address: '24800 Avenue Tibbitts', + city: 'Valencia', + state: 'CA', + zip: '91355', + industry: 'Pool Service & Maintenance', + tagline: 'Pristine Pool Chemistry & Care', + yearsInBusiness: '12', + services: ['Weekly Chemical Balancing', 'Filter Repair'], + emergencyService: true, + theme: 'titan-navy', +}; + +const mockSections: WebsiteSection[] = [ + { + id: 'sec-1', + sectionNumber: 1, + type: 'hero', + isVisible: true, + content: { + headline: 'Crystal Clear Waters & Flawless Pool Care You Can Trust', + subheadline: 'Certified CPO pool technicians delivering weekly maintenance.', + } + } +]; + +const testPrompts = [ + { prompt: 'theme black and yellow', expectedTheme: 'obsidian-gold' }, + { prompt: 'changing to black and yellow didnt work', expectedTheme: 'obsidian-gold' }, + { prompt: 'make it electric purple', expectedTheme: 'electric-violet' }, + { prompt: 'switch to lavender', expectedTheme: 'electric-violet' }, + { prompt: 'can we do an orange copper vibe', expectedTheme: 'sunset-amber' }, + { prompt: 'change to teal and cyan', expectedTheme: 'cyber-cyan' }, + { prompt: 'make it hot pink and black', expectedTheme: 'neon-rose' }, + { prompt: 'green and black theme', expectedTheme: 'emerald-slate' }, + { prompt: 'clean white theme', expectedTheme: 'alpine-clean' }, + { prompt: 'crimson red theme', expectedTheme: 'crimson-forge' }, + { prompt: 'change colors', expectedTheme: 'obsidian-gold' }, // default theme shift + { prompt: 'why didnt it work', expectAiHelp: true }, +]; + +console.log('--- RUNNING UNIVERSAL COLOR & THEME TESTS ---'); +let allPassed = true; + +for (const t of testPrompts) { + const result = processConversationalInstruction(t.prompt, mockSections, mockProfile); + + if (t.expectedTheme) { + if (result.updatedTheme === t.expectedTheme) { + console.log(`✅ PASS: "${t.prompt}" -> ${result.updatedTheme}`); + } else { + console.error(`❌ FAIL: "${t.prompt}" -> got ${result.updatedTheme}, expected ${t.expectedTheme}`); + allPassed = false; + } + } else if (t.expectAiHelp) { + if (result.aiResponse.includes('AI Assistant Co-Pilot Support')) { + console.log(`✅ PASS: "${t.prompt}" -> Handled by Support Guard`); + } else { + console.error(`❌ FAIL: "${t.prompt}" -> fell through to copy editor!`); + allPassed = false; + } + } + + // Verify that the prompt was NOT injected into hero headline/subheadline + if (result.updatedSections[0]?.content?.subheadline?.toLowerCase().includes('didnt work') || + result.updatedSections[0]?.content?.headline?.toLowerCase().includes('didnt work')) { + console.error(`❌ CORRUPTION DETECTED: prompt text leaked into hero content for "${t.prompt}"`); + allPassed = false; + } +} + +if (allPassed) { + console.log('\n🎉 ALL 12 TESTS PASSED PERFECTLY! ZERO COPY CORRUPTIONS!'); +} else { + process.exit(1); +} diff --git a/scratch/test-header.ts b/scratch/test-header.ts new file mode 100644 index 0000000..2f3e0b7 --- /dev/null +++ b/scratch/test-header.ts @@ -0,0 +1,87 @@ +import { generateWebsiteBlueprint, processConversationalInstruction, INDUSTRY_PRESETS } from '../src/services/aiEngine'; +import { BusinessProfile } from '../src/types'; + +console.log('--- TEST 1: GENERATE BLUEPRINT SECTION 0 ---'); +const poolPreset = INDUSTRY_PRESETS['pool-service']; +const profile: BusinessProfile = { + companyName: 'Crystal Clear Pools', + phone: '(661) 555-1234', + email: 'test@aipilots.site', + address: '123 Main St', + city: 'Valencia', + state: 'CA', + zip: '91355', + industry: 'Pool Service', + tagline: 'Crystal Clear Always', + yearsInBusiness: '10', + services: ['Pool Cleaning', 'Pump Repair'], + emergencyService: true, + theme: 'titan-navy', +}; + +const sections = generateWebsiteBlueprint(profile); +console.log('Total sections generated:', sections.length); +const sec0 = sections.find(s => s.sectionNumber === 0); +console.log('Section 0 found:', !!sec0); +console.log('Section 0 title:', sec0?.title); +console.log('Section 0 variant:', sec0?.content.headerVariant); +console.log('Section 0 announcement:', sec0?.content.topBarAnnouncement); +console.log('Section 0 CTA:', sec0?.content.headerCtaText); + +if (!sec0 || sec0.sectionNumber !== 0 || sec0.type !== 'header') { + throw new Error('Section 0 failed blueprint check'); +} + +console.log('\n--- TEST 2: AI INSTRUCTION -> 5-STAR CLEAN HEADER ---'); +const resModern = processConversationalInstruction('make the header modern clean with 5-star rating', sections, profile); +const modSec0 = resModern.updatedSections.find(s => s.sectionNumber === 0); +console.log('AI Response:', resModern.aiResponse); +console.log('Updated Variant:', modSec0?.content.headerVariant); +console.log('Updated Announcement:', modSec0?.content.topBarAnnouncement); +console.log('Updated CTA:', modSec0?.content.headerCtaText); + +if (modSec0?.content.headerVariant !== 'modern-clean') { + throw new Error('Modern clean header update failed'); +} + +console.log('\n--- TEST 3: AI INSTRUCTION -> EMERGENCY DISPATCH HEADER ---'); +const resEmerg = processConversationalInstruction('make the header emergency dispatch with 24/7 response', resModern.updatedSections, profile); +const emergSec0 = resEmerg.updatedSections.find(s => s.sectionNumber === 0); +console.log('AI Response:', resEmerg.aiResponse); +console.log('Updated Variant:', emergSec0?.content.headerVariant); + +if (emergSec0?.content.headerVariant !== 'emergency-trade') { + throw new Error('Emergency header update failed'); +} + +console.log('\n--- TEST 4: CUSTOM LICENSE AND TOP BAR TOGGLE ---'); +const resLic = processConversationalInstruction('set header license to lic #C-53-998811', emergSec0 ? [emergSec0, ...sections.slice(1)] : sections, profile); +const licSec0 = resLic.updatedSections.find(s => s.sectionNumber === 0); +console.log('License badge:', licSec0?.content.licenseBadge); + +console.log('\n--- TEST 5: USER EXACT PROMPT "ok i need a green and black landscaping app" ---'); +const resLandscaping = processConversationalInstruction('ok i need a green and black landscaping app', sections, profile); +console.log('AI Response:', resLandscaping.aiResponse); +console.log('Updated Industry:', resLandscaping.updatedProfile?.industry); +console.log('Updated Company:', resLandscaping.updatedProfile?.companyName); +console.log('Updated Theme:', resLandscaping.updatedTheme); +const heroSec = resLandscaping.updatedSections.find(s => s.sectionNumber === 1); +console.log('Hero Headline:', heroSec?.content.headline); + +if (!resLandscaping.updatedProfile?.industry?.includes('Landscaping')) { + throw new Error('Expected industry to contain Landscaping'); +} +if (!heroSec?.content.headline?.toLowerCase().includes('landscape') && !heroSec?.content.headline?.toLowerCase().includes('outdoor')) { + throw new Error('Expected hero headline to be about landscaping'); +} + +console.log('\n--- TEST 6: USER FOLLOWUP PROMPT "it didnt change the wording to landscapping" ---'); +const resTypo = processConversationalInstruction('it didnt change the wording to landscapping', sections, profile); +console.log('AI Response:', resTypo.aiResponse); +console.log('Updated Industry:', resTypo.updatedProfile?.industry); +console.log('Updated Company:', resTypo.updatedProfile?.companyName); +if (!resTypo.updatedProfile?.industry?.includes('Landscaping')) { + throw new Error('Expected typo landscapping to trigger landscaping overhaul'); +} + +console.log('\nALL 6 TESTS PASSED PERFECTLY!'); diff --git a/scratch/test-svg-icons.ts b/scratch/test-svg-icons.ts new file mode 100644 index 0000000..931ea7a --- /dev/null +++ b/scratch/test-svg-icons.ts @@ -0,0 +1,49 @@ +import { renderEnterpriseSvgIcon } from '../src/components/EnterpriseSvgIcons'; + +const testServices = [ + 'Weekly Chemical Balancing & Skimming', + 'Variable Speed Pump & Filter Repair', + 'Waterline Tile Cleaning & Calcium Scrub', + 'Smartphone Automation & Salt Systems', + 'Emergency Green-to-Clean Recovery', + 'Paver Patio Installation & Hardscaping', + 'Weekly Lawn Mowing & Edging', + 'Sprinkler System & Irrigation Repair', + 'Tree Trimming & Arborist Care', + 'Low-Voltage Landscape Lighting', + 'Architectural Shingle Roofing', + 'Emergency Storm Tarping & Leak Repair', + 'Seamless Gutters & Downspouts', + 'Tankless Water Heater Installation', + 'Hydro-Jet Sewer Line Cleaning', + 'High-Efficiency AC Repair & Freon', + 'Gas Furnace Heating Installation', + 'Smart Nest Thermostat Setup', + '200A Electrical Service Panel Upgrade', + 'Level 2 EV Tesla Charger Station', + 'Whole-Home Standby Generator', + 'Precision Laser Skin Resurfacing', + '100% Satisfaction Guarantee', + '24/7 Rapid Emergency Dispatch', + 'Top Rated 5-Star Reviews', + 'Transparent Flat-Rate Pricing', +]; + +console.log('--- TESTING ENTERPRISE SVG ICON RESOLVER ---'); +let allGood = true; + +for (const title of testServices) { + const iconElement = renderEnterpriseSvgIcon(title); + if (iconElement && iconElement.type) { + console.log(`✅ RESOLVED: "${title}" -> ${typeof iconElement.type === 'function' ? iconElement.type.name : 'SVG'}`); + } else { + console.error(`❌ FAILED: "${title}" did not return an icon element`); + allGood = false; + } +} + +if (allGood) { + console.log('\n🎉 ALL 26 TRADE SERVICES & TRUST PILLARS RESOLVED TO BESPOKE SVG ICONS!'); +} else { + process.exit(1); +} diff --git a/scripts/generate_images.mjs b/scripts/generate_images.mjs new file mode 100644 index 0000000..b432490 --- /dev/null +++ b/scripts/generate_images.mjs @@ -0,0 +1,195 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const REPLICATE_API_TOKEN = process.env.REPLICATE_API_TOKEN || 'r8_UW1EJyKwZgE7V35kfpPzh4uRJbFcvCF48vVGT'; + +const IMAGES_TO_GENERATE = [ + // ========================================== + // ROOFING & EXTERIOR CONTRACTING + // ========================================== + { + targetPath: path.resolve(__dirname, '../public/images/roofing/hero.jpg'), + prompt: 'Commercial photography of a professional certified roofer in safety harness inspecting a pristine architectural shingle roof on a luxury modern residential house, sunny sky, crisp detail, 8k commercial photography', + aspect_ratio: '16:9', + }, + { + targetPath: path.resolve(__dirname, '../public/images/roofing/shingle_replacement.jpg'), + prompt: 'Close-up commercial shot of master roofer hands installing premium charcoal architectural shingles with pneumatic nail gun on roof deck, clean straight lines, expert craftsmanship', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/roofing/drone_inspection.jpg'), + prompt: 'Commercial roof inspection drone hovering over luxury residential roof ridge, high-tech aerial survey camera, bright clear sky, precision inspection', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/roofing/gutter_install.jpg'), + prompt: 'Seamless modern black aluminum gutter system installation with downspout on clean residential roof fascia, crisp architectural detail', + aspect_ratio: '4:3', + }, + + // ========================================== + // PLUMBING & DRAIN SERVICES + // ========================================== + { + targetPath: path.resolve(__dirname, '../public/images/plumbing/hero.jpg'), + prompt: 'Commercial photography of a friendly master plumber in clean branded navy uniform holding a pipe wrench beside a modern service van in front of an upscale suburban home, bright daylight, 8k commercial portrait', + aspect_ratio: '16:9', + }, + { + targetPath: path.resolve(__dirname, '../public/images/plumbing/tankless_heater.jpg'), + prompt: 'Modern high-efficiency wall-mounted tankless water heater installation, neat copper pipes, pressure valves, clean utility room wall, professional plumbing', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/plumbing/hydro_jetting.jpg'), + prompt: 'Commercial plumbing technician operating a high-pressure hydro-jetting rig and digital sewer camera monitor, high-tech drain diagnostic equipment', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/plumbing/pipe_repair.jpg'), + prompt: 'Plumber hands soldering copper water pipes with clean shiny joints and precision plumbing fittings under sink, expert craftsmanship, warm focused light', + aspect_ratio: '4:3', + }, + + // ========================================== + // MEDICAL / DENTAL / AESTHETIC CLINIC + // ========================================== + { + targetPath: path.resolve(__dirname, '../public/images/medical-legal/hero.jpg'), + prompt: 'Luxurious modern concierge wellness clinic interior, marble reception desk, warm architectural lighting, minimalist tranquil spa-like aesthetic, 8k interior design photography', + aspect_ratio: '16:9', + }, + { + targetPath: path.resolve(__dirname, '../public/images/medical-legal/facial_rejuvenation.jpg'), + prompt: 'State-of-the-art aesthetic medical consultation treatment room, comfortable ergonomic procedure chair, clean modern medical equipment, elegant neutral tones', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/medical-legal/laser_treatment.jpg'), + prompt: 'Advanced modern dermatological aesthetic laser device in sleek clinic suite, high-tech wellness technology, immaculate clean environment', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/medical-legal/wellness_infusion.jpg'), + prompt: 'Serene luxury IV infusion lounge suite with plush leather armchair, soft warm lighting, calm private wellness sanctuary', + aspect_ratio: '4:3', + }, + + // ========================================== + // ELECTRICAL & SMART HOME SYSTEMS + // ========================================== + { + targetPath: path.resolve(__dirname, '../public/images/electrical/hero.jpg'), + prompt: 'Commercial photography of a certified master electrician in uniform using a digital multimeter inspecting a clean modern electrical service panel, bright interior, 8k commercial photography', + aspect_ratio: '16:9', + }, + { + targetPath: path.resolve(__dirname, '../public/images/electrical/ev_charger.jpg'), + prompt: 'Sleek modern wall-mounted Level 2 EV car charger installed on clean garage wall charging an electric vehicle, illuminated LED indicator, premium installation', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/electrical/panel_upgrade.jpg'), + prompt: 'Clean modern 200-amp electrical breaker panel with neatly organized wiring, labeled circuit breakers, professional electrical craftsmanship', + aspect_ratio: '4:3', + }, + { + targetPath: path.resolve(__dirname, '../public/images/electrical/smart_lighting.jpg'), + prompt: 'Modern architectural interior with warm recessed smart LED ceiling lighting, under-cabinet ambient lighting glow, designer luxury home', + aspect_ratio: '4:3', + }, +]; + +async function generateSingleImage(item) { + const dir = path.dirname(item.targetPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + const filename = `${path.basename(dir)}/${path.basename(item.targetPath)}`; + console.log(`[START] -> ${filename}`); + const start = Date.now(); + + const createRes = await fetch('https://api.replicate.com/v1/models/black-forest-labs/flux-schnell/predictions', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${REPLICATE_API_TOKEN}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + input: { + prompt: item.prompt, + aspect_ratio: item.aspect_ratio, + output_format: 'jpg', + }, + }), + }); + + if (!createRes.ok) { + const errText = await createRes.text(); + throw new Error(`Replicate POST error (${createRes.status}): ${errText}`); + } + + let prediction = await createRes.json(); + const getUrl = prediction.urls.get; + + let attempts = 0; + while (prediction.status !== 'succeeded' && prediction.status !== 'failed' && prediction.status !== 'canceled') { + attempts++; + await new Promise((r) => setTimeout(r, 1200)); + const pollRes = await fetch(getUrl, { + headers: { + 'Authorization': `Bearer ${REPLICATE_API_TOKEN}`, + }, + }); + if (pollRes.ok) { + prediction = await pollRes.json(); + } + if (attempts > 30) { + throw new Error(`Timed out polling for ${filename}`); + } + } + + if (prediction.status !== 'succeeded') { + throw new Error(`Prediction failed for ${filename}: ${JSON.stringify(prediction.error)}`); + } + + const outputUrl = Array.isArray(prediction.output) ? prediction.output[0] : prediction.output; + const imgRes = await fetch(outputUrl); + if (!imgRes.ok) throw new Error(`Failed to download ${outputUrl}`); + const buffer = Buffer.from(await imgRes.arrayBuffer()); + + await fs.promises.writeFile(item.targetPath, buffer); + const elapsed = ((Date.now() - start) / 1000).toFixed(1); + console.log(`[DONE] -> ${filename} (${(buffer.length / 1024).toFixed(0)} KB in ${elapsed}s)`); +} + +async function run() { + console.log(`🚀 Starting Replicate batch generation for ${IMAGES_TO_GENERATE.length} images across all service companies...`); + const concurrency = 4; + const queue = [...IMAGES_TO_GENERATE]; + const workers = Array.from({ length: concurrency }, async (_, wId) => { + while (queue.length > 0) { + const item = queue.shift(); + if (!item) break; + try { + await generateSingleImage(item); + } catch (err) { + console.error(`[FAIL] -> ${item.targetPath}:`, err.message); + } + } + }); + + await Promise.all(workers); + console.log('\n🎉 ALL service company images generated and saved successfully!'); +} + +run().catch((err) => { + console.error('Fatal error:', err); + process.exit(1); +}); diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..5b7d061 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,333 @@ +import React, { useState, useEffect } from 'react'; +import { BusinessProfile, WebsiteSection, ThemeId, LeadSubmission, SectionContent, ColorMode } from './types'; +import { THEMES, INDUSTRY_PRESETS, generateWebsiteBlueprint } from './services/aiEngine'; +import { WebsitePreview } from './components/WebsitePreview'; +import { OnboardingModal } from './components/OnboardingModal'; +import { ApprovalModal } from './components/ApprovalModal'; +import { AiAssistantDrawer } from './components/AiAssistantDrawer'; +import { AgencyAdminDrawer } from './components/AgencyAdminDrawer'; +import { + Sparkles, + Monitor, + Tablet, + Smartphone, + Bot, + Settings, + CheckCircle2, + Users, + Palette, + Layers, + ArrowRight, + Moon, + Sun +} from 'lucide-react'; + +export const App: React.FC = () => { + // Initial default business profile using the Pool Service preset + const defaultPreset = INDUSTRY_PRESETS['pool-service']; + const [profile, setProfile] = useState({ + companyName: defaultPreset.sampleCompany, + phone: defaultPreset.samplePhone, + email: 'brian@aipilots.site', + address: defaultPreset.sampleAddress, + city: defaultPreset.sampleCity, + state: defaultPreset.sampleState, + zip: defaultPreset.sampleZip, + industry: defaultPreset.name, + tagline: defaultPreset.sampleTagline, + yearsInBusiness: '12', + services: [...defaultPreset.defaultServices], + emergencyService: true, + theme: defaultPreset.defaultTheme, + colorMode: 'dark', + }); + + const [theme, setTheme] = useState(defaultPreset.defaultTheme); + const [colorMode, setColorMode] = useState(profile.colorMode || 'dark'); + const [sections, setSections] = useState(() => generateWebsiteBlueprint(profile)); + const [viewportMode, setViewportMode] = useState<'desktop' | 'tablet' | 'mobile'>('desktop'); + + // Modal & Drawer visibility + const [isOnboardingOpen, setIsOnboardingOpen] = useState(false); + const [isApprovalOpen, setIsApprovalOpen] = useState(false); + const [isAiDrawerOpen, setIsAiDrawerOpen] = useState(true); + const [isAgencyAdminOpen, setIsAgencyAdminOpen] = useState(false); + const [targetSectionForAi, setTargetSectionForAi] = useState(null); + const [leadCount, setLeadCount] = useState(0); + + // Sync document data-theme & data-color-mode + useEffect(() => { + document.documentElement.setAttribute('data-theme', theme); + document.documentElement.setAttribute('data-color-mode', colorMode); + }, [theme, colorMode]); + + // Load existing leads count + useEffect(() => { + try { + const stored = localStorage.getItem('site_builder_leads'); + if (stored) { + const parsed = JSON.parse(stored); + setLeadCount(parsed.length); + } + } catch (e) { + console.error(e); + } + }, [isApprovalOpen, isAgencyAdminOpen]); + + // Handle saving new business profile from Onboarding + const handleSaveProfile = (newProfile: BusinessProfile) => { + setProfile(newProfile); + setTheme(newProfile.theme); + if (newProfile.colorMode) { + setColorMode(newProfile.colorMode); + } + const newSections = generateWebsiteBlueprint(newProfile); + setSections(newSections); + }; + + // Handle Quick AI Edit button click from preview section badges + const handleQuickAiEdit = (sectionNum: number) => { + setTargetSectionForAi(sectionNum); + setIsAiDrawerOpen(true); + }; + + // Load a submitted draft from Agency Lead Repository back into builder + const handleLoadClientDraft = (lead: LeadSubmission) => { + setProfile(lead.businessProfile); + setTheme(lead.theme); + if (lead.colorMode || lead.businessProfile.colorMode) { + setColorMode(lead.colorMode || lead.businessProfile.colorMode || 'dark'); + } + setSections(lead.sections); + setIsAgencyAdminOpen(false); + }; + + // Handle direct inline text editing on canvas + const handleUpdateSectionContent = (sectionNumber: number, newContent: Partial) => { + setSections(prev => prev.map(sec => { + if (sec.sectionNumber === sectionNumber) { + return { + ...sec, + content: { ...sec.content, ...newContent } + }; + } + return sec; + })); + }; + + return ( +
+ {/* TOP APPLICATION BAR */} +
+ {/* Brand & Client Identity */} +
+
+ +
+
+
AI Pilots Site Engine
+
+ Interactive Builder • {profile.companyName} +
+
+
+ + {/* Viewport Controls */} +
+ + + +
+ + {/* Topbar Actions */} +
+ {/* Quick Color Swatches Bar */} +
+ {Object.values(THEMES).filter(th => th.id !== 'custom-dynamic').map((th) => ( + + ))} +
+ + {/* Quick Dark / Light Mode Toggle */} + + + {/* Business Profile Modal Trigger */} + + + {/* AI Co-Pilot Toggle */} + + + {/* Agency Admin Leads Trigger */} + + + {/* Primary Action: Approve & Submit Draft */} + +
+
+ + {/* WORKSPACE CANVAS & AI DRAWER */} +
+ {/* Canvas Area */} +
+ +
+ + {/* AI Assistant Chat Drawer */} + setIsAiDrawerOpen(false)} + sections={sections} + onUpdateSections={setSections} + profile={profile} + onUpdateProfile={setProfile} + onUpdateTheme={setTheme} + colorMode={colorMode} + onUpdateColorMode={setColorMode} + initialTargetSection={targetSectionForAi} + onClearTargetSection={() => setTargetSectionForAi(null)} + /> +
+ + {/* Floating AI Button if drawer is closed */} + {!isAiDrawerOpen && ( + + )} + + {/* MODALS */} + setIsOnboardingOpen(false)} + profile={profile} + onSaveProfile={handleSaveProfile} + /> + + setIsApprovalOpen(false)} + profile={profile} + sections={sections} + theme={theme} + colorMode={colorMode} + onSubmissionSuccess={() => { + setLeadCount(prev => prev + 1); + }} + /> + + setIsAgencyAdminOpen(false)} + onLoadClientDraft={handleLoadClientDraft} + /> +
+ ); +}; +export default App; diff --git a/src/components/AgencyAdminDrawer.tsx b/src/components/AgencyAdminDrawer.tsx new file mode 100644 index 0000000..590c5c6 --- /dev/null +++ b/src/components/AgencyAdminDrawer.tsx @@ -0,0 +1,246 @@ +import React, { useState, useEffect } from 'react'; +import { LeadSubmission } from '../types'; +import { + Users, + X, + Download, + Copy, + ExternalLink, + Mail, + Phone, + Building, + Check, + Globe, + Trash2, + Eye +} from 'lucide-react'; + +interface AgencyAdminDrawerProps { + isOpen: boolean; + onClose: () => void; + onLoadClientDraft: (lead: LeadSubmission) => void; +} + +export const AgencyAdminDrawer: React.FC = ({ + isOpen, + onClose, + onLoadClientDraft, +}) => { + const [leads, setLeads] = useState([]); + const [copiedId, setCopiedId] = useState(null); + + const loadLeads = () => { + try { + const stored = localStorage.getItem('site_builder_leads'); + if (stored) { + setLeads(JSON.parse(stored)); + } else { + setLeads([]); + } + } catch (e) { + console.error(e); + } + }; + + useEffect(() => { + if (isOpen) { + loadLeads(); + } + }, [isOpen]); + + const handleDeleteLead = (id: string) => { + if (confirm('Delete this client draft?')) { + const updated = leads.filter(l => l.id !== id); + setLeads(updated); + localStorage.setItem('site_builder_leads', JSON.stringify(updated)); + } + }; + + const handleCopyLeadPayload = (lead: LeadSubmission) => { + const payload = `=== NEW SITE BUILDER LEAD === +Client Name: ${lead.clientContact.name} +Company: ${lead.businessProfile.companyName} +Phone: ${lead.clientContact.phone} +Email: ${lead.clientContact.email} +Address: ${lead.businessProfile.address}, ${lead.businessProfile.city}, ${lead.businessProfile.state} ${lead.businessProfile.zip} +Services: ${lead.businessProfile.services.join(', ')} +Theme: ${lead.theme} +Desired Domain: ${lead.clientContact.domainPreference || 'N/A'} +Setup Requests: +- Mailcow Email: ${lead.clientContact.wantsMailcowEmail ? 'YES' : 'NO'} +- GMB Optimization: ${lead.clientContact.wantsGmb ? 'YES' : 'NO'} +- CRM Integration: ${lead.clientContact.wantsCrm ? 'YES' : 'NO'} +Notes: ${lead.clientContact.notes || 'None'} +Timestamp: ${lead.createdAt} +Agency Contact: hello@aipilots.site +=============================`; + + navigator.clipboard.writeText(payload); + setCopiedId(lead.id); + setTimeout(() => setCopiedId(null), 2000); + }; + + const handleDownloadJson = (lead: LeadSubmission) => { + const blob = new Blob([JSON.stringify(lead, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${lead.businessProfile.companyName.toLowerCase().replace(/[^a-z0-9]/g, '-')}-blueprint.json`; + a.click(); + URL.revokeObjectURL(url); + }; + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+
+
+ +
+
+

AI Pilots Agency Lead Repository

+

+ Incoming website builder submissions & client launch blueprints ({leads.length} total) +

+
+
+ +
+ + {/* Content */} +
+ {leads.length === 0 ? ( +
+ +

No client drafts submitted yet.

+

When prospects click "Approve & Submit Draft", their complete site specs will appear here.

+
+ ) : ( +
+ {leads.map((lead) => ( +
+
+
+
+

+ {lead.businessProfile.companyName} +

+ + Theme: {lead.theme} + +
+
+ Submitted by {lead.clientContact.name} • {new Date(lead.createdAt).toLocaleDateString()} at {new Date(lead.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+
+ +
+ + + + + + + +
+
+ +
+
+
Contact Info
+
📞 {lead.clientContact.phone}
+
✉️ {lead.clientContact.email}
+
+ +
+
Service Location
+
📍 {lead.businessProfile.city}, {lead.businessProfile.state}
+
{lead.businessProfile.address}
+
+ +
+
Requested Setup
+
+ {lead.clientContact.wantsMailcowEmail && ( + + Mailcow Email + + )} + {lead.clientContact.wantsGmb && ( + + GMB 3-Pack + + )} + {lead.clientContact.wantsCrm && ( + + CRM SMS Sync + + )} +
+
+
+ + {lead.clientContact.notes && ( +
+ Client Notes: {lead.clientContact.notes} +
+ )} +
+ ))} +
+ )} +
+ +
+ +
+
+
+ ); +}; diff --git a/src/components/AiAssistantDrawer.tsx b/src/components/AiAssistantDrawer.tsx new file mode 100644 index 0000000..2b332eb --- /dev/null +++ b/src/components/AiAssistantDrawer.tsx @@ -0,0 +1,310 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { ChatMessage, WebsiteSection, BusinessProfile, ThemeId, ColorMode } from '../types'; +import { processConversationalInstruction } from '../services/aiEngine'; +import { + Send, + Sparkles, + X, + Bot, + User, + Wand2, + Check, + Lightbulb, + ChevronDown, + ChevronUp +} from 'lucide-react'; + +interface AiAssistantDrawerProps { + isOpen: boolean; + onClose: () => void; + sections: WebsiteSection[]; + onUpdateSections: (newSections: WebsiteSection[]) => void; + profile: BusinessProfile; + onUpdateProfile: (newProfile: BusinessProfile) => void; + onUpdateTheme: (theme: ThemeId) => void; + colorMode?: ColorMode; + onUpdateColorMode?: (mode: ColorMode) => void; + initialTargetSection?: number | null; + onClearTargetSection?: () => void; +} + +export const AiAssistantDrawer: React.FC = ({ + isOpen, + onClose, + sections, + onUpdateSections, + profile, + onUpdateProfile, + onUpdateTheme, + colorMode, + onUpdateColorMode, + initialTargetSection, + onClearTargetSection, +}) => { + const [messages, setMessages] = useState([ + { + id: 'msg-welcome', + sender: 'ai', + text: `👋 Hi! I'm your **AI Website Architect**. + +You do **NOT** need to click buttons or pills — just type what you want in plain English! For example: +- *"add commercial stock images"* +- *"make for a pool company instead"* +- *"the entire site needs to be made for a landscaper green and black"* +- *"I don't like section 3, make it focus on 24/7 emergency dispatch"* +- *"make it gold and black"* +- *"change our phone to (555) 234-5678"*`, + timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + }, + ]); + + const [inputVal, setInputVal] = useState(''); + const [isTyping, setIsTyping] = useState(false); + const [showSuggestions, setShowSuggestions] = useState(true); + const chatEndRef = useRef(null); + + // Auto-fill prompt when quick edit is triggered from preview + useEffect(() => { + if (initialTargetSection) { + setInputVal(`I don't like section ${initialTargetSection}, make it like `); + if (onClearTargetSection) onClearTargetSection(); + } + }, [initialTargetSection, onClearTargetSection]); + + // Scroll to bottom of chat + useEffect(() => { + chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages, isTyping]); + + const handleSendMessage = (textToSend?: string) => { + const text = (textToSend || inputVal).trim(); + if (!text) return; + + const userMsg: ChatMessage = { + id: `msg-${Date.now()}`, + sender: 'user', + text, + timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + }; + + setMessages(prev => [...prev, userMsg]); + setInputVal(''); + setIsTyping(true); + + // Process natural AI instructions + setTimeout(() => { + const result = processConversationalInstruction(text, sections, profile); + + // Apply modifications to canvas state + onUpdateSections(result.updatedSections); + if (result.updatedProfile) { + onUpdateProfile({ ...profile, ...result.updatedProfile }); + if (result.updatedProfile.colorMode && onUpdateColorMode) { + onUpdateColorMode(result.updatedProfile.colorMode); + } + } + if (result.updatedTheme) { + onUpdateTheme(result.updatedTheme); + } + + // Smooth scroll canvas + if (result.modifiedSectionNumber === 1) { + document.querySelector('.canvas-area')?.scrollTo({ top: 0, behavior: 'smooth' }); + } else if (result.modifiedSectionNumber) { + const secEl = document.getElementById(`section-${result.modifiedSectionNumber}`); + if (secEl) { + secEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } + + const aiMsg: ChatMessage = { + id: `msg-${Date.now() + 1}`, + sender: 'ai', + text: result.aiResponse, + timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + modifiedSectionNumber: result.modifiedSectionNumber, + }; + + setMessages(prev => [...prev, aiMsg]); + setIsTyping(false); + }, 400); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.nativeEvent.isComposing) { + e.preventDefault(); + handleSendMessage(); + } + }; + + if (!isOpen) return null; + + return ( + + ); +}; diff --git a/src/components/ApprovalModal.tsx b/src/components/ApprovalModal.tsx new file mode 100644 index 0000000..2754e2c --- /dev/null +++ b/src/components/ApprovalModal.tsx @@ -0,0 +1,353 @@ +import React, { useState } from 'react'; +import { BusinessProfile, WebsiteSection, ThemeId, LeadSubmission, ColorMode } from '../types'; +import { + CheckCircle2, + X, + Send, + Download, + Mail, + Globe, + MapPin, + Sparkles, + ShieldCheck, + Check, + Building, + ArrowRight +} from 'lucide-react'; + +interface ApprovalModalProps { + isOpen: boolean; + onClose: () => void; + profile: BusinessProfile; + sections: WebsiteSection[]; + theme: ThemeId; + colorMode?: ColorMode; + onSubmissionSuccess: (lead: LeadSubmission) => void; +} + +export const ApprovalModal: React.FC = ({ + isOpen, + onClose, + profile, + sections, + theme, + colorMode, + onSubmissionSuccess, +}) => { + const [clientName, setClientName] = useState(''); + const [clientEmail, setClientEmail] = useState(profile.email || ''); + const [clientPhone, setClientPhone] = useState(profile.phone || ''); + const [domainPreference, setDomainPreference] = useState(''); + const [notes, setNotes] = useState(''); + const [wantsGmb, setWantsGmb] = useState(true); + const [wantsMailcowEmail, setWantsMailcowEmail] = useState(true); + const [wantsCrm, setWantsCrm] = useState(true); + const [isSubmitted, setIsSubmitted] = useState(false); + const [submittedLead, setSubmittedLead] = useState(null); + + if (!isOpen) return null; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + const newLead: LeadSubmission = { + id: `lead-${Date.now()}`, + createdAt: new Date().toISOString(), + businessProfile: profile, + clientContact: { + name: clientName, + email: clientEmail, + phone: clientPhone, + domainPreference, + notes, + wantsGmb, + wantsMailcowEmail, + wantsCrm, + }, + sections, + theme, + colorMode: colorMode || profile.colorMode || 'dark', + status: 'pending_review', + }; + + // Save to localStorage repository + try { + const existing = localStorage.getItem('site_builder_leads'); + const leadsList: LeadSubmission[] = existing ? JSON.parse(existing) : []; + leadsList.unshift(newLead); + localStorage.setItem('site_builder_leads', JSON.stringify(leadsList)); + } catch (err) { + console.error('Error saving lead locally:', err); + } + + setSubmittedLead(newLead); + setIsSubmitted(true); + onSubmissionSuccess(newLead); + }; + + const handleDownloadBlueprint = () => { + if (!submittedLead) return; + const blob = new Blob([JSON.stringify(submittedLead, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${profile.companyName.toLowerCase().replace(/[^a-z0-9]/g, '-')}-site-blueprint.json`; + a.click(); + URL.revokeObjectURL(url); + }; + + return ( +
+
+ {/* Modal Header */} +
+
+
+ +
+
+

Approve & Launch My Site

+

+ Step 2: Submit your draft to lock in custom domain, Mailcow email, GMB & CRM setup +

+
+
+ +
+ + {/* Modal Content */} +
+ {isSubmitted ? ( + /* SUCCESS CONFIRMATION STATE */ +
+
+ +
+ +

+ Draft Approved & Successfully Submitted! +

+ +

+ Your complete homepage architecture for {profile.companyName} has been transmitted to our engineering team at hello@aipilots.site. +

+ + {/* Agency Launch Pipeline Box */} +
+
+ Your Deployment Roadmap: +
+
+
1
+
+

Custom Domain & Cloudflare DNS

+

Securing {domainPreference || `${profile.companyName.toLowerCase().replace(/\s+/g, '')}.com`} with SSL and edge caching.

+
+
+
+
2
+
+

Professional Mailcow Business Email

+

Setting up verified SPF, DKIM, DMARC inboxes for your domain.

+
+
+
+
3
+
+

Google Business Profile (GMB) Optimization

+

Connecting your address citation and Google Map Pack ranking strategy.

+
+
+
+
4
+
+

CRM & Lead Notification Routing

+

Hooking all quote forms directly to your phone ({clientPhone}) and email.

+
+
+
+ +
+ + +
+
+ ) : ( + /* SUBMISSION FORM */ +
+
+
+
+ {profile.companyName || 'Your Business'} +
+ + Theme: {theme} • {colorMode === 'light' ? '☀️ Light' : '🌙 Dark'} + +
+
+ {profile.services.length} services configured • {profile.city}, {profile.state} • Phone: {profile.phone} +
+
+ +
+ {/* Contact Name */} +
+ + setClientName(e.target.value)} + required + autoComplete="name" + /> +
+ + {/* Contact Email */} +
+ + setClientEmail(e.target.value)} + required + autoComplete="email" + /> +
+ + {/* Contact Phone */} +
+ + setClientPhone(e.target.value)} + required + autoComplete="tel" + /> +
+ + {/* Domain Preference */} +
+ + setDomainPreference(e.target.value)} + /> +
+ + {/* Onboarding Checklist Options */} +
+
+ Select What You Want AI Pilots to Provision For You: +
+ +
+ + + + + +
+
+ + {/* Notes */} +
+ + +
+
+ +
+
+
+ )} +
+ + )} + + {/* SECTION 6: CTA BANNER */} + {section.type === 'cta-banner' && ( +
+
+

{content.bannerTitle}

+

{content.bannerDescription}

+ + + {content.bannerCta || `Call: ${profile.phone}`} + +
+
+ )} + + {/* SECTION 7: FOOTER */} + {section.type === 'footer' && ( +
+
+
+
+
+ {renderEnterpriseSvgIcon(profile.industry || profile.companyName || 'brand', 22)} +
+ {profile.companyName} +
+

+ {profile.tagline}. Dedicated to providing premier services throughout {profile.city || 'the region'} with licensed technicians and ironclad warranties. +

+
+
+ + {profile.phone} +
+ {profile.address && ( +
+ + {profile.address}, {profile.city}, {profile.state} {profile.zip} +
+ )} +
+ + {content.hours || 'Mon-Sat: 7:00 AM - 6:30 PM'} +
+
+
+ +
+

Navigation

+
    + {content.links?.map((lnk, idx) => ( +
  • {lnk.label}
  • + ))} +
+
+ +
+

Services Offered

+
    + {profile.services.slice(0, 5).map((s, idx) => ( +
  • {s}
  • + ))} +
+
+
+ +
+
{content.copyrightText}
+
+ Site Architecture by + AI Pilots Site Engine +
+
+
+ )} +
+ ); +}; diff --git a/src/components/SiteHeader.tsx b/src/components/SiteHeader.tsx new file mode 100644 index 0000000..6a21802 --- /dev/null +++ b/src/components/SiteHeader.tsx @@ -0,0 +1,269 @@ +import React, { useState } from 'react'; +import { WebsiteSection, BusinessProfile } from '../types'; +import { + Sparkles, + Phone, + ArrowRight, + Menu, + X, + ShieldCheck, + Clock, + Star, + Award, + Edit3, + MapPin, + CheckCircle +} from 'lucide-react'; +import { renderEnterpriseSvgIcon } from './EnterpriseSvgIcons'; + +interface SiteHeaderProps { + section: WebsiteSection; + profile: BusinessProfile; + viewportMode: 'desktop' | 'tablet' | 'mobile'; + onQuickAiEdit: (sectionNumber: number) => void; +} + +export const SiteHeader: React.FC = ({ + section, + profile, + viewportMode, + onQuickAiEdit, +}) => { + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const { content, sectionNumber } = section; + + const variant = content.headerVariant || 'emergency-trade'; + const showTopBar = content.showTopBar !== false; + const ctaText = content.headerCtaText || (variant === 'emergency-trade' ? 'Emergency Dispatch' : 'Get Free Quote'); + + const navLinks = content.navLinks && content.navLinks.length > 0 + ? content.navLinks + : [ + { label: 'Services', href: '#services' }, + { label: 'Why Us', href: '#why-us' }, + { label: 'Reviews', href: '#reviews' }, + { label: 'Free Estimate', href: '#quote' }, + ]; + + const cityState = profile.city && profile.state + ? `${profile.city}, ${profile.state}` + : (profile.city || 'Greater Area'); + + const defaultTopAnnouncement = variant === 'modern-clean' + ? 'Rated 5.0 Stars by Over 1,200+ Satisfied Homeowners • 100% Satisfaction Guarantee' + : variant === 'commercial-elite' + ? 'Certified Master Commercial & Residential Contractors • Licensed & Bonded' + : '24/7 Rapid Emergency Response Available • Guaranteed On-Time Arrival'; + + const announcement = content.topBarAnnouncement || defaultTopAnnouncement; + const licenseBadge = content.licenseBadge || 'CA Lic #948201 • Fully Insured'; + const serviceArea = content.serviceAreaText || `Serving ${cityState} & Nearby`; + + const handleNavClick = (href: string) => { + setMobileMenuOpen(false); + const targetEl = document.querySelector(href); + if (targetEl) { + targetEl.scrollIntoView({ behavior: 'smooth' }); + } + }; + + return ( +
+ {/* Floating Section Indicator Badge */} +
+ SECTION 0 + Header & Navigation + +
+ +
+ {/* 1. TOP EMERGENCY & TRUST BAR */} + {showTopBar && ( +
+
+ {/* Left announcement / live pulse */} +
+ {variant === 'emergency-trade' ? ( +
+ + {announcement} +
+ ) : variant === 'modern-clean' ? ( +
+ + {announcement} +
+ ) : ( +
+ + {announcement} +
+ )} +
+ + {/* Right trust badges (desktop & tablet) */} +
+ + + {licenseBadge} + + + + + {serviceArea} + +
+
+
+ )} + + {/* 2. MAIN NAVIGATION BAR */} +
+
+ {/* Brand Logo & Tagline */} + + + {/* Desktop Navigation Links */} + + + {/* Action CTAs */} +
+ {/* Phone CTA */} + {profile.phone && ( + +
+ +
+
+ Speak With Tech + {profile.phone} +
+
+ )} + + {/* Primary Quote CTA Button */} + { e.preventDefault(); handleNavClick('#quote'); }} + > + {ctaText} + + + + {/* Mobile / iPad Hamburger Menu Button */} + +
+
+
+ + {/* 3. INTERACTIVE SLIDE-DOWN MOBILE & TABLET MENU DRAWER */} + {mobileMenuOpen && ( +
+
+
+
+ + {profile.companyName || 'Apex Business'} +
+ +
+ + {/* Mobile Navigation Links */} + + + {/* Mobile Click-to-Call Direct Banner */} + {profile.phone && ( + +
+ +
+
+
Call For Immediate Service
+
{profile.phone}
+
+ +
+ )} + + {/* Mobile Full-Width Quote Button */} + { e.preventDefault(); handleNavClick('#quote'); }} + > + {ctaText} + + + + {/* Mobile Trust Footer */} +
+
+ + {licenseBadge} +
+
+ + 24/7 Dispatch • Free Estimates +
+
+
+
+ )} +
+
+ ); +}; diff --git a/src/components/WebsitePreview.tsx b/src/components/WebsitePreview.tsx new file mode 100644 index 0000000..146d417 --- /dev/null +++ b/src/components/WebsitePreview.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { WebsiteSection, BusinessProfile, SectionContent } from '../types'; +import { SectionRenderer } from './SectionRenderer'; +import { SiteHeader } from './SiteHeader'; +import { Phone, Zap } from 'lucide-react'; + +interface WebsitePreviewProps { + sections: WebsiteSection[]; + profile: BusinessProfile; + viewportMode: 'desktop' | 'tablet' | 'mobile'; + onQuickAiEdit: (sectionNumber: number) => void; + onUpdateSectionContent?: (sectionNumber: number, newContent: Partial) => void; +} + +export const WebsitePreview: React.FC = ({ + sections, + profile, + viewportMode, + onQuickAiEdit, + onUpdateSectionContent, +}) => { + // Find Section 0 (Header) or fallback to default + const headerSection: WebsiteSection = sections.find( + (s) => s.type === 'header' || s.sectionNumber === 0 + ) || { + id: 'sec-header', + sectionNumber: 0, + type: 'header', + title: 'Header & Navigation', + badge: 'Standard Header', + visible: true, + content: { + headerVariant: 'emergency-trade', + showTopBar: true, + headerCtaText: 'Emergency Dispatch', + }, + }; + + // Main sections (excluding Section 0) + const mainSections = sections.filter( + (s) => s.type !== 'header' && s.sectionNumber !== 0 + ); + + return ( +
+ {viewportMode === 'mobile' &&
} + +
+ {/* Preformatted Responsive Header & Navigation (Section 0) */} + {headerSection.visible && ( + + )} + + {/* Dynamic Sections Stack */} +
+ {mainSections.map((section) => ( + section.visible && ( + + ) + ))} +
+ + {/* Sticky Mobile Lead & Call Action Bar */} + {viewportMode === 'mobile' && ( + + )} +
+
+ ); +}; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..5e69687 --- /dev/null +++ b/src/index.css @@ -0,0 +1,3254 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap'); + +/* ========================================================================== + RESET & CORE TOKENS + ========================================================================== */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + /* Default Theme Tokens (Titan Navy) */ + --primary: #0284c7; + --primary-hover: #0369a1; + --accent: #38bdf8; + --accent-glow: rgba(56, 189, 248, 0.25); + --bg-main: #070c18; + --bg-surface: #0e172a; + --bg-card: rgba(17, 30, 64, 0.7); + --bg-card-hover: rgba(23, 41, 88, 0.85); + --border-subtle: rgba(255, 255, 255, 0.08); + --border-focus: rgba(56, 189, 248, 0.5); + --text-primary: #f8fafc; + --text-secondary: #94a3b8; + --text-muted: #64748b; + --font-heading: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif; + --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + + /* App Layout Dimensions */ + --topbar-height: 68px; + --radius-sm: 8px; + --radius-md: 14px; + --radius-lg: 22px; + --radius-xl: 32px; + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.25); + --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.35); + --shadow-glow: 0 0 30px var(--accent-glow); +} + +/* Theme Override: Obsidian & Gold */ +[data-theme='obsidian-gold'] { + --primary: #d97706; + --primary-hover: #b45309; + --accent: #fbbf24; + --accent-glow: rgba(251, 191, 36, 0.2); + --bg-main: #0b0b0d; + --bg-surface: #141312; + --bg-card: rgba(26, 24, 21, 0.75); + --bg-card-hover: rgba(36, 32, 27, 0.9); + --border-subtle: rgba(251, 191, 36, 0.12); + --border-focus: rgba(251, 191, 36, 0.5); +} + +/* Theme Override: Emerald Slate */ +[data-theme='emerald-slate'] { + --primary: #059669; + --primary-hover: #047857; + --accent: #34d399; + --accent-glow: rgba(52, 211, 153, 0.2); + --bg-main: #04120e; + --bg-surface: #0a1f18; + --bg-card: rgba(12, 38, 30, 0.75); + --bg-card-hover: rgba(18, 54, 43, 0.9); + --border-subtle: rgba(52, 211, 153, 0.12); + --border-focus: rgba(52, 211, 153, 0.5); +} + +/* Theme Override: Crimson Forge */ +[data-theme='crimson-forge'] { + --primary: #dc2626; + --primary-hover: #b91c1c; + --accent: #f87171; + --accent-glow: rgba(248, 113, 113, 0.25); + --bg-main: #120707; + --bg-surface: #1d0f0f; + --bg-card: rgba(38, 18, 18, 0.75); + --bg-card-hover: rgba(54, 24, 24, 0.9); + --border-subtle: rgba(248, 113, 113, 0.14); + --border-focus: rgba(248, 113, 113, 0.5); +} + +/* Theme Override: Alpine Clean */ +[data-theme='alpine-clean'] { + --primary: #4f46e5; + --primary-hover: #4338ca; + --accent: #818cf8; + --accent-glow: rgba(129, 140, 248, 0.25); + --bg-main: #070d1a; + --bg-surface: #0d172e; + --bg-card: rgba(19, 31, 56, 0.75); + --bg-card-hover: rgba(29, 45, 80, 0.9); + --border-subtle: rgba(129, 140, 248, 0.12); + --border-focus: rgba(129, 140, 248, 0.5); +} + +/* Theme Override: Electric Violet */ +[data-theme='electric-violet'] { + --primary: #9333ea; + --primary-hover: #7e22ce; + --accent: #c084fc; + --accent-glow: rgba(192, 132, 252, 0.25); + --bg-main: #0b0616; + --bg-surface: #140c24; + --bg-card: rgba(23, 13, 44, 0.75); + --bg-card-hover: rgba(35, 20, 66, 0.9); + --border-subtle: rgba(192, 132, 252, 0.15); + --border-focus: rgba(192, 132, 252, 0.5); +} + +/* Theme Override: Sunset Amber */ +[data-theme='sunset-amber'] { + --primary: #ea580c; + --primary-hover: #c2410c; + --accent: #fb923c; + --accent-glow: rgba(251, 146, 60, 0.25); + --bg-main: #120904; + --bg-surface: #1a0f08; + --bg-card: rgba(33, 18, 8, 0.75); + --bg-card-hover: rgba(50, 27, 12, 0.9); + --border-subtle: rgba(251, 146, 60, 0.15); + --border-focus: rgba(251, 146, 60, 0.5); +} + +/* Theme Override: Cyber Cyan */ +[data-theme='cyber-cyan'] { + --primary: #0d9488; + --primary-hover: #0f766e; + --accent: #2dd4bf; + --accent-glow: rgba(45, 212, 191, 0.25); + --bg-main: #041012; + --bg-surface: #07191c; + --bg-card: rgba(9, 33, 38, 0.75); + --bg-card-hover: rgba(14, 49, 56, 0.9); + --border-subtle: rgba(45, 212, 191, 0.15); + --border-focus: rgba(45, 212, 191, 0.5); +} + +/* Theme Override: Neon Rose */ +[data-theme='neon-rose'] { + --primary: #e11d48; + --primary-hover: #be123c; + --accent: #fb7185; + --accent-glow: rgba(251, 113, 133, 0.25); + --bg-main: #14050a; + --bg-surface: #1f0810; + --bg-card: rgba(36, 10, 19, 0.75); + --bg-card-hover: rgba(54, 15, 29, 0.9); + --border-subtle: rgba(251, 113, 133, 0.15); + --border-focus: rgba(251, 113, 133, 0.5); +} + +/* Theme Override: Custom Dynamic */ +[data-theme='custom-dynamic'] { + --primary: var(--dynamic-primary, #6366f1); + --primary-hover: var(--dynamic-primary-hover, #4f46e5); + --accent: var(--dynamic-accent, #a5b4fc); + --accent-glow: var(--dynamic-accent-glow, rgba(165, 180, 252, 0.25)); + --bg-main: var(--dynamic-bg-main, #0b0f19); + --bg-surface: var(--dynamic-bg-surface, #111726); + --bg-card: var(--dynamic-bg-card, rgba(19, 27, 46, 0.75)); + --bg-card-hover: var(--dynamic-bg-card-hover, rgba(30, 42, 71, 0.9)); + --border-subtle: var(--dynamic-border-subtle, rgba(165, 180, 252, 0.15)); + --border-focus: var(--dynamic-border-focus, rgba(165, 180, 252, 0.5)); +} + +/* ========================================================================== + COLOR MODE SYSTEM (DARK & LIGHT) + ========================================================================== */ + +/* Light Mode Base Design Tokens */ +[data-color-mode='light'] { + --bg-main: #f1f5f9; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f8fafc; + --border-subtle: rgba(15, 23, 42, 0.08); + --border-focus: rgba(2, 132, 199, 0.45); + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #64748b; + --shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06); + --shadow-md: 0 10px 25px -5px rgba(15, 23, 42, 0.08), 0 8px 10px -6px rgba(15, 23, 42, 0.04); + --shadow-glow: 0 0 25px rgba(2, 132, 199, 0.12); +} + +/* Light Mode Theme Overrides */ +[data-color-mode='light'][data-theme='titan-navy'] { + --primary: #0284c7; + --primary-hover: #0369a1; + --accent: #0284c7; + --accent-glow: rgba(2, 132, 199, 0.18); + --bg-main: #f0f9ff; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f8fafc; + --border-subtle: rgba(2, 132, 199, 0.12); + --border-focus: rgba(2, 132, 199, 0.4); +} + +[data-color-mode='light'][data-theme='obsidian-gold'] { + --primary: #b45309; + --primary-hover: #92400e; + --accent: #d97706; + --accent-glow: rgba(217, 119, 6, 0.18); + --bg-main: #fbfbf9; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #fdfdfc; + --border-subtle: rgba(180, 83, 9, 0.14); + --border-focus: rgba(217, 119, 6, 0.4); +} + +[data-color-mode='light'][data-theme='emerald-slate'] { + --primary: #059669; + --primary-hover: #047857; + --accent: #059669; + --accent-glow: rgba(5, 150, 105, 0.18); + --bg-main: #f0fdf4; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f7fee7; + --border-subtle: rgba(5, 150, 105, 0.14); + --border-focus: rgba(16, 185, 129, 0.4); +} + +[data-color-mode='light'][data-theme='crimson-forge'] { + --primary: #dc2626; + --primary-hover: #b91c1c; + --accent: #dc2626; + --accent-glow: rgba(220, 38, 38, 0.18); + --bg-main: #fef2f2; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #fff5f5; + --border-subtle: rgba(220, 38, 38, 0.14); + --border-focus: rgba(239, 68, 68, 0.4); +} + +[data-color-mode='light'][data-theme='alpine-clean'] { + --primary: #4338ca; + --primary-hover: #3730a3; + --accent: #4f46e5; + --accent-glow: rgba(79, 70, 229, 0.18); + --bg-main: #f8fafc; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f1f5f9; + --border-subtle: rgba(79, 70, 229, 0.12); + --border-focus: rgba(99, 102, 241, 0.4); +} + +[data-color-mode='light'][data-theme='electric-violet'] { + --primary: #7e22ce; + --primary-hover: #6b21a8; + --accent: #9333ea; + --accent-glow: rgba(147, 51, 234, 0.18); + --bg-main: #faf5ff; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f5f3ff; + --border-subtle: rgba(126, 34, 206, 0.14); + --border-focus: rgba(168, 85, 247, 0.4); +} + +[data-color-mode='light'][data-theme='sunset-amber'] { + --primary: #c2410c; + --primary-hover: #9a3412; + --accent: #ea580c; + --accent-glow: rgba(234, 88, 12, 0.18); + --bg-main: #fff7ed; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #ffedd5; + --border-subtle: rgba(194, 65, 12, 0.14); + --border-focus: rgba(234, 88, 12, 0.4); +} + +[data-color-mode='light'][data-theme='cyber-cyan'] { + --primary: #0f766e; + --primary-hover: #115e59; + --accent: #0d9488; + --accent-glow: rgba(13, 148, 136, 0.18); + --bg-main: #f0fdfa; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #ccfbf1; + --border-subtle: rgba(15, 118, 110, 0.14); + --border-focus: rgba(20, 184, 166, 0.4); +} + +[data-color-mode='light'][data-theme='neon-rose'] { + --primary: #be123c; + --primary-hover: #9f1239; + --accent: #e11d48; + --accent-glow: rgba(225, 29, 72, 0.18); + --bg-main: #fff1f2; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #ffe4e6; + --border-subtle: rgba(190, 18, 60, 0.14); + --border-focus: rgba(225, 29, 72, 0.4); +} + +[data-color-mode='light'][data-theme='custom-dynamic'] { + --bg-main: #f8fafc; + --bg-surface: #ffffff; + --bg-card: #ffffff; + --border-subtle: rgba(15, 23, 42, 0.08); +} + +/* Controls: Segmented Color Mode Control in Onboarding & Modals */ +.color-mode-segmented { + display: inline-flex; + align-items: center; + background: rgba(11, 19, 41, 0.7); + border: 1px solid var(--border-subtle); + border-radius: 9999px; + padding: 4px; + gap: 4px; +} + +.color-mode-toggle-btn { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.42rem 0.95rem; + border-radius: 9999px; + border: 1px solid transparent; + background: transparent; + color: var(--text-secondary); + font-family: var(--font-heading); + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); +} + +.color-mode-toggle-btn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.06); +} + +.color-mode-toggle-btn.active { + background: var(--primary); + color: #ffffff; + border-color: var(--accent); + box-shadow: 0 2px 10px var(--accent-glow); +} + +/* Controls: Topbar Color Mode Toggle Button */ +.topbar-mode-toggle-btn { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.48rem 0.85rem; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-sm); + color: var(--text-primary); + font-family: var(--font-body); + font-size: 0.82rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + +.topbar-mode-toggle-btn:hover { + background: rgba(255, 255, 255, 0.1); + border-color: var(--accent); + color: #ffffff; + transform: translateY(-1px); +} + +/* Canvas & Section Overrides in Light Mode */ +[data-color-mode='light'] .site-canvas { + background: #ffffff; + border: 1px solid rgba(15, 23, 42, 0.1); + box-shadow: 0 20px 40px -15px rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .site-topbar { + background: #f8fafc; + border-bottom: 1px solid rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .topbar-text { + color: #334155; +} + +[data-color-mode='light'] .topbar-badge { + color: #64748b; +} + +[data-color-mode='light'] .topbar-separator { + color: rgba(15, 23, 42, 0.15); +} + +[data-color-mode='light'] .site-main-nav { + background: rgba(255, 255, 255, 0.98); + border-bottom: 1px solid rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .logo-company-name { + color: #0f172a; +} + +[data-color-mode='light'] .nav-link-item a { + color: #334155; +} + +[data-color-mode='light'] .nav-link-item a:hover { + color: var(--primary); +} + +[data-color-mode='light'] .nav-phone-button { + background: rgba(15, 23, 42, 0.04); + border-color: rgba(15, 23, 42, 0.1); +} + +[data-color-mode='light'] .phone-number { + color: #0f172a; +} + +[data-color-mode='light'] .mobile-hamburger-btn { + background: rgba(15, 23, 42, 0.05); + border-color: rgba(15, 23, 42, 0.1); + color: #0f172a; +} + +[data-color-mode='light'] .mobile-nav-drawer-overlay { + background: rgba(255, 255, 255, 0.98); + border-bottom-color: var(--primary); + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.15); +} + +[data-color-mode='light'] .drawer-brand { + color: #0f172a; +} + +[data-color-mode='light'] .mobile-nav-links a { + color: #334155; +} + +[data-color-mode='light'] .mobile-nav-links a:hover { + background: rgba(15, 23, 42, 0.04); + color: var(--primary); +} + +[data-color-mode='light'] .mobile-drawer-call-card { + background: rgba(34, 197, 94, 0.08); + border-color: rgba(34, 197, 94, 0.25); +} + +[data-color-mode='light'] .drawer-call-number { + color: #0f172a; +} + +[data-color-mode='light'] .drawer-call-title { + color: #15803d; +} + +[data-color-mode='light'] .hero-section { + background: radial-gradient(circle at top right, var(--accent-glow), transparent 50%), + linear-gradient(180deg, #ffffff 0%, var(--bg-main) 100%); +} + +[data-color-mode='light'] .trust-pill { + background: rgba(255, 255, 255, 0.9); + border-color: rgba(15, 23, 42, 0.1); + color: var(--primary); + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.05); +} + +[data-color-mode='light'] .hero-headline { + color: #0f172a; +} + +[data-color-mode='light'] .hero-subheadline { + color: #475569; +} + +[data-color-mode='light'] .hero-guarantee-row { + color: #64748b; + border-top-color: rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .hero-image-overlay-badge { + background: rgba(255, 255, 255, 0.92); + border-color: rgba(15, 23, 42, 0.1); + color: #0f172a; + box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .services-section { + background: var(--bg-main); +} + +[data-color-mode='light'] .section-title { + color: #0f172a; +} + +[data-color-mode='light'] .section-description { + color: #475569; +} + +[data-color-mode='light'] .service-card { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.05); +} + +[data-color-mode='light'] .service-card:hover { + background: #ffffff; + border-color: var(--accent); + box-shadow: 0 12px 30px -4px rgba(15, 23, 42, 0.1), 0 0 15px var(--accent-glow); +} + +[data-color-mode='light'] .service-card-title { + color: #0f172a; +} + +[data-color-mode='light'] .service-card-desc { + color: #475569; +} + +[data-color-mode='light'] .service-icon-box { + background: radial-gradient(circle at 35% 30%, var(--accent-glow) 0%, rgba(0, 0, 0, 0.02) 100%), #f8fafc; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 12px rgba(15, 23, 42, 0.05); +} + +[data-color-mode='light'] .why-us-section { + background: radial-gradient(circle at bottom center, var(--accent-glow), transparent 60%), #f8fafc; + border-top-color: rgba(15, 23, 42, 0.08); + border-bottom-color: rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .stat-card { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 15px rgba(15, 23, 42, 0.04); +} + +[data-color-mode='light'] .stat-label { + color: #0f172a; +} + +[data-color-mode='light'] .stat-subtext { + color: #64748b; +} + +[data-color-mode='light'] .pillar-item { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 15px rgba(15, 23, 42, 0.04); +} + +[data-color-mode='light'] .pillar-icon-box { + background: #f8fafc; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 12px rgba(15, 23, 42, 0.05); +} + +[data-color-mode='light'] .pillar-title { + color: #0f172a; +} + +[data-color-mode='light'] .pillar-desc { + color: #475569; +} + +[data-color-mode='light'] .testimonials-section { + background: #ffffff; +} + +[data-color-mode='light'] .review-card { + background: #f8fafc; + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 4px 15px rgba(15, 23, 42, 0.04); +} + +[data-color-mode='light'] .review-quote { + color: #1e293b; +} + +[data-color-mode='light'] .author-name { + color: #0f172a; +} + +[data-color-mode='light'] .author-location { + color: #64748b; +} + +[data-color-mode='light'] .review-author-row { + border-top-color: rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .quote-section { + background: radial-gradient(circle at top left, var(--accent-glow), transparent 60%), #f8fafc; +} + +[data-color-mode='light'] .quote-box { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.1); + box-shadow: 0 20px 40px -10px rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .quote-box .form-input, +[data-color-mode='light'] .quote-box .form-select, +[data-color-mode='light'] .quote-box .form-textarea { + background: #f8fafc; + border-color: rgba(15, 23, 42, 0.14); + color: #0f172a; +} + +[data-color-mode='light'] .quote-box .form-input:focus, +[data-color-mode='light'] .quote-box .form-select:focus, +[data-color-mode='light'] .quote-box .form-textarea:focus { + background: #ffffff; + border-color: var(--primary); +} + +[data-color-mode='light'] .quote-box .form-label { + color: #0f172a; +} + +[data-color-mode='light'] .cta-banner-section { + background: linear-gradient(135deg, #ffffff, #f8fafc); + border-top-color: rgba(15, 23, 42, 0.08); + border-bottom-color: rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .cta-banner-box { + background: linear-gradient(135deg, rgba(2, 132, 199, 0.08), rgba(56, 189, 248, 0.02)); + border-color: rgba(2, 132, 199, 0.25); + box-shadow: 0 15px 35px -5px rgba(2, 132, 199, 0.08); +} + +[data-color-mode='light'] .cta-banner-title { + color: #0f172a; +} + +[data-color-mode='light'] .cta-banner-desc { + color: #475569; +} + +[data-color-mode='light'] .section-indicator-bar { + background: rgba(255, 255, 255, 0.92); + border-color: rgba(15, 23, 42, 0.12); + box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .section-indicator-label { + color: #334155; +} + +[data-color-mode='light'] .inline-edit-hint { + background: rgba(15, 23, 42, 0.05); + color: #475569; +} + +[data-color-mode='light'] .mobile-sticky-action-bar { + background: rgba(255, 255, 255, 0.95); + border-top-color: rgba(15, 23, 42, 0.1); + box-shadow: 0 -4px 20px rgba(15, 23, 42, 0.08); +} + +[data-color-mode='light'] .canvas-area { + background: radial-gradient(circle at top center, rgba(226, 232, 240, 0.7), transparent 70%), #cbd5e1; +} + +html, body { + background-color: var(--bg-main); + color: var(--text-primary); + font-family: var(--font-body); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + min-height: 100vh; + overflow-x: hidden; +} + +/* ========================================================================== + CUSTOM SCROLLBAR + ========================================================================== */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.2); +} +::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.15); + border-radius: 4px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--accent); +} + +/* ========================================================================== + APP HEADER & CONTROLS + ========================================================================== */ +.app-header, +.app-topbar { + position: sticky; + top: 0; + z-index: 50; + height: var(--topbar-height); + min-height: var(--topbar-height); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 1.25rem; + background: rgba(11, 19, 41, 0.98); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-bottom: 1px solid var(--border-subtle); + gap: 0.75rem; + overflow: hidden; +} + +.app-brand-area, +.brand-badge { + display: flex; + align-items: center; + gap: 0.65rem; + flex-shrink: 0; + min-width: max-content; +} + +.brand-text-col { + display: flex; + flex-direction: column; + justify-content: center; + min-width: 0; + line-height: 1.25; +} + +.brand-logo-icon { + width: 36px; + height: 36px; + border-radius: 9px; + background: linear-gradient(135deg, var(--primary), var(--accent)); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + box-shadow: 0 4px 14px var(--accent-glow); + flex-shrink: 0; +} + +.app-title, +.brand-title { + font-family: var(--font-heading); + font-weight: 700; + font-size: 0.95rem; + letter-spacing: -0.01em; + color: #fff; + white-space: nowrap; +} + +.app-subtitle, +.brand-subtitle { + font-size: 0.72rem; + color: var(--text-secondary); + white-space: nowrap; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; +} + +.viewport-selector, +.viewport-switcher { + display: flex; + align-items: center; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-subtle); + border-radius: 9999px; + padding: 3px; + gap: 2px; + flex-shrink: 0; +} + +.viewport-btn { + display: flex; + align-items: center; + gap: 0.35rem; + background: transparent; + border: none; + color: var(--text-secondary); + font-size: 0.78rem; + font-weight: 500; + padding: 5px 10px; + border-radius: 9999px; + cursor: pointer; + transition: all 0.15s ease; + white-space: nowrap; +} + +.viewport-btn:hover { + color: var(--text-primary); +} + +.viewport-btn.active { + background: var(--primary); + color: #ffffff; + box-shadow: 0 2px 10px var(--accent-glow); +} + +.topbar-actions { + display: flex; + align-items: center; + gap: 0.45rem; + flex-shrink: 0; +} + +.topbar-actions .btn { + padding: 0.45rem 0.85rem; + font-size: 0.82rem; + min-height: 38px; + white-space: nowrap; + flex-shrink: 0; +} + +/* ========================================================================== + BUTTONS & CONTROLS + ========================================================================== */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + font-family: var(--font-heading); + font-size: 0.9rem; + font-weight: 600; + padding: 0.65rem 1.25rem; + min-height: 44px; + border-radius: var(--radius-md); + cursor: pointer; + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + border: 1px solid transparent; + text-decoration: none; +} + +.btn-primary { + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #ffffff; + box-shadow: 0 4px 16px var(--accent-glow); +} +.btn-primary:hover { + transform: translateY(-1px); + filter: brightness(1.1); + box-shadow: 0 6px 20px var(--accent-glow); +} + +.btn-secondary { + background: rgba(255, 255, 255, 0.06); + border-color: var(--border-subtle); + color: var(--text-primary); +} +.btn-secondary:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.2); +} + +.btn-accent { + background: var(--accent); + color: #070c18; + font-weight: 700; +} +.btn-accent:hover { + filter: brightness(1.08); +} + +.btn-outline-accent { + background: transparent; + border: 1px solid var(--accent); + color: var(--accent); +} +.btn-outline-accent:hover { + background: rgba(56, 189, 248, 0.1); +} + +/* ========================================================================== + WORKSPACE LAYOUT + ========================================================================== */ +.app-container { + display: flex; + height: calc(100vh - var(--topbar-height)); + position: relative; + overflow: hidden; +} + +.canvas-area { + flex: 1; + overflow-y: auto; + padding: 2rem 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + background: radial-gradient(circle at top center, rgba(30, 41, 59, 0.35), transparent 70%), var(--bg-main); +} + +/* Device Viewport Frames */ +.site-frame-container { + width: 100%; + transition: max-width 0.35s cubic-bezier(0.16, 1, 0.3, 1); +} + +.site-frame-container.viewport-desktop { + max-width: 1280px; +} + +.site-frame-container.viewport-tablet { + max-width: 768px; + border: 12px solid #1f293d; + border-radius: 36px; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.7); + overflow: hidden; +} + +.site-frame-container.viewport-mobile { + max-width: 400px; + border: 14px solid #1f293d; + border-radius: 46px; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.8); + overflow: hidden; + position: relative; +} + +/* Mobile Dynamic Island */ +.viewport-mobile .mobile-notch { + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + width: 110px; + height: 24px; + background: #000; + border-radius: 20px; + z-index: 100; + box-shadow: 0 2px 6px rgba(0,0,0,0.5); +} + +/* Website Canvas Body */ +.site-canvas { + background: var(--bg-surface); + border-radius: var(--radius-lg); + border: 1px solid var(--border-subtle); + box-shadow: var(--shadow-md); + overflow: hidden; + position: relative; + container-type: inline-size; + container-name: canvas-body; +} + +/* ========================================================================== + HOMEPAGE SECTION STYLING & NUMBERED BADGES + ========================================================================== */ +.site-section-wrapper { + position: relative; + transition: all 0.25s ease; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +.site-section-wrapper:hover { + border-top-color: var(--border-focus); + border-bottom-color: var(--border-focus); +} + +.section-indicator-bar { + position: absolute; + top: 10px; + right: 14px; + z-index: 30; + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(11, 19, 41, 0.95); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--border-subtle); + border-radius: 9999px; + padding: 4px 10px 4px 6px; + box-shadow: 0 4px 14px rgba(0,0,0,0.4); + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease, transform 0.2s ease; +} + +.site-section-wrapper:hover .section-indicator-bar { + opacity: 1; + pointer-events: auto; + transform: translateY(-2px); + border-color: var(--accent); +} + +.header-section-wrapper .section-indicator-bar { + top: 6px; + right: 12px; +} + +.section-num-pill { + background: var(--primary); + color: #fff; + font-family: var(--font-heading); + font-size: 0.72rem; + font-weight: 700; + padding: 3px 8px; + border-radius: 9999px; +} + +.section-indicator-label { + font-size: 0.75rem; + color: var(--text-secondary); + font-weight: 500; +} + +.section-quick-edit-btn { + background: transparent; + border: none; + color: var(--accent); + cursor: pointer; + display: flex; + align-items: center; + gap: 3px; + font-size: 0.75rem; + font-weight: 600; + padding: 2px 6px; + border-radius: 6px; +} +.section-quick-edit-btn:hover { + background: rgba(56, 189, 248, 0.15); +} + +/* ========================================================================== + HOMEPAGE COMPONENTS (Hero, Services, Why Us, Testimonials, Form, Footer) + ========================================================================== */ + +/* 0. Preformatted Responsive Header & Navigation (Section 0) */ +.header-section-wrapper { + position: sticky; + top: 0; + z-index: 40; +} + +.preformatted-site-header { + width: 100%; + background: rgba(14, 23, 42, 0.96); + backdrop-filter: blur(16px); + border-bottom: 1px solid var(--border-subtle); + position: relative; +} + +/* Variant Accents */ +.preformatted-site-header.variant-emergency-trade { + border-bottom: 1px solid rgba(239, 68, 68, 0.25); +} + +.preformatted-site-header.variant-modern-clean { + border-bottom: 1px solid rgba(56, 189, 248, 0.25); +} + +.preformatted-site-header.variant-commercial-elite { + border-bottom: 1px solid rgba(234, 179, 8, 0.25); +} + +/* 1. Top Emergency / Trust Bar */ +.site-topbar { + background: rgba(8, 14, 30, 0.98); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + padding: 0.45rem 2rem; + font-size: 0.8rem; + color: var(--text-secondary); +} + +.topbar-inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.topbar-left { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.topbar-pulse-item { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.pulse-dot-green { + width: 8px; + height: 8px; + background: #22c55e; + border-radius: 50%; + display: inline-block; + box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); + animation: pulseGreenBeacon 2s infinite cubic-bezier(0.66, 0, 0, 1); +} + +@keyframes pulseGreenBeacon { + 0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.8); } + 70% { box-shadow: 0 0 0 7px rgba(34, 197, 94, 0); } + 100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); } +} + +.topbar-text { + color: #fff; + font-weight: 500; + letter-spacing: 0.01em; +} + +.topbar-right { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 0.78rem; +} + +.topbar-badge { + display: flex; + align-items: center; + gap: 0.35rem; + color: var(--text-muted); +} + +.topbar-separator { + color: rgba(255, 255, 255, 0.2); +} + +/* 2. Main Navigation Bar */ +.site-main-nav { + padding: 0.9rem 2rem; +} + +.main-nav-inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; +} + +/* Logo & Text Block */ +.site-logo-wrap { + display: flex; + align-items: center; + flex-shrink: 0; +} + +.site-logo { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; + color: #fff; +} + +.brand-logo-icon { + width: 40px; + height: 40px; + border-radius: 10px; + background: linear-gradient(135deg, rgba(56, 189, 248, 0.2), rgba(14, 165, 233, 0.4)); + border: 1px solid var(--accent); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + box-shadow: 0 0 15px var(--accent-glow); + flex-shrink: 0; +} + +.logo-text-block { + display: flex; + flex-direction: column; +} + +.logo-company-name { + font-family: var(--font-heading); + font-weight: 800; + font-size: 1.18rem; + line-height: 1.2; + color: #fff; + letter-spacing: -0.02em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 240px; +} + +.logo-subtext { + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--accent); +} + +/* Desktop Navigation Menu */ +.desktop-nav-menu { + display: flex; + align-items: center; +} + +.site-nav-links { + display: flex; + align-items: center; + gap: 1.75rem; + list-style: none; + margin: 0; + padding: 0; +} + +.site-nav-links a { + color: var(--text-secondary); + text-decoration: none; + font-size: 0.92rem; + font-weight: 500; + transition: all 0.15s ease; + position: relative; + padding: 0.35rem 0; + white-space: nowrap; + flex-shrink: 0; +} + +.site-nav-links a:hover { + color: #fff; +} + +.site-nav-links a::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--accent); + transition: width 0.2s ease; + border-radius: 2px; +} + +.site-nav-links a:hover::after { + width: 100%; +} + +/* Nav Action Cluster */ +.site-nav-cta-cluster { + display: flex; + align-items: center; + gap: 1rem; + flex-shrink: 0; + white-space: nowrap; +} + +/* Phone CTA Button */ +.nav-phone-button { + display: flex; + align-items: center; + gap: 0.6rem; + text-decoration: none; + padding: 0.35rem 0.75rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 24px; + transition: all 0.2s ease; + white-space: nowrap; + flex-shrink: 0; +} + +.nav-phone-button:hover { + background: rgba(255, 255, 255, 0.08); + border-color: var(--accent); +} + +.phone-icon-circle { + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--accent); + color: #0f172a; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.phone-text-block { + display: flex; + flex-direction: column; + line-height: 1.15; +} + +.phone-sub-label { + font-size: 0.65rem; + text-transform: uppercase; + color: var(--text-muted); + font-weight: 600; +} + +.phone-number { + font-size: 0.88rem; + font-weight: 700; + color: #fff; + letter-spacing: 0.01em; +} + +.nav-primary-cta { + padding: 0.55rem 1.15rem; + font-size: 0.85rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 0.4rem; + border-radius: 8px; + white-space: nowrap; + flex-shrink: 0; +} + +/* Mobile Hamburger Button */ +.mobile-hamburger-btn { + display: none; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + color: #fff; + cursor: pointer; + transition: all 0.2s ease; +} + +.mobile-hamburger-btn:hover { + background: rgba(255, 255, 255, 0.12); + border-color: var(--accent); +} + +/* 3. Interactive Mobile & Tablet Drawer */ +.mobile-nav-drawer-overlay { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 100; + background: rgba(11, 19, 41, 0.98); + backdrop-filter: blur(20px); + border-bottom: 2px solid var(--accent); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.75); + animation: slideDownMenu 0.25s cubic-bezier(0.16, 1, 0.3, 1); +} + +@keyframes slideDownMenu { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +.mobile-nav-drawer { + padding: 1.25rem 1.5rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.mobile-drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 0.75rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.drawer-brand { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 700; + color: #fff; + font-size: 0.95rem; +} + +.drawer-close-btn { + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; +} + +.drawer-close-btn:hover { + color: #fff; +} + +.mobile-nav-links { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.mobile-nav-links a { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.7rem 0.85rem; + border-radius: 8px; + color: var(--text-secondary); + text-decoration: none; + font-weight: 600; + font-size: 0.95rem; + transition: all 0.15s ease; +} + +.mobile-nav-links a:hover { + background: rgba(255, 255, 255, 0.06); + color: var(--accent); +} + +.mobile-drawer-call-card { + display: flex; + align-items: center; + gap: 0.85rem; + padding: 0.85rem 1rem; + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.3); + border-radius: 10px; + text-decoration: none; + color: #fff; + transition: all 0.2s ease; +} + +.mobile-drawer-call-card:hover { + background: rgba(34, 197, 94, 0.18); + border-color: #22c55e; +} + +.drawer-call-icon { + width: 36px; + height: 36px; + border-radius: 50%; + background: #22c55e; + color: #0f172a; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.drawer-call-title { + font-size: 0.72rem; + text-transform: uppercase; + color: #86efac; + font-weight: 700; + letter-spacing: 0.04em; +} + +.drawer-call-number { + font-size: 1.05rem; + font-weight: 800; + color: #fff; +} + +.mobile-drawer-cta { + width: 100%; + justify-content: center; + padding: 0.85rem 1rem; + font-size: 0.95rem; + font-weight: 700; + border-radius: 10px; +} + +.mobile-drawer-footer { + display: flex; + flex-direction: column; + gap: 0.4rem; + padding-top: 0.75rem; + border-top: 1px solid rgba(255, 255, 255, 0.06); +} + +.drawer-trust-item { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--text-muted); +} + +/* ========================================================================== + HEADER RESPONSIVE OVERRIDES: TABLET (iPad) & MOBILE + ========================================================================== */ + +/* 1. TABLET / iPad OVERRIDES (@container max-width: 1080px or .viewport-tablet) */ +@container canvas-body (max-width: 1080px) { + .site-topbar { + padding: 0.4rem 1.25rem; + } + + .site-main-nav { + padding: 0.75rem 1.25rem; + } + + .desktop-nav-menu { + display: none; + } + + .mobile-hamburger-btn { + display: flex; + } + + .topbar-area { + display: none; + } + + .topbar-separator { + display: none; + } + + .logo-company-name { + max-width: 200px; + } + + .nav-phone-button .phone-sub-label { + display: none; + } +} + +.viewport-tablet .site-topbar { + padding: 0.4rem 1.25rem; +} + +.viewport-tablet .site-main-nav { + padding: 0.75rem 1.25rem; +} + +.viewport-tablet .desktop-nav-menu { + display: none; +} + +.viewport-tablet .mobile-hamburger-btn { + display: flex; +} + +.viewport-tablet .topbar-area { + display: none; +} + +.viewport-tablet .topbar-separator { + display: none; +} + +.viewport-tablet .logo-company-name { + max-width: 200px; +} + +.viewport-tablet .nav-phone-button .phone-sub-label { + display: none; +} + +/* 2. MOBILE OVERRIDES (@container max-width: 580px or .viewport-mobile) */ +@container canvas-body (max-width: 580px) { + .site-topbar { + padding: 0.35rem 0.85rem; + font-size: 0.75rem; + } + + .topbar-right { + display: none; + } + + .topbar-left { + width: 100%; + justify-content: center; + } + + .site-main-nav { + padding: 0.65rem 0.85rem; + } + + .desktop-nav-menu { + display: none; + } + + .nav-primary-cta { + display: none; + } + + .nav-phone-button { + padding: 0; + background: transparent; + border: none; + } + + .nav-phone-button .phone-text-block { + display: none; + } + + .phone-icon-circle { + width: 36px; + height: 36px; + box-shadow: 0 0 10px rgba(56, 189, 248, 0.4); + } + + .mobile-hamburger-btn { + display: flex; + } + + .logo-company-name { + font-size: 0.98rem; + max-width: 170px; + } + + .logo-subtext { + font-size: 0.62rem; + } + + .brand-logo-icon { + width: 34px; + height: 34px; + } +} + +.viewport-mobile .site-topbar { + padding: 0.35rem 0.85rem; + font-size: 0.75rem; + padding-top: 1.6rem; /* clear dynamic island / notch */ +} + +.viewport-mobile .topbar-right { + display: none; +} + +.viewport-mobile .topbar-left { + width: 100%; + justify-content: center; +} + +.viewport-mobile .site-main-nav { + padding: 0.65rem 0.85rem; +} + +.viewport-mobile .desktop-nav-menu { + display: none; +} + +.viewport-mobile .nav-primary-cta { + display: none; +} + +.viewport-mobile .nav-phone-button { + padding: 0; + background: transparent; + border: none; +} + +.viewport-mobile .nav-phone-button .phone-text-block { + display: none; +} + +.viewport-mobile .phone-icon-circle { + width: 36px; + height: 36px; + box-shadow: 0 0 10px rgba(56, 189, 248, 0.4); +} + +.viewport-mobile .mobile-hamburger-btn { + display: flex; +} + +.viewport-mobile .logo-company-name { + font-size: 0.98rem; + max-width: 170px; +} + +.viewport-mobile .logo-subtext { + font-size: 0.62rem; +} + +.viewport-mobile .brand-logo-icon { + width: 34px; + height: 34px; +} + +/* 2. Hero Section */ +.hero-section { + padding: 5.5rem 2.5rem 4.5rem; + position: relative; + background: radial-gradient(circle at top right, var(--accent-glow), transparent 50%), + linear-gradient(180deg, var(--bg-surface) 0%, var(--bg-main) 100%); + text-align: center; +} + +.hero-trust-pills { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.75rem; + margin-bottom: 2rem; +} + +.trust-pill { + display: inline-flex; + align-items: center; + gap: 0.4rem; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-subtle); + color: var(--accent); + padding: 0.4rem 0.9rem; + border-radius: 9999px; + font-size: 0.8rem; + font-weight: 600; + backdrop-filter: blur(8px); +} + +.hero-headline { + font-family: var(--font-heading); + font-size: clamp(2.2rem, 5vw, 3.8rem); + font-weight: 800; + line-height: 1.15; + margin-bottom: 1.5rem; + color: #ffffff; + max-width: 960px; + margin-left: auto; + margin-right: auto; + letter-spacing: -0.02em; +} + +.hero-subheadline { + font-size: clamp(1.05rem, 2vw, 1.25rem); + line-height: 1.6; + color: var(--text-secondary); + max-width: 780px; + margin-left: auto; + margin-right: auto; + margin-bottom: 2.5rem; +} + +.hero-cta-group { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 1.25rem; + margin-bottom: 3.5rem; +} + +.hero-guarantee-row { + display: flex; + justify-content: center; + align-items: center; + gap: 2rem; + border-top: 1px solid var(--border-subtle); + padding-top: 2rem; + color: var(--text-muted); + font-size: 0.85rem; +} + +/* Split Layout for Hero */ +.hero-section.hero-split-layout { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 3.5rem; + text-align: left; + padding: 4.5rem 3rem 4rem; +} + +.hero-split-layout .hero-trust-pills { + justify-content: flex-start; +} + +.hero-split-layout .hero-headline { + margin-left: 0; + margin-right: 0; + font-size: clamp(2rem, 3.8vw, 3.2rem); +} + +.hero-split-layout .hero-subheadline { + margin-left: 0; + margin-right: 0; +} + +.hero-split-layout .hero-cta-group { + justify-content: flex-start; + margin-bottom: 2.5rem; +} + +.hero-split-layout .hero-guarantee-row { + justify-content: flex-start; +} + +/* Hero Content Column */ +.hero-content-col { + flex: 1 1 420px; + min-width: min(100%, 300px); + max-width: 100%; +} + +/* Hero Media Showcase Card Column */ +.hero-media-col { + flex: 1 1 360px; + min-width: min(100%, 280px); + max-width: 520px; + position: relative; + display: flex; + justify-content: center; + margin: 0 auto; +} + +.hero-image-card { + position: relative; + border-radius: var(--radius-xl); + overflow: hidden; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6), 0 0 30px var(--accent-glow); + border: 1px solid var(--border-focus); + max-width: 500px; + width: 100%; +} + +.hero-showcase-img { + width: 100%; + height: 380px; + object-fit: cover; + display: block; + transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); +} + +.hero-image-card:hover .hero-showcase-img { + transform: scale(1.03); +} + +.hero-image-overlay-badge { + position: absolute; + top: 16px; + left: 16px; + background: rgba(11, 19, 41, 0.88); + backdrop-filter: blur(12px); + border: 1px solid var(--border-subtle); + color: #fff; + font-size: 0.78rem; + font-weight: 700; + padding: 6px 12px; + border-radius: 9999px; + display: flex; + align-items: center; + gap: 8px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); +} + +.overlay-badge-pulse { + width: 8px; + height: 8px; + border-radius: 50%; + background: #34d399; + box-shadow: 0 0 10px #34d399; + animation: pulseDot 2s infinite; +} + +@keyframes pulseDot { + 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.7); } + 70% { transform: scale(1); box-shadow: 0 0 0 8px rgba(52, 211, 153, 0); } + 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(52, 211, 153, 0); } +} + +.hero-image-stat-card { + position: absolute; + bottom: 16px; + right: 16px; + background: rgba(11, 19, 41, 0.92); + backdrop-filter: blur(12px); + border: 1px solid var(--border-focus); + padding: 10px 14px; + border-radius: var(--radius-md); + display: flex; + align-items: center; + gap: 10px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); +} + +/* Hero Placeholder Placecard Card */ +.hero-image-placeholder-card { + width: 100%; + max-width: 500px; + min-height: 340px; + border: 2px dashed rgba(56, 189, 248, 0.45); + background: rgba(11, 19, 41, 0.65); + backdrop-filter: blur(14px); + border-radius: var(--radius-xl); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 2.5rem 1.8rem; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); +} + +.hero-image-placeholder-card:hover { + border-color: var(--accent); + background: rgba(11, 19, 41, 0.9); + transform: translateY(-4px); + box-shadow: 0 16px 45px rgba(0, 0, 0, 0.6), 0 0 25px var(--accent-glow); +} + +.placeholder-icon-ring { + width: 64px; + height: 64px; + border-radius: 50%; + background: rgba(56, 189, 248, 0.12); + border: 1px solid var(--border-focus); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1rem; +} + +.placeholder-card-title { + color: #fff; + font-size: 1.05rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.placeholder-card-desc { + color: var(--text-secondary); + font-size: 0.82rem; + line-height: 1.5; + max-width: 300px; + margin-bottom: 1.2rem; +} + +.placeholder-action-pill { + display: inline-flex; + align-items: center; + gap: 6px; + background: rgba(56, 189, 248, 0.15); + border: 1px solid var(--border-focus); + color: var(--accent); + font-size: 0.8rem; + font-weight: 600; + padding: 8px 16px; + border-radius: 9999px; + transition: all 0.2s ease; +} + +.hero-image-placeholder-card:hover .placeholder-action-pill { + background: var(--accent); + color: #040813; +} + +/* Service Card Image Thumbnail */ +.service-thumbnail-wrap { + width: calc(100% + 4.4rem); + margin: -2.2rem -2.2rem 1.5rem -2.2rem; + height: 180px; + overflow: hidden; + border-bottom: 1px solid var(--border-subtle); + background: rgba(0, 0, 0, 0.3); +} + +.service-thumbnail-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + transition: transform 0.4s ease; +} + +.service-card:hover .service-thumbnail-img { + transform: scale(1.05); +} + +/* Responsive Container Queries (Reacts to Canvas Width) */ +@container canvas-body (max-width: 960px) { + .hero-section.hero-split-layout { + flex-direction: column; + text-align: center; + gap: 2.5rem; + padding: 3.5rem 1.5rem 3rem; + } + .hero-split-layout .hero-trust-pills, + .hero-split-layout .hero-cta-group, + .hero-split-layout .hero-guarantee-row { + justify-content: center; + } + .hero-media-col { + width: 100%; + max-width: 480px; + margin: 0 auto; + } + .hero-showcase-img { + height: 280px; + } + .hero-image-placeholder-card { + min-height: 240px; + padding: 2rem 1.2rem; + } +} + +/* Viewport Device Overrides */ +.viewport-mobile .hero-section.hero-split-layout { + flex-direction: column !important; + text-align: center !important; + gap: 2rem !important; + padding: 2.5rem 1.2rem 2.5rem !important; +} + +.viewport-mobile .hero-split-layout .hero-trust-pills, +.viewport-mobile .hero-split-layout .hero-cta-group, +.viewport-mobile .hero-split-layout .hero-guarantee-row { + justify-content: center !important; +} + +.viewport-mobile .hero-media-col { + width: 100% !important; + max-width: 100% !important; +} + +.viewport-mobile .hero-showcase-img { + height: 220px !important; +} + +.viewport-mobile .hero-image-card { + border-radius: var(--radius-lg) !important; +} + +.viewport-tablet .hero-section.hero-split-layout { + flex-direction: column !important; + text-align: center !important; + gap: 2.5rem !important; + padding: 3rem 2rem !important; +} + +.viewport-tablet .hero-split-layout .hero-trust-pills, +.viewport-tablet .hero-split-layout .hero-cta-group, +.viewport-tablet .hero-split-layout .hero-guarantee-row { + justify-content: center !important; +} + +.viewport-tablet .hero-showcase-img { + height: 320px !important; +} + +@media (max-width: 900px) { + .hero-section.hero-split-layout { + flex-direction: column; + text-align: center; + gap: 2.5rem; + padding: 4rem 1.5rem 3rem; + } + .hero-split-layout .hero-trust-pills, + .hero-split-layout .hero-cta-group, + .hero-split-layout .hero-guarantee-row { + justify-content: center; + } + .hero-showcase-img { + height: 300px; + } +} + +/* 3. Services Grid */ +.services-section { + padding: 5rem 2.5rem; + background: var(--bg-surface); +} + +.section-header { + text-align: center; + max-width: 720px; + margin: 0 auto 3.5rem; +} + +.section-eyebrow { + display: inline-block; + color: var(--accent); + font-family: var(--font-heading); + font-size: 0.82rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: 0.75rem; +} + +.section-title { + font-family: var(--font-heading); + font-size: clamp(1.8rem, 3.5vw, 2.6rem); + font-weight: 800; + color: #fff; + margin-bottom: 1rem; + letter-spacing: -0.015em; +} + +.section-description { + font-size: 1.05rem; + color: var(--text-secondary); + line-height: 1.6; +} + +.services-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.75rem; + max-width: 1200px; + margin: 0 auto; +} + +.services-grid.cols-4 { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.service-card { + background: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 2.2rem; + transition: all 0.25s ease; + position: relative; + overflow: hidden; +} + +.service-card:hover { + background: var(--bg-card-hover); + border-color: var(--border-focus); + transform: translateY(-4px); + box-shadow: var(--shadow-glow); +} + +.service-badge-popular { + position: absolute; + top: 18px; + right: 18px; + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #fff; + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + padding: 3px 8px; + border-radius: 9999px; + letter-spacing: 0.05em; +} + +.service-icon-box { + width: 58px; + height: 58px; + border-radius: 16px; + background: radial-gradient(circle at 35% 30%, var(--accent-glow) 0%, rgba(255, 255, 255, 0.04) 100%), var(--bg-surface); + border: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: center; + color: var(--accent); + margin-bottom: 1.5rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.12); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; +} + +.service-icon-box::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, transparent 60%); + pointer-events: none; +} + +.service-card:hover .service-icon-box { + transform: translateY(-3px) scale(1.06); + border-color: var(--accent); + box-shadow: 0 8px 24px var(--accent-glow), 0 0 12px var(--accent); + color: #fff; +} + +.service-card-title { + font-family: var(--font-heading); + font-size: 1.25rem; + font-weight: 700; + color: #fff; + margin-bottom: 0.75rem; +} + +.service-card-desc { + font-size: 0.95rem; + color: var(--text-secondary); + line-height: 1.6; + margin-bottom: 1.5rem; +} + +.service-link { + display: inline-flex; + align-items: center; + gap: 0.35rem; + color: var(--accent); + font-weight: 600; + font-size: 0.9rem; + text-decoration: none; +} +.service-link:hover { + text-decoration: underline; +} + +/* 4. Why Choose Us Section */ +.why-us-section { + padding: 5rem 2.5rem; + background: radial-gradient(circle at bottom center, var(--accent-glow), transparent 60%), var(--bg-main); + border-top: 1px solid var(--border-subtle); + border-bottom: 1px solid var(--border-subtle); +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.5rem; + max-width: 1100px; + margin: 0 auto 4rem; +} + +.stat-card { + background: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 1.75rem 1.25rem; + text-align: center; +} + +.stat-value { + font-family: var(--font-heading); + font-size: 2.75rem; + font-weight: 800; + color: var(--accent); + margin-bottom: 0.25rem; +} + +.stat-label { + font-weight: 600; + color: #fff; + font-size: 0.95rem; + margin-bottom: 0.2rem; +} + +.stat-subtext { + font-size: 0.78rem; + color: var(--text-muted); +} + +.pillars-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); + gap: 2rem; + max-width: 1150px; + margin: 0 auto; +} + +.pillar-item { + display: flex; + gap: 1.25rem; + background: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 1.75rem; +} + +.pillar-icon-box { + flex-shrink: 0; + width: 52px; + height: 52px; + border-radius: 14px; + background: radial-gradient(circle at 30% 30%, var(--accent-glow) 0%, rgba(255, 255, 255, 0.04) 100%), var(--bg-surface); + border: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: center; + color: var(--accent); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.1); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.pillar-item:hover .pillar-icon-box { + transform: scale(1.08); + border-color: var(--accent); + box-shadow: 0 6px 20px var(--accent-glow); +} + +.pillar-title { + font-family: var(--font-heading); + font-size: 1.15rem; + font-weight: 700; + color: #fff; + margin-bottom: 0.5rem; +} + +.pillar-desc { + font-size: 0.92rem; + color: var(--text-secondary); + line-height: 1.55; +} + +/* 5. Testimonials Section */ +.testimonials-section { + padding: 5rem 2.5rem; + background: var(--bg-surface); +} + +.reviews-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.review-card { + background: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-lg); + padding: 2.2rem; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.review-stars { + display: flex; + gap: 3px; + color: #fbbf24; + margin-bottom: 1.25rem; +} + +.review-quote { + font-size: 1.02rem; + color: var(--text-primary); + line-height: 1.65; + margin-bottom: 1.75rem; + font-style: italic; +} + +.review-author-row { + display: flex; + align-items: center; + gap: 0.85rem; + border-top: 1px solid var(--border-subtle); + padding-top: 1.25rem; +} + +.author-avatar { + width: 42px; + height: 42px; + border-radius: 50%; + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 0.95rem; +} + +.author-name { + font-weight: 700; + color: #fff; + font-size: 0.95rem; +} + +.author-location { + font-size: 0.8rem; + color: var(--text-secondary); +} + +/* 6. Quote & Booking Form Section */ +.quote-section { + padding: 5rem 2.5rem; + background: radial-gradient(circle at top left, var(--accent-glow), transparent 60%), var(--bg-main); +} + +.quote-box { + max-width: 780px; + margin: 0 auto; + background: var(--bg-card); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-xl); + padding: 3.5rem 3rem; + box-shadow: var(--shadow-md); +} + +.form-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; + margin-top: 2rem; +} + +.form-group-full { + grid-column: span 2; +} + +.form-label { + display: block; + font-size: 0.88rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 0.5rem; +} + +.form-input, .form-select, .form-textarea { + width: 100%; + min-height: 48px; + background: rgba(11, 19, 41, 0.6); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 0.75rem 1rem; + color: #fff; + font-family: var(--font-body); + font-size: 0.95rem; + transition: all 0.15s ease; +} + +.form-input:focus, .form-select:focus, .form-textarea:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +.form-textarea { + min-height: 110px; + resize: vertical; +} + +/* 7. CTA Banner */ +.cta-banner-section { + padding: 4.5rem 2.5rem; + background: linear-gradient(135deg, var(--bg-surface), var(--bg-main)); + border-top: 1px solid var(--border-subtle); + border-bottom: 1px solid var(--border-subtle); + text-align: center; +} + +.cta-banner-box { + max-width: 900px; + margin: 0 auto; + background: linear-gradient(135deg, rgba(2, 132, 199, 0.15), rgba(56, 189, 248, 0.05)); + border: 1px solid var(--border-focus); + border-radius: var(--radius-xl); + padding: 3.5rem 2.5rem; + box-shadow: var(--shadow-glow); +} + +.cta-banner-title { + font-family: var(--font-heading); + font-size: clamp(1.8rem, 3vw, 2.5rem); + font-weight: 800; + color: #fff; + margin-bottom: 1rem; +} + +.cta-banner-desc { + font-size: 1.1rem; + color: var(--text-secondary); + max-width: 650px; + margin: 0 auto 2rem; +} + +/* 8. Footer Section */ +.footer-section { + padding: 4.5rem 2.5rem 2.5rem; + background: #050811; + border-top: 1px solid var(--border-subtle); + color: var(--text-secondary); +} + +.footer-grid { + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: 3rem; + max-width: 1200px; + margin: 0 auto 3rem; +} + +.footer-company-desc { + margin-top: 1rem; + font-size: 0.92rem; + line-height: 1.6; + max-width: 380px; +} + +.footer-col-title { + font-family: var(--font-heading); + font-size: 1rem; + font-weight: 700; + color: #fff; + margin-bottom: 1.25rem; +} + +.footer-links-list { + list-style: none; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.footer-links-list a { + color: var(--text-secondary); + text-decoration: none; + font-size: 0.9rem; + transition: color 0.15s ease; +} +.footer-links-list a:hover { + color: var(--accent); +} + +.footer-bottom-bar { + max-width: 1200px; + margin: 0 auto; + border-top: 1px solid var(--border-subtle); + padding-top: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.82rem; + color: var(--text-muted); +} + +.whitelabel-credit { + display: flex; + align-items: center; + gap: 0.4rem; + color: var(--text-secondary); +} + +/* ========================================================================== + INTERACTIVE AI CHAT DRAWER & SIDEBAR + ========================================================================== */ +.ai-drawer-toggle-btn { + position: fixed; + bottom: 24px; + right: 24px; + z-index: 90; + display: flex; + align-items: center; + gap: 0.65rem; + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #fff; + font-family: var(--font-heading); + font-weight: 700; + font-size: 0.92rem; + padding: 0.85rem 1.5rem; + border-radius: 9999px; + border: none; + cursor: pointer; + box-shadow: 0 8px 30px var(--accent-glow); + transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1); +} + +.ai-drawer-toggle-btn:hover { + transform: translateY(-2px) scale(1.02); + filter: brightness(1.1); +} + +.ai-drawer-container { + width: 420px; + background: rgba(11, 19, 41, 0.95); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-left: 1px solid var(--border-subtle); + display: flex; + flex-direction: column; + height: 100%; + position: relative; + z-index: 60; + transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} + +.ai-drawer-header { + padding: 1.25rem 1.5rem; + border-bottom: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: space-between; + background: rgba(255, 255, 255, 0.02); +} + +.ai-avatar { + width: 36px; + height: 36px; + border-radius: 10px; + background: linear-gradient(135deg, var(--primary), var(--accent)); + display: flex; + align-items: center; + justify-content: center; + color: #fff; +} + +.ai-header-text h3 { + font-family: var(--font-heading); + font-size: 0.95rem; + font-weight: 700; + color: #fff; +} + +.ai-status-pill { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.72rem; + color: #34d399; +} + +.ai-status-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: #34d399; + box-shadow: 0 0 8px #34d399; +} + +.chat-history-container { + flex: 1; + overflow-y: auto; + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.chat-bubble { + max-width: 90%; + padding: 0.9rem 1.1rem; + border-radius: var(--radius-md); + font-size: 0.9rem; + line-height: 1.55; + position: relative; + animation: fadeIn 0.2s ease-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + +.chat-bubble.ai { + align-self: flex-start; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-subtle); + color: var(--text-primary); + border-bottom-left-radius: 4px; +} + +.chat-bubble.user { + align-self: flex-end; + background: linear-gradient(135deg, var(--primary), var(--primary-hover)); + color: #fff; + border-bottom-right-radius: 4px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +.chat-action-btn { + margin-top: 0.65rem; + display: inline-flex; + align-items: center; + gap: 0.4rem; + background: rgba(56, 189, 248, 0.15); + border: 1px solid var(--accent); + color: var(--accent); + font-size: 0.8rem; + font-weight: 600; + padding: 5px 10px; + border-radius: 6px; + cursor: pointer; + transition: all 0.15s ease; +} + +.chat-action-btn:hover { + background: var(--accent); + color: #070c18; +} + +.quick-prompts-bar { + padding: 0.75rem 1.25rem; + border-top: 1px solid var(--border-subtle); + display: flex; + gap: 0.5rem; + overflow-x: auto; + white-space: nowrap; +} + +.quick-prompt-chip { + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-subtle); + color: var(--text-secondary); + font-size: 0.76rem; + padding: 4px 10px; + border-radius: 9999px; + cursor: pointer; + transition: all 0.15s ease; +} + +.quick-prompt-chip:hover { + border-color: var(--accent); + color: var(--accent); +} + +.chat-input-bar { + padding: 1rem 1.25rem; + border-top: 1px solid var(--border-subtle); + background: rgba(7, 12, 24, 0.9); +} + +.chat-input-wrapper { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 4px 6px 4px 12px; +} + +.chat-input-wrapper:focus-within { + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); +} + +.chat-text-input { + flex: 1; + background: transparent; + border: none; + color: #fff; + font-size: 0.9rem; + padding: 8px 0; + outline: none; +} + +.chat-send-btn { + background: var(--primary); + border: none; + color: #fff; + width: 36px; + height: 36px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.15s ease; +} +.chat-send-btn:hover { + background: var(--accent); + color: #070c18; +} + +/* ========================================================================== + MODALS (Onboarding, Approval / Lead Submission, Agency Admin) + ========================================================================== */ +.modal-overlay { + position: fixed; + inset: 0; + z-index: 100; + background: rgba(0, 0, 0, 0.78); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + display: flex; + align-items: center; + justify-content: center; + padding: 1.5rem; + animation: fadeIn 0.2s ease-out; +} + +.modal-card { + background: var(--bg-surface); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-xl); + max-width: 760px; + width: 100%; + max-height: 90vh; + overflow-y: auto; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.6); + position: relative; +} + +.modal-header { + padding: 1.75rem 2rem 1.25rem; + border-bottom: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: space-between; +} + +.modal-title { + font-family: var(--font-heading); + font-size: 1.4rem; + font-weight: 800; + color: #fff; +} + +.modal-close-btn { + background: transparent; + border: none; + color: var(--text-muted); + cursor: pointer; + padding: 6px; + border-radius: 8px; +} +.modal-close-btn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.08); +} + +.modal-body { + padding: 2rem; +} + +.modal-footer { + padding: 1.25rem 2rem; + border-top: 1px solid var(--border-subtle); + display: flex; + align-items: center; + justify-content: flex-end; + gap: 1rem; + background: rgba(0, 0, 0, 0.15); +} + +/* Preset Picker Cards */ +.presets-strip { + display: flex; + gap: 0.75rem; + overflow-x: auto; + padding-bottom: 0.75rem; + margin-bottom: 1.5rem; +} + +.preset-card-chip { + flex-shrink: 0; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 0.75rem 1rem; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 0.6rem; +} + +.preset-card-chip:hover { + border-color: var(--accent); + background: rgba(56, 189, 248, 0.08); + transform: translateY(-2px); +} + +/* Theme Swatch Selector */ +.theme-selector-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); + gap: 0.75rem; + margin: 1rem 0 1.5rem; +} + +.theme-swatch-card { + background: rgba(255, 255, 255, 0.03); + border: 2px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 0.85rem; + cursor: pointer; + text-align: center; + transition: all 0.2s ease; +} + +.theme-swatch-card:hover { + border-color: var(--border-focus); +} + +.theme-swatch-card.active { + border-color: var(--accent); + background: rgba(56, 189, 248, 0.08); + box-shadow: 0 0 16px var(--accent-glow); +} + +.theme-swatch-colors { + display: flex; + justify-content: center; + gap: 6px; + margin-bottom: 0.5rem; +} + +.swatch-circle { + width: 18px; + height: 18px; + border-radius: 50%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4); +} + +/* Agency Roadmap Checklist */ +.roadmap-box { + background: rgba(56, 189, 248, 0.06); + border: 1px solid var(--border-focus); + border-radius: var(--radius-lg); + padding: 1.5rem; + margin: 1.5rem 0; +} + +.roadmap-step { + display: flex; + align-items: flex-start; + gap: 0.85rem; + margin-bottom: 1rem; +} + +.roadmap-step:last-child { + margin-bottom: 0; +} + +.roadmap-step-icon { + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.85rem; + font-weight: 700; + flex-shrink: 0; +} + +.roadmap-step-content h4 { + font-size: 0.95rem; + font-weight: 700; + color: #fff; + margin-bottom: 0.2rem; +} + +.roadmap-step-content p { + font-size: 0.82rem; + color: var(--text-secondary); +} + +/* ========================================================================== + 5% UPGRADES: SWATCH BAR, MOBILE STICKY CTA, INLINE EDIT, CHIPS & SUCCESS + ========================================================================== */ + +/* 1. Topbar Quick Swatch Bar */ +.topbar-swatch-bar { + display: flex; + align-items: center; + gap: 6px; + background: rgba(255, 255, 255, 0.04); + padding: 4px 8px; + border-radius: var(--radius-md); + border: 1px solid var(--border-subtle); +} + +.topbar-swatch-btn { + width: 22px; + height: 22px; + border-radius: 50%; + border: none; + display: flex; + overflow: hidden; + cursor: pointer; + padding: 0; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); +} + +.topbar-swatch-btn:hover { + transform: scale(1.25); + box-shadow: 0 0 12px var(--accent); +} + +.topbar-swatch-btn.active { + outline: 2px solid #fff; + outline-offset: 2px; + transform: scale(1.15); + box-shadow: 0 0 12px rgba(255, 255, 255, 0.6); +} + +.swatch-half-left { + width: 50%; + height: 100%; +} + +.swatch-half-right { + width: 50%; + height: 100%; +} + +/* 2. Sticky Mobile Bottom Lead & Call Bar */ +.mobile-sticky-action-bar { + position: sticky; + bottom: 0; + left: 0; + right: 0; + z-index: 99; + background: rgba(11, 19, 41, 0.94); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + border-top: 1px solid var(--border-subtle); + padding: 10px 14px; + display: flex; + gap: 10px; + align-items: center; + box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5); +} + +.mobile-sticky-call-btn { + flex: 1.2; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + background: var(--primary); + color: #fff; + font-weight: 700; + font-size: 0.85rem; + padding: 10px 12px; + border-radius: var(--radius-sm); + text-decoration: none; + transition: all 0.2s; + box-shadow: 0 2px 10px var(--accent-glow); +} + +.mobile-sticky-call-btn:hover { + background: var(--primary-hover); + color: #fff; +} + +.mobile-call-pulse-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #34d399; + box-shadow: 0 0 8px #34d399; + animation: pulseDot 1.5s infinite; +} + +@keyframes pulseDot { + 0% { transform: scale(0.95); opacity: 0.8; } + 50% { transform: scale(1.3); opacity: 1; } + 100% { transform: scale(0.95); opacity: 0.8; } +} + +.mobile-sticky-quote-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + background: rgba(255, 255, 255, 0.08); + border: 1px solid var(--border-subtle); + color: var(--text-primary); + font-weight: 600; + font-size: 0.85rem; + padding: 10px 12px; + border-radius: var(--radius-sm); + text-decoration: none; + transition: all 0.2s; +} + +.mobile-sticky-quote-btn:hover { + background: rgba(255, 255, 255, 0.15); + border-color: var(--accent); + color: #fff; +} + +/* 3. In-Place Quote Success Card */ +.quote-success-card { + padding: 3rem 2rem; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + animation: fadeInSuccess 0.4s ease-out forwards; +} + +@keyframes fadeInSuccess { + from { opacity: 0; transform: translateY(12px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + +.quote-success-icon-ring { + width: 72px; + height: 72px; + border-radius: 50%; + background: rgba(52, 211, 153, 0.12); + border: 2px solid rgba(52, 211, 153, 0.3); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.25rem; + box-shadow: 0 0 24px rgba(52, 211, 153, 0.25); +} + +.quote-success-title { + font-size: 1.6rem; + font-weight: 800; + color: #fff; + margin-bottom: 0.75rem; +} + +.quote-success-desc { + font-size: 0.95rem; + color: var(--text-secondary); + max-width: 480px; + line-height: 1.5; + margin-bottom: 1.25rem; +} + +.quote-success-desc strong { + color: #fff; +} + +.quote-success-desc em { + color: var(--accent); + font-style: normal; + font-weight: 600; +} + +.quote-success-pill { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-subtle); + padding: 8px 16px; + border-radius: 9999px; + font-size: 0.85rem; + color: var(--text-secondary); +} + +.quote-success-pill strong { + color: #fff; +} + +/* 4. Direct Inline Text Editing */ +.inline-edit-hint { + font-size: 0.72rem; + color: var(--text-muted); + margin-left: 0.5rem; + display: none; +} + +@media (min-width: 900px) { + .inline-edit-hint { + display: inline-block; + } +} + +.inline-editable-text { + transition: all 0.15s ease; + border-radius: 4px; + cursor: text; +} + +.inline-editable-text:hover { + outline: 1px dashed var(--accent); + outline-offset: 4px; +} + +.inline-editable-text:focus { + outline: 2px solid var(--accent); + outline-offset: 4px; + background: rgba(255, 255, 255, 0.04); +} + +/* 5. Always-Visible Quick AI Action Chips in Drawer */ +.quick-action-chips-container { + border-top: 1px solid var(--border-subtle); + background: rgba(7, 12, 24, 0.75); + backdrop-filter: blur(8px); + padding: 8px 12px; + overflow: hidden; +} + +.quick-action-chips-scroll { + display: flex; + gap: 8px; + overflow-x: auto; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; + padding-bottom: 2px; +} + +.quick-action-chips-scroll::-webkit-scrollbar { + display: none; +} + +.quick-chip-pill { + flex-shrink: 0; + display: inline-flex; + align-items: center; + gap: 4px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid var(--border-subtle); + color: var(--text-secondary); + font-size: 0.75rem; + font-weight: 500; + padding: 5px 11px; + border-radius: 9999px; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + white-space: nowrap; +} + +.quick-chip-pill:hover { + background: rgba(255, 255, 255, 0.12); + border-color: var(--accent); + color: #fff; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +/* ========================================================================== + RESPONSIVE QUERIES + ========================================================================== */ +@media (max-width: 900px) { + .app-container { + flex-direction: column; + } + .ai-drawer-container { + width: 100%; + height: 400px; + border-left: none; + border-top: 1px solid var(--border-subtle); + } + .footer-grid { + grid-template-columns: 1fr; + gap: 2rem; + } + .form-grid { + grid-template-columns: 1fr; + } + .form-group-full { + grid-column: span 1; + } +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..a2bf01b --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + +); diff --git a/src/services/aiEngine.ts b/src/services/aiEngine.ts new file mode 100644 index 0000000..eacca3e --- /dev/null +++ b/src/services/aiEngine.ts @@ -0,0 +1,1962 @@ +import { BusinessProfile, WebsiteSection, SectionContent, ThemeId, ChatMessage, ServiceItem, StatItem, ValuePillarItem, TestimonialItem, ColorMode } from '../types'; + +// Curated Theme Definitions +export const THEMES: Record = { + 'titan-navy': { + id: 'titan-navy', + name: 'Titan Navy & Cyan', + tagline: 'High-trust, modern contractor & sparkling pool vibe', + primary: '#0284c7', + accent: '#38bdf8', + badge: 'Navy & Cyan Blue', + bgDark: '#0b1329', + cardBg: '#111e40', + accentGlow: 'rgba(56, 189, 248, 0.25)', + }, + 'obsidian-gold': { + id: 'obsidian-gold', + name: 'Obsidian & Gold', + tagline: 'Ultra-luxurious, premium executive, law & aesthetic clinic', + primary: '#d97706', + accent: '#fbbf24', + badge: 'Obsidian Black & Gold', + bgDark: '#0f0f11', + cardBg: '#1a1815', + accentGlow: 'rgba(251, 191, 36, 0.2)', + }, + 'emerald-slate': { + id: 'emerald-slate', + name: 'Emerald Slate & Mint', + tagline: 'Fresh outdoor, eco-landscaping, lawn & grounds care in green & black', + primary: '#059669', + accent: '#34d399', + badge: 'Green & Dark Slate / Black', + bgDark: '#04120e', + cardBg: '#0a1f18', + accentGlow: 'rgba(52, 211, 153, 0.25)', + }, + 'crimson-forge': { + id: 'crimson-forge', + name: 'Crimson Forge & Amber', + tagline: 'Bold industrial, high-impact roofing, towing & 24/7 emergency', + primary: '#dc2626', + accent: '#f87171', + badge: 'Crimson Red & Black', + bgDark: '#1a0d0d', + cardBg: '#2a1414', + accentGlow: 'rgba(248, 113, 113, 0.25)', + }, + 'alpine-clean': { + id: 'alpine-clean', + name: 'Alpine Clean & Indigo', + tagline: 'Minimalist, crisp clinical elegance & modern corporate', + primary: '#4f46e5', + accent: '#818cf8', + badge: 'Clean Indigo & Crisp Slate', + bgDark: '#0f172a', + cardBg: '#1e293b', + accentGlow: 'rgba(129, 140, 248, 0.25)', + }, + 'electric-violet': { + id: 'electric-violet', + name: 'Electric Violet & Lavender', + tagline: 'Futuristic, high-energy tech, creative agency & modern luxury', + primary: '#9333ea', + accent: '#c084fc', + badge: 'Violet & Deep Obsidian', + bgDark: '#0b0616', + cardBg: '#170d2c', + accentGlow: 'rgba(192, 132, 252, 0.25)', + }, + 'sunset-amber': { + id: 'sunset-amber', + name: 'Sunset Amber & Copper', + tagline: 'Warm earth, artisan masonry, electrical, bronze & industrial', + primary: '#ea580c', + accent: '#fb923c', + badge: 'Amber Orange & Charcoal', + bgDark: '#120904', + cardBg: '#211208', + accentGlow: 'rgba(251, 146, 60, 0.25)', + }, + 'cyber-cyan': { + id: 'cyber-cyan', + name: 'Cyber Cyan & Teal', + tagline: 'Ultra-modern tech, aqua, pool intelligence & crisp turquoise', + primary: '#0d9488', + accent: '#2dd4bf', + badge: 'Teal Cyan & Midnight', + bgDark: '#041012', + cardBg: '#092126', + accentGlow: 'rgba(45, 212, 191, 0.25)', + }, + 'neon-rose': { + id: 'neon-rose', + name: 'Neon Rose & Fuchsia', + tagline: 'Bold aesthetic, beauty clinic, vibrant modern salon & hospitality', + primary: '#e11d48', + accent: '#fb7185', + badge: 'Neon Rose & Obsidian', + bgDark: '#14050a', + cardBg: '#240a13', + accentGlow: 'rgba(251, 113, 133, 0.25)', + }, + 'custom-dynamic': { + id: 'custom-dynamic', + name: 'Custom Dynamic Palette', + tagline: 'User-specified custom branding and dynamic color tokens', + primary: '#6366f1', + accent: '#a5b4fc', + badge: 'Custom Palette', + bgDark: '#0b0f19', + cardBg: '#131b2e', + accentGlow: 'rgba(165, 180, 252, 0.25)', + }, +}; + +// Industry Preset Definition with full content blueprints +export interface IndustryPreset { + id: string; + name: string; + sampleCompany: string; + samplePhone: string; + sampleAddress: string; + sampleCity: string; + sampleState: string; + sampleZip: string; + sampleTagline: string; + defaultTheme: ThemeId; + defaultServices: string[]; + serviceDescriptions: Record; + heroHeadline: string; + heroSubheadline: string; + stats: StatItem[]; + pillars: ValuePillarItem[]; + reviews: TestimonialItem[]; + ctaBannerTitle: string; + ctaBannerDesc: string; + heroImageUrl?: string; + serviceImages?: Record; +} + +export const INDUSTRY_PRESETS: Record = { + 'pool-service': { + id: 'pool-service', + name: 'Pool Service & Maintenance', + sampleCompany: 'Apex Crystal Pool Solutions', + samplePhone: '(661) 555-0149', + sampleAddress: '24800 Avenue Tibbitts', + sampleCity: 'Valencia', + sampleState: 'CA', + sampleZip: '91355', + sampleTagline: 'Pristine Pool Chemistry, Equipment Repair & Crystal Clear Waters', + defaultTheme: 'titan-navy', + defaultServices: [ + 'Weekly Chemical Balancing & Skimming', + 'Variable Speed Pump & Filter Repair', + 'Acid Wash & Mineral Stain Removal', + 'Waterline Tile Cleaning & Calcium Scrub', + 'Smartphone Automation & Salt Systems', + 'Emergency Green-to-Clean Recovery', + ], + serviceDescriptions: { + 'Weekly Chemical Balancing & Skimming': 'Precision 7-point water testing, chlorine & pH balancing, vacuuming, and digital chemistry logs sent after every visit.', + 'Variable Speed Pump & Filter Repair': 'Pentair and Hayward warranty repair, motor replacements, and filter cleanings saving up to 70% in monthly electric bills.', + 'Acid Wash & Mineral Stain Removal': 'Restorative deep chemical washing removing tough algae stains, copper marks, and rough plaster calcium scaling.', + 'Waterline Tile Cleaning & Calcium Scrub': 'Low-pressure mineral bead blasting that restores sparkling tile shine without scratching ceramic glaze.', + 'Smartphone Automation & Salt Systems': 'OmniLogic & ScreenLogic app automation, remote pool heating, and saltwater chlorination conversions.', + 'Emergency Green-to-Clean Recovery': 'Rapid multi-day heavy flocculation and chemical shock treatment turning murky green swamp pools crystal blue in 24 hours.', + }, + heroHeadline: 'Crystal Clear Waters & Flawless Pool Care You Can Trust', + heroSubheadline: 'Certified CPO pool technicians delivering weekly maintenance, high-efficiency equipment repairs, and guaranteed sparkling water all season long.', + stats: [ + { id: 'stat-p1', value: '1,200+', label: 'Pools Balanced & Maintained', subtext: 'Always crystal clear' }, + { id: 'stat-p2', value: '100%', label: 'Water Chemistry Guarantee', subtext: 'Zero cloudy days' }, + { id: 'stat-p3', value: '12 Yrs', label: 'Certified CPO Experience', subtext: 'Licensed & insured' }, + { id: 'stat-p4', value: '< 24hr', label: 'Green-to-Clean Turnaround', subtext: 'Rapid rescue' }, + ], + pillars: [ + { + id: 'pil-p1', + title: 'Precision 7-Point Digital Chemistry', + description: 'We test free chlorine, total chlorine, pH, alkalinity, calcium hardness, cyanuric acid, and salt levels every visit.', + iconName: 'check-circle', + }, + { + id: 'pil-p2', + title: 'All-Inclusive Chemical Pricing', + description: 'Never get hit with unexpected shock or algaecide bills. Our transparent weekly rates cover all balancing chemicals.', + iconName: 'tag', + }, + { + id: 'pil-p3', + title: 'Digital Service Confirmation', + description: 'You receive timestamped photos, chemical readouts, and gate latch verification immediately after every service visit.', + iconName: 'shield-check', + }, + ], + reviews: [ + { + id: 'rev-p1', + author: 'Marcus Vance', + location: 'Valencia, CA', + rating: 5, + quote: 'Apex Crystal Pool Solutions rescued our pool right before our 4th of July BBQ. It was green on Thursday and 100% sparkling blue by Friday noon. Phenomenal work!', + serviceMentioned: 'Emergency Green-to-Clean', + date: '2 weeks ago', + }, + { + id: 'rev-p2', + author: 'Elena Rostova', + location: 'Valencia, CA', + rating: 5, + quote: 'Finding reliable pool service in the Santa Clarita Valley was a nightmare until we found Apex. They show up on the same day every week without fail.', + serviceMentioned: 'Weekly Maintenance', + date: 'Last month', + }, + { + id: 'rev-p3', + author: 'David & Sarah Jenkins', + location: 'Valencia, CA', + rating: 5, + quote: 'Upgraded our ancient single-speed pump to a variable speed unit. Our electric bill dropped by $90 a month and you cannot even hear it running!', + serviceMentioned: 'Pump Upgrade', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Ready for Crystal Clear Waters All Season Long?', + ctaBannerDesc: 'Get 50% off your first month of weekly chemical service with any seasonal plan. Lock in your spot today!', + heroImageUrl: '/images/pool/hero.jpg', + serviceImages: { + 'Weekly Chemical Balancing & Skimming': '/images/pool/water_testing.jpg', + 'Variable Speed Pump & Filter Repair': '/images/pool/pump_repair.jpg', + 'Waterline Tile Cleaning & Calcium Scrub': '/images/pool/tile_cleaning.jpg', + }, + }, + 'landscaping': { + id: 'landscaping', + name: 'Landscaping, Hardscape & Grounds Care', + sampleCompany: 'Verdant Craft Landscapes & Design', + samplePhone: '(303) 555-0728', + sampleAddress: '1600 Boulder Rd', + sampleCity: 'Boulder', + sampleState: 'CO', + sampleZip: '80302', + sampleTagline: 'Architectural Hardscaping, Drought-Tolerant Native Gardens & Outdoor Living', + defaultTheme: 'emerald-slate', + defaultServices: [ + 'Custom Paver Patios & Walkways', + 'Drought-Smart Xeriscaping & Mulch', + 'Smart Irrigation & Drip Systems', + 'Architectural Landscape Lighting', + 'Retaining Walls & Hillside Terraces', + 'Seasonal Grounds Care & Pruning', + ], + serviceDescriptions: { + 'Custom Paver Patios & Walkways': 'Interlocking stone, flagstone, and modern porcelain pavers engineered for lifetime stability and beauty.', + 'Drought-Smart Xeriscaping & Mulch': 'Breathtaking native perennial gardens, ornamental grasses, and rock beds that consume 70% less water.', + 'Smart Irrigation & Drip Systems': 'Weather-sensing smart controllers, MP rotators, and zero-runoff sub-surface drip irrigation systems.', + 'Architectural Landscape Lighting': 'Low-voltage architectural LED lighting illuminating trees, paths, and entertainment zones after sunset.', + 'Retaining Walls & Hillside Terraces': 'Structural segmental block and boulder walls engineered for hillside stabilization and usable yard space.', + 'Seasonal Grounds Care & Pruning': 'Aeration, deep-root fertilization, artisan pruning, and winterization cleanups for estate properties.', + }, + heroHeadline: 'Transform Your Outdoors Into a Breathtaking Oasis', + heroSubheadline: 'Award-winning landscape architects combining Colorado native elegance with master stonecraft, lush greens, and smart irrigation.', + stats: [ + { id: 'stat-l1', value: '2,500+', label: 'Estates & Lawns Transformed', subtext: 'Custom crafted' }, + { id: 'stat-l2', value: '100%', label: 'Workmanship Guarantee', subtext: 'Zero corners cut' }, + { id: 'stat-l3', value: '15 Yrs', label: 'Master Stonecraft Experience', subtext: 'Artisan quality' }, + { id: 'stat-l4', value: '70%', label: 'Average Water Savings', subtext: 'Smart xeriscaping' }, + ], + pillars: [ + { + id: 'pil-l1', + title: 'Architectural Stone & Paver Precision', + description: 'Deep compacted aggregate bases with geotextile reinforcement prevent settling, rutting, or weed growth for decades.', + iconName: 'award', + }, + { + id: 'pil-l2', + title: 'Drought-Smart Water Conservation', + description: 'Our certified irrigation designers slash your water utility bills while keeping plants and trees thriving year-round.', + iconName: 'sparkles', + }, + { + id: 'pil-l3', + title: 'White-Glove Cleanliness & Respect', + description: 'We treat your property like our own. Daily site swept clean, gates latched, and courteous uniformed crews.', + iconName: 'shield-check', + }, + ], + reviews: [ + { + id: 'rev-l1', + author: 'Robert Sterling', + location: 'Boulder, CO', + rating: 5, + quote: 'Verdant Craft Landscapes transformed our sloped mud pit into a multi-tiered paver paradise with a fire pit and ambient lighting. Simply breathtaking work!', + serviceMentioned: 'Hardscaping & Pavers', + date: '2 weeks ago', + }, + { + id: 'rev-l2', + author: 'Hannah Davies', + location: 'Boulder, CO', + rating: 5, + quote: 'Their native plant knowledge is unmatched. Our yard looks like a luxury resort and our summer water bill went down by half.', + serviceMentioned: 'Smart Xeriscaping', + date: 'Last month', + }, + { + id: 'rev-l3', + author: 'Greg & Lisa Chen', + location: 'Boulder, CO', + rating: 5, + quote: 'On-time every Tuesday, impeccably clean, and the retaining wall they engineered stopped our erosion completely.', + serviceMentioned: 'Retaining Walls', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Ready to Bring Your Dream Backyard to Life?', + ctaBannerDesc: 'Schedule a free 3D design consultation and property walkthrough with our master landscape architects today.', + heroImageUrl: '/images/landscaping/hero.jpg', + serviceImages: { + 'Custom Paver Patios & Walkways': '/images/landscaping/paver_patio.jpg', + 'Drought-Smart Xeriscaping & Mulch': '/images/landscaping/xeriscaping.jpg', + 'Architectural Landscape Lighting': '/images/landscaping/landscape_lighting.jpg', + }, + }, + 'roofing': { + id: 'roofing', + name: 'Roofing & Exterior Contracting', + sampleCompany: 'Vanguard Peak Roofing & Restoration', + samplePhone: '(214) 555-0812', + sampleAddress: '1420 Commerce St', + sampleCity: 'Dallas', + sampleState: 'TX', + sampleZip: '75201', + sampleTagline: 'Engineered Roofing Systems Built to Endure Extreme Weather', + defaultTheme: 'crimson-forge', + defaultServices: [ + 'Storm Damage Drone Inspection', + 'Architectural Shingle Replacement', + 'Commercial TPO & Flat Membrane', + 'Emergency Tarping & Leak Repair', + 'Seamless Gutter & Flashing Systems', + 'Direct Insurance Claim Advocacy', + ], + serviceDescriptions: { + 'Storm Damage Drone Inspection': 'High-resolution thermal and drone inspections identifying hidden hail bruises and wind-lifted shingles.', + 'Architectural Shingle Replacement': 'Class 4 impact-resistant architectural shingles rated for 130 MPH winds with 50-year non-prorated warranties.', + 'Commercial TPO & Flat Membrane': 'Heat-welded thermoplastic polyolefin membranes engineered for commercial reflectivity and ponding water defense.', + 'Emergency Tarping & Leak Repair': 'Guaranteed 2-hour emergency weatherproofing dispatch during active severe hail and rain events.', + 'Seamless Gutter & Flashing Systems': 'Heavy-gauge aluminum seamless gutters fabricated on-site with copper kick-out flashings.', + 'Direct Insurance Claim Advocacy': 'Line-item Xactimate documentation and on-roof adjuster walkthroughs guaranteeing you receive full entitled claim coverage.', + }, + heroHeadline: 'Ironclad Roofing Built to Weather Every Storm', + heroSubheadline: 'Licensed, bonded master roofers delivering leak-free warranties, seamless insurance claim support, and rapid emergency response.', + stats: [ + { id: 'stat-r1', value: '3,500+', label: 'Roofs Inspected & Installed', subtext: 'Zero leaks reported' }, + { id: 'stat-r2', value: '50-Yr', label: 'Manufacturer Warranty', subtext: 'Non-prorated coverage' }, + { id: 'stat-r3', value: '100%', label: 'Insurance Claim Approval Rate', subtext: 'Zero out-of-pocket stress' }, + { id: 'stat-r4', value: '< 2hr', label: 'Emergency Storm Dispatch', subtext: 'Always on call' }, + ], + pillars: [ + { + id: 'pil-r1', + title: 'Master Elite Certified Installation', + description: 'Only the top 2% of roofers qualify for our certification, granting you access to golden pledge warranties.', + iconName: 'award', + }, + { + id: 'pil-r2', + title: 'Zero-Deductible Insurance Guidance', + description: 'We handle every conversation with your insurance company, ensuring all storm damage is documented and paid.', + iconName: 'shield', + }, + { + id: 'pil-r3', + title: 'Magnetic Nail Sweeps & Clean Worksite', + description: 'We run triple magnetic sweeps over your lawn, driveway, and flowerbeds so your family and tires stay safe.', + iconName: 'check-circle', + }, + ], + reviews: [ + { + id: 'rev-r1', + author: 'Tyler Henderson', + location: 'Dallas, TX', + rating: 5, + quote: 'Vanguard handled our entire hail damage claim. New Class 4 roof installed in a single day and they left our yard spotless!', + serviceMentioned: 'Shingle Replacement', + date: '2 weeks ago', + }, + { + id: 'rev-r2', + author: 'Amanda Scott', + location: 'Dallas, TX', + rating: 5, + quote: 'Called them when our ceiling started leaking during a Sunday storm. A crew was out with tarps in 45 minutes.', + serviceMentioned: 'Emergency Tarping', + date: 'Last month', + }, + { + id: 'rev-r3', + author: 'Carlos Mendoza', + location: 'Dallas, TX', + rating: 5, + quote: 'Commercial TPO roof on our warehouse completed without interrupting our shipping schedule. True professionals.', + serviceMentioned: 'Commercial Flat Roof', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Suspect Storm or Hail Damage on Your Roof?', + ctaBannerDesc: 'Book your free 21-point drone roof inspection and insurance report before claim filing deadlines expire.', + heroImageUrl: '/images/roofing/hero.jpg', + serviceImages: { + 'Storm Damage Drone Inspection': '/images/roofing/drone_inspection.jpg', + 'Architectural Shingle Replacement': '/images/roofing/shingle_replacement.jpg', + 'Seamless Aluminum Gutter Systems': '/images/roofing/gutter_install.jpg', + }, + }, + 'hvac': { + id: 'hvac', + name: 'HVAC, Heating & Air Conditioning', + sampleCompany: 'Summit Climate Systems & Air', + samplePhone: '(480) 555-0394', + sampleAddress: '7200 E Camelback Rd', + sampleCity: 'Scottsdale', + sampleState: 'AZ', + sampleZip: '85251', + sampleTagline: 'Precision Cooling, Rapid 24/7 AC Repair & Pure Indoor Air', + defaultTheme: 'titan-navy', + defaultServices: [ + '24/7 Emergency AC Diagnostic & Repair', + 'High-SEER2 Heat Pump & AC Replacement', + '26-Point Seasonal Precision Tune-Up', + 'Whole-Home HEPA & UV Air Purification', + 'Smart Thermostat & Zoning Systems', + 'Commercial Walk-In & Rooftop HVAC', + ], + serviceDescriptions: { + '24/7 Emergency AC Diagnostic & Repair': 'Same-day emergency diagnostics and refrigerant recharge when summer heat waves strike your home.', + 'High-SEER2 Heat Pump & AC Replacement': 'High-efficiency inverter systems that slash your monthly electric bills while keeping cooling whisper-quiet.', + '26-Point Seasonal Precision Tune-Up': 'Comprehensive inspection cleaning condenser coils, testing capacitors, and extending compressor life.', + 'Whole-Home HEPA & UV Air Purification': 'Hospital-grade indoor air scrubbers capturing allergens, mold spores, and airborne particulates.', + 'Smart Thermostat & Zoning Systems': 'Multi-room temperature zoning that stops hot second-floor rooms forever.', + 'Commercial Walk-In & Rooftop HVAC': 'Heavy commercial packaged unit diagnostics and refrigeration service with guaranteed 4-hour SLA.', + }, + heroHeadline: 'Instant Relief & Flawless Climate Control', + heroSubheadline: 'Certified master technicians with fully stocked service vans ready for 24/7 emergency dispatch. Zero overtime fees.', + stats: [ + { id: 'stat-h1', value: '4,000+', label: 'AC & Heat Systems Installed', subtext: 'Whisper-quiet comfort' }, + { id: 'stat-h2', value: '100%', label: 'Same-Day Cooling Guarantee', subtext: 'Never sleep in heat' }, + { id: 'stat-h3', value: '18 Yrs', label: 'NATE Certified Technicians', subtext: 'Factory trained' }, + { id: 'stat-h4', value: '$0', label: 'Weekend or Overtime Fees', subtext: 'Flat-rate pricing' }, + ], + pillars: [ + { + id: 'pil-h1', + title: 'Fully Stocked Mobile Warehouses', + description: 'Our trucks carry over 90% of all universal fan motors, capacitors, and contactors to fix your AC on the first visit.', + iconName: 'wrench', + }, + { + id: 'pil-h2', + title: 'Upfront Transparent Flat-Rate Pricing', + description: 'You approve the exact price before our technician turns a single screwdriver. No hourly runaway bills.', + iconName: 'tag', + }, + { + id: 'pil-h3', + title: '10-Year Parts & Labor Warranties', + description: 'We stand behind every system installation with our own labor guarantee in addition to factory parts coverage.', + iconName: 'shield-check', + }, + ], + reviews: [ + { + id: 'rev-h1', + author: 'Bradley Cooper', + location: 'Scottsdale, AZ', + rating: 5, + quote: 'AC died when it was 114 degrees outside. Summit had a tech at our front door in 40 minutes and cold air blowing within an hour.', + serviceMentioned: 'Emergency AC Repair', + date: '2 weeks ago', + }, + { + id: 'rev-h2', + author: 'Jessica Miller', + location: 'Scottsdale, AZ', + rating: 5, + quote: 'Installed a new variable-speed Lennox system. Our power bill dropped 35% and the upstairs is finally the same temp as downstairs.', + serviceMentioned: 'AC Replacement', + date: 'Last month', + }, + { + id: 'rev-h3', + author: 'Ken Washington', + location: 'Scottsdale, AZ', + rating: 5, + quote: 'Honest company. The other contractor told me I needed a whole new unit, but Summit simply replaced a capacitor for a fraction of the price.', + serviceMentioned: 'System Diagnostics', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Need Emergency AC Repair or a Free System Quote?', + ctaBannerDesc: 'Our technicians are rolling across the valley right now. Call immediately for zero-overtime emergency dispatch.', + heroImageUrl: '/images/hvac/hero.jpg', + serviceImages: { + '24/7 Emergency AC Diagnostic & Repair': '/images/hvac/ac_repair.jpg', + 'High-SEER2 Heat Pump & AC Replacement': '/images/hvac/heat_pump.jpg', + 'Smart Thermostat & Zoning Systems': '/images/hvac/smart_thermostat.jpg', + }, + }, + 'plumbing': { + id: 'plumbing', + name: 'Plumbing & Drain Services', + sampleCompany: 'FlowMaster Precision Plumbing', + samplePhone: '(512) 555-0182', + sampleAddress: '800 Congress Ave', + sampleCity: 'Austin', + sampleState: 'TX', + sampleZip: '78701', + sampleTagline: 'Emergency Drain Clearing, Tankless Water Heaters & Pipe Relining', + defaultTheme: 'titan-navy', + defaultServices: [ + '24/7 Emergency Burst Pipe & Leak Repair', + 'Hydro-Jetting & Clogged Drain Snaking', + 'Tankless Water Heater Installation', + 'Sewer HD Camera Video Scoping', + 'Trenchless Epoxy Pipe Relining', + 'Whole-Home Water Filtration Systems', + ], + serviceDescriptions: { + '24/7 Emergency Burst Pipe & Leak Repair': 'Rapid dispatch for burst pipes, slab leaks, and overflowing fixtures with immediate emergency water shutoff.', + 'Hydro-Jetting & Clogged Drain Snaking': 'High-pressure 4000 PSI water blasting removing heavy grease, mineral scale, and tree roots from sewer lines.', + 'Tankless Water Heater Installation': 'Endless hot water with Navien and Rinnai high-efficiency units saving up to 40% on utility bills.', + 'Sewer HD Camera Video Scoping': 'High-definition digital video scoping pinpointing line breaks, bellies, and root blockages with zero yard excavation.', + 'Trenchless Epoxy Pipe Relining': 'Epoxy pipe lining that cures inside damaged lines, restoring pipe integrity without digging up driveways.', + 'Whole-Home Water Filtration Systems': 'Multi-stage carbon and water softener installations delivering bottled-quality water to every tap.', + }, + heroHeadline: 'Master Plumbers Ready for Any Emergency', + heroSubheadline: 'Licensed, background-checked plumbers with transparent upfront pricing and rapid same-day response.', + stats: [ + { id: 'stat-pl1', value: '5,000+', label: 'Drains & Pipes Cleared', subtext: 'Flow guaranteed' }, + { id: 'stat-pl2', value: '100%', label: 'Leak-Free Guarantee', subtext: 'Fixed right first time' }, + { id: 'stat-pl3', value: '20 Yrs', label: 'Master Plumber License', subtext: 'Fully insured' }, + { id: 'stat-pl4', value: '< 30m', label: 'Average Emergency Dispatch', subtext: 'Rapid arrival' }, + ], + pillars: [ + { + id: 'pil-pl1', + title: 'True 24/7 Live Emergency Dispatch', + description: 'A live human answers your call at 3:00 AM and dispatches an on-call master plumber immediately.', + iconName: 'clock', + }, + { + id: 'pil-pl2', + title: 'No-Dig Trenchless Solutions', + description: 'We repair and replace sewer lines from the inside using epoxy sleeves, protecting your driveway and lawn.', + iconName: 'wrench', + }, + { + id: 'pil-pl3', + title: 'Clean Shoe Covers & Floor Mats', + description: 'Our technicians wear protective booties and lay drop cloths to protect your flooring at all times.', + iconName: 'shield-check', + }, + ], + reviews: [ + { + id: 'rev-pl1', + author: 'Daniel Craig', + location: 'Austin, TX', + rating: 5, + quote: 'Main sewer line backed up on a holiday weekend. FlowMaster arrived with a hydro-jetting rig and cleared it in under an hour.', + serviceMentioned: 'Hydro-Jetting', + date: '2 weeks ago', + }, + { + id: 'rev-pl2', + author: 'Samantha Wells', + location: 'Austin, TX', + rating: 5, + quote: 'Swapped our old 50-gallon tank for a tankless heater. Unlimited hot water and our gas bill went down significantly.', + serviceMentioned: 'Tankless Installation', + date: 'Last month', + }, + { + id: 'rev-pl3', + author: 'Kevin Patel', + location: 'Austin, TX', + rating: 5, + quote: 'Found a hidden slab leak using acoustic listening devices without punching random holes in our floor. Superb!', + serviceMentioned: 'Leak Detection', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Dealing with a Plumbing Emergency Right Now?', + ctaBannerDesc: 'Don’t wait for costly water damage. Call our emergency hotline right now for immediate technician dispatch.', + heroImageUrl: '/images/plumbing/hero.jpg', + serviceImages: { + '24/7 Emergency Burst Pipe & Leak Repair': '/images/plumbing/pipe_repair.jpg', + 'Hydro-Jetting & Clogged Drain Snaking': '/images/plumbing/hydro_jetting.jpg', + 'Tankless Water Heater Installation': '/images/plumbing/tankless_heater.jpg', + }, + }, + 'medical-legal': { + id: 'medical-legal', + name: 'Aesthetic Clinic, Dental or Legal Practice', + sampleCompany: 'Aura Premier Medical & Wellness', + samplePhone: '(310) 555-0919', + sampleAddress: '9600 Wilshire Blvd', + sampleCity: 'Beverly Hills', + sampleState: 'CA', + sampleZip: '90212', + sampleTagline: 'Board-Certified Excellence, Concierge Care & State-of-the-Art Results', + defaultTheme: 'obsidian-gold', + defaultServices: [ + 'Private Concierge Consultations', + 'Artisan Non-Surgical Facial Rejuvenation', + 'Advanced Fractional Laser Aesthetics', + 'Cellular Longevity & NAD+ IV Infusions', + 'Precision Biomarker Hormone Wellness', + 'Discreet VIP Recovery Accommodations', + ], + serviceDescriptions: { + 'Private Concierge Consultations': 'One-on-one comprehensive diagnostic assessments tailored to your personal goals and lifestyle.', + 'Artisan Non-Surgical Facial Rejuvenation': 'FDA-cleared neuromodulators and dermal fillers administered with artistic restraint and natural facial symmetry.', + 'Advanced Fractional Laser Aesthetics': 'Laser skin resurfacing, pigment correction, and collagen induction with minimal to zero downtime.', + 'Cellular Longevity & NAD+ IV Infusions': 'High-dose antioxidant infusions and cellular longevity cocktails restoring mitochondrial vitality.', + 'Precision Biomarker Hormone Wellness': 'Comprehensive blood chemistry panels, bioidentical hormone replacement, and metabolic optimization.', + 'Discreet VIP Recovery Accommodations': 'Private rear entrance, dedicated aftercare nurses, and maximum patient confidentiality.', + }, + heroHeadline: 'Excellence in Care, Elevated by Distinction', + heroSubheadline: 'Board-certified specialists delivering personalized, high-touch clinical care in a serene, state-of-the-art sanctuary.', + stats: [ + { id: 'stat-m1', value: '$25M+', label: 'Clinical Portfolio Curated', subtext: 'Discreet excellence' }, + { id: 'stat-m2', value: '100%', label: 'Patient Confidentiality', subtext: 'VIP private access' }, + { id: 'stat-m3', value: '15+ Yrs', label: 'Board-Certified Specialists', subtext: 'Industry vanguard' }, + { id: 'stat-m4', value: '5.0★', label: 'Concierge Patient Rating', subtext: 'Unmatched standards' }, + ], + pillars: [ + { + id: 'pil-m1', + title: 'Unrivaled Medical Discretion', + description: 'Private parking, private elevators, and seamless one-on-one appointments ensuring complete personal privacy.', + iconName: 'shield', + }, + { + id: 'pil-m2', + title: 'Natural, Balanced Aesthetics', + description: 'We believe in subtle refinement that honors your natural facial architecture, avoiding overdone or unnatural results.', + iconName: 'sparkles', + }, + { + id: 'pil-m3', + title: 'Cutting-Edge FDA Technologies', + description: 'Equipped with next-generation lasers and diagnostic devices delivering clinical efficacy with minimal recovery time.', + iconName: 'award', + }, + ], + reviews: [ + { + id: 'rev-m1', + author: 'Victoria Laurent', + location: 'Beverly Hills, CA', + rating: 5, + quote: 'The level of care, discretion, and artistry at Aura is simply world-class. My results are stunning yet completely natural.', + serviceMentioned: 'Facial Rejuvenation', + date: '2 weeks ago', + }, + { + id: 'rev-m2', + author: 'Julian Thorne', + location: 'Beverly Hills, CA', + rating: 5, + quote: 'NAD+ IV therapy restored my focus and energy after months of heavy travel. The private suites are exceptionally tranquil.', + serviceMentioned: 'Longevity Infusions', + date: 'Last month', + }, + { + id: 'rev-m3', + author: 'Claire Montrose', + location: 'Beverly Hills, CA', + rating: 5, + quote: 'Dr. Sterling and the medical team listen attentively and never push unnecessary procedures. A true center of medical integrity.', + serviceMentioned: 'Concierge Consultation', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Experience Elevated Wellness & Personalized Care', + ctaBannerDesc: 'Contact our private concierge team to reserve your confidential assessment and customized treatment roadmap.', + heroImageUrl: '/images/medical-legal/hero.jpg', + serviceImages: { + 'Private Concierge Consultations': '/images/medical-legal/hero.jpg', + 'Artisan Non-Surgical Facial Rejuvenation': '/images/medical-legal/facial_rejuvenation.jpg', + 'Advanced Fractional Laser Aesthetics': '/images/medical-legal/laser_treatment.jpg', + 'Cellular Longevity & NAD+ IV Infusions': '/images/medical-legal/wellness_infusion.jpg', + }, + }, + 'electrical': { + id: 'electrical', + name: 'Electrical, EV Chargers & Smart Power', + sampleCompany: 'VoltCraft Master Electricians', + samplePhone: '(818) 555-0427', + sampleAddress: '15300 Ventura Blvd', + sampleCity: 'Sherman Oaks', + sampleState: 'CA', + sampleZip: '91403', + sampleTagline: 'Certified Master Electricians, EV Charging & Smart Home Power Systems', + defaultTheme: 'obsidian-gold', + defaultServices: [ + 'Level 2 EV Home Charger Installation', + '200-Amp Main Electrical Panel Upgrade', + 'Whole-Home Backup Generator Systems', + 'Recessed Smart LED Architectural Lighting', + 'Knob & Tube / Aluminum Wiring Rewire', + 'Commercial Electrical & Tenant Improvements', + ], + serviceDescriptions: { + 'Level 2 EV Home Charger Installation': 'Tesla Wall Connector, ChargePoint, and universal 48A high-speed home charging stations with city permitting.', + '200-Amp Main Electrical Panel Upgrade': 'Modern Square D and Siemens 200A breaker panels replacing obsolete, hazardous Zinsco and Federal Pacific panels.', + 'Whole-Home Backup Generator Systems': 'Automatic Generac standby generators with smart transfer switches keeping your power on through any grid blackout.', + 'Recessed Smart LED Architectural Lighting': 'Warm dimming recessed LED ceiling illumination, under-cabinet lighting, and Lutron Caséta smartphone control.', + 'Knob & Tube / Aluminum Wiring Rewire': 'Full code-compliant copper rewiring and arc-fault AFCI circuit breaker upgrades protecting homes from electrical fires.', + 'Commercial Electrical & Tenant Improvements': '3-phase power distribution, commercial lighting retrofits, dedicated machinery circuits, and safety inspections.', + }, + heroHeadline: 'Master Electricians Delivering Safe, Flawless Power', + heroSubheadline: 'Licensed electrical contractors specializing in rapid panel upgrades, Tesla EV charger stations, and smart home automation.', + stats: [ + { id: 'stat-e1', value: '6,200+', label: 'Electrical Upgrades Completed', subtext: 'Safe & certified' }, + { id: 'stat-e2', value: '100%', label: 'Code Compliance Guarantee', subtext: 'Passes first inspection' }, + { id: 'stat-e3', value: '25 Yrs', label: 'Master Electrician Experience', subtext: 'Licensed & bonded' }, + { id: 'stat-e4', value: '24/7', label: 'Emergency Power Dispatch', subtext: 'Always ready' }, + ], + pillars: [ + { + id: 'pil-e1', + title: 'Zero-Fail Safety Standards', + description: 'Every project is inspected with thermal imaging cameras and Megger testing to guarantee zero hidden fire hazards.', + iconName: 'shield-check', + }, + { + id: 'pil-e2', + title: 'Guaranteed Permit Approval', + description: 'We handle all city building department permits, utility coordination, and inspections from start to finish.', + iconName: 'award', + }, + { + id: 'pil-e3', + title: 'Upfront Flat-Rate Pricing', + description: 'No hidden hourly surprises. You get a clear, guaranteed quote before any work begins.', + iconName: 'check-circle', + }, + ], + reviews: [ + { + id: 'rev-e1', + author: 'Gregory Miller', + location: 'Sherman Oaks, CA', + rating: 5, + quote: 'Upgraded our 100A panel to 200A and installed a Tesla Wall Connector in our garage in a single day. The city inspector passed it on the spot!', + serviceMentioned: 'Panel Upgrade & EV Charger', + date: '2 weeks ago', + }, + { + id: 'rev-e2', + author: 'Evelyn Hayes', + location: 'Sherman Oaks, CA', + rating: 5, + quote: 'Replaced all ancient incandescent lighting with warm dimmable recessed LEDs and Lutron smart switches. Our living room looks like an architectural magazine.', + serviceMentioned: 'Smart LED Lighting', + date: 'Last month', + }, + { + id: 'rev-e3', + author: 'Marcus Thorne', + location: 'Sherman Oaks, CA', + rating: 5, + quote: 'Power went out in half our house on a Sunday night. VoltCraft arrived in 40 minutes, traced a damaged junction box, and restored power safely.', + serviceMentioned: 'Emergency Power Restoration', + date: '3 weeks ago', + }, + ], + ctaBannerTitle: 'Ready to Upgrade Your Electrical Panel or Install an EV Charger?', + ctaBannerDesc: 'Contact VoltCraft today for a free in-home load calculation and same-week installation scheduling.', + heroImageUrl: '/images/electrical/hero.jpg', + serviceImages: { + 'Level 2 EV Home Charger Installation': '/images/electrical/ev_charger.jpg', + '200-Amp Main Electrical Panel Upgrade': '/images/electrical/panel_upgrade.jpg', + 'Recessed Smart LED Architectural Lighting': '/images/electrical/smart_lighting.jpg', + }, + }, +}; + +// Helper: Match or find industry preset +export function findIndustryPreset(industryOrTrade: string): IndustryPreset | null { + const t = industryOrTrade.toLowerCase(); + if (t.includes('pool') || t.includes('spa') || t.includes('chlorin')) return INDUSTRY_PRESETS['pool-service']; + if (t.includes('landscap') || t.includes('lawn') || t.includes('garden') || t.includes('yard') || t.includes('mow') || t.includes('tree')) return INDUSTRY_PRESETS['landscaping']; + if (t.includes('roof') || t.includes('shingle') || t.includes('gutter')) return INDUSTRY_PRESETS['roofing']; + if (t.includes('hvac') || t.includes('air condition') || t.includes('cool') || t.includes('heat') || t.includes('furnace')) return INDUSTRY_PRESETS['hvac']; + if (t.includes('plumb') || t.includes('drain') || t.includes('pipe') || t.includes('water heater') || t.includes('sewer')) return INDUSTRY_PRESETS['plumbing']; + if (t.includes('clinic') || t.includes('medical') || t.includes('doctor') || t.includes('dental') || t.includes('dentist') || t.includes('aesthetic')) return INDUSTRY_PRESETS['medical-legal']; + if (t.includes('electric') || t.includes('wire') || t.includes('ev') || t.includes('charger') || t.includes('panel') || t.includes('power')) return INDUSTRY_PRESETS['electrical']; + return null; +} + +// Generate Full Structured Website Blueprint from Business Profile +export function generateWebsiteBlueprint(profile: BusinessProfile): WebsiteSection[] { + const cityState = profile.city && profile.state ? `${profile.city}, ${profile.state}` : (profile.city || profile.address || 'Local Community'); + const company = profile.companyName || 'Your Business Name'; + const phone = profile.phone || '(555) 000-0000'; + const tagline = profile.tagline || 'Excellence, Integrity & Unrivaled Local Service'; + const years = profile.yearsInBusiness || '10+'; + + const preset = findIndustryPreset(profile.industry || profile.companyName); + + // Format Services + const activeServices: ServiceItem[] = (profile.services.length > 0 ? profile.services : [ + 'Primary Service Consultation', + 'Complete Maintenance & Repair', + 'Emergency On-Site Response', + 'Quality Inspection & Warranties', + ]).map((s, idx) => { + let desc = `Comprehensive, high-grade ${s.toLowerCase()} tailored for your specific needs with guaranteed quality and workmanship.`; + if (preset && preset.serviceDescriptions[s]) { + desc = preset.serviceDescriptions[s]; + } else { + for (const p of Object.values(INDUSTRY_PRESETS)) { + if (p.serviceDescriptions[s]) { + desc = p.serviceDescriptions[s]; + break; + } + } + } + const icons = ['wrench', 'shield-check', 'sparkles', 'clock', 'star', 'check-circle']; + let svcImg = preset?.serviceImages?.[s]; + if (!svcImg && preset?.serviceImages) { + const lower = s.toLowerCase(); + for (const [key, imgUrl] of Object.entries(preset.serviceImages)) { + const keyLower = key.toLowerCase(); + if (lower.includes(keyLower) || keyLower.includes(lower) || + (lower.includes('water') && keyLower.includes('water')) || + (lower.includes('pump') && keyLower.includes('pump')) || + (lower.includes('tile') && keyLower.includes('tile')) || + (lower.includes('paver') && keyLower.includes('paver')) || + (lower.includes('patio') && keyLower.includes('patio')) || + (lower.includes('xeriscape') && keyLower.includes('xeriscape')) || + (lower.includes('plant') && keyLower.includes('xeriscape')) || + (lower.includes('light') && keyLower.includes('light')) || + (lower.includes('ac') && keyLower.includes('ac')) || + (lower.includes('cool') && keyLower.includes('ac')) || + (lower.includes('heat') && keyLower.includes('heat')) || + (lower.includes('thermostat') && keyLower.includes('thermostat')) || + (lower.includes('roof') && keyLower.includes('shingle')) || + (lower.includes('shingle') && keyLower.includes('shingle')) || + (lower.includes('drone') && keyLower.includes('drone')) || + (lower.includes('gutter') && keyLower.includes('gutter')) || + (lower.includes('burst') && keyLower.includes('pipe')) || + (lower.includes('pipe') && keyLower.includes('pipe')) || + (lower.includes('jet') && keyLower.includes('jet')) || + (lower.includes('drain') && keyLower.includes('drain')) || + (lower.includes('tankless') && keyLower.includes('tankless')) || + (lower.includes('water heater') && keyLower.includes('tankless')) || + (lower.includes('facial') && keyLower.includes('facial')) || + (lower.includes('laser') && keyLower.includes('laser')) || + (lower.includes('infusion') && keyLower.includes('infusion')) || + (lower.includes('iv') && keyLower.includes('infusion')) || + (lower.includes('ev') && keyLower.includes('charger')) || + (lower.includes('charger') && keyLower.includes('charger')) || + (lower.includes('panel') && keyLower.includes('panel')) || + (lower.includes('amp') && keyLower.includes('panel')) || + (lower.includes('lighting') && keyLower.includes('lighting')) + ) { + svcImg = imgUrl; + break; + } + } + + // Fallback: if no keyword match, cycle through available trade images so every card has a photo! + if (!svcImg) { + const availablePresetImages = Object.values(preset.serviceImages); + if (availablePresetImages.length > 0) { + svcImg = availablePresetImages[idx % availablePresetImages.length]; + } else if (preset.heroImageUrl) { + svcImg = preset.heroImageUrl; + } + } + } + + return { + id: `service-${idx + 1}`, + title: s, + description: desc, + iconName: icons[idx % icons.length], + popular: idx === 0 || idx === 1, + imageUrl: svcImg, + }; + }); + + // Tailored Hero Headlines + const heroHeadline = preset ? preset.heroHeadline : `${company} — Trusted Service in ${cityState}`; + const heroSubheadline = preset ? preset.heroSubheadline : `${tagline}. Serving residential & commercial clients with licensed master technicians, transparent pricing, and unmatched reliability.`; + + // Tailored Stats + const statsList = preset ? preset.stats : [ + { id: 'stat-1', value: '1,500+', label: 'Projects Completed', subtext: 'Flawlessly executed' }, + { id: 'stat-2', value: '100%', label: 'Satisfaction Guaranteed', subtext: 'We never cut corners' }, + { id: 'stat-3', value: `${years} Yrs`, label: 'Local Experience', subtext: 'Proudly serving the area' }, + { id: 'stat-4', value: '< 1hr', label: 'Average Response Time', subtext: 'Rapid dispatch' }, + ]; + + // Tailored Pillars + const pillarsList = preset ? preset.pillars : [ + { + id: 'pillar-1', + title: 'Upfront & Transparent Pricing', + description: 'No hidden surprise fees or bait-and-switch estimates. You approve the exact scope before work starts.', + iconName: 'tag', + }, + { + id: 'pillar-2', + title: 'Master Certified Technicians', + description: 'Background-checked, continually trained, and fully insured specialists who treat your property like their own.', + iconName: 'award', + }, + { + id: 'pillar-3', + title: 'Ironclad Workmanship Warranty', + description: 'Every job is backed by our direct company guarantee. If something is not right, we return and fix it free of charge.', + iconName: 'shield', + }, + ]; + + // Tailored Reviews + const reviewsList = preset ? preset.reviews : [ + { + id: 'rev-1', + author: 'Marcus Vance', + location: cityState, + rating: 5, + quote: `${company} exceeded our highest expectations. Their team was polite, prompt, and diagnosed our issue in minutes. Outstanding communication!`, + serviceMentioned: activeServices[0]?.title || 'Standard Service', + date: '2 weeks ago', + }, + { + id: 'rev-2', + author: 'Elena Rostova', + location: cityState, + rating: 5, + quote: `Finding honest contractors is tough, but ${company} is the gold standard. Fair pricing, immaculate cleanup, and the job was completed ahead of schedule.`, + serviceMentioned: activeServices[1]?.title || 'Full Maintenance', + date: 'Last month', + }, + { + id: 'rev-3', + author: 'David & Sarah Jenkins', + location: cityState, + rating: 5, + quote: `Called their emergency line on a weekend and someone arrived within 45 minutes. Saved us thousands in potential damage. We won't use anyone else!`, + serviceMentioned: profile.emergencyService ? 'Emergency Dispatch' : 'Specialized Repair', + date: '3 weeks ago', + }, + ]; + + const ctaTitle = preset ? preset.ctaBannerTitle : 'Ready to Experience the Difference?'; + const ctaDesc = preset ? preset.ctaBannerDesc : `Join hundreds of satisfied property owners in ${cityState}. Call now to speak directly with an on-duty specialist or lock in your estimate.`; + + return [ + // Section 0: Header & Navigation + { + id: 'sec-header', + sectionNumber: 0, + type: 'header', + title: 'Header & Navigation', + badge: 'Preformatted Responsive Navigation', + visible: true, + content: { + headerVariant: 'emergency-trade', + showTopBar: true, + topBarAnnouncement: profile.emergencyService + ? '24/7 Rapid Emergency Response Available • Guaranteed On-Time Arrival' + : `Top-Rated ${profile.industry || 'Trade'} Specialists in ${cityState} • Free Estimates`, + licenseBadge: 'CA Lic #948201 • Bonded & Insured', + serviceAreaText: `Serving ${cityState} & Nearby`, + headerCtaText: profile.emergencyService ? 'Emergency Dispatch' : 'Get Free Quote', + navLinks: [ + { label: 'Services', href: '#services' }, + { label: 'Why Us', href: '#why-us' }, + { label: 'Reviews', href: '#reviews' }, + { label: 'Free Estimate', href: '#quote' }, + ], + }, + }, + + // Section 1: Hero + { + id: 'sec-hero', + sectionNumber: 1, + type: 'hero', + title: 'Hero Banner & Value Proposition', + badge: 'First Impression & High-Conversion Hook', + visible: true, + content: { + headline: heroHeadline, + subheadline: heroSubheadline, + primaryCtaText: 'Request Free Estimate', + secondaryCtaText: `Call Now: ${phone}`, + heroImageUrl: preset?.heroImageUrl, + trustBadges: [ + 'Licensed, Bonded & Insured', + '5.0 Star Google Rating', + `${years} Years in Business`, + profile.emergencyService ? '24/7 Rapid Emergency Response' : 'Same-Day Appointments Available', + ], + }, + }, + + // Section 2: Services Grid + { + id: 'sec-services', + sectionNumber: 2, + type: 'services', + title: 'Core Services & Solutions', + badge: 'Comprehensive Capabilities', + visible: true, + content: { + headline: 'Professional Solutions Tailored to Your Standards', + subheadline: `From routine maintenance to complex emergency projects, ${company} delivers exceptional craftsmanship backed by our 100% satisfaction guarantee.`, + servicesList: activeServices, + columns: 3, + }, + }, + + // Section 3: Why Choose Us + { + id: 'sec-why-us', + sectionNumber: 3, + type: 'why-us', + title: 'Why Choose Us & Track Record', + badge: 'Reputation & Proof of Excellence', + visible: true, + content: { + headline: `Why Homeowners & Businesses Choose ${company}`, + subheadline: 'We have built our reputation one handshake and one five-star review at a time. Here is what sets our team apart.', + stats: statsList, + pillars: pillarsList, + }, + }, + + // Section 4: Testimonials + { + id: 'sec-testimonials', + sectionNumber: 4, + type: 'testimonials', + title: 'Verified Client Reviews & Social Proof', + badge: '5-Star Google Reputation', + visible: true, + content: { + headline: 'What Our Neighbors Are Saying', + subheadline: 'Real feedback from satisfied clients who trust us with their most critical projects.', + averageRating: 5.0, + totalReviews: 128, + reviews: reviewsList, + }, + }, + + // Section 5: Quote & Lead Form + { + id: 'sec-quote-form', + sectionNumber: 5, + type: 'quote-form', + title: 'Interactive Free Estimate & Booking', + badge: 'Direct Lead Capture Engine', + visible: true, + content: { + formTitle: 'Get Your Fast, Free Estimate', + formSubtitle: 'Fill out the form below or call us directly. We respond within 15 minutes during business hours.', + fields: [ + { label: 'Your Full Name', type: 'text', placeholder: 'John Smith', required: true }, + { label: 'Phone Number', type: 'tel', placeholder: '(555) 000-0000', required: true }, + { label: 'Email Address', type: 'email', placeholder: 'john@example.com', required: false }, + { label: 'Service Needed', type: 'select', placeholder: 'Select a service', required: true }, + { label: 'Project Description / Notes', type: 'textarea', placeholder: 'Tell us about your project or timeline...', required: false }, + ], + }, + }, + + // Section 6: Urgency CTA Banner + { + id: 'sec-cta-banner', + sectionNumber: 6, + type: 'cta-banner', + title: 'High-Impact Call To Action Banner', + badge: 'Conversion Booster', + visible: true, + content: { + bannerTitle: ctaTitle, + bannerDescription: ctaDesc, + bannerCta: `Call ${phone}`, + }, + }, + + // Section 7: Footer + { + id: 'sec-footer', + sectionNumber: 7, + type: 'footer', + title: 'Company Footer & Local NAP Citation', + badge: 'GMB & SEO Citation Foundation', + visible: true, + content: { + hours: profile.emergencyService ? '24 Hours / 7 Days a Week' : 'Monday - Saturday: 7:00 AM - 6:30 PM', + copyrightText: `© ${new Date().getFullYear()} ${company}. All Rights Reserved. Licensed & Bonded.`, + links: [ + { label: 'Services', href: '#services' }, + { label: 'Why Choose Us', href: '#why-us' }, + { label: 'Customer Reviews', href: '#reviews' }, + { label: 'Get Estimate', href: '#quote' }, + ], + }, + }, + ]; +} + +// Check what onboarding information is missing +export function analyzeMissingProfileData(profile: BusinessProfile): { + isComplete: boolean; + missingFields: string[]; + suggestedQuestion: string; +} { + const missing: string[] = []; + if (!profile.companyName.trim()) missing.push('Company Name'); + if (!profile.phone.trim()) missing.push('Phone Number'); + if (!profile.city.trim() && !profile.address.trim()) missing.push('City / Service Area'); + if (profile.services.length === 0) missing.push('Primary Services'); + + if (missing.length === 0) { + return { + isComplete: true, + missingFields: [], + suggestedQuestion: `Awesome! I have your company details for **${profile.companyName}**. Would you like me to tailor the headline, add emergency hours, or should we generate your demo site right now?`, + }; + } + + let question = ''; + if (!profile.companyName.trim()) { + question = "Hello! I'm your AI Website Architect. What is your **company or business name**, and what industry are you in?"; + } else if (!profile.phone.trim()) { + question = `Great to meet you, **${profile.companyName}**! What is the best **phone number** where prospective customers can call you directly for quotes?`; + } else if (!profile.city.trim() && !profile.address.trim()) { + question = `Got it! What **city, town, or service area** does ${profile.companyName} primarily serve? (This helps me customize your local SEO and map citations!)`; + } else if (profile.services.length === 0) { + question = `Almost there! What are your **top 3 to 5 core services**? You can also click any of our one-click presets to populate them automatically!`; + } else { + question = `We have most of your details! Do you offer **24/7 emergency response**, or do you have a specific tagline you'd love on the hero banner?`; + } + + return { + isComplete: false, + missingFields: missing, + suggestedQuestion: question, + }; +} + +// Helper: Detect color & theme from text across the entire visual spectrum +function detectThemeFromText(text: string): ThemeId | null { + const t = text.toLowerCase(); + + // Purple / Violet / Magenta / Lavender / Lilac / Plum + if ( + t.includes('purple') || + t.includes('violet') || + t.includes('lavender') || + t.includes('magenta') || + t.includes('lilac') || + t.includes('plum') || + t.includes('electric') || + (t.includes('black') && (t.includes('purple') || t.includes('violet') || t.includes('magenta'))) + ) { + return 'electric-violet'; + } + + // Orange / Amber / Copper / Rust / Bronze / Sunset / Peach + if ( + t.includes('orange') || + t.includes('amber') || + t.includes('copper') || + t.includes('rust') || + t.includes('bronze') || + t.includes('tangerine') || + t.includes('sunset') || + t.includes('terracotta') || + t.includes('peach') || + (t.includes('black') && (t.includes('orange') || t.includes('amber') || t.includes('copper'))) + ) { + return 'sunset-amber'; + } + + // Teal / Cyan / Aqua / Turquoise / Seafoam + if ( + t.includes('teal') || + t.includes('cyan') || + t.includes('turquoise') || + t.includes('aqua') || + t.includes('seafoam') || + t.includes('aquamarine') || + (t.includes('black') && (t.includes('teal') || t.includes('cyan') || t.includes('aqua'))) + ) { + return 'cyber-cyan'; + } + + // Pink / Rose / Fuchsia / Hot Pink / Coral / Blush + if ( + t.includes('pink') || + t.includes('rose') || + t.includes('fuchsia') || + t.includes('coral') || + t.includes('blush') || + (t.includes('black') && (t.includes('pink') || t.includes('rose') || t.includes('fuchsia'))) + ) { + return 'neon-rose'; + } + + // Green / Emerald / Slate / Mint / Lawn / Lime / Olive + if ( + t.includes('green') || + t.includes('gren') || + t.includes('emerald') || + t.includes('mint') || + t.includes('lime') || + t.includes('foliage') || + t.includes('olive') || + t.includes('moss') || + t.includes('sage') || + t.includes('slate') || + (t.includes('black') && (t.includes('green') || t.includes('gren') || t.includes('landscap') || t.includes('nature') || t.includes('lawn'))) + ) { + return 'emerald-slate'; + } + + // Gold / Yellow / Obsidian / Luxury / Bumblebee + if ( + t.includes('yellow') || + t.includes('yelow') || + t.includes('gold') || + t.includes('golden') || + t.includes('obsidian') || + t.includes('luxury') || + t.includes('mustard') || + t.includes('honey') || + t.includes('bumblebee') || + (t.includes('black') && (t.includes('yellow') || t.includes('yelow') || t.includes('gold') || t.includes('golden') || t.includes('lux') || t.includes('vip'))) + ) { + return 'obsidian-gold'; + } + + // Blue / Navy / Azure / Cobalt / Titan / Ocean + if ( + t.includes('blue') || + t.includes('navy') || + t.includes('azure') || + t.includes('cobalt') || + t.includes('titan') || + t.includes('ocean') || + t.includes('water') + ) { + return 'titan-navy'; + } + + // Red / Crimson / Forge / Scarlet / Ruby / Burgundy + if ( + t.includes('red') || + t.includes('crimson') || + t.includes('forge') || + t.includes('scarlet') || + t.includes('ruby') || + t.includes('burgundy') || + t.includes('maroon') || + t.includes('fire') + ) { + return 'crimson-forge'; + } + + // Clean / Alpine / Indigo / Minimal / Monochrome / Silver / Gray + if ( + t.includes('alpine') || + t.includes('indigo') || + t.includes('monochrome') || + t.includes('silver') || + t.includes('gray') || + t.includes('grey') + ) { + return 'alpine-clean'; + } + + return null; +} + +// Helper: Detect industry with typo tolerance +function detectIndustryFromText(text: string): string | null { + const t = text.toLowerCase(); + if (t.includes('pool') || t.includes('spa') || t.includes('chlorin') || t.includes('acid wash')) { + return 'pool-service'; + } + if (t.includes('landscap') || t.includes('lawn') || t.includes('garden') || t.includes('mow') || t.includes('yard') || t.includes('paver') || t.includes('hardscape') || t.includes('tree')) { + return 'landscaping'; + } + if (t.includes('roof') || t.includes('shingle') || t.includes('gutter') || t.includes('tpo')) { + return 'roofing'; + } + if (t.includes('hvac') || t.includes('air condition') || t.includes('cooling') || t.includes('heating') || t.includes('furnace') || t.includes('ac repair')) { + return 'hvac'; + } + if (t.includes('plumb') || t.includes('drain') || t.includes('pipe') || t.includes('water heater') || t.includes('leak')) { + return 'plumbing'; + } + if (t.includes('clinic') || t.includes('medical') || t.includes('doctor') || t.includes('dental') || t.includes('dentist') || t.includes('wellness') || t.includes('aesthetic')) { + return 'medical-legal'; + } + if (t.includes('electric') || t.includes('wire') || t.includes('ev charge') || t.includes('panel') || t.includes('generator') || t.includes('spark') || t.includes('power')) { + return 'electrical'; + } + return null; +} + +// Helper: Smart service image matcher +export function matchServiceImage(serviceTitle: string, preset: IndustryPreset): string | undefined { + if (!preset || !preset.serviceImages) return undefined; + if (preset.serviceImages[serviceTitle]) return preset.serviceImages[serviceTitle]; + + const lower = serviceTitle.toLowerCase(); + for (const [key, imgUrl] of Object.entries(preset.serviceImages)) { + const keyLower = key.toLowerCase(); + if (lower.includes(keyLower) || keyLower.includes(lower)) return imgUrl; + } + + // Check trade-specific keywords + for (const [key, imgUrl] of Object.entries(preset.serviceImages)) { + const keyLower = key.toLowerCase(); + const keywords = keyLower.split(/[\s,&/-]+/).filter(w => w.length > 3); + for (const kw of keywords) { + if (lower.includes(kw)) return imgUrl; + } + } + + return undefined; +} + +// Deep Conversational AI Engine +export function processConversationalInstruction( + instruction: string, + currentSections: WebsiteSection[], + currentProfile: BusinessProfile +): { + updatedSections: WebsiteSection[]; + updatedProfile?: Partial; + updatedTheme?: ThemeId; + aiResponse: string; + modifiedSectionNumber?: number; +} { + const lower = instruction.toLowerCase().trim(); + const sections = JSON.parse(JSON.stringify(currentSections)) as WebsiteSection[]; + + // 1. CHECK FOR INDUSTRY OR FULL-SITE TRANSFORMATION + // Matches: "ok i need a green and black landscaping app", "change the wording to landscaping", "make for a pool company", "switch to roofing", etc. + const detectedIndustryKey = detectIndustryFromText(lower); + const detectedTheme = detectThemeFromText(lower); + const currentPreset = findIndustryPreset(currentProfile.industry || currentProfile.companyName); + + // If the detected trade differs from the current site trade, it's definitely an industry overhaul request! + const isDifferentIndustry = detectedIndustryKey !== null && (!currentPreset || detectedIndustryKey !== currentPreset.id); + + const hasIndustryIntent = ( + lower.includes('make') || + lower.includes('need') || + lower.includes('want') || + lower.includes('build') || + lower.includes('create') || + lower.includes('app') || + lower.includes('site') || + lower.includes('website') || + lower.includes('wording') || + lower.includes('copy') || + lower.includes('text') || + lower.includes('company') || + lower.includes('business') || + lower.includes('for') || + lower.includes('instead') || + lower.includes('switch') || + lower.includes('change') || + lower.includes('turn into') || + lower.includes('entire') || + lower.includes('whole') || + lower.includes('we are') || + lower.includes('we do') || + isDifferentIndustry || + lower.split(/\s+/).length <= 4 + ); + + const isIndustryOverhaul = detectedIndustryKey !== null && hasIndustryIntent; + + if (isIndustryOverhaul && detectedIndustryKey) { + const preset = INDUSTRY_PRESETS[detectedIndustryKey]; + const newTheme = detectedTheme || preset.defaultTheme; + + // Check if user specified an explicit company name in their prompt (e.g. "make a pool site for Blue Wave Pools") + let newCompanyName = preset.sampleCompany; + const forNameMatch = instruction.match(/(?:for|called|named)\s+([A-Z][a-zA-Z0-9\s&]+?)(?:\s+(?:instead|company|site|in)|\s*$)/); + if (forNameMatch && forNameMatch[1] && forNameMatch[1].trim().length > 3 && !forNameMatch[1].toLowerCase().includes('pool') && !forNameMatch[1].toLowerCase().includes('roof')) { + newCompanyName = forNameMatch[1].trim(); + } + + const newProfile: BusinessProfile = { + ...currentProfile, + companyName: newCompanyName, + industry: preset.name, + tagline: preset.sampleTagline, + services: [...preset.defaultServices], + theme: newTheme, + }; + + // Regenerate full structured site blueprint for this trade + const regeneratedSections = generateWebsiteBlueprint(newProfile); + + return { + updatedSections: regeneratedSections, + updatedProfile: newProfile, + updatedTheme: newTheme, + aiResponse: `✨ **Completely transformed your site for ${preset.name}!**\n\n- **Company Name**: Updated to **${newCompanyName}**\n- **Visual Theme**: Switched to **${THEMES[newTheme].name}**\n- **Hero Headline**: "${preset.heroHeadline}"\n- **Services**: All 6 services refreshed with ${preset.name} solutions\n- **Why Choose Us**: Updated with specialized industry metrics, warranties, and certifications\n- **Verified Reviews**: 5-star testimonials tailored directly to ${preset.name}.`, + modifiedSectionNumber: 1, + }; + } + + // 1.5. CHECK FOR DARK MODE / LIGHT MODE REQUESTS + // Matches: "switch to light mode", "make it light", "light theme", "bright mode", "dark mode", "switch to dark", etc. + const isLightModeRequest = ( + lower.includes('light mode') || + lower.includes('light theme') || + lower.includes('switch to light') || + lower.includes('make it light') || + lower.includes('change to light') || + lower.includes('turn into light') || + lower.includes('white background') || + lower.includes('bright mode') || + lower.includes('day mode') || + lower === 'light' + ); + const isDarkModeRequest = ( + lower.includes('dark mode') || + lower.includes('dark theme') || + lower.includes('switch to dark') || + lower.includes('make it dark') || + lower.includes('change to dark') || + lower.includes('turn into dark') || + lower.includes('black background') || + lower.includes('night mode') || + lower.includes('obsidian mode') || + lower === 'dark' + ); + + if (isLightModeRequest || isDarkModeRequest) { + const targetMode: ColorMode = isLightModeRequest ? 'light' : 'dark'; + const targetTheme: ThemeId | undefined = detectedTheme || undefined; + const newProfileUpdate: Partial = { + colorMode: targetMode, + ...(targetTheme ? { theme: targetTheme } : {}) + }; + + return { + updatedSections: sections, + updatedTheme: targetTheme, + updatedProfile: newProfileUpdate, + aiResponse: targetMode === 'light' + ? `☀️ **Switched to Crisp Light Mode!**\n\nYour entire homepage has transitioned to a clean, executive light aesthetic with high-contrast slate typography, pristine white cards, subtle border elevations, and vibrant brand accents${targetTheme ? ` in **${THEMES[targetTheme].name}**` : ''}.` + : `🌙 **Switched to Executive Dark Mode!**\n\nYour homepage has transitioned to obsidian dark styling with luminous glassmorphism cards, deep navy/obsidian canvases, and vivid glowing accents${targetTheme ? ` in **${THEMES[targetTheme].name}**` : ''}.`, + modifiedSectionNumber: 1, + }; + } + + // 2. CHECK FOR STANDALONE THEME / COLOR CHANGES + // Matches: "theme black and yellow", "changing to black and yellow didnt work", "switch theme to gold", "make it blue", "purple", "neon rose", etc. + const isExplicitColorOrThemePrompt = ( + Boolean(detectedTheme) || + lower.includes('theme') || + lower.includes('color') || + lower.includes('colour') || + lower.includes('palette') || + lower.includes('styling') || + lower.includes('shade') || + lower.includes('hues') + ); + + if (isExplicitColorOrThemePrompt) { + const targetTheme: ThemeId = detectedTheme || 'obsidian-gold'; + return { + updatedSections: sections, + updatedTheme: targetTheme, + updatedProfile: { theme: targetTheme }, + aiResponse: `🎨 **Updated Theme to ${THEMES[targetTheme].name}!**\n\nThe ${THEMES[targetTheme].badge} color scheme, glowing accents, and typography styles have been applied across your entire homepage canvas.`, + modifiedSectionNumber: 1, + }; + } + + // 3. CHECK FOR PHONE NUMBER / CONTACT UPDATES + const phoneMatch = instruction.match(/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/); + if (phoneMatch && (lower.includes('phone') || lower.includes('number') || lower.includes('call'))) { + const newPhone = phoneMatch[0]; + sections.forEach(sec => { + if (sec.type === 'hero' && sec.content.secondaryCtaText) { + sec.content.secondaryCtaText = `Call Now: ${newPhone}`; + } + if (sec.type === 'cta-banner' && sec.content.bannerCta) { + sec.content.bannerCta = `Call ${newPhone}`; + } + }); + + return { + updatedSections: sections, + updatedProfile: { phone: newPhone }, + aiResponse: `📞 **Updated Phone Number to ${newPhone}** across your header, hero, urgency banner, and footer!`, + }; + } + + // 3b. CHECK FOR IMAGE / PHOTOGRAPHY / VISUAL ASSET INSTRUCTIONS + // Matches: "add images", "add photos", "include pictures", "put images on services", "add an image to section 1", "remove images", "why no images", "show pictures", etc. + const isImageRequest = ( + lower.includes('image') || + lower.includes('photo') || + lower.includes('picture') || + lower.includes('thumbnail') || + lower.includes('stock img') || + lower.includes('visual') + ); + + if (isImageRequest) { + // 1. Check if user wants to remove/hide images + const isRemoval = lower.includes('remove') || lower.includes('delete') || lower.includes('hide') || lower.includes('without') || lower.includes('no image'); + if (isRemoval) { + sections.forEach(sec => { + if (sec.type === 'hero') { + delete sec.content.heroImageUrl; + } + if (sec.type === 'services' && sec.content.servicesList) { + sec.content.servicesList.forEach(svc => { + delete svc.imageUrl; + }); + } + }); + return { + updatedSections: sections, + aiResponse: `🧹 **Removed Images from Canvas**\n\nAll stock photography has been stripped from your Hero showcase and service cards for a clean minimalist text presentation.`, + modifiedSectionNumber: 1, + }; + } + + // 2. Otherwise, user wants to ADD, UPDATE, or ATTACH images! + // Smart trade detection: + // Check explicit industry in prompt, or check if theme was shifted (e.g. emerald-slate means landscaping!) + let targetIndustryKey = detectedIndustryKey; + if (!targetIndustryKey && currentProfile.theme === 'emerald-slate' && currentProfile.industry.toLowerCase().includes('pool')) { + targetIndustryKey = 'landscaping'; + } + if (!targetIndustryKey && currentProfile.theme === 'crimson-forge' && !currentProfile.industry.toLowerCase().includes('roof')) { + targetIndustryKey = 'roofing'; + } + + const preset = (targetIndustryKey && INDUSTRY_PRESETS[targetIndustryKey]) || + findIndustryPreset(currentProfile.industry || currentProfile.companyName || '') || + INDUSTRY_PRESETS['pool-service']; + + // If target trade is different from current profile trade, overhaul the entire site to match! + if (targetIndustryKey && currentPreset && targetIndustryKey !== currentPreset.id) { + const newProfile: BusinessProfile = { + ...currentProfile, + companyName: preset.sampleCompany, + industry: preset.name, + tagline: preset.sampleTagline, + services: [...preset.defaultServices], + theme: detectedTheme || preset.defaultTheme, + }; + const regeneratedSections = generateWebsiteBlueprint(newProfile); + return { + updatedSections: regeneratedSections, + updatedProfile: newProfile, + updatedTheme: newProfile.theme, + aiResponse: `📸 **Injected Commercial Stock Photography for ${preset.name}!**\n\n- **Hero Showcase**: Attached certified high-resolution **${preset.name}** hero photo with verified technician pulse card.\n- **Service Cards**: Photographic headers attached across all 6 ${preset.name} cards.\n- **Copy & Branding**: Fully synchronized for **${preset.sampleCompany}** with trade-tailored copy.`, + modifiedSectionNumber: 1, + }; + } + + const heroSec = sections.find(s => s.type === 'hero' || s.sectionNumber === 1); + const servicesSec = sections.find(s => s.type === 'services' || s.sectionNumber === 2); + + // Check if targeting only Hero or Section 1 + const isOnlyHero = ( + lower.includes('section 1') || + lower.includes('sec 1') || + lower.includes('hero') || + lower.includes('banner') || + lower.includes('top image') + ) && !lower.includes('service') && !lower.includes('section 2'); + + // Check if targeting only Services or Section 2 + const isOnlyServices = ( + lower.includes('section 2') || + lower.includes('sec 2') || + lower.includes('service') || + lower.includes('card') || + lower.includes('grid') + ) && !lower.includes('hero') && !lower.includes('section 1'); + + // Apply to Hero if requested or general "add images" + if (!isOnlyServices && heroSec) { + heroSec.content.heroImageUrl = preset.heroImageUrl; + } + + // Apply to Services if requested or general "add images" + if (!isOnlyHero && servicesSec && servicesSec.content.servicesList) { + const presetServiceImages = Object.values(preset.serviceImages || {}); + servicesSec.content.servicesList.forEach((svc, idx) => { + const matched = matchServiceImage(svc.title, preset); + if (matched) { + svc.imageUrl = matched; + } else if (presetServiceImages.length > 0) { + svc.imageUrl = presetServiceImages[idx % presetServiceImages.length]; + } else { + svc.imageUrl = preset.heroImageUrl; + } + }); + } + + let responseMsg = ''; + if (isOnlyHero) { + responseMsg = `📸 **Added Hero Showcase Photography!**\n\n- Injected certified high-resolution **${preset.name}** hero photo.\n- Activated live **Verified Master Tech On-Duty** pulse card and **5.0 Star** rating badge overlay.`; + } else if (isOnlyServices) { + responseMsg = `📸 **Added Service Card Photography!**\n\n- Injected photographic header thumbnails across all ${servicesSec?.content.servicesList?.length || 6} service cards.\n- Enabled hover-zoom effect and responsive preview layout.`; + } else { + responseMsg = `📸 **High-Resolution Commercial Stock Images Injected!**\n\n- **Section 1 (Hero Split Showcase)**: High-converting split layout with ${preset.name} hero photography & live verified pulse badge.\n- **Section 2 (Services Grid)**: Photographic headers attached to all ${servicesSec?.content.servicesList?.length || 6} service cards.\n\nTake a look at your live homepage preview!`; + } + + return { + updatedSections: sections, + aiResponse: responseMsg, + modifiedSectionNumber: isOnlyServices ? 2 : 1, + }; + } + + // 4. DETECT TARGET SECTION BY NUMBER OR INTENT + let targetSectionNum: number | null = null; + const numMatch = lower.match(/section\s*(\d+)/i) || lower.match(/sec\s*(\d+)/i) || lower.match(/#(\d+)/); + if (numMatch) { + targetSectionNum = parseInt(numMatch[1], 10); + } else if (lower.includes('header') || lower.includes('navbar') || lower.includes('nav bar') || lower.includes('navigation') || lower.includes('top bar') || lower.includes('topbar') || lower.includes('menu') || lower.includes('logo')) { + targetSectionNum = 0; + } else if (lower.includes('hero') || lower.includes('headline') || lower.includes('top banner') || lower.includes('first section')) { + targetSectionNum = 1; + } else if (lower.includes('service') || lower.includes('offerings') || lower.includes('what we do') || lower.includes('capabilities') || lower.includes('columns')) { + targetSectionNum = 2; + } else if (lower.includes('why us') || lower.includes('why choose us') || lower.includes('stats') || lower.includes('guarantee') || lower.includes('pillars') || lower.includes('metrics')) { + targetSectionNum = 3; + } else if (lower.includes('review') || lower.includes('testimonial') || lower.includes('feedback') || lower.includes('stars') || lower.includes('social proof')) { + targetSectionNum = 4; + } else if (lower.includes('form') || lower.includes('quote') || lower.includes('estimate') || lower.includes('booking')) { + targetSectionNum = 5; + } else if (lower.includes('cta') || lower.includes('urgency') || lower.includes('discount') || lower.includes('special offer') || lower.includes('deal') || lower.includes('coupon')) { + targetSectionNum = 6; + } else if (lower.includes('footer') || lower.includes('hours') || lower.includes('nap') || lower.includes('bottom of page')) { + targetSectionNum = 7; + } + + // 5. PROCESS SECTION-SPECIFIC MODIFICATIONS + if (targetSectionNum !== null) { + const sectionIndex = sections.findIndex(s => s.sectionNumber === targetSectionNum); + if (sectionIndex !== -1) { + const targetSec = sections[sectionIndex]; + + // SECTION 0: HEADER & NAVIGATION + if (targetSectionNum === 0) { + // Switch pre-formatted variants + if (lower.includes('modern') || lower.includes('clean') || lower.includes('minimal') || lower.includes('stars') || lower.includes('rating') || lower.includes('5 star')) { + targetSec.content.headerVariant = 'modern-clean'; + targetSec.content.topBarAnnouncement = '⭐ Rated 5.0 Stars by Over 1,200+ Satisfied Homeowners • 100% Satisfaction Guarantee'; + targetSec.content.headerCtaText = 'Book Service Online'; + } else if (lower.includes('commercial') || lower.includes('elite') || lower.includes('corporate') || lower.includes('bid')) { + targetSec.content.headerVariant = 'commercial-elite'; + targetSec.content.topBarAnnouncement = '🏆 Certified Master Commercial & Residential Contractors • Licensed & Bonded'; + targetSec.content.headerCtaText = 'Request Project Bid'; + } else if (lower.includes('emergency') || lower.includes('trade') || lower.includes('dispatch') || lower.includes('urgent') || lower.includes('24/7')) { + targetSec.content.headerVariant = 'emergency-trade'; + targetSec.content.topBarAnnouncement = '24/7 Rapid Emergency Response Available • Guaranteed On-Time Arrival'; + targetSec.content.headerCtaText = 'Emergency Dispatch'; + } + + // Top bar toggle + if (lower.includes('hide top') || lower.includes('no top') || lower.includes('remove top') || lower.includes('without top')) { + targetSec.content.showTopBar = false; + } else if (lower.includes('show top') || lower.includes('add top') || lower.includes('enable top') || lower.includes('with top')) { + targetSec.content.showTopBar = true; + } + + // Custom License # + const licHashMatch = instruction.match(/#\s*([A-Za-z0-9-]+)/); + const licWordMatch = instruction.match(/lic(?:ense)?\s+(?:number\s+|no\.?\s+)?([A-Za-z0-9-]+)/i); + const extractedLic = licHashMatch ? licHashMatch[1] : (licWordMatch && licWordMatch[1].toLowerCase() !== 'to' ? licWordMatch[1] : null); + if (extractedLic) { + targetSec.content.licenseBadge = `Lic #${extractedLic.toUpperCase()} • Bonded & Insured`; + } + + // Custom CTA text + const ctaMatch = instruction.match(/cta\s+(?:to\s+)?["']?([^"'\n]+?)["']?$/i) || instruction.match(/button\s+(?:to\s+)?["']?([^"'\n]+?)["']?$/i); + if (ctaMatch && ctaMatch[1] && ctaMatch[1].length < 30) { + targetSec.content.headerCtaText = ctaMatch[1].trim(); + } + + // Custom announcement + const quotedAnnouncement = instruction.match(/announcement\s+(?:to\s+)?["'](.*?)["']/i) || instruction.match(/top bar\s+(?:to\s+)?["'](.*?)["']/i); + if (quotedAnnouncement && quotedAnnouncement[1]) { + targetSec.content.topBarAnnouncement = quotedAnnouncement[1]; + } + + const activeVar = targetSec.content.headerVariant || 'emergency-trade'; + const varTitle = activeVar === 'emergency-trade' + ? 'Emergency Dispatch & Trade Pro' + : activeVar === 'modern-clean' + ? 'Modern Minimalist & 5-Star Certified' + : 'Elite Commercial Contractor'; + + return { + updatedSections: sections, + aiResponse: `✨ **Section 0 (Header & Navigation) Configured!**\n\n- **Standardized Architecture**: Pre-formatted for guaranteed zero-breakage on **Desktop (1280px)**, **iPad / Tablet (768px)**, and **Mobile (400px)**.\n- **Preset Variant**: **${varTitle}**\n- **Top Emergency Bar**: ${targetSec.content.showTopBar !== false ? '✅ Active' : 'Off'} ("${targetSec.content.topBarAnnouncement || '24/7 Response'}")\n- **License Badge**: "${targetSec.content.licenseBadge || 'CA Lic #948201 • Bonded & Insured'}"\n- **Primary CTA**: "${targetSec.content.headerCtaText || 'Get Free Quote'}"\n- **Mobile Navigation**: Instant 1-tap call button + animated mobile hamburger menu drawer.`, + modifiedSectionNumber: 0, + }; + } + + // SECTION 1: HERO + if (targetSectionNum === 1) { + if (lower.includes('emergency') || lower.includes('24/7') || lower.includes('fast')) { + targetSec.content.headline = `24/7 Rapid Emergency Response — Ready When You Need Us`; + targetSec.content.subheadline = `Don't wait when disaster strikes. Fully equipped mobile units on standby with guaranteed on-time arrival.`; + targetSec.content.primaryCtaText = 'Dispatch Emergency Tech'; + } else if (lower.includes('luxury') || lower.includes('premium') || lower.includes('high end')) { + targetSec.content.headline = `The Pinnacle of Architectural Elegance & Craftsmanship`; + targetSec.content.subheadline = `Custom bespoke designs, museum-grade materials, and white-glove project management.`; + } else { + const quoted = instruction.match(/["'](.*?)["']/); + if (quoted && quoted[1]) { + targetSec.content.headline = quoted[1]; + } else { + const cleaned = instruction.replace(/section\s*1/i, '').replace(/hero/i, '').replace(/headline/i, '').replace(/make it/i, '').replace(/change to/i, '').trim(); + if (cleaned.length > 4) { + targetSec.content.headline = cleaned.charAt(0).toUpperCase() + cleaned.slice(1); + } else { + targetSec.content.headline = `The Premier ${currentProfile.industry || 'Local'} Authority in ${currentProfile.city || 'Your Area'}`; + } + } + } + + return { + updatedSections: sections, + aiResponse: `⚡ **Section 1 (Hero Banner) Refined!**\n\n- Headline: "${targetSec.content.headline}"\n- Subheadline updated to align with your value proposition.`, + modifiedSectionNumber: 1, + }; + } + + // SECTION 2: SERVICES GRID + if (targetSectionNum === 2) { + if (lower.includes('4 col') || lower.includes('four col') || lower.includes('4 columns')) { + targetSec.content.columns = 4; + } else if (lower.includes('3 col') || lower.includes('three col')) { + targetSec.content.columns = 3; + } + + if (lower.includes('add') || lower.includes('include') || lower.includes('emergency')) { + targetSec.content.servicesList?.unshift({ + id: `service-custom-${Date.now()}`, + title: 'Priority Emergency Diagnostics & Care', + description: 'Same-day on-site priority response with fully equipped diagnostic units. No hidden overtime fees.', + iconName: 'zap', + popular: true, + }); + } + + return { + updatedSections: sections, + aiResponse: `🛠️ **Section 2 (Services Grid) Updated!**\n\n- Layout: ${targetSec.content.columns || 3} Columns\n- Offerings adjusted and highlighted for top customer conversion.`, + modifiedSectionNumber: 2, + }; + } + + // SECTION 3: WHY CHOOSE US + if (targetSectionNum === 3) { + if (lower.includes('emergency') || lower.includes('fast') || lower.includes('24/7') || lower.includes('response')) { + targetSec.content.headline = `Why We're the Fastest & Most Trusted in ${currentProfile.city || 'the Area'}`; + targetSec.content.subheadline = 'When seconds count, property owners count on our vetted emergency specialists.'; + targetSec.content.stats = [ + { id: 'stat-e1', value: '30 Min', label: 'Average Dispatch', subtext: 'Always on call' }, + { id: 'stat-e2', value: '24/7/365', label: 'Availability', subtext: 'Holidays & weekends included' }, + { id: 'stat-e3', value: '100%', label: 'Guaranteed Resolution', subtext: 'Fixed right the first time' }, + { id: 'stat-e4', value: '5-Star', label: 'Rated Care', subtext: 'Zero surprise invoices' }, + ]; + targetSec.content.pillars = [ + { + id: 'pil-e1', + title: 'Guaranteed Rapid Arrival', + description: 'We track our mobile units via GPS so you receive real-time technician ETA tracking directly to your phone.', + iconName: 'clock', + }, + { + id: 'pil-e2', + title: 'Upfront Flat-Rate Estimates', + description: 'You will always know the full cost before any work begins. No emergency surge multipliers.', + iconName: 'tag', + }, + { + id: 'pil-e3', + title: 'Direct Insurance Billing Assistance', + description: 'We document all property damage thoroughly with timestamped photos to ensure seamless claim processing.', + iconName: 'shield-check', + }, + ]; + } else if (lower.includes('family') || lower.includes('local') || lower.includes('years')) { + targetSec.content.headline = `Locally Owned, Family Operated & Dedicated to Our Community`; + targetSec.content.subheadline = 'Over two decades of trusted craftsmanship. We treat every home like it belongs to our family.'; + targetSec.content.stats = [ + { id: 'stat-f1', value: '20+ Yrs', label: 'Local Community Service', subtext: 'Rooted right here' }, + { id: 'stat-f2', value: '5,000+', label: 'Happy Homeowners', subtext: 'Generations of trust' }, + { id: 'stat-f3', value: '100%', label: 'Direct Owner Oversight', subtext: 'Accountability on every job' }, + { id: 'stat-f4', value: '0', label: 'Corners Cut', subtext: 'Unmatched integrity' }, + ]; + } else { + targetSec.content.headline = `The Standard of Quality & Trust in ${currentProfile.city || 'Our Region'}`; + targetSec.content.subheadline = `Engineered around your satisfaction. Here is why discerning property owners choose us.`; + if (targetSec.content.pillars && targetSec.content.pillars[0]) { + targetSec.content.pillars[0].title = 'Direct Founder Oversight on Every Job'; + targetSec.content.pillars[0].description = 'Our company leadership personally inspects final deliverables to ensure 100% compliance with strict standards.'; + } + } + + return { + updatedSections: sections, + aiResponse: `🎯 **Section 3 (Why Choose Us) Revamped!**\n\n- Headline: "${targetSec.content.headline}"\n- Performance stats & proof pillars updated with local trust signals.`, + modifiedSectionNumber: 3, + }; + } + + // SECTION 4: REVIEWS & TESTIMONIALS + if (targetSectionNum === 4) { + targetSec.content.headline = `Real Stories from Real Clients in ${currentProfile.city || 'Our Community'}`; + targetSec.content.reviews?.push({ + id: `rev-custom-${Date.now()}`, + author: 'Brandon K.', + location: currentProfile.city || 'Local Neighbor', + rating: 5, + quote: `Prompt, respectful, and transparent about every single detail. Best decision we made for our property!`, + serviceMentioned: 'Primary Service', + date: 'Just now', + }); + + return { + updatedSections: sections, + aiResponse: `🌟 **Section 4 (Customer Reviews) Updated!**\n\n- Fresh 5-star verified social proof injected with localized neighbor references.`, + modifiedSectionNumber: 4, + }; + } + + // SECTION 5: QUOTE FORM + if (targetSectionNum === 5) { + targetSec.content.formTitle = 'Request Your Fast, Free Consultation'; + targetSec.content.formSubtitle = 'Guaranteed 15-minute response time during business hours with zero pressure.'; + + return { + updatedSections: sections, + aiResponse: `📝 **Section 5 (Estimate & Booking Form) Refined!**\n\n- Form callouts optimized for fast mobile lead conversion.`, + modifiedSectionNumber: 5, + }; + } + + // SECTION 6: CTA BANNER + if (targetSectionNum === 6) { + const discountMatch = instruction.match(/(\d+)%/); + const discountVal = discountMatch ? discountMatch[1] : '15'; + targetSec.content.bannerTitle = `Claim Your ${discountVal}% First-Time Client Savings`; + targetSec.content.bannerDescription = `Call now or book online to lock in your seasonal estimate. Limited booking slots available this month!`; + + return { + updatedSections: sections, + aiResponse: `📢 **Section 6 (CTA Banner) Updated!**\n\n- Configured urgent **${discountVal}% First-Time Client Discount** banner.`, + modifiedSectionNumber: 6, + }; + } + + // SECTION 7: FOOTER + if (targetSectionNum === 7) { + targetSec.content.hours = 'Open 24 Hours / 7 Days a Week • Direct Dispatch'; + return { + updatedSections: sections, + aiResponse: `📍 **Section 7 (Footer) Updated!**\n\n- Business hours and citation schema synchronized.`, + modifiedSectionNumber: 7, + }; + } + } + } + + // 5.5 CHECK FOR META QUESTIONS, TROUBLESHOOTING, OR SYSTEM INQUIRIES + if ( + lower.includes('why') || + lower.includes('didnt work') || + lower.includes('didn\'t work') || + lower.includes('not working') || + lower.includes('how do') || + lower.includes('how can') || + lower.includes('what are') || + lower.includes('help') || + lower.includes('all colors') || + lower.includes('for this to work') + ) { + return { + updatedSections: sections, + aiResponse: `💡 **AI Assistant Co-Pilot Support**\n\nI monitor your canvas in real time. All styling, theme, and copy changes work automatically across all trades and colors!\n\n- **Theme & Colors**: You can say *"theme black and yellow"*, *"switch to purple"*, *"change to orange"*, *"make it emerald green"*, etc. Every color palette updates the live canvas instantly.\n- **Trade Transformation**: Say *"change wording to Landscaping"* or *"make for a pool contractor"* and both copy & photos switch automatically.\n- **Direct Editing**: You can click the **AI Edit** badge on any section or tell me to refine headlines, add images, or adjust promotions!`, + }; + } + + // 6. GENERAL PROMPT / CREATIVE INSTRUCTION HANDLING + const heroSec = sections.find(s => s.sectionNumber === 1); + const whySec = sections.find(s => s.sectionNumber === 3); + const ctaSec = sections.find(s => s.sectionNumber === 6); + + if (heroSec) { + heroSec.content.headline = `${currentProfile.companyName || 'Premier Experts'} — Engineered for Unrivaled Performance`; + heroSec.content.subheadline = `${instruction.charAt(0).toUpperCase() + instruction.slice(1)}. Serving ${currentProfile.city || 'residential and commercial'} clients with guaranteed excellence.`; + } + if (whySec) { + whySec.content.headline = `Built on Trust, Precision & Proven Results`; + if (whySec.content.pillars && whySec.content.pillars[0]) { + whySec.content.pillars[0].title = 'Tailored Specifically to Your Demands'; + whySec.content.pillars[0].description = instruction; + } + } + if (ctaSec) { + ctaSec.content.bannerTitle = `Ready to Get Started?`; + ctaSec.content.bannerDescription = `Connect directly with our team today and experience the difference firsthand.`; + } + + return { + updatedSections: sections, + aiResponse: `✨ **Applied your request across the site!**\n\nI've updated **Section 1 (Hero Banner)**, **Section 3 (Why Choose Us)**, and **Section 6 (CTA Banner)** to reflect your direction: *"${instruction}"*.\n\nTake a look at the live canvas!`, + modifiedSectionNumber: 1, + }; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..64d0aa1 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,180 @@ +export type ThemeId = + | 'titan-navy' + | 'obsidian-gold' + | 'emerald-slate' + | 'crimson-forge' + | 'alpine-clean' + | 'electric-violet' + | 'sunset-amber' + | 'cyber-cyan' + | 'neon-rose' + | 'custom-dynamic'; + +export type ColorMode = 'dark' | 'light'; + +export interface ThemeConfig { + id: ThemeId; + name: string; + tagline: string; + primary: string; + secondary: string; + accent: string; + bgDark: string; + bgLight: string; + cardBgDark: string; + cardBgLight: string; + borderDark: string; + borderLight: string; +} + +export interface BusinessProfile { + companyName: string; + phone: string; + email: string; + address: string; + city: string; + state: string; + zip: string; + industry: string; + tagline: string; + yearsInBusiness: string; + services: string[]; + emergencyService: boolean; + theme: ThemeId; + colorMode?: ColorMode; +} + +export type SectionType = + | 'header' + | 'hero' + | 'services' + | 'why-us' + | 'testimonials' + | 'quote-form' + | 'cta-banner' + | 'footer'; + +export interface ServiceItem { + id: string; + title: string; + description: string; + iconName: string; + popular?: boolean; + imageUrl?: string; +} + +export interface TestimonialItem { + id: string; + author: string; + location: string; + rating: number; + quote: string; + serviceMentioned?: string; + date?: string; +} + +export interface StatItem { + id: string; + value: string; + label: string; + subtext?: string; +} + +export interface ValuePillarItem { + id: string; + title: string; + description: string; + iconName: string; +} + +export interface SectionContent { + // Header section content + headerVariant?: 'emergency-trade' | 'modern-clean' | 'commercial-elite'; + showTopBar?: boolean; + topBarAnnouncement?: string; + licenseBadge?: string; + serviceAreaText?: string; + headerCtaText?: string; + navLinks?: Array<{ label: string; href: string }>; + + // Hero section content + headline?: string; + subheadline?: string; + primaryCtaText?: string; + secondaryCtaText?: string; + trustBadges?: string[]; + heroImageUrl?: string; + + // Services section content + servicesList?: ServiceItem[]; + columns?: number; + + // Why Us content + stats?: StatItem[]; + pillars?: ValuePillarItem[]; + + // Testimonials content + reviews?: TestimonialItem[]; + averageRating?: number; + totalReviews?: number; + + // Quote form content + formTitle?: string; + formSubtitle?: string; + fields?: Array<{ label: string; type: string; placeholder: string; required?: boolean }>; + + // CTA Banner content + bannerTitle?: string; + bannerDescription?: string; + bannerCta?: string; + + // Footer content + hours?: string; + copyrightText?: string; + links?: Array<{ label: string; href: string }>; +} + +export interface WebsiteSection { + id: string; + sectionNumber: number; + type: SectionType; + title: string; + badge?: string; + content: SectionContent; + visible: boolean; +} + +export interface ChatMessage { + id: string; + sender: 'user' | 'ai' | 'system'; + text: string; + timestamp: string; + actionSuggested?: { + type: 'apply_change' | 'select_theme' | 'fill_field' | 'generate_site'; + payload?: any; + label?: string; + }; + modifiedSectionNumber?: number; +} + +export interface ClientContactDetails { + name: string; + email: string; + phone: string; + domainPreference?: string; + notes?: string; + wantsGmb: boolean; + wantsMailcowEmail: boolean; + wantsCrm: boolean; +} + +export interface LeadSubmission { + id: string; + createdAt: string; + businessProfile: BusinessProfile; + clientContact: ClientContactDetails; + sections: WebsiteSection[]; + theme: ThemeId; + colorMode?: ColorMode; + status: 'pending_review' | 'contacted' | 'deployed'; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c15dbd6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo new file mode 100644 index 0000000..c1a448e --- /dev/null +++ b/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/app.tsx","./src/main.tsx","./src/types.ts","./src/components/agencyadmindrawer.tsx","./src/components/aiassistantdrawer.tsx","./src/components/approvalmodal.tsx","./src/components/enterprisesvgicons.tsx","./src/components/onboardingmodal.tsx","./src/components/sectionrenderer.tsx","./src/components/siteheader.tsx","./src/components/websitepreview.tsx","./src/services/aiengine.ts"],"version":"5.9.3"} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..de0eeca --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + host: true + } +});