Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
253a7aa5d2 | ||
|
|
01d4df18a4 | ||
|
|
d6d14902c9 | ||
|
|
0c3f4a1553 | ||
|
|
2f5aee6d4b | ||
|
|
725590574e | ||
|
|
3c6a8ac5ff | ||
|
|
8a1c1c414d | ||
|
|
2a9404d1e5 | ||
|
|
1b4729145a | ||
|
|
debfdc8e2b | ||
|
|
04c6ff164e | ||
| 70f2797703 | |||
| bcd517eb05 | |||
| 5e48dad541 | |||
| ace556fa59 | |||
| 0713049d30 | |||
| 9249b1fa29 | |||
| b75f15bc22 | |||
| 943f440eae |
34
build_log_sky.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
> antigravity_dev@0.1.0 build
|
||||||
|
> next build && node script/copy-server-config.cjs
|
||||||
|
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
▲ Next.js 16.0.3 (Turbopack)
|
||||||
|
|
||||||
|
Creating an optimized production build ...
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
✓ Compiled successfully in 2.8s
|
||||||
|
Running TypeScript ...
|
||||||
|
Collecting page data using 11 workers ...
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
Generating static pages using 11 workers (0/17) ...
|
||||||
|
[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: `npm i baseline-browser-mapping@latest -D`
|
||||||
|
Generating static pages using 11 workers (4/17)
|
||||||
|
Generating static pages using 11 workers (8/17)
|
||||||
|
Error occurred prerendering page "/residential-real-estate/godrej-woods". Read more: https://nextjs.org/docs/messages/prerender-error
|
||||||
|
TypeError: Cannot read properties of undefined (reading 'id')
|
||||||
|
at q (C:\Users\start\sky-and-soil\.next\server\chunks\ssr\_997a77a8._.js:17:32367) {
|
||||||
|
digest: '1668255941'
|
||||||
|
}
|
||||||
|
Export encountered an error on /residential-real-estate/[slug]/page: /residential-real-estate/godrej-woods, exiting the build.
|
||||||
|
⨯ Next.js build worker exited with code: 1 and signal: null
|
||||||
515
package-lock.json
generated
@ -8,11 +8,22 @@
|
|||||||
"name": "antigravity_dev",
|
"name": "antigravity_dev",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/leaflet": "^1.9.21",
|
||||||
|
"@types/react-google-recaptcha": "^2.1.9",
|
||||||
|
"axios": "^1.13.2",
|
||||||
|
"framer-motion": "^12.23.25",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
|
"lucide-react": "^0.554.0",
|
||||||
"next": "16.0.3",
|
"next": "16.0.3",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"react-google-recaptcha": "^3.1.0",
|
||||||
|
"react-leaflet": "^5.0.0",
|
||||||
|
"selenium-webdriver": "^4.38.0",
|
||||||
|
"sitemap": "^9.0.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
@ -278,6 +289,12 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@bazel/runfiles": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.7.1",
|
"version": "1.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
|
||||||
@ -1228,6 +1245,17 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-leaflet/core": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
|
||||||
|
"license": "Hippocratic-2.1",
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@ -1533,6 +1561,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/geojson": {
|
||||||
|
"version": "7946.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||||
|
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
@ -1547,11 +1581,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/leaflet": {
|
||||||
|
"version": "1.9.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
|
||||||
|
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.19.25",
|
"version": "20.19.25",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
|
||||||
"integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==",
|
"integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
@ -1561,7 +1603,6 @@
|
|||||||
"version": "19.2.6",
|
"version": "19.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz",
|
||||||
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
|
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@ -1577,6 +1618,24 @@
|
|||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-google-recaptcha": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz",
|
||||||
|
"integrity": "sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/sax": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.47.0",
|
"version": "8.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz",
|
||||||
@ -2203,6 +2262,12 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@ -2397,6 +2462,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/available-typed-arrays": {
|
"node_modules/available-typed-arrays": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
@ -2423,6 +2494,17 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||||
|
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.4",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||||
@ -2531,7 +2613,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@ -2631,6 +2712,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -2645,6 +2738,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@ -2664,7 +2763,6 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/damerau-levenshtein": {
|
"node_modules/damerau-levenshtein": {
|
||||||
@ -2789,6 +2887,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
@ -2816,7 +2923,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
@ -2928,7 +3034,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -2938,7 +3043,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -2976,7 +3080,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@ -2989,7 +3092,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@ -3604,6 +3706,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||||
|
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
@ -3620,11 +3742,53 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.23.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.25.tgz",
|
||||||
|
"integrity": "sha512-gUHGl2e4VG66jOcH0JHhuJQr6ZNwrET9g31ZG0xdXzT0CznP7fHX4P8Bcvuc4MiUB90ysNnWX2ukHRIggkl6hQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.23.23",
|
||||||
|
"motion-utils": "^12.23.6",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@ -3685,7 +3849,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@ -3710,7 +3873,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dunder-proto": "^1.0.1",
|
"dunder-proto": "^1.0.1",
|
||||||
@ -3798,7 +3960,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -3877,7 +4038,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -3890,7 +4050,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
@ -3906,7 +4065,6 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@ -3932,6 +4090,15 @@
|
|||||||
"hermes-estree": "0.25.1"
|
"hermes-estree": "0.25.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||||
@ -3942,6 +4109,12 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||||
@ -3969,6 +4142,12 @@
|
|||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/internal-slot": {
|
"node_modules/internal-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
|
||||||
@ -4445,7 +4624,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
@ -4524,6 +4702,18 @@
|
|||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@ -4554,6 +4744,12 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/leaflet": {
|
||||||
|
"version": "1.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||||
|
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/levn": {
|
"node_modules/levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||||
@ -4568,6 +4764,15 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.30.2",
|
"version": "1.30.2",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||||
@ -4856,7 +5061,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
@ -4875,6 +5079,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.554.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.554.0.tgz",
|
||||||
|
"integrity": "sha512-St+z29uthEJVx0Is7ellNkgTEhaeSoA42I7JjOCBCrc5X6LYMGSv0P/2uS5HDLTExP5tpiqRD2PyUEOS6s9UXA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
@ -4889,7 +5102,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -4919,6 +5131,27 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
@ -4942,6 +5175,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.23.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||||
|
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.23.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@ -5091,7 +5339,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -5278,6 +5525,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@ -5386,11 +5639,16 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
@ -5398,6 +5656,12 @@
|
|||||||
"react-is": "^16.13.1"
|
"react-is": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@ -5438,6 +5702,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-async-script": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"prop-types": "^15.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.2.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
@ -5450,11 +5727,58 @@
|
|||||||
"react": "^19.2.0"
|
"react": "^19.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-google-recaptcha": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.5.0",
|
||||||
|
"react-async-script": "^1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true,
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/react-leaflet": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
|
||||||
|
"license": "Hippocratic-2.1",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-leaflet/core": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"leaflet": "^1.9.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream/node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
@ -5597,6 +5921,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/safe-push-apply": {
|
"node_modules/safe-push-apply": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
|
||||||
@ -5632,12 +5962,43 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||||
|
"license": "BlueOak-1.0.0"
|
||||||
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/selenium-webdriver": {
|
||||||
|
"version": "4.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.38.0.tgz",
|
||||||
|
"integrity": "sha512-5/UXXFSQmn7FGQkbcpAqvfhzflUdMWtT7QqpEgkFD6Q6rDucxB5EUfzgjmr6JbUj30QodcW3mDXehzoeS/Vy5w==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/SeleniumHQ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/selenium"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@bazel/runfiles": "^6.3.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"tmp": "^0.2.5",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "6.3.1",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
@ -5697,6 +6058,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/sharp": {
|
"node_modules/sharp": {
|
||||||
"version": "0.34.5",
|
"version": "0.34.5",
|
||||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||||
@ -5854,6 +6221,40 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sitemap": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-J/SU27FJ+I52TcDLKZzPRRVQUMj0Pp1i/HLb2lrkU+hrMLM+qdeRjdacrNxnSW48Waa3UcEOGOdX1+0Lob7TgA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^24.9.2",
|
||||||
|
"@types/sax": "^1.2.1",
|
||||||
|
"arg": "^5.0.0",
|
||||||
|
"sax": "^1.4.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sitemap": "dist/esm/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.5",
|
||||||
|
"npm": ">=10.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/@types/node": {
|
||||||
|
"version": "24.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||||
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@ -5884,6 +6285,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string.prototype.includes": {
|
"node_modules/string.prototype.includes": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
||||||
@ -6146,6 +6556,15 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tmp": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@ -6356,7 +6775,6 @@
|
|||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unrs-resolver": {
|
"node_modules/unrs-resolver": {
|
||||||
@ -6435,6 +6853,12 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@ -6550,6 +6974,49 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
|
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
|||||||
15
package.json
@ -4,16 +4,26 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build && node script/copy-server-config.cjs",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/leaflet": "^1.9.21",
|
||||||
|
"@types/react-google-recaptcha": "^2.1.9",
|
||||||
|
"axios": "^1.13.2",
|
||||||
|
"framer-motion": "^12.23.25",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
|
"lucide-react": "^0.554.0",
|
||||||
"next": "16.0.3",
|
"next": "16.0.3",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"react-google-recaptcha": "^3.1.0",
|
||||||
|
"react-leaflet": "^5.0.0",
|
||||||
|
"sitemap": "^9.0.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
@ -23,6 +33,7 @@
|
|||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.0.3",
|
"eslint-config-next": "16.0.3",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
|
"selenium-webdriver": "^4.38.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
public/assets/images/about/about-banner.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/assets/images/about/contact-banner.webp
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
public/assets/images/about/lifestyle-banner.webp
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/assets/images/about/privacy-banner.webp
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
public/assets/images/about/terms-banner.webp
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
public/assets/images/home/back.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/assets/images/home/bottom.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/assets/images/home/experience.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
public/assets/images/home/faq-2.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/assets/images/home/faq-3.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/assets/images/home/faq.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/assets/images/home/front.webp
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
public/assets/images/home/left.webp
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
public/assets/images/home/ready.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
public/assets/images/home/right.webp
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
public/assets/images/home/top.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
public/assets/images/home/top.webp
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/assets/images/home/where.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/assets/images/home/why/customer-first.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/assets/images/home/why/location.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/assets/images/home/why/smart-home.webp
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/assets/images/home/why/transparent-deals.webp
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/assets/images/image.png
Normal file
|
After Width: | Height: | Size: 913 KiB |
BIN
public/assets/images/map-placeholder.png
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
public/assets/images/map-placeholder.webp
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
public/assets/images/master-plan.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
BIN
public/assets/images/projects/barca.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/assets/images/projects/details/barca-1.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/assets/images/projects/details/barca-2.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/assets/images/projects/details/barca-3.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/assets/images/projects/details/hoskote-1.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/assets/images/projects/details/hoskote-2.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/assets/images/projects/details/hoskote-3.webp
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
public/assets/images/projects/details/lakeside-1.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/images/projects/details/lakeside-2.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/assets/images/projects/details/lakeside-3.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/images/projects/details/tiara-1.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/assets/images/projects/details/tiara-2.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/assets/images/projects/details/tiara-3.webp
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/assets/images/projects/details/woods-1.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/assets/images/projects/details/woods-2.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/assets/images/projects/details/woods-3.webp
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/assets/images/projects/godrej-hoskote.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/images/projects/godrej-lakeside.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/assets/images/projects/godrej-tiara.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/assets/images/projects/godrej-woods.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/assets/images/projects/projects-banner.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/assets/images/projects/residential-real-estate.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
4
public/robots.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://skyandsoil.metatronnest.com/sitemap.xml
|
||||||
1
public/sitemap.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://skyandsoil.metatronnest.com/</loc></url><url><loc>https://skyandsoil.metatronnest.com/about/</loc></url><url><loc>https://skyandsoil.metatronnest.com/projects/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/</loc></url><url><loc>https://skyandsoil.metatronnest.com/lifestyle/</loc></url><url><loc>https://skyandsoil.metatronnest.com/contact/</loc></url><url><loc>https://skyandsoil.metatronnest.com/compare/</loc></url><url><loc>https://skyandsoil.metatronnest.com/privacy-policy/</loc></url><url><loc>https://skyandsoil.metatronnest.com/terms-of-service/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/barca-at-godrej-msr-city/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/godrej-woods/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/godrej-hoskote/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/godrej-lakeside-orchard/</loc></url><url><loc>https://skyandsoil.metatronnest.com/residential-real-estate/godrej-tiara/</loc></url></urlset>
|
||||||
43
public/web.config
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<system.webServer>
|
||||||
|
<directoryBrowse enabled="false" />
|
||||||
|
<defaultDocument>
|
||||||
|
<files>
|
||||||
|
<clear />
|
||||||
|
<add value="index.html" />
|
||||||
|
</files>
|
||||||
|
</defaultDocument>
|
||||||
|
<staticContent>
|
||||||
|
<remove fileExtension=".json" />
|
||||||
|
<mimeMap fileExtension=".json" mimeType="application/json" />
|
||||||
|
<remove fileExtension=".webp" />
|
||||||
|
<mimeMap fileExtension=".webp" mimeType="image/webp" />
|
||||||
|
<remove fileExtension=".woff" />
|
||||||
|
<mimeMap fileExtension=".woff" mimeType="font/woff" />
|
||||||
|
<remove fileExtension=".woff2" />
|
||||||
|
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
|
||||||
|
<remove fileExtension=".js" />
|
||||||
|
<mimeMap fileExtension=".js" mimeType="application/javascript" />
|
||||||
|
<remove fileExtension=".css" />
|
||||||
|
<mimeMap fileExtension=".css" mimeType="text/css" />
|
||||||
|
</staticContent>
|
||||||
|
<httpErrors errorMode="Custom">
|
||||||
|
<remove statusCode="404" />
|
||||||
|
<error statusCode="404" path="/404.html" responseMode="ExecuteURL" />
|
||||||
|
</httpErrors>
|
||||||
|
<rewrite>
|
||||||
|
<rules>
|
||||||
|
<rule name="Handling Trailing Slashes" stopProcessing="false">
|
||||||
|
<match url="(.*)" />
|
||||||
|
<conditions>
|
||||||
|
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||||
|
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||||
|
</conditions>
|
||||||
|
<action type="Rewrite" url="{R:1}" />
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</rewrite>
|
||||||
|
<modules runAllManagedModulesForAllRequests="true"/>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
||||||
30
script/copy-server-config.cjs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const PUBLIC_DIR = path.join(__dirname, '..', 'public');
|
||||||
|
const OUT_DIR = path.join(__dirname, '..', 'out');
|
||||||
|
const FILES_TO_COPY = ['.htaccess', 'web.config'];
|
||||||
|
|
||||||
|
// Ensure out directory exists
|
||||||
|
if (!fs.existsSync(OUT_DIR)) {
|
||||||
|
console.warn('⚠ out directory does not exist. Make sure "next build" (static export) has run.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILES_TO_COPY.forEach(filename => {
|
||||||
|
const source = path.join(PUBLIC_DIR, filename);
|
||||||
|
const destination = path.join(OUT_DIR, filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(source)) {
|
||||||
|
fs.copyFileSync(source, destination);
|
||||||
|
console.log(`✓ ${filename} copied to out directory`);
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠ ${filename} not found in public directory`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error copying ${filename}:`, error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
72
script/generate-sitemap.cjs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const { SitemapStream, streamToPromise } = require("sitemap");
|
||||||
|
|
||||||
|
const hostname = "https://skyandsoil.metatronnest.com";
|
||||||
|
const addTrailingSlash = true;
|
||||||
|
|
||||||
|
const shouldAddSlash = (url) => {
|
||||||
|
if (url === "/") return false;
|
||||||
|
if (/\.[a-z0-9]{2,6}(\?.*)?$/i.test(url)) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatUrl = (url) => {
|
||||||
|
if (!url.startsWith("/")) url = "/" + url;
|
||||||
|
if (addTrailingSlash && shouldAddSlash(url) && !url.endsWith("/")) return url + "/";
|
||||||
|
if (!addTrailingSlash && url.endsWith("/") && url !== "/") return url.slice(0, -1);
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ✅ Static pages
|
||||||
|
const staticLinks = [
|
||||||
|
{ url: "/" },
|
||||||
|
{ url: "/about/" },
|
||||||
|
{ url: "/projects/" },
|
||||||
|
{ url: "/residential-real-estate/" },
|
||||||
|
{ url: "/lifestyle/" },
|
||||||
|
{ url: "/contact/" },
|
||||||
|
{ url: "/compare/" },
|
||||||
|
{ url: "/privacy-policy/" },
|
||||||
|
{ url: "/terms-of-service/" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// ✅ Dynamic property pages (manual slugs)
|
||||||
|
const propertyPages = [
|
||||||
|
{ slug: "/residential-real-estate/barca-at-godrej-msr-city/" },
|
||||||
|
{ slug: "/residential-real-estate/godrej-woods/" },
|
||||||
|
{ slug: "/residential-real-estate/godrej-hoskote/" },
|
||||||
|
{ slug: "/residential-real-estate/godrej-lakeside-orchard/" },
|
||||||
|
{ slug: "/residential-real-estate/godrej-tiara/" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const propertyLinks = propertyPages.map(page => ({ url: page.slug }));
|
||||||
|
|
||||||
|
// Combine static + property links
|
||||||
|
const allLinks = [...staticLinks, ...propertyLinks].map(link => ({
|
||||||
|
url: formatUrl(link.url),
|
||||||
|
}));
|
||||||
|
|
||||||
|
async function generateSitemap() {
|
||||||
|
try {
|
||||||
|
const sitemap = new SitemapStream({ hostname });
|
||||||
|
const writeStream = fs.createWriteStream(path.resolve(__dirname, "../public/sitemap.xml"));
|
||||||
|
|
||||||
|
sitemap.pipe(writeStream);
|
||||||
|
|
||||||
|
console.log("📦 Writing URLs to sitemap:");
|
||||||
|
allLinks.forEach(link => {
|
||||||
|
console.log(" -", hostname + link.url);
|
||||||
|
sitemap.write(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
sitemap.end();
|
||||||
|
await streamToPromise(sitemap);
|
||||||
|
|
||||||
|
console.log("✅ sitemap.xml created successfully!");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Error creating sitemap.xml:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSitemap();
|
||||||
1
script/image_alt_issues.csv
Normal file
@ -0,0 +1 @@
|
|||||||
|
Page URL,Image Src,Alt Text,Issue Type
|
||||||
|
261
script/seo-test-selenium.cjs
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
// 🚀 Full SEO + Broken Link + 404 + Accessibility + Image Alt CSV Export
|
||||||
|
// Run with: node seo_full_audit.js
|
||||||
|
|
||||||
|
const { Builder, By } = require("selenium-webdriver");
|
||||||
|
const chrome = require("selenium-webdriver/chrome");
|
||||||
|
const axios = require("axios");
|
||||||
|
const xml2js = require("xml2js");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
// CSV file for Image Alt issues
|
||||||
|
const csvPath = path.join(__dirname, "image_alt_issues.csv");
|
||||||
|
fs.writeFileSync(csvPath, "Page URL,Image Src,Alt Text,Issue Type\n", "utf8");
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// 1️⃣ Fetch URLs from sitemap.xml
|
||||||
|
// ==========================
|
||||||
|
async function getUrlsFromSitemap(sitemapUrl) {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(sitemapUrl);
|
||||||
|
const parsed = await xml2js.parseStringPromise(res.data);
|
||||||
|
return parsed.urlset.url.map((u) => u.loc[0]);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Failed to load sitemap:", err.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// 2️⃣ Check HTTP Status
|
||||||
|
// ==========================
|
||||||
|
async function checkLinkStatus(url) {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(url, {
|
||||||
|
timeout: 10000,
|
||||||
|
validateStatus: () => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data.toLowerCase().includes("page not found")) {
|
||||||
|
return "Soft 404";
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status;
|
||||||
|
} catch (err) {
|
||||||
|
return err.response ? err.response.status : "❌ No Response";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// 3️⃣ Main SEO + Accessibility + Image Alt Audit
|
||||||
|
// ==========================
|
||||||
|
async function checkSEO(url, siteDomain) {
|
||||||
|
const options = new chrome.Options();
|
||||||
|
options.addArguments("--headless", "--no-sandbox", "--disable-gpu");
|
||||||
|
|
||||||
|
const driver = await new Builder()
|
||||||
|
.forBrowser("chrome")
|
||||||
|
.setChromeOptions(options)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pageStatus = await checkLinkStatus(url);
|
||||||
|
if (pageStatus === 404 || pageStatus === "Soft 404") {
|
||||||
|
console.log(`\n🚫 ${url} → ❌ Page not found (${pageStatus})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await driver.get(url);
|
||||||
|
const pageSource = await driver.getPageSource();
|
||||||
|
|
||||||
|
// Basic SEO Elements
|
||||||
|
const title = await driver.getTitle();
|
||||||
|
const descElem = await driver.findElements(By.css('meta[name="description"]'));
|
||||||
|
const canonicalElem = await driver.findElements(By.css('link[rel="canonical"]'));
|
||||||
|
const robotsElem = await driver.findElements(By.css('meta[name="robots"]'));
|
||||||
|
const viewportElem = await driver.findElements(By.css('meta[name="viewport"]'));
|
||||||
|
const charset = await driver.findElements(By.css('meta[charset]'));
|
||||||
|
const htmlTag = await driver.findElement(By.css("html"));
|
||||||
|
const langAttr = await htmlTag.getAttribute("lang").catch(() => "");
|
||||||
|
const h1Tags = await driver.findElements(By.css("h1"));
|
||||||
|
const h2Tags = await driver.findElements(By.css("h2"));
|
||||||
|
|
||||||
|
// Meta Description
|
||||||
|
let descContent = descElem.length > 0 ? await descElem[0].getAttribute("content") : "";
|
||||||
|
const descLength = descContent.length;
|
||||||
|
const descStatus =
|
||||||
|
descLength === 0
|
||||||
|
? "❌ Missing"
|
||||||
|
: descLength < 50
|
||||||
|
? `⚠️ Too short (${descLength})`
|
||||||
|
: descLength > 160
|
||||||
|
? `⚠️ Too long (${descLength})`
|
||||||
|
: "✅ Perfect";
|
||||||
|
|
||||||
|
// Title length check
|
||||||
|
const titleLength = title.length;
|
||||||
|
const titleStatus =
|
||||||
|
titleLength === 0
|
||||||
|
? "❌ Missing"
|
||||||
|
: titleLength < 30
|
||||||
|
? `⚠️ Too short (${titleLength})`
|
||||||
|
: titleLength > 65
|
||||||
|
? `⚠️ Too long (${titleLength})`
|
||||||
|
: "✅ Perfect";
|
||||||
|
|
||||||
|
// Canonical
|
||||||
|
const canonicalURL =
|
||||||
|
canonicalElem.length > 0 ? await canonicalElem[0].getAttribute("href") : "❌ Missing";
|
||||||
|
|
||||||
|
// 🖼️ Image Accessibility Audit
|
||||||
|
const imgs = await driver.findElements(By.css("img"));
|
||||||
|
let missingAlt = 0;
|
||||||
|
let emptyAlt = 0;
|
||||||
|
let duplicateAlt = [];
|
||||||
|
const altTextMap = new Map();
|
||||||
|
|
||||||
|
for (const img of imgs) {
|
||||||
|
const src = await img.getAttribute("src");
|
||||||
|
const alt = (await img.getAttribute("alt"))?.trim() ?? null;
|
||||||
|
|
||||||
|
if (alt === null) {
|
||||||
|
missingAlt++;
|
||||||
|
fs.appendFileSync(csvPath, `"${url}","${src}","","Missing Alt"\n`, "utf8");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alt === "") {
|
||||||
|
emptyAlt++;
|
||||||
|
fs.appendFileSync(csvPath, `"${url}","${src}","(empty)","Empty Alt"\n`, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altTextMap.has(alt)) {
|
||||||
|
altTextMap.set(alt, altTextMap.get(alt) + 1);
|
||||||
|
} else {
|
||||||
|
altTextMap.set(alt, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [altText, count] of altTextMap.entries()) {
|
||||||
|
if (altText && count > 1) {
|
||||||
|
duplicateAlt.push({ altText, count });
|
||||||
|
fs.appendFileSync(
|
||||||
|
csvPath,
|
||||||
|
`"${url}","","${altText}","Duplicate Alt (${count} times)"\n`,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect tracking & schema tags
|
||||||
|
const hasGTM = pageSource.includes("googletagmanager.com/gtm.js");
|
||||||
|
const hasClarity = pageSource.includes("clarity.ms/tag");
|
||||||
|
const hasFBPixel = pageSource.includes("fbevents.js") || pageSource.includes("fbq(");
|
||||||
|
const hasAnalytics = pageSource.includes("www.googletagmanager.com/gtag/js");
|
||||||
|
|
||||||
|
const ogTags = await driver.findElements(By.css("meta[property^='og:']"));
|
||||||
|
const twitterTags = await driver.findElements(By.css("meta[name^='twitter:']"));
|
||||||
|
const schemaScripts = await driver.findElements(By.css('script[type="application/ld+json"]'));
|
||||||
|
|
||||||
|
// Links check
|
||||||
|
const anchorTags = await driver.findElements(By.css("a[href]"));
|
||||||
|
const brokenLinks = [];
|
||||||
|
for (const a of anchorTags) {
|
||||||
|
const href = await a.getAttribute("href");
|
||||||
|
if (!href || href.startsWith("#") || href.startsWith("mailto:")) continue;
|
||||||
|
|
||||||
|
const fullUrl = href.startsWith("http")
|
||||||
|
? href
|
||||||
|
: `${siteDomain}${href.startsWith("/") ? href : `/${href}`}`;
|
||||||
|
|
||||||
|
if (fullUrl.includes(siteDomain)) {
|
||||||
|
const status = await checkLinkStatus(fullUrl);
|
||||||
|
if (status === 404 || status === "Soft 404" || status === "❌ No Response") {
|
||||||
|
brokenLinks.push({ link: fullUrl, status });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazy loading check
|
||||||
|
const images = await driver.findElements(By.css("img, video, iframe"));
|
||||||
|
const lazyLoadCount = await Promise.all(
|
||||||
|
images.map(async (img) => {
|
||||||
|
const loading = await img.getAttribute("loading");
|
||||||
|
return loading === "lazy";
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const lazyLoaded = lazyLoadCount.filter((v) => v).length;
|
||||||
|
|
||||||
|
// Console Summary
|
||||||
|
console.log(`\n🔍 Checking: ${url}`);
|
||||||
|
console.log("-------------------------------------------");
|
||||||
|
console.log("Title:", titleStatus);
|
||||||
|
console.log("Meta Description:", descStatus);
|
||||||
|
console.log("Canonical URL:", canonicalURL);
|
||||||
|
console.log("Meta Robots:", robotsElem.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Viewport:", viewportElem.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Charset:", charset.length > 0 ? "✅ Found" : "❌ Missing");
|
||||||
|
console.log("HTML lang:", langAttr ? `✅ ${langAttr}` : "⚠️ Missing");
|
||||||
|
console.log("H1 Tags:", h1Tags.length > 0 ? `✅ ${h1Tags.length}` : "❌ Missing");
|
||||||
|
console.log("H2 Tags:", h2Tags.length > 0 ? `ℹ️ ${h2Tags.length}` : "⚠️ None");
|
||||||
|
console.log("Images:", imgs.length);
|
||||||
|
console.log(
|
||||||
|
"Missing Alt:",
|
||||||
|
missingAlt > 0 ? `❌ ${missingAlt}` : "✅ None"
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"Empty Alt:",
|
||||||
|
emptyAlt > 0 ? `⚠️ ${emptyAlt}` : "✅ None"
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"Duplicate Alt:",
|
||||||
|
duplicateAlt.length > 0 ? `⚠️ ${duplicateAlt.length}` : "✅ None"
|
||||||
|
);
|
||||||
|
console.log("Lazy Loaded Images:", lazyLoaded > 0 ? `✅ ${lazyLoaded}` : "⚠️ None");
|
||||||
|
console.log("Open Graph Tags:", ogTags.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Twitter Tags:", twitterTags.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Schema Markup:", schemaScripts.length > 0 ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Google Analytics:", hasAnalytics ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("GTM:", hasGTM ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Clarity:", hasClarity ? "✅ Found" : "⚠️ Missing");
|
||||||
|
console.log("Facebook Pixel:", hasFBPixel ? "✅ Found" : "⚠️ Missing");
|
||||||
|
|
||||||
|
if (brokenLinks.length > 0) {
|
||||||
|
console.log("\n❌ Broken Links:");
|
||||||
|
brokenLinks.forEach((b) => console.log(` → ${b.link} [${b.status}]`));
|
||||||
|
} else {
|
||||||
|
console.log("✅ No broken links found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Error on ${url}:`, err.message);
|
||||||
|
} finally {
|
||||||
|
await driver.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// 4️⃣ Run Full Site Audit
|
||||||
|
// ==========================
|
||||||
|
(async () => {
|
||||||
|
const sitemapUrl = "http://localhost:3000/sitemap.xml"; // your sitemap
|
||||||
|
const siteDomain = "https://skyandsoil.metatronnest.com"; // your domain
|
||||||
|
|
||||||
|
console.log("📄 Fetching URLs from sitemap...");
|
||||||
|
const urls = await getUrlsFromSitemap(sitemapUrl);
|
||||||
|
|
||||||
|
if (urls.length === 0) {
|
||||||
|
console.error("❌ No URLs found in sitemap.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Found ${urls.length} URLs in sitemap.`);
|
||||||
|
console.log("🚀 Starting Full SEO + Accessibility + Broken Link Audit...");
|
||||||
|
|
||||||
|
for (const url of urls) {
|
||||||
|
await checkSEO(url, siteDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\n✅ Full SEO Audit Completed!");
|
||||||
|
console.log(`📁 CSV Report: ${csvPath}`);
|
||||||
|
})();
|
||||||
@ -22,6 +22,7 @@ export default function AboutPage() {
|
|||||||
{ label: "Home", href: "/" },
|
{ label: "Home", href: "/" },
|
||||||
{ label: "About" }
|
{ label: "About" }
|
||||||
]}
|
]}
|
||||||
|
backgroundImage="/assets/images/about/about-banner.webp"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<About />
|
<About />
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export default function ContactPage() {
|
|||||||
{ label: "Home", href: "/" },
|
{ label: "Home", href: "/" },
|
||||||
{ label: "Contact" }
|
{ label: "Contact" }
|
||||||
]}
|
]}
|
||||||
|
backgroundImage="/assets/images/about/contact-banner.webp"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<ContactCTA />
|
<ContactCTA />
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
--color-noir-slate: #1C1C1C;
|
--color-noir-slate: #1C1C1C;
|
||||||
--color-porcelain-white: #FFFCF0;
|
--color-porcelain-white: #FFFCF0;
|
||||||
--color-limestone: #F3F1E6;
|
--color-limestone: #F3F1E6;
|
||||||
|
--color-gray-900: lab(8.11897% .811279 -12.254);
|
||||||
|
|
||||||
--font-sans: var(--font-inter);
|
--font-sans: var(--font-inter);
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,25 @@ const inter = Inter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
metadataBase: new URL("https://skyandsoil.metatronnest.com"),
|
||||||
title: {
|
title: {
|
||||||
default: "Sky and Soil | Premium Real Estate in North Bengaluru",
|
default: "Sky and Soil | Premium Real Estate in North Bengaluru",
|
||||||
template: "%s | Sky and Soil"
|
template: "%s | Sky and Soil"
|
||||||
},
|
},
|
||||||
description: "Discover luxury apartments, villas, and plots in North Bengaluru. Sky and Soil connects you with nature-inspired living spaces and Godrej Properties.",
|
description: "Discover luxury apartments, villas, and plots in North Bengaluru. Sky and Soil connects you with nature-inspired living spaces and Godrej Properties.",
|
||||||
keywords: ["Real Estate", "Bengaluru", "Luxury Homes", "Godrej Properties", "North Bengaluru", "Villas", "Apartments"],
|
keywords: ["Real Estate", "Bengaluru", "Luxury Homes", "Godrej Properties", "North Bengaluru", "Villas", "Apartments"],
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
googleBot: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
alternates: {
|
||||||
|
canonical: "./",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
import { ThemeProvider } from "@/components/ThemeProvider";
|
import { ThemeProvider } from "@/components/ThemeProvider";
|
||||||
@ -28,8 +41,16 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
|
<head>
|
||||||
|
{/* ✅ Meta Robots */}
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
|
||||||
|
{/* Canonical handled by metadata above */}
|
||||||
|
</head>
|
||||||
|
|
||||||
<body
|
<body
|
||||||
className={`${inter.variable} antialiased`}
|
className={`${inter.variable} antialiased`}
|
||||||
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
@ -40,7 +61,7 @@ export default function RootLayout({
|
|||||||
<CompareProvider>
|
<CompareProvider>
|
||||||
{children}
|
{children}
|
||||||
<CompareBar />
|
<CompareBar />
|
||||||
<MouseAnimation />
|
{/* <MouseAnimation /> */}
|
||||||
</CompareProvider>
|
</CompareProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export default function LifestylePage() {
|
|||||||
{ label: "Home", href: "/" },
|
{ label: "Home", href: "/" },
|
||||||
{ label: "Lifestyle" }
|
{ label: "Lifestyle" }
|
||||||
]}
|
]}
|
||||||
|
backgroundImage="/assets/images/about/lifestyle-banner.webp"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Lifestyle />
|
<Lifestyle />
|
||||||
|
|||||||
33
src/app/loading.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export default function Loading() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100vh",
|
||||||
|
backgroundColor: "#f5f5f5",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="loader"></div>
|
||||||
|
<style>{`
|
||||||
|
.loader {
|
||||||
|
border: 4px solid #e0e0e0;
|
||||||
|
border-top: 4px solid #4a90e2; /* Matching a standard blue/brand color or adjust if needed */
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
166
src/app/privacy-policy/page.tsx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
import InnerBanner from "@/components/InnerBanner";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Privacy Policy | Your Data, Security & Rights",
|
||||||
|
description: "Understand how your personal information is collected, used, and protected. Our Privacy Policy ensures transparency and your data rights.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PrivacyPolicy() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white dark:bg-black">
|
||||||
|
{/* Header */}
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* Inner Banner */}
|
||||||
|
<InnerBanner
|
||||||
|
title="Privacy Policy"
|
||||||
|
subtitle="Learn how we protect and manage your data"
|
||||||
|
breadcrumbs={[
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Privacy Policy" }
|
||||||
|
]}
|
||||||
|
backgroundImage="/assets/images/about/privacy-banner.webp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Page Content */}
|
||||||
|
<div className="max-w-4xl mx-auto px-6 py-20">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-8">
|
||||||
|
Privacy Policy
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||||
|
Last updated: {new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">1. Introduction</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Welcome to Sky and Soil. We respect your privacy and are committed to protecting your personal data.
|
||||||
|
This privacy policy will inform you about how we look after your personal data when you visit our website
|
||||||
|
and tell you about your privacy rights and how the law protects you.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">2. Information We Collect</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
We may collect, use, store and transfer different kinds of personal data about you which we have grouped together as follows:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li><strong>Identity Data:</strong> includes first name, last name, username or similar identifier.</li>
|
||||||
|
<li><strong>Contact Data:</strong> includes email address, telephone numbers, and mailing address.</li>
|
||||||
|
<li><strong>Technical Data:</strong> includes internet protocol (IP) address, browser type and version, time zone setting and location, browser plug-in types and versions, operating system and platform.</li>
|
||||||
|
<li><strong>Usage Data:</strong> includes information about how you use our website and services.</li>
|
||||||
|
<li><strong>Marketing and Communications Data:</strong> includes your preferences in receiving marketing from us and your communication preferences.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">3. How We Use Your Information</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
We will only use your personal data when the law allows us to. Most commonly, we will use your personal data in the following circumstances:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>To provide and maintain our services</li>
|
||||||
|
<li>To notify you about changes to our services</li>
|
||||||
|
<li>To provide customer support</li>
|
||||||
|
<li>To gather analysis or valuable information so that we can improve our services</li>
|
||||||
|
<li>To monitor the usage of our services</li>
|
||||||
|
<li>To detect, prevent and address technical issues</li>
|
||||||
|
<li>To provide you with news, special offers and general information about other goods, services and events which we offer</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">4. Data Security</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
We have put in place appropriate security measures to prevent your personal data from being accidentally lost,
|
||||||
|
used or accessed in an unauthorized way, altered or disclosed. In addition, we limit access to your personal data
|
||||||
|
to those employees, agents, contractors and other third parties who have a business need to know.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">5. Data Retention</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
We will only retain your personal data for as long as necessary to fulfil the purposes we collected it for,
|
||||||
|
including for the purposes of satisfying any legal, accounting, or reporting requirements.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">6. Your Legal Rights</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Under certain circumstances, you have rights under data protection laws in relation to your personal data, including the right to:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>Request access to your personal data</li>
|
||||||
|
<li>Request correction of your personal data</li>
|
||||||
|
<li>Request erasure of your personal data</li>
|
||||||
|
<li>Object to processing of your personal data</li>
|
||||||
|
<li>Request restriction of processing your personal data</li>
|
||||||
|
<li>Request transfer of your personal data</li>
|
||||||
|
<li>Right to withdraw consent</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">7. Cookies</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Our website uses cookies to distinguish you from other users of our website. This helps us to provide you
|
||||||
|
with a good experience when you browse our website and also allows us to improve our site.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">8. Third-Party Links</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Our website may include links to third-party websites, plug-ins and applications. Clicking on those links
|
||||||
|
or enabling those connections may allow third parties to collect or share data about you. We do not control
|
||||||
|
these third-party websites and are not responsible for their privacy statements.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">9. Contact Us</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
If you have any questions about this Privacy Policy, please contact us:
|
||||||
|
</p>
|
||||||
|
<ul className="list-none text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>Email: hello@skyandsoil.com</li>
|
||||||
|
<li>Phone: +91 80 1234 5678</li>
|
||||||
|
<li>Address: Bangalore, Karnataka</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="inline-flex items-center text-primary hover:underline"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke="currentColor"
|
||||||
|
className="w-5 h-5 mr-2"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />
|
||||||
|
</svg>
|
||||||
|
Back to Home
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,11 +1,99 @@
|
|||||||
import PropertiesClient from "@/components/PropertiesClient";
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
import InnerBanner from "@/components/InnerBanner";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Our Properties | Sky and Soil Real Estate",
|
title: "Our Projects | Premium Residential & Commercial Spaces",
|
||||||
description: "Browse our exclusive collection of premium apartments, villas, and plots in North Bengaluru. Find your perfect home with Sky and Soil.",
|
description: "Explore premium residential and commercial spaces built for comfort, convenience, and superior lifestyle experiences.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PropertiesPage() {
|
export default function ProjectsPage() {
|
||||||
return <PropertiesClient />;
|
const categories = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Residential Real Estate",
|
||||||
|
description: "Discover our premium residential properties featuring modern architecture, luxury amenities, and prime locations. From spacious apartments to exclusive villas, find your dream home with world-class facilities and exceptional living experiences.",
|
||||||
|
image: "/assets/images/projects/residential-real-estate.jpg",
|
||||||
|
href: "/residential-real-estate",
|
||||||
|
properties: "Residential Real Estate"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-50 dark:bg-black">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
<InnerBanner
|
||||||
|
title="Our Projects"
|
||||||
|
subtitle="Explore our diverse portfolio of real estate projects"
|
||||||
|
breadcrumbs={[
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Projects" }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="max-w-7xl mx-auto px-6 py-24">
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">
|
||||||
|
Project Categories
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||||
|
Browse through our carefully curated collection of properties
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-8">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<div
|
||||||
|
key={category.id}
|
||||||
|
className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-800 hover:border-primary dark:hover:border-primary shadow-lg hover:shadow-2xl transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-12 gap-0">
|
||||||
|
{/* Image Section - Left */}
|
||||||
|
<div className="md:col-span-5 relative h-64 md:h-96 overflow-hidden">
|
||||||
|
<Image
|
||||||
|
src={category.image}
|
||||||
|
alt={category.title}
|
||||||
|
fill
|
||||||
|
className="object-cover group-hover:scale-110 transition-transform duration-500"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent md:bg-gradient-to-r md:from-transparent md:to-black/10" />
|
||||||
|
|
||||||
|
{/* Badge */}
|
||||||
|
<div className="absolute top-4 left-4 bg-primary text-white px-4 py-2 rounded-full text-sm font-semibold shadow-lg">
|
||||||
|
{category.properties}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Section - Right */}
|
||||||
|
<div className="md:col-span-7 p-6 md:p-10 lg:p-12 flex flex-col justify-center">
|
||||||
|
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-foreground mb-4 group-hover:text-primary transition-colors">
|
||||||
|
{category.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 text-sm md:text-base lg:text-lg mb-8 leading-relaxed">
|
||||||
|
{category.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={category.href}
|
||||||
|
className="inline-flex items-center gap-2 px-8 py-4 bg-gradient-to-r from-primary to-blue-600 text-white rounded-xl font-semibold hover:shadow-xl transition-all duration-300 transform hover:scale-105 w-fit"
|
||||||
|
>
|
||||||
|
View More
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,294 +0,0 @@
|
|||||||
import { use } from "react";
|
|
||||||
import Header from "@/components/Header";
|
|
||||||
import Footer from "@/components/Footer";
|
|
||||||
import Image from "next/image";
|
|
||||||
import PropertyGallery from "@/components/PropertyGallery";
|
|
||||||
import PropertyNav from "@/components/PropertyNav";
|
|
||||||
import InnerBanner from "@/components/InnerBanner";
|
|
||||||
import { properties } from "@/data/properties";
|
|
||||||
import { notFound } from "next/navigation";
|
|
||||||
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
// Required for static site generation with dynamic routes
|
|
||||||
export function generateStaticParams() {
|
|
||||||
return properties.map((property) => ({
|
|
||||||
id: property.id.toString(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
|
|
||||||
const resolvedParams = await params;
|
|
||||||
const property = properties.find(p => p.id === parseInt(resolvedParams.id));
|
|
||||||
|
|
||||||
if (!property) {
|
|
||||||
return {
|
|
||||||
title: "Property Not Found | Sky and Soil",
|
|
||||||
description: "The requested property could not be found."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: `${property.title} | Sky and Soil Real Estate`,
|
|
||||||
description: `Explore ${property.title} in ${property.location}. ${property.overview.bhk} ${property.category} starting at ${property.price}.`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const sections = [
|
|
||||||
{ id: "overview", label: "Overview" },
|
|
||||||
{ id: "about", label: "About" },
|
|
||||||
{ id: "amenities", label: "Amenities" },
|
|
||||||
{ id: "floor-plans", label: "Floor Plans" },
|
|
||||||
{ id: "location", label: "Location" },
|
|
||||||
{ id: "pricing", label: "Pricing" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function PropertyDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
|
||||||
const resolvedParams = await params;
|
|
||||||
const property = properties.find(p => p.id === parseInt(resolvedParams.id));
|
|
||||||
|
|
||||||
if (!property) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50 dark:bg-black">
|
|
||||||
<Header />
|
|
||||||
|
|
||||||
<InnerBanner
|
|
||||||
title={property.title}
|
|
||||||
subtitle={property.location}
|
|
||||||
breadcrumbs={[
|
|
||||||
{ label: "Home", href: "/" },
|
|
||||||
{ label: "Properties", href: "/projects" },
|
|
||||||
{ label: property.title }
|
|
||||||
]}
|
|
||||||
backgroundImage={property.image}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
{/* Sticky Navigation */}
|
|
||||||
<PropertyNav sections={sections} />
|
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6 py-8">
|
|
||||||
{/* Property Header */}
|
|
||||||
<div className="bg-white dark:bg-gray-900 rounded-2xl p-8 mb-8 shadow-sm border border-gray-200 dark:border-gray-800">
|
|
||||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center gap-3 mb-3">
|
|
||||||
<h1 className="text-3xl md:text-4xl font-bold text-foreground">{property.title}</h1>
|
|
||||||
<span className="px-4 py-1.5 bg-gradient-to-r from-green-500 to-emerald-500 text-white rounded-full text-sm font-semibold shadow-md">
|
|
||||||
{property.status}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center text-gray-600 dark:text-gray-400">
|
|
||||||
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
|
||||||
</svg>
|
|
||||||
{property.location}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-right">
|
|
||||||
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Starting from</div>
|
|
||||||
<div className="text-4xl font-bold bg-gradient-to-r from-primary to-blue-600 bg-clip-text text-transparent">
|
|
||||||
{property.price}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Quick Stats */}
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 pt-6 border-t border-gray-200 dark:border-gray-800">
|
|
||||||
<div className="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-2xl font-bold text-primary mb-1">{property.overview.bhk}</div>
|
|
||||||
<div className="text-sm text-gray-600 dark:text-gray-400">Configuration</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-2xl font-bold text-primary mb-1">{property.overview.size}</div>
|
|
||||||
<div className="text-sm text-gray-600 dark:text-gray-400">Area</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-2xl font-bold text-primary mb-1">{property.overview.possession}</div>
|
|
||||||
<div className="text-sm text-gray-600 dark:text-gray-400">Possession</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-2xl font-bold text-primary mb-1">{property.overview.totalUnits}</div>
|
|
||||||
<div className="text-sm text-gray-600 dark:text-gray-400">Total Units</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Image Gallery */}
|
|
||||||
<PropertyGallery images={property.images} title={property.title} />
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
||||||
{/* Main Content */}
|
|
||||||
<div className="lg:col-span-2 space-y-8">
|
|
||||||
{/* Overview Section */}
|
|
||||||
<div id="overview" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
Overview
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
|
|
||||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-gray-500 dark:text-gray-400 text-sm mb-2">Property Type</div>
|
|
||||||
<div className="text-lg font-semibold text-foreground">{property.category}</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-gray-500 dark:text-gray-400 text-sm mb-2">RERA Status</div>
|
|
||||||
<div className="text-lg font-semibold text-green-600">Approved</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<div className="text-gray-500 dark:text-gray-400 text-sm mb-2">Availability</div>
|
|
||||||
<div className="text-lg font-semibold text-foreground">Available</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* About Section */}
|
|
||||||
<div id="about" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
About this Property
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-700 dark:text-gray-300 leading-relaxed text-lg">{property.description}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Amenities Section */}
|
|
||||||
<div id="amenities" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
Amenities
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
{property.amenities.map((amenity, idx) => (
|
|
||||||
<div key={idx} className="flex items-center gap-3 p-4 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 rounded-xl hover:shadow-md transition-shadow">
|
|
||||||
<div className="w-10 h-10 bg-primary rounded-full flex items-center justify-center flex-shrink-0">
|
|
||||||
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<span className="text-gray-700 dark:text-gray-300 font-medium">{amenity}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floor Plans Section */}
|
|
||||||
<div id="floor-plans" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
Floor Plans
|
|
||||||
</h2>
|
|
||||||
<div className="bg-gradient-to-br from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-900 rounded-2xl p-12 text-center">
|
|
||||||
<svg className="w-20 h-20 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
||||||
</svg>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 text-lg mb-4">Floor plans available on request</p>
|
|
||||||
<button className="px-6 py-3 bg-primary text-white rounded-lg hover:bg-blue-700 transition-colors font-medium">
|
|
||||||
Request Floor Plans
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Location Section */}
|
|
||||||
<div id="location" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
Location
|
|
||||||
</h2>
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 rounded-xl p-8 text-center">
|
|
||||||
<svg className="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
|
|
||||||
</svg>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">Interactive map coming soon</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Pricing Section */}
|
|
||||||
<div id="pricing" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32">
|
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-3">
|
|
||||||
<div className="w-1 h-8 bg-primary rounded-full"></div>
|
|
||||||
Pricing Details
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex justify-between items-center p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
|
|
||||||
<span className="text-gray-700 dark:text-gray-300">Base Price</span>
|
|
||||||
<span className="text-xl font-bold text-primary">{property.price}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400">* Prices are subject to change. Please contact us for the latest pricing and offers.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
|
||||||
<div className="lg:col-span-1">
|
|
||||||
<div className="sticky top-32 space-y-6">
|
|
||||||
{/* Contact Form */}
|
|
||||||
<div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-2xl p-6 shadow-lg">
|
|
||||||
<h3 className="text-xl font-bold text-foreground mb-4">Get in Touch</h3>
|
|
||||||
<form className="space-y-4">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Your Name"
|
|
||||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
placeholder="Email Address"
|
|
||||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
placeholder="Phone Number"
|
|
||||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all"
|
|
||||||
/>
|
|
||||||
<textarea
|
|
||||||
rows={4}
|
|
||||||
placeholder="Message (Optional)"
|
|
||||||
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="w-full bg-gradient-to-r from-primary to-blue-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-all transform hover:scale-105"
|
|
||||||
>
|
|
||||||
Request Callback
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Quick Actions */}
|
|
||||||
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-2xl p-6 border border-gray-200 dark:border-gray-700">
|
|
||||||
<h3 className="text-lg font-bold text-foreground mb-4">Quick Actions</h3>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<button className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:shadow-md transition-all text-foreground">
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
|
|
||||||
</svg>
|
|
||||||
Share Property
|
|
||||||
</button>
|
|
||||||
<button className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:shadow-md transition-all text-foreground">
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
||||||
</svg>
|
|
||||||
Save to Wishlist
|
|
||||||
</button>
|
|
||||||
<button className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:shadow-md transition-all text-foreground">
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
|
|
||||||
</svg>
|
|
||||||
Print Details
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
39
src/app/residential-real-estate/[slug]/page.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import PropertyDetailClient from "@/components/PropertyDetailClient";
|
||||||
|
import { properties } from "@/data/properties";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
// Generate static params for all properties
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
return properties.map((property) => ({
|
||||||
|
slug: property.slug,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate metadata for SEO
|
||||||
|
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
const property = properties.find((p) => p.slug === slug);
|
||||||
|
|
||||||
|
if (!property) {
|
||||||
|
return {
|
||||||
|
title: "Property Not Found",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: property.metaTitle || property.title,
|
||||||
|
description: property.metaDescription || property.description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PropertyDetailPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
|
const { slug } = await params;
|
||||||
|
const property = properties.find((p) => p.slug === slug);
|
||||||
|
|
||||||
|
if (!property) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PropertyDetailClient property={property} />;
|
||||||
|
}
|
||||||
11
src/app/residential-real-estate/page.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import PropertiesClient from "@/components/PropertiesClient";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Residential Real Estate | Sky and Soil",
|
||||||
|
description: "Browse our exclusive collection of premium apartments, villas, and plots in North Bengaluru.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ResidentialRealEstatePage() {
|
||||||
|
return <PropertiesClient />;
|
||||||
|
}
|
||||||
175
src/app/terms-of-service/page.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
import InnerBanner from "@/components/InnerBanner";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Terms of Service | Website Use & User Guidelines",
|
||||||
|
description: "Read the Terms of Service to know your rights, responsibilities, and the rules for using our website safely and effectively.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TermsOfService() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white dark:bg-black">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
<InnerBanner
|
||||||
|
title="Terms of Service"
|
||||||
|
subtitle="Please read our terms and conditions carefully"
|
||||||
|
breadcrumbs={[
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Terms of Service" }
|
||||||
|
]}
|
||||||
|
backgroundImage="/assets/images/about/terms-banner.webp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="max-w-4xl mx-auto px-6 py-20">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-8">
|
||||||
|
Terms of Service
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||||
|
Last updated: {new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">1. Agreement to Terms</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
By accessing and using the Sky and Soil website, you accept and agree to be bound by the terms and
|
||||||
|
provision of this agreement. If you do not agree to abide by the above, please do not use this service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">2. Use License</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Permission is granted to temporarily access the materials (information or software) on Sky and Soil's
|
||||||
|
website for personal, non-commercial transitory viewing only. This is the grant of a license, not a
|
||||||
|
transfer of title, and under this license you may not:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>Modify or copy the materials</li>
|
||||||
|
<li>Use the materials for any commercial purpose or for any public display (commercial or non-commercial)</li>
|
||||||
|
<li>Attempt to decompile or reverse engineer any software contained on Sky and Soil's website</li>
|
||||||
|
<li>Remove any copyright or other proprietary notations from the materials</li>
|
||||||
|
<li>Transfer the materials to another person or "mirror" the materials on any other server</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">3. Property Information Disclaimer</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
All property information, including but not limited to descriptions, photographs, floor plans, pricing,
|
||||||
|
and availability, is provided for informational purposes only and is subject to change without notice.
|
||||||
|
While we strive to ensure accuracy, we make no warranties or representations regarding the completeness
|
||||||
|
or accuracy of such information.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">4. User Responsibilities</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
As a user of this website, you agree to:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>Provide accurate and complete information when submitting inquiries or contact forms</li>
|
||||||
|
<li>Not use the website for any unlawful purpose or to solicit others to perform unlawful acts</li>
|
||||||
|
<li>Not infringe on the intellectual property rights of others</li>
|
||||||
|
<li>Not transmit any viruses, malware, or other malicious code</li>
|
||||||
|
<li>Not attempt to gain unauthorized access to any portion of the website</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">5. Intellectual Property</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
The content, organization, graphics, design, compilation, magnetic translation, digital conversion,
|
||||||
|
and other matters related to the website are protected under applicable copyrights, trademarks, and
|
||||||
|
other proprietary rights. The copying, redistribution, use, or publication by you of any such matters
|
||||||
|
or any part of the website is strictly prohibited.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">6. Limitation of Liability</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
In no event shall Sky and Soil or its suppliers be liable for any damages (including, without limitation,
|
||||||
|
damages for loss of data or profit, or due to business interruption) arising out of the use or inability
|
||||||
|
to use the materials on Sky and Soil's website, even if Sky and Soil or a Sky and Soil authorized
|
||||||
|
representative has been notified orally or in writing of the possibility of such damage.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">7. Accuracy of Materials</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
The materials appearing on Sky and Soil's website could include technical, typographical, or photographic
|
||||||
|
errors. Sky and Soil does not warrant that any of the materials on its website are accurate, complete, or
|
||||||
|
current. Sky and Soil may make changes to the materials contained on its website at any time without notice.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">8. Links to Third-Party Sites</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Sky and Soil has not reviewed all of the sites linked to its website and is not responsible for the
|
||||||
|
contents of any such linked site. The inclusion of any link does not imply endorsement by Sky and Soil
|
||||||
|
of the site. Use of any such linked website is at the user's own risk.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">9. Modifications to Terms</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
Sky and Soil may revise these terms of service for its website at any time without notice. By using
|
||||||
|
this website you are agreeing to be bound by the then current version of these terms of service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">10. Governing Law</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
These terms and conditions are governed by and construed in accordance with the laws of India, and you
|
||||||
|
irrevocably submit to the exclusive jurisdiction of the courts in Bangalore, Karnataka.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">11. Contact Information</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
|
If you have any questions about these Terms of Service, please contact us:
|
||||||
|
</p>
|
||||||
|
<ul className="list-none text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
|
<li>Email: hello@skyandsoil.com</li>
|
||||||
|
<li>Phone: +91 80 1234 5678</li>
|
||||||
|
<li>Address: Bangalore, Karnataka</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="inline-flex items-center text-primary hover:underline"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke="currentColor"
|
||||||
|
className="w-5 h-5 mr-2"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />
|
||||||
|
</svg>
|
||||||
|
Back to Home
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,44 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useRef, MouseEvent } from "react";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
import { FloatingHouse, RotatingKey, GrowingBuilding } from "./PropertyAnimations";
|
import { FloatingHouse, RotatingKey, GrowingBuilding } from "./PropertyAnimations";
|
||||||
|
import { motion, useScroll, useTransform } from "framer-motion";
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
export default function About() {
|
export default function About() {
|
||||||
const [rotation, setRotation] = useState({ x: -10, y: 0 });
|
const containerRef = useRef(null);
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const { scrollYProgress } = useScroll({
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
target: containerRef,
|
||||||
|
offset: ["start end", "end start"]
|
||||||
|
});
|
||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => {
|
// Parallax effect: Moving the image vertically as the user scrolls
|
||||||
if (!containerRef.current) return;
|
// The image height is significantly larger than the container
|
||||||
|
// We move from 0% (top aligned) to a negative percentage to reveal the bottom
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const y = useTransform(scrollYProgress, [0, 1], ["0%", "-50%"]);
|
||||||
const x = e.clientX - rect.left;
|
|
||||||
const y = e.clientY - rect.top;
|
|
||||||
|
|
||||||
const width = rect.width;
|
|
||||||
const height = rect.height;
|
|
||||||
|
|
||||||
// Calculate rotation based on mouse position
|
|
||||||
// Y-axis rotation (horizontal mouse movement): -180 to 180 degrees
|
|
||||||
const rotateY = ((x / width) * 360) - 180;
|
|
||||||
|
|
||||||
// X-axis rotation (vertical mouse movement): -90 (top view) to 90 (bottom view)
|
|
||||||
// Inverting Y so top of screen corresponds to seeing the top face
|
|
||||||
const rotateX = -(((y / height) * 180) - 90);
|
|
||||||
|
|
||||||
setRotation({ x: rotateX, y: rotateY });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
|
||||||
setIsHovering(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
|
||||||
setIsHovering(false);
|
|
||||||
setRotation({ x: -10, y: 0 }); // Reset to default angle
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="about" className="py-24 bg-white dark:bg-black relative overflow-hidden">
|
<section id="about" className="py-24 bg-white dark:bg-black relative overflow-hidden">
|
||||||
@ -55,7 +32,7 @@ export default function About() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6 relative z-10">
|
<div className="max-w-7xl mx-auto px-6 relative z-10">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-16 items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
||||||
{/* Text Content */}
|
{/* Text Content */}
|
||||||
<div className="space-y-8 z-10">
|
<div className="space-y-8 z-10">
|
||||||
<h2 className="text-4xl font-bold tracking-tight text-foreground">
|
<h2 className="text-4xl font-bold tracking-tight text-foreground">
|
||||||
@ -72,136 +49,40 @@ export default function About() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="text-primary font-medium hover:text-blue-700 transition-colors flex items-center gap-2 group">
|
<Link href="/about" className="text-primary font-medium hover:text-blue-700 transition-colors flex items-center gap-2 group">
|
||||||
Learn More About Us
|
Learn More About Us
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-4 h-4 group-hover:translate-x-1 transition-transform">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-4 h-4 group-hover:translate-x-1 transition-transform">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3D Cube Container */}
|
{/* Parallax Image Container */}
|
||||||
<div
|
<div
|
||||||
className="h-[500px] w-full flex items-center justify-center perspective-container cursor-move"
|
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onMouseMove={handleMouseMove}
|
className="
|
||||||
onMouseEnter={handleMouseEnter}
|
h-[400px]
|
||||||
onMouseLeave={handleMouseLeave}
|
w-full
|
||||||
|
mx-auto
|
||||||
|
rounded-2xl
|
||||||
|
shadow-2xl
|
||||||
|
overflow-hidden
|
||||||
|
relative
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div
|
<motion.div
|
||||||
className={`cube relative w-64 h-64 md:w-80 md:h-80 transform-style-3d ${!isHovering ? 'animate-spin-slow' : ''}`}
|
style={{ y }}
|
||||||
style={isHovering ? { transform: `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)` } : {}}
|
className="absolute top-[-10%] left-0 w-full h-[160%] will-change-transform"
|
||||||
>
|
>
|
||||||
{/* Front Face */}
|
<img
|
||||||
<div className="absolute inset-0 transform translate-z-32 md:translate-z-40 bg-white shadow-xl border border-white/20">
|
src="/assets/images/home/where.webp"
|
||||||
<Image
|
alt="Where Sky Meets Soil"
|
||||||
src="/assets/images/front-side.jfif"
|
className="w-full h-full object-cover"
|
||||||
alt="Front View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Front View</div>
|
</motion.div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Back Face */}
|
|
||||||
<div className="absolute inset-0 transform rotate-y-90 translate-z-32 md:translate-z-40 bg-white shadow-xl border border-white/20">
|
|
||||||
<Image
|
|
||||||
src="/assets/images/back-side.jfif"
|
|
||||||
alt="Back View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Back View</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Face */}
|
|
||||||
<div className="absolute inset-0 transform rotate-y-90 translate-z-32 md:translate-z-40 bg-white shadow-xl border border-white/20">
|
|
||||||
<Image
|
|
||||||
src="/assets/images/right-side.jfif"
|
|
||||||
alt="Right View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Right View</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Left Face */}
|
|
||||||
<div className="absolute inset-0 transform -rotate-y-90 translate-z-32 md:translate-z-40 bg-white shadow-xl border border-white/20">
|
|
||||||
<Image
|
|
||||||
src="/assets/images/left-side.jfif"
|
|
||||||
alt="Left View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Left View</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Top Face */}
|
|
||||||
<div className="absolute inset-0 transform rotate-x-90 translate-z-32 md:translate-z-40 bg-gray-100 border border-white/20 flex items-center justify-center">
|
|
||||||
<Image
|
|
||||||
src="/assets/images/top-side.jfif"
|
|
||||||
alt="Top View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Top View</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Bottom Face */}
|
|
||||||
<div className="absolute inset-0 transform -rotate-x-90 translate-z-32 md:translate-z-40 bg-gray-100 border border-white/20 flex items-center justify-center shadow-2xl">
|
|
||||||
<Image
|
|
||||||
src="/assets/images/bottom-side.jfif"
|
|
||||||
alt="Bottom View"
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute bottom-4 left-4 bg-black/70 text-white text-xs px-2 py-1 rounded">Bottom View</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style jsx global>{`
|
|
||||||
.perspective-container {
|
|
||||||
perspective: 1000px;
|
|
||||||
}
|
|
||||||
.transform-style-3d {
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
transition: transform 0.1s ease-out; /* Smooth transition for mouse movement */
|
|
||||||
}
|
|
||||||
.translate-z-32 {
|
|
||||||
transform: rotateY(0deg) translateZ(8rem);
|
|
||||||
}
|
|
||||||
.translate-z-40 {
|
|
||||||
transform: rotateY(0deg) translateZ(10rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom transforms for faces */
|
|
||||||
.cube > div:nth-child(1) { transform: rotateY(0deg) translateZ(128px); }
|
|
||||||
.cube > div:nth-child(2) { transform: rotateY(180deg) translateZ(128px); }
|
|
||||||
.cube > div:nth-child(3) { transform: rotateY(90deg) translateZ(128px); }
|
|
||||||
.cube > div:nth-child(4) { transform: rotateY(-90deg) translateZ(128px); }
|
|
||||||
.cube > div:nth-child(5) { transform: rotateX(90deg) translateZ(128px); }
|
|
||||||
.cube > div:nth-child(6) { transform: rotateX(-90deg) translateZ(128px); }
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.cube > div:nth-child(1) { transform: rotateY(0deg) translateZ(160px); }
|
|
||||||
.cube > div:nth-child(2) { transform: rotateY(180deg) translateZ(160px); }
|
|
||||||
.cube > div:nth-child(3) { transform: rotateY(90deg) translateZ(160px); }
|
|
||||||
.cube > div:nth-child(4) { transform: rotateY(-90deg) translateZ(160px); }
|
|
||||||
.cube > div:nth-child(5) { transform: rotateX(90deg) translateZ(160px); }
|
|
||||||
.cube > div:nth-child(6) { transform: rotateX(-90deg) translateZ(160px); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin-slow {
|
|
||||||
from { transform: rotateX(-10deg) rotateY(0deg); }
|
|
||||||
to { transform: rotateX(-10deg) rotateY(360deg); }
|
|
||||||
}
|
|
||||||
.animate-spin-slow {
|
|
||||||
animation: spin-slow 20s linear infinite;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,13 +15,24 @@ export default function CompareBar() {
|
|||||||
<div className="flex items-center justify-between gap-4">
|
<div className="flex items-center justify-between gap-4">
|
||||||
<div className="flex items-center gap-4 flex-1 overflow-x-auto hide-scrollbar">
|
<div className="flex items-center gap-4 flex-1 overflow-x-auto hide-scrollbar">
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
<svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
className="w-7 h-7 text-primary"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M4 7h11m0 0l-4-4m4 4l-4 4M20 17H9m0 0l4-4m-4 4l4 4"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span className="font-semibold text-foreground">Compare Properties ({compareList.length}/4)</span>
|
<span className="font-semibold text-foreground">Compare Properties ({compareList.length}/4)</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3 pt-3 pr-3">
|
||||||
{compareList.map((property) => (
|
{compareList.map((property) => (
|
||||||
<div key={property.id} className="relative group flex-shrink-0">
|
<div key={property.id} className="relative group flex-shrink-0">
|
||||||
<div className="w-20 h-20 rounded-lg overflow-hidden border-2 border-gray-200 dark:border-gray-700">
|
<div className="w-20 h-20 rounded-lg overflow-hidden border-2 border-gray-200 dark:border-gray-700">
|
||||||
|
|||||||
@ -59,6 +59,8 @@ export default function CompareClient() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
|
<h2 className="text-3xl font-bold text-foreground mb-8">Property Comparison</h2>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full border-collapse">
|
<table className="w-full border-collapse">
|
||||||
<thead>
|
<thead>
|
||||||
@ -230,6 +232,7 @@ export default function CompareClient() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
287
src/components/ConnectivityMap.tsx
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
||||||
|
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';
|
||||||
|
import L from 'leaflet';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
|
||||||
|
// Fix for default markers in Next.js
|
||||||
|
const DefaultIcon = L.icon({
|
||||||
|
iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png',
|
||||||
|
shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png',
|
||||||
|
iconSize: [25, 41],
|
||||||
|
iconAnchor: [12, 41]
|
||||||
|
});
|
||||||
|
L.Marker.prototype.options.icon = DefaultIcon;
|
||||||
|
|
||||||
|
// Custom Icons
|
||||||
|
const createCustomIcon = (color: string, number?: string) => {
|
||||||
|
return L.divIcon({
|
||||||
|
className: 'custom-icon',
|
||||||
|
html: `<div style="
|
||||||
|
background-color: ${color};
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 3px solid white;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
">${number || ''}</div>`,
|
||||||
|
iconSize: [36, 36],
|
||||||
|
iconAnchor: [18, 18],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const homeIcon = L.divIcon({
|
||||||
|
className: 'home-icon',
|
||||||
|
html: `<div style="
|
||||||
|
background-color: #7C3AED;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 4px solid white;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
color: white;
|
||||||
|
">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="width: 28px; height: 28px;">
|
||||||
|
<path d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.06l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.69z" />
|
||||||
|
<path d="M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 01-.75-.75v-4.5a.75.75 0 00-.75-.75h-3a.75.75 0 00-.75.75V21a.75.75 0 01-.75.75H5.625a1.875 1.875 0 01-1.875-1.875v-6.198a2.29 2.29 0 00.091-.086L12 5.43z" />
|
||||||
|
</svg>
|
||||||
|
</div>`,
|
||||||
|
iconSize: [48, 48],
|
||||||
|
iconAnchor: [24, 24],
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Mock Data
|
||||||
|
type LocationData = {
|
||||||
|
category: string;
|
||||||
|
items: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
dist: string;
|
||||||
|
time: string;
|
||||||
|
lat: number;
|
||||||
|
lng: number;
|
||||||
|
count: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROPERTY_LOCATION: [number, number] = [12.9385, 77.7297]; // Approximate Varthur
|
||||||
|
|
||||||
|
const MOCK_DATA: Record<string, LocationData['items']> = {
|
||||||
|
'Commute': [
|
||||||
|
{ id: 1, name: "Dommasandra Circle Metro Station", dist: "3.62 Km", time: "7 mins", lat: 12.9250, lng: 77.7450, count: 6 },
|
||||||
|
{ id: 2, name: "Sompura Metro Station", dist: "7.05 Km", time: "15 mins", lat: 12.9100, lng: 77.7600, count: 4 },
|
||||||
|
{ id: 3, name: "Sarjapur Metro Station", dist: "8.28 Km", time: "18 mins", lat: 12.8900, lng: 77.7800, count: 3 },
|
||||||
|
{ id: 4, name: "Ambedkar nagar Metro Station", dist: "8.89 Km", time: "18 mins", lat: 12.9550, lng: 77.7100, count: 5 },
|
||||||
|
],
|
||||||
|
'Education': [
|
||||||
|
{ id: 5, name: "Whitefield Global School", dist: "2.5 Km", time: "6 mins", lat: 12.9550, lng: 77.7350, count: 8 },
|
||||||
|
{ id: 6, name: "Greenwood High", dist: "4.1 Km", time: "10 mins", lat: 12.9150, lng: 77.7550, count: 5 },
|
||||||
|
],
|
||||||
|
'Hospitals': [
|
||||||
|
{ id: 7, name: "Manipal Hospital Varthur", dist: "1.2 Km", time: "4 mins", lat: 12.9420, lng: 77.7320, count: 2 },
|
||||||
|
],
|
||||||
|
'Work': [
|
||||||
|
{ id: 8, name: "RGA Tech Park", dist: "5.5 Km", time: "12 mins", lat: 12.9050, lng: 77.7150, count: 12 },
|
||||||
|
],
|
||||||
|
'Entertainment': [
|
||||||
|
{ id: 9, name: "Nexus Whitefield", dist: "3.2 Km", time: "9 mins", lat: 12.9600, lng: 77.7400, count: 7 },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
function MapController({ center }: { center: [number, number] }) {
|
||||||
|
const map = useMap();
|
||||||
|
useEffect(() => {
|
||||||
|
map.setView(center, 13);
|
||||||
|
}, [center, map]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ZoomHandler({ zoomIn, zoomOut }: { zoomIn: () => void, zoomOut: () => void }) {
|
||||||
|
return (
|
||||||
|
<div className="absolute top-4 left-4 flex flex-col gap-2 z-[400]">
|
||||||
|
<button
|
||||||
|
onClick={zoomIn}
|
||||||
|
className="w-8 h-8 bg-white dark:bg-gray-800 rounded shadow-md flex items-center justify-center hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||||
|
>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-bold text-lg">+</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={zoomOut}
|
||||||
|
className="w-8 h-8 bg-white dark:bg-gray-800 rounded shadow-md flex items-center justify-center hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||||
|
style={{ lineHeight: '0' }}
|
||||||
|
>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-bold text-lg">−</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function ConnectivityMap() {
|
||||||
|
const [activeTab, setActiveTab] = useState("Commute");
|
||||||
|
const tabsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeTab && tabsRef.current) {
|
||||||
|
const activeBtn = document.getElementById(`connectivity-tab-${activeTab}`);
|
||||||
|
if (activeBtn) {
|
||||||
|
const container = tabsRef.current;
|
||||||
|
const activeBtnRect = activeBtn.getBoundingClientRect();
|
||||||
|
const containerRect = container.getBoundingClientRect();
|
||||||
|
const scrollLeft = container.scrollLeft + (activeBtnRect.left - containerRect.left) - (container.offsetWidth / 2) + (activeBtn.offsetWidth / 2);
|
||||||
|
|
||||||
|
container.scrollTo({
|
||||||
|
left: scrollLeft,
|
||||||
|
behavior: "smooth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeTab]);
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [mapZoom, setMapZoom] = useState(13);
|
||||||
|
const [mapRef, setMapRef] = useState<L.Map | null>(null);
|
||||||
|
|
||||||
|
const activeData = useMemo(() => {
|
||||||
|
let data = activeTab === "Search"
|
||||||
|
? Object.values(MOCK_DATA).flat()
|
||||||
|
: MOCK_DATA[activeTab] || [];
|
||||||
|
|
||||||
|
if (searchQuery) {
|
||||||
|
data = data.filter(item =>
|
||||||
|
item.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}, [activeTab, searchQuery]);
|
||||||
|
|
||||||
|
const handleZoomIn = () => {
|
||||||
|
if (mapRef) mapRef.zoomIn();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleZoomOut = () => {
|
||||||
|
if (mapRef) mapRef.zoomOut();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative z-0 bg-white dark:bg-gray-900 rounded-xl overflow-hidden border border-gray-200 dark:border-gray-700">
|
||||||
|
{/* Tabs */}
|
||||||
|
<div ref={tabsRef} className="flex overflow-x-auto border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 scrollbar-hide">
|
||||||
|
{["Commute", "Education", "Hospitals", "Work", "Entertainment", "Search"].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
id={`connectivity-tab-${tab}`}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveTab(tab);
|
||||||
|
setSearchQuery(""); // Clear search when switching tabs
|
||||||
|
}}
|
||||||
|
className={`px-4 py-3 text-sm font-medium whitespace-nowrap flex items-center gap-2 transition-colors border-b-2 ${activeTab === tab
|
||||||
|
? "text-orange-500 border-orange-500 bg-orange-50/30 dark:bg-orange-900/10"
|
||||||
|
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 border-transparent"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Icons */}
|
||||||
|
{tab === "Commute" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path d="M6.5 3c-1.051 0-2.093.04-3.125.117A1.49 1.49 0 002 4.607V10.5h9V4.606c0-.771-.59-1.43-1.375-1.489A41.568 41.568 0 006.5 3zM2 12v2.5A1.5 1.5 0 003.5 16h.041a3 3 0 015.918 0h.791a.75.75 0 00.75-.75V12H2z" /><path d="M6.5 18a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM13.25 5a.75.75 0 00-.75.75v8.514a3.001 3.001 0 014.893 1.44c.37-.275.61-.719.595-1.227a24.905 24.905 0 00-1.784-8.549A1.486 1.486 0 0014.823 5H13.25zM14.5 18a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" /></svg>}
|
||||||
|
{tab === "Education" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path d="M10.75 16.82A7.462 7.462 0 0115 15.5c.71 0 1.396.098 2.046.282A.75.75 0 0018 15.06v-11a.75.75 0 00-.546-.721A9.006 9.006 0 0015 3a8.963 8.963 0 00-4.25 1.065V16.82zM9.25 4.065A8.963 8.963 0 005 3c-.85 0-1.673.118-2.454.339A.75.75 0 002 4.06v11a.75.75 0 00.954.721A7.506 7.506 0 015 15.5c1.579 0 3.042.487 4.25 1.32V4.065z" /></svg>}
|
||||||
|
{tab === "Hospitals" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path fillRule="evenodd" d="M10 2a6 6 0 00-6 6c0 1.887-.454 3.665-1.257 5.234a.75.75 0 00.515 1.076 32.91 32.91 0 003.256.508 3.5 3.5 0 006.972 0 32.903 32.903 0 003.256-.508.75.75 0 00.515-1.076A11.448 11.448 0 0116 8a6 6 0 00-6-6zM8.05 14.943a33.54 33.54 0 003.9 0 2 2 0 01-3.9 0z" clipRule="evenodd" /></svg>}
|
||||||
|
{tab === "Work" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path fillRule="evenodd" d="M4 16.5v-13h-.25a.75.75 0 010-1.5h12.5a.75.75 0 010 1.5H16v13h.25a.75.75 0 010 1.5h-3.5a.75.75 0 01-.75-.75v-2.5a.75.75 0 00-.75-.75h-2.5a.75.75 0 00-.75.75v2.5a.75.75 0 01-.75.75h-3.5a.75.75 0 010-1.5H4zm3-11a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zM7.5 9a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1zM11 5.5a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm.5 3.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1z" clipRule="evenodd" /></svg>}
|
||||||
|
{tab === "Entertainment" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path fillRule="evenodd" d="M6 5v1H4.667a1.75 1.75 0 00-1.743 1.598l-.826 9.5A1.75 1.75 0 003.84 19H16.16a1.75 1.75 0 001.743-1.902l-.826-9.5A1.75 1.75 0 0015.333 6H14V5a4 4 0 00-8 0zm4-2.5A2.5 2.5 0 007.5 5v1h5V5A2.5 2.5 0 0010 2.5zM7.5 10a2.5 2.5 0 005 0V8.75a.75.75 0 011.5 0V10a4 4 0 01-8 0V8.75a.75.75 0 011.5 0V10z" clipRule="evenodd" /></svg>}
|
||||||
|
{tab === "Search" && <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"><path fillRule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clipRule="evenodd" /></svg>}
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col lg:flex-row h-[450px] relative">
|
||||||
|
{/* Map Area */}
|
||||||
|
<div className="flex-1 h-full relative">
|
||||||
|
<MapContainer
|
||||||
|
center={PROPERTY_LOCATION}
|
||||||
|
zoom={13}
|
||||||
|
style={{ height: '100%', width: '100%' }}
|
||||||
|
zoomControl={false}
|
||||||
|
ref={setMapRef}
|
||||||
|
>
|
||||||
|
<TileLayer
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
/>
|
||||||
|
<MapController center={PROPERTY_LOCATION} />
|
||||||
|
<ZoomHandler zoomIn={handleZoomIn} zoomOut={handleZoomOut} />
|
||||||
|
|
||||||
|
{/* Property Marker */}
|
||||||
|
<Marker position={PROPERTY_LOCATION} icon={homeIcon} />
|
||||||
|
|
||||||
|
{/* POI Markers */}
|
||||||
|
{activeData.map((item) => (
|
||||||
|
<Marker
|
||||||
|
key={item.id}
|
||||||
|
position={[item.lat, item.lng]}
|
||||||
|
icon={createCustomIcon('#F97316', item.count.toString())}
|
||||||
|
>
|
||||||
|
<Popup>{item.name}</Popup>
|
||||||
|
</Marker>
|
||||||
|
))}
|
||||||
|
</MapContainer>
|
||||||
|
|
||||||
|
{/* Layer Toggle Button (Bottom Left) */}
|
||||||
|
<div className="absolute bottom-4 left-4 z-[400]">
|
||||||
|
<button className="w-10 h-10 bg-white dark:bg-gray-800 rounded shadow-md flex items-center justify-center hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 text-gray-600 dark:text-gray-400">
|
||||||
|
<path fillRule="evenodd" d="M3.25 3A2.25 2.25 0 001 5.25v9.5A2.25 2.25 0 003.25 17h9.5A2.25 2.25 0 0015 14.75v-9.5A2.25 2.25 0 0012.75 3h-9.5zm9.5 1.5a.75.75 0 00-.75.75V8a.75.75 0 001.5 0V5.25a.75.75 0 00-.75-.75zm0 5.5a.75.75 0 00-.75.75v2.75a.75.75 0 001.5 0v-2.75a.75.75 0 00-.75-.75zM10 7a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 7zm-3.25.75a.75.75 0 00-1.5 0v4.5a.75.75 0 001.5 0v-4.5z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating List Card (Right Side) */}
|
||||||
|
<div className="absolute top-4 right-4 bottom-4 w-full max-w-sm z-[400] flex flex-col pointer-events-none">
|
||||||
|
<div className="rounded-lg shadow-xl overflow-hidden flex flex-col h-full pointer-events-auto border border-gray-100 dark:border-gray-800">
|
||||||
|
{/* Search Input for Search Tab */}
|
||||||
|
{activeTab === "Search" && (
|
||||||
|
<div className="p-3 border-b border-gray-100 dark:border-gray-800">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search location..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="w-full px-4 py-2 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto custom-scrollbar p-2">
|
||||||
|
{activeData.length > 0 ? (
|
||||||
|
activeData.map((item) => (
|
||||||
|
<div key={item.id} className="p-4 mb-2 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 rounded-lg cursor-pointer transition-all border border-gray-100 dark:border-gray-800 shadow-sm hover:shadow-md">
|
||||||
|
<h4 className="font-semibold text-gray-900 dark:text-white text-base mb-2">{item.name}</h4>
|
||||||
|
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<span className="font-medium">{item.dist}</span>
|
||||||
|
<span className="text-orange-300">|</span>
|
||||||
|
<span className="text-gray-500 font-medium">{item.time}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="p-8 text-center text-gray-400">
|
||||||
|
No results found
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,59 +1,247 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { User, Phone, Mail, MapPin, Send } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { useState, ChangeEvent, FormEvent, useEffect } from "react";
|
||||||
|
import ReCAPTCHA from "react-google-recaptcha";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
location: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormErrors {
|
||||||
|
name?: string;
|
||||||
|
phone?: string;
|
||||||
|
email?: string;
|
||||||
|
location?: string;
|
||||||
|
captcha?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function ContactCTA() {
|
export default function ContactCTA() {
|
||||||
|
const [formData, setFormData] = useState<FormData>({
|
||||||
|
name: "",
|
||||||
|
phone: "",
|
||||||
|
email: "",
|
||||||
|
location: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [captchaToken, setCaptchaToken] = useState<string | null>(null);
|
||||||
|
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
||||||
|
const [alert, setAlert] = useState<{ show: boolean; type: string; message: string }>({
|
||||||
|
show: false,
|
||||||
|
type: "",
|
||||||
|
message: "",
|
||||||
|
});
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||||
|
// Clear error when user types
|
||||||
|
if (formErrors[name as keyof FormErrors]) {
|
||||||
|
setFormErrors(prev => ({ ...prev, [name]: undefined }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCaptchaChange = (token: string | null) => {
|
||||||
|
setCaptchaToken(token);
|
||||||
|
if (token && formErrors.captcha) {
|
||||||
|
setFormErrors(prev => ({ ...prev, captcha: undefined }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e: FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const errors: FormErrors = {};
|
||||||
|
if (!formData.name.trim()) errors.name = "Name is required.";
|
||||||
|
if (!formData.phone.trim()) errors.phone = "Phone number is required.";
|
||||||
|
if (!formData.email.trim()) errors.email = "Email is required.";
|
||||||
|
if (!formData.location) errors.location = "Please select a location.";
|
||||||
|
if (!captchaToken) errors.captcha = "Please verify the CAPTCHA.";
|
||||||
|
|
||||||
|
setFormErrors(errors);
|
||||||
|
if (Object.keys(errors).length > 0) return;
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
|
||||||
|
const emailData = {
|
||||||
|
name: formData.name,
|
||||||
|
phone: formData.phone,
|
||||||
|
email: formData.email,
|
||||||
|
subject: `New Inquiry from ${formData.name} - ${formData.location}`,
|
||||||
|
message: `Name: ${formData.name}<br />Phone: ${formData.phone}<br />Email: ${formData.email}<br />Location Interest: ${formData.location}`,
|
||||||
|
to: "hello@skyandsoil.com",
|
||||||
|
senderName: "Sky and Soil Contact Form",
|
||||||
|
recaptchaToken: captchaToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post("https://mailserver.metatronnest.com/send", emailData, {
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
setAlert({
|
||||||
|
show: true,
|
||||||
|
type: "success",
|
||||||
|
message: res?.data?.message || "Message sent successfully! We will contact you soon.",
|
||||||
|
});
|
||||||
|
|
||||||
|
setFormData({ name: "", phone: "", email: "", location: "" });
|
||||||
|
setCaptchaToken(null);
|
||||||
|
setFormErrors({});
|
||||||
|
} catch (error) {
|
||||||
|
setAlert({
|
||||||
|
show: true,
|
||||||
|
type: "danger",
|
||||||
|
message: "Failed to send message. Please try again later.",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (alert.show) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setAlert(prev => ({ ...prev, show: false }));
|
||||||
|
}, 5000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [alert.show]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="contact" className="py-24 bg-secondary dark:bg-gray-900">
|
<section id="contact" className="relative py-24 bg-[#f3f1e6] dark:bg-gray-900">
|
||||||
<div className="max-w-4xl mx-auto px-6">
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-3xl shadow-xl overflow-hidden">
|
<div className="relative z-10 max-w-6xl mx-auto px-6">
|
||||||
<div className="p-8 md:p-12 text-center">
|
<div className="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl overflow-hidden border border-gray-100 dark:border-gray-700 flex flex-col md:flex-row">
|
||||||
|
|
||||||
|
{/* Left Side - Form */}
|
||||||
|
<div className="w-full md:w-1/2 p-8 md:p-12">
|
||||||
|
<div className="text-left mb-8">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
|
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
|
||||||
Ready to Visit Aurora Springs?
|
Ready to Visit Aurora Springs?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-8">
|
<p className="text-gray-600 dark:text-gray-400">
|
||||||
Schedule a private tour or request a call back from our relationship manager.
|
Schedule a private tour or request a call back from our relationship manager.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form className="space-y-4 max-w-md mx-auto">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
<div>
|
{alert.show && (
|
||||||
|
<div className={`p-4 rounded-xl text-sm font-medium ${alert.type === 'success' ? 'bg-green-50 text-green-700 border border-green-200' : 'bg-red-50 text-red-700 border border-red-200'}`}>
|
||||||
|
{alert.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors">
|
||||||
|
<User size={20} />
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={handleChange}
|
||||||
placeholder="Your Name"
|
placeholder="Your Name"
|
||||||
className="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-2 focus:ring-blue-100 dark:focus:ring-blue-900 outline-none transition-all"
|
className={`w-full pl-12 pr-4 py-4 rounded-xl bg-gray-50 dark:bg-gray-700/50 border ${formErrors.name ? 'border-red-500' : 'border-gray-200 dark:border-gray-600'} text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-4 focus:ring-primary/10 outline-none transition-all duration-300`}
|
||||||
/>
|
/>
|
||||||
|
{formErrors.name && <small className="text-red-500 text-xs ml-1">{formErrors.name}</small>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors">
|
||||||
|
<Phone size={20} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
|
name="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleChange}
|
||||||
placeholder="Phone Number"
|
placeholder="Phone Number"
|
||||||
className="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-2 focus:ring-blue-100 dark:focus:ring-blue-900 outline-none transition-all"
|
className={`w-full pl-12 pr-4 py-4 rounded-xl bg-gray-50 dark:bg-gray-700/50 border ${formErrors.phone ? 'border-red-500' : 'border-gray-200 dark:border-gray-600'} text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-4 focus:ring-primary/10 outline-none transition-all duration-300`}
|
||||||
/>
|
/>
|
||||||
|
{formErrors.phone && <small className="text-red-500 text-xs ml-1">{formErrors.phone}</small>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors">
|
||||||
|
<Mail size={20} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
placeholder="Email Address"
|
placeholder="Email Address"
|
||||||
className="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-2 focus:ring-blue-100 dark:focus:ring-blue-900 outline-none transition-all"
|
className={`w-full pl-12 pr-4 py-4 rounded-xl bg-gray-50 dark:bg-gray-700/50 border ${formErrors.email ? 'border-red-500' : 'border-gray-200 dark:border-gray-600'} text-foreground placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary focus:ring-4 focus:ring-primary/10 outline-none transition-all duration-300`}
|
||||||
/>
|
/>
|
||||||
|
{formErrors.email && <small className="text-red-500 text-xs ml-1">{formErrors.email}</small>}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<select className="w-full px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 focus:border-primary focus:ring-2 focus:ring-blue-100 dark:focus:ring-blue-900 outline-none transition-all">
|
<div className="relative group">
|
||||||
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-primary transition-colors">
|
||||||
|
<MapPin size={20} />
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
name="location"
|
||||||
|
value={formData.location}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`w-full pl-12 pr-4 py-4 rounded-xl bg-gray-50 dark:bg-gray-700/50 border ${formErrors.location ? 'border-red-500' : 'border-gray-200 dark:border-gray-600'} text-gray-600 dark:text-gray-300 focus:border-primary focus:ring-4 focus:ring-primary/10 outline-none transition-all duration-300 appearance-none`}
|
||||||
|
>
|
||||||
<option value="">Select Preferred Location</option>
|
<option value="">Select Preferred Location</option>
|
||||||
<option value="north-bengaluru">North Bengaluru</option>
|
<option value="North Bengaluru">North Bengaluru</option>
|
||||||
<option value="whitefield">Whitefield</option>
|
<option value="Whitefield">Whitefield</option>
|
||||||
<option value="sarjapur">Sarjapur</option>
|
<option value="Sarjapur">Sarjapur</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none">
|
||||||
|
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{formErrors.location && <small className="text-red-500 text-xs ml-1">{formErrors.location}</small>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2">
|
||||||
|
<ReCAPTCHA
|
||||||
|
sitekey="6Lea8ZYrAAAAAHaghaLjDx_K084IFATZby7Rzqhl"
|
||||||
|
onChange={handleCaptchaChange}
|
||||||
|
/>
|
||||||
|
{formErrors.captcha && <small className="text-red-500 text-xs ml-1">{formErrors.captcha}</small>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full py-3.5 text-base font-medium text-white bg-primary rounded-xl hover:bg-blue-600 transition-all shadow-lg hover:shadow-xl active:scale-95"
|
disabled={isSubmitting}
|
||||||
|
className={`w-full py-4 text-base font-semibold text-white bg-primary rounded-xl hover:bg-blue-600 transition-all shadow-lg hover:shadow-primary/30 active:scale-[0.98] flex items-center justify-center gap-2 group ${isSubmitting ? 'opacity-70 cursor-not-allowed' : ''}`}
|
||||||
>
|
>
|
||||||
Request a Call Back
|
<span>{isSubmitting ? 'Sending...' : 'Request a Call Back'}</span>
|
||||||
|
{!isSubmitting && <Send size={18} className="group-hover:translate-x-1 transition-transform" />}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p className="mt-6 text-xs text-gray-400 dark:text-gray-500">
|
<p className="mt-6 text-xs text-gray-400 dark:text-gray-500 flex items-center gap-2">
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
We respect your privacy. No spam, ever.
|
We respect your privacy. No spam, ever.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Right Side - Image */}
|
||||||
|
<div className="w-full md:w-1/2 relative min-h-[400px] md:min-h-full">
|
||||||
|
<Image
|
||||||
|
src="/assets/images/home/ready.webp"
|
||||||
|
alt="Luxury Interior"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent md:bg-gradient-to-l" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
const faqs = [
|
const faqs = [
|
||||||
@ -27,9 +27,9 @@ const faqs = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const carouselImages = [
|
const carouselImages = [
|
||||||
{ src: "/1-f63fe3ad.png", label: "Building Exterior" },
|
{ src: "/assets/images/home/faq.webp", label: "Building Exterior" },
|
||||||
{ src: "/hero-image.jpg", label: "Luxury Amenities" },
|
{ src: "/assets/images/home/faq-2.webp", label: "Luxury Amenities" },
|
||||||
{ src: "/1-f63fe3ad.png", label: "Modern Architecture" }
|
{ src: "/assets/images/home/faq-3.webp", label: "Modern Architecture" }
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function FAQ() {
|
export default function FAQ() {
|
||||||
@ -38,6 +38,85 @@ export default function FAQ() {
|
|||||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
|
|
||||||
|
// Derive activeTab from activeCard to ensure sync
|
||||||
|
const activeTab = carouselImages[activeCard].label;
|
||||||
|
|
||||||
|
// Reset open FAQ when active card changes
|
||||||
|
useEffect(() => {
|
||||||
|
setOpenIndex(null);
|
||||||
|
}, [activeCard]);
|
||||||
|
|
||||||
|
const faqs = [
|
||||||
|
// Building Exterior
|
||||||
|
{
|
||||||
|
category: "Building Exterior",
|
||||||
|
question: "What materials are used for the exterior?",
|
||||||
|
answer: "We use high-grade, weather-resistant materials including natural stone cladding and premium texture paints to ensure durability and elegance."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Building Exterior",
|
||||||
|
question: "Is the building earthquake resistant?",
|
||||||
|
answer: "Yes, all our structures are RCC framed and designed to be earthquake resistant, adhering to the highest safety standards and seismic zones."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Building Exterior",
|
||||||
|
question: "How is the exterior maintenance handled?",
|
||||||
|
answer: "The society association manages exterior maintenance, including regular cleaning and painting, ensuring the building retains its pristine look for years."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Building Exterior",
|
||||||
|
question: "Are there eco-friendly features in the design?",
|
||||||
|
answer: "Absolutely. We incorporate vertical gardens, solar reflective paints, and rainwater harvesting systems into the building's exterior design."
|
||||||
|
},
|
||||||
|
|
||||||
|
// Luxury Amenities
|
||||||
|
{
|
||||||
|
category: "Luxury Amenities",
|
||||||
|
question: "What recreational facilities are available?",
|
||||||
|
answer: "Residents enjoy access to a fully equipped clubhouse, temperature-controlled swimming pool, indoor games room, and a dedicated yoga deck."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Luxury Amenities",
|
||||||
|
question: "Is there a gym within the premises?",
|
||||||
|
answer: "Yes, we provide a state-of-the-art gymnasium with modern cardio and strength training equipment, open 24/7 for residents."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Luxury Amenities",
|
||||||
|
question: "Are there play areas for children?",
|
||||||
|
answer: "We have safe, designated play zones for children of all ages, featuring modern play equipment and soft flooring for safety."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Luxury Amenities",
|
||||||
|
question: "Do you offer concierge services?",
|
||||||
|
answer: "Yes, our premium properties feature a concierge desk to assist residents with daily tasks, guest management, and facility bookings."
|
||||||
|
},
|
||||||
|
|
||||||
|
// Modern Architecture
|
||||||
|
{
|
||||||
|
category: "Modern Architecture",
|
||||||
|
question: "What is the architectural style of the project?",
|
||||||
|
answer: "Our projects feature contemporary architecture with clean lines, open floor plans, and large windows to maximize natural light and ventilation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Modern Architecture",
|
||||||
|
question: "Who are the architects behind the design?",
|
||||||
|
answer: "We collaborate with award-winning international architects who specialize in creating sustainable and aesthetically stunning living spaces."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Modern Architecture",
|
||||||
|
question: "Are the homes Vastu compliant?",
|
||||||
|
answer: "Yes, the majority of our units are designed in accordance with Vastu Shastra principles to ensure harmony and positive energy flow."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Modern Architecture",
|
||||||
|
question: "How is privacy ensured in the design?",
|
||||||
|
answer: "The layout is thoughtfully designed to ensure no two apartments look into each other, providing maximum privacy for all residents."
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const categories = carouselImages.map(img => img.label);
|
||||||
|
const filteredFaqs = faqs.filter(faq => faq.category === activeTab);
|
||||||
|
|
||||||
const toggleFAQ = (index: number) => {
|
const toggleFAQ = (index: number) => {
|
||||||
setOpenIndex(openIndex === index ? null : index);
|
setOpenIndex(openIndex === index ? null : index);
|
||||||
};
|
};
|
||||||
@ -136,17 +215,36 @@ export default function FAQ() {
|
|||||||
|
|
||||||
{/* Right Column: FAQ Content */}
|
{/* Right Column: FAQ Content */}
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-12">
|
<div className="mb-8">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
|
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
|
||||||
Frequently Asked Questions
|
Frequently Asked Questions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-gray-600 dark:text-gray-400">
|
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8">
|
||||||
Everything you need to know about our properties and services.
|
Everything you need to know about our properties and services.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-8">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<button
|
||||||
|
key={category}
|
||||||
|
onClick={() => {
|
||||||
|
const index = categories.indexOf(category);
|
||||||
|
setActiveCard(index);
|
||||||
|
}}
|
||||||
|
className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${activeTab === category
|
||||||
|
? "bg-primary text-white shadow-lg scale-105"
|
||||||
|
: "bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{faqs.map((faq, index) => (
|
{filteredFaqs.map((faq, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="bg-white dark:bg-black border border-gray-200 dark:border-gray-800 rounded-2xl overflow-hidden transition-all duration-300 hover:shadow-md"
|
className="bg-white dark:bg-black border border-gray-200 dark:border-gray-800 rounded-2xl overflow-hidden transition-all duration-300 hover:shadow-md"
|
||||||
|
|||||||
@ -4,14 +4,14 @@ import Image from "next/image";
|
|||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-gray-50 dark:bg-black pt-16 pb-8 border-t border-gray-200 dark:border-gray-800">
|
<footer className="bg-gray-50 dark:bg-black pt-20 pb-10 border-t border-gray-200 dark:border-gray-800">
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-12 mb-12">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-12 mb-12">
|
||||||
<div className="col-span-1 md:col-span-1">
|
<div className="col-span-1 md:col-span-1">
|
||||||
<Link href="/" className="inline-block relative w-48 h-16">
|
<Link href="/" className="inline-block relative w-48 h-16">
|
||||||
<Image
|
<Image
|
||||||
src="/assets/images/blue-logo.png"
|
src="/assets/images/blue-logo.png"
|
||||||
alt="Sky and Soil Logo"
|
alt="Sky and Soil Footer Logo"
|
||||||
fill
|
fill
|
||||||
className="object-contain"
|
className="object-contain"
|
||||||
/>
|
/>
|
||||||
@ -24,20 +24,18 @@ export default function Footer() {
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-foreground mb-4">Quick Links</h4>
|
<h4 className="font-semibold text-foreground mb-4">Quick Links</h4>
|
||||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<li><Link href="#about" className="hover:text-primary transition-colors">About Us</Link></li>
|
<li><Link href="/about" className="hover:text-primary transition-colors">About Us</Link></li>
|
||||||
<li><Link href="#projects" className="hover:text-primary transition-colors">Projects</Link></li>
|
<li><Link href="/projects" className="hover:text-primary transition-colors">Projects</Link></li>
|
||||||
<li><Link href="#lifestyle" className="hover:text-primary transition-colors">Lifestyle</Link></li>
|
<li><Link href="/lifestyle" className="hover:text-primary transition-colors">Lifestyle</Link></li>
|
||||||
<li><Link href="#contact" className="hover:text-primary transition-colors">Contact</Link></li>
|
<li><Link href="/contact" className="hover:text-primary transition-colors">Contact</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-foreground mb-4">Legal</h4>
|
<h4 className="font-semibold text-foreground mb-4">Legal</h4>
|
||||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<li><Link href="#" className="hover:text-primary transition-colors">Privacy Policy</Link></li>
|
<li><Link href="/privacy-policy" className="hover:text-primary transition-colors">Privacy Policy</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary transition-colors">Terms of Service</Link></li>
|
<li><Link href="/terms-of-service" className="hover:text-primary transition-colors">Terms of Service</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary transition-colors">Disclaimer</Link></li>
|
|
||||||
<li><Link href="#" className="hover:text-primary transition-colors">RERA Compliance</Link></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -51,9 +49,20 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-gray-200 dark:border-gray-800 pt-8 flex flex-col md:flex-row items-center justify-between text-xs text-gray-400 dark:text-gray-500">
|
<div className="border-t border-gray-200 dark:border-gray-800 pt-8 flex flex-col items-center justify-center text-xs text-gray-400 dark:text-gray-500 text-center gap-2">
|
||||||
<p>© {new Date().getFullYear()} Sky and Soil. All rights reserved.</p>
|
<p>
|
||||||
<p>Designed with precision.</p>
|
© {new Date().getFullYear()} Sky and Soil | Powered by
|
||||||
|
<a
|
||||||
|
href="https://metatroncubesolutions.com/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="ml-1"
|
||||||
|
style={{ color: "#11147e", textDecoration: "none" }}
|
||||||
|
>
|
||||||
|
MetatronCube
|
||||||
|
</a>
|
||||||
|
— All Rights Reserved
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -1,15 +1,20 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Set initial scroll state
|
||||||
|
setIsScrolled(window.scrollY > 50);
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
setIsScrolled(window.scrollY > 50);
|
setIsScrolled(window.scrollY > 50);
|
||||||
};
|
};
|
||||||
@ -18,22 +23,52 @@ export default function Header() {
|
|||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Prevent hydration mismatch
|
||||||
|
// if (!mounted) {
|
||||||
|
// return (
|
||||||
|
// <header className="fixed top-0 left-0 right-0 z-50 bg-transparent py-6">
|
||||||
|
// <div className="w-full px-[50px] max-[375px]:px-[25px] flex items-center justify-between">
|
||||||
|
// <Link href="/" className="flex items-center gap-3 group">
|
||||||
|
// <div className="relative w-60 h-20 -left-[83px]">
|
||||||
|
// <Image
|
||||||
|
// src="/assets/images/white-logo.png"
|
||||||
|
// alt="Sky and Soil Logo"
|
||||||
|
// fill
|
||||||
|
// className="object-contain"
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// </Link>
|
||||||
|
// <div className="flex items-center gap-4">
|
||||||
|
// <button className="p-2 -mr-2 text-white">
|
||||||
|
// <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
||||||
|
// <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||||
|
// </svg>
|
||||||
|
// </button>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// </header>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
const isLight = resolvedTheme === "light";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header
|
<header
|
||||||
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled
|
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled
|
||||||
? "bg-white/80 dark:bg-black/80 backdrop-blur-md shadow-sm py-4"
|
? "bg-white/25 dark:bg-black/25 backdrop-blur-md shadow-sm py-4"
|
||||||
: "bg-transparent py-6"
|
: "bg-transparent py-6"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="max-w-7xl mx-auto px-6 flex items-center justify-between">
|
<div className="w-full px-[50px] max-[375px]:px-[25px] flex items-center justify-between">
|
||||||
<Link href="/" className="flex items-center gap-3 group">
|
<Link href="/" className="flex items-center gap-3 group">
|
||||||
<div className="relative w-60 h-20 transition-transform group-hover:scale-105">
|
<div className="relative w-60 h-20 transition-transform group-hover:scale-105 -left-[83px]">
|
||||||
<Image
|
<Image
|
||||||
src={isScrolled ? "/assets/images/blue-logo.png" : "/assets/images/white-logo.png"}
|
src={(isScrolled && isLight) ? "/assets/images/blue-logo.png" : "/assets/images/white-logo.png"}
|
||||||
alt="Sky and Soil Logo"
|
alt="Sky and Soil Logo"
|
||||||
fill
|
fill
|
||||||
className="object-contain"
|
className="object-contain"
|
||||||
|
priority
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <span className={`text-2xl font-semibold tracking-tight group-hover:text-primary transition-colors ${isScrolled ? "text-foreground" : "text-white"
|
{/* <span className={`text-2xl font-semibold tracking-tight group-hover:text-primary transition-colors ${isScrolled ? "text-foreground" : "text-white"
|
||||||
@ -46,14 +81,25 @@ export default function Header() {
|
|||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`p-2 -mr-2 transition-colors ${isScrolled ? "text-foreground" : "text-white"
|
className={`p-2 -mr-2 ${(isScrolled && isLight) ? "text-primary" : "text-white"}`}
|
||||||
}`}
|
|
||||||
onClick={() => setIsSidebarOpen(true)}
|
onClick={() => setIsSidebarOpen(true)}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke="currentColor"
|
||||||
|
className="w-6 h-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -12,12 +12,13 @@ export default function Hero() {
|
|||||||
playsInline
|
playsInline
|
||||||
className="absolute inset-0 w-full h-full object-cover"
|
className="absolute inset-0 w-full h-full object-cover"
|
||||||
poster="/assets/images/aerial-view.mov" // Fallback image
|
poster="/assets/images/aerial-view.mov" // Fallback image
|
||||||
|
aria-label="Aerial view of Bangalore showcasing premium real estate locations"
|
||||||
>
|
>
|
||||||
<source src="/assets/images/aerial-view.mov" type="video/mp4" />
|
<source src="/assets/images/aerial-view.mov" type="video/mp4" />
|
||||||
{/* Note: This is a placeholder video. Replace with your Bangalore drone shot. */}
|
{/* Note: This is a placeholder video. Replace with your Bangalore drone shot. */}
|
||||||
</video>
|
</video>
|
||||||
{/* Overlay */}
|
|
||||||
<div className="absolute inset-0 bg-black/50 dark:bg-black/60 backdrop-blur-[1px]"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Location Pins (Decorative) */}
|
{/* Location Pins (Decorative) */}
|
||||||
|
|||||||
@ -16,12 +16,12 @@ export default function InnerBanner({
|
|||||||
backgroundImage = "/hero-image.jpg"
|
backgroundImage = "/hero-image.jpg"
|
||||||
}: InnerBannerProps) {
|
}: InnerBannerProps) {
|
||||||
return (
|
return (
|
||||||
<div className="relative h-[450px] w-full overflow-hidden">
|
<div className="relative h-[450px] w-full overflow-hidden pt-20">
|
||||||
{/* Background Image */}
|
{/* Background Image */}
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<Image
|
<Image
|
||||||
src={backgroundImage}
|
src={backgroundImage}
|
||||||
alt={title}
|
alt={`${title} - Sky and Soil Real Estate`}
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
priority
|
priority
|
||||||
@ -35,9 +35,9 @@ export default function InnerBanner({
|
|||||||
{/* Breadcrumbs */}
|
{/* Breadcrumbs */}
|
||||||
{breadcrumbs && breadcrumbs.length > 0 && (
|
{breadcrumbs && breadcrumbs.length > 0 && (
|
||||||
<nav className="mb-4">
|
<nav className="mb-4">
|
||||||
<ol className="flex items-center gap-2 text-sm text-white/80">
|
<ol className="flex flex-wrap items-center gap-x-2 gap-y-2 text-sm text-white/80">
|
||||||
{breadcrumbs.map((crumb, index) => (
|
{breadcrumbs.map((crumb, index) => (
|
||||||
<li key={index} className="flex items-center gap-2">
|
<li key={index} className="flex items-center gap-1 md:gap-2">
|
||||||
{crumb.href ? (
|
{crumb.href ? (
|
||||||
<a
|
<a
|
||||||
href={crumb.href}
|
href={crumb.href}
|
||||||
|
|||||||
@ -44,8 +44,8 @@ export default function Lifestyle() {
|
|||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/hero-image.jpg"
|
src="/assets/images/home/experience.webp"
|
||||||
alt="Lifestyle"
|
alt="Aurora Lifestyle Clubhouse and Amenities"
|
||||||
fill
|
fill
|
||||||
className={`object-cover transition-transform duration-200 ease-out ${zoomProps.show ? 'scale-[2.5]' : 'scale-100'}`}
|
className={`object-cover transition-transform duration-200 ease-out ${zoomProps.show ? 'scale-[2.5]' : 'scale-100'}`}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
|
|||||||
<button
|
<button
|
||||||
key={category}
|
key={category}
|
||||||
onClick={() => setActiveTab(category)}
|
onClick={() => setActiveTab(category)}
|
||||||
className={`px-6 py-2.5 rounded-full text-sm font-medium transition-all duration-300 ${activeTab === category
|
className={`px-4 py-2.5 rounded-full text-sm font-medium transition-all duration-300 ${activeTab === category
|
||||||
? "bg-white dark:bg-gray-700 text-foreground shadow-sm"
|
? "bg-white dark:bg-gray-700 text-foreground shadow-sm"
|
||||||
: "text-gray-500 dark:text-gray-400 hover:text-foreground"
|
: "text-gray-500 dark:text-gray-400 hover:text-foreground"
|
||||||
}`}
|
}`}
|
||||||
@ -95,12 +95,12 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
|
|||||||
<div
|
<div
|
||||||
key={property.id}
|
key={property.id}
|
||||||
className={activeTab === "All"
|
className={activeTab === "All"
|
||||||
? "min-w-[300px] md:min-w-[380px] snap-center"
|
? "flex-shrink-0 snap-center w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)]"
|
||||||
: "flex-shrink-0 w-full max-w-md mx-auto snap-center"
|
: "flex-shrink-0 w-full max-w-md mx-auto snap-center"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/properties/${property.id}`}
|
href={`/residential-real-estate/${property.slug}`}
|
||||||
className="group/card block bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-100 dark:border-gray-800 hover:border-gray-200 dark:hover:border-gray-700 shadow-lg hover:shadow-2xl transition-all duration-300 h-full flex flex-col"
|
className="group/card block bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-100 dark:border-gray-800 hover:border-gray-200 dark:hover:border-gray-700 shadow-lg hover:shadow-2xl transition-all duration-300 h-full flex flex-col"
|
||||||
>
|
>
|
||||||
{/* Image */}
|
{/* Image */}
|
||||||
@ -154,7 +154,7 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{filteredProperties.map((property) => (
|
{filteredProperties.map((property) => (
|
||||||
<Link
|
<Link
|
||||||
href={`/properties/${property.id}`}
|
href={`/residential-real-estate/${property.slug}`}
|
||||||
key={property.id}
|
key={property.id}
|
||||||
className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-100 dark:border-gray-800 hover:border-gray-200 dark:hover:border-gray-700 shadow-sm hover:shadow-xl transition-all duration-300 flex flex-col"
|
className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-100 dark:border-gray-800 hover:border-gray-200 dark:hover:border-gray-700 shadow-sm hover:shadow-xl transition-all duration-300 flex flex-col"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -72,12 +72,15 @@ export default function PropertiesClient() {
|
|||||||
{ label: "Home", href: "/" },
|
{ label: "Home", href: "/" },
|
||||||
{ label: "Properties" }
|
{ label: "Properties" }
|
||||||
]}
|
]}
|
||||||
|
backgroundImage="/assets/images/projects/projects-banner.webp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<PropertyFilters onFilterChange={handleFilterChange} />
|
<PropertyFilters onFilterChange={handleFilterChange} />
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6 py-12">
|
<div className="max-w-7xl mx-auto px-6 py-12">
|
||||||
|
<h2 className="text-3xl font-bold text-foreground mb-8">Browse Our Properties</h2>
|
||||||
|
|
||||||
{/* Property Grid */}
|
{/* Property Grid */}
|
||||||
{filteredProperties.length > 0 ? (
|
{filteredProperties.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
|||||||
@ -62,17 +62,18 @@ export default function PropertyCard({ property }: PropertyCardProps) {
|
|||||||
setIsWishlisted(false);
|
setIsWishlisted(false);
|
||||||
} else {
|
} else {
|
||||||
// Add to wishlist
|
// Add to wishlist
|
||||||
wishlist.push(property.id);
|
const updated = [...wishlist, property.id];
|
||||||
localStorage.setItem("wishlist", JSON.stringify(wishlist));
|
localStorage.setItem("wishlist", JSON.stringify(updated));
|
||||||
setIsWishlisted(true);
|
setIsWishlisted(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/properties/${property.id}`}>
|
<Link
|
||||||
<div className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden shadow-md hover:shadow-2xl transition-all duration-300 border border-gray-200 dark:border-gray-800">
|
href={`/residential-real-estate/${property.slug}`}
|
||||||
{/* Image Section */}
|
className="group block bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-100 dark:border-gray-800 hover:border-gray-200 dark:hover:border-gray-700 shadow-sm hover:shadow-xl transition-all duration-300"
|
||||||
<div className="relative h-64 overflow-hidden">
|
>
|
||||||
|
<div className="relative h-64 w-full overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={property.image}
|
src={property.image}
|
||||||
alt={property.title}
|
alt={property.title}
|
||||||
@ -102,9 +103,21 @@ export default function PropertyCard({ property }: PropertyCardProps) {
|
|||||||
}`}
|
}`}
|
||||||
aria-label={inCompare ? "Remove from compare" : "Add to compare"}
|
aria-label={inCompare ? "Remove from compare" : "Add to compare"}
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="w-5 h-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M8 7h8v10H8zM4 11h8v10H4z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleShareClick}
|
onClick={handleShareClick}
|
||||||
@ -146,7 +159,7 @@ export default function PropertyCard({ property }: PropertyCardProps) {
|
|||||||
|
|
||||||
<div className="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-800">
|
<div className="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-800">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl font-bold text-primary">{property.price}</div>
|
<div className="text-2xl font-bold text-primary whitespace-nowrap">{property.price}</div>
|
||||||
<div className="text-sm text-gray-500 dark:text-gray-400">{property.overview.size}</div>
|
<div className="text-sm text-gray-500 dark:text-gray-400">{property.overview.size}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
@ -155,7 +168,6 @@ export default function PropertyCard({ property }: PropertyCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
2493
src/components/PropertyDetailClient.tsx
Normal file
@ -49,10 +49,10 @@ export default function PropertyFilters({ onFilterChange }: PropertyFiltersProps
|
|||||||
const hasActiveFilters = filters.search || filters.type !== "all" || filters.budgetMin || filters.budgetMax || filters.bhk !== "all" || filters.sortBy !== "popularity";
|
const hasActiveFilters = filters.search || filters.type !== "all" || filters.budgetMin || filters.budgetMax || filters.bhk !== "all" || filters.sortBy !== "popularity";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 sticky top-0 z-40">
|
<div className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 lg:sticky lg:top-28 lg:z-40">
|
||||||
<div className="max-w-7xl mx-auto px-6 py-4">
|
<div className="max-w-7xl mx-auto px-6 py-4">
|
||||||
{/* Main Filter Bar */}
|
{/* Main Filter Bar */}
|
||||||
<div className="flex flex-col md:flex-row gap-4 items-center">
|
<div className="flex flex-col [@media(min-width:920px)]:flex-row gap-4 items-center">
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="flex-1 w-full">
|
<div className="flex-1 w-full">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|||||||
@ -1,151 +1,313 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface PropertyGalleryProps {
|
interface PropertyGalleryProps {
|
||||||
|
|
||||||
images: string[];
|
images: string[];
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function PropertyGallery({ images, title }: PropertyGalleryProps) {
|
export default function PropertyGallery({ images, title }: PropertyGalleryProps) {
|
||||||
|
|
||||||
const [activeImage, setActiveImage] = useState(0);
|
const [activeImage, setActiveImage] = useState(0);
|
||||||
|
|
||||||
const [showLightbox, setShowLightbox] = useState(false);
|
const [showLightbox, setShowLightbox] = useState(false);
|
||||||
|
|
||||||
const [lightboxIndex, setLightboxIndex] = useState(0);
|
const [lightboxIndex, setLightboxIndex] = useState(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const openLightbox = (index: number) => {
|
const openLightbox = (index: number) => {
|
||||||
|
|
||||||
setLightboxIndex(index);
|
setLightboxIndex(index);
|
||||||
|
|
||||||
setShowLightbox(true);
|
setShowLightbox(true);
|
||||||
|
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const closeLightbox = () => {
|
const closeLightbox = () => {
|
||||||
|
|
||||||
setShowLightbox(false);
|
setShowLightbox(false);
|
||||||
|
|
||||||
document.body.style.overflow = 'unset';
|
document.body.style.overflow = 'unset';
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Ensure we have at least 5 images for the gallery (1 big + 4 small)
|
||||||
|
|
||||||
|
const displayImages = [...images];
|
||||||
|
|
||||||
|
while (displayImages.length < 5) {
|
||||||
|
|
||||||
|
displayImages.push("/assets/images/projects/details/barca-2.webp");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
setLightboxIndex((prev) => (prev + 1) % images.length);
|
|
||||||
|
setLightboxIndex((prev) => (prev + 1) % displayImages.length);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const prevImage = () => {
|
const prevImage = () => {
|
||||||
setLightboxIndex((prev) => (prev - 1 + images.length) % images.length);
|
|
||||||
|
setLightboxIndex((prev) => (prev - 1 + displayImages.length) % displayImages.length);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||||
|
|
||||||
<div className="md:col-span-2 relative h-[500px] rounded-2xl overflow-hidden group cursor-pointer" onClick={() => openLightbox(activeImage)}>
|
<div className="md:col-span-2 relative h-[500px] rounded-2xl overflow-hidden group cursor-pointer" onClick={() => openLightbox(activeImage)}>
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={images[activeImage]}
|
|
||||||
|
src={displayImages[activeImage]}
|
||||||
|
|
||||||
alt={title}
|
alt={title}
|
||||||
|
|
||||||
fill
|
fill
|
||||||
|
|
||||||
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors duration-300" />
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors duration-300" />
|
||||||
|
|
||||||
<div className="absolute bottom-4 right-4 bg-black/70 text-white px-3 py-1.5 rounded-lg text-sm opacity-0 group-hover:opacity-100 transition-opacity">
|
<div className="absolute bottom-4 right-4 bg-black/70 text-white px-3 py-1.5 rounded-lg text-sm opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
|
||||||
Click to enlarge
|
Click to enlarge
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-1 gap-4">
|
|
||||||
{images.slice(0, 2).map((img, idx) => (
|
<div className="grid grid-cols-2 grid-rows-2 gap-4 h-[500px]">
|
||||||
|
|
||||||
|
{displayImages.slice(0, 3).map((img, idx) => (
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
||||||
key={idx}
|
key={idx}
|
||||||
|
|
||||||
onClick={() => setActiveImage(idx)}
|
onClick={() => setActiveImage(idx)}
|
||||||
className={`relative h-[160px] rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ${activeImage === idx ? 'ring-4 ring-primary scale-105' : 'hover:scale-105'
|
|
||||||
|
className={`relative w-full h-full rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ${activeImage === idx ? 'ring-4 ring-primary scale-95' : 'hover:scale-95'
|
||||||
|
|
||||||
}`}
|
}`}
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<Image src={img} alt={`View ${idx + 1}`} fill className="object-cover" />
|
<Image src={img} alt={`View ${idx + 1}`} fill className="object-cover" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
))}
|
))}
|
||||||
{/* View All Photos Button */}
|
|
||||||
|
{/* View All Photos Button (4th slot) */}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
||||||
onClick={() => openLightbox(0)}
|
onClick={() => openLightbox(0)}
|
||||||
className="relative h-[160px] rounded-xl overflow-hidden cursor-pointer group"
|
|
||||||
|
className="relative w-full h-full rounded-xl overflow-hidden cursor-pointer group"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={images[2] || images[0]}
|
|
||||||
|
src={displayImages[3]}
|
||||||
|
|
||||||
alt="View all photos"
|
alt="View all photos"
|
||||||
|
|
||||||
fill
|
fill
|
||||||
|
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="absolute inset-0 bg-black/60 group-hover:bg-black/70 transition-colors flex flex-col items-center justify-center text-white">
|
<div className="absolute inset-0 bg-black/60 group-hover:bg-black/70 transition-colors flex flex-col items-center justify-center text-white">
|
||||||
|
|
||||||
<svg className="w-10 h-10 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-10 h-10 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span className="font-semibold text-lg">View All Photos</span>
|
<span className="font-semibold text-lg">View All Photos</span>
|
||||||
<span className="text-sm opacity-90">({images.length} images)</span>
|
|
||||||
|
<span className="text-sm opacity-90">({displayImages.length} images)</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Lightbox Modal */}
|
{/* Lightbox Modal */}
|
||||||
|
|
||||||
{showLightbox && (
|
{showLightbox && (
|
||||||
|
|
||||||
<div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center">
|
<div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center">
|
||||||
|
|
||||||
{/* Close Button */}
|
{/* Close Button */}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
||||||
onClick={closeLightbox}
|
onClick={closeLightbox}
|
||||||
|
|
||||||
className="absolute top-4 right-4 text-white hover:text-gray-300 transition-colors z-10"
|
className="absolute top-4 right-4 text-white hover:text-gray-300 transition-colors z-10"
|
||||||
|
|
||||||
aria-label="Close gallery"
|
aria-label="Close gallery"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Image Counter */}
|
{/* Image Counter */}
|
||||||
|
|
||||||
<div className="absolute top-4 left-1/2 -translate-x-1/2 text-white text-lg font-medium bg-black/50 px-4 py-2 rounded-full">
|
<div className="absolute top-4 left-1/2 -translate-x-1/2 text-white text-lg font-medium bg-black/50 px-4 py-2 rounded-full">
|
||||||
{lightboxIndex + 1} / {images.length}
|
|
||||||
|
{lightboxIndex + 1} / {displayImages.length}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Previous Button */}
|
{/* Previous Button */}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
||||||
onClick={prevImage}
|
onClick={prevImage}
|
||||||
|
|
||||||
className="absolute left-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
className="absolute left-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
||||||
|
|
||||||
aria-label="Previous image"
|
aria-label="Previous image"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Main Image */}
|
{/* Main Image */}
|
||||||
|
|
||||||
<div className="relative w-full h-full max-w-6xl max-h-[90vh] mx-4">
|
<div className="relative w-full h-full max-w-6xl max-h-[90vh] mx-4">
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={images[lightboxIndex]}
|
|
||||||
|
src={displayImages[lightboxIndex]}
|
||||||
|
|
||||||
alt={`${title} - Image ${lightboxIndex + 1}`}
|
alt={`${title} - Image ${lightboxIndex + 1}`}
|
||||||
|
|
||||||
fill
|
fill
|
||||||
|
|
||||||
className="object-contain"
|
className="object-contain"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Next Button */}
|
{/* Next Button */}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
||||||
onClick={nextImage}
|
onClick={nextImage}
|
||||||
|
|
||||||
className="absolute right-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
className="absolute right-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
||||||
|
|
||||||
aria-label="Next image"
|
aria-label="Next image"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Thumbnail Strip */}
|
{/* Thumbnail Strip */}
|
||||||
|
|
||||||
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 overflow-x-auto max-w-[90vw] px-4 py-2 bg-black/50 rounded-lg hide-scrollbar">
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 overflow-x-auto max-w-[90vw] px-4 py-2 bg-black/50 rounded-lg hide-scrollbar">
|
||||||
{images.map((img, idx) => (
|
|
||||||
|
{displayImages.map((img, idx) => (
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
||||||
key={idx}
|
key={idx}
|
||||||
|
|
||||||
onClick={() => setLightboxIndex(idx)}
|
onClick={() => setLightboxIndex(idx)}
|
||||||
|
|
||||||
className={`relative w-20 h-20 flex-shrink-0 rounded-lg overflow-hidden cursor-pointer transition-all ${lightboxIndex === idx ? 'ring-4 ring-white scale-110' : 'opacity-60 hover:opacity-100'
|
className={`relative w-20 h-20 flex-shrink-0 rounded-lg overflow-hidden cursor-pointer transition-all ${lightboxIndex === idx ? 'ring-4 ring-white scale-110' : 'opacity-60 hover:opacity-100'
|
||||||
|
|
||||||
}`}
|
}`}
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<Image src={img} alt={`Thumbnail ${idx + 1}`} fill className="object-cover" />
|
<Image src={img} alt={`Thumbnail ${idx + 1}`} fill className="object-cover" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
|
||||||
interface PropertyNavProps {
|
interface PropertyNavProps {
|
||||||
sections: { id: string; label: string }[];
|
sections: { id: string; label: string }[];
|
||||||
@ -8,6 +8,7 @@ interface PropertyNavProps {
|
|||||||
|
|
||||||
export default function PropertyNav({ sections }: PropertyNavProps) {
|
export default function PropertyNav({ sections }: PropertyNavProps) {
|
||||||
const [activeSection, setActiveSection] = useState(sections[0]?.id || "");
|
const [activeSection, setActiveSection] = useState(sections[0]?.id || "");
|
||||||
|
const navRef = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@ -29,6 +30,19 @@ export default function PropertyNav({ sections }: PropertyNavProps) {
|
|||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, [sections]);
|
}, [sections]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeSection && navRef.current) {
|
||||||
|
const activeBtn = document.getElementById(`nav-btn-${activeSection}`);
|
||||||
|
if (activeBtn) {
|
||||||
|
activeBtn.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'nearest',
|
||||||
|
inline: 'center'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeSection]);
|
||||||
|
|
||||||
const scrollToSection = (id: string) => {
|
const scrollToSection = (id: string) => {
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
if (element) {
|
if (element) {
|
||||||
@ -44,12 +58,14 @@ export default function PropertyNav({ sections }: PropertyNavProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-20 z-30 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md border-b border-gray-200 dark:border-gray-800 shadow-sm">
|
<div className="sticky top-28 z-30 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md border-b border-gray-200 dark:border-gray-800 shadow-sm">
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
<nav className="flex gap-1 overflow-x-auto hide-scrollbar py-3">
|
<nav ref={navRef} className="flex gap-1 overflow-x-auto hide-scrollbar py-3">
|
||||||
{sections.map((section) => (
|
{sections.map((section) => (
|
||||||
<button
|
<button
|
||||||
key={section.id}
|
key={section.id}
|
||||||
|
id={`nav-btn-${section.id}`}
|
||||||
onClick={() => scrollToSection(section.id)}
|
onClick={() => scrollToSection(section.id)}
|
||||||
className={`px-6 py-2 rounded-lg font-medium whitespace-nowrap transition-all duration-200 ${activeSection === section.id
|
className={`px-6 py-2 rounded-lg font-medium whitespace-nowrap transition-all duration-200 ${activeSection === section.id
|
||||||
? "bg-primary text-white shadow-md"
|
? "bg-primary text-white shadow-md"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
|
|
||||||
@ -11,6 +11,8 @@ interface SidebarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
||||||
|
const [isProjectsOpen, setIsProjectsOpen] = useState(false);
|
||||||
|
|
||||||
// Prevent scrolling when sidebar is open
|
// Prevent scrolling when sidebar is open
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@ -42,7 +44,7 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|||||||
<div className="relative w-40 h-12">
|
<div className="relative w-40 h-12">
|
||||||
<Image
|
<Image
|
||||||
src="/assets/images/blue-logo.png"
|
src="/assets/images/blue-logo.png"
|
||||||
alt="Sky and Soil Logo"
|
alt="Sky and Soil Sidebar Logo"
|
||||||
fill
|
fill
|
||||||
className="object-contain"
|
className="object-contain"
|
||||||
/>
|
/>
|
||||||
@ -76,6 +78,8 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|||||||
>
|
>
|
||||||
About
|
About
|
||||||
</Link>
|
</Link>
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
<Link
|
<Link
|
||||||
href="/projects"
|
href="/projects"
|
||||||
className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
|
className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
|
||||||
@ -83,6 +87,36 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|||||||
>
|
>
|
||||||
Projects
|
Projects
|
||||||
</Link>
|
</Link>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsProjectsOpen(!isProjectsOpen)}
|
||||||
|
className="p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke="currentColor"
|
||||||
|
className={`w-5 h-5 transition-transform duration-200 ${isProjectsOpen ? "rotate-180" : ""}`}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M19.5 8.25l-7.5 7.5-7.5-7.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={`overflow-hidden transition-all duration-300 ${isProjectsOpen ? "max-h-40 opacity-100" : "max-h-0 opacity-0"}`}>
|
||||||
|
<Link
|
||||||
|
href="/residential-real-estate"
|
||||||
|
className="block text-base font-medium text-gray-500 dark:text-gray-400 hover:text-primary transition-colors pl-4 py-2"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Residential Real Estate
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/lifestyle"
|
href="/lifestyle"
|
||||||
className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
|
className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
|
||||||
|
|||||||
@ -126,7 +126,7 @@ export default function Testimonials() {
|
|||||||
{testimonials.map((item, index) => (
|
{testimonials.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="min-w-[350px] md:min-w-[400px] snap-center"
|
className="flex-shrink-0 snap-center w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)]"
|
||||||
>
|
>
|
||||||
<div className="bg-white dark:bg-gray-900 p-6 rounded-2xl border border-gray-200 dark:border-gray-800 hover:shadow-xl transition-all duration-300 h-full flex flex-col">
|
<div className="bg-white dark:bg-gray-900 p-6 rounded-2xl border border-gray-200 dark:border-gray-800 hover:shadow-xl transition-all duration-300 h-full flex flex-col">
|
||||||
{/* Header with Avatar and Info */}
|
{/* Header with Avatar and Info */}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
import { FloatingHouse, RotatingKey, GrowingBuilding } from "./PropertyAnimations";
|
import { FloatingHouse, RotatingKey, GrowingBuilding } from "./PropertyAnimations";
|
||||||
|
|
||||||
export default function WhyChooseUs() {
|
export default function WhyChooseUs() {
|
||||||
@ -11,39 +12,22 @@ export default function WhyChooseUs() {
|
|||||||
{
|
{
|
||||||
title: "Prime Locations",
|
title: "Prime Locations",
|
||||||
description: "Strategically located in the heart of North Bengaluru's growth corridor.",
|
description: "Strategically located in the heart of North Bengaluru's growth corridor.",
|
||||||
icon: (
|
image: "/assets/images/home/why/location.webp",
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Smart Homes",
|
title: "Smart Homes",
|
||||||
description: "Future-ready infrastructure with integrated smart home automation.",
|
description: "Future-ready infrastructure with integrated smart home automation.",
|
||||||
icon: (
|
image: "/assets/images/home/why/smart-home.webp",
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Transparent Deals",
|
title: "Transparent Deals",
|
||||||
description: "100% clear documentation and legal compliance for peace of mind.",
|
description: "100% clear documentation and legal compliance for peace of mind.",
|
||||||
icon: (
|
image: "/assets/images/home/why/transparent-deals.webp",
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Customer First",
|
title: "Customer First",
|
||||||
description: "Dedicated support team to guide you through every step of the journey.",
|
description: "Dedicated support team to guide you through every step of the journey.",
|
||||||
icon: (
|
image: "/assets/images/home/why/customer-first.webp",
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.182 15.182a4.5 4.5 0 01-6.364 0M21 12a9 9 0 11-18 0 9 9 0 0118 0zM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75zm-.375 0h.008v.015h-.008V9.75zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75zm-.375 0h.008v.015h-.008V9.75z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -103,7 +87,7 @@ export default function WhyChooseUs() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{features.map((feature, index) => (
|
{features.map((feature, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
@ -115,8 +99,15 @@ export default function WhyChooseUs() {
|
|||||||
transitionDelay: `${index * 100}ms`
|
transitionDelay: `${index * 100}ms`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/30 text-primary rounded-full group-hover:scale-110 group-hover:rotate-6 transition-all duration-300">
|
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/30 rounded-full group-hover:scale-110 group-hover:rotate-6 transition-all duration-300 relative w-20 h-20 flex items-center justify-center">
|
||||||
{feature.icon}
|
<div className="relative w-[80%] h-[80%]">
|
||||||
|
<Image
|
||||||
|
src={feature.image}
|
||||||
|
alt={feature.title}
|
||||||
|
fill
|
||||||
|
className="object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-semibold text-foreground mb-3">
|
<h3 className="text-xl font-semibold text-foreground mb-3">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
|
|||||||
@ -1,111 +1,888 @@
|
|||||||
|
export interface Amenity {
|
||||||
|
name: string;
|
||||||
|
available: boolean;
|
||||||
|
rare?: boolean;
|
||||||
|
category?: "Lifestyle" | "Sports" | "Natural";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FaqItem {
|
||||||
|
question: string;
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FloorPlan {
|
||||||
|
id: string;
|
||||||
|
price: string;
|
||||||
|
area: string;
|
||||||
|
direction: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type Property = {
|
export type Property = {
|
||||||
id: number;
|
id: number;
|
||||||
|
slug: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
metaTitle?: string;
|
||||||
|
metaDescription?: string;
|
||||||
category: "Apartments" | "Premium Homes" | "Luxury" | "Villas" | "Plots";
|
category: "Apartments" | "Premium Homes" | "Luxury" | "Villas" | "Plots";
|
||||||
location: string;
|
location: string;
|
||||||
price: string;
|
price: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: string;
|
status: string;
|
||||||
image: string;
|
image: string;
|
||||||
images: string[]; // For gallery
|
images: string[];
|
||||||
overview: {
|
overview: {
|
||||||
bhk: string;
|
bhk: string;
|
||||||
size: string;
|
size: string;
|
||||||
possession: string;
|
possession: string;
|
||||||
totalUnits: string;
|
totalUnits: string;
|
||||||
|
badges: string[];
|
||||||
|
stats: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
subtext?: string;
|
||||||
|
icon: "land" | "metro" | "road" | "openArea" | "density" | "clubhouse";
|
||||||
|
}[];
|
||||||
|
description: string;
|
||||||
};
|
};
|
||||||
amenities: string[];
|
amenities: string[]; // Keep for backward compatibility if needed, or deprecate
|
||||||
|
|
||||||
|
// New dynamic fields
|
||||||
|
detailedOverview?: {
|
||||||
|
landArea: string;
|
||||||
|
landAreaAvg: string;
|
||||||
|
metroDistance: string;
|
||||||
|
metroDistanceAvg: string;
|
||||||
|
approachRoad: string;
|
||||||
|
approachRoadAvg: string;
|
||||||
|
openArea: string;
|
||||||
|
openAreaAvg: string;
|
||||||
|
unitDensity: string;
|
||||||
|
unitDensityAvg: string;
|
||||||
|
clubHouse: string;
|
||||||
|
clubHouseAvg: string;
|
||||||
|
};
|
||||||
|
connectivity?: {
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
pricingDetails?: {
|
||||||
|
totalRange: string; // Defaults to price if not set
|
||||||
|
emi: string;
|
||||||
|
projectAvg: string;
|
||||||
|
marketAvg: string;
|
||||||
|
};
|
||||||
|
masterPlan?: {
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
totalUnits: string;
|
||||||
|
waterSource: string;
|
||||||
|
parkArea: string;
|
||||||
|
landType: string;
|
||||||
|
};
|
||||||
|
floorPlans?: FloorPlan[];
|
||||||
|
detailedAmenities?: Record<string, Amenity[]>; // Grouped by category
|
||||||
|
approvals?: {
|
||||||
|
name: string;
|
||||||
|
available: boolean;
|
||||||
|
}[];
|
||||||
|
documents?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}[];
|
||||||
|
builder?: {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
establishedYear: string;
|
||||||
|
completedProjects: string;
|
||||||
|
};
|
||||||
|
locality?: {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
mapImage: string;
|
||||||
|
pros: string[];
|
||||||
|
cons: string[];
|
||||||
|
};
|
||||||
|
skyandsoilClarity?: {
|
||||||
|
familiesHelped: string;
|
||||||
|
};
|
||||||
|
pricingUpdateDate?: string;
|
||||||
|
reraInfo?: {
|
||||||
|
isApproved: boolean;
|
||||||
|
approvalDate: string;
|
||||||
|
};
|
||||||
|
brochureCard?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
prosConsCard?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
buttonText: string;
|
||||||
|
};
|
||||||
|
faq?: Record<string, FaqItem[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const properties: Property[] = [
|
export const properties: Property[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
slug: "barca-at-godrej-msr-city",
|
||||||
title: "BARCA at Godrej MSR City",
|
title: "BARCA at Godrej MSR City",
|
||||||
|
metaTitle: "BARCA at Godrej MSR City",
|
||||||
|
metaDescription: "Experience luxury living at BARCA, Godrej MSR City. These 3 BHK apartments blend modern architecture with natural serenity for a truly refined lifestyle.",
|
||||||
category: "Apartments",
|
category: "Apartments",
|
||||||
location: "Shettigere Road, Bangalore",
|
location: "Shettigere Road, Bangalore",
|
||||||
price: "₹ 1.9 CR*",
|
price: "₹ 1.9 CR*",
|
||||||
description: "Experience the pinnacle of luxury living at BARCA, Godrej MSR City. These 3 BHK apartments are designed for those who appreciate the finer things in life, offering a perfect blend of modern architecture and natural serenity.",
|
description: "Experience the pinnacle of luxury living at BARCA, Godrej MSR City. These 3 BHK apartments are designed for those who appreciate the finer things in life, offering a perfect blend of modern architecture and natural serenity.",
|
||||||
status: "New Launch",
|
status: "New Launch",
|
||||||
image: "/hero-image.jpg", // Using hero image as placeholder
|
image: "/assets/images/projects/barca.webp",
|
||||||
images: ["/hero-image.jpg", "/hero-image.jpg", "/hero-image.jpg"],
|
images: ["/assets/images/projects/details/barca-1.webp", "/assets/images/projects/details/barca-2.webp", "/assets/images/projects/details/barca-3.webp"],
|
||||||
overview: {
|
overview: {
|
||||||
bhk: "3 BHK",
|
bhk: "3 BHK",
|
||||||
size: "1500 - 2200 Sq.Ft",
|
size: "1500 - 2200 Sq.Ft",
|
||||||
possession: "Dec 2027",
|
possession: "Dec 2027",
|
||||||
totalUnits: "400",
|
totalUnits: "400",
|
||||||
|
badges: ["Better", "Average", "Subpar"],
|
||||||
|
stats: [
|
||||||
|
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
|
||||||
|
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
|
||||||
|
{ label: "Approach Road", value: "15 meters", subtext: "Avg is 15 meters", icon: "road" },
|
||||||
|
{ label: "Open Area", value: "45%", subtext: "Avg is 45%", icon: "openArea" },
|
||||||
|
{ label: "Unit Density", value: "13 units/acre", subtext: "Avg is 13 units/acre", icon: "density" },
|
||||||
|
{ label: "Club House", value: "50000 sqft", subtext: "Avg is 50000 sqft", icon: "clubhouse" },
|
||||||
|
],
|
||||||
|
description: "BARCA at Godrej MSR City is located in Shettigere Road, Bangalore. In its vicinity, the closest metro is Dommasandra Circle Metro Station. It takes approximately 84 mins to reach the Kempegowda Airport from this property. The area is well-connected to major IT hubs, schools, and hospitals, making it an ideal choice for families and professionals alike."
|
||||||
},
|
},
|
||||||
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
||||||
|
pricingDetails: {
|
||||||
|
totalRange: "₹1.9 CR*",
|
||||||
|
emi: "₹87,500*",
|
||||||
|
projectAvg: "₹8,500",
|
||||||
|
marketAvg: "₹8,200"
|
||||||
},
|
},
|
||||||
|
masterPlan: {
|
||||||
|
description: "The master plan of BARCA at Godrej MSR City is thoughtfully designed to maximize open spaces and green areas. Spread across 50 acres, the project features a central clubhouse, multiple parks, and dedicated zones for recreational activities. The layout ensures optimal sunlight and ventilation for all units while maintaining privacy.",
|
||||||
|
image: "/assets/images/image.png",
|
||||||
|
totalUnits: "400 Premium",
|
||||||
|
waterSource: "Cauvery Water",
|
||||||
|
parkArea: "22.5 Acres",
|
||||||
|
landType: "Residential"
|
||||||
|
},
|
||||||
|
skyandsoilClarity: {
|
||||||
|
familiesHelped: "300+",
|
||||||
|
},
|
||||||
|
pricingUpdateDate: "03 Nov 2025",
|
||||||
|
reraInfo: {
|
||||||
|
isApproved: true,
|
||||||
|
approvalDate: "Nov 2024"
|
||||||
|
},
|
||||||
|
brochureCard: {
|
||||||
|
title: "See BARCA at Godrej MSR City brochure",
|
||||||
|
description: "Beware, beautiful brochures don't show hidden realities"
|
||||||
|
},
|
||||||
|
prosConsCard: {
|
||||||
|
title: "See the truth beyond the brochures",
|
||||||
|
description: "See every risk & potential clearly with our experts",
|
||||||
|
buttonText: "Get Pros & Cons"
|
||||||
|
},
|
||||||
|
floorPlans: [
|
||||||
|
{ id: "30 x 40", price: "₹1.24 Crores", area: "1200 sqft", direction: "North West, South East, North" },
|
||||||
|
{ id: "30 x 50", price: "₹1.55 Crores", area: "1500 sqft", direction: "North West, South East, East, West" },
|
||||||
|
{ id: "30 x 60", price: "₹1.86 Crores", area: "1800 sqft", direction: "North, East, West" },
|
||||||
|
{ id: "40 x 60", price: "₹2.48 Crores", area: "2400 sqft", direction: "East, West" },
|
||||||
|
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
|
||||||
|
],
|
||||||
|
approvals: [
|
||||||
|
{ name: "Approved by BDA", available: true },
|
||||||
|
{ name: "Approved by BWSSB", available: false },
|
||||||
|
{ name: "Approved by MOEF", available: false },
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ title: "City Development Plan", description: "Used to identify land types, sensitive zones/flood risks, future developments etc." },
|
||||||
|
{ title: "Legal Opinion", description: "Used to only declare any litigations by the builder. Do NOT mistake for a clean title." },
|
||||||
|
{ title: "Development Rights", description: "Outlines the agreed terms of development between the land owner & the builder" },
|
||||||
|
],
|
||||||
|
builder: {
|
||||||
|
name: "Modern Spaaces",
|
||||||
|
description: "Established in 2012, Modern Spaces focuses on delivering thoughtfully designed residential and commercial developments. With over 1 million sq. ft. completed, the company caters to mid-segment and premium buyers in Bengaluru. Key projects like Modern Greens, Modern Heights, and Modern Meadows are recognized for their innovative layouts and contemporary features. Modern Spaces integrates green practices such as energy-efficient construction and landscaped open spaces into its developments. Known for timely delivery and quality construction, Modern Spaces continues to serve Bengaluru's growing real estate market.",
|
||||||
|
establishedYear: "2012",
|
||||||
|
completedProjects: "No Data",
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
name: "Varthur",
|
||||||
|
description: "Varthur is a suburb situated in the Eastern Periphery of Bangalore. It is part of the Whitefield township and is known for the Varthur Lake, the second largest lake in Bangalore.",
|
||||||
|
mapImage: "/assets/images/image.png",
|
||||||
|
pros: [
|
||||||
|
"Proximity to Tech Hubs: Close to major IT parks like Whitefield, making it ideal for professionals.",
|
||||||
|
"Well-Established Infrastructure: Good schools, hospitals, and shopping centers nearby.",
|
||||||
|
"Green Spaces: Presence of Varthur Lake and other green areas for recreation.",
|
||||||
|
],
|
||||||
|
cons: [
|
||||||
|
"Traffic Congestion: Can get crowded during peak hours.",
|
||||||
|
"Pollution Levels: Air quality can vary depending on traffic and construction activity.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
detailedAmenities: {
|
||||||
|
Lifestyle: [
|
||||||
|
{ name: "Gym - Indoor", available: true, rare: true },
|
||||||
|
{ name: "Gym - Outdoor", available: true },
|
||||||
|
{ name: "Kids Play Area", available: true, rare: true },
|
||||||
|
{ name: "Amphitheatre", available: false },
|
||||||
|
{ name: "Cafe/Restaurant", available: false },
|
||||||
|
],
|
||||||
|
Sports: [
|
||||||
|
{ name: "Running Track", available: true, rare: true },
|
||||||
|
{ name: "Badminton", available: false },
|
||||||
|
{ name: "Basketball", available: false },
|
||||||
|
{ name: "Cricket Ground", available: false },
|
||||||
|
{ name: "Cricket Pitch", available: false },
|
||||||
|
],
|
||||||
|
Natural: [
|
||||||
|
{ name: "Army Land", available: false },
|
||||||
|
{ name: "Forest", available: false },
|
||||||
|
{ name: "Golf Course", available: false },
|
||||||
|
{ name: "Lake", available: false },
|
||||||
|
{ name: "Mountain/Hill", available: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
faq: {
|
||||||
|
'Project Details': [
|
||||||
|
{ question: "What is the total land area?", answer: "The project is spread across 5 acres of prime land." },
|
||||||
|
{ question: "How many units are there?", answer: "There are a total of 450 luxury units." },
|
||||||
|
{ question: "What is the completion date?", answer: "The project is expected to be completed by Dec 2026." },
|
||||||
|
{ question: "Is it RERA approved?", answer: "Yes, the project is fully RERA approved." },
|
||||||
|
],
|
||||||
|
'Location & Connectivity': [
|
||||||
|
{ question: "How far is the nearest metro station?", answer: "The nearest metro station is just 2km away." },
|
||||||
|
{ question: "Are there schools nearby?", answer: "Yes, there are several international schools within a 3km radius." },
|
||||||
|
{ question: "How is the road connectivity?", answer: "The project is well-connected to the Outer Ring Road and Whitefield." },
|
||||||
|
{ question: "Is there a hospital nearby?", answer: "Manipal Hospital is located 1.5km from the project." },
|
||||||
|
],
|
||||||
|
'Price & Booking': [
|
||||||
|
{ question: "What is the booking amount?", answer: "The booking amount is 10% of the total sale value." },
|
||||||
|
{ question: "Are there any bank offers?", answer: "Yes, we have tie-ups with SBI, HDFC, and ICICI for home loans." },
|
||||||
|
{ question: "What are the additional charges?", answer: "Additional charges include GST, registration, and maintenance deposits." },
|
||||||
|
{ question: "Is the price negotiable?", answer: "Prices are competitive, but we can discuss offers on table." },
|
||||||
|
],
|
||||||
|
'Services': [
|
||||||
|
{ question: "What is Service Guided Home Buying (GHB) and Peace of Mind (POM) service?", answer: "Service Guided Home Buying (GHB) and Peace of Mind (POM) service provide end-to-end assistance in your home buying journey, ensuring a hassle-free and transparent experience from selection to possession." },
|
||||||
|
{ question: "Why should I get an advisor to help me buy a property?", answer: "An advisor acts as your unbiased partner, helping you navigate the complex real estate market, verify documents, negotiate prices, and avoid common pitfalls." },
|
||||||
|
{ question: "Which type of properties do you help with?", answer: "We assist with all types of residential properties including apartments, villas, plots, and gated communities across various budget segments." },
|
||||||
|
{ question: "Can I buy homes via Sky and Soil?", answer: "Yes, you can explore, shortlist, and book homes directly through Sky and Soil with the help of our expert advisors." },
|
||||||
|
{ question: "How do I know I am seeing all possible options in the market?", answer: "Our database covers 99% of the market inventory, and our advisors provide unbiased recommendations based on your specific requirements, not just what we want to sell." },
|
||||||
|
{ question: "How can I be assured that I am paying the right price?", answer: "We provide data-backed market analysis and comparative pricing reports to ensure you get the best value for your investment." },
|
||||||
|
{ question: "What other services can Sky & Soil help me with after booking?", answer: "Post-booking, we assist with legal verification, loan processing, registration, and even interior design and property management services." },
|
||||||
|
{ question: "What is the cost of the services?", answer: "Our basic advisory services are free for home buyers. We charge a nominal fee only for premium services like legal verification and dedicated relationship management." },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// ... rest of the properties would arguably need to be updated to match the type, but since the user only cares about the detail page for potentially *one* property or generic behavior,
|
||||||
|
// I MUST update the other objects to at least matches the Type to avoid Typescript errors.
|
||||||
|
// I will use partial/empty data for others to satisfy the compiler.
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
slug: "godrej-woods",
|
||||||
title: "Godrej Woods",
|
title: "Godrej Woods",
|
||||||
|
metaTitle: "Godrej Woods",
|
||||||
|
metaDescription: "Godrej Woods offers premium 2 & 3 BHK apartments in Thanisandra. Enjoy a forest-themed lifestyle with world-class amenities and excellent connectivity.",
|
||||||
category: "Premium Homes",
|
category: "Premium Homes",
|
||||||
location: "Thanisandra, North Bangalore",
|
location: "Thanisandra, North Bangalore",
|
||||||
price: "₹ 1.62 CR*",
|
price: "₹ 1.62 CR*",
|
||||||
description: "Godrej Woods offers premium 2 & 3 BHK apartments nestled in the lush greenery of Thanisandra. Enjoy a forest-themed lifestyle with world-class amenities and excellent connectivity to the airport and tech parks.",
|
description: "Godrej Woods offers premium 2 & 3 BHK apartments nestled in the lush greenery of Thanisandra. Enjoy a forest-themed lifestyle with world-class amenities and excellent connectivity to the airport and tech parks.",
|
||||||
status: "Best Seller",
|
status: "Best Seller",
|
||||||
image: "/hero-image.jpg",
|
image: "/assets/images/projects/godrej-woods.webp",
|
||||||
images: ["/hero-image.jpg", "/hero-image.jpg", "/hero-image.jpg"],
|
images: ["/assets/images/projects/details/woods-1.webp", "/assets/images/projects/details/woods-2.webp", "/assets/images/projects/details/woods-3.webp"],
|
||||||
overview: {
|
overview: {
|
||||||
bhk: "2 & 3 BHK",
|
bhk: "3 BHK",
|
||||||
size: "1100 - 1600 Sq.Ft",
|
size: "1500 - 2200 Sq.Ft",
|
||||||
possession: "June 2026",
|
possession: "Dec 2027",
|
||||||
totalUnits: "600",
|
totalUnits: "400",
|
||||||
|
badges: ["Better", "Average", "Subpar"],
|
||||||
|
stats: [
|
||||||
|
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
|
||||||
|
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
|
||||||
|
{ label: "Approach Road", value: "15 meters", subtext: "Avg is 15 meters", icon: "road" },
|
||||||
|
{ label: "Open Area", value: "45%", subtext: "Avg is 45%", icon: "openArea" },
|
||||||
|
{ label: "Unit Density", value: "13 units/acre", subtext: "Avg is 13 units/acre", icon: "density" },
|
||||||
|
{ label: "Club House", value: "50000 sqft", subtext: "Avg is 50000 sqft", icon: "clubhouse" },
|
||||||
|
],
|
||||||
|
description: "BARCA at Godrej MSR City is located in Shettigere Road, Bangalore. In its vicinity, the closest metro is Dommasandra Circle Metro Station. It takes approximately 84 mins to reach the Kempegowda Airport from this property. The area is well-connected to major IT hubs, schools, and hospitals, making it an ideal choice for families and professionals alike."
|
||||||
},
|
},
|
||||||
amenities: ["Forest Trail", "Infinity Pool", "Yoga Deck", "Co-working Space", "Sports Courts", "Amphitheater"],
|
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
||||||
|
pricingDetails: {
|
||||||
|
totalRange: "₹1.62 CR",
|
||||||
|
emi: "₹74,500",
|
||||||
|
projectAvg: "₹7,800",
|
||||||
|
marketAvg: "₹7,500"
|
||||||
|
},
|
||||||
|
masterPlan: {
|
||||||
|
description: "Godrej Woods master plan embraces a forest-themed concept with over 60% green cover. The project spans 32 acres with tree-lined pathways, botanical gardens, and a central forest park. The low-density layout ensures privacy and tranquility for all residents.",
|
||||||
|
image: "/assets/images/image.png",
|
||||||
|
totalUnits: "350",
|
||||||
|
waterSource: "Cauvery Water",
|
||||||
|
parkArea: "19.2 Acres",
|
||||||
|
landType: "Residential"
|
||||||
|
},
|
||||||
|
skyandsoilClarity: {
|
||||||
|
familiesHelped: "300+",
|
||||||
|
},
|
||||||
|
pricingUpdateDate: "15 Oct 2025",
|
||||||
|
reraInfo: {
|
||||||
|
isApproved: true,
|
||||||
|
approvalDate: "Aug 2024"
|
||||||
|
},
|
||||||
|
brochureCard: {
|
||||||
|
title: "See Godrej Woods brochure",
|
||||||
|
description: "Download detailed brochure to explore forest-themed living"
|
||||||
|
},
|
||||||
|
prosConsCard: {
|
||||||
|
title: "Forest living insights from experts",
|
||||||
|
description: "Discover every aspect of forest-themed community lifestyle",
|
||||||
|
buttonText: "Get Expert Analysis"
|
||||||
|
},
|
||||||
|
floorPlans: [
|
||||||
|
{ id: "30 x 40", price: "₹1.24 Crores", area: "1200 sqft", direction: "North West, South East, North" },
|
||||||
|
{ id: "30 x 50", price: "₹1.55 Crores", area: "1500 sqft", direction: "North West, South East, East, West" },
|
||||||
|
{ id: "30 x 60", price: "₹1.86 Crores", area: "1800 sqft", direction: "North, East, West" },
|
||||||
|
{ id: "40 x 60", price: "₹2.48 Crores", area: "2400 sqft", direction: "East, West" },
|
||||||
|
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
|
||||||
|
],
|
||||||
|
approvals: [
|
||||||
|
{ name: "Approved by BDA", available: true },
|
||||||
|
{ name: "Approved by BWSSB", available: false },
|
||||||
|
{ name: "Approved by MOEF", available: false },
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ title: "City Development Plan", description: "Used to identify land types, sensitive zones/flood risks, future developments etc." },
|
||||||
|
{ title: "Legal Opinion", description: "Used to only declare any litigations by the builder. Do NOT mistake for a clean title." },
|
||||||
|
{ title: "Development Rights", description: "Outlines the agreed terms of development between the land owner & the builder" },
|
||||||
|
],
|
||||||
|
builder: {
|
||||||
|
name: "Modern Spaaces",
|
||||||
|
description: "Established in 2012, Modern Spaces focuses on delivering thoughtfully designed residential and commercial developments. With over 1 million sq. ft. completed, the company caters to mid-segment and premium buyers in Bengaluru. Key projects like Modern Greens, Modern Heights, and Modern Meadows are recognized for their innovative layouts and contemporary features. Modern Spaces integrates green practices such as energy-efficient construction and landscaped open spaces into its developments. Known for timely delivery and quality construction, Modern Spaces continues to serve Bengaluru's growing real estate market.",
|
||||||
|
establishedYear: "2012",
|
||||||
|
completedProjects: "No Data",
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
name: "Varthur",
|
||||||
|
description: "Varthur is a suburb situated in the Eastern Periphery of Bangalore. It is part of the Whitefield township and is known for the Varthur Lake, the second largest lake in Bangalore.",
|
||||||
|
mapImage: "/assets/images/map-placeholder.webp",
|
||||||
|
pros: [
|
||||||
|
"Proximity to Tech Hubs: Close to major IT parks like Whitefield, making it ideal for professionals.",
|
||||||
|
"Well-Established Infrastructure: Good schools, hospitals, and shopping centers nearby.",
|
||||||
|
"Green Spaces: Presence of Varthur Lake and other green areas for recreation.",
|
||||||
|
],
|
||||||
|
cons: [
|
||||||
|
"Traffic Congestion: Can get crowded during peak hours.",
|
||||||
|
"Pollution Levels: Air quality can vary depending on traffic and construction activity.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
detailedAmenities: {
|
||||||
|
Lifestyle: [
|
||||||
|
{ name: "Gym - Indoor", available: true, rare: true },
|
||||||
|
{ name: "Gym - Outdoor", available: true },
|
||||||
|
{ name: "Kids Play Area", available: true, rare: true },
|
||||||
|
{ name: "Amphitheatre", available: false },
|
||||||
|
{ name: "Cafe/Restaurant", available: false },
|
||||||
|
],
|
||||||
|
Sports: [
|
||||||
|
{ name: "Running Track", available: true, rare: true },
|
||||||
|
{ name: "Badminton", available: false },
|
||||||
|
{ name: "Basketball", available: false },
|
||||||
|
{ name: "Cricket Ground", available: false },
|
||||||
|
{ name: "Cricket Pitch", available: false },
|
||||||
|
],
|
||||||
|
Natural: [
|
||||||
|
{ name: "Army Land", available: false },
|
||||||
|
{ name: "Forest", available: false },
|
||||||
|
{ name: "Golf Course", available: false },
|
||||||
|
{ name: "Lake", available: false },
|
||||||
|
{ name: "Mountain/Hill", available: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
faq: {
|
||||||
|
'Project Details': [
|
||||||
|
{ question: "What is the total land area?", answer: "The project is spread across 5 acres of prime land." },
|
||||||
|
{ question: "How many units are there?", answer: "There are a total of 450 luxury units." },
|
||||||
|
{ question: "What is the completion date?", answer: "The project is expected to be completed by Dec 2026." },
|
||||||
|
{ question: "Is it RERA approved?", answer: "Yes, the project is fully RERA approved." },
|
||||||
|
],
|
||||||
|
'Location & Connectivity': [
|
||||||
|
{ question: "How far is the nearest metro station?", answer: "The nearest metro station is just 2km away." },
|
||||||
|
{ question: "Are there schools nearby?", answer: "Yes, there are several international schools within a 3km radius." },
|
||||||
|
{ question: "How is the road connectivity?", answer: "The project is well-connected to the Outer Ring Road and Whitefield." },
|
||||||
|
{ question: "Is there a hospital nearby?", answer: "Manipal Hospital is located 1.5km from the project." },
|
||||||
|
],
|
||||||
|
'Price & Booking': [
|
||||||
|
{ question: "What is the booking amount?", answer: "The booking amount is 10% of the total sale value." },
|
||||||
|
{ question: "Are there any bank offers?", answer: "Yes, we have tie-ups with SBI, HDFC, and ICICI for home loans." },
|
||||||
|
{ question: "What are the additional charges?", answer: "Additional charges include GST, registration, and maintenance deposits." },
|
||||||
|
{ question: "Is the price negotiable?", answer: "Prices are competitive, but we can discuss offers on table." },
|
||||||
|
],
|
||||||
|
'Services': [
|
||||||
|
{ question: "What is Service Guided Home Buying (GHB) and Peace of Mind (POM) service?", answer: "Service Guided Home Buying (GHB) and Peace of Mind (POM) service provide end-to-end assistance in your home buying journey, ensuring a hassle-free and transparent experience from selection to possession." },
|
||||||
|
{ question: "Why should I get an advisor to help me buy a property?", answer: "An advisor acts as your unbiased partner, helping you navigate the complex real estate market, verify documents, negotiate prices, and avoid common pitfalls." },
|
||||||
|
{ question: "Which type of properties do you help with?", answer: "We assist with all types of residential properties including apartments, villas, plots, and gated communities across various budget segments." },
|
||||||
|
{ question: "Can I buy homes via Sky and Soil?", answer: "Yes, you can explore, shortlist, and book homes directly through Sky and Soil with the help of our expert advisors." },
|
||||||
|
{ question: "How do I know I am seeing all possible options in the market?", answer: "Our database covers 99% of the market inventory, and our advisors provide unbiased recommendations based on your specific requirements, not just what we want to sell." },
|
||||||
|
{ question: "How can I be assured that I am paying the right price?", answer: "We provide data-backed market analysis and comparative pricing reports to ensure you get the best value for your investment." },
|
||||||
|
{ question: "What other services can Sky & Soil help me with after booking?", answer: "Post-booking, we assist with legal verification, loan processing, registration, and even interior design and property management services." },
|
||||||
|
{ question: "What is the cost of the services?", answer: "Our basic advisory services are free for home buyers. We charge a nominal fee only for premium services like legal verification and dedicated relationship management." },
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
|
slug: "godrej-hoskote",
|
||||||
title: "Godrej Hoskote",
|
title: "Godrej Hoskote",
|
||||||
|
metaTitle: "Godrej Hoskote",
|
||||||
|
metaDescription: "Discover community living at Godrej Hoskote. Premium 2 & 3 BHK homes designed for comfort and convenience, with easy access to industrial hubs and schools.",
|
||||||
category: "Apartments",
|
category: "Apartments",
|
||||||
location: "Hoskote, Soukya Road Ext",
|
location: "Hoskote, Soukya Road Ext",
|
||||||
price: "₹ 1.17 Cr*",
|
price: "₹ 1.17 Cr*",
|
||||||
description: "Discover the joy of community living at Godrej Hoskote. These premium 2 & 3 BHK homes are designed to provide a comfortable and convenient lifestyle, with easy access to industrial hubs and educational institutions.",
|
description: "Discover the joy of community living at Godrej Hoskote. These premium 2 & 3 BHK homes are designed to provide a comfortable and convenient lifestyle, with easy access to industrial hubs and educational institutions.",
|
||||||
status: "New Launch",
|
status: "New Launch",
|
||||||
image: "/hero-image.jpg",
|
image: "/assets/images/projects/godrej-hoskote.webp",
|
||||||
images: ["/hero-image.jpg", "/hero-image.jpg", "/hero-image.jpg"],
|
images: ["/assets/images/projects/details/hoskote-1.webp", "/assets/images/projects/details/hoskote-2.webp", "/assets/images/projects/details/hoskote-3.webp"],
|
||||||
overview: {
|
overview: {
|
||||||
bhk: "2 & 3 BHK",
|
bhk: "3 BHK",
|
||||||
size: "1050 - 1750 Sq.Ft",
|
size: "1500 - 2200 Sq.Ft",
|
||||||
possession: "On Request",
|
possession: "Dec 2027",
|
||||||
totalUnits: "800",
|
totalUnits: "400",
|
||||||
|
badges: ["Better", "Average", "Subpar"],
|
||||||
|
stats: [
|
||||||
|
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
|
||||||
|
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
|
||||||
|
{ label: "Approach Road", value: "15 meters", subtext: "Avg is 15 meters", icon: "road" },
|
||||||
|
{ label: "Open Area", value: "45%", subtext: "Avg is 45%", icon: "openArea" },
|
||||||
|
{ label: "Unit Density", value: "13 units/acre", subtext: "Avg is 13 units/acre", icon: "density" },
|
||||||
|
{ label: "Club House", value: "50000 sqft", subtext: "Avg is 50000 sqft", icon: "clubhouse" },
|
||||||
|
],
|
||||||
|
description: "BARCA at Godrej MSR City is located in Shettigere Road, Bangalore. In its vicinity, the closest metro is Dommasandra Circle Metro Station. It takes approximately 84 mins to reach the Kempegowda Airport from this property. The area is well-connected to major IT hubs, schools, and hospitals, making it an ideal choice for families and professionals alike."
|
||||||
},
|
},
|
||||||
amenities: ["Community Hall", "Jogging Track", "Swimming Pool", "Retail Plaza", "Senior Citizen Corner", "Pet Park"],
|
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
||||||
|
pricingDetails: {
|
||||||
|
totalRange: "₹1.17 CR",
|
||||||
|
emi: "₹53,800",
|
||||||
|
projectAvg: "₹6,200",
|
||||||
|
marketAvg: "₹5,900"
|
||||||
|
},
|
||||||
|
masterPlan: {
|
||||||
|
description: "Godrej Hoskote master plan focuses on community living with interconnected spaces. Across 28 acres, the development features a central plaza, sports complex, and community gardens. The layout promotes walkability and social interaction among residents.",
|
||||||
|
image: "/assets/images/image.png",
|
||||||
|
totalUnits: "480",
|
||||||
|
waterSource: "Borewell",
|
||||||
|
parkArea: "12.6 Acres",
|
||||||
|
landType: "Residential"
|
||||||
|
},
|
||||||
|
skyandsoilClarity: {
|
||||||
|
familiesHelped: "300+",
|
||||||
|
},
|
||||||
|
pricingUpdateDate: "20 Oct 2025",
|
||||||
|
reraInfo: {
|
||||||
|
isApproved: true,
|
||||||
|
approvalDate: "Sep 2024"
|
||||||
|
},
|
||||||
|
brochureCard: {
|
||||||
|
title: "See Godrej Hoskote brochure",
|
||||||
|
description: "Get comprehensive details about community living at Hoskote"
|
||||||
|
},
|
||||||
|
prosConsCard: {
|
||||||
|
title: "Community living clarity",
|
||||||
|
description: "Get detailed pros & cons analysis for this location",
|
||||||
|
buttonText: "View Analysis"
|
||||||
|
},
|
||||||
|
floorPlans: [
|
||||||
|
{ id: "30 x 40", price: "₹1.24 Crores", area: "1200 sqft", direction: "North West, South East, North" },
|
||||||
|
{ id: "30 x 50", price: "₹1.55 Crores", area: "1500 sqft", direction: "North West, South East, East, West" },
|
||||||
|
{ id: "30 x 60", price: "₹1.86 Crores", area: "1800 sqft", direction: "North, East, West" },
|
||||||
|
{ id: "40 x 60", price: "₹2.48 Crores", area: "2400 sqft", direction: "East, West" },
|
||||||
|
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
|
||||||
|
],
|
||||||
|
approvals: [
|
||||||
|
{ name: "Approved by BDA", available: true },
|
||||||
|
{ name: "Approved by BWSSB", available: false },
|
||||||
|
{ name: "Approved by MOEF", available: false },
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ title: "City Development Plan", description: "Used to identify land types, sensitive zones/flood risks, future developments etc." },
|
||||||
|
{ title: "Legal Opinion", description: "Used to only declare any litigations by the builder. Do NOT mistake for a clean title." },
|
||||||
|
{ title: "Development Rights", description: "Outlines the agreed terms of development between the land owner & the builder" },
|
||||||
|
],
|
||||||
|
builder: {
|
||||||
|
name: "Modern Spaaces",
|
||||||
|
description: "Established in 2012, Modern Spaces focuses on delivering thoughtfully designed residential and commercial developments. With over 1 million sq. ft. completed, the company caters to mid-segment and premium buyers in Bengaluru. Key projects like Modern Greens, Modern Heights, and Modern Meadows are recognized for their innovative layouts and contemporary features. Modern Spaces integrates green practices such as energy-efficient construction and landscaped open spaces into its developments. Known for timely delivery and quality construction, Modern Spaces continues to serve Bengaluru's growing real estate market.",
|
||||||
|
establishedYear: "2012",
|
||||||
|
completedProjects: "No Data",
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
name: "Varthur",
|
||||||
|
description: "Varthur is a suburb situated in the Eastern Periphery of Bangalore. It is part of the Whitefield township and is known for the Varthur Lake, the second largest lake in Bangalore.",
|
||||||
|
mapImage: "/assets/images/map-placeholder.webp",
|
||||||
|
pros: [
|
||||||
|
"Proximity to Tech Hubs: Close to major IT parks like Whitefield, making it ideal for professionals.",
|
||||||
|
"Well-Established Infrastructure: Good schools, hospitals, and shopping centers nearby.",
|
||||||
|
"Green Spaces: Presence of Varthur Lake and other green areas for recreation.",
|
||||||
|
],
|
||||||
|
cons: [
|
||||||
|
"Traffic Congestion: Can get crowded during peak hours.",
|
||||||
|
"Pollution Levels: Air quality can vary depending on traffic and construction activity.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
detailedAmenities: {
|
||||||
|
Lifestyle: [
|
||||||
|
{ name: "Gym - Indoor", available: true, rare: true },
|
||||||
|
{ name: "Gym - Outdoor", available: true },
|
||||||
|
{ name: "Kids Play Area", available: true, rare: true },
|
||||||
|
{ name: "Amphitheatre", available: false },
|
||||||
|
{ name: "Cafe/Restaurant", available: false },
|
||||||
|
],
|
||||||
|
Sports: [
|
||||||
|
{ name: "Running Track", available: true, rare: true },
|
||||||
|
{ name: "Badminton", available: false },
|
||||||
|
{ name: "Basketball", available: false },
|
||||||
|
{ name: "Cricket Ground", available: false },
|
||||||
|
{ name: "Cricket Pitch", available: false },
|
||||||
|
],
|
||||||
|
Natural: [
|
||||||
|
{ name: "Army Land", available: false },
|
||||||
|
{ name: "Forest", available: false },
|
||||||
|
{ name: "Golf Course", available: false },
|
||||||
|
{ name: "Lake", available: false },
|
||||||
|
{ name: "Mountain/Hill", available: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
faq: {
|
||||||
|
'Project Details': [
|
||||||
|
{ question: "What is the total land area?", answer: "The project is spread across 5 acres of prime land." },
|
||||||
|
{ question: "How many units are there?", answer: "There are a total of 450 luxury units." },
|
||||||
|
{ question: "What is the completion date?", answer: "The project is expected to be completed by Dec 2026." },
|
||||||
|
{ question: "Is it RERA approved?", answer: "Yes, the project is fully RERA approved." },
|
||||||
|
],
|
||||||
|
'Location & Connectivity': [
|
||||||
|
{ question: "How far is the nearest metro station?", answer: "The nearest metro station is just 2km away." },
|
||||||
|
{ question: "Are there schools nearby?", answer: "Yes, there are several international schools within a 3km radius." },
|
||||||
|
{ question: "How is the road connectivity?", answer: "The project is well-connected to the Outer Ring Road and Whitefield." },
|
||||||
|
{ question: "Is there a hospital nearby?", answer: "Manipal Hospital is located 1.5km from the project." },
|
||||||
|
],
|
||||||
|
'Price & Booking': [
|
||||||
|
{ question: "What is the booking amount?", answer: "The booking amount is 10% of the total sale value." },
|
||||||
|
{ question: "Are there any bank offers?", answer: "Yes, we have tie-ups with SBI, HDFC, and ICICI for home loans." },
|
||||||
|
{ question: "What are the additional charges?", answer: "Additional charges include GST, registration, and maintenance deposits." },
|
||||||
|
{ question: "Is the price negotiable?", answer: "Prices are competitive, but we can discuss offers on table." },
|
||||||
|
],
|
||||||
|
'Services': [
|
||||||
|
{ question: "What is Service Guided Home Buying (GHB) and Peace of Mind (POM) service?", answer: "Service Guided Home Buying (GHB) and Peace of Mind (POM) service provide end-to-end assistance in your home buying journey, ensuring a hassle-free and transparent experience from selection to possession." },
|
||||||
|
{ question: "Why should I get an advisor to help me buy a property?", answer: "An advisor acts as your unbiased partner, helping you navigate the complex real estate market, verify documents, negotiate prices, and avoid common pitfalls." },
|
||||||
|
{ question: "Which type of properties do you help with?", answer: "We assist with all types of residential properties including apartments, villas, plots, and gated communities across various budget segments." },
|
||||||
|
{ question: "Can I buy homes via Sky and Soil?", answer: "Yes, you can explore, shortlist, and book homes directly through Sky and Soil with the help of our expert advisors." },
|
||||||
|
{ question: "How do I know I am seeing all possible options in the market?", answer: "Our database covers 99% of the market inventory, and our advisors provide unbiased recommendations based on your specific requirements, not just what we want to sell." },
|
||||||
|
{ question: "How can I be assured that I am paying the right price?", answer: "We provide data-backed market analysis and comparative pricing reports to ensure you get the best value for your investment." },
|
||||||
|
{ question: "What other services can Sky & Soil help me with after booking?", answer: "Post-booking, we assist with legal verification, loan processing, registration, and even interior design and property management services." },
|
||||||
|
{ question: "What is the cost of the services?", answer: "Our basic advisory services are free for home buyers. We charge a nominal fee only for premium services like legal verification and dedicated relationship management." },
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
|
slug: "godrej-lakeside-orchard",
|
||||||
title: "Godrej Lakeside Orchard",
|
title: "Godrej Lakeside Orchard",
|
||||||
|
metaTitle: "Godrej Lakeside Orchard",
|
||||||
|
metaDescription: "Wake up to serene lake views at Godrej Lakeside Orchard. Luxury 2, 3, 3.5 & 4.5 BHK apartments offer a resort-like living experience on Sarjapur Road.",
|
||||||
category: "Luxury",
|
category: "Luxury",
|
||||||
location: "Sarjapur Road, Bangalore",
|
location: "Sarjapur Road, Bangalore",
|
||||||
price: "₹ 1.89 Cr*",
|
price: "₹ 1.89 Cr*",
|
||||||
description: "Wake up to the serene views of a lake and orchard at Godrej Lakeside Orchard. These luxury 2, 3, 3.5 & 4.5 BHK apartments offer a resort-like living experience on Sarjapur Road.",
|
description: "Wake up to the serene views of a lake and orchard at Godrej Lakeside Orchard. These luxury 2, 3, 3.5 & 4.5 BHK apartments offer a resort-like living experience on Sarjapur Road.",
|
||||||
status: "Trending",
|
status: "Trending",
|
||||||
image: "/hero-image.jpg",
|
image: "/assets/images/projects/godrej-lakeside.webp",
|
||||||
images: ["/hero-image.jpg", "/hero-image.jpg", "/hero-image.jpg"],
|
images: ["/assets/images/projects/details/lakeside-1.webp", "/assets/images/projects/details/lakeside-2.webp", "/assets/images/projects/details/lakeside-3.webp"],
|
||||||
overview: {
|
overview: {
|
||||||
bhk: "2, 3, 3.5 & 4.5 BHK",
|
bhk: "3 BHK",
|
||||||
size: "1200 - 2800 Sq.Ft",
|
size: "1500 - 2200 Sq.Ft",
|
||||||
possession: "2028",
|
possession: "Dec 2027",
|
||||||
totalUnits: "500",
|
totalUnits: "400",
|
||||||
|
badges: ["Better", "Average", "Subpar"],
|
||||||
|
stats: [
|
||||||
|
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
|
||||||
|
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
|
||||||
|
{ label: "Approach Road", value: "15 meters", subtext: "Avg is 15 meters", icon: "road" },
|
||||||
|
{ label: "Open Area", value: "45%", subtext: "Avg is 45%", icon: "openArea" },
|
||||||
|
{ label: "Unit Density", value: "13 units/acre", subtext: "Avg is 13 units/acre", icon: "density" },
|
||||||
|
{ label: "Club House", value: "50000 sqft", subtext: "Avg is 50000 sqft", icon: "clubhouse" },
|
||||||
|
],
|
||||||
|
description: "BARCA at Godrej MSR City is located in Shettigere Road, Bangalore. In its vicinity, the closest metro is Dommasandra Circle Metro Station. It takes approximately 84 mins to reach the Kempegowda Airport from this property. The area is well-connected to major IT hubs, schools, and hospitals, making it an ideal choice for families and professionals alike."
|
||||||
},
|
},
|
||||||
amenities: ["Lakeside Promenade", "Fruit Orchard", "Clubhouse", "Tennis Court", "Spa & Sauna", "Library"],
|
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
||||||
|
pricingDetails: {
|
||||||
|
totalRange: "₹1.89 CR",
|
||||||
|
emi: "₹87,000",
|
||||||
|
projectAvg: "₹9,200",
|
||||||
|
marketAvg: "₹8,800"
|
||||||
|
},
|
||||||
|
masterPlan: {
|
||||||
|
description: "Godrej Lakeside Orchard master plan is centered around the stunning 8-acre lake and orchard theme. The 45-acre development features waterfront promenades, floating gardens, and orchard zones. The resort-style layout offers unparalleled views and lifestyle amenities.",
|
||||||
|
image: "/assets/images/image.png",
|
||||||
|
totalUnits: "520",
|
||||||
|
waterSource: "Lake Water",
|
||||||
|
parkArea: "25.2 Acres",
|
||||||
|
landType: "Residential"
|
||||||
|
},
|
||||||
|
skyandsoilClarity: {
|
||||||
|
familiesHelped: "300+",
|
||||||
|
},
|
||||||
|
pricingUpdateDate: "25 Oct 2025",
|
||||||
|
reraInfo: {
|
||||||
|
isApproved: true,
|
||||||
|
approvalDate: "Jul 2024"
|
||||||
|
},
|
||||||
|
brochureCard: {
|
||||||
|
title: "See Godrej Lakeside Orchard brochure",
|
||||||
|
description: "Explore luxury lakeside living with our detailed brochure"
|
||||||
|
},
|
||||||
|
prosConsCard: {
|
||||||
|
title: "Lakeside living insights",
|
||||||
|
description: "Expert analysis of luxury lakefront lifestyle & amenities",
|
||||||
|
buttonText: "Get Insights"
|
||||||
|
},
|
||||||
|
floorPlans: [
|
||||||
|
{ id: "30 x 40", price: "₹1.24 Crores", area: "1200 sqft", direction: "North West, South East, North" },
|
||||||
|
{ id: "30 x 50", price: "₹1.55 Crores", area: "1500 sqft", direction: "North West, South East, East, West" },
|
||||||
|
{ id: "30 x 60", price: "₹1.86 Crores", area: "1800 sqft", direction: "North, East, West" },
|
||||||
|
{ id: "40 x 60", price: "₹2.48 Crores", area: "2400 sqft", direction: "East, West" },
|
||||||
|
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
|
||||||
|
],
|
||||||
|
approvals: [
|
||||||
|
{ name: "Approved by BDA", available: true },
|
||||||
|
{ name: "Approved by BWSSB", available: false },
|
||||||
|
{ name: "Approved by MOEF", available: false },
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ title: "City Development Plan", description: "Used to identify land types, sensitive zones/flood risks, future developments etc." },
|
||||||
|
{ title: "Legal Opinion", description: "Used to only declare any litigations by the builder. Do NOT mistake for a clean title." },
|
||||||
|
{ title: "Development Rights", description: "Outlines the agreed terms of development between the land owner & the builder" },
|
||||||
|
],
|
||||||
|
builder: {
|
||||||
|
name: "Modern Spaaces",
|
||||||
|
description: "Established in 2012, Modern Spaces focuses on delivering thoughtfully designed residential and commercial developments. With over 1 million sq. ft. completed, the company caters to mid-segment and premium buyers in Bengaluru. Key projects like Modern Greens, Modern Heights, and Modern Meadows are recognized for their innovative layouts and contemporary features. Modern Spaces integrates green practices such as energy-efficient construction and landscaped open spaces into its developments. Known for timely delivery and quality construction, Modern Spaces continues to serve Bengaluru's growing real estate market.",
|
||||||
|
establishedYear: "2012",
|
||||||
|
completedProjects: "No Data",
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
name: "Varthur",
|
||||||
|
description: "Varthur is a suburb situated in the Eastern Periphery of Bangalore. It is part of the Whitefield township and is known for the Varthur Lake, the second largest lake in Bangalore.",
|
||||||
|
mapImage: "/assets/images/map-placeholder.webp",
|
||||||
|
pros: [
|
||||||
|
"Proximity to Tech Hubs: Close to major IT parks like Whitefield, making it ideal for professionals.",
|
||||||
|
"Well-Established Infrastructure: Good schools, hospitals, and shopping centers nearby.",
|
||||||
|
"Green Spaces: Presence of Varthur Lake and other green areas for recreation.",
|
||||||
|
],
|
||||||
|
cons: [
|
||||||
|
"Traffic Congestion: Can get crowded during peak hours.",
|
||||||
|
"Pollution Levels: Air quality can vary depending on traffic and construction activity.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
detailedAmenities: {
|
||||||
|
Lifestyle: [
|
||||||
|
{ name: "Gym - Indoor", available: true, rare: true },
|
||||||
|
{ name: "Gym - Outdoor", available: true },
|
||||||
|
{ name: "Kids Play Area", available: true, rare: true },
|
||||||
|
{ name: "Amphitheatre", available: false },
|
||||||
|
{ name: "Cafe/Restaurant", available: false },
|
||||||
|
],
|
||||||
|
Sports: [
|
||||||
|
{ name: "Running Track", available: true, rare: true },
|
||||||
|
{ name: "Badminton", available: false },
|
||||||
|
{ name: "Basketball", available: false },
|
||||||
|
{ name: "Cricket Ground", available: false },
|
||||||
|
{ name: "Cricket Pitch", available: false },
|
||||||
|
],
|
||||||
|
Natural: [
|
||||||
|
{ name: "Army Land", available: false },
|
||||||
|
{ name: "Forest", available: false },
|
||||||
|
{ name: "Golf Course", available: false },
|
||||||
|
{ name: "Lake", available: false },
|
||||||
|
{ name: "Mountain/Hill", available: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
faq: {
|
||||||
|
'Project Details': [
|
||||||
|
{ question: "What is the total land area?", answer: "The project is spread across 5 acres of prime land." },
|
||||||
|
{ question: "How many units are there?", answer: "There are a total of 450 luxury units." },
|
||||||
|
{ question: "What is the completion date?", answer: "The project is expected to be completed by Dec 2026." },
|
||||||
|
{ question: "Is it RERA approved?", answer: "Yes, the project is fully RERA approved." },
|
||||||
|
],
|
||||||
|
'Location & Connectivity': [
|
||||||
|
{ question: "How far is the nearest metro station?", answer: "The nearest metro station is just 2km away." },
|
||||||
|
{ question: "Are there schools nearby?", answer: "Yes, there are several international schools within a 3km radius." },
|
||||||
|
{ question: "How is the road connectivity?", answer: "The project is well-connected to the Outer Ring Road and Whitefield." },
|
||||||
|
{ question: "Is there a hospital nearby?", answer: "Manipal Hospital is located 1.5km from the project." },
|
||||||
|
],
|
||||||
|
'Price & Booking': [
|
||||||
|
{ question: "What is the booking amount?", answer: "The booking amount is 10% of the total sale value." },
|
||||||
|
{ question: "Are there any bank offers?", answer: "Yes, we have tie-ups with SBI, HDFC, and ICICI for home loans." },
|
||||||
|
{ question: "What are the additional charges?", answer: "Additional charges include GST, registration, and maintenance deposits." },
|
||||||
|
{ question: "Is the price negotiable?", answer: "Prices are competitive, but we can discuss offers on table." },
|
||||||
|
],
|
||||||
|
'Services': [
|
||||||
|
{ question: "What is Service Guided Home Buying (GHB) and Peace of Mind (POM) service?", answer: "Service Guided Home Buying (GHB) and Peace of Mind (POM) service provide end-to-end assistance in your home buying journey, ensuring a hassle-free and transparent experience from selection to possession." },
|
||||||
|
{ question: "Why should I get an advisor to help me buy a property?", answer: "An advisor acts as your unbiased partner, helping you navigate the complex real estate market, verify documents, negotiate prices, and avoid common pitfalls." },
|
||||||
|
{ question: "Which type of properties do you help with?", answer: "We assist with all types of residential properties including apartments, villas, plots, and gated communities across various budget segments." },
|
||||||
|
{ question: "Can I buy homes via Sky and Soil?", answer: "Yes, you can explore, shortlist, and book homes directly through Sky and Soil with the help of our expert advisors." },
|
||||||
|
{ question: "How do I know I am seeing all possible options in the market?", answer: "Our database covers 99% of the market inventory, and our advisors provide unbiased recommendations based on your specific requirements, not just what we want to sell." },
|
||||||
|
{ question: "How can I be assured that I am paying the right price?", answer: "We provide data-backed market analysis and comparative pricing reports to ensure you get the best value for your investment." },
|
||||||
|
{ question: "What other services can Sky & Soil help me with after booking?", answer: "Post-booking, we assist with legal verification, loan processing, registration, and even interior design and property management services." },
|
||||||
|
{ question: "What is the cost of the services?", answer: "Our basic advisory services are free for home buyers. We charge a nominal fee only for premium services like legal verification and dedicated relationship management." },
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
|
slug: "godrej-tiara",
|
||||||
title: "Godrej Tiara",
|
title: "Godrej Tiara",
|
||||||
|
metaTitle: "Godrej Tiara",
|
||||||
|
metaDescription: "Godrej Tiara presents exclusive 3 & 4 BHK apartments in Yeshwanthpur. Experience unmatched luxury, sophisticated design, and panoramic city skyline views.",
|
||||||
category: "Luxury",
|
category: "Luxury",
|
||||||
location: "Yeshwanthpur, Bangalore",
|
location: "Yeshwanthpur, Bangalore",
|
||||||
price: "Price on Request",
|
price: "₹ 1.89 Cr*",
|
||||||
description: "Godrej Tiara presents exclusive 3 & 4 BHK apartments in the heart of Yeshwanthpur. Experience unmatched luxury, sophisticated design, and panoramic views of the city skyline.",
|
description: "Godrej Tiara presents exclusive 3 & 4 BHK apartments in the heart of Yeshwanthpur. Experience unmatched luxury, sophisticated design, and panoramic views of the city skyline.",
|
||||||
status: "Premium",
|
status: "Premium",
|
||||||
image: "/hero-image.jpg",
|
image: "/assets/images/projects/godrej-tiara.webp",
|
||||||
images: ["/hero-image.jpg", "/hero-image.jpg", "/hero-image.jpg"],
|
images: ["/assets/images/projects/details/tiara-1.webp", "/assets/images/projects/details/tiara-2.webp", "/assets/images/projects/details/tiara-3.webp"],
|
||||||
overview: {
|
overview: {
|
||||||
bhk: "3 & 4 BHK",
|
bhk: "3 BHK",
|
||||||
size: "2000 - 3500 Sq.Ft",
|
size: "1500 - 2200 Sq.Ft",
|
||||||
possession: "2027",
|
possession: "Dec 2027",
|
||||||
totalUnits: "250",
|
totalUnits: "400",
|
||||||
|
badges: ["Better", "Average", "Subpar"],
|
||||||
|
stats: [
|
||||||
|
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
|
||||||
|
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
|
||||||
|
{ label: "Approach Road", value: "15 meters", subtext: "Avg is 15 meters", icon: "road" },
|
||||||
|
{ label: "Open Area", value: "45%", subtext: "Avg is 45%", icon: "openArea" },
|
||||||
|
{ label: "Unit Density", value: "13 units/acre", subtext: "Avg is 13 units/acre", icon: "density" },
|
||||||
|
{ label: "Club House", value: "50000 sqft", subtext: "Avg is 50000 sqft", icon: "clubhouse" },
|
||||||
|
],
|
||||||
|
description: "BARCA at Godrej MSR City is located in Shettigere Road, Bangalore. In its vicinity, the closest metro is Dommasandra Circle Metro Station. It takes approximately 84 mins to reach the Kempegowda Airport from this property. The area is well-connected to major IT hubs, schools, and hospitals, making it an ideal choice for families and professionals alike."
|
||||||
},
|
},
|
||||||
amenities: ["Sky Lounge", "Temperature Controlled Pool", "Private Cinema", "Concierge Service", "Business Center", "Valet Parking"],
|
amenities: ["Swimming Pool", "Clubhouse", "Gymnasium", "Landscaped Gardens", "Kids Play Area", "24/7 Security"],
|
||||||
|
pricingDetails: {
|
||||||
|
totalRange: "₹1.89 CR*",
|
||||||
|
emi: "₹86,800",
|
||||||
|
projectAvg: "₹10,500",
|
||||||
|
marketAvg: "₹10,000"
|
||||||
|
},
|
||||||
|
masterPlan: {
|
||||||
|
description: "Godrej Tiara master plan embodies luxury with its tri-tower design offering skyline views. The 15-acre premium development features a sky lounge, rooftop gardens, and exclusive amenities. The carefully planned layout ensures maximum privacy and breathtaking city views from every unit.",
|
||||||
|
image: "/assets/images/image.png",
|
||||||
|
totalUnits: "300 Luxury Apartments",
|
||||||
|
waterSource: "BWSSB",
|
||||||
|
parkArea: "6.8 Acres",
|
||||||
|
landType: "Residential"
|
||||||
|
},
|
||||||
|
skyandsoilClarity: {
|
||||||
|
familiesHelped: "300+",
|
||||||
|
},
|
||||||
|
pricingUpdateDate: "01 Nov 2025",
|
||||||
|
reraInfo: {
|
||||||
|
isApproved: true,
|
||||||
|
approvalDate: "Oct 2024"
|
||||||
|
},
|
||||||
|
brochureCard: {
|
||||||
|
title: "See Godrej Tiara brochure",
|
||||||
|
description: "Discover exclusive luxury apartments with panoramic city views"
|
||||||
|
},
|
||||||
|
prosConsCard: {
|
||||||
|
title: "Premium living analysis",
|
||||||
|
description: "Get expert evaluation of this exclusive luxury development",
|
||||||
|
buttonText: "Get Premium Analysis"
|
||||||
|
},
|
||||||
|
floorPlans: [
|
||||||
|
{ id: "30 x 40", price: "₹1.24 Crores", area: "1200 sqft", direction: "North West, South East, North" },
|
||||||
|
{ id: "30 x 50", price: "₹1.55 Crores", area: "1500 sqft", direction: "North West, South East, East, West" },
|
||||||
|
{ id: "30 x 60", price: "₹1.86 Crores", area: "1800 sqft", direction: "North, East, West" },
|
||||||
|
{ id: "40 x 60", price: "₹2.48 Crores", area: "2400 sqft", direction: "East, West" },
|
||||||
|
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
|
||||||
|
],
|
||||||
|
approvals: [
|
||||||
|
{ name: "Approved by BDA", available: true },
|
||||||
|
{ name: "Approved by BWSSB", available: false },
|
||||||
|
{ name: "Approved by MOEF", available: false },
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ title: "City Development Plan", description: "Used to identify land types, sensitive zones/flood risks, future developments etc." },
|
||||||
|
{ title: "Legal Opinion", description: "Used to only declare any litigations by the builder. Do NOT mistake for a clean title." },
|
||||||
|
{ title: "Development Rights", description: "Outlines the agreed terms of development between the land owner & the builder" },
|
||||||
|
],
|
||||||
|
builder: {
|
||||||
|
name: "Modern Spaaces",
|
||||||
|
description: "Established in 2012, Modern Spaces focuses on delivering thoughtfully designed residential and commercial developments. With over 1 million sq. ft. completed, the company caters to mid-segment and premium buyers in Bengaluru. Key projects like Modern Greens, Modern Heights, and Modern Meadows are recognized for their innovative layouts and contemporary features. Modern Spaces integrates green practices such as energy-efficient construction and landscaped open spaces into its developments. Known for timely delivery and quality construction, Modern Spaces continues to serve Bengaluru's growing real estate market.",
|
||||||
|
establishedYear: "2012",
|
||||||
|
completedProjects: "No Data",
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
name: "Varthur",
|
||||||
|
description: "Varthur is a suburb situated in the Eastern Periphery of Bangalore. It is part of the Whitefield township and is known for the Varthur Lake, the second largest lake in Bangalore.",
|
||||||
|
mapImage: "/assets/images/map-placeholder.webp",
|
||||||
|
pros: [
|
||||||
|
"Proximity to Tech Hubs: Close to major IT parks like Whitefield, making it ideal for professionals.",
|
||||||
|
"Well-Established Infrastructure: Good schools, hospitals, and shopping centers nearby.",
|
||||||
|
"Green Spaces: Presence of Varthur Lake and other green areas for recreation.",
|
||||||
|
],
|
||||||
|
cons: [
|
||||||
|
"Traffic Congestion: Can get crowded during peak hours.",
|
||||||
|
"Pollution Levels: Air quality can vary depending on traffic and construction activity.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
detailedAmenities: {
|
||||||
|
Lifestyle: [
|
||||||
|
{ name: "Gym - Indoor", available: true, rare: true },
|
||||||
|
{ name: "Gym - Outdoor", available: true },
|
||||||
|
{ name: "Kids Play Area", available: true, rare: true },
|
||||||
|
{ name: "Amphitheatre", available: false },
|
||||||
|
{ name: "Cafe/Restaurant", available: false },
|
||||||
|
],
|
||||||
|
Sports: [
|
||||||
|
{ name: "Running Track", available: true, rare: true },
|
||||||
|
{ name: "Badminton", available: false },
|
||||||
|
{ name: "Basketball", available: false },
|
||||||
|
{ name: "Cricket Ground", available: false },
|
||||||
|
{ name: "Cricket Pitch", available: false },
|
||||||
|
],
|
||||||
|
Natural: [
|
||||||
|
{ name: "Army Land", available: false },
|
||||||
|
{ name: "Forest", available: false },
|
||||||
|
{ name: "Golf Course", available: false },
|
||||||
|
{ name: "Lake", available: false },
|
||||||
|
{ name: "Mountain/Hill", available: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
faq: {
|
||||||
|
'Project Details': [
|
||||||
|
{ question: "What is the total land area?", answer: "The project is spread across 5 acres of prime land." },
|
||||||
|
{ question: "How many units are there?", answer: "There are a total of 450 luxury units." },
|
||||||
|
{ question: "What is the completion date?", answer: "The project is expected to be completed by Dec 2026." },
|
||||||
|
{ question: "Is it RERA approved?", answer: "Yes, the project is fully RERA approved." },
|
||||||
|
],
|
||||||
|
'Location & Connectivity': [
|
||||||
|
{ question: "How far is the nearest metro station?", answer: "The nearest metro station is just 2km away." },
|
||||||
|
{ question: "Are there schools nearby?", answer: "Yes, there are several international schools within a 3km radius." },
|
||||||
|
{ question: "How is the road connectivity?", answer: "The project is well-connected to the Outer Ring Road and Whitefield." },
|
||||||
|
{ question: "Is there a hospital nearby?", answer: "Manipal Hospital is located 1.5km from the project." },
|
||||||
|
],
|
||||||
|
'Price & Booking': [
|
||||||
|
{ question: "What is the booking amount?", answer: "The booking amount is 10% of the total sale value." },
|
||||||
|
{ question: "Are there any bank offers?", answer: "Yes, we have tie-ups with SBI, HDFC, and ICICI for home loans." },
|
||||||
|
{ question: "What are the additional charges?", answer: "Additional charges include GST, registration, and maintenance deposits." },
|
||||||
|
{ question: "Is the price negotiable?", answer: "Prices are competitive, but we can discuss offers on table." },
|
||||||
|
],
|
||||||
|
'Services': [
|
||||||
|
{ question: "What is Service Guided Home Buying (GHB) and Peace of Mind (POM) service?", answer: "Service Guided Home Buying (GHB) and Peace of Mind (POM) service provide end-to-end assistance in your home buying journey, ensuring a hassle-free and transparent experience from selection to possession." },
|
||||||
|
{ question: "Why should I get an advisor to help me buy a property?", answer: "An advisor acts as your unbiased partner, helping you navigate the complex real estate market, verify documents, negotiate prices, and avoid common pitfalls." },
|
||||||
|
{ question: "Which type of properties do you help with?", answer: "We assist with all types of residential properties including apartments, villas, plots, and gated communities across various budget segments." },
|
||||||
|
{ question: "Can I buy homes via Sky and Soil?", answer: "Yes, you can explore, shortlist, and book homes directly through Sky and Soil with the help of our expert advisors." },
|
||||||
|
{ question: "How do I know I am seeing all possible options in the market?", answer: "Our database covers 99% of the market inventory, and our advisors provide unbiased recommendations based on your specific requirements, not just what we want to sell." },
|
||||||
|
{ question: "How can I be assured that I am paying the right price?", answer: "We provide data-backed market analysis and comparative pricing reports to ensure you get the best value for your investment." },
|
||||||
|
{ question: "What other services can Sky & Soil help me with after booking?", answer: "Post-booking, we assist with legal verification, loan processing, registration, and even interior design and property management services." },
|
||||||
|
{ question: "What is the cost of the services?", answer: "Our basic advisory services are free for home buyers. We charge a nominal fee only for premium services like legal verification and dedicated relationship management." },
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||