Moved to tanstack start from nextjs using google antigravity

This commit is contained in:
tanshu committed 2026-04-19 08:15:50 +00:00
1 parent 7b1ee49d11
commit 31f88749f9
33 files changed
+3148 -12638

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
{
"mcpServers": {
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp", "--api-key", "ctx7sk-200afd5d-1a75-4c28-a3fb-3f3a26a721a0"]
}
}
}
-25
View File
@@ -1,25 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'standalone',
// optional hardening
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**',
},
],
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
};
export default nextConfig;
-8613
View File
File diff suppressed because it is too large. Load diff
+11 -7
View File
@@ -6,17 +6,22 @@
"node": ">=18.18.0"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"dev": "vite dev",
"build": "vite build",
"start": "node .output/server/index.mjs",
"lint": "eslint ."
},
"dependencies": {
"@tanstack/react-router": "^1.168.23",
"@tanstack/react-start": "^1.167.42",
"@unpic/react": "^1.0.2",
"@vitejs/plugin-react": "^6.0.1",
"framer-motion": "^12.23.9",
"lucide-react": "^0.525.0",
"next": "^15.4.4",
"nitro": "^3.0.260415-beta",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react-dom": "^19.1.0",
"vite": "^8.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -26,7 +31,6 @@
"@types/react-dom": "^19",
"autoprefixer": "^10.4.21",
"eslint": "^9.32.0",
"eslint-config-next": "^15.4.4",
"tailwindcss": "^3.4.17",
"typescript": "^5"
}
-9
View File
@@ -1,9 +0,0 @@
<svg width="1920" height="1090" viewBox="0 0 1920 1090" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect y="-69.8184" width="1920" height="1282.91" fill="url(#pattern0_2586_368)"/>
<defs>
<pattern id="pattern0_2586_368" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2586_368" transform="matrix(0.000244308 0 0 0.000365631 -0.00034235 0)"/>
</pattern>
<image id="image0_2586_368" width="4096" height="2735" preserveAspectRatio="none" xlink:href="data:image/jpeg;base64,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 truncated
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 9.5 MiB

-14
View File
@@ -1,14 +0,0 @@
<svg width="1920" height="1090" viewBox="0 0 1920 1090" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<mask id="mask0_2586_561" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="1920" height="1090">
<rect width="1920" height="1090" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_2586_561)">
<rect x="-20" width="1954" height="1110" fill="url(#pattern0_2586_561)"/>
</g>
<defs>
<pattern id="pattern0_2586_561" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2586_561" transform="scale(0.000568182 0.0010002)"/>
</pattern>
<image id="image0_2586_561" width="1760" height="1000" preserveAspectRatio="none" xlink:href="data:image/jpeg;base64,/9j/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////7gAhQWRvYmUAZEAAAAABAwAQAwIDBgAAAAAAAAAAAAAAAP/bAIQAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQIBAQEBAQECAgICAgICAgICAgICAgMDAwMDAwMDAwMDAwMDAwEBAQEBAQECAQECAwICAgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD/8IAEQgD6AbgAwERAAIRAQMRAf/EARAAAAAFBQEBAAAAAAAAAAAAAAAFBgcIAQIDBAkKCwEBAAAHAQEBAAAAAAAAAAAAAAECAwQFBgcICQoQAAEEAQMDAgUDBQACAgIBBQECAwQFBgARByESCBMJEDAxIhQgQBVQQTIjFiQXMwpCJTQmGRg1GhEAAgECBQMCBAMFBQcCAgITAQIDEQQAITESBUETBlEiYTIUB3GBI5GhQhUIscHRUjMQIDDw4SQW8WJAcpJDglM0JRcJUKKyY8LiJtJzRFQ1GBIAAQMCBAIHBQYFAwMDAgILAQARAiEDMUFREmEE8HGBkSITBRChscEyMNHhQhQGIEDxUiNQYhVyMweCkiRDFqKyU2M0wtJzJReDRAj/2gAMAwEBAhEDEQAAAPfsAAAAAAAAAAAAAAAI+MY409h8SVLine truncated
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 MiB

-9
View File
@@ -1,9 +0,0 @@
<svg width="1918" height="1090" viewBox="0 0 1918 1090" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="1918" height="1090" fill="url(#pattern0_2586_581)"/>
<defs>
<pattern id="pattern0_2586_581" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2586_581" transform="scale(0.0005683 0.001)"/>
</pattern>
<image id="image0_2586_581" width="1760" height="1000" preserveAspectRatio="none" xlink:href="data:image/jpeg;base64,/9j/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////7gAhQWRvYmUAZEAAAAABAwAQAwIDBgAAAAAAAAAAAAAAAP/bAIQAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQIBAQEBAQECAgICAgICAgICAgICAgMDAwMDAwMDAwMDAwMDAwEBAQEBAQECAQECAwICAgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD/8IAEQgD6AbgAwERAAIRAQMRAf/EAQwAAAEEAgMBAQAAAAAAAAAAAAcFBggJAwQBAgoACwEAAgIDAQEBAQAAAAAAAAAABAUDBgECBwAICQoQAAEEAgICAgIBAwQCAgECBwMBAgQFBgcACBESEwkUFSEQIhYgMRcKQSMyGDAkQBlQQjM0JSYRAAICAgEDAwMCBQIEBAEBGQECAwQRBQYAIRIxEwdBIhRRYXEyIxUIgUKRoSQWscFSM/DRYhcQ4UM0JSDxcoJTJjCSNQlEGKJAULLCGRIAAQMCBAMFBgUCBQQBAQATAQARAiEDMUFREmFxBPCBkSITobHB0TIFEOFCIxTxUiBichUGMIIzJJJAQ6LSNCWywlNjkxZQcwfi8oMmF//aAAwDAQECEQMRAAAAsF4F91zI31jCJY4ZUrq0brDX4VWGgwVpvSLine truncated
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 MiB

-9
View File
@@ -1,9 +0,0 @@
<svg width="1839" height="1090" viewBox="0 0 1839 1090" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="-79" width="1918" height="1090" fill="url(#pattern0_2586_583)"/>
<defs>
<pattern id="pattern0_2586_583" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2586_583" transform="scale(0.0005683 0.001)"/>
</pattern>
<image id="image0_2586_583" width="1760" height="1000" preserveAspectRatio="none" xlink:href="data:image/jpeg;base64,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 truncated
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1014 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 943 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

