Compare commits
No commits in common. "main" and "feature-estelle-köhler" have entirely different histories.
main
...
feature-es
160
.gitignore
vendored
@ -52,20 +52,6 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# ---> VisualStudio
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
@ -468,3 +454,149 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
# Social_Cooking_Team
|
||||
# msc-uxd-fs26-test
|
||||
|
||||
Hier im Readme schreiben wir etwas dazu, wie/ob wir KI benutzen.
|
||||
Test-Repository für Unterrichtszwecke
|
||||
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 400 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@ -1,6 +0,0 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 23 27.6667" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path d="M22 11.5C22 19.6667 11.5 26.6667 11.5 26.6667C11.5 26.6667 1 19.6667 1 11.5C1 8.71523 2.10625 6.04451 4.07538 4.07538C6.04451 2.10625 8.71523 1 11.5 1C14.2848 1 16.9555 2.10625 18.9246 4.07538C20.8938 6.04451 22 8.71523 22 11.5Z" stroke="var(--stroke-0, #D44B24)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.5 15C13.433 15 15 13.433 15 11.5C15 9.567 13.433 8 11.5 8C9.567 8 8 9.567 8 11.5C8 13.433 9.567 15 11.5 15Z" stroke="var(--stroke-0, #D44B24)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 762 B |
@ -1,9 +0,0 @@
|
||||
<svg width="104" height="50" viewBox="0 0 104 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M83.0693 30.3941C83.0693 28.5407 83.4727 26.9102 84.2794 25.5025C85.1121 24.0948 86.283 22.9922 87.7923 22.1945C89.3276 21.3968 91.1491 20.998 93.2569 20.998C94.8963 20.998 96.3665 21.2678 97.6676 21.8074C98.9947 22.347 100.036 23.0743 100.79 23.9893C101.545 24.8808 101.922 25.8661 101.922 26.9453C101.922 28.0715 101.558 28.8692 100.829 29.3384C100.101 29.8076 99.0207 30.0422 97.5895 30.0422H89.3926C89.3926 30.6522 89.6529 31.1566 90.1733 31.5554C90.7198 31.9543 91.6305 32.1537 92.9056 32.1537C93.5561 32.1537 94.1546 32.095 94.7011 31.9777C95.2736 31.8604 95.8461 31.7549 96.4185 31.661C97.017 31.5437 97.6806 31.4851 98.4092 31.4851C99.4501 31.4851 100.309 31.8252 100.985 32.5056C101.662 33.1625 102 34.0775 102 35.2506C102 36.6348 101.207 37.714 99.6192 38.4882C98.0579 39.2624 96.0022 39.6495 93.4521 39.6495C91.6565 39.6495 89.9651 39.3328 88.3778 38.6993C86.8165 38.0659 85.5414 37.0688 84.5526 35.7081C83.5637 34.3239 83.0693 32.5525 83.0693 30.3941ZM95.716 28.8809C96.1063 28.8809 96.2364 28.7167 96.1063 28.3882C96.0022 28.0597 95.6899 27.7196 95.1695 27.3676C94.6491 27.0157 93.8944 26.8398 92.9056 26.8398C91.7606 26.8398 90.9019 27.0627 90.3294 27.5084C89.783 27.9542 89.5097 28.4117 89.5097 28.8809H95.716ZM90.6027 15.7896C91.3313 15.3908 92.0599 15.0154 92.7885 14.6635C93.5171 14.2881 94.3628 13.8541 95.3256 13.3614C96.3145 12.8687 97.2643 12.7045 98.175 12.8687C99.0858 13.0095 99.7493 13.5139 100.166 14.382C100.608 15.25 100.673 16.036 100.361 16.7398C100.049 17.4202 99.359 17.9598 98.2921 18.3586C97.3553 18.7105 96.3014 19.039 95.1305 19.344C93.9855 19.649 93.0097 19.8601 92.203 19.9774C91.5785 20.0713 90.993 20.0243 90.4465 19.8366C89.9001 19.6255 89.4967 19.2149 89.2365 18.6049C89.0023 17.995 89.0153 17.4553 89.2755 16.9861C89.5358 16.5169 89.9781 16.1181 90.6027 15.7896Z" fill="#CD4918"/>
|
||||
<path d="M75.1806 39.6495C73.8014 39.6495 72.5524 39.368 71.4334 38.8049C70.3405 38.2418 69.4818 37.4442 68.8573 36.4119C68.2327 35.3561 67.9205 34.1244 67.9205 32.7168V26.5231L66.4763 26.699C65.6956 26.7929 65.006 26.5465 64.4075 25.96C63.809 25.3735 63.5098 24.6344 63.5098 23.7429C63.5098 22.8514 63.809 22.1241 64.4075 21.561C65.006 20.9745 65.6956 20.7282 66.4763 20.822L67.9205 20.998L67.8814 18.6049C67.8554 17.6665 68.1547 16.9275 68.7792 16.3879C69.4037 15.8248 70.3145 15.5433 71.5115 15.5433C72.7085 15.5433 73.6323 15.8248 74.2828 16.3879C74.9334 16.9275 75.2326 17.6665 75.1806 18.6049L75.1025 20.998L77.1712 20.7516C78.5764 20.5639 79.6433 20.7986 80.3719 21.4555C81.1005 22.0889 81.4648 22.8514 81.4648 23.7429C81.4648 24.6344 81.1005 25.4086 80.3719 26.0656C79.6693 26.699 78.6415 26.9336 77.2883 26.7694L75.1025 26.5231V31.7666C75.1025 32.0716 75.2196 32.3531 75.4538 32.6112C75.688 32.8458 75.9742 32.9162 76.3125 32.8223C76.7028 32.7285 77.1062 32.5291 77.5225 32.2241C77.9649 31.9191 78.5244 31.7666 79.2009 31.7666C80.2158 31.7666 80.9964 32.095 81.5429 32.752C82.0894 33.4089 82.3626 34.2183 82.3626 35.1802C82.3626 35.9309 82.0633 36.6582 81.4648 37.3621C80.8924 38.0424 80.0727 38.5938 79.0058 39.0161C77.9389 39.4384 76.6638 39.6495 75.1806 39.6495Z" fill="#CD4918"/>
|
||||
<path d="M54.709 17.0565C54.709 16.0477 55.0473 15.2383 55.7238 14.6283C56.4264 13.9948 57.3892 13.6781 58.6123 13.6781C59.9134 13.6781 60.8762 13.9831 61.5007 14.5931C62.1512 15.2031 62.4765 16.0125 62.4765 17.0213C62.4765 18.0067 62.1512 18.8043 61.5007 19.4143C60.8762 20.0009 59.9134 20.2941 58.6123 20.2941C57.3372 20.2941 56.3614 20.0009 55.6848 19.4143C55.0343 18.8043 54.709 18.0184 54.709 17.0565ZM58.6123 39.5439C57.3892 39.5439 56.4785 39.2389 55.88 38.6289C55.2815 38.0189 54.9822 37.2565 54.9822 36.3415V24.306C54.9822 23.2971 55.2815 22.4995 55.88 21.9129C56.4785 21.3264 57.3892 21.0331 58.6123 21.0331C59.8353 21.0331 60.7461 21.3264 61.3446 21.9129C61.9691 22.4995 62.2813 23.2737 62.2813 24.2356V36.3415C62.2813 37.2565 61.9561 38.0189 61.3055 38.6289C60.681 39.2389 59.7832 39.5439 58.6123 39.5439Z" fill="#CD4918"/>
|
||||
<path d="M44.0485 39.5439C42.3571 39.5439 40.9129 39.0982 39.7159 38.2067C38.5189 37.3151 37.53 35.8371 36.7494 33.7725L33.6658 25.7488C33.2494 24.6931 33.2494 23.7195 33.6658 22.8279C34.1081 21.9364 34.9148 21.3734 36.0858 21.1387C37.1267 20.9276 38.0374 21.0214 38.8181 21.4203C39.6248 21.8191 40.1973 22.4878 40.5355 23.4262L43.5801 31.4851C43.9444 32.4235 44.3868 33.1156 44.9072 33.5614C45.4537 34.0071 46.1172 34.1127 46.8979 33.8781C47.6265 33.62 48.0168 33.1977 48.0689 32.6112C48.1469 32.0012 48.0038 31.2974 47.6395 30.4997C47.3273 29.7724 46.9109 29.1507 46.3905 28.6346C45.87 28.1184 45.4016 27.5788 44.9853 27.0157C44.5689 26.4527 44.3608 25.7488 44.3608 24.9042C44.3608 23.6843 44.7901 22.7341 45.6488 22.0537C46.5336 21.3499 47.6916 21.0214 49.1228 21.0684C50.3458 21.0918 51.3476 21.4789 52.1283 22.2297C52.9089 22.9804 53.2993 23.9306 53.2993 25.0802C53.2993 25.7137 53.1822 26.394 52.948 27.1213C52.7138 27.8486 52.4536 28.6111 52.1673 29.4088L50.606 33.8429C50.1897 35.016 49.7213 36.0365 49.2008 36.9046C48.7064 37.7492 48.0559 38.4061 47.2492 38.8753C46.4685 39.3211 45.4016 39.5439 44.0485 39.5439Z" fill="#CD4918"/>
|
||||
<path d="M16.3409 39.544C15.1959 39.544 14.2721 39.239 13.5695 38.629C12.893 37.9956 12.5547 37.2213 12.5547 36.3064V24.658C12.5547 23.6726 12.88 22.8397 13.5305 22.1593C14.1811 21.479 15.1178 21.1388 16.3409 21.1388C17.5379 21.1388 18.4486 21.479 19.0732 22.1593C19.6977 22.8397 20.0099 23.696 20.0099 24.7283V26.4879H20.7125C20.7125 25.1506 20.9728 24.0949 21.4932 23.3207C22.0397 22.5465 22.7292 21.9951 23.5619 21.6667C24.3946 21.3148 25.2273 21.1388 26.06 21.1388C28.0637 21.1388 29.5339 21.7957 30.4707 23.1095C31.4075 24.3999 31.8759 26.2416 31.8759 28.6346V36.3064C31.8759 37.2213 31.5376 37.9956 30.8611 38.629C30.1845 39.239 29.2737 39.544 28.1288 39.544C26.9578 39.544 26.021 39.239 25.3184 38.629C24.6418 37.9956 24.3036 37.2213 24.3036 36.3064V30.0775C24.3036 29.2329 24.1214 28.5759 23.7571 28.1067C23.4188 27.6375 22.9114 27.4029 22.2348 27.4029C21.4542 27.4029 20.8817 27.661 20.5174 28.1771C20.1791 28.6933 20.0099 29.3502 20.0099 30.1478V36.3064C20.0099 37.2213 19.6847 37.9956 19.0341 38.629C18.3836 39.239 17.4858 39.544 16.3409 39.544Z" fill="#CD4918"/>
|
||||
<path d="M6.05941 39.6144C4.8624 39.6144 3.88658 39.2859 3.13195 38.629C2.37732 37.9721 2 37.1392 2 36.1304V17.5141C2 16.4818 2.36431 15.6137 3.09292 14.9099C3.84755 14.206 4.83638 13.8541 6.05941 13.8541C7.3605 13.8541 8.37535 14.206 9.10397 14.9099C9.8586 15.5902 10.2359 16.4348 10.2359 17.4437V36.1304C10.2359 37.1392 9.83258 37.9721 9.0259 38.629C8.24524 39.2859 7.25641 39.6144 6.05941 39.6144Z" fill="#CD4918"/>
|
||||
<path d="M19.9137 11.7901C20.0941 12.142 19.9271 12.5614 19.5405 12.7257C19.4344 12.7707 19.323 12.7913 19.2148 12.7913C18.9231 12.7913 18.6448 12.6412 18.5128 12.385C17.8511 11.0957 17.5027 9.53236 17.5027 7.86486C17.5027 7.47638 17.849 7.16108 18.2758 7.16108C18.7025 7.16108 19.0489 7.47638 19.0489 7.86486C19.0489 9.30809 19.355 10.7016 19.9137 11.7901ZM24.4595 7.16108C24.0327 7.16108 23.6864 7.47638 23.6864 7.86486C23.6864 9.30809 23.3792 10.7016 22.8205 11.7901C22.6401 12.142 22.8071 12.5614 23.1937 12.7257C23.2999 12.7707 23.4112 12.7913 23.5194 12.7913C23.8111 12.7913 24.0894 12.6412 24.2214 12.385C24.8832 11.0957 25.2316 9.53236 25.2316 7.86486C25.2316 7.47638 24.8852 7.16108 24.4585 7.16108H24.4595ZM30.3866 8.33405C30.3866 9.38504 29.9722 10.4032 29.2187 11.2008C28.8105 11.6325 28.325 12.3278 28.325 13.1827V17.7178C28.325 19.1414 27.054 20.2984 25.4903 20.2984H17.2439C15.6802 20.2984 14.4092 19.1414 14.4092 17.7178V13.1827C14.4092 12.3278 13.9227 11.6334 13.5155 11.2008C12.7631 10.4022 12.3477 9.3841 12.3477 8.33405C12.3477 6.11291 14.1052 4.28026 16.4904 3.94807C17.5965 2.72255 19.3911 2 21.3671 2C23.3431 2 25.1378 2.72255 26.2438 3.94807C28.628 4.28026 30.3866 6.11291 30.3866 8.33405ZM26.7788 17.7178V16.5449H15.9554V17.7178C15.9554 18.3644 16.5337 18.8908 17.2439 18.8908H25.4903C26.2005 18.8908 26.7788 18.3644 26.7788 17.7178ZM28.8404 8.33405C28.8404 6.74444 27.5189 5.44479 25.7665 5.31341C25.5387 5.29558 25.3326 5.18861 25.1996 5.02064C24.4224 4.02596 22.9535 3.40851 21.3671 3.40851C19.7807 3.40851 18.3118 4.02596 17.5346 5.02064C17.4016 5.18955 17.1955 5.29652 16.9677 5.31341C15.2153 5.44572 13.8938 6.74444 13.8938 8.33405C13.8938 9.05848 14.1691 9.73317 14.6886 10.284C15.5174 11.1633 15.9554 12.1655 15.9554 13.1827V15.1373H26.7788V13.1827C26.7788 12.1655 27.2169 11.1623 28.0456 10.284C28.5662 9.73317 28.8404 9.05848 28.8404 8.33405Z" fill="#CD4918"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@ -1,642 +0,0 @@
|
||||
:root {
|
||||
--max-width: 1120px;
|
||||
--content-width: 760px;
|
||||
--header-height: 4.5rem;
|
||||
|
||||
--control-min-height: 3rem;
|
||||
--input-min-height: 3.5rem;
|
||||
--card-min-height: 6rem;
|
||||
|
||||
--color-bg: var(--butter);
|
||||
--color-surface: var(--white);
|
||||
--color-surface-soft: var(--butter-light);
|
||||
--color-text: var(--black);
|
||||
--color-text-secondary: rgba(34, 33, 26, 0.8);
|
||||
--color-muted: rgba(34, 33, 26, 0.68);
|
||||
--color-border: rgba(102, 52, 13, 0.16);
|
||||
--color-border-strong: var(--brown);
|
||||
--color-divider: rgba(102, 52, 13, 0.14);
|
||||
--color-primary: var(--olive);
|
||||
--color-primary-hover: var(--olive-dark);
|
||||
--color-progress-bg: rgba(212, 75, 36, 0.18);
|
||||
--color-focus: var(--blue);
|
||||
--color-error: var(--error);
|
||||
--shadow-soft: 0 12px 30px rgba(102, 52, 13, 0.1);
|
||||
--input-border-soft: rgba(102, 52, 13, 0.2);
|
||||
--input-border-focus: rgba(107, 107, 5, 0.45);
|
||||
--input-shadow-focus: 0 0 0 4px rgba(107, 107, 5, 0.12);
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
a,
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
background: var(--color-bg);
|
||||
border-top: 2px solid #232323;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.site-nav {
|
||||
width: min(100% - 2rem, var(--max-width));
|
||||
margin: 0 auto;
|
||||
min-height: var(--header-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.site-nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-5);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.site-nav-links a {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.site-nav-links li:last-child a {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #231f20;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.event-create-page {
|
||||
width: min(100% - 2rem, var(--max-width));
|
||||
margin: 0 auto;
|
||||
padding: var(--space-5) 0 0;
|
||||
}
|
||||
|
||||
.event-flow-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.event-form {
|
||||
min-height: calc(100vh - var(--header-height) - 9rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: none;
|
||||
padding: var(--space-4) 0 var(--space-7);
|
||||
}
|
||||
|
||||
.submission-success {
|
||||
padding: var(--space-4) 0 var(--space-7);
|
||||
}
|
||||
|
||||
.step--active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.step-layout {
|
||||
width: min(100%, var(--content-width));
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.step-layout--intro {
|
||||
min-height: 60vh;
|
||||
align-content: center;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-7);
|
||||
}
|
||||
|
||||
.step-copy,
|
||||
.step-fields,
|
||||
.form-field,
|
||||
fieldset {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.step-copy {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.step-fields {
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.form-field,
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.step-kicker {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
color: var(--color-muted);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: clamp(2rem, 4vw, 4rem);
|
||||
line-height: 1.03;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
margin: 0;
|
||||
max-width: 42rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: clamp(1rem, 1.4vw, 1.2rem);
|
||||
}
|
||||
|
||||
.intro-card,
|
||||
.review-card {
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.intro-card {
|
||||
max-width: 24rem;
|
||||
padding: var(--space-6);
|
||||
border-radius: 1.75rem;
|
||||
background: linear-gradient(135deg, var(--color-surface), var(--color-surface-soft));
|
||||
}
|
||||
|
||||
.intro-card--image {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.intro-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
border-radius: 1.875rem;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
label,
|
||||
legend {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
margin: -0.25rem 0 0;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="number"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: var(--input-min-height);
|
||||
padding: 1rem 1.1rem;
|
||||
border: 1px solid var(--input-border-soft);
|
||||
border-radius: 1.125rem;
|
||||
background: var(--butter-light);
|
||||
color: var(--color-text);
|
||||
box-shadow: 0 1px 2px rgba(102, 52, 13, 0.04);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 9rem;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input[type="text"]:hover,
|
||||
input[type="date"]:hover,
|
||||
input[type="time"]:hover,
|
||||
input[type="number"]:hover,
|
||||
textarea:hover {
|
||||
border-color: rgba(102, 52, 13, 0.28);
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="time"]:focus,
|
||||
input[type="number"]:focus,
|
||||
textarea:focus {
|
||||
border-color: var(--input-border-focus);
|
||||
box-shadow: var(--input-shadow-focus);
|
||||
background: var(--butter-light);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.field-invalid {
|
||||
border-color: var(--tomato) !important;
|
||||
box-shadow: 0 0 0 2px rgba(212, 75, 36, 0.14);
|
||||
}
|
||||
|
||||
.field-row {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.option-grid {
|
||||
display: grid;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.option-card {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
min-height: var(--card-min-height);
|
||||
padding: 1rem 1rem 1rem 1.05rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 1rem;
|
||||
background: var(--butter-light);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.option-card small {
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.option-card:hover {
|
||||
background: var(--olive-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.option-card input {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option-card:has(input:checked) {
|
||||
border: 1px solid var(--color-primary);
|
||||
background: var(--color-primary);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.option-card:has(input:checked) small {
|
||||
color: rgba(247, 246, 230, 0.88);
|
||||
}
|
||||
|
||||
.option-card--invalid {
|
||||
border-color: var(--tomato) !important;
|
||||
box-shadow: 0 0 0 2px rgba(212, 75, 36, 0.14);
|
||||
}
|
||||
|
||||
.counter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.counter input {
|
||||
width: 6rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.counter-button,
|
||||
.button {
|
||||
min-height: var(--control-min-height);
|
||||
}
|
||||
|
||||
.counter-button {
|
||||
width: var(--control-min-height);
|
||||
height: var(--control-min-height);
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: 50%;
|
||||
background: var(--color-primary);
|
||||
color: var(--white);
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
box-shadow: 0 6px 16px rgba(107, 107, 5, 0.18);
|
||||
transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.counter-button:hover {
|
||||
background: var(--color-primary-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.counter-button:focus-visible {
|
||||
outline: 3px solid rgba(107, 107, 5, 0.22);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.review-card {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.review-card--success {
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
padding: var(--space-3) 0 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.review-list {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.review-item {
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
padding: 1rem 1.1rem;
|
||||
border: 1px solid var(--input-border-soft);
|
||||
border-radius: 1.125rem;
|
||||
background: var(--butter-light);
|
||||
box-shadow: 0 1px 2px rgba(102, 52, 13, 0.04);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.review-item:last-child {
|
||||
border-bottom: 1px solid var(--input-border-soft);
|
||||
}
|
||||
|
||||
.review-item:hover {
|
||||
border-color: rgba(102, 52, 13, 0.28);
|
||||
background: rgba(247, 246, 230, 0.92);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.review-item:focus-visible {
|
||||
outline: 3px solid rgba(107, 107, 5, 0.2);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.review-item dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.review-item dd {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.submission-success-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flow-footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 5;
|
||||
margin-top: auto;
|
||||
background: var(--color-bg);
|
||||
backdrop-filter: none;
|
||||
padding-top: var(--space-4);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.progress-wrap {
|
||||
position: relative;
|
||||
width: min(100%, var(--content-width));
|
||||
margin: 0 auto;
|
||||
padding-top: 4.35rem;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
height: 0.375rem;
|
||||
background: var(--color-progress-bg);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
background: var(--tomato);
|
||||
transition: width 0.25s ease;
|
||||
}
|
||||
|
||||
.progress-marker {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateX(-50%);
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
gap: 0.2rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.progress-marker::after {
|
||||
content: "";
|
||||
width: 0.125rem;
|
||||
height: 1rem;
|
||||
background: var(--tomato);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.progress-marker__circle {
|
||||
width: 2.9rem;
|
||||
height: 2.9rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: var(--tomato);
|
||||
color: var(--butter-light);
|
||||
font-size: 1.35rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
box-shadow: 0 10px 24px rgba(212, 75, 36, 0.18);
|
||||
}
|
||||
|
||||
.flow-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
width: min(100%, var(--content-width));
|
||||
margin: 0 auto;
|
||||
padding: var(--space-4) 0;
|
||||
}
|
||||
|
||||
.flow-actions-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
min-height: 1.5rem;
|
||||
margin: 0;
|
||||
color: var(--color-error);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.9rem 1.35rem;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--color-border);
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button--ghost:hover {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.button--text {
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
min-width: 10rem;
|
||||
border-color: var(--color-primary);
|
||||
background: var(--color-primary);
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.button--primary:hover {
|
||||
background: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
.button--primary:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.button--intro {
|
||||
justify-self: start;
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
width: min(100% - 2rem, var(--max-width));
|
||||
margin: 0 auto;
|
||||
padding: var(--space-5) 0 var(--space-6);
|
||||
color: var(--color-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a:focus-visible,
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
textarea:focus-visible {
|
||||
outline: 3px solid var(--color-focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.site-nav {
|
||||
flex-wrap: wrap;
|
||||
padding: var(--space-3) 0;
|
||||
}
|
||||
|
||||
.site-nav-links {
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.flow-actions,
|
||||
.flow-actions-right {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.button--text {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.event-flow-header {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.step-layout--intro {
|
||||
width: min(100%, 56rem);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.field-row {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.option-grid--3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.option-grid--4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@ -1,910 +0,0 @@
|
||||
.container { max-width: 1100px; margin: 0 auto; padding: 20px; }
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Shared Typography Tokens
|
||||
Reuse common text styles across nav, controls and buttons
|
||||
--------------------------------------------------------- */
|
||||
.nav-link,
|
||||
.login-pill,
|
||||
.category-item,
|
||||
.meta-filter select,
|
||||
.meta-filter input[type="date"],
|
||||
.btn-primary,
|
||||
.empty-state-btn,
|
||||
.detail-primary-btn {
|
||||
font-family: "Jost", sans-serif;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-ui);
|
||||
}
|
||||
|
||||
.event-date-time,
|
||||
.event-location {
|
||||
color: var(--olive);
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Heading hierarchy: page title > detail title > card title > section title */
|
||||
.overview-title {
|
||||
margin: 0 0 var(--space-4);
|
||||
color: var(--olive);
|
||||
font-family: "Bagel Fat One", cursive;
|
||||
font-size: clamp(38px, 5vw, 44px);
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
margin: 0;
|
||||
color: var(--brown);
|
||||
font-family: "Bagel Fat One", cursive;
|
||||
font-size: clamp(44px, 6vw, 52px);
|
||||
line-height: 1.15;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
margin: 0 0 var(--space-3);
|
||||
color: var(--brown);
|
||||
font-family: "Bagel Fat One", cursive;
|
||||
font-size: clamp(32px, 4.2vw, 44px);
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.detail-section-title {
|
||||
margin: 0 0 10px;
|
||||
color: var(--brown);
|
||||
font-family: "Bagel Fat One", cursive;
|
||||
font-size: 28px;
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Overview Header + Filters
|
||||
--------------------------------------------------------- */
|
||||
.filter-label {
|
||||
margin: 0 0 var(--space-1);
|
||||
color: var(--olive);
|
||||
font-size: 18px;
|
||||
letter-spacing: var(--ls-label);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.category-group {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: 0;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
border: 2px solid var(--olive);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--butter);
|
||||
color: var(--black);
|
||||
line-height: 1.3;
|
||||
padding: var(--space-1) var(--space-4);
|
||||
min-height: 40px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.category-item:hover {
|
||||
background: #faf8e8;
|
||||
}
|
||||
|
||||
.category-item.active {
|
||||
border-color: transparent;
|
||||
background: var(--olive);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.meta-filter-group {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.meta-filter {
|
||||
display: block;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.meta-filter span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.meta-filter select,
|
||||
.meta-filter input[type="date"] {
|
||||
border: 2px solid var(--olive);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--butter);
|
||||
color: var(--black);
|
||||
line-height: 1.3;
|
||||
padding: var(--space-1) var(--space-4);
|
||||
min-height: 40px;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.meta-filter select:focus,
|
||||
.meta-filter input[type="date"]:focus {
|
||||
outline: 2px solid rgba(107, 107, 5, 0.35);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Overview Event Cards
|
||||
--------------------------------------------------------- */
|
||||
.event-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.event-card {
|
||||
/* Core card container for every overview event entry. */
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-5) 40px var(--space-4);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-6);
|
||||
cursor: pointer;
|
||||
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.1);
|
||||
}
|
||||
|
||||
.event-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.event-date-time {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.event-top-row {
|
||||
/* Primary metadata line: location + date/time/guest counters. */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-5);
|
||||
margin-bottom: var(--space-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.event-location {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.event-location img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
filter: brightness(0) saturate(100%) invert(35%) sepia(95%) saturate(501%) hue-rotate(22deg) brightness(96%) contrast(96%);
|
||||
}
|
||||
|
||||
.event-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.event-tag {
|
||||
border: 1px solid var(--tomato);
|
||||
color: var(--tomato);
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 7px 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-ui);
|
||||
line-height: 1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.event-spec-chip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.event-specs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.event-capacity {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.event-hints {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.event-side {
|
||||
/* Right-side action area: availability status and optional signup button. */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding-top: 36px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.event-spots {
|
||||
color: var(--olive);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.event-spots-full {
|
||||
/* Sold-out visual state, intentionally high-contrast and always filled. */
|
||||
border: 2px solid var(--tomato);
|
||||
border-radius: var(--radius-pill);
|
||||
color: var(--white);
|
||||
background: var(--tomato);
|
||||
opacity: 1;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
padding: 7px 14px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--olive);
|
||||
color: #fffde8;
|
||||
border: none;
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 10px 22px;
|
||||
font-size: 16px;
|
||||
line-height: 1.3;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Overview Empty State
|
||||
--------------------------------------------------------- */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 42px var(--space-5);
|
||||
border: 2px solid var(--olive-light);
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
margin-top: 8px;
|
||||
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.08);
|
||||
}
|
||||
|
||||
.empty-state-kicker {
|
||||
margin: 0 0 8px;
|
||||
color: var(--olive);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: var(--ls-label);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
margin: 0;
|
||||
font-family: "Jost", sans-serif;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: var(--brown);
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
margin: 12px 0 0;
|
||||
font-size: 18px;
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.empty-state-link {
|
||||
display: inline-block;
|
||||
margin-top: 20px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.empty-state-btn {
|
||||
border: 0;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--olive);
|
||||
color: var(--white);
|
||||
font-weight: 600;
|
||||
padding: 10px 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty-state-btn:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Detail Page
|
||||
--------------------------------------------------------- */
|
||||
.detail-page {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.detail-back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: var(--space-1);
|
||||
width: fit-content;
|
||||
color: var(--tomato);
|
||||
text-decoration: none;
|
||||
font-family: "Jost", sans-serif;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-none);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.detail-back span {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.detail-hero {
|
||||
display: grid;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.detail-top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-5);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
margin: 0;
|
||||
font-size: 26px;
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
.detail-chip-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-content-grid {
|
||||
/* Two-column detail layout: content stack (left) + gallery (right). */
|
||||
display: grid;
|
||||
grid-template-columns: minmax(260px, 0.75fr) minmax(0, 1.25fr);
|
||||
grid-template-areas: "side gallery";
|
||||
gap: var(--space-3);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.detail-side-stack {
|
||||
grid-area: side;
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.host-card {
|
||||
border: 2px solid var(--butter);
|
||||
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.1);
|
||||
}
|
||||
|
||||
.host-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.host-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
background: var(--olive);
|
||||
color: var(--white);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.host-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.host-role {
|
||||
padding: 4px 10px;
|
||||
border: 1px solid var(--olive-light);
|
||||
border-radius: var(--radius-pill);
|
||||
background: rgba(107, 107, 5, 0.08);
|
||||
color: var(--olive);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: var(--ls-label);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.host-card p {
|
||||
margin: 0 0 10px;
|
||||
font-size: 20px;
|
||||
line-height: 1.3;
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.detail-gallery {
|
||||
grid-area: gallery;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.detail-gallery-large {
|
||||
/* Editorial mosaic: first image spans two rows, side images stack vertically. */
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
grid-template-rows: repeat(2, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.detail-gallery-large img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 220px;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--black);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 12px rgba(34, 28, 26, 0.14);
|
||||
}
|
||||
|
||||
.detail-gallery-item {
|
||||
display: block;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.detail-gallery-image {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.detail-gallery-item:hover .detail-gallery-image,
|
||||
.detail-gallery-item:focus-visible .detail-gallery-image {
|
||||
transform: scale(1.015);
|
||||
}
|
||||
|
||||
.detail-gallery-large img:first-child {
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
.detail-lightbox {
|
||||
/* Full-screen overlay for enlarged gallery image view. */
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 200;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.detail-lightbox.is-open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.detail-lightbox-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.detail-lightbox-content {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
max-width: min(96vw, 1100px);
|
||||
max-height: 90vh;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.detail-lightbox-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
object-fit: contain;
|
||||
border-radius: 16px;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.detail-lightbox-close {
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
right: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--white);
|
||||
font-size: 40px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.detail-panel-compact {
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.detail-menu-list {
|
||||
margin: 20px;
|
||||
list-style: disc;
|
||||
font-size: 20px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.detail-participants-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.detail-participants-link {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--tomato);
|
||||
letter-spacing: var(--ls-label);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.detail-avatar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
background: var(--tomato);
|
||||
color: var(--white);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.participant-more {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.detail-action-bar {
|
||||
/* Sticky bottom CTA bar with summary and booking controls. */
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid var(--olive-light);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 10px 14px;
|
||||
position: sticky;
|
||||
bottom: 18px;
|
||||
z-index: 12;
|
||||
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.1);
|
||||
}
|
||||
|
||||
.detail-action-summary {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.detail-action-summary small {
|
||||
font-size: 17px;
|
||||
opacity: 0.75;
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
.detail-action-meta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.detail-action-location {
|
||||
color: var(--olive);
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.detail-action-location img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.detail-action-meta-text {
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
.detail-action-summary strong {
|
||||
font-size: 40px;
|
||||
font-family: "Bagel Fat One", cursive;
|
||||
line-height: 1.2;
|
||||
font-weight: 400;
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
.detail-action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.detail-spots-pill {
|
||||
border: 2px solid var(--olive-light);
|
||||
border-radius: var(--radius-pill);
|
||||
opacity: 0.5;
|
||||
font-size: 14px;
|
||||
letter-spacing: var(--ls-ui);
|
||||
padding: 7px 14px;
|
||||
color: var(--olive);
|
||||
}
|
||||
|
||||
.detail-spots-pill-full {
|
||||
border-color: var(--tomato);
|
||||
color: var(--tomato);
|
||||
opacity: 1;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-primary-btn {
|
||||
border: 2px solid var(--tomato);
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--tomato);
|
||||
color: var(--white);
|
||||
padding: 10px 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.detail-primary-btn:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Responsive: Tablet (<= 850px)
|
||||
--------------------------------------------------------- */
|
||||
@media (max-width: 850px) {
|
||||
.top-nav {
|
||||
padding-left: 16px;
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
max-width: 84px;
|
||||
}
|
||||
|
||||
.overview-title {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.top-nav-links {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-link,
|
||||
.login-pill {
|
||||
font-size: 15px;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.profile-pill {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 17px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.category-group {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
font-size: 15px;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.meta-filter {
|
||||
min-width: 155px;
|
||||
}
|
||||
|
||||
.meta-filter select,
|
||||
.meta-filter input[type="date"] {
|
||||
font-size: 15px;
|
||||
padding: 8px 14px;
|
||||
min-height: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.event-card {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 16px;
|
||||
padding: 22px 20px 20px;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.event-date-time,
|
||||
.event-location,
|
||||
.event-spots {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.event-side {
|
||||
padding-top: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.event-side-full {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
font-size: 17px;
|
||||
padding: 8px 18px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.detail-top-row {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.detail-content-grid {
|
||||
grid-template-columns: minmax(230px, 0.8fr) minmax(0, 1.2fr);
|
||||
grid-template-areas: "side gallery";
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.detail-side-stack {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-gallery-large {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(3, minmax(180px, auto));
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.detail-gallery-large img {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.detail-gallery-large img:first-child {
|
||||
grid-row: auto;
|
||||
}
|
||||
|
||||
.detail-lightbox {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.detail-lightbox-close {
|
||||
top: -36px;
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.detail-section-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.detail-action-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-action-summary strong {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.detail-action-buttons {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Responsive: Mobile (<= 640px)
|
||||
--------------------------------------------------------- */
|
||||
@media (max-width: 640px) {
|
||||
.top-nav {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.top-nav-wrap {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.overview-title {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.top-nav-links {
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.top-nav-links .nav-link.active {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.detail-content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"side"
|
||||
"gallery";
|
||||
}
|
||||
|
||||
.meta-filter-group {
|
||||
width: auto;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.meta-filter {
|
||||
flex: 1 1 160px;
|
||||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
@ -1,534 +0,0 @@
|
||||
/* Instagram and Invite logo in gallery info area */
|
||||
.gallery__icon--instagram {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
background: none;
|
||||
/* Olive green filter for PNG: #6b6b05 */
|
||||
filter: brightness(0) saturate(100%) invert(27%) sepia(81%) saturate(749%) hue-rotate(24deg) brightness(90%) contrast(90%);
|
||||
}
|
||||
|
||||
.gallery__icon--invite {
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
object-fit: contain;
|
||||
margin-left: 0;
|
||||
transform: translate(-4%, -1%);
|
||||
position: relative;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
:root {
|
||||
--black: #000000;
|
||||
--white: #ffffff;
|
||||
--button-green: #6b6b05;
|
||||
--button-green-dark: #514c04;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: #FFFDE3; /* butter background color from stylesheet */
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
background: var(--white);
|
||||
padding: 40px;
|
||||
border: 1px solid var(--black);
|
||||
}
|
||||
|
||||
/* --- 1. Basic Layout Logic --- */
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
border: 1px solid var(--black);
|
||||
margin-bottom: 40px; /* Using your 40px margin from the first block */
|
||||
}
|
||||
|
||||
.header__brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.brand__logo {
|
||||
height: 34px;
|
||||
width: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Grouping container for 'Event finden' and 'Login' */
|
||||
.header__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.top-nav-wrap {
|
||||
background: #FFFDE3;
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 30px;
|
||||
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 58px;
|
||||
padding: 12px 24px;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
box-sizing: border-box;
|
||||
margin: 0 0 40px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
max-width: 104px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.top-nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #221c1a;
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
padding: 8px 20px;
|
||||
border: 2px solid #e5e1b7;
|
||||
border-radius: 20px;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active,
|
||||
.nav-link:focus-visible {
|
||||
background: #6b6b05;
|
||||
color: #ffffff;
|
||||
border-color: #6b6b05;
|
||||
}
|
||||
|
||||
.nav-link--login {
|
||||
background: #6b6b05;
|
||||
color: #ffffff;
|
||||
border-color: #6b6b05;
|
||||
}
|
||||
|
||||
.nav-link--login:hover,
|
||||
.nav-link--login:focus-visible {
|
||||
background: #ffffff;
|
||||
color: #6b6b05;
|
||||
border-color: #6b6b05;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
width: min(100% - 4rem, 1120px);
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 50px;
|
||||
margin-bottom: 80px;
|
||||
align-items: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.how-it-works {
|
||||
margin-bottom: 70px;
|
||||
}
|
||||
|
||||
.how-it-works__header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.how-it-works__header h2 {
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
color: #221c1a;
|
||||
}
|
||||
|
||||
.how-it-works__steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(180px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.how-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 28px 20px;
|
||||
background: var(--white) !important;
|
||||
border: 2px solid var(--tomato) !important;
|
||||
border-radius: 28px;
|
||||
box-shadow: 0 12px 30px rgba(212, 75, 36, 0.08);
|
||||
}
|
||||
|
||||
.how-step__icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: var(--button-green);
|
||||
color: var(--white);
|
||||
border-radius: 50%;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.how-step__number {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #f4efd7;
|
||||
color: #221c1a;
|
||||
border-radius: 50%;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.how-step__label {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
font-weight: 600;
|
||||
color: #221c1a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.how-step__icon--brown {
|
||||
background: #66340d;
|
||||
}
|
||||
|
||||
.how-step__label--brown {
|
||||
color: var(--tomato);
|
||||
}
|
||||
|
||||
.how-step__label--big {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.how-step--numbered {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.how-step__corner-number {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 16px;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.how-step__corner-number--brown {
|
||||
color: var(--tomato);
|
||||
}
|
||||
|
||||
.how-step__png {
|
||||
width: 192px;
|
||||
height: 192px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.how-step__png--brown {
|
||||
filter: brightness(0) saturate(100%) invert(39%) sepia(84%) saturate(1682%) hue-rotate(349deg) brightness(93%) contrast(86%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.how-it-works__steps {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.hero__left {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.hero__left p {
|
||||
margin: 24px 0 32px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.hero__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-card {
|
||||
width: 100%;
|
||||
max-width: 396px;
|
||||
overflow: hidden;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.hero-image {
|
||||
width: 100%;
|
||||
max-width: 396px;
|
||||
max-height: 464px;
|
||||
height: auto;
|
||||
border-radius: 30px;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.hero {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.hero__right {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.hero-image {
|
||||
min-height: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Carousel gallery */
|
||||
.gallery__carousel {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gallery__track {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 30px; /* Space between photos and dots */
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.gallery__item {
|
||||
flex: 0 0 calc((100% - 40px) / 3);
|
||||
min-width: calc((100% - 40px) / 3);
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.06);
|
||||
aspect-ratio: 2 / 3;
|
||||
}
|
||||
|
||||
.gallery__item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.gallery__arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
backdrop-filter: none;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
font-size: 1.6rem;
|
||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
transition: transform 0.2s ease, color 0.2s ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.gallery__arrow:hover {
|
||||
transform: translateY(-50%) scale(1.15);
|
||||
color: #e5e1b7;
|
||||
}
|
||||
|
||||
.gallery__arrow--prev {
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.gallery__arrow--next {
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
/* Center arrow removed – using side arrows only */
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.gallery__track {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.gallery__item {
|
||||
flex: 0 0 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- 2. Button & Link Styling --- */
|
||||
.btn {
|
||||
border: none;
|
||||
background: var(--button-green);
|
||||
color: var(--white);
|
||||
padding: 12px 22px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
text-decoration: none; /* Keeps the link from having an underline */
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
font-size: 0.95rem;
|
||||
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--button-green-dark);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.nav__link {
|
||||
color: var(--black); /* Fixes the purple link issue */
|
||||
}
|
||||
|
||||
/* --- 3. The "X" Box Logic --- */
|
||||
.image-card__placeholder, .placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 250px;
|
||||
position: relative;
|
||||
border: 1px solid var(--black);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.image-card__placeholder::before, .image-card__placeholder::after,
|
||||
.placeholder::before, .placeholder::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
}
|
||||
|
||||
/* Drawing the diagonal lines for the wireframe look */
|
||||
.image-card__placeholder::before, .placeholder::before {
|
||||
background: linear-gradient(to top left, transparent 49.5%, var(--black) 49.5%, var(--black) 50.5%, transparent 50.5%);
|
||||
}
|
||||
|
||||
.image-card__placeholder::after, .placeholder::after {
|
||||
background: linear-gradient(to top right, transparent 49.5%, var(--black) 49.5%, var(--black) 50.5%, transparent 50.5%);
|
||||
}
|
||||
/* The "X" Box Logic - IMPORTANT */
|
||||
.image-card__placeholder, .placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 250px; /* Ensures the box has height */
|
||||
position: relative; /* REQUIRED for the X lines to stay inside */
|
||||
border: 1px solid var(--black);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
/* Creating the diagonal lines */
|
||||
.image-card__placeholder::before, .image-card__placeholder::after,
|
||||
.placeholder::before, .placeholder::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.image-card__placeholder::before, .placeholder::before {
|
||||
background: linear-gradient(to top left, transparent 49.5%, var(--black) 49.5%, var(--black) 50.5%, transparent 50.5%);
|
||||
}
|
||||
|
||||
.image-card__placeholder::after, .placeholder::after {
|
||||
background: linear-gradient(to top right, transparent 49.5%, var(--black) 49.5%, var(--black) 50.5%, transparent 50.5%);
|
||||
}
|
||||
|
||||
/* Badges and Buttons */
|
||||
.social-badge {
|
||||
display: flex;
|
||||
justify-content: center; /* This centers the dots */
|
||||
gap: 10px;
|
||||
margin-top: 20px; /* This creates space between the photos and the dots */
|
||||
/* REMOVE any line that says "position: absolute" or "bottom: 16px" */
|
||||
}
|
||||
|
||||
.social-badge__dot { width: 12px; height: 12px; background: var(--black); border-radius: 50%; }
|
||||
|
||||
.profile-badge {
|
||||
width: 60px; height: 60px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--black);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.gallery__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.gallery__handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--black);
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.gallery__icon {
|
||||
font-size: 1.15rem;
|
||||
color: #DD541A;
|
||||
}
|
||||
|
||||
.gallery__at {
|
||||
font-size: 1.1rem;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.gallery__brand {
|
||||
font-family: 'Inter', sans-serif;
|
||||
color: #DD541A;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
border: none;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.footer__link {
|
||||
color: var(--black);
|
||||
text-decoration: underline;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
@ -1,216 +0,0 @@
|
||||
.container {
|
||||
margin-top: 5rem;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--radius-lg);
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
flex: 1;
|
||||
background-color: var(--white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.image-section img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
flex: 1;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Formularelemente */
|
||||
.info-box {
|
||||
background-color: var(--olive-light);
|
||||
padding: 15px;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
color: var(--black);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: var(--black);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: var(--olive-dark);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Hilfstexte & Fehler */
|
||||
.signup-hint, .login-hint {
|
||||
text-align: left;
|
||||
margin-top: 20px;
|
||||
color: var(--black);
|
||||
font-size: 1rem;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
.signup-hint a, .login-hint a {
|
||||
color: var(--blue);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.signup-hint a:hover, .login-hint a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--error);
|
||||
font-size: 13px;
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-group.has-error input {
|
||||
border-color: var(--error);
|
||||
}
|
||||
|
||||
.form-group.has-error .error-message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Modal / Popup Styles */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
animation: slideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { transform: translateY(-50px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
color: #0084ff;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 28px;
|
||||
color: #999;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Responsive Anpassungen */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@ -1,247 +0,0 @@
|
||||
/* Font Import */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Bagel+Fat+One&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@300;400;600&display=swap');
|
||||
|
||||
/* Reset / Normalize */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Colors + Main Font */
|
||||
:root {
|
||||
--max-width: 1200px;
|
||||
--padding: 1.5rem;
|
||||
|
||||
|
||||
--tomato: #D44B24;
|
||||
--tomato-dark: #D44B24;
|
||||
--olive: #6B6B05;
|
||||
--olive-dark: #5C5C05;
|
||||
--olive-light: #C8CC7A;
|
||||
--butter: #F5F1BC;
|
||||
--butter-light: #F7F6E6;
|
||||
--white: #ffffff;
|
||||
--black: #22211A;
|
||||
--brown: #66340d;
|
||||
--blue: #3489DA;
|
||||
--blue-dark: #1D70BF;
|
||||
--error: #FF3B30;
|
||||
|
||||
--font-main: 'Jost', sans-serif;
|
||||
--font-size-base: 1rem;
|
||||
|
||||
/* Spacing Scale */
|
||||
--space-1: 0.5rem;
|
||||
--space-2: 0.75rem;
|
||||
--space-3: 1rem;
|
||||
--space-4: 1.25rem;
|
||||
--space-5: 1.5rem;
|
||||
--space-6: 2rem;
|
||||
--space-7: 3rem;
|
||||
--space-8: 4rem;
|
||||
|
||||
|
||||
/* Radius Scale */
|
||||
--radius-pill: 62.4375rem;
|
||||
--radius-sm: 0.5rem;
|
||||
--radius-md: 1.5rem;
|
||||
--radius-lg: 1.875rem;
|
||||
|
||||
/* Letter Spacing */
|
||||
--ls-none: 0;
|
||||
--ls-sm: 2.5%;
|
||||
--ls-label: 0.045rem;
|
||||
}
|
||||
|
||||
/* Base Styles */
|
||||
body {
|
||||
font-family: var(--font-main);
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.5;
|
||||
color: var(--black);
|
||||
background-color: var(--butter);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Bagel Fat One';
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
letter-spacing: var(--ls-sm);
|
||||
color: var(--brown);
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: 'Jost', sans-serif;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
||||
.main-content {
|
||||
margin-top: var(--space-8);
|
||||
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Top Navigation */
|
||||
.top-nav-wrap {
|
||||
position: sticky;
|
||||
height: 58px;
|
||||
top: 1rem;
|
||||
z-index: 1000;
|
||||
background: none;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
background: var(--white);
|
||||
/*backdrop-filter: blur(3px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(3px) saturate(140%);*/
|
||||
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 0.1875rem 0.75rem rgba(102, 52, 13, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 3rem;
|
||||
padding: 0.1875rem 0.75rem 0.1875rem var(--space-5);
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 3.125rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
max-width: 6.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
color: var(--black);
|
||||
font-size: 1.125rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-sm);
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover, .nav-tab:active,
|
||||
.nav-tab:focus-visible {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
.button-small:hover, .button-small:active,
|
||||
.button-small:focus-visible {
|
||||
background: var(--olive-dark);
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.nav-tab-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
|
||||
/* Buttons */
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1.25rem;
|
||||
background-color: var(--olive);
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
font-family: 'Jost', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: var(--butter-light);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: var(--olive-dark);
|
||||
}
|
||||
|
||||
|
||||
.button-small {
|
||||
background: var(--olive);
|
||||
color: var(--butter-light);
|
||||
font-size: 1.125rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-sm);
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
|
||||
padding: var(--space-1) var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.button-small:hover, .button-small:active,
|
||||
.button-small:focus-visible {
|
||||
background: var(--olive-dark);
|
||||
color: var(--butter-light);
|
||||
}
|
||||
|
||||
.profile-pill {
|
||||
width: 2.375rem;
|
||||
height: 2.375rem;
|
||||
border-radius: 1.1875rem;
|
||||
background: var(--tomato);
|
||||
color: var(--butter);
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--ls-ui);
|
||||
line-height: 1.3;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.text-center {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Media Queries (Responsive) */
|
||||
@media (max-width: 48rem) {
|
||||
.container {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
122
data/events.json
@ -1,122 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Italienische Tavolata",
|
||||
"location": "Luzern",
|
||||
"date": "19. MÄR. 2026",
|
||||
"time": "18:30 UHR",
|
||||
"category": "DINNER",
|
||||
"diet": "VEGGIE",
|
||||
"spots": 6,
|
||||
"host": {
|
||||
"name": "Ferdinando",
|
||||
"initial": "F"
|
||||
},
|
||||
"hostMessage": [
|
||||
"Ciao zusammen! Ich liebe die italienische Küche, nicht nur wegen des Essens, sondern wegen des Gefühls: Alle sitzen an einem langen Tisch, teilen sich grosse Platten und geniessen die Zeit.",
|
||||
"Genau das möchte ich mit euch teilen. Ich bereite dafuer eine klassische Tavolata vor, bei der verschiedene Gerichte in die Mitte des Tisches kommen und sich jeder bedient."
|
||||
],
|
||||
"menu": [
|
||||
"Bruschetta-Variationen und Antipasti",
|
||||
"Hausgemachte Pasta mit saisonalem Gemüse",
|
||||
"Optional mit Salsiccia-Ragu",
|
||||
"Tiramisu als Dolce"
|
||||
],
|
||||
"specifications": [
|
||||
"Glutenfrei"
|
||||
],
|
||||
"participants": [
|
||||
"Ferdinando",
|
||||
"Alina",
|
||||
"Ramon",
|
||||
"Franca"
|
||||
|
||||
],
|
||||
"gallery": [
|
||||
"https://i.pinimg.com/736x/f8/48/e2/f848e218a5bd6702c9fbb225c8eebb3e.jpg",
|
||||
"https://i.pinimg.com/736x/b1/3a/00/b13a00d5ee7a93b0e14f757d27043370.jpg",
|
||||
"https://i.pinimg.com/1200x/78/5b/05/785b052394a8337c9a3b152a9745a580.jpg",
|
||||
"https://i.pinimg.com/1200x/82/5e/9c/825e9c735e6fccdf6e7a4ac5dc2b2b7b.jpg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Noche Peruana",
|
||||
"location": "Chur",
|
||||
"date": "11. APR. 2026",
|
||||
"time": "19:00 UHR",
|
||||
"category": "DINNER",
|
||||
"diet": "Omnivore",
|
||||
"spots": 4,
|
||||
"host": {
|
||||
"name": "Camila",
|
||||
"initial": "C"
|
||||
},
|
||||
"hostMessage": [
|
||||
"¡Hola a todos! Ich lade euch ein auf eine kulinarische Reise nach Peru.",
|
||||
"Ich koche für euch ein authentisches peruanisches Sharing-Menü, das vor Lebensfreude nur so sprüht. Freut euch auf eine Explosion aus leuchtenden Farben, fein abgestimmter Schärfe und der unverwechselbaren Frische verschiedenster Kräuter.",
|
||||
"Wir genießen den Abend gemeinsam in mehreren kleinen Gängen, ganz nach dem Sharing-Prinzip. Dabei entdecken wir die klassischen Aromen meiner Heimatstadt Lima – von traditionell bis modern interpretiert.",
|
||||
"Es wird gesellig, aromatisch und ein echtes Erlebnis für alle Sinne. ¡Buen provecho!"
|
||||
],
|
||||
"menu": [
|
||||
"Ceviche mit Limette und Koriander",
|
||||
"Aji de Gallina",
|
||||
"Lomo Saltado",
|
||||
"Suspiro a la Limena"
|
||||
],
|
||||
"specifications": [
|
||||
|
||||
],
|
||||
"participants": [
|
||||
"Camila",
|
||||
"Mara",
|
||||
"Luis",
|
||||
"Tobias"
|
||||
],
|
||||
"gallery": [
|
||||
"https://i.pinimg.com/736x/f4/4c/59/f44c597ce62067eef2b03091f30d2279.jpg",
|
||||
"https://i.pinimg.com/736x/1d/41/eb/1d41ebe23ce4dde7853ab93fce1cfdb2.jpg",
|
||||
"https://i.pinimg.com/736x/8d/9e/2e/8d9e2e13651f11ef64f661cb1d959738.jpg",
|
||||
"https://i.pinimg.com/1200x/ee/bf/f0/eebff08d0d4d20e1b4505495925fb1d8.jpg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Japanese Delight",
|
||||
"location": "ZÜRICH",
|
||||
"date": "02. MAI. 2026",
|
||||
"time": "12:30 UHR",
|
||||
"category": "LUNCH",
|
||||
"diet": "Pescetarisch",
|
||||
"spots": 8,
|
||||
"host": {
|
||||
"name": "Akiko",
|
||||
"initial": "A"
|
||||
},
|
||||
"hostMessage": [
|
||||
"Willkommen zu einem entspannten Lunch mit japanischen Klassikern und hausgemachten Beilagen.",
|
||||
"Ich zeige euch, wie wir verschiedene kleine Teller kombinieren, damit jeder probieren kann."
|
||||
],
|
||||
"menu": [
|
||||
"Miso Suppe",
|
||||
"Sashimi Variation",
|
||||
"Matcha Mochi"
|
||||
],
|
||||
"specifications": [
|
||||
"Glutenfrei",
|
||||
"Laktosefrei"
|
||||
|
||||
],
|
||||
"participants": [
|
||||
"Akiko",
|
||||
"Jan",
|
||||
"Mina"
|
||||
],
|
||||
"gallery": [
|
||||
"https://i.pinimg.com/1200x/e2/6a/f5/e26af5c24b805081a3f304d240818302.jpg",
|
||||
"https://i.pinimg.com/736x/21/77/4a/21774adee4ae0e4f7a1494e33ab3856b.jpg",
|
||||
"https://i.pinimg.com/1200x/b1/fb/3a/b1fb3a7809f4046843904ac8800daacc.jpg",
|
||||
"https://i.pinimg.com/1200x/c6/93/42/c69342ec621333e853c35bda891d8bc6.jpg"
|
||||
]
|
||||
}
|
||||
]
|
||||
122
estelle-köhler.html
Normal file
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Estelle's Comedy Show</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #111;
|
||||
--text-color: #eee;
|
||||
--accent-color: #ff69b4; /* pink accent */
|
||||
--accent2-color: #9370db; /* lila accent */
|
||||
--font-stack: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font-stack);
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
text-align: center;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
header {
|
||||
background-color: #222;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.hero {
|
||||
padding: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.hero img {
|
||||
max-height: 400px;
|
||||
width: auto;
|
||||
border-radius: 8px;
|
||||
border: 4px solid var(--accent-color);
|
||||
}
|
||||
.tour {
|
||||
padding: 2rem;
|
||||
}
|
||||
.tour table {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.tour th, .tour td {
|
||||
border: 1px solid #444;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.tour th {
|
||||
background-color: #222;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.hero img {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Estelle's Comedy Show</h1>
|
||||
</header>
|
||||
|
||||
<section class="hero">
|
||||
<h2>Live on stage!</h2>
|
||||
<img src="image_2e567a.png" alt="Estelle auf der Bühne">
|
||||
</section>
|
||||
|
||||
<section class="tour">
|
||||
<h2>Tour-Daten</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Datum</th><th>Ort</th><th>Status</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>15. April 2026</td><td>Zürich</td><td>Tickets verfügbar</td></tr>
|
||||
<tr><td>22. April 2026</td><td>Bern</td><td>Ausverkauft</td></tr>
|
||||
<tr><td>30. April 2026</td><td>Basel</td><td>Tickets verfügbar</td></tr>
|
||||
<tr><td>5. Mai 2026</td><td>Lausanne</td><td>Vorverkauf</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>© 2026 Estelle's Comedy Show</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// cursor laugh effect
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = 'Ha Ha Ha';
|
||||
span.style.position = 'fixed';
|
||||
span.style.left = e.pageX + 'px';
|
||||
span.style.top = e.pageY + 'px';
|
||||
span.style.color = 'var(--accent-color)';
|
||||
span.style.pointerEvents = 'none';
|
||||
span.style.opacity = '1';
|
||||
span.style.transition = 'transform 1s ease-out, opacity 1s ease-out';
|
||||
const size = Math.random() * 10 + 14;
|
||||
span.style.fontSize = size + 'px';
|
||||
span.style.transform = `translate(-50%, -50%) rotate(${(Math.random()*40-20)}deg)`;
|
||||
document.body.appendChild(span);
|
||||
requestAnimationFrame(() => {
|
||||
span.style.transform += ' translateY(-30px)';
|
||||
span.style.opacity = '0';
|
||||
});
|
||||
setTimeout(() => document.body.removeChild(span), 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
estelle-köhler.txt
Normal file
@ -0,0 +1 @@
|
||||
Welcome to the chaos
|
||||
BIN
estelle.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
@ -1,418 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Event erstellen | Invité</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/event_create.css" />
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<main class="event-create-page">
|
||||
<section class="event-flow-header" aria-label="Event erstellen Aktionen">
|
||||
</section>
|
||||
|
||||
<form id="eventForm" class="event-form" novalidate>
|
||||
<section
|
||||
class="step step--active step--intro"
|
||||
data-step="0"
|
||||
aria-labelledby="intro-title"
|
||||
>
|
||||
<div class="step-layout step-layout--intro">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Event erstellen</p>
|
||||
<h1 id="intro-title">Hey <span id="username">{{username}}</span>, was hast du vor?</h1>
|
||||
<p class="step-text">
|
||||
Erzähl uns von deiner Idee – vom Essen bis zur Stimmung. Ob Dinner, Brunch
|
||||
oder etwas ganz Eigenes – wir helfen dir dabei, dein Event in sieben Schritten aufzubauen.
|
||||
</p>
|
||||
<button type="button" class="button button--primary button--intro" data-start-flow>
|
||||
Los geht’s!
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<aside class="intro-card intro-card--image" aria-label="Stimmungsbild zur Event-Erstellung">
|
||||
<img
|
||||
class="intro-image"
|
||||
src="assets/eventcreate_foodtable.jpg"
|
||||
alt="Ein gedeckter Tisch mit gemeinsamem Essen"
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="1" aria-labelledby="step1-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 1</p>
|
||||
<h2 id="step1-title">Was hast du vor?</h2>
|
||||
<p class="step-text">
|
||||
Erzähl uns, was für ein Event du planst. Ist es ein gemütlicher Brunch,
|
||||
ein Dinner mit Wow-Effekt oder einfach ein entspannter Abend mit gutem Essen?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<fieldset class="form-field">
|
||||
<legend>Art des Essens / Eventtyp</legend>
|
||||
|
||||
<div class="option-grid option-grid--4">
|
||||
<label class="option-card">
|
||||
<input type="radio" name="eventType" value="Brunch" required />
|
||||
<span>Brunch</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card">
|
||||
<input type="radio" name="eventType" value="Lunch" />
|
||||
<span>Lunch</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card">
|
||||
<input type="radio" name="eventType" value="Kaffee + Kuchen" />
|
||||
<span>Kaffee + Kuchen</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card">
|
||||
<input type="radio" name="eventType" value="Dinner" />
|
||||
<span>Dinner</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="form-field">
|
||||
<legend>Maximale Personenanzahl</legend>
|
||||
|
||||
<div class="counter" data-counter>
|
||||
<button
|
||||
type="button"
|
||||
class="counter-button"
|
||||
data-counter-action="decrease"
|
||||
aria-label="Personenzahl verringern"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
id="maxGuests"
|
||||
name="maxGuests"
|
||||
min="1"
|
||||
step="1"
|
||||
value="4"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="counter-button"
|
||||
data-counter-action="increase"
|
||||
aria-label="Personenzahl erhöhen"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="2" aria-labelledby="step2-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 2</p>
|
||||
<h2 id="step2-title">Was kommt auf den Tisch?</h2>
|
||||
<p class="step-text">
|
||||
Mach uns neugierig. Was gibt es zu essen? Gibt es eine bestimmte Ernährungsform oder ein Motto? Je mehr du verrätst, desto besser können sich deine Gäste auf dein Event freuen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<fieldset class="form-field">
|
||||
<legend>Ernährungsform</legend>
|
||||
|
||||
<div class="option-grid option-grid--3">
|
||||
<label class="option-card">
|
||||
<input type="radio" name="dietType" value="Omnivor" required />
|
||||
<span>Omnivor</span>
|
||||
<small>Fleisch und/oder Fisch</small>
|
||||
</label>
|
||||
|
||||
<label class="option-card">
|
||||
<input type="radio" name="dietType" value="Vegetarisch" />
|
||||
<span>Vegetarisch</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card">
|
||||
<input type="radio" name="dietType" value="Vegan" />
|
||||
<span>Vegan</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="menuDescription">Was ist das Menü?</label>
|
||||
<textarea id="menuDescription" name="menuDescription" rows="5" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="3" aria-labelledby="step3-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 3</p>
|
||||
<h2 id="step3-title">Gibt es etwas zu beachten?</h2>
|
||||
<p class="step-text">
|
||||
Gibt es Allergien, Unverträglichkeiten oder andere Hinweise, die für dein Event wichtig sind? So wissen deine Gäste gleich, worauf sie sich einstellen können.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<fieldset class="form-field">
|
||||
<legend>Allergene / Unverträglichkeiten</legend>
|
||||
<p class="field-hint">Optional – nur auswählen, wenn es für dein Event relevant ist.</p>
|
||||
|
||||
<div class="option-grid option-grid--3">
|
||||
<label class="option-card option-card--checkbox">
|
||||
<input type="checkbox" name="allergies" value="glutenfrei" />
|
||||
<span>glutenfrei</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card option-card--checkbox">
|
||||
<input type="checkbox" name="allergies" value="laktosefrei" />
|
||||
<span>laktosefrei</span>
|
||||
</label>
|
||||
|
||||
<label class="option-card option-card--checkbox">
|
||||
<input type="checkbox" name="allergies" value="ohne Nüsse" />
|
||||
<span>ohne Nüsse</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="allergiesOther">Weitere Unverträglichkeiten oder Hinweise (optional)</label>
|
||||
<textarea id="allergiesOther" name="allergiesOther" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="4" aria-labelledby="step4-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 4</p>
|
||||
<h2 id="step4-title">Wann findet dein Event statt?</h2>
|
||||
<p class="step-text">
|
||||
Wähle Datum und Uhrzeit für dein Event. So können deine Gäste direkt einschätzen, ob der Termin für sie passt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<div class="field-row">
|
||||
<div class="form-field">
|
||||
<label for="eventDate">Datum</label>
|
||||
<input type="date" id="eventDate" name="eventDate" required />
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="eventTime">Uhrzeit</label>
|
||||
<input type="time" id="eventTime" name="eventTime" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="5" aria-labelledby="step5-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 5</p>
|
||||
<h2 id="step5-title">Wo findet dein Event statt?</h2>
|
||||
<p class="step-text">
|
||||
Sag uns, wo dein Event stattfindet. Keine Sorge: Die genaue Adresse sehen Gäste erst nach der Buchung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<div class="form-field">
|
||||
<label for="eventAddress">Adresse</label>
|
||||
<input type="text" id="eventAddress" name="eventAddress" autocomplete="street-address" required />
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="eventCity">Ort</label>
|
||||
<input type="text" id="eventCity" name="eventCity" autocomplete="address-level2" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="6" aria-labelledby="step6-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 6</p>
|
||||
<h2 id="step6-title">Gib deinem Event den letzten Schliff.</h2>
|
||||
<p class="step-text">
|
||||
Jetzt bekommt dein Event seinen Namen und die Atmosphäre, die Lust aufs Dabeisein macht.
|
||||
Ein klarer Titel (z.B. "Italienische Tavolata") und ein guter Beschreibungstext (Ablauf etc.) machen den Unterschied.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="step-fields">
|
||||
<div class="form-field">
|
||||
<label for="eventTitle">Wie soll dein Event heißen?</label>
|
||||
<input type="text" id="eventTitle" name="eventTitle" required />
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="eventDescription">Beschreibung des Event-Abends</label>
|
||||
<textarea id="eventDescription" name="eventDescription" rows="6" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="step" data-step="7" aria-labelledby="step7-title">
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Schritt 7</p>
|
||||
<h2 id="step7-title">Dein Event auf einen Blick.</h2>
|
||||
<p class="step-text">
|
||||
Schau dir alle Details nochmal in Ruhe an. Wenn alles passt,
|
||||
kannst du dein Event jetzt veröffentlichen und Gäste einladen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="review-card" aria-live="polite">
|
||||
<dl class="review-list">
|
||||
<div class="review-item" data-edit-step="1" data-edit-field="eventType" role="button" tabindex="0" aria-label="Eventtyp bearbeiten">
|
||||
<dt>Eventtyp</dt>
|
||||
<dd data-review="eventType">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="1" data-edit-field="maxGuests" role="button" tabindex="0" aria-label="Maximale Personenanzahl bearbeiten">
|
||||
<dt>Maximale Personenanzahl</dt>
|
||||
<dd data-review="maxGuests">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="2" data-edit-field="dietType" role="button" tabindex="0" aria-label="Ernährungsform bearbeiten">
|
||||
<dt>Ernährungsform</dt>
|
||||
<dd data-review="dietType">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="2" data-edit-field="menuDescription" role="button" tabindex="0" aria-label="Menü bearbeiten">
|
||||
<dt>Menü</dt>
|
||||
<dd data-review="menuDescription">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="3" data-edit-field="allergiesOther" role="button" tabindex="0" aria-label="Allergene und Unverträglichkeiten bearbeiten">
|
||||
<dt>Allergene / Unverträglichkeiten</dt>
|
||||
<dd data-review="allergies">Keine Angabe</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="4" data-edit-field="eventDate" role="button" tabindex="0" aria-label="Datum bearbeiten">
|
||||
<dt>Datum</dt>
|
||||
<dd data-review="eventDate">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="4" data-edit-field="eventTime" role="button" tabindex="0" aria-label="Uhrzeit bearbeiten">
|
||||
<dt>Uhrzeit</dt>
|
||||
<dd data-review="eventTime">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="5" data-edit-field="eventAddress" role="button" tabindex="0" aria-label="Adresse bearbeiten">
|
||||
<dt>Adresse</dt>
|
||||
<dd data-review="eventAddress">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="5" data-edit-field="eventCity" role="button" tabindex="0" aria-label="Ort bearbeiten">
|
||||
<dt>Ort</dt>
|
||||
<dd data-review="eventCity">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="6" data-edit-field="eventTitle" role="button" tabindex="0" aria-label="Eventtitel bearbeiten">
|
||||
<dt>Eventtitel</dt>
|
||||
<dd data-review="eventTitle">–</dd>
|
||||
</div>
|
||||
|
||||
<div class="review-item" data-edit-step="6" data-edit-field="eventDescription" role="button" tabindex="0" aria-label="Event-Abend bearbeiten">
|
||||
<dt>Event-Abend</dt>
|
||||
<dd data-review="eventDescription">–</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="flow-footer" id="flowFooter" hidden>
|
||||
<div class="progress-wrap" aria-hidden="true">
|
||||
<div class="progress-marker" id="progressMarker">
|
||||
<span class="progress-marker__circle" id="progressMarkerLabel">1</span>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<span id="progressBar" class="progress-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-actions">
|
||||
<button type="button" id="backButton" class="button button--text">Zurück</button>
|
||||
|
||||
<div class="flow-actions-right">
|
||||
<p id="errorMessage" class="error-message" role="alert" aria-live="assertive"></p>
|
||||
<button type="button" id="nextButton" class="button button--primary">Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section
|
||||
id="submissionSuccess"
|
||||
class="submission-success"
|
||||
aria-labelledby="success-title"
|
||||
aria-live="polite"
|
||||
hidden
|
||||
>
|
||||
<div class="step-layout">
|
||||
<div class="step-copy">
|
||||
<p class="step-kicker">Event erstellt</p>
|
||||
<h2 id="success-title">Dein Event ist ready.</h2>
|
||||
<p class="step-text">
|
||||
Sieht gut aus: Deine Idee ist jetzt live und bereit für Gäste.
|
||||
Im Profil kannst du dein Event anschauen, verwalten oder direkt das nächste planen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="review-card review-card--success">
|
||||
<div class="submission-success-actions">
|
||||
<a class="button button--primary" href="event_overview.html">Weiter zu deinem Profil</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<p>© Social Cooking</p>
|
||||
</footer>
|
||||
|
||||
<script src="js/event_create.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,41 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Event-Detail</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/event_overview.css">
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main content: detail page gets fully injected by JavaScript -->
|
||||
<main class="container">
|
||||
<!-- Render target: loading, error state or full detail layout -->
|
||||
<div id="detail-view">
|
||||
<p>Lädt Event-Details...</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Page logic: fetch by URL id, compose detail UI, handle gallery lightbox -->
|
||||
<script src="js/event_detail.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -1,71 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Event-Overview</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/event_overview.css">
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main content: page headline, filter controls and dynamic event list -->
|
||||
<main class="container">
|
||||
<!-- Page headline -->
|
||||
<h1 class="overview-title">Events</h1>
|
||||
|
||||
<!-- Filter section: category chips + location/date filters -->
|
||||
<section class="filter-section">
|
||||
<p class="filter-label">Was darf es sein?</p>
|
||||
<div class="filter-row">
|
||||
<!-- Primary category filter buttons -->
|
||||
<div class="category-group">
|
||||
<button class="category-item" type="button" data-cat="BRUNCH">BRUNCH</button>
|
||||
<button class="category-item" type="button" data-cat="LUNCH">LUNCH</button>
|
||||
<button class="category-item" type="button" data-cat="DINNER">DINNER</button>
|
||||
<button class="category-item" type="button" data-cat="COFFEE">COFFEE</button>
|
||||
<button class="category-item active" type="button" data-cat="ALLE">ALLE</button>
|
||||
</div>
|
||||
|
||||
<!-- Secondary filters populated/handled by JavaScript -->
|
||||
<div class="meta-filter-group" aria-label="Weitere Filter">
|
||||
<label class="meta-filter" for="location-filter">
|
||||
<span>Ort</span>
|
||||
<select id="location-filter">
|
||||
<option value="ALLE_ORTE">Alle Orte</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="meta-filter" for="date-filter">
|
||||
<span>Datum</span>
|
||||
<input id="date-filter" type="date">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Render target: event cards or empty state -->
|
||||
<section id="event-grid" class="event-list"></section>
|
||||
</main>
|
||||
|
||||
<!-- Page logic: data loading, filtering and card rendering -->
|
||||
<script src="js/event_overview.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
134
index.html
@ -1,134 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Invité | Events entdecken</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/landingpage.css?v=2" />
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css?v=2">
|
||||
|
||||
<!-- Font Awesome
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-dU7ZrF1pFq5kVnPzlV9+04YhARzNjCX5Q5P1shgMpuN4s5I8mI8QD4981h7kYtV7sSgNldR0z5pZW5bS2ZpY3Q==" crossorigin="anonymous" referrerpolicy="no-referrer" /> -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<!-- Hero: uses .hero, .btn, .image-card, and .hero-image for a polished first impression -->
|
||||
<section class="hero">
|
||||
<div class="hero__left">
|
||||
<h1>Dein Platz am Tisch wartet schon.</h1>
|
||||
<p>Egal, ob du leidenschaftlich gerne den Kochlöffel schwingst oder dich einfach auf ein hausgemachtes Essen in guter Gesellschaft freust: Bei Invité bringst du Menschen zusammen. Finde Events, die zu deinem Geschmack passen, und geniesse unkomplizierte Begegnungen ohne Networking-Zwang.</p>
|
||||
<a class="btn" href="login.html">Anmelden</a>
|
||||
</div>
|
||||
|
||||
<div class="hero__right">
|
||||
<div class="image-card">
|
||||
<img class="hero-image" src="assets/Startpage ingredients.jpg" alt="Startpage Ingredients" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="how-it-works">
|
||||
<div class="how-it-works__header">
|
||||
<h2>So funktioniert's</h2>
|
||||
</div>
|
||||
<div class="how-it-works__steps">
|
||||
<article class="how-step how-step--numbered">
|
||||
<span class="how-step__corner-number how-step__corner-number--brown">1</span>
|
||||
<img src="assets/register icon.png" alt="Register" class="how-step__png how-step__png--brown" />
|
||||
<p class="how-step__label how-step__label--brown how-step__label--big">Anmelden</p>
|
||||
</article>
|
||||
<article class="how-step how-step--numbered">
|
||||
<span class="how-step__corner-number how-step__corner-number--brown">2</span>
|
||||
<img src="assets/add-event icon.png" alt="Event erstellen" class="how-step__png how-step__png--brown" />
|
||||
<p class="how-step__label how-step__label--brown how-step__label--big">Event erstellen</p>
|
||||
</article>
|
||||
<article class="how-step how-step--numbered">
|
||||
<span class="how-step__corner-number how-step__corner-number--brown">3</span>
|
||||
<img src="assets/Plate icon.png" alt="Gemeinsam essen" class="how-step__png how-step__png--brown" />
|
||||
<p class="how-step__label how-step__label--brown how-step__label--big">Gemeinsam essen</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Main Content: uses .gallery, .gallery__carousel, .gallery__track, .gallery__item, and .gallery__info to present event carousel content -->
|
||||
<section class="gallery">
|
||||
<div class="gallery__carousel">
|
||||
<button type="button" class="gallery__arrow gallery__arrow--prev" aria-label="Vorheriges Bild">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
|
||||
<div class="gallery__track">
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Red checkered social eating.jpg" alt="Red checkered social eating">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Pasta and many forks.jpg" alt="Pasta and many forks">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Zoomed in asian eating.jpg" alt="Zoomed in asian eating">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Burger eating together.jpg" alt="Burger eating together">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Cake cutting figs.jpg" alt="Cake cutting figs">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Cooking woman at home.jpg" alt="Cooking woman at home">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Eating and laughing girls.jpg" alt="Eating and laughing girls">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Pasta in cheese.jpg" alt="Pasta in cheese">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Salad roommates.jpg" alt="Salad roommates">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Sharing food table.jpg" alt="Sharing food table">
|
||||
</article>
|
||||
<article class="gallery__item">
|
||||
<img src="assets/Spicy food zoomed.jpg" alt="Spicy food zoomed">
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<button type="button" class="gallery__arrow gallery__arrow--next" aria-label="Nächstes Bild">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="gallery__info">
|
||||
<div class="gallery__handle" style="display: flex; align-items: center; gap: 16px;">
|
||||
<img src="assets/instagram.png" alt="Instagram" class="gallery__icon--instagram" />
|
||||
<img src="assets/logo_invite.svg" alt="Invité Logo" class="gallery__icon--invite" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="js/index-carousel.js"></script>
|
||||
<footer class="footer">
|
||||
<a href="#" class="footer__link">Impressum</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,761 +0,0 @@
|
||||
// =============================
|
||||
// SETUP: Wichtige HTML-Elemente holen
|
||||
// Diese Konstanten verbinden unser JavaScript mit dem HTML.
|
||||
// So können wir später Buttons, Formularfelder und Bereiche steuern.
|
||||
// =============================
|
||||
const form = document.getElementById("eventForm");
|
||||
const steps = Array.from(document.querySelectorAll(".step"));
|
||||
const backButton = document.getElementById("backButton");
|
||||
const nextButton = document.getElementById("nextButton");
|
||||
const progressBar = document.getElementById("progressBar");
|
||||
const progressMarker = document.getElementById("progressMarker");
|
||||
const progressMarkerLabel = document.getElementById("progressMarkerLabel");
|
||||
const errorMessage = document.getElementById("errorMessage");
|
||||
const usernameElement = document.getElementById("username");
|
||||
const flowFooter = document.getElementById("flowFooter");
|
||||
const submissionSuccess = document.getElementById("submissionSuccess");
|
||||
const EVENTS_STORAGE_KEY = "socialCookingEvents";
|
||||
|
||||
// =============================
|
||||
// STATE: aktueller Schritt im Flow
|
||||
// currentStep merkt sich, auf welchem Schritt der User gerade ist.
|
||||
// lastStep ist der letzte Schritt im Formular.
|
||||
// =============================
|
||||
let currentStep = 0;
|
||||
const lastStep = steps.length - 1;
|
||||
|
||||
// Text für den Weiter-Button je nach Schritt
|
||||
const nextLabels = {
|
||||
0: "Weiter",
|
||||
1: "Weiter",
|
||||
2: "Weiter",
|
||||
3: "Weiter",
|
||||
4: "Weiter",
|
||||
5: "Weiter",
|
||||
6: "Weiter",
|
||||
7: "Event veröffentlichen"
|
||||
};
|
||||
|
||||
// Demo-Wert: Später könnte der Name z. B. aus einem User-Profil kommen
|
||||
usernameElement.textContent = "Mia";
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 1: Kleine Hilfsfunktionen
|
||||
// Diese Funktionen helfen uns später an mehreren Stellen im Code.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Gibt alle Eingabefelder eines bestimmten Schritts zurück.
|
||||
* Rückgabe: Array mit input-, textarea- und select-Feldern.
|
||||
*/
|
||||
function getStepFields(stepIndex) {
|
||||
return Array.from(
|
||||
steps[stepIndex].querySelectorAll("input, textarea, select")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt eine Fehlermeldung im Formular an.
|
||||
* Wenn keine Nachricht übergeben wird, wird die Meldung geleert.
|
||||
*/
|
||||
function setErrorMessage(message = "") {
|
||||
errorMessage.textContent = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt alle Fehlermarkierungen innerhalb eines Schritts.
|
||||
*/
|
||||
function clearStepInvalidState(stepIndex) {
|
||||
if (!steps[stepIndex]) return;
|
||||
|
||||
steps[stepIndex]
|
||||
.querySelectorAll(".field-invalid, .option-card--invalid")
|
||||
.forEach(element => {
|
||||
element.classList.remove("field-invalid", "option-card--invalid");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert ein einzelnes Feld visuell als ungültig.
|
||||
*/
|
||||
function markFieldInvalid(field) {
|
||||
field.classList.add("field-invalid");
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert eine ganze Radio-Gruppe visuell als ungültig.
|
||||
*/
|
||||
function markRadioGroupInvalid(group) {
|
||||
group.forEach(field => {
|
||||
const card = field.closest(".option-card");
|
||||
|
||||
if (card) {
|
||||
card.classList.add("option-card--invalid");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 2: Schritt anzeigen & Oberfläche aktualisieren
|
||||
// showStep() ist eine der wichtigsten Funktionen:
|
||||
// Sie bestimmt, welcher Schritt sichtbar ist,
|
||||
// und aktualisiert gleichzeitig die restliche Oberfläche.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Zeigt den gewünschten Schritt an.
|
||||
* Dabei werden auch Buttons, Progress Bar und Review aktualisiert.
|
||||
*/
|
||||
function showStep(index) {
|
||||
currentStep = index;
|
||||
submissionSuccess.hidden = true;
|
||||
clearStepInvalidState(index);
|
||||
|
||||
// Nur der aktuelle Schritt soll sichtbar sein
|
||||
steps.forEach((step, stepIndex) => {
|
||||
step.classList.toggle("step--active", stepIndex === index);
|
||||
});
|
||||
|
||||
updateFlowVisibility(index);
|
||||
updateReviewIfNeeded(index);
|
||||
updateProgressBar(index, lastStep);
|
||||
setErrorMessage("");
|
||||
|
||||
// Für bessere UX: bei jedem Schritt wieder nach oben scrollen
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Steuert Sichtbarkeit von Footer und Buttons.
|
||||
* Im Intro-Schritt werden Footer und Zurück-Button versteckt.
|
||||
* Zusätzlich bekommt der Weiter-Button je nach Schritt ein anderes Label.
|
||||
*/
|
||||
function updateFlowVisibility(stepIndex) {
|
||||
const isIntroStep = stepIndex === 0;
|
||||
|
||||
flowFooter.hidden = isIntroStep;
|
||||
backButton.hidden = isIntroStep;
|
||||
nextButton.textContent = nextLabels[stepIndex];
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 3: Fortschrittsanzeige
|
||||
// Die Progress Bar zeigt, wie weit der User im Flow ist.
|
||||
// Das Intro zählt noch nicht als eigentlicher Formularschritt.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Berechnet den Fortschritt in Prozent und setzt die Breite der Progress Bar.
|
||||
* Beispiel:
|
||||
* - Intro = 0%
|
||||
* - erster echter Formularschritt = 0%
|
||||
* - letzter Schritt = 100%
|
||||
*/
|
||||
function updateProgressBar(stepIndex, totalStepIndex) {
|
||||
let progress = 0;
|
||||
let markerPosition = 0;
|
||||
let markerStep = 1;
|
||||
let markerTransform = "translateX(-50%)";
|
||||
|
||||
if (stepIndex > 0) {
|
||||
progress = ((stepIndex - 1) / (totalStepIndex - 1)) * 100;
|
||||
markerPosition = ((stepIndex - 1) / (totalStepIndex - 1)) * 100;
|
||||
markerStep = stepIndex;
|
||||
}
|
||||
|
||||
progressBar.style.width = `${progress}%`;
|
||||
progressMarker.style.left = `${markerPosition}%`;
|
||||
progressMarker.style.transform = markerTransform;
|
||||
progressMarker.hidden = stepIndex === 0;
|
||||
progressMarkerLabel.textContent = String(markerStep);
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 4: Review-Daten vorbereiten
|
||||
// Im letzten Schritt werden alle eingegebenen Daten nochmals angezeigt.
|
||||
// Dafür brauchen wir Funktionen, die Feldwerte sauber auslesen und formatieren.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Führt das Update der Review nur aus,
|
||||
* wenn wirklich der letzte Schritt geöffnet ist.
|
||||
*/
|
||||
function updateReviewIfNeeded(stepIndex) {
|
||||
if (stepIndex === lastStep) {
|
||||
updateReview();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Wert eines Formularfeldes zurück.
|
||||
* Rückgabe:
|
||||
* - eingegebener Text / ausgewählte Option
|
||||
* - oder "–", falls nichts vorhanden ist
|
||||
*/
|
||||
function getFieldValue(name) {
|
||||
const field = form.elements[name];
|
||||
|
||||
if (!field) return "–";
|
||||
|
||||
// Spezialfall: Radio-Gruppen verhalten sich anders als normale Inputs
|
||||
if (field instanceof RadioNodeList) {
|
||||
return field.value || "–";
|
||||
}
|
||||
|
||||
return field.value.trim() || "–";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle ausgewählten Checkbox-Werte als Text zurück.
|
||||
* Beispiel: "Vegetarisch, Vegan"
|
||||
* Falls nichts ausgewählt wurde: "Keine Angabe"
|
||||
*/
|
||||
function getCheckboxValues(name) {
|
||||
const checked = Array.from(
|
||||
form.querySelectorAll(`input[name="${name}"]:checked`)
|
||||
);
|
||||
|
||||
return checked.length
|
||||
? checked.map(item => item.value).join(", ")
|
||||
: "Keine Angabe";
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert ein Datum für die Review-Anzeige.
|
||||
* Beispiel: aus "2026-03-26" wird "26.03.2026"
|
||||
*/
|
||||
function formatDate(value) {
|
||||
if (!value || value === "–") return "–";
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
|
||||
return new Intl.DateTimeFormat("de-CH", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric"
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibt einen Wert in das passende Feld der Review-Ansicht.
|
||||
* Gesucht wird ein HTML-Element mit data-review="..."
|
||||
*/
|
||||
function updateReviewField(fieldName, value) {
|
||||
const target = document.querySelector(`[data-review="${fieldName}"]`);
|
||||
|
||||
if (target) {
|
||||
target.textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut den Text für Allergien / Hinweise zusammen.
|
||||
* Dabei werden Checkboxen und zusätzliches Freitextfeld kombiniert.
|
||||
*/
|
||||
function buildAllergiesReviewValue() {
|
||||
const selected = getCheckboxValues("allergies");
|
||||
const notes = getFieldValue("allergiesOther");
|
||||
|
||||
if (selected === "Keine Angabe" && notes === "–") return "Keine Angabe";
|
||||
|
||||
if (selected !== "Keine Angabe" && notes !== "–") {
|
||||
return `${selected}, ${notes}`;
|
||||
}
|
||||
|
||||
return selected !== "Keine Angabe" ? selected : notes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest alle wichtigen Formularwerte aus
|
||||
* und schreibt sie gesammelt in die Review-Ansicht.
|
||||
*/
|
||||
function updateReview() {
|
||||
const reviewValues = getReviewValues();
|
||||
|
||||
Object.entries(reviewValues).forEach(([key, value]) => {
|
||||
updateReviewField(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest alle wichtigen Formularwerte gesammelt aus.
|
||||
*/
|
||||
function getReviewValues() {
|
||||
return {
|
||||
eventTitle: getFieldValue("eventTitle"),
|
||||
eventType: getFieldValue("eventType"),
|
||||
menuDescription: getFieldValue("menuDescription"),
|
||||
eventDescription: getFieldValue("eventDescription"),
|
||||
maxGuests: getFieldValue("maxGuests"),
|
||||
dietType: getFieldValue("dietType"),
|
||||
allergies: buildAllergiesReviewValue(),
|
||||
eventDate: formatDate(getFieldValue("eventDate")),
|
||||
eventTime: getFieldValue("eventTime"),
|
||||
eventAddress: getFieldValue("eventAddress"),
|
||||
eventCity: getFieldValue("eventCity")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest lokal gespeicherte Events robust aus dem Browser-Storage.
|
||||
*/
|
||||
function getStoredEvents() {
|
||||
try {
|
||||
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch (error) {
|
||||
console.error("Lokale Events konnten nicht gelesen werden:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speichert die komplette Eventliste zurück in den Browser-Storage.
|
||||
*/
|
||||
function setStoredEvents(events) {
|
||||
localStorage.setItem(EVENTS_STORAGE_KEY, JSON.stringify(events));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert ein ISO-Datum in das bestehende Eventformat der Demo-Daten.
|
||||
*/
|
||||
function formatDateForStorage(value) {
|
||||
if (!value) return "";
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
|
||||
const monthMap = {
|
||||
0: "JAN",
|
||||
1: "FEB",
|
||||
2: "MRZ",
|
||||
3: "APR",
|
||||
4: "MAI",
|
||||
5: "JUN",
|
||||
6: "JUL",
|
||||
7: "AUG",
|
||||
8: "SEP",
|
||||
9: "OKT",
|
||||
10: "NOV",
|
||||
11: "DEZ"
|
||||
};
|
||||
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const month = monthMap[date.getMonth()];
|
||||
const year = date.getFullYear();
|
||||
|
||||
return `${day}. ${month}. ${year}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert die Zeit in das bestehende Eventformat der Demo-Daten.
|
||||
*/
|
||||
function formatTimeForStorage(value) {
|
||||
return value ? `${value} UHR` : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Zerlegt das Menü-Textarea in saubere Listenpunkte.
|
||||
*/
|
||||
function buildMenuItems(value) {
|
||||
return value
|
||||
.split("\n")
|
||||
.map(item => item.replace(/^[•-]\s*/, "").trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leitet den gewählten Eventtyp in die Kategorien der Übersicht über.
|
||||
*/
|
||||
function mapEventTypeToCategory(value) {
|
||||
const categoryMap = {
|
||||
Brunch: "BRUNCH",
|
||||
Lunch: "LUNCH",
|
||||
Dinner: "DINNER",
|
||||
"Kaffee + Kuchen": "COFFEE"
|
||||
};
|
||||
|
||||
return categoryMap[value] || value.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut aus den Formulardaten ein lokal speicherbares Event-Objekt.
|
||||
*/
|
||||
function buildStoredEvent() {
|
||||
const eventType = getFieldValue("eventType");
|
||||
const dietType = getFieldValue("dietType");
|
||||
const menuDescription = form.elements.menuDescription.value.trim();
|
||||
const eventDescription = form.elements.eventDescription.value.trim();
|
||||
const eventDate = form.elements.eventDate.value;
|
||||
const eventTime = form.elements.eventTime.value;
|
||||
const eventCity = form.elements.eventCity.value.trim();
|
||||
|
||||
return {
|
||||
id: Date.now(),
|
||||
title: form.elements.eventTitle.value.trim(),
|
||||
location: eventCity,
|
||||
address: form.elements.eventAddress.value.trim(),
|
||||
date: formatDateForStorage(eventDate),
|
||||
time: formatTimeForStorage(eventTime),
|
||||
category: mapEventTypeToCategory(eventType),
|
||||
diet: dietType,
|
||||
spots: Number(form.elements.maxGuests.value),
|
||||
host: {
|
||||
name: usernameElement.textContent.trim() || "Host",
|
||||
initial: (usernameElement.textContent.trim().charAt(0) || "H").toUpperCase()
|
||||
},
|
||||
hostMessage: [eventDescription],
|
||||
menu: buildMenuItems(menuDescription),
|
||||
specifications: getCheckboxValues("allergies") === "Keine Angabe"
|
||||
? []
|
||||
: getCheckboxValues("allergies").split(", ").filter(Boolean),
|
||||
allergiesNote: form.elements.allergiesOther.value.trim(),
|
||||
participants: [usernameElement.textContent.trim() || "Host"],
|
||||
gallery: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
source: "local"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Speichert das aktuell erstellte Event lokal im Browser.
|
||||
*/
|
||||
function saveCurrentEvent() {
|
||||
const storedEvents = getStoredEvents();
|
||||
const nextEvents = [buildStoredEvent(), ...storedEvents];
|
||||
setStoredEvents(nextEvents);
|
||||
}
|
||||
|
||||
// =============================
|
||||
// STEP 5: Validierung
|
||||
// Bevor der User weitergehen darf, prüfen wir:
|
||||
// 1. Sind Pflichtfelder ausgefüllt?
|
||||
// 2. Wurde bei Pflicht-Radios etwas ausgewählt?
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Prüft, ob der aktuelle Schritt gültig ist.
|
||||
* Rückgabe:
|
||||
* - true = alles okay
|
||||
* - false = es gibt einen Fehler
|
||||
*/
|
||||
function validateCurrentStep() {
|
||||
// Intro und Review müssen nicht validiert werden
|
||||
if (currentStep === 0 || currentStep === lastStep) return true;
|
||||
|
||||
const fields = getStepFields(currentStep);
|
||||
clearStepInvalidState(currentStep);
|
||||
|
||||
// Zuerst Radio-Gruppen prüfen
|
||||
const radioCheck = validateRadioGroups(fields);
|
||||
if (!radioCheck.isValid) {
|
||||
setErrorMessage(radioCheck.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Danach normale Pflichtfelder prüfen
|
||||
const requiredCheck = validateRequiredFields(fields);
|
||||
if (!requiredCheck.isValid) {
|
||||
setErrorMessage(requiredCheck.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
setErrorMessage("");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft alle Radio-Gruppen eines Schritts.
|
||||
* Rückgabe:
|
||||
* - Objekt mit isValid: true/false
|
||||
* - bei Fehler zusätzlich eine passende Meldung
|
||||
*/
|
||||
function validateRadioGroups(fields) {
|
||||
const names = [...new Set(fields.filter(f => f.type === "radio").map(f => f.name))];
|
||||
|
||||
for (const name of names) {
|
||||
const group = Array.from(form.querySelectorAll(`input[name="${name}"]`));
|
||||
const required = group.some(f => f.required);
|
||||
const selected = group.some(f => f.checked);
|
||||
|
||||
if (required && !selected) {
|
||||
markRadioGroupInvalid(group);
|
||||
return {
|
||||
isValid: false,
|
||||
message: "Bitte wähle eine Option aus."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft alle Pflichtfelder ausser Radios und Checkboxen.
|
||||
* Rückgabe:
|
||||
* - Objekt mit isValid: true/false
|
||||
* - bei Fehler zusätzlich eine passende Meldung
|
||||
*/
|
||||
function validateRequiredFields(fields) {
|
||||
for (const field of fields) {
|
||||
if (field.type === "radio" || field.type === "checkbox") continue;
|
||||
|
||||
if (!field.checkValidity()) {
|
||||
markFieldInvalid(field);
|
||||
return {
|
||||
isValid: false,
|
||||
message: "Bitte fülle alle Pflichtfelder aus."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: true };
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 6: Navigation mit Zurück / Weiter
|
||||
// Diese Funktionen bestimmen, was beim Klicken auf die Buttons passiert.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Einen Schritt zurückgehen.
|
||||
* Wenn der User im ersten Formularschritt ist, geht es zurück zum Intro.
|
||||
*/
|
||||
function handleBackClick() {
|
||||
if (currentStep > 1) {
|
||||
showStep(currentStep - 1);
|
||||
} else {
|
||||
showStep(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Einen Schritt weitergehen.
|
||||
* Vorher wird geprüft, ob der aktuelle Schritt gültig ist.
|
||||
* Im letzten Schritt wird stattdessen das Formular abgeschickt.
|
||||
*/
|
||||
function handleNextClick() {
|
||||
if (!validateCurrentStep()) return;
|
||||
|
||||
if (currentStep < lastStep) {
|
||||
showStep(currentStep + 1);
|
||||
} else {
|
||||
form.requestSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 7: Submit
|
||||
// Aktuell ist das nur eine Demo.
|
||||
// Später könnte hier ein API-Call oder Speichern in einer Datenbank passieren.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Reagiert auf das Absenden des Formulars.
|
||||
* preventDefault verhindert, dass die Seite neu lädt.
|
||||
*/
|
||||
function handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
saveCurrentEvent();
|
||||
steps.forEach(step => step.classList.remove("step--active"));
|
||||
flowFooter.hidden = true;
|
||||
submissionSuccess.hidden = false;
|
||||
setErrorMessage("");
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 8: Counter-Felder (+ / -)
|
||||
// Für Zahlenfelder wie z. B. Anzahl Gäste.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Sucht alle Counter-Komponenten
|
||||
* und verbindet die Plus-/Minus-Buttons mit den passenden Funktionen.
|
||||
*/
|
||||
function registerCounterHandlers() {
|
||||
document.querySelectorAll("[data-counter]").forEach(counter => {
|
||||
const input = counter.querySelector("input[type='number']");
|
||||
const dec = counter.querySelector("[data-counter-action='decrease']");
|
||||
const inc = counter.querySelector("[data-counter-action='increase']");
|
||||
|
||||
dec.addEventListener("click", () => updateCounterValue(input, -1));
|
||||
inc.addEventListener("click", () => updateCounterValue(input, 1));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Erhöht oder verringert den Wert eines Zahlenfelds.
|
||||
* Der Wert darf dabei nie kleiner als das definierte Minimum werden.
|
||||
*/
|
||||
function updateCounterValue(input, change) {
|
||||
const min = Number(input.min || 1);
|
||||
const currentValue = Number(input.value || min);
|
||||
input.value = Math.max(min, currentValue + change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Macht aus "-"+Enter im Menüfeld eine einfache Bullet-Liste.
|
||||
*/
|
||||
function registerMenuBulletHandler() {
|
||||
const menuField = document.getElementById("menuDescription");
|
||||
|
||||
if (!menuField) return;
|
||||
|
||||
menuField.addEventListener("keydown", event => {
|
||||
if (event.key !== "Enter") return;
|
||||
|
||||
const { selectionStart, selectionEnd, value } = menuField;
|
||||
const lineStart = value.lastIndexOf("\n", selectionStart - 1) + 1;
|
||||
const lineEnd = value.indexOf("\n", selectionStart);
|
||||
const currentLineEnd = lineEnd === -1 ? value.length : lineEnd;
|
||||
const currentLine = value.slice(lineStart, currentLineEnd);
|
||||
const trimmedLine = currentLine.trim();
|
||||
|
||||
if (trimmedLine !== "-" && !currentLine.startsWith("• ")) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const isEmptyBullet = currentLine.trim() === "•";
|
||||
|
||||
if (isEmptyBullet) {
|
||||
const beforeLine = value.slice(0, lineStart);
|
||||
const afterLine = value.slice(currentLineEnd);
|
||||
const separator = beforeLine.endsWith("\n") || afterLine.startsWith("\n") ? "" : "\n";
|
||||
const nextValue = `${beforeLine}${separator}${afterLine}`.replace(/\n{3,}/g, "\n\n");
|
||||
|
||||
menuField.value = nextValue;
|
||||
const caretPosition = Math.min(lineStart, nextValue.length);
|
||||
menuField.setSelectionRange(caretPosition, caretPosition);
|
||||
menuField.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
return;
|
||||
}
|
||||
|
||||
const bulletLine = currentLine.startsWith("• ") ? currentLine : currentLine.replace("-", "•");
|
||||
const updatedLine = bulletLine.startsWith("• ") ? bulletLine : `• ${trimmedLine.slice(1).trimStart()}`;
|
||||
const beforeLine = value.slice(0, lineStart);
|
||||
const afterLine = value.slice(currentLineEnd);
|
||||
const nextValue = `${beforeLine}${updatedLine}\n• ${afterLine}`;
|
||||
const caretPosition = beforeLine.length + updatedLine.length + 3;
|
||||
|
||||
menuField.value = nextValue;
|
||||
menuField.setSelectionRange(caretPosition, caretPosition);
|
||||
menuField.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Springt aus der Review zurück zum passenden Schritt
|
||||
* und fokussiert das gewünschte Feld für direktes Weiterbearbeiten.
|
||||
*/
|
||||
function registerReviewEditHandlers() {
|
||||
document.querySelectorAll(".review-item[data-edit-step]").forEach(item => {
|
||||
const activateEdit = () => {
|
||||
const stepIndex = Number(item.dataset.editStep);
|
||||
const fieldName = item.dataset.editField;
|
||||
|
||||
showStep(stepIndex);
|
||||
focusFieldByName(fieldName);
|
||||
};
|
||||
|
||||
item.addEventListener("click", activateEdit);
|
||||
item.addEventListener("keydown", event => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
activateEdit();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt Fehlermarkierungen, sobald der User ein Feld korrigiert.
|
||||
*/
|
||||
function registerValidationFeedbackHandlers() {
|
||||
form.querySelectorAll("input, textarea, select").forEach(field => {
|
||||
const clearInvalidState = () => {
|
||||
field.classList.remove("field-invalid");
|
||||
|
||||
if (field.type === "radio") {
|
||||
const group = Array.from(form.querySelectorAll(`input[name="${field.name}"]`));
|
||||
const hasSelection = group.some(item => item.checked);
|
||||
|
||||
if (hasSelection) {
|
||||
group.forEach(item => {
|
||||
const card = item.closest(".option-card");
|
||||
|
||||
if (card) {
|
||||
card.classList.remove("option-card--invalid");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
field.addEventListener("input", clearInvalidState);
|
||||
field.addEventListener("change", clearInvalidState);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt den Fokus auf ein bestimmtes Feld oder die erste Option einer Radio-Gruppe.
|
||||
*/
|
||||
function focusFieldByName(fieldName) {
|
||||
const field = form.elements[fieldName];
|
||||
|
||||
if (!field) return;
|
||||
|
||||
const focusTarget = field instanceof RadioNodeList ? field[0] : field;
|
||||
|
||||
if (focusTarget && typeof focusTarget.focus === "function") {
|
||||
window.setTimeout(() => {
|
||||
focusTarget.focus();
|
||||
}, 150);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// STEP 9: Alles starten
|
||||
// Hier werden alle Event Listener registriert
|
||||
// und der Flow startet mit dem Intro-Schritt.
|
||||
// =============================
|
||||
|
||||
/**
|
||||
* Initialisiert den kompletten Event-Erstellungs-Flow.
|
||||
* Diese Funktion wird einmal beim Laden der Seite aufgerufen.
|
||||
*/
|
||||
function initEventCreationFlow() {
|
||||
// Buttons, die den Flow starten
|
||||
document.querySelectorAll("[data-start-flow]").forEach(btn => {
|
||||
btn.addEventListener("click", () => showStep(1));
|
||||
});
|
||||
|
||||
// Navigation
|
||||
backButton.addEventListener("click", handleBackClick);
|
||||
nextButton.addEventListener("click", handleNextClick);
|
||||
|
||||
// Formular absenden
|
||||
form.addEventListener("submit", handleFormSubmit);
|
||||
|
||||
// Counter aktivieren
|
||||
registerCounterHandlers();
|
||||
registerMenuBulletHandler();
|
||||
registerValidationFeedbackHandlers();
|
||||
registerReviewEditHandlers();
|
||||
|
||||
// Startzustand: Intro anzeigen
|
||||
submissionSuccess.hidden = true;
|
||||
showStep(0);
|
||||
}
|
||||
|
||||
// Startpunkt des Skripts
|
||||
initEventCreationFlow();
|
||||
@ -1,272 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||
// -------------------------------------------------------------
|
||||
// DOM entry point and shared asset path.
|
||||
// -------------------------------------------------------------
|
||||
const detailContainer = document.getElementById('detail-view');
|
||||
const locationIconPath = 'assets/location-pin.svg';
|
||||
|
||||
// Read event id from query string (detail page deep-link support).
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const eventId = parseInt(params.get('id'));
|
||||
|
||||
if (!eventId) {
|
||||
window.location.href = 'event_overview.html';
|
||||
return;
|
||||
}
|
||||
|
||||
function getStoredEvents() {
|
||||
try {
|
||||
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch (error) {
|
||||
console.error('Lokale Events konnten nicht gelesen werden.', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch data source and resolve the matching event record.
|
||||
try {
|
||||
const response = await fetch('data/events.json');
|
||||
const apiEvents = await response.json();
|
||||
const allEvents = [...getStoredEvents(), ...apiEvents];
|
||||
const event = allEvents.find(e => e.id === eventId);
|
||||
|
||||
if (event) {
|
||||
renderDetailPage(event);
|
||||
} else {
|
||||
detailContainer.innerHTML = "<h1>Event wurde nicht gefunden.</h1><a href='event_overview.html'>Zurück zur Übersicht</a>";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Laden der Details:", error);
|
||||
}
|
||||
|
||||
// Format localized date token into full readable date.
|
||||
function formatEventDate(dateString) {
|
||||
const labels = {
|
||||
JAN: 'Januar',
|
||||
FEB: 'Februar',
|
||||
'MÄR': 'März',
|
||||
MRZ: 'März',
|
||||
APR: 'April',
|
||||
MAI: 'Mai',
|
||||
JUN: 'Juni',
|
||||
JUL: 'Juli',
|
||||
AUG: 'August',
|
||||
SEP: 'September',
|
||||
OKT: 'Oktober',
|
||||
NOV: 'November',
|
||||
DEZ: 'Dezember'
|
||||
};
|
||||
|
||||
const match = dateString.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/);
|
||||
if (!match) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
const day = Number(match[1]);
|
||||
const monthLabel = labels[match[2]];
|
||||
const year = match[3];
|
||||
|
||||
return monthLabel ? `${day}. ${monthLabel} ${year}` : dateString;
|
||||
}
|
||||
|
||||
// Normalize time casing for UI consistency.
|
||||
function formatEventTime(timeString) {
|
||||
return timeString.replace('UHR', 'Uhr').trim();
|
||||
}
|
||||
|
||||
// Map diet keys to readable labels while keeping unknown values untouched.
|
||||
function getDietLabel(diet) {
|
||||
const labels = {
|
||||
VEGGIE: 'Vegetarisch',
|
||||
VEGAN: 'Vegan',
|
||||
FLEISCH: 'Fleisch',
|
||||
FISCH: 'Fisch'
|
||||
};
|
||||
|
||||
return labels[diet] || diet;
|
||||
}
|
||||
|
||||
// Compose and inject the full detail UI for a single event.
|
||||
function renderDetailPage(event) {
|
||||
// Core display values and resilient fallbacks for optional data fields.
|
||||
const displayDate = formatEventDate(event.date);
|
||||
const displayTime = formatEventTime(event.time);
|
||||
const dietLabel = getDietLabel(event.diet);
|
||||
const eventCategory = event.category || 'EVENT';
|
||||
const hostName = event.host?.name || 'Host';
|
||||
const hostInitial = (event.host?.initial || hostName.charAt(0) || 'H').charAt(0).toUpperCase();
|
||||
const hostMessage = Array.isArray(event.hostMessage) && event.hostMessage.length > 0
|
||||
? event.hostMessage
|
||||
: ['Der Host hat für dieses Event noch keine Nachricht hinterlegt.'];
|
||||
const menuItems = Array.isArray(event.menu) && event.menu.length > 0
|
||||
? event.menu
|
||||
: ['Menü wird in Kuerze bekannt gegeben.'];
|
||||
const specifications = Array.isArray(event.specifications) && event.specifications.length > 0
|
||||
? event.specifications
|
||||
: [];
|
||||
const participants = Array.isArray(event.participants) ? event.participants : [];
|
||||
const galleryImages = Array.isArray(event.gallery) && event.gallery.length > 0
|
||||
? event.gallery
|
||||
: [event.image, event.image, event.image];
|
||||
const visibleParticipants = participants.slice(0, 6);
|
||||
const remainingParticipants = Math.max(0, participants.length - visibleParticipants.length);
|
||||
const totalGuests = Number.isFinite(event.spots) ? event.spots : 0;
|
||||
const confirmedGuests = participants.length;
|
||||
const freePlaces = Math.max(0, totalGuests - confirmedGuests);
|
||||
const isFull = freePlaces === 0;
|
||||
const detailChips = [
|
||||
`<span class="event-tag">${eventCategory}</span>`,
|
||||
`<span class="event-tag">${dietLabel}</span>`,
|
||||
...specifications.map(item => `<span class="event-tag">${item}</span>`)
|
||||
].join('');
|
||||
|
||||
// Render complete detail page layout including:
|
||||
// hero metadata, host card, menu, participants, gallery and sticky action bar.
|
||||
detailContainer.innerHTML = `
|
||||
<div class="detail-page">
|
||||
<a class="detail-back" href="event_overview.html">
|
||||
<span aria-hidden="true">‹</span>
|
||||
Alle Events
|
||||
</a>
|
||||
|
||||
<section class="detail-hero">
|
||||
<div class="detail-top-row">
|
||||
<span class="event-location">
|
||||
<img src="${locationIconPath}" alt="">
|
||||
${event.location}
|
||||
</span>
|
||||
<p class="event-date-time">${displayDate} | ${displayTime} | ${confirmedGuests}/${totalGuests} Gaeste</p>
|
||||
</div>
|
||||
<h1 class="detail-title">${event.title}</h1>
|
||||
<div class="event-meta-row detail-chip-row">
|
||||
${detailChips}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-content-grid">
|
||||
<div class="detail-side-stack">
|
||||
<article class="host-card detail-panel">
|
||||
<header class="host-header">
|
||||
<span class="host-avatar">${hostInitial}</span>
|
||||
<span class="host-name">${hostName}</span>
|
||||
<span class="host-role">Host</span>
|
||||
</header>
|
||||
${hostMessage.map(paragraph => `<p>${paragraph}</p>`).join('')}
|
||||
</article>
|
||||
|
||||
<article class="detail-panel detail-panel-compact">
|
||||
<h2 class="detail-section-title">Menue</h2>
|
||||
<ul class="detail-menu-list">
|
||||
${menuItems.map(item => `<li>${item}</li>`).join('')}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="detail-panel detail-panel-compact">
|
||||
<div class="detail-participants-head">
|
||||
<h2 class="detail-section-title">Teilnehmer</h2>
|
||||
<a href="#" class="detail-participants-link">Alle ansehen</a>
|
||||
</div>
|
||||
<div class="detail-avatar-row">
|
||||
${visibleParticipants.map(name => `<span class="participant-avatar">${name.charAt(0).toUpperCase()}</span>`).join('')}
|
||||
${remainingParticipants > 0 ? `<span class="participant-more">+${remainingParticipants}</span>` : ''}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="detail-gallery detail-gallery-large">
|
||||
${galleryImages.slice(0, 9).map((img, index) => `
|
||||
<button class="detail-gallery-item" type="button" aria-label="Bild ${index + 1} gross anzeigen" data-fullsrc="${img}">
|
||||
<img src="${img}" alt="${event.title} Bild ${index + 1}" class="detail-gallery-image">
|
||||
</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="detail-action-bar">
|
||||
<div class="detail-action-summary">
|
||||
<small class="detail-action-meta">
|
||||
<span class="event-location detail-action-location">
|
||||
<img src="${locationIconPath}" alt="">
|
||||
${event.location}
|
||||
</span>
|
||||
<span class="detail-action-meta-text">| ${displayDate} | ${displayTime} | ${confirmedGuests}/${totalGuests} Gaeste</span>
|
||||
</small>
|
||||
<strong>${event.title}</strong>
|
||||
</div>
|
||||
|
||||
<div class="detail-action-buttons">
|
||||
<span class="detail-spots-pill${isFull ? ' detail-spots-pill-full' : ''}">
|
||||
${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plaetze frei`}
|
||||
</span>
|
||||
<button class="detail-primary-btn" type="button" ${isFull ? 'disabled' : ''}>
|
||||
Anmelden
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="detail-lightbox" aria-hidden="true">
|
||||
<div class="detail-lightbox-backdrop" data-close-lightbox="true"></div>
|
||||
<figure class="detail-lightbox-content" role="dialog" aria-modal="true" aria-label="Bildansicht">
|
||||
<button class="detail-lightbox-close" type="button" aria-label="Schliessen">×</button>
|
||||
<img class="detail-lightbox-image" src="" alt="Grossansicht Eventbild">
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Lightbox behavior for gallery images:
|
||||
// open on image click, close via backdrop, close button or ESC.
|
||||
// ---------------------------------------------------------
|
||||
const lightbox = detailContainer.querySelector('.detail-lightbox');
|
||||
const lightboxImage = detailContainer.querySelector('.detail-lightbox-image');
|
||||
const lightboxClose = detailContainer.querySelector('.detail-lightbox-close');
|
||||
const galleryButtons = detailContainer.querySelectorAll('.detail-gallery-item');
|
||||
|
||||
// Central close helper to keep all close paths consistent.
|
||||
function closeLightbox() {
|
||||
if (!lightbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightbox.classList.remove('is-open');
|
||||
lightbox.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
if (lightbox && lightboxImage) {
|
||||
// Open with selected image source.
|
||||
galleryButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const imageSrc = button.getAttribute('data-fullsrc');
|
||||
if (!imageSrc) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightboxImage.src = imageSrc;
|
||||
lightbox.classList.add('is-open');
|
||||
lightbox.setAttribute('aria-hidden', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
// Close when user clicks on backdrop.
|
||||
lightbox.addEventListener('click', event => {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLElement && target.hasAttribute('data-close-lightbox')) {
|
||||
closeLightbox();
|
||||
}
|
||||
});
|
||||
|
||||
// Close via dedicated icon/button.
|
||||
lightboxClose?.addEventListener('click', closeLightbox);
|
||||
|
||||
// Close with keyboard for accessibility.
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Escape') {
|
||||
closeLightbox();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,279 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||
// -------------------------------------------------------------
|
||||
// DOM references used throughout the page lifecycle.
|
||||
// -------------------------------------------------------------
|
||||
const eventGrid = document.getElementById('event-grid');
|
||||
const filterButtons = document.querySelectorAll('.category-item');
|
||||
const locationFilter = document.getElementById('location-filter');
|
||||
const dateFilter = document.getElementById('date-filter');
|
||||
const locationIconPath = 'assets/location-pin.svg';
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// In-memory state for fetched events and currently active category.
|
||||
// -------------------------------------------------------------
|
||||
let allEvents = [];
|
||||
let activeCategory = 'ALLE';
|
||||
|
||||
function getStoredEvents() {
|
||||
try {
|
||||
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch (error) {
|
||||
console.error('Lokale Events konnten nicht gelesen werden.', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Initial data bootstrap:
|
||||
// 1) fetch JSON,
|
||||
// 2) populate select options,
|
||||
// 3) restore filter state from sessionStorage,
|
||||
// 4) render filtered list.
|
||||
// -------------------------------------------------------------
|
||||
async function fetchEvents() {
|
||||
try {
|
||||
const response = await fetch('data/events.json');
|
||||
const apiEvents = await response.json();
|
||||
const localEvents = getStoredEvents();
|
||||
allEvents = [...localEvents, ...apiEvents];
|
||||
populateMetaFilters();
|
||||
|
||||
const savedCategory = sessionStorage.getItem('activeFilter') || 'ALLE';
|
||||
const savedLocation = sessionStorage.getItem('activeLocation') || 'ALLE_ORTE';
|
||||
const savedDate = sessionStorage.getItem('activeDate') || '';
|
||||
|
||||
activeCategory = savedCategory;
|
||||
if (locationFilter) {
|
||||
locationFilter.value = hasOption(locationFilter, savedLocation) ? savedLocation : 'ALLE_ORTE';
|
||||
}
|
||||
if (dateFilter) {
|
||||
dateFilter.value = savedDate;
|
||||
}
|
||||
|
||||
applyFilters();
|
||||
} catch (error) {
|
||||
console.error('Fehler:', error);
|
||||
eventGrid.innerHTML = '<p>Events konnten nicht geladen werden.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Build location options dynamically from loaded events.
|
||||
function populateMetaFilters() {
|
||||
const locations = [...new Set(allEvents.map(event => event.location))].sort();
|
||||
|
||||
if (locationFilter) {
|
||||
locations.forEach(location => {
|
||||
const option = document.createElement('option');
|
||||
option.value = location;
|
||||
option.textContent = location;
|
||||
locationFilter.appendChild(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Convert localized event date (e.g. 19. MÄR. 2026) into ISO format for date input comparison.
|
||||
function parseEventDateToIso(dateString) {
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
const months = {
|
||||
JAN: '01',
|
||||
FEB: '02',
|
||||
'MÄR': '03',
|
||||
MRZ: '03',
|
||||
APR: '04',
|
||||
MAI: '05',
|
||||
JUN: '06',
|
||||
JUL: '07',
|
||||
AUG: '08',
|
||||
SEP: '09',
|
||||
OKT: '10',
|
||||
NOV: '11',
|
||||
DEZ: '12'
|
||||
};
|
||||
|
||||
const match = dateString.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/);
|
||||
if (!match) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const day = String(match[1]).padStart(2, '0');
|
||||
const month = months[match[2]];
|
||||
const year = match[3];
|
||||
|
||||
return month ? `${year}-${month}-${day}` : '';
|
||||
}
|
||||
|
||||
// Convert short month notation into full German month label for UI display.
|
||||
function formatEventDate(dateString) {
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||
const [year, month, day] = dateString.split('-');
|
||||
return `${Number(day)}. ${['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'][Number(month) - 1]} ${year}`;
|
||||
}
|
||||
|
||||
const labels = {
|
||||
JAN: 'Januar',
|
||||
FEB: 'Februar',
|
||||
'MÄR': 'März',
|
||||
MRZ: 'März',
|
||||
APR: 'April',
|
||||
MAI: 'Mai',
|
||||
JUN: 'Juni',
|
||||
JUL: 'Juli',
|
||||
AUG: 'August',
|
||||
SEP: 'September',
|
||||
OKT: 'Oktober',
|
||||
NOV: 'November',
|
||||
DEZ: 'Dezember'
|
||||
};
|
||||
|
||||
const match = dateString.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/);
|
||||
if (!match) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
const day = Number(match[1]);
|
||||
const monthLabel = labels[match[2]];
|
||||
const year = match[3];
|
||||
|
||||
return monthLabel ? `${day}. ${monthLabel} ${year}` : dateString;
|
||||
}
|
||||
|
||||
// Normalize time label from UHR to Uhr for consistent typography.
|
||||
function formatEventTime(timeString) {
|
||||
if (!timeString) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return timeString.includes('UHR')
|
||||
? timeString.replace('UHR', 'Uhr').trim()
|
||||
: `${timeString} Uhr`;
|
||||
}
|
||||
|
||||
// Safely verify whether a value exists in the given select element.
|
||||
function hasOption(selectElement, value) {
|
||||
return Array.from(selectElement.options).some(option => option.value === value);
|
||||
}
|
||||
|
||||
// Apply all filters together (category, location, date), update button state, render and persist.
|
||||
function applyFilters() {
|
||||
const selectedLocation = locationFilter ? locationFilter.value : 'ALLE_ORTE';
|
||||
const selectedDate = dateFilter ? dateFilter.value : '';
|
||||
|
||||
filterButtons.forEach(btn => {
|
||||
if (btn.getAttribute('data-cat') === activeCategory) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
const filtered = allEvents.filter(event => {
|
||||
const categoryMatch = activeCategory === 'ALLE' || event.category === activeCategory;
|
||||
const locationMatch = selectedLocation === 'ALLE_ORTE' || event.location === selectedLocation;
|
||||
const eventDateIso = parseEventDateToIso(event.date);
|
||||
const dateMatch = !selectedDate || eventDateIso === selectedDate;
|
||||
|
||||
return categoryMatch && locationMatch && dateMatch;
|
||||
});
|
||||
|
||||
renderEvents(filtered);
|
||||
|
||||
sessionStorage.setItem('activeFilter', activeCategory);
|
||||
sessionStorage.setItem('activeLocation', selectedLocation);
|
||||
sessionStorage.setItem('activeDate', selectedDate);
|
||||
}
|
||||
|
||||
// Render either:
|
||||
// - empty state call-to-action when no results match,
|
||||
// - or event cards with status and metadata.
|
||||
function renderEvents(events) {
|
||||
eventGrid.innerHTML = '';
|
||||
|
||||
if (events.length === 0) {
|
||||
eventGrid.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<p class="empty-state-kicker">Keine Treffer</p>
|
||||
<h3>Schade, aktuell gibt es hier keine Events.</h3>
|
||||
<p>Starte dein eigenes Dinner und bringe die Community an deinen Tisch.</p>
|
||||
<a class="empty-state-link" href="event_create.html">
|
||||
<button class="empty-state-btn" type="button">Event erstellen</button>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
events.forEach(event => {
|
||||
// Card shell and click-through navigation to detail page.
|
||||
const card = document.createElement('article');
|
||||
card.className = 'event-card';
|
||||
card.style.cursor = 'pointer';
|
||||
card.onclick = () => {
|
||||
window.location.href = `event_detail.html?id=${event.id}`;
|
||||
};
|
||||
|
||||
const displayDate = formatEventDate(event.date);
|
||||
const displayTime = formatEventTime(event.time);
|
||||
|
||||
// Capacity logic:
|
||||
// spots = total capacity, participants.length = booked seats.
|
||||
const bookedSeats = event.participants ? event.participants.length : 0;
|
||||
const totalCapacity = event.spots;
|
||||
const freePlaces = Math.max(0, totalCapacity - bookedSeats);
|
||||
const isFull = freePlaces === 0;
|
||||
|
||||
// Build optional specification chips only when data exists.
|
||||
const specsChips = event.specifications && event.specifications.length > 0
|
||||
? event.specifications.map(spec => `<span class="event-tag">${spec}</span>`).join('')
|
||||
: '';
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="event-main">
|
||||
<div class="event-top-row">
|
||||
<span class="event-location">
|
||||
<img src="${locationIconPath}" alt="">
|
||||
${event.location}
|
||||
</span>
|
||||
<p class="event-date-time">${displayDate} | ${displayTime} | ${bookedSeats}/${totalCapacity} Gaeste</p>
|
||||
</div>
|
||||
<h2 class="event-title">${event.title}</h2>
|
||||
<div class="event-meta-row">
|
||||
<span class="event-tag">${event.category}</span>
|
||||
<span class="event-tag">${event.diet}</span>
|
||||
${specsChips}
|
||||
</div>
|
||||
</div>
|
||||
<div class="event-side${isFull ? ' event-side-full' : ''}">
|
||||
<span class="event-spots${isFull ? ' event-spots-full' : ''}">${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plätze FREI`}</span>
|
||||
${isFull ? '' : '<button class="btn-primary" type="button">Anmelden</button>'}
|
||||
</div>
|
||||
`;
|
||||
|
||||
eventGrid.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
// Category filter interactions.
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
activeCategory = button.getAttribute('data-cat');
|
||||
applyFilters();
|
||||
});
|
||||
});
|
||||
|
||||
// Secondary filter interactions.
|
||||
if (locationFilter) {
|
||||
locationFilter.addEventListener('change', applyFilters);
|
||||
}
|
||||
|
||||
if (dateFilter) {
|
||||
dateFilter.addEventListener('change', applyFilters);
|
||||
}
|
||||
|
||||
// Kick off initial load/render cycle.
|
||||
fetchEvents();
|
||||
});
|
||||
@ -1,52 +0,0 @@
|
||||
const carouselTrack = document.querySelector('.gallery__track');
|
||||
const prevArrow = document.querySelector('.gallery__arrow--prev');
|
||||
const nextArrow = document.querySelector('.gallery__arrow--next');
|
||||
|
||||
if (carouselTrack) {
|
||||
const items = Array.from(carouselTrack.querySelectorAll('.gallery__item'));
|
||||
const getItemsPerPage = () => (window.matchMedia('(max-width: 900px)').matches ? 1 : 3);
|
||||
let itemsPerPage = getItemsPerPage();
|
||||
const pageCount = Math.ceil(items.length / itemsPerPage);
|
||||
let activePage = 0;
|
||||
|
||||
function scrollToPage(page) {
|
||||
activePage = page;
|
||||
const pageWidth = carouselTrack.clientWidth;
|
||||
carouselTrack.scrollTo({ left: pageWidth * page, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function showNext() {
|
||||
activePage = (activePage + 1) % pageCount;
|
||||
scrollToPage(activePage);
|
||||
}
|
||||
|
||||
function showPrev() {
|
||||
activePage = (activePage - 1 + pageCount) % pageCount;
|
||||
scrollToPage(activePage);
|
||||
}
|
||||
|
||||
function refreshCarousel() {
|
||||
const responsiveItemsPerPage = getItemsPerPage();
|
||||
if (responsiveItemsPerPage !== itemsPerPage) {
|
||||
itemsPerPage = responsiveItemsPerPage;
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
if (nextArrow) nextArrow.addEventListener('click', showNext);
|
||||
if (prevArrow) prevArrow.addEventListener('click', showPrev);
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'ArrowRight') {
|
||||
showNext();
|
||||
}
|
||||
if (event.key === 'ArrowLeft') {
|
||||
showPrev();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
refreshCarousel();
|
||||
scrollToPage(activePage);
|
||||
});
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
const prevBtn = document.querySelector('.arrow--prev');
|
||||
const nextBtn = document.querySelector('.arrow--next');
|
||||
const items = Array.from(document.querySelectorAll('.gallery__item'));
|
||||
let activeIndex = 0;
|
||||
|
||||
function updateGallery() {
|
||||
items.forEach((item, i) => {
|
||||
item.style.opacity = i === activeIndex ? '1' : '0.35';
|
||||
item.style.transform = i === activeIndex ? 'scale(1)' : 'scale(0.95)';
|
||||
});
|
||||
}
|
||||
|
||||
function showNext() {
|
||||
activeIndex = (activeIndex + 1) % items.length;
|
||||
updateGallery();
|
||||
}
|
||||
|
||||
function showPrev() {
|
||||
activeIndex = (activeIndex - 1 + items.length) % items.length;
|
||||
updateGallery();
|
||||
}
|
||||
|
||||
nextBtn.addEventListener('click', showNext);
|
||||
prevBtn.addEventListener('click', showPrev);
|
||||
|
||||
// keyboard support
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'ArrowRight') {
|
||||
showNext();
|
||||
}
|
||||
if (event.key === 'ArrowLeft') {
|
||||
showPrev();
|
||||
}
|
||||
});
|
||||
|
||||
updateGallery();
|
||||
71
js/login.js
@ -1,71 +0,0 @@
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const emailInput = document.getElementById('email');
|
||||
const passwortInput = document.getElementById('passwort');
|
||||
const emailError = document.getElementById('emailError');
|
||||
const passwortError = document.getElementById('passwortError');
|
||||
|
||||
// Validierungsfunktion
|
||||
function validateForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
let isValid = true;
|
||||
|
||||
// Email-Validierung
|
||||
const emailValue = emailInput.value.trim();
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const emailGroup = emailInput.parentElement;
|
||||
|
||||
if (!emailValue) {
|
||||
emailGroup.classList.add('has-error');
|
||||
emailError.textContent = 'Bitte gib deine E-Mail Adresse ein.';
|
||||
isValid = false;
|
||||
} else if (!emailRegex.test(emailValue)) {
|
||||
emailGroup.classList.add('has-error');
|
||||
emailError.textContent = 'Bitte gib eine gültige E-Mail Adresse ein.';
|
||||
isValid = false;
|
||||
} else {
|
||||
emailGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Passwort-Validierung
|
||||
const passwortValue = passwortInput.value;
|
||||
const passwortGroup = passwortInput.parentElement;
|
||||
|
||||
if (!passwortValue) {
|
||||
passwortGroup.classList.add('has-error');
|
||||
passwortError.textContent = 'Bitte gib dein Passwort ein.';
|
||||
isValid = false;
|
||||
} else if (passwortValue.length < 6) {
|
||||
passwortGroup.classList.add('has-error');
|
||||
passwortError.textContent = 'Dein Passwort ist zu kurz. Bitte überprüfe dein Passwort.';
|
||||
isValid = false;
|
||||
} else {
|
||||
passwortGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Wenn alle Validierungen bestanden, Form absenden
|
||||
if (isValid) {
|
||||
//alert('Login erfolgreich! (Dies ist eine Demo)');
|
||||
|
||||
// Weiterleitung zur event overview Page
|
||||
window.location.href = 'event_overview.html';
|
||||
}
|
||||
}
|
||||
|
||||
// Fehlerbehandlung bei Input-Änderung (entfernt Fehler wenn Benutzer korrigiert)
|
||||
emailInput.addEventListener('input', function() {
|
||||
const emailGroup = this.parentElement;
|
||||
if (this.value.trim()) {
|
||||
emailGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
passwortInput.addEventListener('input', function() {
|
||||
const passwortGroup = this.parentElement;
|
||||
if (this.value) {
|
||||
passwortGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
// Form Submit Event
|
||||
loginForm.addEventListener('submit', validateForm);
|
||||
128
js/signup.js
@ -1,128 +0,0 @@
|
||||
const signupForm = document.getElementById('signupForm');
|
||||
const vornameInput = document.getElementById('vorname');
|
||||
const nachnameInput = document.getElementById('nachname');
|
||||
const emailInput = document.getElementById('email');
|
||||
const passwortInput = document.getElementById('passwort');
|
||||
const welcomeModal = document.getElementById('welcomeModal');
|
||||
|
||||
// Funktion zum Öffnen des Welcome Modals
|
||||
function openWelcomeModal() {
|
||||
welcomeModal.classList.add('show');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
// Funktion zum Schließen des Welcome Modals
|
||||
function closeWelcomeModal() {
|
||||
welcomeModal.classList.remove('show');
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
|
||||
// Validierungsfunktion
|
||||
function validateForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
let isValid = true;
|
||||
|
||||
// Vorname-Validierung
|
||||
const vornameValue = vornameInput.value.trim();
|
||||
const vornameGroup = vornameInput.parentElement;
|
||||
|
||||
if (!vornameValue) {
|
||||
vornameGroup.classList.add('has-error');
|
||||
isValid = false;
|
||||
} else {
|
||||
vornameGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Nachname-Validierung
|
||||
const nachnameValue = nachnameInput.value.trim();
|
||||
const nachnameGroup = nachnameInput.parentElement;
|
||||
|
||||
if (!nachnameValue) {
|
||||
nachnameGroup.classList.add('has-error');
|
||||
isValid = false;
|
||||
} else {
|
||||
nachnameGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Email-Validierung
|
||||
const emailValue = emailInput.value.trim();
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const emailGroup = emailInput.parentElement;
|
||||
|
||||
if (!emailValue) {
|
||||
emailGroup.classList.add('has-error');
|
||||
document.getElementById('emailError').textContent = 'Bitte gib deine E-Mail Adresse ein.';
|
||||
isValid = false;
|
||||
} else if (!emailRegex.test(emailValue)) {
|
||||
emailGroup.classList.add('has-error');
|
||||
document.getElementById('emailError').textContent = 'Bitte gib eine gültige E-Mail Adresse ein.';
|
||||
isValid = false;
|
||||
} else {
|
||||
emailGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Passwort-Validierung
|
||||
const passwortValue = passwortInput.value;
|
||||
const passwortGroup = passwortInput.parentElement;
|
||||
|
||||
if (!passwortValue) {
|
||||
passwortGroup.classList.add('has-error');
|
||||
document.getElementById('passwortError').textContent = 'Bitte gib ein Passwort ein.';
|
||||
isValid = false;
|
||||
} else if (passwortValue.length < 8) {
|
||||
passwortGroup.classList.add('has-error');
|
||||
document.getElementById('passwortError').textContent = 'Dein Passwort muss mindestens 8 Zeichen lang sein.';
|
||||
isValid = false;
|
||||
} else {
|
||||
passwortGroup.classList.remove('has-error');
|
||||
}
|
||||
|
||||
// Wenn alle Validierungen bestanden, Modal anzeigen
|
||||
if (isValid) {
|
||||
openWelcomeModal();
|
||||
// Hier würde später die Registrierung zum Backend gesendet
|
||||
|
||||
// Weiterleitung zur event overview Page
|
||||
window.location.href = 'event_overview.html';
|
||||
}
|
||||
}
|
||||
|
||||
// Fehlerbehandlung bei Input-Änderung (entfernt Fehler wenn Benutzer korrigiert)
|
||||
vornameInput.addEventListener('input', function() {
|
||||
const vornameGroup = this.parentElement;
|
||||
if (this.value.trim()) {
|
||||
vornameGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
nachnameInput.addEventListener('input', function() {
|
||||
const nachnameGroup = this.parentElement;
|
||||
if (this.value.trim()) {
|
||||
nachnameGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
emailInput.addEventListener('input', function() {
|
||||
const emailGroup = this.parentElement;
|
||||
if (this.value.trim()) {
|
||||
emailGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
passwortInput.addEventListener('input', function() {
|
||||
const passwortGroup = this.parentElement;
|
||||
if (this.value) {
|
||||
passwortGroup.classList.remove('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
// Modal schließen wenn außerhalb geklickt wird
|
||||
welcomeModal.addEventListener('click', function(event) {
|
||||
if (event.target === welcomeModal) {
|
||||
closeWelcomeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Form Submit Event
|
||||
signupForm.addEventListener('submit', validateForm);
|
||||
64
login.html
@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Social Cooking</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/login_signup.css">
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<div class="container">
|
||||
<div class="image-section">
|
||||
<img src="assets/cooking.jpg" alt="Social Cooking">
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h1>Login</h1>
|
||||
|
||||
<form id="loginForm">
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail</label>
|
||||
<input type="email" id="email" name="email" required placeholder="deine.email@example.com">
|
||||
<div class="error-message" id="emailError">Bitte gib eine gültige E-Mail Adresse ein.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="passwort">Passwort</label>
|
||||
<input type="password" id="passwort" name="passwort" required placeholder="Gib dein Passwort ein">
|
||||
<div class="error-message" id="passwortError">Bitte gib dein Passwort ein.</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">Login</button>
|
||||
|
||||
<div class="signup-hint">
|
||||
Du hast noch keinen Account? <a href="signup.html">Hier geht es zur Anmeldung.</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- Schliesst main-content -->
|
||||
<script src="js/login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
96
signup.html
@ -1,96 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Kontaktseite - Invité</title>
|
||||
|
||||
<!-- Stylesheet für diese Seite-->
|
||||
<link rel="stylesheet" href="css/login_signup.css">
|
||||
<!-- Globales Stylesheet -->
|
||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Top Navigation mit Seitenlinks -->
|
||||
<header class="top-nav-wrap">
|
||||
<div class="top-nav">
|
||||
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||
</a>
|
||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<div class="container">
|
||||
<div class="image-section">
|
||||
<img src="assets/cooking.jpg" alt="Social Cooking">
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h1>Erstelle deinen Account</h1>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Hinweis:</strong> Sichtbar auf der Plattform ist nur dein Vorname. Erst einer Anmeldung zum Event ist der Nachname für die Teilnehmenden sichtbar.
|
||||
</div>
|
||||
|
||||
<form id="signupForm">
|
||||
<div class="form-group">
|
||||
<label for="vorname">Vorname *</label>
|
||||
<input type="text" id="vorname" name="vorname" required placeholder="Dein Vorname">
|
||||
<div class="error-message" id="vornameError">Bitte gib deinen Vornamen ein.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="nachname">Nachname *</label>
|
||||
<input type="text" id="nachname" name="nachname" required placeholder="Dein Nachname">
|
||||
<div class="error-message" id="nachnameError">Bitte gib deinen Nachnamen ein.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail *</label>
|
||||
<input type="email" id="email" name="email" required placeholder="deine.email@example.com">
|
||||
<div class="error-message" id="emailError">Bitte gib eine gültige E-Mail Adresse ein.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="passwort">Passwort *</label>
|
||||
<input type="password" id="passwort" name="passwort" required placeholder="Mindestens 8 Zeichen">
|
||||
<div class="error-message" id="passwortError">Dein Passwort muss mindestens 8 Zeichen lang sein.</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">Konto erstellen</button>
|
||||
|
||||
|
||||
<div class="login-hint">
|
||||
Du hast bereits einen Account? <a href="login.html">Hier geht es zum Login.</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- Schließt main-content -->
|
||||
|
||||
<!-- Welcome Modal -->
|
||||
<div id="welcomeModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button class="close-btn" onclick="closeWelcomeModal()">×</button>
|
||||
<h2>🎉 Willkommen bei Invité!</h2>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Hier findest du die Übersicht zu den aktuellsten Events.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-primary" onclick="closeWelcomeModal()">Weiter zu den Events</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/signup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||