full nav change

This commit is contained in:
priyanshuvish
2025-10-17 17:59:08 +05:30
parent 475956ae1a
commit ac5eded430
24 changed files with 1495 additions and 1479 deletions

378
package-lock.json generated
View File

@@ -35,6 +35,7 @@
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "*",
"cmdk": "^1.1.1",
@@ -53,7 +54,7 @@
"recharts": "^2.15.2",
"sonner": "^2.0.3",
"tailwind-merge": "*",
"tailwindcss": "^4.1.13",
"tailwindcss": "^4.1.14",
"vaul": "^1.1.2"
},
"devDependencies": {
@@ -92,7 +93,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -109,7 +109,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -126,7 +125,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -143,7 +141,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -160,7 +157,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -177,7 +173,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -194,7 +189,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -211,7 +205,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -228,7 +221,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -245,7 +237,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -262,7 +253,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -279,7 +269,6 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -296,7 +285,6 @@
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -313,7 +301,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -330,7 +317,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -347,7 +333,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -364,7 +349,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -381,7 +365,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -398,7 +381,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -415,7 +397,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -432,7 +413,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -449,7 +429,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -466,7 +445,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -483,7 +461,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -500,7 +477,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -517,7 +493,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1956,7 +1931,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1970,7 +1944,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1984,7 +1957,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1998,7 +1970,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2012,7 +1983,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2026,7 +1996,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2040,7 +2009,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2054,7 +2022,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2068,7 +2035,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2082,7 +2048,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2096,7 +2061,6 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2110,7 +2074,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2124,7 +2087,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2138,7 +2100,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2152,7 +2113,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2166,7 +2126,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2180,7 +2139,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2194,7 +2152,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2208,7 +2165,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2222,7 +2178,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2236,7 +2191,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2484,6 +2438,12 @@
"tailwindcss": "4.1.13"
}
},
"node_modules/@tailwindcss/node/node_modules/tailwindcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"license": "MIT"
},
"node_modules/@tailwindcss/oxide": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz",
@@ -2730,6 +2690,274 @@
"tailwindcss": "4.1.13"
}
},
"node_modules/@tailwindcss/postcss/node_modules/tailwindcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"license": "MIT"
},
"node_modules/@tailwindcss/vite": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.14.tgz",
"integrity": "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==",
"license": "MIT",
"dependencies": {
"@tailwindcss/node": "4.1.14",
"@tailwindcss/oxide": "4.1.14",
"tailwindcss": "4.1.14"
},
"peerDependencies": {
"vite": "^5.2.0 || ^6 || ^7"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/node": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz",
"integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==",
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"enhanced-resolve": "^5.18.3",
"jiti": "^2.6.0",
"lightningcss": "1.30.1",
"magic-string": "^0.30.19",
"source-map-js": "^1.2.1",
"tailwindcss": "4.1.14"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz",
"integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.4",
"tar": "^7.5.1"
},
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@tailwindcss/oxide-android-arm64": "4.1.14",
"@tailwindcss/oxide-darwin-arm64": "4.1.14",
"@tailwindcss/oxide-darwin-x64": "4.1.14",
"@tailwindcss/oxide-freebsd-x64": "4.1.14",
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14",
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.14",
"@tailwindcss/oxide-linux-arm64-musl": "4.1.14",
"@tailwindcss/oxide-linux-x64-gnu": "4.1.14",
"@tailwindcss/oxide-linux-x64-musl": "4.1.14",
"@tailwindcss/oxide-wasm32-wasi": "4.1.14",
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.14",
"@tailwindcss/oxide-win32-x64-msvc": "4.1.14"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-android-arm64": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz",
"integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-arm64": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz",
"integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-x64": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz",
"integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-freebsd-x64": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz",
"integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz",
"integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz",
"integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-musl": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz",
"integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-gnu": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz",
"integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-musl": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz",
"integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz",
"integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
],
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.5.0",
"@emnapi/runtime": "^1.5.0",
"@emnapi/wasi-threads": "^1.1.0",
"@napi-rs/wasm-runtime": "^1.0.5",
"@tybys/wasm-util": "^0.10.1",
"tslib": "^2.4.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz",
"integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-x64-msvc": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz",
"integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
@@ -2797,14 +3025,13 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz",
"integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -3125,7 +3352,6 @@
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -3182,7 +3408,6 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -3227,7 +3452,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -3273,9 +3497,9 @@
}
},
"node_modules/jiti": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -3561,9 +3785,9 @@
}
},
"node_modules/minizlib": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
"license": "MIT",
"dependencies": {
"minipass": "^7.1.2"
@@ -3572,21 +3796,6 @@
"node": ">= 18"
}
},
"node_modules/mkdirp": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"license": "MIT",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/motion": {
"version": "12.23.12",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.23.12.tgz",
@@ -3675,7 +3884,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -3974,7 +4182,6 @@
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
"integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -4056,9 +4263,9 @@
}
},
"node_modules/tailwindcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz",
"integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==",
"license": "MIT"
},
"node_modules/tapable": {
@@ -4075,16 +4282,15 @@
}
},
"node_modules/tar": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz",
"integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
"license": "ISC",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
"minipass": "^7.1.2",
"minizlib": "^3.0.1",
"mkdirp": "^3.0.1",
"minizlib": "^3.1.0",
"yallist": "^5.0.0"
},
"engines": {
@@ -4101,7 +4307,6 @@
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -4124,7 +4329,7 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/use-callback-ref": {
@@ -4218,7 +4423,6 @@
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",

View File

@@ -30,6 +30,7 @@
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "*",
"cmdk": "^1.1.1",
@@ -48,7 +49,7 @@
"recharts": "^2.15.2",
"sonner": "^2.0.3",
"tailwind-merge": "*",
"tailwindcss": "^4.1.13",
"tailwindcss": "^4.1.14",
"vaul": "^1.1.2"
},
"devDependencies": {

View File

@@ -1,6 +1,5 @@
import { ReactNode } from 'react';
import Navbar from './components/Navbar';
import { CitySubmenu } from './components/CitySubmenu';
import { Footer } from './components/Footer';
interface User {
@@ -11,7 +10,6 @@ interface User {
interface LayoutProps {
children: ReactNode;
activeCity?: string;
showCitySubmenu?: boolean;
onSignInClick?: () => void; // ✅ optional
onSignOutClick?: () => void;
user?: User | null;
@@ -20,7 +18,6 @@ interface LayoutProps {
export function Layout({
children,
activeCity = 'Melbourne',
showCitySubmenu = false,
onSignInClick,
onSignOutClick,
user
@@ -37,9 +34,6 @@ export function Layout({
user={user}
/>
{/* City Submenu */}
{showCitySubmenu && <CitySubmenu onClose={() => {}} />}
{/* Main Content */}
<main className="flex-1">{children}</main>

View File

@@ -282,7 +282,6 @@ export function AttractionsPage({
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
showCitySubmenu={true}
>
<div className="container mx-auto px-4 pt-56 pb-16">
{/* Page Header */}

View File

@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import Navbar from './Navbar';
import { CitySubmenu } from './CitySubmenu';
// import { CitySubmenu } from './CitySubmenu';
import { MobileAppSection } from './MobileAppSection';
import { WhyChooseCityCards } from './WhyChooseCityCards';
import { EnhancedTestimonials } from './EnhancedTestimonials';
@@ -226,7 +226,7 @@ export function BlogsPage({
user={user}
/>
<CitySubmenu
{/* <CitySubmenu
currentPage={currentPage}
onClose={() => {}}
onHomeClick={onHomeClick}
@@ -235,7 +235,7 @@ export function BlogsPage({
onPassesClick={onPassesClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
/>
/> */}
<div className="container mx-auto px-4 pt-52 pb-12 relative z-10">
{/* Page Header */}

View File

@@ -15,6 +15,7 @@ import { Textarea } from './ui/textarea';
import Navbar from './Navbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface CheckoutPageProps {
onBackClick: () => void;
@@ -179,33 +180,12 @@ export function CheckoutPage({
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Navbar
activeCity="Paris"
onCityChange={() => {}}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Header Section */}
<section className="pt-32 pb-8 bg-gradient-to-br from-muted/30 to-background">
@@ -748,20 +728,7 @@ export function CheckoutPage({
</DialogContent>
</Dialog>
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
</Layout>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import { Button } from './ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import SubNavbar from './SubNavbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { EnhancedTestimonials } from './EnhancedTestimonials';
@@ -13,6 +13,7 @@ import { FAQPage } from './FAQPage';
import { HowItWorks } from './HowItWorks';
import { WhyChooseCityCards } from './WhyChooseCityCards';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface User {
email: string;
@@ -73,35 +74,15 @@ export function CityCardsPage({
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Navbar
activeCity=""
onCityChange={() => {}}
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage="citycards"
isUserSignedIn={!!user}
user={user}
/>
>
{/* Sub Navbar */}
<SubNavbar
{/* Sub Navbar */}
{/* <SubNavbar
activeTab="citycards"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -109,283 +90,270 @@ export function CityCardsPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-secondary/5 to-background"></div>
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-secondary/5 to-background"></div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight mb-6">
<span className="font-light">What Are</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
City Cards?
</span>
</h1>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 mb-8 max-w-2xl mx-auto">
CityCards are your all-in-one pass to explore cities like never before. Get access to top attractions,
skip the lines, and save money while discovering amazing experiences.
</p>
</motion.div>
</div>
{/* Decorative elements */}
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl"></div>
<div className="absolute bottom-20 right-10 w-32 h-32 bg-secondary/10 rounded-full blur-xl"></div>
</section>
{/* Get Your City Cards Section */}
<section className="py-16">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Get your City Cards</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Choose from our range of passes designed for every type of traveler and every budget
</p>
</motion.div>
<div className="grid md:grid-cols-2 gap-8 max-w-5xl mx-auto">
{/* Selective Pass */}
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="relative flex"
transition={{ duration: 0.6 }}
>
<Card className="relative transition-all duration-300 cursor-pointer flex flex-col w-full border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30">
<CardHeader className="text-center pb-6 pt-8 flex-shrink-0">
<CardTitle className="font-poppins text-2xl font-bold mb-2 text-gray-900">
SELECTIVE PASS
</CardTitle>
<CardDescription className="text-gray-600 mb-6 leading-relaxed font-poppins h-[48px] flex items-center justify-center">
Perfect for travelers who want to explore selected attractions at their own pace with essential features.
</CardDescription>
{/* Pricing */}
<div className="mb-6">
<div className="flex items-baseline justify-center gap-2 mb-2">
<span className="text-4xl font-bold text-gray-900">$59.99</span>
<span className="text-gray-500 font-poppins">/ per person</span>
</div>
<div className="text-sm text-gray-500 font-poppins h-[20px]">
<span className="line-through mr-2">$89.99</span>
<span className="text-green-600 font-medium">Save 33%</span>
</div>
</div>
</CardHeader>
<CardContent className="pt-0 flex flex-col flex-1">
{/* Features List - Fixed Height */}
<div className="space-y-3 mb-8 flex-1 min-h-[200px]">
{[
'Access to selected attractions',
'Limited number of attractions per pass',
'Flexible validity period',
'Priority entry where available',
'Mobile ticket delivery',
'Standard customer support'
].map((feature, index) => (
<div key={index} className="flex items-start gap-3">
<Check className="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm text-gray-700 font-poppins leading-relaxed">{feature}</span>
</div>
))}
</div>
{/* CTA Button - Fixed at bottom */}
<div className="mt-auto flex-shrink-0">
<Button
className="w-full py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 bg-gray-900 hover:bg-primary text-white hover:shadow-lg font-poppins"
onClick={onCheckoutClick}
>
PURCHASE NOW
</Button>
<p className="text-xs text-gray-500 text-center mt-4 font-poppins h-[32px] flex items-center justify-center">
Free cancellation up to 24 hours Instant delivery
</p>
</div>
</CardContent>
</Card>
</motion.div>
{/* Unlimited Pass */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="relative flex"
>
<Card className="relative transition-all duration-300 cursor-pointer flex flex-col w-full ring-2 ring-primary shadow-xl">
{/* Popular Badge */}
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2 z-10">
<Badge className="bg-gradient-to-r from-yellow-400 to-orange-500 text-black px-6 py-2 font-semibold shadow-lg">
Most Popular
</Badge>
</div>
<CardHeader className="text-center pb-6 pt-8 flex-shrink-0">
<CardTitle className="font-poppins text-2xl font-bold mb-2 text-gray-900">
UNLIMITED CARD
</CardTitle>
<CardDescription className="text-gray-600 mb-6 leading-relaxed font-poppins h-[48px] flex items-center justify-center">
The ultimate experience for adventure seekers who want unlimited access to all attractions with premium features.
</CardDescription>
{/* Pricing */}
<div className="mb-6">
<div className="flex items-baseline justify-center gap-2 mb-2">
<span className="text-4xl font-bold text-gray-900">$89.99</span>
<span className="text-gray-500 font-poppins">/ per person</span>
</div>
<div className="text-sm text-gray-500 font-poppins h-[20px]">
<span className="line-through mr-2">$149.99</span>
<span className="text-green-600 font-medium">Save 40%</span>
</div>
</div>
</CardHeader>
<CardContent className="pt-0 flex flex-col flex-1">
{/* Features List - Fixed Height */}
<div className="space-y-3 mb-8 flex-1 min-h-[200px]">
{[
'Unlimited access to all attractions',
'Time-limited validity (7 days)',
'Skip-the-line access',
'Expert guide inclusion',
'Mobile app access',
'Premium customer support'
].map((feature, index) => (
<div key={index} className="flex items-start gap-3">
<Check className="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm text-gray-700 font-poppins leading-relaxed">{feature}</span>
</div>
))}
</div>
{/* CTA Button - Fixed at bottom */}
<div className="mt-auto flex-shrink-0">
<Button
className="w-full py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white shadow-lg hover:shadow-xl font-poppins"
onClick={onCheckoutClick}
>
PURCHASE NOW
</Button>
<p className="text-xs text-gray-500 text-center mt-4 font-poppins h-[32px] flex items-center justify-center">
Free cancellation up to 24 hours Instant delivery
</p>
</div>
</CardContent>
</Card>
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight mb-6">
<span className="font-light">What Are</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
City Cards?
</span>
</h1>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 mb-8 max-w-2xl mx-auto">
CityCards are your all-in-one pass to explore cities like never before. Get access to top attractions,
skip the lines, and save money while discovering amazing experiences.
</p>
</motion.div>
</div>
</div>
</section>
{/* How It Works Section */}
<HowItWorks />
{/* Decorative elements */}
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl"></div>
<div className="absolute bottom-20 right-10 w-32 h-32 bg-secondary/10 rounded-full blur-xl"></div>
</section>
{/* Why Choose CityCards Section */}
<WhyChooseCityCards />
{/* Get Your City Cards Section */}
<section className="py-16">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Get your City Cards</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Choose from our range of passes designed for every type of traveler and every budget
</p>
</motion.div>
{/* Benefits Section */}
<div className="grid md:grid-cols-2 gap-8 max-w-5xl mx-auto">
{/* Selective Pass */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="relative flex"
>
<Card className="relative transition-all duration-300 cursor-pointer flex flex-col w-full border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30">
<CardHeader className="text-center pb-6 pt-8 flex-shrink-0">
<CardTitle className="font-poppins text-2xl font-bold mb-2 text-gray-900">
SELECTIVE PASS
</CardTitle>
<CardDescription className="text-gray-600 mb-6 leading-relaxed font-poppins h-[48px] flex items-center justify-center">
Perfect for travelers who want to explore selected attractions at their own pace with essential features.
</CardDescription>
{/* Pricing */}
<div className="mb-6">
<div className="flex items-baseline justify-center gap-2 mb-2">
<span className="text-4xl font-bold text-gray-900">$59.99</span>
<span className="text-gray-500 font-poppins">/ per person</span>
</div>
<div className="text-sm text-gray-500 font-poppins h-[20px]">
<span className="line-through mr-2">$89.99</span>
<span className="text-green-600 font-medium">Save 33%</span>
</div>
</div>
</CardHeader>
{/* Ready to Explore Melbourne Section */}
<CardContent className="pt-0 flex flex-col flex-1">
{/* Features List - Fixed Height */}
<div className="space-y-3 mb-8 flex-1 min-h-[200px]">
{[
'Access to selected attractions',
'Limited number of attractions per pass',
'Flexible validity period',
'Priority entry where available',
'Mobile ticket delivery',
'Standard customer support'
].map((feature, index) => (
<div key={index} className="flex items-start gap-3">
<Check className="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm text-gray-700 font-poppins leading-relaxed">{feature}</span>
</div>
))}
</div>
{/* CTA Button - Fixed at bottom */}
<div className="mt-auto flex-shrink-0">
<Button
className="w-full py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 bg-gray-900 hover:bg-primary text-white hover:shadow-lg font-poppins"
onClick={onCheckoutClick}
>
PURCHASE NOW
</Button>
{/* FAQ Section */}
<section className="py-16 bg-gray-50">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Frequently Asked Questions</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Everything you need to know about CityCards
</p>
</motion.div>
<p className="text-xs text-gray-500 text-center mt-4 font-poppins h-[32px] flex items-center justify-center">
Free cancellation up to 24 hours Instant delivery
</p>
</div>
</CardContent>
</Card>
</motion.div>
<div className="max-w-3xl mx-auto">
<div className="space-y-4">
{[
{
question: "How do CityCards work?",
answer: "CityCards are digital passes that give you access to multiple attractions in a city. Simply download our app, activate your card, and show it at participating attractions for instant entry."
},
{
question: "Can I use my CityCard for multiple days?",
answer: "Yes! Depending on the pass you choose, CityCards can be valid for 1, 2, 3, 5, or 7 consecutive days from your first use."
},
{
question: "What's included in an Unlimited Pass?",
answer: "The Unlimited Pass includes access to all participating attractions in your chosen city, plus additional perks like discounts at restaurants and shops."
},
{
question: "Do I need to book attractions in advance?",
answer: "Most attractions allow walk-in access with your CityCard, but some popular attractions may require advance booking through our app to guarantee entry."
},
{
question: "What if I don't use all my attractions?",
answer: "There's no pressure to visit every attraction. Your CityCard gives you the flexibility to explore at your own pace and choose what interests you most."
}
].map((faq, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.1 }}
>
<Card className="hover:shadow-md transition-shadow duration-300">
<CardContent className="p-6">
<h3 className="font-merchant text-lg mb-3">{faq.question}</h3>
<p className="text-gray-600 font-poppins">{faq.answer}</p>
</CardContent>
</Card>
</motion.div>
))}
{/* Unlimited Pass */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="relative flex"
>
<Card className="relative transition-all duration-300 cursor-pointer flex flex-col w-full ring-2 ring-primary shadow-xl">
{/* Popular Badge */}
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2 z-10">
<Badge className="bg-gradient-to-r from-yellow-400 to-orange-500 text-black px-6 py-2 font-semibold shadow-lg">
Most Popular
</Badge>
</div>
<CardHeader className="text-center pb-6 pt-8 flex-shrink-0">
<CardTitle className="font-poppins text-2xl font-bold mb-2 text-gray-900">
UNLIMITED CARD
</CardTitle>
<CardDescription className="text-gray-600 mb-6 leading-relaxed font-poppins h-[48px] flex items-center justify-center">
The ultimate experience for adventure seekers who want unlimited access to all attractions with premium features.
</CardDescription>
{/* Pricing */}
<div className="mb-6">
<div className="flex items-baseline justify-center gap-2 mb-2">
<span className="text-4xl font-bold text-gray-900">$89.99</span>
<span className="text-gray-500 font-poppins">/ per person</span>
</div>
<div className="text-sm text-gray-500 font-poppins h-[20px]">
<span className="line-through mr-2">$149.99</span>
<span className="text-green-600 font-medium">Save 40%</span>
</div>
</div>
</CardHeader>
<CardContent className="pt-0 flex flex-col flex-1">
{/* Features List - Fixed Height */}
<div className="space-y-3 mb-8 flex-1 min-h-[200px]">
{[
'Unlimited access to all attractions',
'Time-limited validity (7 days)',
'Skip-the-line access',
'Expert guide inclusion',
'Mobile app access',
'Premium customer support'
].map((feature, index) => (
<div key={index} className="flex items-start gap-3">
<Check className="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm text-gray-700 font-poppins leading-relaxed">{feature}</span>
</div>
))}
</div>
{/* CTA Button - Fixed at bottom */}
<div className="mt-auto flex-shrink-0">
<Button
className="w-full py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white shadow-lg hover:shadow-xl font-poppins"
onClick={onCheckoutClick}
>
PURCHASE NOW
</Button>
<p className="text-xs text-gray-500 text-center mt-4 font-poppins h-[32px] flex items-center justify-center">
Free cancellation up to 24 hours Instant delivery
</p>
</div>
</CardContent>
</Card>
</motion.div>
</div>
</div>
</div>
</section>
</section>
{/* Mobile App Section */}
<MobileAppSection />
{/* How It Works Section */}
<HowItWorks />
{/* Customer Reviews */}
<EnhancedTestimonials />
{/* Why Choose CityCards Section */}
<WhyChooseCityCards />
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
{/* Benefits Section */}
{/* Ready to Explore Melbourne Section */}
{/* FAQ Section */}
<section className="py-16 bg-gray-50">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Frequently Asked Questions</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Everything you need to know about CityCards
</p>
</motion.div>
<div className="max-w-3xl mx-auto">
<div className="space-y-4">
{[
{
question: "How do CityCards work?",
answer: "CityCards are digital passes that give you access to multiple attractions in a city. Simply download our app, activate your card, and show it at participating attractions for instant entry."
},
{
question: "Can I use my CityCard for multiple days?",
answer: "Yes! Depending on the pass you choose, CityCards can be valid for 1, 2, 3, 5, or 7 consecutive days from your first use."
},
{
question: "What's included in an Unlimited Pass?",
answer: "The Unlimited Pass includes access to all participating attractions in your chosen city, plus additional perks like discounts at restaurants and shops."
},
{
question: "Do I need to book attractions in advance?",
answer: "Most attractions allow walk-in access with your CityCard, but some popular attractions may require advance booking through our app to guarantee entry."
},
{
question: "What if I don't use all my attractions?",
answer: "There's no pressure to visit every attraction. Your CityCard gives you the flexibility to explore at your own pace and choose what interests you most."
}
].map((faq, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.1 }}
>
<Card className="hover:shadow-md transition-shadow duration-300">
<CardContent className="p-6">
<h3 className="font-merchant text-lg mb-3">{faq.question}</h3>
<p className="text-gray-600 font-poppins">{faq.answer}</p>
</CardContent>
</Card>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* Mobile App Section */}
<MobileAppSection />
{/* Customer Reviews */}
<EnhancedTestimonials />
</Layout>
</div>
);
}

View File

@@ -1,223 +1,223 @@
import { useState, useEffect } from 'react';
import { motion } from 'motion/react';
import { useLocation, useNavigate } from 'react-router-dom';
// import { useState, useEffect } from 'react';
// import { motion } from 'motion/react';
// import { useLocation, useNavigate } from 'react-router-dom';
interface CitySubmenuProps {
onClose: () => void;
}
// interface CitySubmenuProps {
// onClose: () => void;
// }
interface SubmenuItem {
id: string;
label: string;
path?: string;
action?: () => void;
}
// interface SubmenuItem {
// id: string;
// label: string;
// path?: string;
// action?: () => void;
// }
export function CitySubmenu({ onClose }: CitySubmenuProps) {
const [isScrolled, setIsScrolled] = useState(false);
const [activeItem, setActiveItem] = useState<string | null>(null);
// export function CitySubmenu({ onClose }: CitySubmenuProps) {
// const [isScrolled, setIsScrolled] = useState(false);
// const [activeItem, setActiveItem] = useState<string | null>(null);
const location = useLocation();
const navigate = useNavigate();
// const location = useLocation();
// const navigate = useNavigate();
// Direct submenu items for Melbourne
const submenuItems: SubmenuItem[] = [
{
id: 'melbourne',
label: 'Melbourne',
path: '/melbourne'
},
{
id: 'attractions',
label: 'Attractions',
path: '/attractions'
},
{
id: 'buy-now',
label: 'Buy Now',
path: '/passes'
},
{
id: 'blogs',
label: 'Blogs',
path: '/blogs'
},
{
id: 'how-it-works',
label: 'How It Works',
path: '/how-it-works'
}
];
// // Direct submenu items for Melbourne
// const submenuItems: SubmenuItem[] = [
// {
// id: 'melbourne',
// label: 'Melbourne',
// path: '/melbourne'
// },
// {
// id: 'attractions',
// label: 'Attractions',
// path: '/attractions'
// },
// {
// id: 'buy-now',
// label: 'Buy Now',
// path: '/passes'
// },
// {
// id: 'blogs',
// label: 'Blogs',
// path: '/blogs'
// },
// {
// id: 'how-it-works',
// label: 'How It Works',
// path: '/how-it-works'
// }
// ];
// Handle scroll effects
useEffect(() => {
const handleScroll = () => {
const scrolled = window.scrollY > 20;
setIsScrolled(scrolled);
};
// // Handle scroll effects
// useEffect(() => {
// const handleScroll = () => {
// const scrolled = window.scrollY > 20;
// setIsScrolled(scrolled);
// };
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// window.addEventListener('scroll', handleScroll);
// return () => window.removeEventListener('scroll', handleScroll);
// }, []);
// Determine active item based on current route
useEffect(() => {
const currentItem = submenuItems.find(item =>
item.path && location.pathname === item.path
);
setActiveItem(currentItem?.id || null);
}, [location.pathname]);
// // Determine active item based on current route
// useEffect(() => {
// const currentItem = submenuItems.find(item =>
// item.path && location.pathname === item.path
// );
// setActiveItem(currentItem?.id || null);
// }, [location.pathname]);
const handleSubmenuItemClick = (item: SubmenuItem) => {
if (item.path) {
navigate(item.path);
}
setActiveItem(item.id);
item.action?.();
onClose();
};
// const handleSubmenuItemClick = (item: SubmenuItem) => {
// if (item.path) {
// navigate(item.path);
// }
// setActiveItem(item.id);
// item.action?.();
// onClose();
// };
const isSubmenuItemActive = (itemId: string) => {
return activeItem === itemId;
};
// const isSubmenuItemActive = (itemId: string) => {
// return activeItem === itemId;
// };
// Render direct submenu items
const renderSubmenu = () => (
<div className="flex items-center gap-1">
{submenuItems.map((item) => (
<motion.button
key={item.id}
onClick={() => handleSubmenuItemClick(item)}
className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${
isSubmenuItemActive(item.id)
? 'bg-primary text-white shadow-md'
: 'text-gray-700 hover:text-white hover:bg-gray-800'
}`}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{item.label}
// // Render direct submenu items
// const renderSubmenu = () => (
// <div className="flex items-center gap-1">
// {submenuItems.map((item) => (
// <motion.button
// key={item.id}
// onClick={() => handleSubmenuItemClick(item)}
// className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${
// isSubmenuItemActive(item.id)
// ? 'bg-primary text-white shadow-md'
// : 'text-gray-700 hover:text-white hover:bg-gray-800'
// }`}
// whileHover={{ scale: 1.02 }}
// whileTap={{ scale: 0.98 }}
// >
// {item.label}
{!isSubmenuItemActive(item.id) && (
<motion.div
className="absolute inset-0 bg-gray-800 rounded-full -z-10"
initial={{ opacity: 0, scale: 0.9 }}
whileHover={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.2 }}
/>
)}
</motion.button>
))}
</div>
);
// {!isSubmenuItemActive(item.id) && (
// <motion.div
// className="absolute inset-0 bg-gray-800 rounded-full -z-10"
// initial={{ opacity: 0, scale: 0.9 }}
// whileHover={{ opacity: 1, scale: 1 }}
// transition={{ duration: 0.2 }}
// />
// )}
// </motion.button>
// ))}
// </div>
// );
return (
<>
{/* Desktop Submenu */}
<motion.div
className="fixed top-[120px] left-1/2 transform -translate-x-1/2 z-30 hidden lg:block"
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1],
delay: 0.4
}}
>
<motion.div
className="bg-white rounded-full px-2 py-2 shadow-lg border border-gray-200"
initial={{ scale: 0.9 }}
animate={{
scale: isScrolled ? 0.95 : 1,
y: isScrolled ? 2 : 0,
boxShadow: isScrolled
? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
}}
transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
>
{renderSubmenu()}
</motion.div>
</motion.div>
// return (
// <>
// {/* Desktop Submenu */}
// <motion.div
// className="fixed top-[120px] left-1/2 transform -translate-x-1/2 z-30 hidden lg:block"
// initial={{ y: -20, opacity: 0 }}
// animate={{ y: 0, opacity: 1 }}
// transition={{
// duration: 0.6,
// ease: [0.25, 0.1, 0.25, 1],
// delay: 0.4
// }}
// >
// <motion.div
// className="bg-white rounded-full px-2 py-2 shadow-lg border border-gray-200"
// initial={{ scale: 0.9 }}
// animate={{
// scale: isScrolled ? 0.95 : 1,
// y: isScrolled ? 2 : 0,
// boxShadow: isScrolled
// ? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
// : "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
// }}
// transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
// >
// {renderSubmenu()}
// </motion.div>
// </motion.div>
{/* Mobile Submenu */}
<motion.div
className="fixed top-[100px] left-4 right-4 z-30 md:hidden"
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1],
delay: 0.4
}}
>
<motion.div
className="bg-white rounded-2xl px-3 py-3 shadow-lg border border-gray-200"
initial={{ scale: 0.9 }}
animate={{
scale: isScrolled ? 0.95 : 1,
y: isScrolled ? 2 : 0,
boxShadow: isScrolled
? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
}}
transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
>
<div className="flex items-center gap-2 overflow-x-auto scrollbar-hide">
{submenuItems.map((item) => (
<motion.button
key={item.id}
onClick={() => handleSubmenuItemClick(item)}
className={`relative px-3 py-2 text-sm font-medium transition-all duration-300 whitespace-nowrap rounded-xl flex-shrink-0 ${
isSubmenuItemActive(item.id)
? 'bg-primary text-white shadow-md'
: 'text-gray-700 hover:text-white hover:bg-gray-800'
}`}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{item.label}
// {/* Mobile Submenu */}
// <motion.div
// className="fixed top-[100px] left-4 right-4 z-30 md:hidden"
// initial={{ y: -20, opacity: 0 }}
// animate={{ y: 0, opacity: 1 }}
// transition={{
// duration: 0.6,
// ease: [0.25, 0.1, 0.25, 1],
// delay: 0.4
// }}
// >
// <motion.div
// className="bg-white rounded-2xl px-3 py-3 shadow-lg border border-gray-200"
// initial={{ scale: 0.9 }}
// animate={{
// scale: isScrolled ? 0.95 : 1,
// y: isScrolled ? 2 : 0,
// boxShadow: isScrolled
// ? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
// : "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
// }}
// transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
// >
// <div className="flex items-center gap-2 overflow-x-auto scrollbar-hide">
// {submenuItems.map((item) => (
// <motion.button
// key={item.id}
// onClick={() => handleSubmenuItemClick(item)}
// className={`relative px-3 py-2 text-sm font-medium transition-all duration-300 whitespace-nowrap rounded-xl flex-shrink-0 ${
// isSubmenuItemActive(item.id)
// ? 'bg-primary text-white shadow-md'
// : 'text-gray-700 hover:text-white hover:bg-gray-800'
// }`}
// whileHover={{ scale: 1.02 }}
// whileTap={{ scale: 0.98 }}
// >
// {item.label}
{!isSubmenuItemActive(item.id) && (
<motion.div
className="absolute inset-0 bg-gray-800 rounded-xl -z-10"
initial={{ opacity: 0, scale: 0.9 }}
whileHover={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.2 }}
/>
)}
</motion.button>
))}
</div>
</motion.div>
</motion.div>
// {!isSubmenuItemActive(item.id) && (
// <motion.div
// className="absolute inset-0 bg-gray-800 rounded-xl -z-10"
// initial={{ opacity: 0, scale: 0.9 }}
// whileHover={{ opacity: 1, scale: 1 }}
// transition={{ duration: 0.2 }}
// />
// )}
// </motion.button>
// ))}
// </div>
// </motion.div>
// </motion.div>
{/* Medium Screen Submenu */}
<motion.div
className="fixed top-[110px] left-1/2 transform -translate-x-1/2 z-30 hidden md:block lg:hidden"
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1],
delay: 0.4
}}
>
<motion.div
className="bg-white rounded-full px-2 py-2 shadow-lg border border-gray-200"
initial={{ scale: 0.9 }}
animate={{
scale: isScrolled ? 0.95 : 1,
y: isScrolled ? 2 : 0,
boxShadow: isScrolled
? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
}}
transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
>
{renderSubmenu()}
</motion.div>
</motion.div>
</>
);
}
// {/* Medium Screen Submenu */}
// <motion.div
// className="fixed top-[110px] left-1/2 transform -translate-x-1/2 z-30 hidden md:block lg:hidden"
// initial={{ y: -20, opacity: 0 }}
// animate={{ y: 0, opacity: 1 }}
// transition={{
// duration: 0.6,
// ease: [0.25, 0.1, 0.25, 1],
// delay: 0.4
// }}
// >
// <motion.div
// className="bg-white rounded-full px-2 py-2 shadow-lg border border-gray-200"
// initial={{ scale: 0.9 }}
// animate={{
// scale: isScrolled ? 0.95 : 1,
// y: isScrolled ? 2 : 0,
// boxShadow: isScrolled
// ? "0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
// : "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
// }}
// transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
// >
// {renderSubmenu()}
// </motion.div>
// </motion.div>
// </>
// );
// }

View File

@@ -2,9 +2,10 @@ import { motion } from 'motion/react';
import { Wifi, MapPin, Camera, Users, Smartphone, QrCode, Check, ArrowRight } from 'lucide-react';
import { useState } from 'react';
import Navbar from './Navbar';
import { SubNavbar } from './SubNavbar';
// import { SubNavbar } from './SubNavbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface EsimsPageProps {
onBackClick: () => void;
@@ -90,34 +91,15 @@ export function EsimsPage({
return (
<div className="min-h-screen bg-white">
{/* Navbar */}
<Navbar
activeCity="Paris"
onCityChange={() => {}}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
>
{/* SubNavbar for Products */}
<SubNavbar
{/* SubNavbar for Products */}
{/* <SubNavbar
activeTab="esims"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -125,190 +107,190 @@ export function EsimsPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section - eSIM Focus */}
<section className="relative pt-52 pb-20 overflow-hidden" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="font-poppins text-5xl md:text-6xl lg:text-7xl leading-tight mb-6">
<span className="font-normal" style={{ color: '#1F2937' }}>Stay Connected Instantly</span>
<br />
<span className="font-normal" style={{ color: '#1F2937' }}>with Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Complimentary eSIM
</span>
</h1>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal text-gray-600 mb-8 max-w-3xl mx-auto">
Because every unforgettable trip starts with seamless connectivity.
</p>
</motion.div>
</div>
</section>
{/* Benefits Section */}
<section className="py-20 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>With your </span>
<span className="font-bold" style={{ color: '#F95F62' }}>eSIM</span>
<span className="font-light" style={{ color: '#1F2937' }}>, you can:</span>
</h2>
</motion.div>
<div className="grid md:grid-cols-2 gap-8 max-w-5xl mx-auto">
{benefits.map((benefit, index) => {
const IconComponent = benefit.icon;
return (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex gap-4 p-6 rounded-2xl transition-all duration-300 hover:shadow-lg"
style={{ backgroundColor: '#FFF5F5' }}
>
<div className="flex-shrink-0">
<div className="w-12 h-12 rounded-full flex items-center justify-center" style={{ backgroundColor: '#F95F62' }}>
<IconComponent className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-poppins text-lg font-semibold mb-2" style={{ color: '#1F2937' }}>
{benefit.title}
</h3>
<p className="font-poppins text-base" style={{ color: '#4B5563' }}>
{benefit.description}
</p>
</div>
</motion.div>
);
})}
</div>
</div>
</section>
{/* QR Code Process Section */}
<section className="py-20" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>Simple </span>
<span className="font-bold" style={{ color: '#F95F62' }}>3-Step Process</span>
</h2>
<p className="font-poppins text-xl" style={{ color: '#4B5563' }}>
Get connected in seconds
</p>
</motion.div>
<div className="max-w-5xl mx-auto">
<div className="grid md:grid-cols-3 gap-8">
{qrSteps.map((step, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.2 }}
className="relative"
>
{/* Connecting Line */}
{index < qrSteps.length - 1 && (
<div className="hidden md:block absolute top-16 left-1/2 w-full h-0.5 -z-10" style={{ backgroundColor: '#F95F62', opacity: 0.3 }} />
)}
<div className="bg-white rounded-2xl p-8 text-center shadow-lg hover:shadow-xl transition-shadow duration-300">
{/* Step Number */}
<div className="w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-6" style={{ backgroundColor: '#F95F62' }}>
<span className="font-poppins font-bold text-2xl text-white">{step.step}</span>
</div>
{/* Icon */}
<div className="mb-4">
{index === 0 && <QrCode className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
{index === 1 && <Smartphone className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
{index === 2 && <Wifi className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
</div>
{/* Label */}
<h3 className="font-poppins text-xl font-bold mb-3" style={{ color: '#1F2937' }}>
{step.label}
</h3>
{/* Description */}
<p className="font-poppins text-base" style={{ color: '#4B5563' }}>
{step.description}
</p>
</div>
</motion.div>
))}
</div>
</div>
{/* CityCard Logo Trust Anchor */}
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-center mt-16"
>
<div className="inline-flex items-center gap-3 px-6 py-3 bg-white rounded-full shadow-md">
<Check className="w-5 h-5" style={{ color: '#F95F62' }} />
<span className="font-poppins font-semibold text-lg" style={{ color: '#1F2937' }}>
Powered by <span style={{ color: '#F95F62' }}>CityCard</span>
</span>
</div>
</motion.div>
</div>
</section>
{/* Closing Statement Section */}
<section className="py-20 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<p className="font-poppins text-2xl md:text-3xl leading-relaxed mb-8" style={{ color: '#1F2937' }}>
It's one more way <span className="font-bold" style={{ color: '#F95F62' }}>CityCard</span> makes your journey <span className="font-semibold" style={{ color: '#F95F62' }}>smarter</span> and more <span className="font-semibold" style={{ color: '#F95F62' }}>effortless</span>.
</p>
<button
onClick={onCheckoutClick}
className="group px-10 py-5 rounded-full flex items-center gap-3 mx-auto transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-lg text-white"
style={{ backgroundColor: '#F95F62' }}
{/* Hero Section - eSIM Focus */}
<section className="relative pt-52 pb-20 overflow-hidden" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
Start Your Journey Today
<ArrowRight className="w-6 h-6 group-hover:translate-x-1 transition-transform duration-300" />
</button>
</motion.div>
</div>
</section>
<h1 className="font-poppins text-5xl md:text-6xl lg:text-7xl leading-tight mb-6">
<span className="font-normal" style={{ color: '#1F2937' }}>Stay Connected Instantly</span>
<br />
<span className="font-normal" style={{ color: '#1F2937' }}>with Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Complimentary eSIM
</span>
</h1>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal text-gray-600 mb-8 max-w-3xl mx-auto">
Because every unforgettable trip starts with seamless connectivity.
</p>
</motion.div>
</div>
</section>
<Footer />
{/* Benefits Section */}
<section className="py-20 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>With your </span>
<span className="font-bold" style={{ color: '#F95F62' }}>eSIM</span>
<span className="font-light" style={{ color: '#1F2937' }}>, you can:</span>
</h2>
</motion.div>
<div className="grid md:grid-cols-2 gap-8 max-w-5xl mx-auto">
{benefits.map((benefit, index) => {
const IconComponent = benefit.icon;
return (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex gap-4 p-6 rounded-2xl transition-all duration-300 hover:shadow-lg"
style={{ backgroundColor: '#FFF5F5' }}
>
<div className="flex-shrink-0">
<div className="w-12 h-12 rounded-full flex items-center justify-center" style={{ backgroundColor: '#F95F62' }}>
<IconComponent className="w-6 h-6 text-white" />
</div>
</div>
<div>
<h3 className="font-poppins text-lg font-semibold mb-2" style={{ color: '#1F2937' }}>
{benefit.title}
</h3>
<p className="font-poppins text-base" style={{ color: '#4B5563' }}>
{benefit.description}
</p>
</div>
</motion.div>
);
})}
</div>
</div>
</section>
{/* QR Code Process Section */}
<section className="py-20" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>Simple </span>
<span className="font-bold" style={{ color: '#F95F62' }}>3-Step Process</span>
</h2>
<p className="font-poppins text-xl" style={{ color: '#4B5563' }}>
Get connected in seconds
</p>
</motion.div>
<div className="max-w-5xl mx-auto">
<div className="grid md:grid-cols-3 gap-8">
{qrSteps.map((step, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.2 }}
className="relative"
>
{/* Connecting Line */}
{index < qrSteps.length - 1 && (
<div className="hidden md:block absolute top-16 left-1/2 w-full h-0.5 -z-10" style={{ backgroundColor: '#F95F62', opacity: 0.3 }} />
)}
<div className="bg-white rounded-2xl p-8 text-center shadow-lg hover:shadow-xl transition-shadow duration-300">
{/* Step Number */}
<div className="w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-6" style={{ backgroundColor: '#F95F62' }}>
<span className="font-poppins font-bold text-2xl text-white">{step.step}</span>
</div>
{/* Icon */}
<div className="mb-4">
{index === 0 && <QrCode className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
{index === 1 && <Smartphone className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
{index === 2 && <Wifi className="w-12 h-12 mx-auto" style={{ color: '#F95F62' }} />}
</div>
{/* Label */}
<h3 className="font-poppins text-xl font-bold mb-3" style={{ color: '#1F2937' }}>
{step.label}
</h3>
{/* Description */}
<p className="font-poppins text-base" style={{ color: '#4B5563' }}>
{step.description}
</p>
</div>
</motion.div>
))}
</div>
</div>
{/* CityCard Logo Trust Anchor */}
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-center mt-16"
>
<div className="inline-flex items-center gap-3 px-6 py-3 bg-white rounded-full shadow-md">
<Check className="w-5 h-5" style={{ color: '#F95F62' }} />
<span className="font-poppins font-semibold text-lg" style={{ color: '#1F2937' }}>
Powered by <span style={{ color: '#F95F62' }}>CityCard</span>
</span>
</div>
</motion.div>
</div>
</section>
{/* Closing Statement Section */}
<section className="py-20 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<p className="font-poppins text-2xl md:text-3xl leading-relaxed mb-8" style={{ color: '#1F2937' }}>
It's one more way <span className="font-bold" style={{ color: '#F95F62' }}>CityCard</span> makes your journey <span className="font-semibold" style={{ color: '#F95F62' }}>smarter</span> and more <span className="font-semibold" style={{ color: '#F95F62' }}>effortless</span>.
</p>
<button
onClick={onCheckoutClick}
className="group px-10 py-5 rounded-full flex items-center gap-3 mx-auto transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-lg text-white"
style={{ backgroundColor: '#F95F62' }}
>
Start Your Journey Today
<ArrowRight className="w-6 h-6 group-hover:translate-x-1 transition-transform duration-300" />
</button>
</motion.div>
</div>
</section>
</Layout>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import { Input } from './ui/input';
import { Badge } from './ui/badge';
import { Card, CardContent } from './ui/card';
import Navbar from './Navbar';
import { CitySubmenu } from './CitySubmenu';
// import { CitySubmenu } from './CitySubmenu';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { WhyChooseCityCards } from './WhyChooseCityCards';
@@ -214,7 +214,7 @@ export function FAQPage({
user={user}
/>
<CitySubmenu
{/* <CitySubmenu
currentPage={currentPage}
onClose={() => {}}
onHomeClick={onHomeClick}
@@ -223,7 +223,7 @@ export function FAQPage({
onPassesClick={onPassesClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
/>
/> */}
<div className="container mx-auto px-4 pt-52 pb-12">
{/* Page Header */}

View File

@@ -2,11 +2,12 @@ import { motion } from 'motion/react';
import { BadgePercent, Clock, Crown, Check, ArrowRight } from 'lucide-react';
import { Button } from './ui/button';
import Navbar from './Navbar';
import { SubNavbar } from './SubNavbar';
// import { SubNavbar } from './SubNavbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import cityCardsLogo from '../assets/cityLogo.png';
import marriottHotelImage from '../assets/marriott-hotel.png';
import { Layout } from '../Layout';
interface HotelDiscountsPageProps {
onBackClick: () => void;
@@ -80,35 +81,15 @@ export function HotelDiscountsPage({
return (
<div className="min-h-screen bg-white">
{/* Navbar */}
<Navbar
activeCity="Paris"
onCityChange={() => {}}
onHomeClick={onHomeClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
>
{/* Sub Navbar */}
<SubNavbar
{/* Sub Navbar */}
{/* <SubNavbar
activePage="hotel-discounts"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -116,184 +97,184 @@ export function HotelDiscountsPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section */}
<section className="relative pt-64 pb-20 overflow-hidden">
{/* Background Image with Overlay */}
<div className="absolute inset-0 z-0">
<ImageWithFallback
src={marriottHotelImage}
alt="Marriott Hotel"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/40 via-black/40 to-background" />
</div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center"
>
{/* CityCard Logo */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex justify-center mb-8"
>
</motion.div>
{/* Heading */}
<h1 className="font-poppins text-4xl md:text-5xl lg:text-6xl leading-tight mb-6 font-semibold text-white max-w-4xl mx-auto">
Enjoy 20% Off Iconic Marriott Hotels Exclusively with CityCard
</h1>
{/* Tagline */}
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal text-white/90 max-w-2xl mx-auto">
Make every stay as unforgettable as the city you're exploring.
</p>
</motion.div>
</div>
</section>
{/* Section 1: Partnership Introduction - White Background */}
<section className="py-20 md:py-28 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<p className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-relaxed font-normal mb-8" style={{ color: '#1F2937' }}>
Your CityCard unlocks more than just attractions — it also opens doors to exceptional stays.
</p>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal" style={{ color: '#4B5563' }}>
Thanks to our exclusive partnership with <span className="font-semibold" style={{ color: '#F95F62' }}>Marriott Hotels</span>, CityCard holders enjoy <span className="font-semibold" style={{ color: '#F95F62' }}>20% off best available rates</span> across a curated selection of properties in the city.
</p>
</motion.div>
</div>
</section>
{/* Section 2: Hotel Variety - Pink Background */}
<section className="py-20 md:py-28" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-6">
<span className="font-light" style={{ color: '#1F2937' }}>Choose from a </span>
<span className="font-bold" style={{ color: '#F95F62' }}>Wide Variety</span>
</h2>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal" style={{ color: '#4B5563' }}>
Choose from a wide variety of Marriott hotels — from elegant urban hideaways and premium city-centre locations to luxurious five-star experiences — all designed to make your trip <span className="font-semibold" style={{ color: '#F95F62' }}>effortless</span>, <span className="font-semibold" style={{ color: '#F95F62' }}>comfortable</span>, and <span className="font-semibold" style={{ color: '#F95F62' }}>memorable</span>.
</p>
</motion.div>
</div>
</section>
{/* Section 3: Benefits - White Background with Pink Cards */}
<section className="py-20 md:py-28 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
{/* Heading */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>Simply use your CityCard</span>
<br />
<span className="font-bold" style={{ color: '#F95F62' }}>booking link to:</span>
</h2>
</motion.div>
{/* Benefits Grid */}
<div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto mb-16">
{benefits.map((benefit, index) => {
const IconComponent = benefit.icon;
return (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex flex-col items-center text-center p-6 rounded-2xl transition-all duration-300 hover:shadow-lg"
style={{ backgroundColor: '#FFF5F5' }}
>
<div className="w-16 h-16 rounded-full flex items-center justify-center mb-6" style={{ backgroundColor: '#F95F62' }}>
<IconComponent className="w-8 h-8 text-white" />
</div>
<h3 className="font-poppins text-lg font-semibold mb-3" style={{ color: '#1F2937' }}>
{benefit.title}
</h3>
<p className="font-poppins text-base font-normal" style={{ color: '#4B5563' }}>
{benefit.description}
</p>
</motion.div>
);
})}
{/* Hero Section */}
<section className="relative pt-64 pb-20 overflow-hidden">
{/* Background Image with Overlay */}
<div className="absolute inset-0 z-0">
<ImageWithFallback
src={marriottHotelImage}
alt="Marriott Hotel"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/40 via-black/40 to-background" />
</div>
{/* Closing Statement */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-center"
>
<div className="max-w-4xl mx-auto mb-8">
<p className="font-poppins text-2xl md:text-3xl leading-relaxed font-normal" style={{ color: '#1F2937' }}>
It's just one more way <span className="font-bold" style={{ color: '#F95F62' }}>CityCard</span> makes exploring <span className="font-semibold" style={{ color: '#F95F62' }}>smarter</span>, <span className="font-semibold" style={{ color: '#F95F62' }}>simpler</span>, and <span className="font-semibold" style={{ color: '#F95F62' }}>more rewarding</span>.
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center"
>
{/* CityCard Logo */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex justify-center mb-8"
>
</motion.div>
{/* Heading */}
<h1 className="font-poppins text-4xl md:text-5xl lg:text-6xl leading-tight mb-6 font-semibold text-white max-w-4xl mx-auto">
Enjoy 20% Off Iconic Marriott Hotels Exclusively with CityCard
</h1>
{/* Tagline */}
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal text-white/90 max-w-2xl mx-auto">
Make every stay as unforgettable as the city you're exploring.
</p>
</motion.div>
</div>
</section>
{/* Section 1: Partnership Introduction - White Background */}
<section className="py-20 md:py-28 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<p className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-relaxed font-normal mb-8" style={{ color: '#1F2937' }}>
Your CityCard unlocks more than just attractions — it also opens doors to exceptional stays.
</p>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal" style={{ color: '#4B5563' }}>
Thanks to our exclusive partnership with <span className="font-semibold" style={{ color: '#F95F62' }}>Marriott Hotels</span>, CityCard holders enjoy <span className="font-semibold" style={{ color: '#F95F62' }}>20% off best available rates</span> across a curated selection of properties in the city.
</p>
</motion.div>
</div>
</section>
{/* Section 2: Hotel Variety - Pink Background */}
<section className="py-20 md:py-28" style={{ backgroundColor: '#FFF5F5' }}>
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-6">
<span className="font-light" style={{ color: '#1F2937' }}>Choose from a </span>
<span className="font-bold" style={{ color: '#F95F62' }}>Wide Variety</span>
</h2>
<p className="font-poppins text-xl md:text-2xl leading-relaxed font-normal" style={{ color: '#4B5563' }}>
Choose from a wide variety of Marriott hotels — from elegant urban hideaways and premium city-centre locations to luxurious five-star experiences — all designed to make your trip <span className="font-semibold" style={{ color: '#F95F62' }}>effortless</span>, <span className="font-semibold" style={{ color: '#F95F62' }}>comfortable</span>, and <span className="font-semibold" style={{ color: '#F95F62' }}>memorable</span>.
</p>
</motion.div>
</div>
</section>
{/* Section 3: Benefits - White Background with Pink Cards */}
<section className="py-20 md:py-28 bg-white">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
{/* Heading */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light" style={{ color: '#1F2937' }}>Simply use your CityCard</span>
<br />
<span className="font-bold" style={{ color: '#F95F62' }}>booking link to:</span>
</h2>
</motion.div>
{/* Benefits Grid */}
<div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto mb-16">
{benefits.map((benefit, index) => {
const IconComponent = benefit.icon;
return (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex flex-col items-center text-center p-6 rounded-2xl transition-all duration-300 hover:shadow-lg"
style={{ backgroundColor: '#FFF5F5' }}
>
<div className="w-16 h-16 rounded-full flex items-center justify-center mb-6" style={{ backgroundColor: '#F95F62' }}>
<IconComponent className="w-8 h-8 text-white" />
</div>
<h3 className="font-poppins text-lg font-semibold mb-3" style={{ color: '#1F2937' }}>
{benefit.title}
</h3>
<p className="font-poppins text-base font-normal" style={{ color: '#4B5563' }}>
{benefit.description}
</p>
</motion.div>
);
})}
</div>
<button
onClick={onCheckoutClick}
className="group px-10 py-5 rounded-full flex items-center gap-3 mx-auto transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-lg text-white"
style={{ backgroundColor: '#F95F62' }}
{/* Closing Statement */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-center"
>
Get Your CityCard Today
<ArrowRight className="w-6 h-6 group-hover:translate-x-1 transition-transform duration-300" />
</button>
</motion.div>
</div>
</section>
<div className="max-w-4xl mx-auto mb-8">
<p className="font-poppins text-2xl md:text-3xl leading-relaxed font-normal" style={{ color: '#1F2937' }}>
It's just one more way <span className="font-bold" style={{ color: '#F95F62' }}>CityCard</span> makes exploring <span className="font-semibold" style={{ color: '#F95F62' }}>smarter</span>, <span className="font-semibold" style={{ color: '#F95F62' }}>simpler</span>, and <span className="font-semibold" style={{ color: '#F95F62' }}>more rewarding</span>.
</p>
</div>
{/* Trust Anchor Section with Logo */}
<section className="py-16 bg-white border-t border-gray-100">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="flex flex-col items-center text-center"
>
<img
src={cityCardsLogo}
alt="CityCard"
className="h-12 md:h-14 w-auto object-contain mb-6"
/>
<p className="font-poppins text-lg md:text-xl font-normal max-w-2xl" style={{ color: '#4B5563' }}>
Your gateway to unforgettable city experiences and exceptional hotel stays
</p>
</motion.div>
</div>
</section>
<button
onClick={onCheckoutClick}
className="group px-10 py-5 rounded-full flex items-center gap-3 mx-auto transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-lg text-white"
style={{ backgroundColor: '#F95F62' }}
>
Get Your CityCard Today
<ArrowRight className="w-6 h-6 group-hover:translate-x-1 transition-transform duration-300" />
</button>
</motion.div>
</div>
</section>
{/* Trust Anchor Section with Logo */}
<section className="py-16 bg-white border-t border-gray-100">
<div className="container mx-auto px-6 sm:px-8 md:px-12 lg:px-16 xl:px-20">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="flex flex-col items-center text-center"
>
<img
src={cityCardsLogo}
alt="CityCard"
className="h-12 md:h-14 w-auto object-contain mb-6"
/>
<p className="font-poppins text-lg md:text-xl font-normal max-w-2xl" style={{ color: '#4B5563' }}>
Your gateway to unforgettable city experiences and exceptional hotel stays
</p>
</motion.div>
</div>
</section>
</Layout>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import { CitySubmenu } from './CitySubmenu';
// import { CitySubmenu } from './CitySubmenu';
import { AttractionHassleFreeSection } from './AttractionHassleFreeSection';
import { MobileAppSection } from './MobileAppSection';
import { WhyChooseCityCards } from './WhyChooseCityCards';
@@ -189,7 +189,7 @@ export function HowItWorksPage({
user={user}
/>
<CitySubmenu
{/* <CitySubmenu
currentPage={currentPage}
onClose={() => {}}
onHomeClick={onHomeClick}
@@ -198,7 +198,7 @@ export function HowItWorksPage({
onPassesClick={onPassesClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
/>
/> */}
<div className="container mx-auto px-4 pt-52 pb-12 relative z-10">
{/* Page Header */}

View File

@@ -5,12 +5,13 @@ import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import SubNavbar from './SubNavbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { HowItWorks } from './HowItWorks';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface User {
email: string;
@@ -75,34 +76,15 @@ export function MagicItineraryPage({
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Navbar
activeCity=""
onCityChange={() => {}}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onHomeClick={onHomeClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage="magic-itinerary"
isUserSignedIn={!!user}
user={user}
/>
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Sub Navbar */}
<SubNavbar
{/* <SubNavbar
activeTab="magic-itinerary"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -110,7 +92,7 @@ export function MagicItineraryPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
@@ -379,20 +361,8 @@ export function MagicItineraryPage({
{/* Customer Reviews */}
<EnhancedTestimonials />
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
</Layout>
</div>
);
}

View File

@@ -379,22 +379,6 @@ export function MelbournePage({
{/* Melbourne FAQ Section */}
<MelbourneFAQ />
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onHomeClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onContactUsClick={onContactUsClick}
currentPage="melbourne"
/>
</div>
</Layout>
);

View File

@@ -56,16 +56,50 @@ export default function Navbar({
const [activeLanguageDropdown, setActiveLanguageDropdown] = useState(false);
const [activeCartDropdown, setActiveCartDropdown] = useState(false);
const [activeUserDropdown, setActiveUserDropdown] = useState(false);
const [activeExploreCardDropdown, setActiveExploreCardDropdown] = useState(false);
const [activeCityDropdown, setActiveCityDropdown] = useState(false);
const languageRef = useRef<HTMLDivElement>(null);
const cartRef = useRef<HTMLDivElement>(null);
const userRef = useRef<HTMLDivElement>(null);
const exploreCardRef = useRef<HTMLDivElement>(null);
const cityRef = useRef<HTMLDivElement>(null);
const location = useLocation();
const navigate = useNavigate();
// Available cities
const cities = [
{ id: 'melbourne', label: 'Melbourne' },
{ id: 'sydney', label: 'Sydney' },
{ id: 'brisbane', label: 'Brisbane' },
{ id: 'perth', label: 'Perth' }
];
// Check if we're on landing page
const isLandingPage = location.pathname === '/';
// Landing page navigation items
const landingPageNavigationItems = [
{ label: 'CityCards', path: '/citycards' },
{ label: 'Postcards', path: '/postcards' },
{ label: 'eSIMs', path: '/esims' },
{ label: 'Hotel Discount', path: '/hotel-discounts' },
{ label: 'Offers', path: '/offers' }
];
// Melbourne-specific navigation items (without /melbourne in paths)
const melbourneNavigationItems = [
{ label: 'Attractions', path: '/attractions' },
{ label: 'Magic Itinerary', path: '/magic-itinerary' },
{ label: 'Super Savings', path: '/comming-soon' },
{ label: 'How It Works', path: '/how-it-works' },
{ label: 'Your Card', path: '/passes' }
];
// Get current navigation items based on page and city
const navigationItems = isLandingPage
? landingPageNavigationItems
: (activeCity.toLowerCase() === 'melbourne' ? melbourneNavigationItems : landingPageNavigationItems);
// Languages available
const languages: DropdownItem[] = [
{ id: 'en', label: 'English', icon: <span className="text-base">🇺🇸</span> },
@@ -74,61 +108,26 @@ export default function Navbar({
{ id: 'it', label: 'Italiano', icon: <span className="text-base">🇮🇹</span> },
];
// Explore Card dropdown items with router navigation
const exploreCardItems: DropdownItem[] = [
{
id: 'citycards',
label: 'CityCards',
path: '/citycards'
},
{
id: 'magic-itinerary',
label: 'Magic Itinerary',
path: '/magic-itinerary'
},
{
id: 'postcards',
label: 'PostCards',
path: '/postcards'
},
{
id: 'offers',
label: 'Offers',
path: '/offers'
},
{
id: 'esims',
label: 'eSIMs',
path: '/esims'
},
{
id: 'hotel-discounts',
label: 'Hotel Discounts',
path: '/hotel-discounts'
}
];
// Mock cart items
const cartItems: CartItem[] = [
{ id: '1', name: 'Sydney 2-Day Pass', price: '$89', quantity: 1 },
{ id: '2', name: 'Melbourne Premium Pass', price: '$129', quantity: 1 },
];
// Section IDs for navigation
const sectionIds = [
'hero-section',
'why-choose-section',
'variety-adventures-section',
'how-it-works-section',
'magic-itinerary-section',
'book-attraction-section',
'custom-postcards-section',
'upcoming-cities-section',
'trust-section',
'mobile-app-section'
];
const scrollToSection = (index: number) => {
const sectionIds = [
'hero-section',
'why-choose-section',
'variety-adventures-section',
'how-it-works-section',
'magic-itinerary-section',
'book-attraction-section',
'custom-postcards-section',
'upcoming-cities-section',
'trust-section',
'mobile-app-section'
];
const sectionId = sectionIds[index];
const element = document.getElementById(sectionId);
if (element) {
@@ -153,27 +152,27 @@ export default function Navbar({
}, []);
// Close dropdowns when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
setTimeout(() => {
if (languageRef.current && !languageRef.current.contains(event.target as Node)) {
setActiveLanguageDropdown(false);
}
if (cartRef.current && !cartRef.current.contains(event.target as Node)) {
setActiveCartDropdown(false);
}
if (userRef.current && !userRef.current.contains(event.target as Node)) {
setActiveUserDropdown(false);
}
if (exploreCardRef.current && !exploreCardRef.current.contains(event.target as Node)) {
setActiveExploreCardDropdown(false);
}
}, 10);
};
// useEffect(() => {
// const handleClickOutside = (event: MouseEvent) => {
// setTimeout(() => {
// if (languageRef.current && !languageRef.current.contains(event.target as Node)) {
// setActiveLanguageDropdown(false);
// }
// if (cartRef.current && !cartRef.current.contains(event.target as Node)) {
// setActiveCartDropdown(false);
// }
// if (userRef.current && !userRef.current.contains(event.target as Node)) {
// setActiveUserDropdown(false);
// }
// if (cityRef.current && !cityRef.current.contains(event.target as Node)) {
// setActiveCityDropdown(false);
// }
// }, 10);
// };
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// document.addEventListener('mousedown', handleClickOutside);
// return () => document.removeEventListener('mousedown', handleClickOutside);
// }, []);
const handleNavClick = (path: string) => {
navigate(path);
@@ -184,10 +183,21 @@ export default function Navbar({
return location.pathname === path;
};
// Handle Explore Card dropdown item click
const handleExploreCardItemClick = (path: string) => {
navigate(path);
setActiveExploreCardDropdown(false);
// Handle city change
// Handle city change
const handleCityChange = (city: string) => {
console.log('City selected:', city); // Debug log
onCityChange(city);
setActiveCityDropdown(false);
// Navigate based on city selection
if (city.toLowerCase() === 'melbourne') {
console.log('Navigating to Melbourne'); // Debug log
navigate('/melbourne');
} else {
console.log('Navigating to coming soon'); // Debug log
navigate('/comming-soon');
}
};
// Calculate cart total
@@ -196,14 +206,7 @@ export default function Navbar({
return total + (price * item.quantity);
}, 0);
// Navigation items with router paths
const navigationItems = [
{ label: 'How It Works', path: '/how-it-works' },
{ label: 'Your Card', path: '/passes' },
{ label: 'FAQ', path: '/faq' }
];
// Dropdown component with proper ref forwarding and glassmorphism
// Simple Dropdown component without blinking
const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(({
isOpen,
onToggle,
@@ -212,86 +215,70 @@ export default function Navbar({
title,
className = ""
}, ref) => (
<div ref={ref} className={`relative ${className}`} style={{ height: 'auto', minHeight: 'auto' }}>
<motion.button
onClick={onToggle}
className="relative"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
<div ref={ref} className={`relative ${className}`}>
<div
onClick={(e) => {
e.stopPropagation();
onToggle();
}}
className="cursor-pointer"
>
{trigger}
</motion.button>
</div>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
className="absolute top-full right-0 mt-2 bg-white/95 backdrop-blur-xl rounded-2xl shadow-xl border border-white/20 min-w-[280px] max-w-[320px] overflow-hidden z-50"
style={{
position: 'absolute',
top: '100%',
right: '0',
marginTop: '0.5rem'
}}
>
{title && (
<div className="px-5 py-4 border-b border-gray-100/50">
<h3 className="font-merchant font-semibold text-gray-900 text-base">{title}</h3>
</div>
)}
<>
{/* Backdrop to capture outside clicks */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-40"
onClick={(e) => {
e.stopPropagation();
onToggle();
}}
/>
<div className="py-3 px-2">
{items.map((item, index) => (
<motion.button
key={item.id}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
{/* Dropdown content */}
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
className="absolute top-full left-0 mt-2 bg-white/95 backdrop-blur-xl rounded-2xl shadow-xl border border-white/20 min-w-[200px] overflow-hidden z-50"
onClick={(e) => e.stopPropagation()} // Prevent clicks inside from closing
>
{title && (
<div className="px-5 py-4 border-b border-gray-100/50">
<h3 className="font-merchant font-semibold text-gray-900 text-base">{title}</h3>
</div>
)}
// Handle router navigation for items with paths
if (item.path) {
navigate(item.path);
}
<div className="py-2">
{items.map((item, index) => (
<div
key={item.id}
onClick={(e) => {
e.stopPropagation();
console.log('City dropdown item clicked:', item.label);
// Handle action if provided
if (item.action && item.id !== 'total') {
item.action();
}
// Only close dropdown for actionable items
if (item.id === 'checkout' || item.path || (item.id !== 'total' && !item.action)) {
setTimeout(() => onToggle(), 100);
}
}}
className={`w-full flex items-center justify-between text-left transition-colors duration-200 ${item.id === 'checkout'
? 'bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-medium rounded-lg px-4 py-2.5 mb-1 mt-2'
: item.id === 'total'
? 'cursor-default font-semibold border-t border-gray-100/50 pt-4 px-3 py-2'
: 'hover:bg-gray-50/80 px-3 py-2.5 rounded-md'
}`}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 }}
disabled={item.id === 'total'}
>
<div className="flex items-center space-x-3">
{item.icon}
<span className={`text-sm font-medium ${item.id === 'checkout' ? 'text-white' :
item.id === 'total' ? 'text-gray-900' : 'text-gray-700'
}`}>{item.label}</span>
if (item.action) {
item.action();
}
// Don't call onToggle here - let the backdrop handle closing
}}
className="px-4 py-2.5 hover:bg-gray-50/80 cursor-pointer transition-colors duration-200"
>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-gray-700">{item.label}</span>
</div>
</div>
{item.badge && (
<span className="bg-primary text-primary-foreground text-xs px-2 py-1 rounded-full">
{item.badge}
</span>
)}
</motion.button>
))}
</div>
</motion.div>
))}
</div>
</motion.div>
</>
)}
</AnimatePresence>
</div>
@@ -304,14 +291,14 @@ export default function Navbar({
<>
{/* Desktop Navbar - Enhanced Glassmorphism */}
<motion.nav
className="fixed top-6 left-0 right-0 z-50 hidden lg:block"
className="fixed top-0 left-0 right-0 z-50 hidden lg:block"
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.6, ease: [0.25, 0.1, 0.25, 1], delay: 0.2 }}
>
<div className="container mx-auto px-4">
<div className="">
<motion.div
className={`w-full transition-all duration-500 ease-out rounded-full px-8 py-4 bg-white backdrop-blur-[20px] border border-white/20 ${isScrolled
className={`w-full transition-all duration-500 ease-out px-8 py-4 bg-white backdrop-blur-[20px] border border-white/20 ${isScrolled
? 'shadow-[0_10px_15px_-3px_rgba(0,0,0,0.08),0_4px_6px_-2px_rgba(0,0,0,0.05)]'
: 'shadow-lg shadow-black/5'
}`}
@@ -340,20 +327,12 @@ export default function Navbar({
</motion.div>
<div className="absolute left-1/2 -translate-x-1/2 flex items-center gap-5">
{/* Explore Card - First Item - No Dropdown */}
<Link
to="/citycards"
className="flex items-center space-x-1 text-gray-700 hover:text-gray-900 text-base font-medium transition-colors duration-200 cursor-pointer rounded-lg hover:bg-gray-50/50 px-2 py-1"
>
<span>Explore Card</span>
</Link>
{/* Other Navigation Items */}
{/* Navigation Items based on page type */}
{navigationItems.map((item) => (
<Link
key={item.path}
to={item.path}
className={`relative px-0 py-2 text-base font-medium transition-all duration-200 whitespace-nowrap group capitalize ${isNavItemActive(item.path)
className={`relative px-0 py-2 text-base font-medium transition-all duration-200 whitespace-nowrap group ${isNavItemActive(item.path)
? 'text-primary'
: 'text-gray-700 hover:text-gray-900'
}`}
@@ -384,6 +363,40 @@ export default function Navbar({
/>
</Link>
))}
{/* City Dropdown */}
{/* City Dropdown */}
<Dropdown
ref={cityRef}
isOpen={activeCityDropdown}
onToggle={() => {
console.log('City dropdown toggled');
// Add a small delay to prevent rapid state changes
setTimeout(() => {
setActiveCityDropdown(!activeCityDropdown);
}, 50);
}}
items={cities.map(city => ({
id: 'city-change',
label: city.label,
action: () => {
console.log('City action called:', city.id);
handleCityChange(city.id);
}
}))}
title="Select City"
trigger={
<div className="flex items-center space-x-1.5 text-gray-700 hover:text-gray-900 text-base font-medium transition-colors duration-200 cursor-pointer rounded-lg hover:bg-gray-50/50 px-2 py-1">
<span>
{isLandingPage
? 'Select City'
: activeCity.charAt(0).toUpperCase() + activeCity.slice(1)
}
</span>
<ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${activeCityDropdown ? 'rotate-180' : ''}`} />
</div>
}
/>
</div>
{/* Right Section */}
@@ -512,6 +525,30 @@ export default function Navbar({
{/* Mobile Actions */}
<div className="flex items-center space-x-2">
{/* Mobile City Selector */}
<Dropdown
ref={cityRef}
isOpen={activeCityDropdown}
onToggle={() => setActiveCityDropdown(!activeCityDropdown)}
items={cities.map(city => ({
id: 'city-change',
label: city.label,
action: () => handleCityChange(city.id)
}))}
title="Select City"
trigger={
<div className="flex items-center space-x-1 text-gray-700 hover:text-gray-900 text-sm font-medium transition-colors duration-200 cursor-pointer rounded-lg hover:bg-gray-50/50 px-2 py-1">
<span>
{isLandingPage
? 'Select City'
: activeCity.charAt(0).toUpperCase() + activeCity.slice(1)
}
</span>
<ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${activeCityDropdown ? 'rotate-180' : ''}`} />
</div>
}
/>
{/* Mobile Cart */}
<motion.button
className="relative text-gray-700 hover:text-gray-900 p-2 transition-colors duration-200 rounded-lg hover:bg-gray-50/50"
@@ -572,15 +609,10 @@ export default function Navbar({
</div>
<div className="p-6 space-y-6">
{/* Mobile Navigation Links */}
{/* Navigation Links based on page type */}
<div className="space-y-4">
{[
{ label: 'How It Works', path: '/how-it-works' },
{ label: 'Cities', path: '/melbourne' },
{ label: 'Attractions', path: '/attractions' },
{ label: 'Your Card', path: '/passes' },
{ label: 'Deals', path: '/offers' }
].map((item) => (
<h3 className="text-lg font-semibold text-gray-900 px-4">Navigation</h3>
{navigationItems.map((item) => (
<motion.button
key={item.path}
onClick={() => handleNavClick(item.path)}
@@ -596,22 +628,21 @@ export default function Navbar({
))}
</div>
{/* Mobile Explore Card Section */}
{/* City Selection in Mobile Menu */}
<div className="space-y-4">
<h3 className="text-lg font-semibold text-gray-900 px-4">Explore Card</h3>
{exploreCardItems.map((item) => (
<h3 className="text-lg font-semibold text-gray-900 px-4">Select City</h3>
{cities.map((city) => (
<motion.button
key={item.id}
onClick={() => {
if (item.path) {
handleNavClick(item.path);
}
}}
className="w-full flex items-center justify-between py-3 px-4 rounded-lg text-left transition-colors duration-200 text-gray-700 hover:bg-gray-100/70 hover:text-gray-900"
key={city.id}
onClick={() => handleCityChange(city.id)}
className={`w-full flex items-center justify-between py-3 px-4 rounded-lg text-left transition-colors duration-200 ${activeCity === city.id
? 'bg-primary/10 text-primary font-medium'
: 'text-gray-700 hover:bg-gray-100/70 hover:text-gray-900'
}`}
whileHover={{ x: 4 }}
whileTap={{ scale: 0.98 }}
>
<span className="text-base">{item.label}</span>
<span className="text-base">{city.label}</span>
</motion.button>
))}
</div>

View File

@@ -8,13 +8,14 @@ import { Badge } from './ui/badge';
import { Separator } from './ui/separator';
import { Checkbox } from './ui/checkbox';
import Navbar from './Navbar';
import SubNavbar from './SubNavbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { TrustSection } from './TrustSection';
import { MobileAppSection } from './MobileAppSection';
import { ReviewsSection } from './ReviewsSection';
import { TrustedCompanies } from './TrustedCompanies';
import { Layout } from '../Layout';
interface OffersPageProps {
onBackClick: () => void;
@@ -280,35 +281,15 @@ export function OffersPage({
// Not logged in - show marketing/landing page
return (
<div className="min-h-screen bg-background">
<Navbar
activeCity="Paris"
onCityChange={() => {}}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Sub Navbar */}
<SubNavbar
{/* <SubNavbar
activeTab="offers"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -316,7 +297,7 @@ export function OffersPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
@@ -592,24 +573,7 @@ export function OffersPage({
<MobileAppSection />
</section>
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
/>
</Layout>
</div>
);
}

View File

@@ -5,13 +5,14 @@ import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import SubNavbar from './SubNavbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { CustomPostcards } from './CustomPostcards';
import { HowItWorks } from './HowItWorks';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface User {
email: string;
@@ -72,34 +73,15 @@ export function PostCardsPage({
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Navbar
activeCity=""
onCityChange={() => {}}
onSignInClick={onSignInClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
currentPage="postcards"
isUserSignedIn={!!user}
user={user}
/>
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Sub Navbar */}
<SubNavbar
{/* <SubNavbar
activeTab="postcards"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
@@ -107,7 +89,7 @@ export function PostCardsPage({
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
@@ -249,20 +231,8 @@ export function PostCardsPage({
{/* Customer Reviews */}
<EnhancedTestimonials />
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
</Layout>
</div>
);
}

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { motion, useScroll, useTransform } from 'motion/react';
import { ArrowLeft } from 'lucide-react';
import Navbar from './Navbar';
import { CitySubmenu } from './CitySubmenu';
// import { CitySubmenu } from './CitySubmenu';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { WhyChooseCityCards } from './WhyChooseCityCards';
@@ -95,7 +95,7 @@ export function PrivacyPolicyPage({
user={user}
/>
<CitySubmenu
{/* <CitySubmenu
currentPage={currentPage}
onClose={() => {}}
onHomeClick={onHomeClick}
@@ -104,7 +104,7 @@ export function PrivacyPolicyPage({
onPassesClick={onPassesClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
/>
/> */}
<div className="container mx-auto px-4 pt-52 pb-12 relative z-10">
{/* Page Header */}

View File

@@ -1,107 +1,107 @@
import { motion } from 'motion/react';
import { useNavigate } from 'react-router-dom';
// import { motion } from 'motion/react';
// import { useNavigate } from 'react-router-dom';
interface SubNavbarProps {
activePage?: 'citycards' | 'magic-itinerary' | 'postcards' | 'offers' | 'esims' | 'hotel-discounts';
activeTab?: 'citycards' | 'magic-itinerary' | 'postcards' | 'offers' | 'esims' | 'hotel-discounts';
}
// interface SubNavbarProps {
// activePage?: 'citycards' | 'magic-itinerary' | 'postcards' | 'offers' | 'esims' | 'hotel-discounts';
// activeTab?: 'citycards' | 'magic-itinerary' | 'postcards' | 'offers' | 'esims' | 'hotel-discounts';
// }
export function SubNavbar({
activeTab,
activePage,
}: SubNavbarProps) {
const navigate = useNavigate();
const activeProduct = activePage || activeTab;
// export function SubNavbar({
// activeTab,
// activePage,
// }: SubNavbarProps) {
// const navigate = useNavigate();
// const activeProduct = activePage || activeTab;
const products = [
{
id: 'citycards',
label: 'CityCards',
path: '/citycards'
},
{
id: 'magic-itinerary',
label: 'Magic Itinerary',
path: '/magic-itinerary'
},
{
id: 'postcards',
label: 'PostCards',
path: '/postcards'
},
{
id: 'offers',
label: 'Offers',
path: '/offers'
},
{
id: 'esims',
label: 'eSIMs',
path: '/esims'
},
{
id: 'hotel-discounts',
label: 'Hotel Discounts',
path: '/hotel-discounts'
}
];
// const products = [
// {
// id: 'citycards',
// label: 'CityCards',
// path: '/citycards'
// },
// {
// id: 'magic-itinerary',
// label: 'Magic Itinerary',
// path: '/magic-itinerary'
// },
// {
// id: 'postcards',
// label: 'PostCards',
// path: '/postcards'
// },
// {
// id: 'offers',
// label: 'Offers',
// path: '/offers'
// },
// {
// id: 'esims',
// label: 'eSIMs',
// path: '/esims'
// },
// {
// id: 'hotel-discounts',
// label: 'Hotel Discounts',
// path: '/hotel-discounts'
// }
// ];
const handleProductClick = (path: string) => {
navigate(path);
};
// const handleProductClick = (path: string) => {
// navigate(path);
// };
return (
<motion.div
className="fixed top-[120px] left-1/2 transform -translate-x-1/2 z-30 hidden lg:block"
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1],
delay: 0.4
}}
>
<motion.div
className="bg-white rounded-full px-2 py-2 shadow-lg border border-gray-200"
initial={{ scale: 0.9 }}
animate={{
scale: 1,
y: 0,
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
}}
transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
>
<div className="flex items-center gap-1">
{products.map((product) => (
<motion.button
key={product.id}
onClick={() => handleProductClick(product.path)}
className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${
activeProduct === product.id
? 'bg-primary text-white shadow-md'
: 'text-gray-700 hover:text-white hover:bg-gray-800'
}`}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{product.label}
// return (
// <motion.div
// className="fixed w-full z-30 hidden lg:block"
// initial={{ y: -20, opacity: 0 }}
// animate={{ y: 0, opacity: 1 }}
// transition={{
// duration: 0.6,
// ease: [0.25, 0.1, 0.25, 1],
// delay: 0.4
// }}
// style={{ top: '80px' }}
// >
// <motion.div
// className="bg-white px-2 py-2 shadow-lg border border-gray-200"
// initial={{ scale: 0.9 }}
// animate={{
// scale: 1,
// y: 0,
// boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)"
// }}
// transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
// >
// <div className="flex items-center gap-1 justify-center">
// {products.map((product) => (
// <motion.button
// key={product.id}
// onClick={() => handleProductClick(product.path)}
// className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${activeProduct === product.id
// ? 'bg-primary text-white shadow-md'
// : 'text-gray-700 hover:text-white hover:bg-gray-800'
// }`}
// whileHover={{ scale: 1.02 }}
// whileTap={{ scale: 0.98 }}
// >
// {product.label}
{/* Hover effect for non-active items */}
{activeProduct !== product.id && (
<motion.div
className="absolute inset-0 bg-gray-800 rounded-full -z-10"
initial={{ opacity: 0, scale: 0.9 }}
whileHover={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.2 }}
/>
)}
</motion.button>
))}
</div>
</motion.div>
</motion.div>
);
}
// {/* Hover effect for non-active items */}
// {activeProduct !== product.id && (
// <motion.div
// className="absolute inset-0 bg-gray-800 rounded-full -z-10"
// initial={{ opacity: 0, scale: 0.9 }}
// whileHover={{ opacity: 1, scale: 1 }}
// transition={{ duration: 0.2 }}
// />
// )}
// </motion.button>
// ))}
// </div>
// </motion.div>
// </motion.div>
// );
// }
export default SubNavbar;
// export default SubNavbar;

View File

@@ -1,3 +1,4 @@
@import "tailwindcss";
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap");
/*! tailwindcss v4.1.3 | MIT License | https://tailwindcss.com */
@layer properties {

View File

@@ -22,11 +22,10 @@ const ComingSoonPage: React.FC<ComingSoonPageProps> = ({
return (
<Layout
activeCity=""
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
showCitySubmenu={false}
>
<div
className="relative z-10 min-h-screen flex items-end justify-start pt-24 pb-16 bg-cover bg-center bg-no-repeat"

View File

@@ -434,7 +434,7 @@ export function FAQPage({ onHomeClick,
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
// user={user}
showCitySubmenu={false}
// showCitySubmenu={false}
>
{/* Main Content */}

View File

@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
import { ChevronDown, MapPin, Star, Shield, Clock, Smartphone } from 'lucide-react';
import Navbar from '../components/Navbar';
import { Footer } from '../components/Footer';
import { CitySubmenu } from '../components/CitySubmenu';
// import { CitySubmenu } from '../components/CitySubmenu';
import heroBannerImage from '../assets/landing-hero.png';
import { Button } from '../components/ui/button';
import { LandingWhyChooseCityCards } from '../components/LandingWhyChooseCityCards';
@@ -102,9 +102,9 @@ export function LandingPage({ onSignInClick,
/>
{/* City Submenu */}
<CitySubmenu
{/* <CitySubmenu
onClose={() => { }}
/>
/> */}
{/* Hero Section */}
<div
className="relative z-10 min-h-screen flex items-end justify-start pt-24 pb-16 bg-cover bg-center bg-no-repeat"

View File

@@ -1,3 +1,4 @@
@import "tailwindcss";
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
@custom-variant dark (&:is(.dark *));