+76
View File
@@ -0,0 +1,76 @@
import { createRootRoute, Outlet, HeadContent, Scripts } from '@tanstack/react-router';
import appCss from './globals.css?url';
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1, maximum-scale=5, user-scalable=yes" },
{ title: "Mozimo - India's Premier European Style Bean to Bar Chocolate Experience" },
{ name: "description", content: "Mozimo's journey is a passionate pursuit of crafting exceptional chocolate that celebrates the origin of each cocoa bean. Experience the epitome of indulgence with our perfectly tempered chocolate." },
{ name: "keywords", content: "chocolate, bean to bar, artisanal chocolate, premium chocolate, Indian chocolate, European style chocolate, cocoa beans, chocolate tempering" },
{ name: "author", content: "Mozimo Chocolate" },
{ name: "google-site-verification", content: "vV-fEo3yXs8m0NUhB6JWhYI4pSH0uYVkbT8pgUWuU1I" },
// OpenGraph
{ property: "og:title", content: "Mozimo - Premium Artisanal Chocolate" },
{ property: "og:description", content: "India's Premier European Style Bean to Bar Chocolate Experience" },
{ property: "og:url", content: "https://mozimo.com" },
{ property: "og:site_name", content: "Mozimo Chocolate" },
{ property: "og:image", content: "/og-image.jpg" },
{ property: "og:locale", content: "en_US" },
{ property: "og:type", content: "website" },
// Twitter
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: "Mozimo - Premium Artisanal Chocolate" },
{ name: "twitter:description", content: "India's Premier European Style Bean to Bar Chocolate Experience" },
{ name: "twitter:image", content: "/og-image.jpg" },
],
links: [
{ rel: 'stylesheet', href: appCss },
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossOrigin: 'anonymous' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Playfair+Display:wght@400;500;600;700;800;900&display=swap' },
{ rel: 'icon', href: '/favicon.ico' },
{ rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' },
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },
{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },
{ rel: 'manifest', href: '/site.webmanifest' },
// Preload critical images
{ rel: 'preload', as: 'image', href: '/banners/slider-1.jpg' },
{ rel: 'preload', as: 'image', href: '/logo/logo.svg' },
{ rel: 'preload', as: 'image', href: '/bst/bs1.svg' },
// DNS prefetch
{ rel: 'dns-prefetch', href: '//fonts.googleapis.com' },
{ rel: 'dns-prefetch', href: '//fonts.gstatic.com' },
],
}),
component: RootLayout,
notFoundComponent: () => {
return (
<div>
<p>Not found!</p>
</div>
)
},
});
function RootLayout() {
return (
<html lang="en">
<head>
<HeadContent />
</head>
{/* We apply a generic font-sans class or remove Next.js specific font variables.
Since the Inter Google Font is loaded via the stylesheet above, it will automatically apply
if configured in Tailwind CSS. */}
<body>
<Outlet />
<Scripts />
</body>
</html>
);
}
+425
View File
@@ -0,0 +1,425 @@
import { createFileRoute } from "@tanstack/react-router";
import { motion } from "framer-motion";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { Image } from "@unpic/react";
export const Route = createFileRoute('/about')({
component: AboutPage,
});
// Premium animation variants
const fadeInUp = {
initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.2,
},
},
};
function AboutPage() {
return (
<div className="min-h-screen bg-white font-renner">
<Header />
{/* Hero Section */}
<section className="pt-32 md:pt-40 pb-16 md:pb-24">
<div className="w-full">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center space-y-8"
>
<motion.h1
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(48px, 8vw, 80px)",
lineHeight: "90%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Welcome to the World of Mozimo Magic
</motion.h1>
<motion.div
className="w-full px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
<p
className="text-lg md:text-xl lg:text-2xl text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(18px, 3vw, 24px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
Indulge in the Luxurious. Immerse in the Captivating. Savor the Fresh. At Mozimo, we create a chocolate experience that transports you to a world of opulence, with mesmerizing aromas and visuals, and delights your taste buds with the vibrant flavors of freshly crafted chocolates. Join us on a journey of pure luxury, captivating moments, and a fresh perspective on the art of chocolate-making.
</p>
</motion.div>
{/* Hero Image */}
<motion.div
className="relative w-full mt-12"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }}
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 rounded-lg"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/0.jpg"
alt="Mozimo chocolate hero image"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/10 via-transparent to-transparent" />
</motion.div>
</motion.div>
</motion.div>
</div>
</section>
{/* Our Founders Section */}
<section className="py-16 md:py-24 bg-white">
<div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Founders Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="order-2 lg:order-1"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/1.jpg"
alt="Priyanka and Amritanshu, founders of Mozimo, sitting in their chocolate shop"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Founders Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="space-y-6 md:space-y-8 order-1 lg:order-2 px-8 md:px-16 lg:px-24"
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Our Founders
</motion.h2>
<motion.h3
className="text-2xl md:text-3xl font-semibold text-[#8B4513] font-renner"
style={{
fontWeight: 500,
fontSize: "clamp(20px, 3vw, 28px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Priyanka and Amritanshu.
</motion.h3>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
are the proud founders of Mozimo, a bean-to-bar chocolate shop that celebrates the rich and distinctive flavors of single origin cocoa beans. Their journey with Mozimo has been a passionate pursuit of creating exceptional chocolate that tantalizes the taste buds and tells the unique story of each cocoa bean&apos;s origin.
</motion.p>
</motion.div>
</div>
</div>
</section>
{/* Capturing the Essence of Italian Chocolate Section */}
<section className="py-16 md:py-24 bg-[#F5E6D3]">
<div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Italian Chocolate Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="order-2 lg:order-1 space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21]"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Capturing the essence of
</motion.h2>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "Samantha Signature",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Italian Chocolate
</motion.h2>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
With over 15 years of hospitality experience, these avid travelers scoured the globe for the finest cocoa beans and techniques. Deep in remote cocoa farms, they cultivated relationships with farmers committed to sustainability. Carefully selecting beans, they honor each harvest&apos;s stories. In their cozy workshop, they roast, crack, and refine beans with modern techniques, capturing their essence in every Mozimo chocolate bar.
</motion.p>
</motion.div>
{/* Cocoa Farm Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="order-1 lg:order-2"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/2.jpg"
alt="Cocoa beans drying in open-air structure with farmers tending to the beans"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
</div>
</div>
</section>
{/* Chocolate Making Process Section */}
<section className="py-16 md:py-24 bg-white">
<div className="w-full">
{/* Two Images Side by Side */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={staggerContainer}
className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-12 md:mb-16"
>
<motion.div
variants={fadeInLeft}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/3.jpg"
alt="Chocolatier holding cocoa nibs with chocolate pieces and beans arranged artistically"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
<motion.div
variants={fadeInRight}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/4.jpg"
alt="Thick chocolate bars with whole hazelnuts on chocolate shavings"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Text Content */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
>
Under the mentorship of the master chocolatier Gabriele Rinaudo, Priyanka and Amritanshu dedicated themselves to mastering the intricate art of chocolate making. With unwavering determination and a thirst for knowledge, they immersed themselves in the world of cocoa, learning the nuances of sourcing the finest ingredients and perfecting the delicate techniques that transform raw beans into exquisite chocolate creations.
</motion.p>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
In addition to their training under Gabriele Rinaudo, they embarked on a transformative journey to Italy to further enrich their understanding of the art of chocolate making. This immersive experience in Italy not only broadened their knowledge but also deepened their appreciation for the timeless artistry and dedication that define the world of chocolate making.
</motion.p>
</motion.div>
</div>
</section>
{/* Vision and Passion Section */}
<section className="py-16 md:py-24 bg-[#F5E6D3]">
<div className="w-full">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center space-y-8 md:space-y-12"
>
{/* Founders Outdoor Image */}
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/5.jpg"
alt="Priyanka and Amritanshu in white chef coats with modern tree sculpture in background"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
{/* Vision and Passion Text */}
<motion.div
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
<p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
Passionate about chocolate as a medium for creativity and exploration, they view it not just as a confection but as a canvas for artistic expression. Constantly seeking inspiration, they push the boundaries of chocolate-making, committed to crafting sensory experiences that delight the taste buds, eyes, and soul. Proud to be part of the global craft chocolate revolution, they aim to inspire others while championing cocoa diversity and sustainability. Back in the workshop, cacao beans are roasted, cracked, and refined with modern techniques, crafting each bar of Mozimo chocolate into a masterpiece, capturing the essence of its origins.
</p>
</motion.div>
</motion.div>
</div>
</section>
<Footer />
</div>
);
}
-428
View File
@@ -1,428 +0,0 @@
"use client";
import { motion } from "framer-motion";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import Image from "next/image";
// Premium animation variants
const fadeInUp = {
initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.2,
},
},
};
export default function AboutPage() {
return (
<div className="min-h-screen bg-white font-renner">
<Header />
{/* Hero Section */}
<section className="pt-32 md:pt-40 pb-16 md:pb-24">
<div className="w-full">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center space-y-8"
>
<motion.h1
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(48px, 8vw, 80px)",
lineHeight: "90%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Welcome to the World of Mozimo Magic
</motion.h1>
<motion.div
className="w-full px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
<p
className="text-lg md:text-xl lg:text-2xl text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(18px, 3vw, 24px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
Indulge in the Luxurious. Immerse in the Captivating. Savor the Fresh. At Mozimo, we create a chocolate experience that transports you to a world of opulence, with mesmerizing aromas and visuals, and delights your taste buds with the vibrant flavors of freshly crafted chocolates. Join us on a journey of pure luxury, captivating moments, and a fresh perspective on the art of chocolate-making.
</p>
</motion.div>
{/* Hero Image */}
<motion.div
className="relative w-full mt-12"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }}
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 rounded-lg"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/0.jpg"
alt="Mozimo chocolate hero image"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/10 via-transparent to-transparent" />
</motion.div>
</motion.div>
</motion.div>
</div>
</section>
{/* Our Founders Section */}
<section className="py-16 md:py-24 bg-white">
<div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Founders Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="order-2 lg:order-1"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/1.jpg"
alt="Priyanka and Amritanshu, founders of Mozimo, sitting in their chocolate shop"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Founders Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="space-y-6 md:space-y-8 order-1 lg:order-2 px-8 md:px-16 lg:px-24"
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Our Founders
</motion.h2>
<motion.h3
className="text-2xl md:text-3xl font-semibold text-[#8B4513] font-renner"
style={{
fontWeight: 500,
fontSize: "clamp(20px, 3vw, 28px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Priyanka and Amritanshu.
</motion.h3>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
are the proud founders of Mozimo, a bean-to-bar chocolate shop that celebrates the rich and distinctive flavors of single origin cocoa beans. Their journey with Mozimo has been a passionate pursuit of creating exceptional chocolate that tantalizes the taste buds and tells the unique story of each cocoa bean&apos;s origin.
</motion.p>
</motion.div>
</div>
</div>
</section>
{/* Capturing the Essence of Italian Chocolate Section */}
<section className="py-16 md:py-24 bg-[#F5E6D3]">
<div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Italian Chocolate Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="order-2 lg:order-1 space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21]"
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Capturing the essence of
</motion.h2>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{
fontFamily: "Samantha Signature",
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 48px)",
lineHeight: "100%",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Italian Chocolate
</motion.h2>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
With over 15 years of hospitality experience, these avid travelers scoured the globe for the finest cocoa beans and techniques. Deep in remote cocoa farms, they cultivated relationships with farmers committed to sustainability. Carefully selecting beans, they honor each harvest&apos;s stories. In their cozy workshop, they roast, crack, and refine beans with modern techniques, capturing their essence in every Mozimo chocolate bar.
</motion.p>
</motion.div>
{/* Cocoa Farm Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="order-1 lg:order-2"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/2.jpg"
alt="Cocoa beans drying in open-air structure with farmers tending to the beans"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
</div>
</div>
</section>
{/* Chocolate Making Process Section */}
<section className="py-16 md:py-24 bg-white">
<div className="w-full">
{/* Two Images Side by Side */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={staggerContainer}
className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-12 md:mb-16"
>
<motion.div
variants={fadeInLeft}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/3.jpg"
alt="Chocolatier holding cocoa nibs with chocolate pieces and beans arranged artistically"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
<motion.div
variants={fadeInRight}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/4.jpg"
alt="Thick chocolate bars with whole hazelnuts on chocolate shavings"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Text Content */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
>
Under the mentorship of the master chocolatier Gabriele Rinaudo, Priyanka and Amritanshu dedicated themselves to mastering the intricate art of chocolate making. With unwavering determination and a thirst for knowledge, they immersed themselves in the world of cocoa, learning the nuances of sourcing the finest ingredients and perfecting the delicate techniques that transform raw beans into exquisite chocolate creations.
</motion.p>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
In addition to their training under Gabriele Rinaudo, they embarked on a transformative journey to Italy to further enrich their understanding of the art of chocolate making. This immersive experience in Italy not only broadened their knowledge but also deepened their appreciation for the timeless artistry and dedication that define the world of chocolate making.
</motion.p>
</motion.div>
</div>
</section>
{/* Vision and Passion Section */}
<section className="py-16 md:py-24 bg-[#F5E6D3]">
<div className="w-full">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center space-y-8 md:space-y-12"
>
{/* Founders Outdoor Image */}
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/aboutus/5.jpg"
alt="Priyanka and Amritanshu in white chef coats with modern tree sculpture in background"
fill
className="object-cover img-premium"
quality={90}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
{/* Vision and Passion Text */}
<motion.div
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
<p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
Passionate about chocolate as a medium for creativity and exploration, they view it not just as a confection but as a canvas for artistic expression. Constantly seeking inspiration, they push the boundaries of chocolate-making, committed to crafting sensory experiences that delight the taste buds, eyes, and soul. Proud to be part of the global craft chocolate revolution, they aim to inspire others while championing cocoa diversity and sustainability. Back in the workshop, cacao beans are roasted, cracked, and refined with modern techniques, crafting each bar of Mozimo chocolate into a masterpiece, capturing the essence of its origins.
</p>
</motion.div>
</motion.div>
</div>
</section>
<Footer />
</div>
);
}
+103
View File
@@ -0,0 +1,103 @@
import { createFileRoute } from '@tanstack/react-router';
// Instagram Basic Display API configuration
const INSTAGRAM_ACCESS_TOKEN = process.env.INSTAGRAM_ACCESS_TOKEN || 'IGQWRQSUY4b3RQWU9ZARHlVVUFDaWxJZAWFOUVllM0NxMk1zSHJ5X2JGc2dpRUxyTjBaeDNhUm0yaFZAQeUotVU9VUmFEQkJxU25CdFp3bURSZAGtnLXhCZAHVId21SWUNiNjVzc0pjZAlhPNDRxTDFzZAEQ0ZAXoyVlpUaHcZD';
interface InstagramPostData {
id: string;
caption?: string;
media_type: string;
media_url?: string;
thumbnail_url?: string;
permalink: string;
timestamp: string;
}
export const Route = createFileRoute('/api/instagram')({
server: {
handlers: {
GET: async () => {
try {
if (!INSTAGRAM_ACCESS_TOKEN) {
return Response.json(
{ error: 'Instagram access token not configured' },
{ status: 500 }
);
}
// First, get the user ID using the access token
const userResponse = await fetch(
`https://graph.instagram.com/me?fields=id,username&access_token=${INSTAGRAM_ACCESS_TOKEN}`
);
if (!userResponse.ok) {
const errorText = await userResponse.text();
console.error('Failed to fetch user info:', errorText);
// Check if token is expired
if (errorText.includes('Session has expired') || errorText.includes('code":190')) {
return Response.json(
{
error: 'Instagram access token has expired',
details: 'The access token needs to be refreshed. Please generate a new token from Instagram Developer Console.',
code: 'TOKEN_EXPIRED'
},
{ status: 401 }
);
}
throw new Error('Failed to authenticate with Instagram');
}
const userData = await userResponse.json();
console.log('Instagram user data:', userData);
// Fetch user's media (posts)
const mediaResponse = await fetch(
`https://graph.instagram.com/me/media?fields=id,caption,media_type,media_url,thumbnail_url,permalink,timestamp&access_token=${INSTAGRAM_ACCESS_TOKEN}&limit=6`
);
if (!mediaResponse.ok) {
const errorText = await mediaResponse.text();
console.error('Failed to fetch media:', errorText);
// Check if token is expired
if (errorText.includes('Session has expired') || errorText.includes('code":190')) {
return Response.json(
{
error: 'Instagram access token has expired',
details: 'The access token needs to be refreshed. Please generate a new token from Instagram Developer Console.',
code: 'TOKEN_EXPIRED'
},
{ status: 401 }
);
}
throw new Error('Failed to fetch Instagram posts');
}
const mediaData = await mediaResponse.json();
console.log('Instagram media data:', mediaData);
// Process and format the posts
const posts = mediaData.data.map((post: InstagramPostData) => ({
id: post.id,
caption: post.caption || '',
mediaUrl: post.media_type === 'VIDEO' ? (post.thumbnail_url || post.media_url) : post.media_url,
permalink: post.permalink,
mediaType: post.media_type,
timestamp: post.timestamp
}));
return Response.json({ posts });
} catch (error) {
console.error('Instagram API Error:', error);
return Response.json(
{ error: 'Failed to fetch Instagram posts', details: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}
}
}
});
-97
View File
@@ -1,97 +0,0 @@
import { NextResponse } from 'next/server';
// Instagram Basic Display API configuration
const INSTAGRAM_ACCESS_TOKEN = process.env.INSTAGRAM_ACCESS_TOKEN || 'IGQWRQSUY4b3RQWU9ZARHlVVUFDaWxJZAWFOUVllM0NxMk1zSHJ5X2JGc2dpRUxyTjBaeDNhUm0yaFZAQeUotVU9VUmFEQkJxU25CdFp3bURSZAGtnLXhCZAHVId21SWUNiNjVzc0pjZAlhPNDRxTDFzZAEQ0ZAXoyVlpUaHcZD';
interface InstagramPostData {
id: string;
caption?: string;
media_type: string;
media_url?: string;
thumbnail_url?: string;
permalink: string;
timestamp: string;
}
export async function GET() {
try {
if (!INSTAGRAM_ACCESS_TOKEN) {
return NextResponse.json(
{ error: 'Instagram access token not configured' },
{ status: 500 }
);
}
// First, get the user ID using the access token
const userResponse = await fetch(
`https://graph.instagram.com/me?fields=id,username&access_token=${INSTAGRAM_ACCESS_TOKEN}`
);
if (!userResponse.ok) {
const errorText = await userResponse.text();
console.error('Failed to fetch user info:', errorText);
// Check if token is expired
if (errorText.includes('Session has expired') || errorText.includes('code":190')) {
return NextResponse.json(
{
error: 'Instagram access token has expired',
details: 'The access token needs to be refreshed. Please generate a new token from Instagram Developer Console.',
code: 'TOKEN_EXPIRED'
},
{ status: 401 }
);
}
throw new Error('Failed to authenticate with Instagram');
}
const userData = await userResponse.json();
console.log('Instagram user data:', userData);
// Fetch user's media (posts)
const mediaResponse = await fetch(
`https://graph.instagram.com/me/media?fields=id,caption,media_type,media_url,thumbnail_url,permalink,timestamp&access_token=${INSTAGRAM_ACCESS_TOKEN}&limit=6`
);
if (!mediaResponse.ok) {
const errorText = await mediaResponse.text();
console.error('Failed to fetch media:', errorText);
// Check if token is expired
if (errorText.includes('Session has expired') || errorText.includes('code":190')) {
return NextResponse.json(
{
error: 'Instagram access token has expired',
details: 'The access token needs to be refreshed. Please generate a new token from Instagram Developer Console.',
code: 'TOKEN_EXPIRED'
},
{ status: 401 }
);
}
throw new Error('Failed to fetch Instagram posts');
}
const mediaData = await mediaResponse.json();
console.log('Instagram media data:', mediaData);
// Process and format the posts
const posts = mediaData.data.map((post: InstagramPostData) => ({
id: post.id,
caption: post.caption || '',
mediaUrl: post.media_type === 'VIDEO' ? (post.thumbnail_url || post.media_url) : post.media_url,
permalink: post.permalink,
mediaType: post.media_type,
timestamp: post.timestamp
}));
return NextResponse.json({ posts });
} catch (error) {
console.error('Instagram API Error:', error);
return NextResponse.json(
{ error: 'Failed to fetch Instagram posts', details: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}
+488
View File
@@ -0,0 +1,488 @@
import { createFileRoute } from '@tanstack/react-router';
import { motion } from "framer-motion";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import Section from "@/components/Section";
import Button from "@/components/Button";
import BannerSlider from "@/components/BannerSlider";
import { Image } from "@unpic/react";
import { useState, useEffect, useRef, useCallback } from "react";
import ProductCategory from "@/components/ProductCategory";
import SocialMedia from "@/components/SocialMedia";
export const Route = createFileRoute('/')({
component: Home,
});
// Premium animation variants
const fadeInUp = {
initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.2,
},
},
};
const categories = [
"Bars",
"Barks",
"Pralines",
"Spreads",
"Dragees",
"Gelatos",
];
const partnerImages = [
"/partners/p1.svg",
"/partners/p2.svg",
"/partners/p3.svg",
"/partners/p4.svg",
];
function Home() {
const [currentCategory, setCurrentCategory] = useState(0);
const categoriesSectionRef = useRef<HTMLDivElement>(null);
const currentCategoryRef = useRef(currentCategory);
// Update ref when currentCategory changes
useEffect(() => {
currentCategoryRef.current = currentCategory;
}, [currentCategory]);
// Optimized wheel event handler
const handleWheel = useCallback((e: WheelEvent) => {
e.preventDefault();
// Determine scroll direction
if (e.deltaY > 0) {
// Scroll down - next category
if (currentCategoryRef.current < categories.length - 1) {
setCurrentCategory((prev) => prev + 1);
}
} else if (e.deltaY < 0) {
// Scroll up - previous category
if (currentCategoryRef.current > 0) {
setCurrentCategory((prev) => prev - 1);
}
}
}, []);
// Wheel event for scroll-based category switching
useEffect(() => {
const section = categoriesSectionRef.current;
if (!section) return;
let isScrolling = false;
let scrollTimeout: NodeJS.Timeout;
const onWheel = (e: WheelEvent) => {
if (isScrolling) return;
isScrolling = true;
clearTimeout(scrollTimeout);
handleWheel(e);
// Reset scrolling flag after delay
scrollTimeout = setTimeout(() => {
isScrolling = false;
}, 800);
};
section.addEventListener("wheel", onWheel, { passive: false });
return () => {
section.removeEventListener("wheel", onWheel);
clearTimeout(scrollTimeout);
};
}, [handleWheel]);
const [spotlightIndex, setSpotlightIndex] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setSpotlightIndex((prev) => (prev + 1) % partnerImages.length);
}, 2500);
return () => clearInterval(timer);
}, []);
// Memoize expensive components
return (
<div className="min-h-screen bg-white font-renner">
<Header />
<BannerSlider />
{/* Brand Story */}
<Section background="white" id="about">
<div className="grid grid-cols-1 lg:grid-cols-2 items-center">
{/* Brand Story - Left Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="space-y-6 md:space-y-8 order-2 lg:order-1 px-4 md:px-0" // ← Added mobile padding
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient p-4 md:p-0"
style={{
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 58px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Brand Story
</motion.h2>
<div
className="space-y-4 md:space-y-6 text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
>
Mozimo&apos;s journey is a passionate pursuit of crafting
exceptional chocolate that celebrates the origin of each cocoa
bean.
</motion.p>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
We meticulously roast, crack, winnow, and refine beans in-house,
using modern techniques to highlight their natural flavors.
</motion.p>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
Each chocolate is a masterpiece, capturing the essence of cocoa
in its purest form.
</motion.p>
</div>
<motion.div
className="pt-4"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
viewport={{ once: true }}
>
<Button variant="white" size="lg" className="btn-premium">
Discover more
</Button>
</motion.div>
</motion.div>
{/* Brand Story - Right Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0"
>
<motion.div
className="relative w-full h-[300px] md:h-[400px] lg:h-[500px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-bottom-left"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/bst/bs1.svg"
alt="Cocoa pods and beans - Mozimo chocolate ingredients"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
draggable={false}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Chocolate Tempering - Left Video */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="relative order-3 lg:order-3 w-screen md:w-auto -mx-4 md:mx-0 py-4 md:py-0"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[700px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-top-right"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<video
src="https://qa-dugout.s3.ap-south-1.amazonaws.com/testing/v1.mp4"
className="w-full h-full object-cover"
autoPlay
loop
muted
playsInline
preload="auto"
aria-label="Chocolate pouring from metallic spout - Mozimo chocolate tempering process"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Chocolate Tempering - Right Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="space-y-4 md:space-y-6 order-4 lg:order-4 px-4 md:px-0 ml-4 md:ml-8 lg:ml-12"
>
<div className="space-y-3 md:space-y-4">
<motion.h2
className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] font-renner text-gradient"
style={{
fontWeight: 200,
fontSize: "clamp(24px, 4vw, 40px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Discover the delicate art of our
</motion.h2>
<motion.h3
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#8B4513] font-samantha"
style={{
fontFamily: "Samantha Signature",
fontWeight: 400,
fontStyle: "normal",
fontSize: "clamp(35px, 8vw, 50px)",
lineHeight: "clamp(20px, 4vw, 25px)",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Chocolate Tempering
</motion.h3>
</div>
<motion.div
className="w-100 h-px bg-gradient-to-r from-transparent via-[#8B4513] to-transparent"
initial={{ scaleX: 0 }}
whileInView={{ scaleX: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
viewport={{ once: true }}
/>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
Crafted to perfection Mozimo delivers a sublime sensory experience
with every bite. Experience the epitome of indulgence with our
perfectly tempered chocolate: velvety smooth, exquisitely rich,
and artfully balanced. Each bite offers a symphony of nuanced
cocoa flavors, melting luxuriously!
</motion.p>
</motion.div>
</div>
</Section>
{/* Our Collections Section */}
<Section background="cream" id="shop">
<motion.h2
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] text-center mb-8 md:mb-12 font-renner text-gradient"
style={{
fontWeight: 300,
fontSize: "clamp(24px, 4vw, 36px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Our Collections
</motion.h2>
<motion.div
className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:px-0 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8"
variants={staggerContainer}
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
>
{[
{
src: "/collection/cl1.svg",
title: "Bestsellers",
alt: "Bestsellers - Mozimo chocolate bars with geometric patterns",
},
{
src: "/collection/cl2.svg",
title: "New Arrivals",
alt: "New Arrivals - Stacked chocolate barks with nuts and toppings",
},
{
src: "/collection/cl3.svg",
title: "Gift Collection",
alt: "Gift Collection - Mozimo packaged chocolate products and gift items",
},
].map((item) => (
<motion.div
key={item.title}
variants={fadeInUp}
className="snap-start w-[calc(50%)] flex-shrink-0 sm:w-auto sm:flex-shrink sm:min-w-0 group flex flex-col items-center justify-end transition-all duration-500 hover:-translate-y-2"
whileHover={{
scale: 1.02,
transition: { duration: 0.3 },
}}
// style={{ minHeight: "480px" }}
>
<Image
src={item.src}
alt={item.alt}
width={320}
height={400}
layout="constrained"
className="object-contain img-premium group-hover:scale-105 transition-transform duration-500"
draggable={false}
style={{ maxHeight: "380px", width: "100%" }}
/>
<div className="p-4 md:p-6 text-center mb-2 w-full">
<h3
className="text-lg md:text-xl font-semibold text-[#3C2A21] font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 20px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
{item.title}
</h3>
</div>
</motion.div>
))}
</motion.div>
</Section>
<ProductCategory />
{/* Social Media Call to Action */}
<SocialMedia />
{/* In the Spotlight Section */}
<Section background="white">
<motion.h2
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-3xl md:text-4xl lg:text-5xl font-moneta text-[#703133] mb-6 md:mb-8 text-center text-gradient"
style={{
fontWeight: 400,
fontSize: "clamp(28px, 5vw, 48px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
In the Spotlight
</motion.h2>
<div className="w-full flex justify-center items-center py-8 md:py-12">
<div className="w-full max-w-[700px] px-4 overflow-hidden">
<motion.div
className="flex gap-8 py-6"
animate={{ x: `-${spotlightIndex * 152}px` }}
transition={{ type: "spring", stiffness: 80, damping: 18 }}
>
{Array(12)
.fill(0)
.map((_, i) => {
const imgIdx = i % partnerImages.length;
const img = partnerImages[imgIdx];
return (
<motion.div
key={i}
className="w-24 h-24 md:w-32 md:h-32 lg:w-36 lg:h-36 bg-white rounded-full flex items-center justify-center shadow-premium hover:shadow-premium-hover flex-shrink-0 transition-all duration-300"
style={{
boxShadow: "0 4px 20px 0 rgba(60,42,33,0.15)",
margin: "20px 8px",
}}
whileHover={{
scale: 1.1,
rotate: 5,
transition: { duration: 0.3 },
}}
>
<Image
src={img}
alt={`Partner ${imgIdx + 1}`}
width={120}
height={120}
layout="constrained"
className="w-16 h-16 md:w-20 md:h-20 lg:w-20 lg:h-20 object-contain img-premium"
draggable={false}
/>
</motion.div>
);
})}
</motion.div>
</div>
</div>
</Section>
<Footer />
</div>
);
}
-96
View File
@@ -1,96 +0,0 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Mozimo - India's Premier European Style Bean to Bar Chocolate Experience",
description: "Mozimo's journey is a passionate pursuit of crafting exceptional chocolate that celebrates the origin of each cocoa bean. Experience the epitome of indulgence with our perfectly tempered chocolate.",
keywords: "chocolate, bean to bar, artisanal chocolate, premium chocolate, Indian chocolate, European style chocolate, cocoa beans, chocolate tempering",
authors: [{ name: "Mozimo Chocolate" }],
creator: "Mozimo Chocolate",
publisher: "Mozimo Chocolate",
formatDetection: {
email: false,
address: false,
telephone: false,
},
metadataBase: new URL('https://mozimo.com'),
openGraph: {
title: "Mozimo - Premium Artisanal Chocolate",
description: "India's Premier European Style Bean to Bar Chocolate Experience",
url: 'https://mozimo.com',
siteName: 'Mozimo Chocolate',
images: [
{
url: '/og-image.jpg',
width: 1200,
height: 630,
alt: 'Mozimo Chocolate - Premium Artisanal Chocolate',
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: "Mozimo - Premium Artisanal Chocolate",
description: "India's Premier European Style Bean to Bar Chocolate Experience",
images: ['/og-image.jpg'],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
verification: {
google: 'your-google-verification-code',
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
{/* Mobile viewport meta tag */}
<meta name="google-site-verification" content="vV-fEo3yXs8m0NUhB6JWhYI4pSH0uYVkbT8pgUWuU1I" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, user-scalable=yes" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Playfair+Display:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
{/* Preload critical images */}
<link rel="preload" as="image" href="/banners/b1.svg" />
<link rel="preload" as="image" href="/logo/logo.svg" />
<link rel="preload" as="image" href="/bst/bs1.svg" />
{/* DNS prefetch for external resources */}
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//fonts.gstatic.com" />
</head>
<body className={inter.className}>
{children}
</body>
</html>
);
}
-486
View File
@@ -1,486 +0,0 @@
"use client";
import { motion } from "framer-motion";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import Section from "@/components/Section";
import Button from "@/components/Button";
import BannerSlider from "@/components/BannerSlider";
import Image from "next/image";
import { useState, useEffect, useRef, useCallback } from "react";
import ProductCategory from "@/components/ProductCategory";
import SocialMedia from "@/components/SocialMedia";
// Premium animation variants
const fadeInUp = {
initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const staggerContainer = {
animate: {
transition: {
staggerChildren: 0.2,
},
},
};
const categories = [
"Bars",
"Barks",
"Pralines",
"Spreads",
"Dragees",
"Gelatos",
];
const partnerImages = [
"/partners/p1.svg",
"/partners/p2.svg",
"/partners/p3.svg",
"/partners/p4.svg",
];
export default function Home() {
const [currentCategory, setCurrentCategory] = useState(0);
const categoriesSectionRef = useRef<HTMLDivElement>(null);
const currentCategoryRef = useRef(currentCategory);
// Update ref when currentCategory changes
useEffect(() => {
currentCategoryRef.current = currentCategory;
}, [currentCategory]);
// Optimized wheel event handler
const handleWheel = useCallback((e: WheelEvent) => {
e.preventDefault();
// Determine scroll direction
if (e.deltaY > 0) {
// Scroll down - next category
if (currentCategoryRef.current < categories.length - 1) {
setCurrentCategory((prev) => prev + 1);
}
} else if (e.deltaY < 0) {
// Scroll up - previous category
if (currentCategoryRef.current > 0) {
setCurrentCategory((prev) => prev - 1);
}
}
}, []);
// Wheel event for scroll-based category switching
useEffect(() => {
const section = categoriesSectionRef.current;
if (!section) return;
let isScrolling = false;
let scrollTimeout: NodeJS.Timeout;
const onWheel = (e: WheelEvent) => {
if (isScrolling) return;
isScrolling = true;
clearTimeout(scrollTimeout);
handleWheel(e);
// Reset scrolling flag after delay
scrollTimeout = setTimeout(() => {
isScrolling = false;
}, 800);
};
section.addEventListener("wheel", onWheel, { passive: false });
return () => {
section.removeEventListener("wheel", onWheel);
clearTimeout(scrollTimeout);
};
}, [handleWheel]);
const [spotlightIndex, setSpotlightIndex] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setSpotlightIndex((prev) => (prev + 1) % partnerImages.length);
}, 2500);
return () => clearInterval(timer);
}, []);
// Memoize expensive components
return (
<div className="min-h-screen bg-white font-renner">
<Header />
<BannerSlider />
{/* Brand Story */}
<Section background="white" id="about">
<div className="grid grid-cols-1 lg:grid-cols-2 items-center">
{/* Brand Story - Left Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="space-y-6 md:space-y-8 order-2 lg:order-1 px-4 md:px-0" // ← Added mobile padding
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient p-4 md:p-0"
style={{
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 58px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Brand Story
</motion.h2>
<div
className="space-y-4 md:space-y-6 text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
>
Mozimo&apos;s journey is a passionate pursuit of crafting
exceptional chocolate that celebrates the origin of each cocoa
bean.
</motion.p>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
We meticulously roast, crack, winnow, and refine beans in-house,
using modern techniques to highlight their natural flavors.
</motion.p>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
Each chocolate is a masterpiece, capturing the essence of cocoa
in its purest form.
</motion.p>
</div>
<motion.div
className="pt-4"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
viewport={{ once: true }}
>
<Button variant="white" size="lg" className="btn-premium">
Discover more
</Button>
</motion.div>
</motion.div>
{/* Brand Story - Right Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0"
>
<motion.div
className="relative w-full h-[300px] md:h-[400px] lg:h-[500px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-bottom-left"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<Image
src="/bst/bs1.svg"
alt="Cocoa pods and beans - Mozimo chocolate ingredients"
fill
className="object-cover img-premium"
draggable={false}
quality={85}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Chocolate Tempering - Left Video */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="relative order-3 lg:order-3 w-screen md:w-auto -mx-4 md:mx-0 py-4 md:py-0"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[700px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-top-right"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
>
<video
src="https://qa-dugout.s3.ap-south-1.amazonaws.com/testing/v1.mp4"
className="w-full h-full object-cover"
autoPlay
loop
muted
playsInline
preload="auto"
aria-label="Chocolate pouring from metallic spout - Mozimo chocolate tempering process"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Chocolate Tempering - Right Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="space-y-4 md:space-y-6 order-4 lg:order-4 px-4 md:px-0 ml-4 md:ml-8 lg:ml-12"
>
<div className="space-y-3 md:space-y-4">
<motion.h2
className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] font-renner text-gradient"
style={{
fontWeight: 200,
fontSize: "clamp(24px, 4vw, 40px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Discover the delicate art of our
</motion.h2>
<motion.h3
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#8B4513] font-samantha"
style={{
fontFamily: "Samantha Signature",
fontWeight: 400,
fontStyle: "normal",
fontSize: "clamp(35px, 8vw, 50px)",
lineHeight: "clamp(20px, 4vw, 25px)",
letterSpacing: "0%",
color: "#703133",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Chocolate Tempering
</motion.h3>
</div>
<motion.div
className="w-100 h-px bg-gradient-to-r from-transparent via-[#8B4513] to-transparent"
initial={{ scaleX: 0 }}
whileInView={{ scaleX: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
viewport={{ once: true }}
/>
<motion.p
className="text-gray-700 leading-relaxed font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 18px)",
lineHeight: "160%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
Crafted to perfection Mozimo delivers a sublime sensory experience
with every bite. Experience the epitome of indulgence with our
perfectly tempered chocolate: velvety smooth, exquisitely rich,
and artfully balanced. Each bite offers a symphony of nuanced
cocoa flavors, melting luxuriously!
</motion.p>
</motion.div>
</div>
</Section>
{/* Our Collections Section */}
<Section background="cream" id="shop">
<motion.h2
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] text-center mb-8 md:mb-12 font-renner text-gradient"
style={{
fontWeight: 300,
fontSize: "clamp(24px, 4vw, 36px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
Our Collections
</motion.h2>
<motion.div
className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:px-0 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8"
variants={staggerContainer}
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
>
{[
{
src: "/collection/cl1.svg",
title: "Bestsellers",
alt: "Bestsellers - Mozimo chocolate bars with geometric patterns",
},
{
src: "/collection/cl2.svg",
title: "New Arrivals",
alt: "New Arrivals - Stacked chocolate barks with nuts and toppings",
},
{
src: "/collection/cl3.svg",
title: "Gift Collection",
alt: "Gift Collection - Mozimo packaged chocolate products and gift items",
},
].map((item) => (
<motion.div
key={item.title}
variants={fadeInUp}
className="snap-start w-[calc(50%)] flex-shrink-0 sm:w-auto sm:flex-shrink sm:min-w-0 group flex flex-col items-center justify-end transition-all duration-500 hover:-translate-y-2"
whileHover={{
scale: 1.02,
transition: { duration: 0.3 },
}}
// style={{ minHeight: "480px" }}
>
<Image
src={item.src}
alt={item.alt}
width={320}
height={400}
className="object-contain img-premium group-hover:scale-105 transition-transform duration-500"
draggable={false}
quality={85}
style={{ maxHeight: "380px", width: "100%" }}
/>
<div className="p-4 md:p-6 text-center mb-2 w-full">
<h3
className="text-lg md:text-xl font-semibold text-[#3C2A21] font-renner"
style={{
fontWeight: 300,
fontSize: "clamp(16px, 2.5vw, 20px)",
lineHeight: "100%",
letterSpacing: "0%",
fontFamily: "Renner*",
}}
>
{item.title}
</h3>
</div>
</motion.div>
))}
</motion.div>
</Section>
<ProductCategory />
{/* Social Media Call to Action */}
<SocialMedia />
{/* In the Spotlight Section */}
<Section background="white">
<motion.h2
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-3xl md:text-4xl lg:text-5xl font-moneta text-[#703133] mb-6 md:mb-8 text-center text-gradient"
style={{
fontWeight: 400,
fontSize: "clamp(28px, 5vw, 48px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
>
In the Spotlight
</motion.h2>
<div className="w-full flex justify-center items-center py-8 md:py-12">
<div className="w-full max-w-[700px] px-4 overflow-hidden">
<motion.div
className="flex gap-8 py-6"
animate={{ x: `-${spotlightIndex * 152}px` }}
transition={{ type: "spring", stiffness: 80, damping: 18 }}
>
{Array(12)
.fill(0)
.map((_, i) => {
const imgIdx = i % partnerImages.length;
const img = partnerImages[imgIdx];
return (
<motion.div
key={i}
className="w-24 h-24 md:w-32 md:h-32 lg:w-36 lg:h-36 bg-white rounded-full flex items-center justify-center shadow-premium hover:shadow-premium-hover flex-shrink-0 transition-all duration-300"
style={{
boxShadow: "0 4px 20px 0 rgba(60,42,33,0.15)",
margin: "20px 8px",
}}
whileHover={{
scale: 1.1,
rotate: 5,
transition: { duration: 0.3 },
}}
>
<Image
src={img}
alt={`Partner ${imgIdx + 1}`}
width={120}
height={120}
className="w-16 h-16 md:w-20 md:h-20 lg:w-20 lg:h-20 object-contain img-premium"
draggable={false}
quality={85}
/>
</motion.div>
);
})}
</motion.div>
</div>
</div>
</Section>
<Footer />
</div>
);
}
+8 -13
View File
@@ -1,14 +1,13 @@
"use client";
import { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import Image from "next/image";
import { Image } from "@unpic/react";
const banners = [
{ id: 1, src: "/banners/b1.svg", alt: "Mozimo Chocolate Banner 1" },
{ id: 2, src: "/banners/b3.svg", alt: "Mozimo Chocolate Banner 2" },
{ id: 3, src: "/banners/b2.svg", alt: "Mozimo Chocolate Banner 3" },
{ id: 4, src: "/banners/b4.svg", alt: "Mozimo Chocolate Banner 4" },
{ id: 1, src: "/banners/slider-5.jpg", alt: "Macha Collection" },
{ id: 2, src: "/banners/slider-1.jpg", alt: "Mozimo Chocolate Banner 1" },
{ id: 3, src: "/banners/slider-3.jpg", alt: "Mozimo Chocolate Banner 2" },
{ id: 4, src: "/banners/slider-2.jpg", alt: "Mozimo Chocolate Banner 3" },
{ id: 5, src: "/banners/slider-4.jpg", alt: "Mozimo Chocolate Banner 4" },
];
export default function BannerSlider() {
@@ -124,13 +123,9 @@ export default function BannerSlider() {
<Image
src={banners[currentIndex].src}
alt={banners[currentIndex].alt}
fill
className="object-cover img-premium"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
draggable={false}
priority={currentIndex === 0}
loading={currentIndex === 0 ? "eager" : "lazy"}
sizes="100vw"
quality={85}
/>
{/* Premium overlay with gradient */}
+83 -60
View File
@@ -1,76 +1,99 @@
'use client';
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
import { Link } from "@tanstack/react-router";
import { ReactNode } from "react";
interface ButtonProps {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'white';
size?: 'sm' | 'md' | 'lg';
variant?: "primary" | "secondary" | "white";
size?: "sm" | "md" | "lg";
className?: string;
onClick?: () => void;
onClick?: () => void; // if provided, this forces client usage by the parent
href?: string;
target?: string;
rel?: string;
type?: "button" | "submit" | "reset";
}
export default function Button({
children,
variant = 'primary',
size = 'md',
className = '',
export default function Button({
children,
variant = "primary",
size = "md",
className = "",
onClick,
href
href,
target,
rel,
type = "button",
}: ButtonProps) {
const baseClasses = 'font-renner rounded-full transition-all duration-500 inline-flex items-center justify-center relative overflow-hidden shadow-premium hover:shadow-premium-hover';
const variantClasses = {
primary: 'bg-gradient-to-r from-[#8B4513] to-[#A0522D] text-white hover:from-[#3C2A21] hover:to-[#8B4513] shadow-glow hover:shadow-glow',
secondary: 'bg-gradient-to-r from-[#DAA520] to-[#CD7F32] text-white hover:from-[#B8860B] hover:to-[#DAA520] shadow-glow hover:shadow-glow',
white: 'bg-white text-[#3C2A21] shadow-premium hover:shadow-premium-hover hover:bg-gray-50 border border-gray-200 hover:border-[#8B4513]'
const base =
"font-renner rounded-full inline-flex items-center justify-center relative overflow-hidden " +
"shadow-premium hover:shadow-premium-hover transition-transform duration-200 ease-out " +
"hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.98] focus-visible:outline-none " +
"focus-visible:ring-2 focus-visible:ring-[#8B4513]/40";
const variantClasses: Record<NonNullable<ButtonProps["variant"]>, string> = {
primary:
"bg-gradient-to-r from-[#8B4513] to-[#A0522D] text-white " +
"hover:from-[#3C2A21] hover:to-[#8B4513] shadow-glow hover:shadow-glow",
secondary:
"bg-gradient-to-r from-[#DAA520] to-[#CD7F32] text-white " +
"hover:from-[#B8860B] hover:to-[#DAA520] shadow-glow hover:shadow-glow",
white:
"bg-white text-[#3C2A21] hover:bg-gray-50 border border-gray-200 hover:border-[#8B4513]",
};
const sizeClasses = {
sm: 'px-3 py-2 text-sm md:px-4 md:py-2 md:text-sm',
md: 'px-4 py-2 text-sm md:px-6 md:py-3 md:text-base',
lg: 'px-6 py-3 text-base md:px-8 md:py-4 md:text-lg'
const sizeClasses: Record<NonNullable<ButtonProps["size"]>, string> = {
sm: "px-3 py-2 text-sm md:px-4 md:py-2",
md: "px-4 py-2 text-sm md:px-6 md:py-3 md:text-base",
lg: "px-6 py-3 text-base md:px-8 md:py-4 md:text-lg",
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
const buttonContent = (
<motion.div
className={classes}
style={{
fontWeight: 300,
fontSize: 'clamp(14px, 2.5vw, 16px)',
lineHeight: '100%',
letterSpacing: '0%'
}}
whileHover={{
scale: 1.05,
y: -3,
transition: { duration: 0.3, ease: "easeOut" }
}}
whileTap={{
scale: 0.98,
transition: { duration: 0.1 }
}}
onClick={onClick}
>
{/* Shimmer effect overlay */}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full hover:translate-x-full transition-transform duration-700 ease-out" />
{/* Button content */}
<span className="relative z-10">{children}</span>
</motion.div>
const classes = `${base} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
// shimmer overlay via CSS only (no JS)
const content = (
<>
<span
aria-hidden="true"
className="pointer-events-none absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent
group-hover:translate-x-full transition-transform duration-700 ease-out"
/>
<span
className="relative z-10"
style={{
fontWeight: 300,
fontSize: "clamp(14px, 2.5vw, 16px)",
lineHeight: "100%",
letterSpacing: "0%",
}}
>
{children}
</span>
</>
);
// If it's a link, use Next <Link> for internal, <a> for external
if (href) {
const isExternal = /^https?:\/\//.test(href);
if (isExternal) {
return (
<a href={href} target={target ?? "_blank"} rel={rel ?? "noopener noreferrer"} className={`group ${classes}`}>
{content}
</a>
);
}
return (
<a href={href} className="inline-block">
{buttonContent}
</a>
<Link to={href} className={`group ${classes}`}>
{content}
</Link>
);
}
return buttonContent;
}
// Button element (onClick will require parent to be client; this file itself stays SSR-friendly)
return (
<button type={type} onClick={onClick} className={`group ${classes}`}>
{content}
</button>
);
}
+2 -8
View File
@@ -1,7 +1,5 @@
"use client";
import { motion } from "framer-motion";
import Image from "next/image";
import { Image } from "@unpic/react";
export default function DesktopFooter() {
return (
@@ -25,13 +23,9 @@ export default function DesktopFooter() {
src="/logo/logo.svg"
alt="Mozimo Logo"
className="object-contain drop-shadow-lg"
priority
sizes="40px"
width={80}
height={80}
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
layout="fixed"
/>
{/* Tagline */}
<p
+7 -17
View File
@@ -1,14 +1,12 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import Image from "next/image";
import { Image } from "@unpic/react";
import { useState, useEffect } from "react";
import { usePathname } from "next/navigation";
import { useLocation } from "@tanstack/react-router";
export default function Header() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const pathname = usePathname();
const pathname = useLocation({ select: (loc) => loc.pathname });
useEffect(() => {
const handleScroll = () => {
@@ -136,13 +134,8 @@ export default function Header() {
<Image
src="/logo/logo.svg"
alt="Mozimo Logo"
fill
className="object-contain drop-shadow-lg"
priority
sizes="(max-width: 768px) 40px, 96px"
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
layout="fullWidth"
className="w-full h-full object-contain drop-shadow-lg"
/>
</motion.div>
</motion.div>
@@ -293,11 +286,8 @@ export default function Header() {
<Image
src="/logo/logo.svg"
alt="Mozimo Logo"
fill
className="object-contain drop-shadow-lg"
priority
sizes="(max-width: 768px) 40px, 96px"
quality={95}
layout="fullWidth"
className="w-full h-full object-contain drop-shadow-lg"
/>
</motion.div>
+2 -8
View File
@@ -1,7 +1,5 @@
"use client";
import { motion } from "framer-motion";
import Image from "next/image";
import { Image } from "@unpic/react";
export default function MobileFooter() {
return (
@@ -24,13 +22,9 @@ export default function MobileFooter() {
src="/logo/logo.svg"
alt="Mozimo Logo"
className="object-contain drop-shadow-lg"
priority
sizes="40px"
width={80}
height={80}
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
layout="fixed"
/>
</motion.div>
+59 -143
View File
@@ -1,13 +1,7 @@
"use client";
import { useState, useRef, useCallback, useMemo, useEffect } from "react";
import { motion } from "framer-motion";
import { useState, useCallback, useMemo, useEffect } from "react";
import { LazyMotion, domAnimation, m } from "framer-motion";
import Section from "@/components/Section";
import Image from "next/image";
export default function ProductCategory() {
const [currentCategory, setCurrentCategory] = useState(0);
const categoriesSectionRef = useRef<HTMLDivElement>(null);
const imageCache = useRef<{ [key: string]: HTMLImageElement }>({});
import { Image } from "@unpic/react";
const categories = [
"Bars",
@@ -34,180 +28,102 @@ export default function ProductCategory() {
"/categories/c5.svg",
"/categories/c6.svg",
];
// Preload all images on component mount
useEffect(() => {
const preloadImages = async () => {
const loadPromises = categoryImages.map((src) => {
return new Promise<void>((resolve) => {
if (imageCache.current[src]) {
resolve();
return;
}
const img = new window.Image();
img.onload = () => {
imageCache.current[src] = img;
resolve();
};
img.onerror = () => resolve(); // Continue even if one fails
img.src = src;
});
});
await Promise.all(loadPromises);
};
preloadImages();
}, [categoryImages]);
// Ultra-fast animation variants (no delays)
const fadeInLeft = {
initial: { opacity: 0, x: -20 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.3, ease: [0.23, 1, 0.32, 1] },
};
const fadeInRight = {
initial: { opacity: 0, x: 20 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.3, ease: [0.23, 1, 0.32, 1] },
};
export default function ProductCategory() {
const [currentCategory, setCurrentCategory] = useState(0);
// Instant category change with no debouncing
const handleCategoryChange = useCallback((idx: number) => {
setCurrentCategory(idx);
}, []);
// Optimized category buttons
// Preload only neighbor images ✅
useEffect(() => {
const next = (currentCategory + 1) % categoryImages.length;
const prev = (currentCategory - 1 + categoryImages.length) % categoryImages.length;
[next, prev].forEach((i) => {
const img = new window.Image();
img.src = categoryImages[i];
});
}, [currentCategory]);
const categoryButtons = useMemo(() => {
return categories.map((category, idx) => {
const isActive = idx === currentCategory;
return (
<motion.li
key={category}
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2, delay: idx * 0.02 }}
viewport={{ once: true }}
>
<li key={category}>
<a
href={categoryLinks[idx]}
target="_blank"
rel="noopener noreferrer"
className={`group flex items-center gap-4 w-full text-left font-moneta transition-all duration-100 ease-out will-change-transform
${
isActive
? "text-[#703133] font-normal"
: "text-[#703133] opacity-70 font-light hover:opacity-90"
}
className={`group flex items-center gap-4 w-full text-left transition-opacity duration-150
${isActive ? "opacity-100" : "opacity-70 hover:opacity-90"}
`}
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: isActive ? 400 : 300,
fontSize: isActive
? "clamp(22px, 5vw, 64px)"
: "clamp(18px, 4vw, 56px)",
fontSize: isActive ? "clamp(22px, 5vw, 64px)" : "clamp(18px, 4vw, 56px)",
lineHeight: "120%",
letterSpacing: "0%",
transform: isActive ? "translateX(4px)" : "translateX(0)",
color: "#703133",
}}
onClick={() => handleCategoryChange(idx)}
onMouseEnter={() => handleCategoryChange(idx)}
onTouchStart={() => handleCategoryChange(idx)}
>
{/* Mobile optimized thumbnail */}
<div className="block lg:hidden w-16 h-16 rounded-full overflow-hidden shrink-0 will-change-transform">
{/* Mobile thumbnail */}
<div className="block lg:hidden w-16 h-16 rounded-full overflow-hidden shrink-0">
<Image
src={categoryImages[idx]}
alt={category}
width={64}
height={64}
className={`w-full h-full object-cover transition-transform duration-100 ${
isActive ? "scale-110" : "scale-100"
}`}
className={`w-full h-full object-cover transition-transform duration-150 ${isActive ? "scale-110" : "scale-100"}`}
draggable={false}
quality={60}
priority={idx < 2}
loading={idx < 2 ? "eager" : "lazy"}
layout="fixed"
/>
</div>
<span className="will-change-transform">{category}</span>
<span>{category}</span>
</a>
</motion.li>
</li>
);
});
}, [currentCategory, handleCategoryChange, categories, categoryImages, categoryLinks]);
// Ultra-optimized image switcher with instant transitions
const categoryImageComponent = useMemo(
() => (
<div className="relative w-full h-full overflow-hidden rounded-2xl">
{/* Render all images but show only active one */}
{categoryImages.map((src, idx) => (
<motion.div
key={idx}
initial={false}
animate={{
opacity: idx === currentCategory ? 1 : 0,
scale: idx === currentCategory ? 1 : 0.95,
}}
transition={{
duration: 0.15,
ease: [0.23, 1, 0.32, 1],
}}
className="absolute inset-0 will-change-transform"
style={{
zIndex: idx === currentCategory ? 2 : 1,
}}
>
<Image
src={src}
alt={categories[idx]}
fill
className="object-cover"
draggable={false}
quality={85}
priority={idx < 3}
loading={idx < 3 ? "eager" : "lazy"}
sizes="(max-width: 768px) 100vw, 50vw"
/>
</motion.div>
))}
</div>
),
[currentCategory, categories, categoryImages]
);
}, [currentCategory, handleCategoryChange]);
return (
<Section background="white" id="categories">
<div
ref={categoriesSectionRef}
className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]"
>
{/* Left Column - Categories List */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-100px" }}
variants={fadeInLeft}
className="flex flex-col justify-center h-full bg-white px-4 md:px-8 py-6 md:py-12 order-2 lg:order-1"
>
<ul className="space-y-4 md:space-y-6">{categoryButtons}</ul>
</motion.div>
<LazyMotion features={domAnimation}>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]">
{/* Left */}
<div className="flex flex-col justify-center h-full bg-white px-4 md:px-8 py-6 md:py-12 order-2 lg:order-1">
<ul className="space-y-4 md:space-y-6">{categoryButtons}</ul>
</div>
{/* Right Column - Large Category Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-100px" }}
variants={fadeInRight}
className="relative hidden lg:flex items-center justify-center h-full min-h-[300px] md:min-h-[400px] bg-white order-1 lg:order-2 p-4"
>
{categoryImageComponent}
</motion.div>
</div>
{/* Right (render ONLY active image) */}
<div className="relative hidden lg:flex items-center justify-center h-full min-h-[300px] md:min-h-[400px] bg-white order-1 lg:order-2 p-4">
<div className="relative w-full h-full overflow-hidden rounded-2xl">
<m.div
key={currentCategory}
initial={{ opacity: 0, scale: 0.985 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.18, ease: [0.23, 1, 0.32, 1] }}
className="absolute inset-0"
>
<Image
src={categoryImages[currentCategory]}
alt={categories[currentCategory]}
layout="fullWidth"
className="w-full h-full object-cover"
draggable={false}
/>
</m.div>
</div>
</div>
</div>
</LazyMotion>
</Section>
);
}
+3 -5
View File
@@ -1,7 +1,6 @@
"use client";
import { useEffect, useRef } from "react";
import { motion } from "framer-motion";
import Image from "next/image";
import { Image } from "@unpic/react";
import Section from "./Section";
import { useInstagram } from "@/hooks/useInstagram";
@@ -245,10 +244,9 @@ export default function SocialMedia() {
<Image
src={post.mediaUrl}
alt={post.caption || "Instagram post"}
fill
className="object-cover img-premium"
layout="fullWidth"
className="w-full h-full object-cover img-premium"
draggable={false}
quality={85}
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/bst/bs1.svg";
+104
View File
@@ -0,0 +1,104 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './app/__root'
import { Route as AboutRouteImport } from './app/about'
import { Route as IndexRouteImport } from './app/index'
import { Route as ApiInstagramRouteImport } from './app/api/instagram'
const AboutRoute = AboutRouteImport.update({
id: '/about',
path: '/about',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const ApiInstagramRoute = ApiInstagramRouteImport.update({
id: '/api/instagram',
path: '/api/instagram',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/api/instagram': typeof ApiInstagramRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/api/instagram': typeof ApiInstagramRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/about': typeof AboutRoute
'/api/instagram': typeof ApiInstagramRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about' | '/api/instagram'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/about' | '/api/instagram'
id: '__root__' | '/' | '/about' | '/api/instagram'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
ApiInstagramRoute: typeof ApiInstagramRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/about': {
id: '/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AboutRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/api/instagram': {
id: '/api/instagram'
path: '/api/instagram'
fullPath: '/api/instagram'
preLoaderRoute: typeof ApiInstagramRouteImport
parentRoute: typeof rootRouteImport
}
}
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
ApiInstagramRoute: ApiInstagramRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
}
}
+22
View File
@@ -0,0 +1,22 @@
import { createRouter as createTanStackRouter } from '@tanstack/react-router';
// Import the generated route tree
import { routeTree } from './routeTree.gen';
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
});
return router;
}
export function getRouter() {
return createRouter();
}
// Register the router instance for type safety
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>;
}
}
+26
View File
@@ -0,0 +1,26 @@
import { defineConfig } from 'vite';
import { tanstackStart } from '@tanstack/react-start/plugin/vite';
import viteReact from '@vitejs/plugin-react';
import { nitro } from 'nitro/vite';
export default defineConfig({
server: {
port: 3000,
},
resolve: {
// Enables Vite to resolve imports using path aliases like in Next.js
alias: {
'@': '/src',
},
},
plugins: [
tanstackStart({
srcDirectory: 'src', // Points to the src directory
router: {
routesDirectory: 'app', // Maps the router to the app directory
},
}),
viteReact(),
nitro(),
],
});
+1721 -2591
View File
File diff suppressed because it is too large. Load diff