Initial commit
This commit is contained in:
1
.env.production
Normal file
1
.env.production
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DISABLE_ESLINT_PLUGIN=true
|
||||||
83
.gitea/workflows/build.yml
Normal file
83
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
branch:
|
||||||
|
description: 'Branch to build'
|
||||||
|
required: true
|
||||||
|
default: 'main'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout branch
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ gitea.event.inputs.branch }}
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
|
||||||
|
- name: Cache node_modules
|
||||||
|
id: cache-node-modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-modules-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -d node_modules ] && [ "${{ steps.cache-node-modules.outputs.cache-hit }}" == "true" ]; then
|
||||||
|
echo "Cache hit, verifying dependencies..."
|
||||||
|
npm ci --prefer-offline --no-audit --silent 2>&1 | tee install.log || npm install --no-audit --silent 2>&1 | tee install.log
|
||||||
|
else
|
||||||
|
echo "Cache miss, installing dependencies..."
|
||||||
|
npm ci --prefer-offline --no-audit --silent 2>&1 | tee install.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build (react-scripts build)
|
||||||
|
env:
|
||||||
|
CI: 'false'
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
npm run build 2>&1 | tee build.log
|
||||||
|
timeout-minutes: 5
|
||||||
|
|
||||||
|
- name: Save node_modules cache
|
||||||
|
if: always()
|
||||||
|
uses: actions/cache@v3
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Verify build folder exists
|
||||||
|
run: test -d build || (echo "No build folder. Check build logs above."; exit 1)
|
||||||
|
|
||||||
|
- name: Upload logs (always)
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-logs
|
||||||
|
path: |
|
||||||
|
install.log
|
||||||
|
build.log
|
||||||
|
npm-debug.log*
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
- name: Build completed
|
||||||
|
if: success()
|
||||||
|
run: echo "Build completed successfully"
|
||||||
41
package.json
Normal file
41
package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "bulls-bikes-website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-scripts": "^5.0.1",
|
||||||
|
"lucide-react": "^0.400.0",
|
||||||
|
"framer-motion": "^11.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"postcss": "^8.4.0",
|
||||||
|
"autoprefixer": "^10.4.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-Black-1767007286170.otf
Normal file
1
public/images/SourceSansPro-Black-1767007286170.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-Bold-1767007286085.otf
Normal file
1
public/images/SourceSansPro-Bold-1767007286085.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-ExtraLight-1767007286087.otf
Normal file
1
public/images/SourceSansPro-ExtraLight-1767007286087.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-Light-1767007286119.otf
Normal file
1
public/images/SourceSansPro-Light-1767007286119.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-Regular-1767007286089.otf
Normal file
1
public/images/SourceSansPro-Regular-1767007286089.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/SourceSansPro-Semibold-1767007286116.otf
Normal file
1
public/images/SourceSansPro-Semibold-1767007286116.otf
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525005009945-1767007285740.png
Normal file
1
public/images/ZEG_525005009945-1767007285740.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525005309941-1767007285825.png
Normal file
1
public/images/ZEG_525005309941-1767007285825.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525803760447_main-1767007285823.png
Normal file
1
public/images/ZEG_525803760447_main-1767007285823.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525803790447_main-1767007285674.png
Normal file
1
public/images/ZEG_525803790447_main-1767007285674.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525900040644-1767007285720.png
Normal file
1
public/images/ZEG_525900040644-1767007285720.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525900240841-1767007286110.png
Normal file
1
public/images/ZEG_525900240841-1767007286110.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525900250841-1767007285893.png
Normal file
1
public/images/ZEG_525900250841-1767007285893.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525907650741-1767007285715.png
Normal file
1
public/images/ZEG_525907650741-1767007285715.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_525907850741-1767007285785.png
Normal file
1
public/images/ZEG_525907850741-1767007285785.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526502829945_main-1767007285787.png
Normal file
1
public/images/ZEG_526502829945_main-1767007285787.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526502839945_main-1767007285780.png
Normal file
1
public/images/ZEG_526502839945_main-1767007285780.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526502849945_main-1767007285823.png
Normal file
1
public/images/ZEG_526502849945_main-1767007285823.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526801000737_main-1767007285744.png
Normal file
1
public/images/ZEG_526801000737_main-1767007285744.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526801130636_main-1767007285759.png
Normal file
1
public/images/ZEG_526801130636_main-1767007285759.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ZEG_526801140636_main-1767007285741.png
Normal file
1
public/images/ZEG_526801140636_main-1767007285741.png
Normal file
File diff suppressed because one or more lines are too long
1
public/images/at-1767007285888.svg
Normal file
1
public/images/at-1767007285888.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWF0IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTAgMTcwLjdoNTEydjE3MC42SDB6Ii8+CiAgPHBhdGggZmlsbD0iI2M4MTAyZSIgZD0iTTAgMGg1MTJ2MTcwLjdIMHptMCAzNDEuM2g1MTJWNTEySDB6Ii8+Cjwvc3ZnPgo=
|
||||||
1
public/images/be-1767007285986.svg
Normal file
1
public/images/be-1767007285986.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWJlIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utd2lkdGg9IjFwdCI+CiAgICA8cGF0aCBmaWxsPSIjMDAwMDAxIiBkPSJNMCAwaDE3MC43djUxMkgweiIvPgogICAgPHBhdGggZmlsbD0iI2ZmZDkwYyIgZD0iTTE3MC43IDBoMTcwLjZ2NTEySDE3MC43eiIvPgogICAgPHBhdGggZmlsbD0iI2YzMTgzMCIgZD0iTTM0MS4zIDBINTEydjUxMkgzNDEuM3oiLz4KICA8L2c+Cjwvc3ZnPgo=
|
||||||
1
public/images/bulls_typo-1767007286156.svg
Normal file
1
public/images/bulls_typo-1767007286156.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMzUiIGhlaWdodD0iMTAiIHZpZXdCb3g9IjAgMCAxMzUgMTAiPgogIDxwYXRoIGQ9Ik02Ny43ODIxMjUsMCBMNjQuMjc5OTY0OCw4LjAxMjcwMzEgTDc5LDguMDEyNzAzMSBMNzguMTIwMzU2MywxMCBMNTYsMTAgTDYwLjE0NjEzNzksMC40MzE5MDU0NjUgQzYwLjI1OTg2NzIsMC4xNjk1NzE2NCA2MC41MTY5MzA3LDAgNjAuODAwOTYwOSwwIEw2MC44MDA5NjA5LDAgTDY3Ljc4MjEyNSwwIFogTTQwLjc3ODIwNjQsMCBDNDAuNzc4MjA2NCwwIDM4LjA5MDEyNDQsNi4xODYwNDUxNCAzNy43MzQ1ODcxLDcuMDA3NTYyMzMgQzM3LjM3ODE4MTksNy44MjkwNzk1MiAzNy40Nzc5ODY5LDguMDE0MDAyMTMgMzguNzUzNzU1NSw4LjAxNDAwMjEzIEwzOC43NTM3NTU1LDguMDE0MDAyMTMgTDQ2LjY5NDE4NTMsOC4wMTIyMjk3MSBDNDcuOTIxMDYzOCw4LjAxNTE4Mzc0IDQ4LjIwODYxOCw3LjgwOTg3ODI5IDQ4LjU0Nzk1NTEsNy4wMzA2MDM4IEM0OC43MzcxNTA3LDYuNTk1NDc0NDIgNTAuNzYxODkwOSwxLjg4OTk5MTczIDUxLjM4OTkzOTMsMC40Mjk4MTIxMjMgQzUxLjUwMjQ3MzEsMC4xNjgzODAwMDcgNTEuNzU1NjAxOCwwLjAwMDI5NTQwMzUyMSA1Mi4wMzUzNDUxLDAuMDAwMjk1NDAzNTIxIEw1Mi4wMzUzNDUxLDAuMDAwMjk1NDAzNTIxIEw1OSwwIEM1OSwwIDU3Ljg2NzcxOTIsMi43MTM4NzIxNSA1Ni4zMzE1ODk4LDYuMzMwNDk3NDYgQzU0Ljc5NDg4MTcsOS45NDcxMjI3NyA1NC42NTM0MTg5LDEwIDQ5LjY1NjUxNDEsMTAgTDQ5LjY1NjUxNDEsMTAgTDMzLjk1Njc1LDEwIEMyOS45NDgwNTk2LDEwIDI5LjM2NjI5NzUsOS42NTY3NDExMSAzMC41MzMwMDM4LDYuODE0NjYzODMgTDMwLjUzMzAwMzgsNi44MTQ2NjM4MyBMMzMuMjY5Mzk3MiwwLjQzMTI4OTE0MSBDMzMuMzgxNjQxNiwwLjE2OTI2NjIxOCAzMy42MzUwNTk2LDAgMzMuOTE1MzgxNiwwIEwzMy45MTUzODE2LDAgWiBNMTM1LDAgTDEzNC4xNDY4MywxLjk4Nzg4Nzc0IEwxMTQuMzk5NDU4LDEuOTg3ODg3NzQgQzExNC4xMjQyMjMsMS45ODc4ODc3NCAxMTMuODYyNzIsMi4xMzQ3MTE5NiAxMTMuNzg0NDE2LDIuMzMzMjM0ODYgTDExMy43ODQ0MTYsMi4zMzMyMzQ4NiBMMTEzLjEwMzM0MSwzLjk3NjA3MDkgTDEzMS43NjYxMzYsMy45NzYwNzA5IEMxMzMuMTcyMTEzLDMuOTc2MDcwOSAxMzIuOTM1NzM4LDQuODczMjY0NCAxMzIuODQ2MDM5LDUuMDc0NDQ2MDkgTDEzMi44NDYwMzksNS4wNzQ0NDYwOSBMMTMxLjM5NTY1LDguNDM1NzQ1OTQgQzEzMS4xNTIyNjMsOC45MTM0NDE2NSAxMzAuNTMzNDIzLDkuOTk4MjI3NDcgMTI5LjA0NTA1MSw5Ljk5ODIyNzQ3IEwxMjkuMDQ1MDUxLDkuOTk4MjI3NDcgTDEwMywxMCBMMTAzLjg5NjQxMyw4LjAxMjcwMzEgTDEyMy41MjE2NTMsOC4wMTI3MDMxIEMxMjMuODE4MjE4LDguMDEyNzAzMSAxMjQuMTAwMTczLDcuODU0MzU3NDYgMTI0LjE4MzczNiw3LjY0MDQ3MjY3IEwxMjQuMTgzNzM2LDcuNjQwNDcyNjcgTDEyNC43Nzg5MSw2LjM3NzU0ODAxIEMxMjQuODY3MTQ5LDYuMTYwMTE4MTcgMTI0LjY3NDAxNyw1Ljk2MzM2NzggMTI0LjM3MjE5Myw1Ljk2MzM2NzggTDEyNC4zNzIxOTMsNS45NjMzNjc4IEwxMDYuMjc4ODYsNS45NjMzNjc4IEMxMDQuODk2ODQyLDUuOTYzMzY3OCAxMDUuMTE4MzE1LDUuMTE0OTE4NzYgMTA1LjM0MjcxMSw0LjYxODAyMDY4IEMxMDUuNTY1NjQ1LDQuMTIxMTIyNiAxMDYuNTM2NTY0LDEuNzA2OTQyMzkgMTA2Ljc3OTM2NywxLjIyODM2MDQxIEMxMDcuMDIyNDYxLDAuNzUwNjY0Njk3IDEwNy41NDQyOTcsMCAxMDkuMDMyNjcsMCBMMTA5LjAzMjY3LDAgTDEzNSwwIFogTTMyLDAgTDMwLjk2ODMwNzksMi4zODM2MTEwMSBDMzAuNzkzNDM1LDIuNzQ5MDI1MTcgMzAuMzk4OTQ3MywzLjEzNTQxMjk3IDI5Ljc2NDY2NzIsMy4yNzc1MDIwNyBMMjkuNzY0NjY3MiwzLjI3NzUwMjA3IEwyNi40MDY2OTY2LDMuOTc1ODM1OTkgTDI5LjgyMTEwNjEsNC45Njg5ODI2MyBDMjkuODU1OTA1Myw0Ljk4NzAwMjI1IDI5Ljg2ODE4NzMsNS4wMjk4MzU3NiAyOS44NTAzNDkxLDUuMDcxMTkyMjUgQzI5LjM4MDEyMjEsNi4xNjMyOTkwNyAyOC44NzA0MTcxLDcuMzQyODQ1MzMgMjguNzg2NzgyMiw3LjUyMTg1OTg2IEMyNy45NTQ4MTk2LDkuMzAzNDM4NSAyNy4zNzU4MDg4LDkuODAzODUyMDYgMjUuMzk5Mjc2Miw5Ljk0NDc1OTU0IEMyNC42NzE0MTg2LDkuOTk2NDU1MTYgMjMuNzU2MTEzNiwxMCAyMi41NjU5MjQ2LDEwIEMyMi41MDUwNjc1LDEwIDIyLjQ0MjQwMTIsOS45OTk5OTk5MSAyMi4zNzc5NzgxLDkuOTk5OTk5NzQgTDAsOS45OTk0MDkxOSBMMy41ODIyNjQxNCwxLjc1OTEyNzk3IEM0LjAzMTQzNjIsMC43MjU4MDY0NTIgNS4wODcxMDc1LDAgNi4xNDA3MzE4MSwwIEw2LjE0MDczMTgxLDAgTDMyLDAgWiBNOTEuNzgyMjY3OSwwIEw4OC4yODAxNTI0LDguMDEyNzAzMSBMMTAzLDguMDEyNzAzMSBMMTAyLjEyMDA3NCwxMCBMODAsMTAgTDg0LjE0NjM3ODEsMC40MzE5MDU0NjUgQzg0LjI1OTgxMjksMC4xNjk1NzE2NCA4NC41MTY4NzMxLDAgODQuODAwODk5NywwIEw4NC44MDA4OTk3LDAgTDkxLjc4MjI2NzksMCBaIE0yMS45MDAzNTQ2LDUuOTYzNjA2MjkgTDkuMjIyMDYzODIsNS45NjM2MDYyOSBMOC4zMjU0NzQyOCw4LjAxMjUyNTExIEwxOS41NTk3NDcsOC4wMTIyMjk3MSBDMjAuNzk5OTQxNSw4LjAxNTE4Mzc0IDIxLjA5MDkwOTEsNy44MDk1ODI4OSAyMS40MzM2MzY3LDcuMDMwMzA4NCBDMjEuNTMzMDYyOCw2LjgwNDYyMDExIDIxLjM1MzgwMzQsNy4yMTM0NTg1OCAyMS45MDAzNTQ2LDUuOTYzNjA2MjkgTDIxLjkwMDM1NDYsNS45NjM2MDYyOSBaIE0yMy4wMTEyOTUxLDEuOTg3NzcwMjkgTDExLjAxOTMzNjksMS45ODc3NzAyOSBDMTAuOTIyNTQyNywyLjIwODE0MTMyIDEwLjEyMDcwMDQsMy45NzU4MzU5OSAxMC4xMjA3MDA0LDMuOTc1ODM1OTkgTDEwLjEyMDcwMDQsMy45NzU4MzU5OSBMMjIuNzgwNTY4LDMuOTc1ODM1OTkgQzIyLjc4MDU2OCwzLjk3NTgzNTk5IDIzLjE3OTczNDYsMy4wOTExMDI0NSAyMy4zODM4NTA2LDIuNjM0NzA0MDEgQzIzLjUzMDM1NzksMi4zMDcxMDE1IDIzLjM0NjQxOTYsMS45ODgwNjU3IDIzLjAxMTI5NTEsMS45ODc3NzAyOSBMMjMuMDExMjk1MSwxLjk4Nzc3MDI5IFoiLz4KPC9zdmc+Cg==
|
||||||
1
public/images/ch-1767007285889.svg
Normal file
1
public/images/ch-1767007285889.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWNoIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utd2lkdGg9IjFwdCI+CiAgICA8cGF0aCBmaWxsPSJyZWQiIGQ9Ik0wIDBoNTEydjUxMkgweiIvPgogICAgPGcgZmlsbD0iI2ZmZiI+CiAgICAgIDxwYXRoIGQ9Ik05NiAyMDhoMzIwdjk2SDk2eiIvPgogICAgICA8cGF0aCBkPSJNMjA4IDk2aDk2djMyMGgtOTZ6Ii8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K
|
||||||
1
public/images/de-1767007285889.svg
Normal file
1
public/images/de-1767007285889.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWRlIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPHBhdGggZmlsbD0iI2ZjMCIgZD0iTTAgMzQxLjNoNTEyVjUxMkgweiIvPgogIDxwYXRoIGZpbGw9IiMwMDAwMDEiIGQ9Ik0wIDBoNTEydjE3MC43SDB6Ii8+CiAgPHBhdGggZmlsbD0icmVkIiBkPSJNMCAxNzAuN2g1MTJ2MTcwLjZIMHoiLz4KPC9zdmc+Cg==
|
||||||
1
public/images/fr-1767007285892.svg
Normal file
1
public/images/fr-1767007285892.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWZyIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTAgMGg1MTJ2NTEySDB6Ii8+CiAgPHBhdGggZmlsbD0iIzAwMDA5MSIgZD0iTTAgMGgxNzAuN3Y1MTJIMHoiLz4KICA8cGF0aCBmaWxsPSIjZTEwMDBmIiBkPSJNMzQxLjMgMEg1MTJ2NTEySDM0MS4zeiIvPgo8L3N2Zz4K
|
||||||
1
public/images/gb-1767007285890.svg
Normal file
1
public/images/gb-1767007285890.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWdiIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPHBhdGggZmlsbD0iIzAxMjE2OSIgZD0iTTAgMGg1MTJ2NTEySDB6Ii8+CiAgPHBhdGggZmlsbD0iI0ZGRiIgZD0iTTUxMiAwdjY0TDMyMiAyNTZsMTkwIDE4N3Y2OWgtNjdMMjU0IDMyNCA2OCA1MTJIMHYtNjhsMTg2LTE4N0wwIDc0VjBoNjJsMTkyIDE4OEw0NDAgMHoiLz4KICA8cGF0aCBmaWxsPSIjQzgxMDJFIiBkPSJtMTg0IDMyNCAxMSAzNEw0MiA1MTJIMHYtM3ptMTI0LTEyIDU0IDggMTUwIDE0N3Y0NXpNNTEyIDAgMzIwIDE5NmwtNC00NEw0NjYgMHpNMCAxbDE5MyAxODktNTktOEwwIDQ5eiIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik0xNzYgMHY1MTJoMTYwVjB6TTAgMTc2djE2MGg1MTJWMTc2eiIvPgogIDxwYXRoIGZpbGw9IiNDODEwMkUiIGQ9Ik0wIDIwOHY5Nmg1MTJ2LTk2ek0yMDggMHY1MTJoOTZWMHoiLz4KPC9zdmc+Cg==
|
||||||
1
public/images/ico_facebook-1767007286258.svg
Normal file
1
public/images/ico_facebook-1767007286258.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMSIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDExIDIwIj4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMS4zNjYwOCw2LjQ1Mjg0IEwyMS4zNjYwOCw5LjQ5ODg0IEwxOS41NTQ0OCw5LjQ5ODg0IEMxOC44OTI4OCw5LjQ5ODg0IDE4LjQ0Njg4LDkuNjM3NjQgMTguMjE2MDgsOS45MTQ0NCBDMTcuOTg1MjgsMTAuMTkxMjQgMTcuODcwMDgsMTAuNjA2ODQgMTcuODcwMDgsMTEuMTYwNDQgTDE3Ljg3MDA4LDEzLjM0MTI0IEwyMS4yNTA4OCwxMy4zNDEyNCBMMjAuODAwODgsMTYuNzU2ODQgTDE3Ljg3MDA4LDE2Ljc1Njg0IEwxNy44NzAwOCwyNS41MTQ0NCBMMTQuMzM5MjgsMjUuNTE0NDQgTDE0LjMzOTI4LDE2Ljc1Njg0IEwxMS4zOTY4OCwxNi43NTY4NCBMMTEuMzk2ODgsMTMuMzQxMjQgTDE0LjMzOTI4LDEzLjM0MTI0IEwxNC4zMzkyOCwxMC44MjYwNCBDMTQuMzM5MjgsOS4zOTUyNCAxNC43MzkyOCw4LjI4NTY0IDE1LjUzOTI4LDcuNDk3MjQgQzE2LjMzOTI4LDYuNzA4NDQgMTcuNDA0NDgsNi4zMTQ0NCAxOC43MzUyOCw2LjMxNDQ0IEMxOS44NjYwOCw2LjMxNDQ0IDIwLjc0Mjg4LDYuMzYwNDQgMjEuMzY2MDgsNi40NTI4NCBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTEgLTYpIi8+Cjwvc3ZnPgo=
|
||||||
1
public/images/ico_instagram-1767007286157.svg
Normal file
1
public/images/ico_instagram-1767007286157.svg
Normal file
File diff suppressed because one or more lines are too long
1
public/images/ico_youtube-1767007286275.svg
Normal file
1
public/images/ico_youtube-1767007286275.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMCAwIDIwIDE0Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy44ODM4MzY3LDE4LjU1OTE4MzcgTDE5LjE3NTI2NTMsMTUuODI2MTIyNCBMMTMuODgzODM2NywxMy4wNjA0MDgyIEwxMy44ODM4MzY3LDE4LjU1OTE4MzcgWiBNMTUuOTA2Mjg1Nyw5LjEzNTUxMDIgQzE3LjEzMDc3NTUsOS4xMzU1MTAyIDE4LjMxMzIyNDUsOS4xNTE4MzY3MyAxOS40NTQwNDA4LDkuMTg0ODk3OTYgQzIwLjU5NDQ0OSw5LjIxNzU1MTAyIDIxLjQzMDc3NTUsOS4yNTIyNDQ5IDIxLjk2MzAyMDQsOS4yODg1NzE0MyBMMjIuNzYwOTc5Niw5LjMzMjI0NDkgQzIyLjc2ODMyNjUsOS4zMzIyNDQ5IDIyLjgyOTk1OTIsOS4zMzc5NTkxOCAyMi45NDY2OTM5LDkuMzQ4NTcxNDMgQzIzLjA2MzQyODYsOS4zNTk1OTE4NCAyMy4xNDcxMDIsOS4zNzA2MTIyNCAyMy4xOTgxMjI0LDkuMzgxNjMyNjUgQzIzLjI0OTE0MjksOS4zOTIyNDQ5IDIzLjMzNDg1NzEsOS40MDg5Nzk1OSAyMy40NTUyNjUzLDkuNDMwNjEyMjQgQzIzLjU3NTI2NTMsOS40NTI2NTMwNiAyMy42NzkzNDY5LDkuNDgxNjMyNjUgMjMuNzY2NjkzOSw5LjUxNzk1OTE4IEMyMy44NTQwNDA4LDkuNTU0NjkzODggMjMuOTU2MDgxNiw5LjYwMjA0MDgyIDI0LjA3MjgxNjMsOS42NjA0MDgxNiBDMjQuMTg5NTUxLDkuNzE4MzY3MzUgMjQuMzAyMjA0MSw5Ljc4OTc5NTkyIDI0LjQxMTU5MTgsOS44NzM0NjkzOSBDMjQuNTIwOTc5Niw5Ljk1NzE0Mjg2IDI0LjYyNjY5MzksMTAuMDUzODc3NiAyNC43Mjg3MzQ3LDEwLjE2MzI2NTMgQzI0Ljc3MjQwODIsMTAuMjA2OTM4OCAyNC44Mjg3MzQ3LDEwLjI3NDI4NTcgMjQuODk4MTIyNCwxMC4zNjUzMDYxIEMyNC45Njc1MTAyLDEwLjQ1NjMyNjUgMjUuMDczMjI0NSwxMC42Njk3OTU5IDI1LjIxNTI2NTMsMTEuMDA0ODk4IEMyNS4zNTczMDYxLDExLjM0IDI1LjQ1NDA0MDgsMTEuNzA4MTYzMyAyNS41MDUwNjEyLDEyLjEwODk3OTYgQzI1LjU2MzAyMDQsMTIuNTc1NTEwMiAyNS42MDg3MzQ3LDEzLjA3MzA2MTIgMjUuNjQxMzg3OCwxMy42MDEyMjQ1IEMyNS42NzQ0NDksMTQuMTI5Nzk1OSAyNS42OTQ0NDksMTQuNTQzMjY1MyAyNS43MDE3OTU5LDE0Ljg0MjA0MDggTDI1LjcwMTc5NTksMTYuNzY2MTIyNCBDMjUuNzA5MTQyOSwxNy44MjMyNjUzIDI1LjY0MzQyODYsMTguODggMjUuNTA1MDYxMiwxOS45MzY3MzQ3IEMyNS40NTQwNDA4LDIwLjMzNzU1MSAyNS4zNjI2MTIyLDIwLjcgMjUuMjMxNTkxOCwyMS4wMjQ0ODk4IEMyNS4xMDA1NzE0LDIxLjM0ODU3MTQgMjQuOTgzODM2NywyMS41NzMwNjEyIDI0Ljg4MTc5NTksMjEuNjk2NzM0NyBMMjQuNzI4NzM0NywyMS44ODI0NDkgQzI0LjYyNjY5MzksMjEuOTkxODM2NyAyNC41MjA5Nzk2LDIyLjA4ODU3MTQgMjQuNDExNTkxOCwyMi4xNzIyNDQ5IEMyNC4zMDIyMDQxLDIyLjI1NjMyNjUgMjQuMTg5NTUxLDIyLjMyNTMwNjEgMjQuMDcyODE2MywyMi4zOCBDMjMuOTU2MDgxNiwyMi40MzQ2OTM5IDIzLjg1NDA0MDgsMjIuNDgwNDA4MiAyMy43NjY2OTM5LDIyLjUxNjczNDcgQzIzLjY3OTM0NjksMjIuNTUzMDYxMiAyMy41NzUyNjUzLDIyLjU4MjQ0OSAyMy40NTUyNjUzLDIyLjYwNDA4MTYgQzIzLjMzNDg1NzEsMjIuNjI2MTIyNCAyMy4yNDc1MTAyLDIyLjY0MjQ0OSAyMy4xOTI4MTYzLDIyLjY1MzQ2OTQgQzIzLjEzODEyMjQsMjIuNjY0MDgxNiAyMy4wNTQwNDA4LDIyLjY3NTEwMiAyMi45NDEzODc4LDIyLjY4NjEyMjQgQzIyLjgyODMyNjUsMjIuNjk3MTQyOSAyMi43NjgzMjY1LDIyLjcwMjQ0OSAyMi43NjA5Nzk2LDIyLjcwMjQ0OSBDMjAuOTMxNTkxOCwyMi44NDEyMjQ1IDE4LjY0NjY5MzksMjIuOTEwMjA0MSAxNS45MDYyODU3LDIyLjkxMDIwNDEgQzE0LjM5NzcxNDMsMjIuODk1NTEwMiAxMy4wODc1MTAyLDIyLjg3MTgzNjcgMTEuOTc2MDgxNiwyMi44MzkxODM3IEMxMC44NjQ2NTMxLDIyLjgwNjUzMDYgMTAuMTM0MDQwOCwyMi43NzkxODM3IDkuNzg0MjQ0OSwyMi43NTcxNDI5IEw5LjI0ODMyNjUzLDIyLjcxMzQ2OTQgTDguODU0ODU3MTQsMjIuNjY5Nzk1OSBDOC41OTI0MDgxNiwyMi42MzM0Njk0IDguMzk0MDQwODIsMjIuNTk2NzM0NyA4LjI1OTM0Njk0LDIyLjU2MDQwODIgQzguMTI0MjQ0OSwyMi41MjQwODE2IDcuOTM4NTMwNjEsMjIuNDQ3MzQ2OSA3LjcwMTc5NTkyLDIyLjMzMTAyMDQgQzcuNDY0NjUzMDYsMjIuMjE0Mjg1NyA3LjI1ODkzODc4LDIyLjA2NDg5OCA3LjA4MzgzNjczLDIxLjg4MjQ0OSBDNy4wNDAxNjMyNywyMS44Mzg3NzU1IDYuOTgzODM2NzMsMjEuNzcxNDI4NiA2LjkxNDQ0ODk4LDIxLjY4MDQwODIgQzYuODQ1MDYxMjIsMjEuNTg5Mzg3OCA2LjczOTc1NTEsMjEuMzc1OTE4NCA2LjU5NzMwNjEyLDIxLjA0MDgxNjMgQzYuNDU1MjY1MzEsMjAuNzA1NzE0MyA2LjM1ODUzMDYxLDIwLjMzNzU1MSA2LjMwNzkxODM3LDE5LjkzNjczNDcgQzYuMjQ5NTUxMDIsMTkuNDcwMjA0MSA2LjIwMzgzNjczLDE4Ljk3MjY1MzEgNi4xNzExODM2NywxOC40NDQ0ODk4IEM2LjEzODEyMjQ1LDE3LjkxNTkxODQgNi4xMTgxMjI0NSwxNy41MDI0NDkgNi4xMTA3NzU1MSwxNy4yMDM2NzM1IEw2LjExMDc3NTUxLDE1LjI3OTU5MTggQzYuMTAzODM2NzMsMTQuMjIyODU3MSA2LjE2OTE0Mjg2LDEzLjE2NTcxNDMgNi4zMDc5MTgzNywxMi4xMDg5Nzk2IEM2LjM1ODUzMDYxLDExLjcwODE2MzMgNi40NDk5NTkxOCwxMS4zNDU3MTQzIDYuNTgwOTc5NTksMTEuMDIxMjI0NSBDNi43MTI0MDgxNiwxMC42OTcxNDI5IDYuODI4NzM0NjksMTAuNDczMDYxMiA2LjkzMDc3NTUxLDEwLjM0ODk3OTYgTDcuMDgzODM2NzMsMTAuMTYzMjY1MyBDNy4xODU4Nzc1NSwxMC4wNTM4Nzc2IDcuMjkxNTkxODQsOS45NTcxNDI4NiA3LjQwMDk3OTU5LDkuODczNDY5MzkgQzcuNTEwMzY3MzUsOS43ODk3OTU5MiA3LjYyMzQyODU3LDkuNzE4MzY3MzUgNy43Mzk3NTUxLDkuNjYwNDA4MTYgQzcuODU2NDg5OCw5LjYwMjA0MDgyIDcuOTU4NTMwNjEsOS41NTQ2OTM4OCA4LjA0NTg3NzU1LDkuNTE3OTU5MTggQzguMTMzMjI0NDksOS40ODE2MzI2NSA4LjIzNzMwNjEyLDkuNDUyNjUzMDYgOC4zNTc3MTQyOSw5LjQzMDYxMjI0IEM4LjQ3NzcxNDI5LDkuNDA4OTc5NTkgOC41NjM0Mjg1Nyw5LjM5MjI0NDkgOC42MTQ0NDg5OCw5LjM4MTYzMjY1IEM4LjY2NTQ2OTM5LDkuMzcwNjEyMjQgOC43NDkxNDI4Niw5LjM1OTU5MTg0IDguODY1ODc3NTUsOS4zNDg1NzE0MyBDOC45ODI2MTIyNCw5LjMzNzk1OTE4IDkuMDQ0MjQ0OSw5LjMzMjI0NDkgOS4wNTE1OTE4NCw5LjMzMjI0NDkgQzEwLjg4MDk3OTYsOS4yMDEyMjQ0OSAxMy4xNjU4Nzc2LDkuMTM1NTEwMiAxNS45MDYyODU3LDkuMTM1NTEwMiBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNiAtOSkiLz4KPC9zdmc+Cg==
|
||||||
1
public/images/it-1767007285989.svg
Normal file
1
public/images/it-1767007285989.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWl0IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utd2lkdGg9IjFwdCI+CiAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz4KICAgIDxwYXRoIGZpbGw9IiMwMDkyNDYiIGQ9Ik0wIDBoMTcwLjd2NTEySDB6Ii8+CiAgICA8cGF0aCBmaWxsPSIjY2UyYjM3IiBkPSJNMzQxLjMgMEg1MTJ2NTEySDM0MS4zeiIvPgogIDwvZz4KPC9zdmc+Cg==
|
||||||
1
public/images/logo-desktop-1767007285623.svg
Normal file
1
public/images/logo-desktop-1767007285623.svg
Normal file
File diff suppressed because one or more lines are too long
1
public/images/logo-mobile-1767007285646.svg
Normal file
1
public/images/logo-mobile-1767007285646.svg
Normal file
File diff suppressed because one or more lines are too long
1
public/images/lt-1767007285987.svg
Normal file
1
public/images/lt-1767007285987.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLWx0IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utd2lkdGg9IjFwdCIgdHJhbnNmb3JtPSJzY2FsZSguNTEzMTQgMS4wMzIyKSI+CiAgICA8cmVjdCB3aWR0aD0iMTA2MyIgaGVpZ2h0PSI3MDguNyIgZmlsbD0iIzAwNmE0NCIgcng9IjAiIHJ5PSIwIiB0cmFuc2Zvcm09InNjYWxlKC45Mzg2NSAuNjk2ODYpIi8+CiAgICA8cmVjdCB3aWR0aD0iMTA2MyIgaGVpZ2h0PSIyMzYuMiIgeT0iNDc1LjYiIGZpbGw9IiNjMTI3MmQiIHJ4PSIwIiByeT0iMCIgdHJhbnNmb3JtPSJzY2FsZSguOTM4NjUgLjY5Njg2KSIvPgogICAgPHBhdGggZmlsbD0iI2ZkYjkxMyIgZD0iTTAgMGg5OTcuOHYxNjQuNkgweiIvPgogIDwvZz4KPC9zdmc+Cg==
|
||||||
1
public/images/nl-1767007285892.svg
Normal file
1
public/images/nl-1767007285892.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLW5sIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPHBhdGggZmlsbD0iI2FlMWMyOCIgZD0iTTAgMGg1MTJ2MTcwLjdIMHoiLz4KICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAxNzAuN2g1MTJ2MTcwLjZIMHoiLz4KICA8cGF0aCBmaWxsPSIjMjE0NjhiIiBkPSJNMCAzNDEuM2g1MTJWNTEySDB6Ii8+Cjwvc3ZnPgo=
|
||||||
1
public/images/pl-1767007285986.svg
Normal file
1
public/images/pl-1767007285986.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJmbGFnLWljb25zLXBsIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgPGcgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01MTIgNTEySDBWMGg1MTJ6Ii8+CiAgICA8cGF0aCBmaWxsPSIjZGMxNDNjIiBkPSJNNTEyIDUxMkgwVjI1Nmg1MTJ6Ii8+CiAgPC9nPgo8L3N2Zz4K
|
||||||
1232
public/index.html
Normal file
1232
public/index.html
Normal file
File diff suppressed because it is too large
Load Diff
24
src/App.js
Normal file
24
src/App.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Header from './components/Header';
|
||||||
|
import Hero from './components/Hero';
|
||||||
|
import TopEBikes from './components/TopEBikes';
|
||||||
|
import ProductSections from './components/ProductSections';
|
||||||
|
import Footer from './components/Footer';
|
||||||
|
import CookieConsent from './components/CookieConsent';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<Header />
|
||||||
|
<main>
|
||||||
|
<Hero />
|
||||||
|
<TopEBikes />
|
||||||
|
<ProductSections />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
<CookieConsent />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
49
src/components/CookieConsent.js
Normal file
49
src/components/CookieConsent.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Settings } from 'lucide-react';
|
||||||
|
|
||||||
|
const CookieConsent = () => {
|
||||||
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 shadow-lg z-50">
|
||||||
|
<div className="container mx-auto px-4 py-4">
|
||||||
|
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between space-y-4 lg:space-y-0">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Settings className="w-6 h-6 text-gray-600 mt-1 flex-shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-1">Privacy Settings</h3>
|
||||||
|
<p className="text-sm text-gray-600 leading-relaxed">
|
||||||
|
We use Cookies to offer a variety of services, to continuously improve them and to display advertisements based on your interests on our website, social media and partner websites. By clicking on "OK" you consent to the use of Cookies. You can view, change or revoke your Cookie settings at any time via the privacy policy page. You can find out more in the privacy policy.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3 w-full lg:w-auto">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsVisible(false)}
|
||||||
|
className="px-6 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||||
|
>
|
||||||
|
More
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsVisible(false)}
|
||||||
|
className="px-6 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||||
|
>
|
||||||
|
Deny
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsVisible(false)}
|
||||||
|
className="px-6 py-2 text-sm font-medium text-white bg-bulls-blue hover:bg-blue-600 rounded transition-colors duration-200"
|
||||||
|
>
|
||||||
|
Accept All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CookieConsent;
|
||||||
46
src/components/Footer.js
Normal file
46
src/components/Footer.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Facebook, Instagram, Youtube } from 'lucide-react';
|
||||||
|
|
||||||
|
const Footer = () => {
|
||||||
|
return (
|
||||||
|
<footer className="bg-bulls-light-gray py-12">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
ONLY DEALERS, NEVER ONLINE STORES | FIND YOUR DEALER
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-center space-x-6">
|
||||||
|
<Facebook className="w-6 h-6 text-gray-600 hover:text-bulls-blue cursor-pointer" />
|
||||||
|
<Instagram className="w-6 h-6 text-gray-600 hover:text-bulls-blue cursor-pointer" />
|
||||||
|
<Youtube className="w-6 h-6 text-gray-600 hover:text-bulls-blue cursor-pointer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-gray-300 pt-8">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
|
||||||
|
<div className="flex flex-wrap justify-center md:justify-start space-x-6 text-sm">
|
||||||
|
<a href="#" className="text-gray-600 hover:text-bulls-blue">IMPRINT</a>
|
||||||
|
<a href="#" className="text-gray-600 hover:text-bulls-blue">DATA PRIVACY NOTICE</a>
|
||||||
|
<a href="#" className="text-gray-600 hover:text-bulls-blue">COOKIES</a>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="w-8 h-6 bg-gray-300 rounded"></div>
|
||||||
|
<span className="text-sm text-gray-600">Deutschland</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 text-center text-xs text-gray-500">
|
||||||
|
<p className="mb-2">
|
||||||
|
* Prices are non-binding recommended retail prices including VAT.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Actual prices may vary and are determined by the respective BULLS dealer. Subject to technical changes, errors and omissions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
91
src/components/Header.js
Normal file
91
src/components/Header.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Search, Menu, X, MapPin, User } from 'lucide-react';
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="fixed top-0 left-0 right-0 z-50 bg-white/95 backdrop-blur-sm border-b border-gray-200">
|
||||||
|
{/* Top Bar */}
|
||||||
|
<div className="bg-gray-100 py-2">
|
||||||
|
<div className="container mx-auto px-4 flex justify-between items-center text-sm">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<button className="flex items-center space-x-1 text-gray-600 hover:text-bulls-blue">
|
||||||
|
<MapPin className="w-4 h-4" />
|
||||||
|
<span>Dealer search</span>
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center space-x-1 text-gray-600 hover:text-bulls-blue">
|
||||||
|
<User className="w-4 h-4" />
|
||||||
|
<span>About us</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<Search className="w-4 h-4 text-gray-600 hover:text-bulls-blue cursor-pointer" />
|
||||||
|
<div className="w-6 h-6 bg-gray-300 rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Navigation */}
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="flex items-center justify-between h-16">
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="w-16 h-8 bg-bulls-dark transform skew-x-12 flex items-center justify-center">
|
||||||
|
<span className="text-white font-bold text-lg transform -skew-x-12">BULLS</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Desktop Navigation */}
|
||||||
|
<nav className="hidden md:flex items-center space-x-8">
|
||||||
|
<div className="relative group">
|
||||||
|
<button className="flex items-center space-x-1 text-gray-700 hover:text-bulls-blue font-medium">
|
||||||
|
<span>E-BIKES</span>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="relative group">
|
||||||
|
<button className="flex items-center space-x-1 text-gray-700 hover:text-bulls-blue font-medium">
|
||||||
|
<span>BIKES</span>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="relative group">
|
||||||
|
<button className="flex items-center space-x-1 text-gray-700 hover:text-bulls-blue font-medium">
|
||||||
|
<span>SERVICE</span>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Mobile Menu Button */}
|
||||||
|
<button
|
||||||
|
className="md:hidden p-2"
|
||||||
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
|
>
|
||||||
|
{isMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
{isMenuOpen && (
|
||||||
|
<div className="md:hidden bg-white border-t border-gray-200">
|
||||||
|
<nav className="container mx-auto px-4 py-4 space-y-4">
|
||||||
|
<a href="#" className="block text-gray-700 hover:text-bulls-blue font-medium">E-BIKES</a>
|
||||||
|
<a href="#" className="block text-gray-700 hover:text-bulls-blue font-medium">BIKES</a>
|
||||||
|
<a href="#" className="block text-gray-700 hover:text-bulls-blue font-medium">SERVICE</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
70
src/components/Hero.js
Normal file
70
src/components/Hero.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
|
|
||||||
|
const fadeUpPreset = (delay = 0, duration = 1.2) => ({
|
||||||
|
initial: { opacity: 0, y: 20 },
|
||||||
|
whileInView: { opacity: 1, y: 0 },
|
||||||
|
viewport: { once: true, amount: 0.2 },
|
||||||
|
transition: { delay, duration, ease: "easeOut" }
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hero = () => {
|
||||||
|
const shouldReduce = useReducedMotion();
|
||||||
|
|
||||||
|
if (shouldReduce) {
|
||||||
|
return (
|
||||||
|
<section className="relative h-screen flex items-center justify-start bg-cover bg-center" style={{
|
||||||
|
backgroundImage: "linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('https://www.bulls-bikes.com/media/image/bulls-grinder-3-hero-2026.jpg')"
|
||||||
|
}}>
|
||||||
|
<div className="container mx-auto px-4 pt-24">
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<h1 className="text-5xl md:text-6xl lg:text-7xl font-bold text-white mb-4 text-shadow">
|
||||||
|
READY FOR 2026
|
||||||
|
</h1>
|
||||||
|
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-6 text-shadow">
|
||||||
|
ONE OF OUR HIGHLIGHTS:
|
||||||
|
</h2>
|
||||||
|
<h3 className="text-6xl md:text-7xl lg:text-8xl font-bold text-white text-shadow">
|
||||||
|
THE GRINDER 3
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.section
|
||||||
|
{...fadeUpPreset(0.1, 1.0)}
|
||||||
|
className="relative h-screen flex items-center justify-start bg-cover bg-center"
|
||||||
|
style={{
|
||||||
|
backgroundImage: "linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('https://www.bulls-bikes.com/media/image/bulls-grinder-3-hero-2026.jpg')"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="container mx-auto px-4 pt-24">
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<motion.h1
|
||||||
|
{...fadeUpPreset(0.2, 1.0)}
|
||||||
|
className="text-5xl md:text-6xl lg:text-7xl font-bold text-white mb-4 text-shadow"
|
||||||
|
>
|
||||||
|
READY FOR 2026
|
||||||
|
</motion.h1>
|
||||||
|
<motion.h2
|
||||||
|
{...fadeUpPreset(0.4, 1.0)}
|
||||||
|
className="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-6 text-shadow"
|
||||||
|
>
|
||||||
|
ONE OF OUR HIGHLIGHTS:
|
||||||
|
</motion.h2>
|
||||||
|
<motion.h3
|
||||||
|
{...fadeUpPreset(0.6, 1.0)}
|
||||||
|
className="text-6xl md:text-7xl lg:text-8xl font-bold text-white text-shadow"
|
||||||
|
>
|
||||||
|
THE GRINDER 3
|
||||||
|
</motion.h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Hero;
|
||||||
98
src/components/ProductSections.js
Normal file
98
src/components/ProductSections.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
|
import { ArrowRight } from 'lucide-react';
|
||||||
|
|
||||||
|
const fadeUpPreset = (delay = 0, duration = 1.2) => ({
|
||||||
|
initial: { opacity: 0, y: 20 },
|
||||||
|
whileInView: { opacity: 1, y: 0 },
|
||||||
|
viewport: { once: true, amount: 0.2 },
|
||||||
|
transition: { delay, duration, ease: "easeOut" }
|
||||||
|
});
|
||||||
|
|
||||||
|
const ProductSections = () => {
|
||||||
|
const shouldReduce = useReducedMotion();
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'VUCA EVO',
|
||||||
|
subtitle: 'pinion',
|
||||||
|
description: 'MAXIMUM PERFORMANCE WITH A MINIMUM OF MAINTENANCE',
|
||||||
|
buttonText: 'SEE ALL MODELS',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-vuca-evo-pinion-section.jpg',
|
||||||
|
textColor: 'text-white',
|
||||||
|
buttonStyle: 'btn-secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'NO LIMITS!',
|
||||||
|
subtitle: '',
|
||||||
|
description: '',
|
||||||
|
buttonText: 'CHOOSE YOUR BIKE',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-no-limits-section.jpg',
|
||||||
|
textColor: 'text-white',
|
||||||
|
buttonStyle: 'btn-secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'MAXIMUM OFFROAD POWER',
|
||||||
|
subtitle: '',
|
||||||
|
description: '',
|
||||||
|
buttonText: 'CHOOSE YOUR E-BIKE',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-maximum-offroad-power-section.jpg',
|
||||||
|
textColor: 'text-white',
|
||||||
|
buttonStyle: 'btn-secondary'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const SectionCard = ({ section, index }) => {
|
||||||
|
const content = (
|
||||||
|
<div
|
||||||
|
className="relative h-96 md:h-[500px] bg-cover bg-center flex items-center justify-center text-center"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url('${section.image}')`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="max-w-2xl px-4">
|
||||||
|
{section.subtitle && (
|
||||||
|
<h3 className={`text-2xl md:text-3xl font-light ${section.textColor} mb-2 text-shadow`}>
|
||||||
|
{section.subtitle}
|
||||||
|
</h3>
|
||||||
|
)}
|
||||||
|
<h2 className={`text-4xl md:text-5xl lg:text-6xl font-bold ${section.textColor} mb-4 text-shadow`}>
|
||||||
|
{section.title}
|
||||||
|
</h2>
|
||||||
|
{section.description && (
|
||||||
|
<p className={`text-lg md:text-xl ${section.textColor} mb-8 text-shadow`}>
|
||||||
|
{section.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<button className={`${section.buttonStyle} inline-flex items-center space-x-2`}>
|
||||||
|
<span>{section.buttonText}</span>
|
||||||
|
<ArrowRight className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldReduce) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div {...fadeUpPreset(index * 0.2, 1.0)}>
|
||||||
|
{content}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-0">
|
||||||
|
{sections.map((section, index) => (
|
||||||
|
<SectionCard key={section.id} section={section} index={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductSections;
|
||||||
129
src/components/TopEBikes.js
Normal file
129
src/components/TopEBikes.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
|
import { Star } from 'lucide-react';
|
||||||
|
|
||||||
|
const fadeUpPreset = (delay = 0, duration = 1.2) => ({
|
||||||
|
initial: { opacity: 0, y: 20 },
|
||||||
|
whileInView: { opacity: 1, y: 0 },
|
||||||
|
viewport: { once: true, amount: 0.2 },
|
||||||
|
transition: { delay, duration, ease: "easeOut" }
|
||||||
|
});
|
||||||
|
|
||||||
|
const bikes = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'COPPERHEAD EVO 1',
|
||||||
|
price: 'from 3,199.00*',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-copperhead-evo-1-product.jpg',
|
||||||
|
rating: 5,
|
||||||
|
colors: ['#333', '#666', '#999']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'EVO CX 1',
|
||||||
|
price: '4,799.00*',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-evo-cx-1-product.jpg',
|
||||||
|
rating: 5,
|
||||||
|
colors: ['#333', '#666']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'GRINDER 3',
|
||||||
|
price: '4,499.00*',
|
||||||
|
image: 'https://www.bulls-bikes.com/media/image/bulls-grinder-3-product.jpg',
|
||||||
|
rating: 5,
|
||||||
|
colors: ['#333', '#666', '#999', '#ccc']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const TopEBikes = () => {
|
||||||
|
const shouldReduce = useReducedMotion();
|
||||||
|
|
||||||
|
const BikeCard = ({ bike, index }) => {
|
||||||
|
const content = (
|
||||||
|
<div className="bg-white rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
|
||||||
|
<div className="aspect-w-16 aspect-h-12 bg-gray-100">
|
||||||
|
<img
|
||||||
|
src={bike.image}
|
||||||
|
alt={bike.name}
|
||||||
|
className="w-full h-64 object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center mb-2">
|
||||||
|
{[...Array(bike.rating)].map((_, i) => (
|
||||||
|
<Star key={i} className="w-4 h-4 fill-yellow-400 text-yellow-400" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-bulls-dark mb-2">{bike.name}</h3>
|
||||||
|
<p className="text-lg font-semibold text-bulls-blue mb-4">{bike.price}</p>
|
||||||
|
<div className="flex space-x-2 mb-4">
|
||||||
|
{bike.colors.map((color, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="w-6 h-6 rounded-full border-2 border-gray-300"
|
||||||
|
style={{ backgroundColor: color }}
|
||||||
|
></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldReduce) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div {...fadeUpPreset(index * 0.1, 0.8)}>
|
||||||
|
{content}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sectionContent = (
|
||||||
|
<section className="py-16 bg-white">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h2 className="text-4xl font-bold text-bulls-dark mb-4">TOP E-BIKES</h2>
|
||||||
|
<div className="flex justify-center space-x-2 mb-8">
|
||||||
|
<div className="w-3 h-3 bg-bulls-blue rounded-full"></div>
|
||||||
|
<div className="w-3 h-3 bg-gray-300 rounded-full"></div>
|
||||||
|
<div className="w-3 h-3 bg-gray-300 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
|
{bikes.map((bike, index) => (
|
||||||
|
<BikeCard key={bike.id} bike={bike} index={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center mt-12">
|
||||||
|
<div className="flex justify-center space-x-1">
|
||||||
|
{[...Array(8)].map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`w-2 h-2 rounded-full ${
|
||||||
|
i === 0 ? 'bg-bulls-blue' : 'bg-gray-300'
|
||||||
|
}`}
|
||||||
|
></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldReduce) {
|
||||||
|
return sectionContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div {...fadeUpPreset(0.1, 1.0)}>
|
||||||
|
{sectionContent}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TopEBikes;
|
||||||
48
src/index.css
Normal file
48
src/index.css
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Arial', 'Helvetica', sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.btn-primary {
|
||||||
|
@apply bg-bulls-blue text-white px-6 py-3 rounded-md hover:bg-blue-600 transition-colors duration-300 font-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply bg-white text-bulls-dark px-6 py-3 rounded-md hover:bg-gray-100 transition-colors duration-300 font-medium border border-gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
@apply text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-subtitle {
|
||||||
|
@apply text-xl md:text-2xl text-white mb-8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.text-shadow {
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-overlay {
|
||||||
|
background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4));
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/index.js
Normal file
13
src/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
const container = document.getElementById('root');
|
||||||
|
const root = createRoot(container);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
25
tailwind.config.js
Normal file
25
tailwind.config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
"./src/**/*.{js,jsx,ts,tsx}",
|
||||||
|
"./public/index.html"
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
'bulls-gray': '#2d2d2d',
|
||||||
|
'bulls-light-gray': '#f5f5f5',
|
||||||
|
'bulls-blue': '#00a8e6',
|
||||||
|
'bulls-dark': '#1a1a1a',
|
||||||
|
'bulls-text': '#333333'
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
'sans': ['Arial', 'Helvetica', 'sans-serif']
|
||||||
|
},
|
||||||
|
backgroundImage: {
|
||||||
|
'hero-gradient': 'linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4))'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
5
vercel.json
Normal file
5
vercel.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"installCommand": "npm install",
|
||||||
|
"buildCommand": "CI=false npm run build",
|
||||||
|
"outputDirectory": "build"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user