Compare commits

...

20 Commits
main ... main

Author SHA1 Message Date
akash
253a7aa5d2 Mobile responisve are fixed 2025-12-08 23:20:50 +05:30
akash
01d4df18a4 loader issue fixed 2025-12-08 19:24:12 +05:30
akash
d6d14902c9 property detail page map scoll optimmized 2025-12-08 15:10:31 +05:30
akash
0c3f4a1553 About section image updated 2025-12-08 11:39:21 +05:30
akash
2f5aee6d4b About section image update 2025-12-08 11:26:34 +05:30
akash
725590574e header trnsprent increased 2025-12-06 23:30:20 +05:30
akash
3c6a8ac5ff correction are fixed 2025-12-06 21:58:04 +05:30
akash
8a1c1c414d nav issue fixed 2025-12-06 19:03:15 +05:30
akash
2a9404d1e5 Map integration updated 2025-12-05 23:20:44 +05:30
akash
1b4729145a new strucure are updated 2025-12-05 21:43:53 +05:30
akash
debfdc8e2b Property detail page update 2025-12-05 20:23:05 +05:30
akash
04c6ff164e home page about setion new image format update 2025-12-05 15:39:36 +05:30
70f2797703 details page updated 2025-11-29 19:44:34 +05:30
bcd517eb05 meta title and description updated 2025-11-29 19:16:28 +05:30
5e48dad541 hero banner black overlay removed 2025-11-28 13:54:32 +05:30
ace556fa59 banner images updated 2025-11-26 19:08:32 +05:30
0713049d30 pages updated 2025-11-26 19:01:02 +05:30
9249b1fa29 faq tab updated 2025-11-26 13:54:46 +05:30
b75f15bc22 projects images are updated 2025-11-25 22:11:07 +05:30
943f440eae contact integration, images, seo test, sitemap updated 2025-11-25 20:18:07 +05:30
92 changed files with 6062 additions and 883 deletions

34
build_log_sky.txt Normal file
View 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
View File

@ -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",

View File

@ -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"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

4
public/robots.txt Normal file
View File

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://skyandsoil.metatronnest.com/sitemap.xml

1
public/sitemap.xml Normal file
View 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
View 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>

View 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);
}
});

View 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();

View File

@ -0,0 +1 @@
Page URL,Image Src,Alt Text,Issue Type
1 Page URL Image Src Alt Text Issue Type

View 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}`);
})();

View File

@ -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 />

View File

@ -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 />

View File

@ -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);

View File

@ -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>

View File

@ -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
View 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>
);
}

View 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>
);
}

View File

@ -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>
);
} }

View File

@ -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>
);
}

View 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} />;
}

View 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 />;
}

View 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>
);
}

View File

@ -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 </motion.div>
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>
</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>
); );
} }

View File

@ -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">
@ -60,8 +71,8 @@ export default function CompareBar() {
<Link <Link
href="/compare" href="/compare"
className={`px-6 py-3 rounded-lg font-semibold transition-all ${compareList.length >= 2 className={`px-6 py-3 rounded-lg font-semibold transition-all ${compareList.length >= 2
? "bg-primary text-white hover:bg-blue-700 shadow-lg hover:shadow-xl" ? "bg-primary text-white hover:bg-blue-700 shadow-lg hover:shadow-xl"
: "bg-gray-300 text-gray-500 cursor-not-allowed" : "bg-gray-300 text-gray-500 cursor-not-allowed"
}`} }`}
onClick={(e) => { onClick={(e) => {
if (compareList.length < 2) { if (compareList.length < 2) {

View File

@ -59,177 +59,180 @@ export default function CompareClient() {
</Link> </Link>
</div> </div>
) : ( ) : (
<div className="overflow-x-auto"> <>
<table className="w-full border-collapse"> <h2 className="text-3xl font-bold text-foreground mb-8">Property Comparison</h2>
<thead> <div className="overflow-x-auto">
<tr className="bg-white dark:bg-gray-900 sticky top-20 z-10 shadow-md"> <table className="w-full border-collapse">
<th className="p-4 text-left font-semibold text-foreground border-b-2 border-gray-200 dark:border-gray-800 w-48"> <thead>
Features <tr className="bg-white dark:bg-gray-900 sticky top-20 z-10 shadow-md">
</th> <th className="p-4 text-left font-semibold text-foreground border-b-2 border-gray-200 dark:border-gray-800 w-48">
{compareList.map((property) => ( Features
<th key={property.id} className="p-4 border-b-2 border-gray-200 dark:border-gray-800 min-w-[280px]"> </th>
<div className="relative"> {compareList.map((property) => (
<th key={property.id} className="p-4 border-b-2 border-gray-200 dark:border-gray-800 min-w-[280px]">
<div className="relative">
<button
onClick={() => removeFromCompare(property.id)}
className="absolute -top-2 -right-2 w-8 h-8 bg-red-500 text-white rounded-full flex items-center justify-center hover:bg-red-600 transition-colors shadow-md z-10"
aria-label="Remove from comparison"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<div className="relative h-40 rounded-xl overflow-hidden mb-3">
<Image
src={property.image}
alt={property.title}
fill
className="object-cover"
/>
</div>
<h3 className="font-bold text-foreground text-lg mb-1">{property.title}</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">{property.location}</p>
</div>
</th>
))}
{/* Add Property Slot */}
{compareList.length < 4 && (
<th className="p-4 border-b-2 border-gray-200 dark:border-gray-800 min-w-[280px]">
<button <button
onClick={() => removeFromCompare(property.id)} onClick={() => setShowAddModal(true)}
className="absolute -top-2 -right-2 w-8 h-8 bg-red-500 text-white rounded-full flex items-center justify-center hover:bg-red-600 transition-colors shadow-md z-10" className="w-full h-40 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-xl flex items-center justify-center hover:border-primary hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-all group"
aria-label="Remove from comparison"
> >
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="text-center">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> <svg className="w-12 h-12 mx-auto text-gray-400 group-hover:text-primary mb-2 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
<p className="text-sm text-gray-500 dark:text-gray-400 group-hover:text-primary font-medium transition-colors">Add Property</p>
</div>
</button> </button>
<div className="relative h-40 rounded-xl overflow-hidden mb-3"> </th>
<Image )}
src={property.image} </tr>
alt={property.title} </thead>
fill <tbody>
className="object-cover" {/* Price */}
/> <tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
</div> <td className="p-4 font-semibold text-foreground">Price</td>
<h3 className="font-bold text-foreground text-lg mb-1">{property.title}</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">{property.location}</p>
</div>
</th>
))}
{/* Add Property Slot */}
{compareList.length < 4 && (
<th className="p-4 border-b-2 border-gray-200 dark:border-gray-800 min-w-[280px]">
<button
onClick={() => setShowAddModal(true)}
className="w-full h-40 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-xl flex items-center justify-center hover:border-primary hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-all group"
>
<div className="text-center">
<svg className="w-12 h-12 mx-auto text-gray-400 group-hover:text-primary mb-2 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
<p className="text-sm text-gray-500 dark:text-gray-400 group-hover:text-primary font-medium transition-colors">Add Property</p>
</div>
</button>
</th>
)}
</tr>
</thead>
<tbody>
{/* Price */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Price</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center">
<span className="text-2xl font-bold text-primary">{property.price}</span>
</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Configuration */}
<tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Configuration</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.bhk}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Area */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Area</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.size}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Possession */}
<tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Possession</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.possession}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Total Units */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Total Units</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.totalUnits}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Category */}
<tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Property Type</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.category}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Status */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Status</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center">
<span className={`inline-block px-3 py-1 rounded-full text-sm font-semibold ${property.status === "Sold Out"
? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
: property.status === "New Launch"
? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400"
: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"
}`}>
{property.status}
</span>
</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Amenities Header */}
<tr className="bg-gray-100 dark:bg-gray-800">
<td colSpan={compareList.length + (compareList.length < 4 ? 2 : 1)} className="p-4 font-bold text-foreground text-lg">
Amenities
</td>
</tr>
{/* Amenities - Get all unique amenities */}
{Array.from(new Set(compareList.flatMap(p => p.amenities))).map((amenity, idx) => (
<tr key={idx} className={idx % 2 === 0 ? "bg-white dark:bg-gray-900" : "bg-gray-50 dark:bg-gray-900/50"}>
<td className="p-4 text-foreground">{amenity}</td>
{compareList.map((property) => ( {compareList.map((property) => (
<td key={property.id} className="p-4 text-center"> <td key={property.id} className="p-4 text-center">
{property.amenities.includes(amenity) ? ( <span className="text-2xl font-bold text-primary">{property.price}</span>
<svg className="w-6 h-6 text-green-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
) : (
<svg className="w-6 h-6 text-red-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
)}
</td> </td>
))} ))}
{compareList.length < 4 && <td className="p-4"></td>} {compareList.length < 4 && <td className="p-4"></td>}
</tr> </tr>
))}
{/* View Details */} {/* Configuration */}
<tr className="bg-white dark:bg-gray-900"> <tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Actions</td> <td className="p-4 font-semibold text-foreground">Configuration</td>
{compareList.map((property) => ( {compareList.map((property) => (
<td key={property.id} className="p-4 text-center"> <td key={property.id} className="p-4 text-center text-foreground">{property.overview.bhk}</td>
<Link ))}
href={`/properties/${property.id}`} {compareList.length < 4 && <td className="p-4"></td>}
className="inline-block px-6 py-2 bg-primary text-white rounded-lg hover:bg-blue-700 transition-colors font-medium" </tr>
>
View Details {/* Area */}
</Link> <tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Area</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.size}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Possession */}
<tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Possession</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.possession}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Total Units */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Total Units</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.overview.totalUnits}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Category */}
<tr className="bg-gray-50 dark:bg-gray-900/50 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Property Type</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center text-foreground">{property.category}</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Status */}
<tr className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<td className="p-4 font-semibold text-foreground">Status</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center">
<span className={`inline-block px-3 py-1 rounded-full text-sm font-semibold ${property.status === "Sold Out"
? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
: property.status === "New Launch"
? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400"
: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"
}`}>
{property.status}
</span>
</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
{/* Amenities Header */}
<tr className="bg-gray-100 dark:bg-gray-800">
<td colSpan={compareList.length + (compareList.length < 4 ? 2 : 1)} className="p-4 font-bold text-foreground text-lg">
Amenities
</td> </td>
</tr>
{/* Amenities - Get all unique amenities */}
{Array.from(new Set(compareList.flatMap(p => p.amenities))).map((amenity, idx) => (
<tr key={idx} className={idx % 2 === 0 ? "bg-white dark:bg-gray-900" : "bg-gray-50 dark:bg-gray-900/50"}>
<td className="p-4 text-foreground">{amenity}</td>
{compareList.map((property) => (
<td key={property.id} className="p-4 text-center">
{property.amenities.includes(amenity) ? (
<svg className="w-6 h-6 text-green-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
) : (
<svg className="w-6 h-6 text-red-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
)}
</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
))} ))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr> {/* View Details */}
</tbody> <tr className="bg-white dark:bg-gray-900">
</table> <td className="p-4 font-semibold text-foreground">Actions</td>
</div> {compareList.map((property) => (
<td key={property.id} className="p-4 text-center">
<Link
href={`/properties/${property.id}`}
className="inline-block px-6 py-2 bg-primary text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
>
View Details
</Link>
</td>
))}
{compareList.length < 4 && <td className="p-4"></td>}
</tr>
</tbody>
</table>
</div>
</>
)} )}
</div> </div>
</div> </div>

View 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='&copy; <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>
);
}

View File

@ -1,59 +1,247 @@
export default function ContactCTA() { "use client";
return (
<section id="contact" className="py-24 bg-secondary 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="p-8 md:p-12 text-center">
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
Ready to Visit Aurora Springs?
</h2>
<p className="text-gray-600 dark:text-gray-400 mb-8">
Schedule a private tour or request a call back from our relationship manager.
</p>
<form className="space-y-4 max-w-md mx-auto"> import { User, Phone, Mail, MapPin, Send } from "lucide-react";
<div> 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() {
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 (
<section id="contact" className="relative py-24 bg-[#f3f1e6] dark:bg-gray-900">
<div className="relative z-10 max-w-6xl mx-auto px-6">
<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">
Ready to Visit Aurora Springs?
</h2>
<p className="text-gray-600 dark:text-gray-400">
Schedule a private tour or request a call back from our relationship manager.
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-5">
{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>
<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>
<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>
<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>
<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>

View File

@ -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);
}; };
@ -125,8 +204,8 @@ export default function FAQ() {
key={index} key={index}
onClick={() => setActiveCard(index)} onClick={() => setActiveCard(index)}
className={`w-2 h-2 rounded-full transition-all duration-300 ${index === activeCard className={`w-2 h-2 rounded-full transition-all duration-300 ${index === activeCard
? 'bg-primary w-8' ? 'bg-primary w-8'
: 'bg-gray-400 hover:bg-gray-600' : 'bg-gray-400 hover:bg-gray-600'
}`} }`}
aria-label={`Go to slide ${index + 1}`} aria-label={`Go to slide ${index + 1}`}
/> />
@ -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"

View File

@ -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>&copy; {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>

View File

@ -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>

View File

@ -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) */}

View File

@ -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}

View File

@ -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={{

View File

@ -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"
> >

View File

@ -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">

View File

@ -62,97 +62,109 @@ 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"> >
<Image <div className="relative h-64 w-full overflow-hidden">
src={property.image} <Image
alt={property.title} src={property.image}
fill alt={property.title}
className="object-cover group-hover:scale-110 transition-transform duration-500" fill
/> className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
{/* Status Badge */} {/* Status Badge */}
{property.status && ( {property.status && (
<div className={`absolute top-4 left-4 px-3 py-1 rounded-full text-xs font-semibold ${property.status === "Sold Out" <div className={`absolute top-4 left-4 px-3 py-1 rounded-full text-xs font-semibold ${property.status === "Sold Out"
? "bg-red-500 text-white" ? "bg-red-500 text-white"
: property.status === "New Launch" : property.status === "New Launch"
? "bg-green-500 text-white" ? "bg-green-500 text-white"
: "bg-white/90 text-gray-900" : "bg-white/90 text-gray-900"
}`}> }`}>
{property.status} {property.status}
</div>
)}
{/* Action Buttons */}
<div className="absolute top-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={handleCompareClick}
className={`p-2 rounded-full shadow-lg transition-all ${inCompare
? "bg-primary text-white scale-110"
: "bg-white hover:bg-gray-100 text-gray-700"
}`}
aria-label={inCompare ? "Remove from compare" : "Add to compare"}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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" />
</svg>
</button>
<button
onClick={handleShareClick}
className="p-2 bg-white rounded-full shadow-lg hover:bg-gray-100 transition-colors"
aria-label="Share"
>
<svg className="w-5 h-5 text-gray-700" 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>
</button>
<button
onClick={handleWishlistClick}
className={`p-2 rounded-full shadow-lg transition-all ${isWishlisted
? "bg-red-500 text-white scale-110"
: "bg-white hover:bg-gray-100 text-gray-700"
}`}
aria-label={isWishlisted ? "Remove from wishlist" : "Add to wishlist"}
>
<svg className="w-5 h-5" fill={isWishlisted ? "currentColor" : "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>
</button>
</div> </div>
)}
{/* Action Buttons */}
<div className="absolute top-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={handleCompareClick}
className={`p-2 rounded-full shadow-lg transition-all ${inCompare
? "bg-primary text-white scale-110"
: "bg-white hover:bg-gray-100 text-gray-700"
}`}
aria-label={inCompare ? "Remove from compare" : "Add to compare"}
>
<svg
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>
</button>
<button
onClick={handleShareClick}
className="p-2 bg-white rounded-full shadow-lg hover:bg-gray-100 transition-colors"
aria-label="Share"
>
<svg className="w-5 h-5 text-gray-700" 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>
</button>
<button
onClick={handleWishlistClick}
className={`p-2 rounded-full shadow-lg transition-all ${isWishlisted
? "bg-red-500 text-white scale-110"
: "bg-white hover:bg-gray-100 text-gray-700"
}`}
aria-label={isWishlisted ? "Remove from wishlist" : "Add to wishlist"}
>
<svg className="w-5 h-5" fill={isWishlisted ? "currentColor" : "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>
</button>
</div>
</div>
{/* Content Section */}
<div className="p-6">
<h3 className="text-xl font-bold text-foreground mb-2 group-hover:text-primary transition-colors">
{property.title}
</h3>
<div className="flex items-center text-gray-600 dark:text-gray-400 text-sm mb-4">
<svg className="w-4 h-4 mr-1" 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" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{property.location}
</div> </div>
{/* Content Section */} <div className="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-800">
<div className="p-6"> <div>
<h3 className="text-xl font-bold text-foreground mb-2 group-hover:text-primary transition-colors"> <div className="text-2xl font-bold text-primary whitespace-nowrap">{property.price}</div>
{property.title} <div className="text-sm text-gray-500 dark:text-gray-400">{property.overview.size}</div>
</h3>
<div className="flex items-center text-gray-600 dark:text-gray-400 text-sm mb-4">
<svg className="w-4 h-4 mr-1" 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" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{property.location}
</div> </div>
<div className="text-right">
<div className="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-800"> <div className="text-sm font-semibold text-foreground">{property.overview.bhk}</div>
<div> <div className="text-xs text-gray-500 dark:text-gray-400">Possession: {property.overview.possession}</div>
<div className="text-2xl font-bold text-primary">{property.price}</div>
<div className="text-sm text-gray-500 dark:text-gray-400">{property.overview.size}</div>
</div>
<div className="text-right">
<div className="text-sm font-semibold text-foreground">{property.overview.bhk}</div>
<div className="text-xs text-gray-500 dark:text-gray-400">Possession: {property.overview.possession}</div>
</div>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View 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">

View File

@ -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>
)} )}
</> </>
); );
} }

View File

@ -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,16 +58,18 @@ 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"
: "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800" : "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
}`} }`}
> >
{section.label} {section.label}

View File

@ -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,13 +78,45 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
> >
About About
</Link> </Link>
<Link <div className="flex flex-col space-y-2">
href="/projects" <div className="flex items-center justify-between">
className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors" <Link
onClick={onClose} href="/projects"
> className="text-lg font-medium text-gray-600 dark:text-gray-300 hover:text-primary transition-colors"
Projects onClick={onClose}
</Link> >
Projects
</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"

View File

@ -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 */}

View File

@ -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}

View File

@ -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." },
]
}
}, },
]; ];